# HG changeset patch # User samer # Date 1356193071 0 # Node ID 7357e1dc2ad6f0bb2242b8b5557207a21fbf28d7 # Parent 289445d368a7914519c423c8fb20c1ed48b5bc64 Simplified scheduler library with new schedule representation. diff -r 289445d368a7 -r 7357e1dc2ad6 arrows/aica.m --- a/arrows/aica.m Wed Dec 19 22:46:05 2012 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,22 +0,0 @@ -% aica - ICA -% -% aica :: -% model(real) ~'probability model for components', -% [[N,N]] ~'initial weight matrix', -% options { -% rate :: nonneg/1e-4 ~'adaptation rate'; -% } -% -> arrow({[[N]]},{[[N]]},aica_state). - -function o=aica(model,W0,varargin) - opts=prefs('rate',1e-4,varargin{:}); - score=scorefn(model); - - rate=opts.rate; - o=loop(@update,@(s)W0); - - function [y,W]=update(x,W) -% y=W'*x; W=W+rate*(W-(W*y)*score(y)'); - y=W*x; W=W+rate*(W-(score(y)/size(y,2))*(y'*W)); - end -end diff -r 289445d368a7 -r 7357e1dc2ad6 arrows/audio_bench1.m --- a/arrows/audio_bench1.m Wed Dec 19 22:46:05 2012 +0000 +++ b/arrows/audio_bench1.m Sat Dec 22 16:17:51 2012 +0000 @@ -1,10 +1,10 @@ -% audio_bench - put given arrow between audio input and output +% audio_bench1 - supply audio to given arrow % % audio_bench :: % N:natural ~'audio frame size', % M:natural ~'audio hop size', % list(string) ~'list of audio file names', -% arrow({[[N,W]]}, {[[N,W]]}, S) ~'arrow to process audio buffers', +% arrow({[[N,W]]}, Outputs, S) ~'arrow to process audio buffers', % options { % fs :: nonneg/22050 ~'audio sampling rate'; % scope :: natural/0 ~'figure for waveform plot, 0 to disable'; @@ -16,7 +16,7 @@ % period :: nonneg/0.003 ~'if running timer period in seconds'; % liveaudio :: bool/0 ~'if true, ignore files and use live audio input'; % } -% -> arrow( {}, {}, T) ~'arrow describing whole system', +% -> arrow( {}, Outputs, T) ~'arrow describing whole system', % [[1,P]->[2]} ~'path to state S in overall state T', % T, ~'final state if returned from repl'. % diff -r 289445d368a7 -r 7357e1dc2ad6 arrows/stats/aica.m --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/arrows/stats/aica.m Sat Dec 22 16:17:51 2012 +0000 @@ -0,0 +1,22 @@ +% aica - ICA +% +% aica :: +% model(real) ~'probability model for components', +% [[N,N]] ~'initial weight matrix', +% options { +% rate :: nonneg/1e-4 ~'adaptation rate'; +% } +% -> arrow({[[N]]},{[[N]]},aica_state). + +function o=aica(model,W0,varargin) + opts=prefs('rate',1e-4,varargin{:}); + score=scorefn(model); + + rate=opts.rate; + o=loop(@update,@(s)W0); + + function [y,W]=update(x,W) +% y=W'*x; W=W+rate*(W-(W*y)*score(y)'); + y=W*x; W=W+rate*(W-(score(y)/size(y,2))*(y'*W)); + end +end diff -r 289445d368a7 -r 7357e1dc2ad6 audio/playaudio_async.m --- a/audio/playaudio_async.m Wed Dec 19 22:46:05 2012 +0000 +++ b/audio/playaudio_async.m Sat Dec 22 16:17:51 2012 +0000 @@ -1,4 +1,3 @@ -function [Sched,GetData]=playaudio_async(Y,Snk,varargin) % playaudio_async - Play stream of audio data asynchronously % % playaudio :: @@ -40,5 +39,6 @@ % % NB: all the audio buffers must be the same size for this to work. % NB: the rewind function should only be called when the timer is stopped. +function [Sched,GetData]=playaudio_async(Y,Snk,varargin) [Sched,GetData]=playaudio_unfold(size(Y,2),@decons,Y,Snk),varargin{:}); end diff -r 289445d368a7 -r 7357e1dc2ad6 sched/HRTimer.java --- a/sched/HRTimer.java Wed Dec 19 22:46:05 2012 +0000 +++ b/sched/HRTimer.java Sat Dec 22 16:17:51 2012 +0000 @@ -3,8 +3,8 @@ public class HRTimer { - private static long min_sleep=1000000; // 1 ms - private static long wake_before=500000; // 0.1ms + private static long min_sleep=1200000; // 1.2 ms + private static long wake_before=500000; // 0.5ms public static void setMinimumSleepTime(long t) { min_sleep=t; } public static void setWakePrealarm(long t) { wake_before=t; } diff -r 289445d368a7 -r 7357e1dc2ad6 sched/README --- a/sched/README Wed Dec 19 22:46:05 2012 +0000 +++ b/sched/README Sat Dec 22 16:17:51 2012 +0000 @@ -1,31 +1,66 @@ Functions for timed execution -nows - current time in seconds -nows_hr - current time in seconds, high resolution timer -now_ns_hr - current time in seconds, high resolution timer -sleeptill_hr +Some of these functions rely on a high-resolution timer implemented +in Java - you will need to compile HRTimer.java and put it somewhere in +your classpath for these to work. -sched - -msched - Each event is an action which returns the next event to schedule -ssched - Single action with threaded state, action returns time for next event -rsched - Schedule regular periodic events, single action, threaded state -rsched2 - -lsched - States and times are predermined in a sequence -rlsched - Sequences of states to be acted upon at regular intervals -sch_wait.m -sched_at - Use given timer to schedule one-shot event -sched_rec -schedabs.m -schedabs_hr.m -iterate_sched.m +TYPES -timed_action - Call action and return actual time just after execution -isrunning.m -mrndnotes.m -srndnotes.m -statuscb.m + time ::= double ~ a type synonym standing for absolute time in seconds. + A1, A2 ... -> B1, ... ~ a proper function with no side effects. + A1, A2 ... => B1, ... ~ a 'function' which may be stateful and have side effects. + void ~ not really a type, but shorthand for an empty list of types. -timer_gc.m -timer_release.m -timer_wait.m + + timed_action(Ins:arglist(N),Outs:arglist(M)) ::= + double ~'scheduled time', Ins{:} + => double ~'timing error', Outs{:}. + + schedule ::= timed_action({},{schedule}). + + struct { F1; F2; .. } ~ a structure with *at-least* the fields specified by F1, F2 etc. + A field specification is like Name :: Type. + struct {} denotes the supertype of all structures. + + options { O1; O2; .. } ~ represents one or more arguments specifying optional named parameters. + + Options are a list of name/value pairs or structures containing name value pairs. I will + attempt to formalise this as follows: + + Option specification (the O1, O2 etc above) looks like + + option_spec --> Name :: Type + ; Name :: Type/Default where + + Option arguments can be passed as arbitrary sequences of name/value pairs or structs. If the + option specification is options Spec (where Spec is the list O1,O2 etc), then + + options_args(Spec) --> void + ; Name::string, Value::Type, options_args(Spec) + ; struct {}, options_args(Spec). + + +now_ns_hr - current time in nanoseconds using high resolution timer in JVM +nows - Current time in seconds +nows_hr - Current time using high resolution timer in JVM +sleeptil_hr - Sleep until absolute time + +rsched - regular state threading scheduler +schedrec_ - Schedule events where each event returns the next event. +schedrec_hr - execute action recursively at given time using high-resolution timer +sched_after - one shot, relative scheduler using Matlab timer +sched_at - Use one-shot timer to schedule action + +list_schedule - Make event action from list of times and parameters +null_schedule - Make empty schedule +st_schedule - Make state-threading event action from state transformer and state + +timed_action - return time-aware action function given ordinary action function + +timer_gc - Deletes all timers that have been released using releasetimer +timer_release - Mark timer for garbage collection +timer_wait - Wait for timer to stop running +isrunning - true if Matlab timer object is running +statuscb - Returns status message timer callback function handle + diff -r 289445d368a7 -r 7357e1dc2ad6 sched/isrunning.m --- a/sched/isrunning.m Wed Dec 19 22:46:05 2012 +0000 +++ b/sched/isrunning.m Sat Dec 22 16:17:51 2012 +0000 @@ -1,4 +1,4 @@ -% isrunning - true if timer is running +% isrunning - true if Matlab timer object is running % -% isrunning :: timer -> bool. +% isrunning :: timer => bool. function f=isrunning(T), f=isvalid(T) && strcmp(get(T,'Running'),'on'); diff -r 289445d368a7 -r 7357e1dc2ad6 sched/iterate_sched.m --- a/sched/iterate_sched.m Wed Dec 19 22:46:05 2012 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,23 +0,0 @@ -function tm=iterate_sched(nextfn,X,T,varargin) -% iterate_sched - Iterate function under control of timer -% -% iterate_sched :: -% (A->action A) ~'state transformer action', -% A ~'initial state', -% real ~'time between updates in seconds' -% options { -% drawnow :: {0,1} /0 ~'call drawnow after each iteration'; -% busy_mode :: {'queue','drop'} /'queue' ~'See TIMER'; -% its :: natural / inf ~'iteration limit' -% -> timer. - - opts=prefs('its',inf,'drawnow',0,'busy_mode','queue','props',{},varargin{:}); - if opts.drawnow, post=@drawnow; else post=@nop; end - - tm=rlsched(@action,nows,T,opts.its,iterdata(nextfn,X)); - function t1=action(S,t0,t1) - disp(S) - post(); - end -end - diff -r 289445d368a7 -r 7357e1dc2ad6 sched/lsched.m --- a/sched/lsched.m Wed Dec 19 22:46:05 2012 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,73 +0,0 @@ -function Timer=lsched(action,evs) -% lsched - SCHEDule using List of times and parameters to fixed action -% -% lsched :: -% ( S ~'action parameter' -% double ~'scheduled time', -% double ~'actual time' -% -> double ~'actual execution time' -% ) ~'the action to perform at each time', -% seq cell { -% double ~'time of event', -% S ~'argument to pass to action' -% } ~'the list of times and parameters to action' -% -> action timer ~'timer being used'. -% -% Note: this timer will DROP events if they cannot be scheduled roughly -% on time. However, if an event is scheduled with time to spare, but -% for some reason is called late, then it is not dropped. - - persistent ERROR % for estimating of timing errors - - ERROR=[]; - - warning('off','MATLAB:TIMER:STARTDELAYPRECISION'); - - Timer=timer; - if isempty(evs), return; end - - [t0,s0]=cdeal(head(evs)); - - % plan is to use a one shot timer to schedule next - % event. Then, after the timer stops, the StopFcn callback - % is called and we schedule the next event and restart the - % timer. This is really messy - should use my own timer - % facilities written in Java.. - set(Timer,'UserData',{t0,s0,next(evs)}); % fst event and rest - set(Timer,'TimerFcn',@onalarm,'StopFcn',@chain); - set(Timer,'StartDelay',max(0,t0-nows)); - start(Timer); - - function onalarm(o,e) - [t0,s0,evs]=cdeal(get(o,'UserData')); - % could potentially call a 'stalled' - % action if we are very late here - tt=a(s0,t0,e.Data.time); - ERROR=vertcat(ERROR,tt-t0); - end - - % called - function chain(o,e) - [t0,s0,evs]=cdeal(get(o,'UserData')); - - if ~isempty(evs) - correction=mean(ERROR); - - while 1 - [t1,s1]=cdeal(head(evs)); - delay=t1-correction-nows; - if delay>=0 break; end - fprintf('\n| dropping event, lag=%g',-delay); - evs=next(evs); - end - - set(o,'UserData',{t1,s1,next(evs)}); - set(o,'StartDelay',delay); - start(o); - else - fprintf('\n| stopping\n'); - plot(ERROR); - timer_release(o); - end - end -end diff -r 289445d368a7 -r 7357e1dc2ad6 sched/msched.m --- a/sched/msched.m Wed Dec 19 22:46:05 2012 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,62 +0,0 @@ -function Timer=msched(e0) -% msched - Schedule events where each event returns the next event. -% -% msched :: -% cell { -% double ~'time of event', -% event ~'first event action' -% } -% -> action timer ~'timer being used'. -% -% event ::= -% ( double ~'scheduled time', -% double ~'actual time' -% -> action ( -% event ~'the next event action', -% double ~'time for next action', -% double ~'execution time of current event' -% ) -% ). - -% this is most like a Routine or Task in Supercollider, except -% that instead of coroutining via yield, each action explicitly -% returns the continuation action. - - persistent ERROR - - ERROR=[]; - warning('off','MATLAB:TIMER:STARTDELAYPRECISION'); - - [t0,a0]=cdeal(e0); - - Timer=timer; - set(Timer,'TimerFcn',@(o,e)timercb(o,e,t0,a0),'StopFcn',@chain); - set(Timer,'StartDelay',max(0,t0-nows)); % there will be some small error here.. - start(Timer); - - function timercb(o,e,t,a) - [a1,t1,tt]=a(t,e.Data.time); - set(o,'UserData',{a1,t1}); - ERROR=vertcat(ERROR,tt-t); - end - - function chain(o,e) - [a1,t1]=cdeal(get(o,'UserData')); - correction=mean(ERROR); - while ~isempty(t1) - tnow=nows; - delay=t1-correction-tnow; - if delay>=0, - set(o,'TimerFcn',@(oo,ee)timercb(oo,ee,t1,a1)); - set(o,'StartDelay',delay); - start(o); - return; - end - [a1,t1,tt]=a1(t1,tnow); - end - - fprintf('\n| stopping\n'); - set(o,'UserData',ERROR); - timer_release(o); - end -end diff -r 289445d368a7 -r 7357e1dc2ad6 sched/now_ns_hr.m --- a/sched/now_ns_hr.m Wed Dec 19 22:46:05 2012 +0000 +++ b/sched/now_ns_hr.m Sat Dec 22 16:17:51 2012 +0000 @@ -1,7 +1,6 @@ -function t=now_ns_hr -% now_ns_hr - Now in nanoseconds using high resolution timer in JVM +% now_ns_hr - current time in nanoseconds using high resolution timer in JVM % % now_ns_hr :: unit -> real ~'time in nanoseconds'. -t=saml.sched.HRTimer.now; +function t=now_ns_hr, t=saml.sched.HRTimer.now; end diff -r 289445d368a7 -r 7357e1dc2ad6 sched/nows.m --- a/sched/nows.m Wed Dec 19 22:46:05 2012 +0000 +++ b/sched/nows.m Sat Dec 22 16:17:51 2012 +0000 @@ -1,4 +1,4 @@ -function T=nows, T=now*86400; end % nows - Current time in seconds % % nows :: () -> action double. +function T=nows, T=now*86400; end diff -r 289445d368a7 -r 7357e1dc2ad6 sched/nows_hr.m --- a/sched/nows_hr.m Wed Dec 19 22:46:05 2012 +0000 +++ b/sched/nows_hr.m Sat Dec 22 16:17:51 2012 +0000 @@ -1,7 +1,6 @@ -function t=nows_hr -% nows_hr - Now using high resolution timer in JVM +% nows_hr - Current time using high resolution timer in JVM % % nows_hr :: unit -> real ~'time in seconds'. -t=saml.sched.HRTimer.now/1e9; +function t=nows_hr, t=saml.sched.HRTimer.now/1e9; end diff -r 289445d368a7 -r 7357e1dc2ad6 sched/rlsched.m --- a/sched/rlsched.m Wed Dec 19 22:46:05 2012 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,51 +0,0 @@ -function Timer=rlsched(a,t0,dt,n,ss) -% rlsched - Schedule list of events at regular intervals -% -% rlsched :: -% ( S ~'state' -% double ~'scheduled time', -% double ~'actual time' -% -> action double ~'execution time of current event' -% ), -% double ~'start time', -% double ~'timer period' -% natural~'iterations' -% seq S ~'sequence of states' -% -> action timer ~'timer being used'. -% -% SEMANTICS; -% -% This scheduler will drop events that are definitely late, but some events may -% execute that may turn out to be late. - - warning('off','MATLAB:TIMER:STARTDELAYPRECISION'); - - Timer=timer; - set(Timer,'ExecutionMode','fixedrate','Period',dt,'BusyMode','drop'); - set(Timer,'UserData',{t0,ss},'TimerFcn',@timercb,'TasksToExecute',n); - set(Timer,'StartDelay',max(0,t0-nows)); - start(Timer); - - function timercb(o,e) - [t0,s0]=cdeal(get(o,'UserData')); - te=datevec(e.Data.time); - if t0>=te - tt=a(head(s0),t0,e.Data.time); - else - fprintf('\n| late by %g',te-t0); - end - t1=t0+dt; - s1=next(s0); - - while ~isempty(s1) - if t1>tt - set(o,'UserData',{t1,s1}); - return; - end - fprintf('\n| skipping'); - t1=t1+dt; - s1=next(s1); - end - stop(o); - end -end diff -r 289445d368a7 -r 7357e1dc2ad6 sched/rsched.m --- a/sched/rsched.m Wed Dec 19 22:46:05 2012 +0000 +++ b/sched/rsched.m Sat Dec 22 16:17:51 2012 +0000 @@ -1,19 +1,10 @@ -function api=rsched(a,S0,dt,T0,varargin) -% rsched - Schedule regular events with state using timer +% rsched - regular state threading scheduler % % rsched :: -% ( S ~'state' -% nonneg ~'current period', -% double ~'scheduled time', -% datenum ~'actual time' -% -> action ( -% S ~'the next event', -% double ~'execution time of current event' -% ) -% ) ~'state transformer', +% timed_action({nonneg,S},{S}) ~'timed action from current period and state to state', % S ~'first state', -% double ~'timer period', -% double ~'start time', +% nonneg ~'timer period', +% time ~'start time', % options { % its :: natural/inf ~'iteration limit'; % defer :: bool/0 ~ 'if true, do not start timer'; @@ -24,37 +15,38 @@ % S ~'current state', % real ~'scheduled start time' % datenum ~'actual time' -% -> action unit)/@nop ~ 'called on timer start'; +% => void)/@nop ~ 'called on timer start'; % % onstop :: ( % S ~'current state', -% datenum ~'actual time' -% -> action unit)/@nop ~ 'called on timer stop'; +% datenum ~'actual time' % => void)/@nop ~ 'called on timer stop'; % % onfinish:: ( % S ~'final state', % datanum ~'actual time' -% -> action unit)/@nop ~ 'called if state seq finishes'; +% => void)/@nop ~ 'called if state seq finishes'; % % busy_mode :: {'queue','drop'}; % exec_mode :: {'fixedRate','fixedDelay','fixedSpacing'} % } -% -> action struct { +% => struct { % timer :: timer ~'timer being used'; -% startat :: (double -> action unit) ~'start timer at given time'; -% start :: (unit -> action unit) ~'start timer asap'; -% stop :: (unit -> action unit) ~'stop timer'; -% rewind :: (unit -> action unit) ~'rewind to first state'; -% trim :: (double -> action double)~'trim timing errors'; -% dispose :: (unit -> action unit) ~'stop timer'; -% getstate :: (unit -> action S) ~'current state'; -% setstate :: (S -> action unit) ~'set state (only when stopped)'; -% isrunning:: (unit -> action bool) ~'true if scheduler is running' +% startat :: (double => void) ~'start timer at given time'; +% start :: (void => void) ~'start timer asap'; +% stop :: (void => void) ~'stop timer'; +% rewind :: (void => void) ~'rewind to first state'; +% trim :: (double => double)~'trim timing errors'; +% dispose :: (void => void) ~'stop timer'; +% getstate :: (void => S) ~'current state'; +% setstate :: (S => void) ~'set state (only when stopped)'; +% isrunning:: (void => bool) ~'true if scheduler is running'; +% wait :: (void => void) ~'wait for sched to stop' % }. % % NB: switching on final state availability option 'final' can make % timer callbacks much slower due to requirement for copying state. +function api=rsched(schedfn,S0,dt,T0,varargin) warning('off','MATLAB:TIMER:STARTDELAYPRECISION'); warning('off','MATLAB:TIMER:RATEPRECISION'); @@ -65,7 +57,7 @@ % Implementation note: it's MUCH faster to use a local variable % as mutable state instead of using the timer getter and setter. - State={T0,S0}; + State=S0; SchedStart=T0; FinalState=[]; STARTERR=getparam(opts,'error',0); DT=dt; @@ -85,22 +77,23 @@ 'setstate', @setstate, ... 'trim', @trim, ... 'timer', @()Timer ... + 'wait', @sched_wait ); - if opts.final, api.finalstate=@()State{1}; end + if opts.final, api.finalstate=@()FinalState; end if ~opts.defer, startat(T0); end function check(msg), if isrunning(Timer), error(msg); end; end function err=trim(derr), STARTERR=STARTERR+derr; err=STARTERR; end - function s=getstate, s=State{2}; end + function s=getstate, s=State; end function setstate(s), check('Cannot set state of running scheduler'); - State{2}=s; + State=s; end function startat(t0) % !! what if timer is already running? check('Timer is already running'); - State{1}=t0; + SchedStart=t0; start_delay = t0-nows-STARTERR if start_delay<0 fprintf('\n| WARNING: start delay=%f, starting now.',start_delay); @@ -112,50 +105,38 @@ function startnow % !! what if timer is already running? check('Timer is already running'); - State{1}=nows+STARTERR; + SchedStart=nows+STARTERR; set(Timer,'StartDelay',0); start(Timer); end function stopfn(o,e), - opts.onstop(State{2},e.Data.time); - if isempty(State{2}), - opts.onfinish(State{1},e.Data.time); + opts.onstop(State,e.Data.time); + if isempty(State), + opts.onfinish(State,e.Data.time); end end function startfn(o,e) DT=get(o,'Period'); % use current period in timer callback - opts.onstart(State{2},State{1},e.Data.time); + opts.onstart(State,SchedStart,e.Data.time); end % if final state not required we can use faster assign in place function adv2(o,e) - t0=State{1}; - [State{2},tt]=a(State{2},DT,t0,e.Data.time); - % could accumulate stats about tt-t0 here - - if isempty(State{2}), - State{1}=[]; - stop(o); - else - State{1}=t0+DT; - end + [err,State]=schedfn(SchedStart,DT,State); + if isempty(State), stop(o); end + SchedStart=SchedStart+DT; end % final state required function adv(o,e) - [t0,s0]=State{:}; - [s1,tt]=a(s0,DT,t0,e.Data.time); - % could accumulate stats about tt-t0 here + [err,s1]=schedfn(SchedStart,DT,State); + if isempty(s1), FinalState=State; stop(o); end + SchedStart=SchedStart+DT; + State=s1; + end - if isempty(s1), - State{1}=s0; - State{2}=[]; - stop(o); - else - State{1}=t0+DT; - State{2}=s1; - end + function sched_wait + while Timer.isrunning(); pause(0.05); end end -end diff -r 289445d368a7 -r 7357e1dc2ad6 sched/rsched2.m --- a/sched/rsched2.m Wed Dec 19 22:46:05 2012 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,124 +0,0 @@ -function api=rsched(schedfn,S0,dt,T0,varargin) -% rsched - Schedule regular events with state using timer -% -% rsched :: -% ( S ~'state' -% nonneg ~'current period', -% double ~'scheduled time', -% datenum ~'actual time' -% -> action ( -% S ~'the next event', -% double ~'execution time of current event' -% ) -% ) ~'action to schedule regularly', -% S ~'first state', -% double ~'timer period', -% double ~'start time', -% options { -% its :: natural/inf ~'iteration limit'; -% defer :: bool/0 ~ 'if true, do not start timer'; -% error :: real/0 ~ 'estimate of timing error'; -% -% onstart :: ( -% S ~'current state', -% real ~'scheduled start time' -% datenum ~'actual time' -% -> action unit)/@nop ~ 'called on timer start'; -% -% onstop :: ( -% S ~'current state', -% datenum ~'actual time' -% -> action unit)/@nop ~ 'called on timer stop'; -% -% onfinish:: ( -% S ~'final state', -% datanum ~'actual time' -% -> action unit)/@nop ~ 'called if state seq finishes'; -% -% busy_mode :: {'queue','drop'}; -% exec_mode :: {'fixedRate','fixedDelay','fixedSpacing'} -% } -% -> action struct { -% timer :: timer ~'timer being used'; -% startat :: (double -> action unit) ~'start timer at given time'; -% start :: (unit -> action unit) ~'start timer asap'; -% stop :: (unit -> action unit) ~'stop timer'; -% rewind :: (unit -> action unit) ~'rewind to first state'; -% trim :: (double -> action double)~'trim timing errors'; -% dispose :: (unit -> action unit) ~'stop timer'; -% getstate :: (unit -> action S) ~'current state'; -% setstate :: (S -> action unit) ~'set state (only when stopped)'; -% isrunning:: (unit -> action boo) ~'true if scheduler is running' -% }. -% -% !! What about collecting final state when done? - - - warning('off','MATLAB:TIMER:STARTDELAYPRECISION'); - warning('off','MATLAB:TIMER:RATEPRECISION'); - - opts=prefs('defer',0,'its',inf, ... - 'onstart',@nop,'onstop',@nop,'onfinish',@nop, ... - 'busy_mode','queue','exec_mode','fixedRate',varargin{:}); - - % Implementation note: it's MUCH faster to use a local variable - % as mutable state instead of using the timer getter and setter. - State=S0; SchedStart=T0; - STARTERR=getparam(opts,'error',0); - - Timer=timer('ExecutionMode',opts.exec_mode,'BusyMode',opts.busy_mode, ... - 'StartFcn',@startfn,'StopFcn',@stopfn,'TimerFcn',@timercb, ... - 'Period',dt,'TasksToExecute',opts.its); - - api = struct(... - 'dispose', @()delete(Timer), ... - 'isrunning',@()isrunning(Timer), ... - 'startat', @startat, ... - 'start', @startnow, ... - 'stop', @()stop(Timer), ... - 'rewind', @()setstate(S0), ... - 'getstate', @getstate, ... - 'setstate', @setstate, ... - 'trim', @trim ... - ); - - if ~opts.defer, startat(T0); end - - function check(msg), if isrunning(Timer), error(msg); end; end - function err=trim(derr), STARTERR=STARTERR+derr; err=STARTERR; end - function s=getstate, s=State; end - function setstate(s), - check('Cannot set state of running scheduler'); - State=s; - end - - function startat(t0) % !! what if timer is already running? - check('Timer is already running'); - SchedStart=t0; - set(Timer,'StartDelay',t0-nows-STARTERR); - start(Timer); - end - - function startnow % !! what if timer is already running? - check('Timer is already running'); - SchedStart=nows+STARTERR; - set(Timer,'StartDelay',0); - start(Timer); - end - - function stopfn(o,e), - opts.onstop(State,e.Data.time); - if isempty(State), - opts.onfinish([],e.Data.time); - end - end - - function startfn(o,e) - opts.onstart(State,SchedStart,e.Data.time); - end - - function timercb(o,e) - State=schedfn(State); - if isempty(State), stop(o); end - end -end diff -r 289445d368a7 -r 7357e1dc2ad6 sched/sch_wait.m --- a/sched/sch_wait.m Wed Dec 19 22:46:05 2012 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,2 +0,0 @@ -function sch_wait(S) - while S.isrunning(); pause(0.05); end diff -r 289445d368a7 -r 7357e1dc2ad6 sched/sched.m --- a/sched/sched.m Wed Dec 19 22:46:05 2012 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,26 +0,0 @@ -function x=sched(f,dt,nout) -% sched - execute action after delay -% -% sched :: -% (unit -> action (A1,...,AN)) ~'action with N return values', -% nonneg ~'delay in seconds', -% M:natural ~'number of output arguments to collect' -% -> unit -> action (A1,...,AM). -% -% After use, timer is marked for deletion but you must call -% timer_gc to actually delete it. - - if nargin<3, nout=0; end - rc=cell(1,nout); - tim=timer('TimerFcn',@(t,e)timfn,'StopFcn',@stopfn,'StartDelay',dt); - start(tim); - x=@()collect(); - - function timfn, [rc{:}]=f(); end - function stopfn(t,e), timer_release(t); end - function varargout=collect(), - if isvalid(tim), wait(tim); end - varargout=rc; - end -end - diff -r 289445d368a7 -r 7357e1dc2ad6 sched/sched_at.m --- a/sched/sched_at.m Wed Dec 19 22:46:05 2012 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,11 +0,0 @@ -function sched_at(Timer,T,F) -% sched_at - Use one-shot timer to schedule action -% -% sched_at :: timer(off), double, (timer(on), timer_event -> action) -> action. -% -% Can only be used on a stopped timer, whereas callback executes with -% a running timer. -% sched_at - Use one-shot timer to schedule callback - -set(Timer,'TimerFcn',F,'StartDelay',quant(0.001,T-nows)); -start(Timer); diff -r 289445d368a7 -r 7357e1dc2ad6 sched/sched_rec.m --- a/sched/sched_rec.m Wed Dec 19 22:46:05 2012 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,11 +0,0 @@ -function sched_rec(t,action) - - t=t*1e9; - t1=sleeptill_hr(t); - dt=action((t1-t)*1e-9); - while ~isempty(dt) - t=t+dt*1e9; - t1=sleeptill_hr(t); - dt=action((t1-t)*1e-9); - end - diff -r 289445d368a7 -r 7357e1dc2ad6 sched/schedabs.m --- a/sched/schedabs.m Wed Dec 19 22:46:05 2012 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,11 +0,0 @@ -function schedabs(t,action) - - t=t*1e9; - t1=sleeptill_hr(t); - dt=action((t1-t)*1e-9); - while ~isempty(dt) - t=t+dt*1e9; - t1=sleeptill_hr(t); - dt=action((t1-t)*1e-9); - end - diff -r 289445d368a7 -r 7357e1dc2ad6 sched/schedabs_hr.m --- a/sched/schedabs_hr.m Wed Dec 19 22:46:05 2012 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,4 +0,0 @@ -function schedabs_hr(t,action) - - t1=sleeptill_hr(t*1e9); - action(t1*1e-9-t); diff -r 289445d368a7 -r 7357e1dc2ad6 sched/sleeptill_hr.m --- a/sched/sleeptill_hr.m Wed Dec 19 22:46:05 2012 +0000 +++ b/sched/sleeptill_hr.m Sat Dec 22 16:17:51 2012 +0000 @@ -1,1 +1,4 @@ -function sleeptill_hr(t), saml.sched.HRTimer.sleepUntil(t); +% sleeptil_hr - Sleep until absolute time +% +% sleeptill_hr :: time => double ~'how late we are'. +function dt=sleeptill_hr(t), dt=saml.sched.HRTimer.sleepUntil(t*1e9)*1e-9; diff -r 289445d368a7 -r 7357e1dc2ad6 sched/ssched.m --- a/sched/ssched.m Wed Dec 19 22:46:05 2012 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,62 +0,0 @@ -function Timer=ssched(ee) -% ssched - Schedule events using timer with state threading through event calls -% -% ssched :: -% cell { -% ( S ~'state' -% double ~'scheduled time', -% double ~'actual time' -% -> S ~'next state', -% double ~'next execution time', -% double ~'actual execution time' -% ), -% double ~'time of first event', -% S ~'initial state' -% } -% -> action timer ~'timer being used'. -% -% Semantics: -% -% Because subsequent events are dependent on the return values from previous -% events, we must call each event action even if we know it is late. It is up -% the actions themselves to decide what to do if they are late. - - persistent ERROR - ERROR=[]; - - warning('off','MATLAB:TIMER:STARTDELAYPRECISION'); - - [a,t0,s0]=cdeal(ee); - - Timer=timer; - set(Timer,'UserData',{t0,s0}); - set(Timer,'TimerFcn',@timercb,'StopFcn',@chain); - set(Timer,'StartDelay',max(0,t0-nows)); % there will be some small error here.. - start(Timer); - - function timercb(o,e) - [t0,s0]=cdeal(get(o,'UserData')); - [s1,t1,tt]=a(s0,t0,e.Data.time); - set(o,'UserData',{t1,s1}); - ERROR=vertcat(ERROR,tt-t0); - end - - function chain(o,e) - [t1,s1]=cdeal(get(o,'UserData')); - correction=mean(ERROR); - while ~isempty(t1) - tnow=nows; - delay=t1-correction-tnow; - if delay>=0, - set(o,'StartDelay',delay); - start(o); - return; - end - [s1,t1,tt]=a(s1,t1,tnow); - end - - fprintf('\n| stopping\n'); - set(o,'UserData',ERROR); - delete(o); - end -end diff -r 289445d368a7 -r 7357e1dc2ad6 sched/statuscb.m --- a/sched/statuscb.m Wed Dec 19 22:46:05 2012 +0000 +++ b/sched/statuscb.m Sat Dec 22 16:17:51 2012 +0000 @@ -1,10 +1,10 @@ -function f=statuscb(msg) % statuscb - Returns status message timer callback function handle % -% statuscb :: string -> (timer(_), timer_event -> action). +% statuscb :: string => (timer, timer_event => void). % % Eg, % set(Timer,'StartFcn',statuscb('starting')); % set(Timer,'StopFcn',statuscb('stopping')); -f=@(t,e)fprintf('%s: %s\n',mat2str(e.Data.time),msg); +function f=statuscb(msg) + f=@(t,e)fprintf('%s: %s\n',mat2str(e.Data.time),msg); diff -r 289445d368a7 -r 7357e1dc2ad6 sched/timed_action.m --- a/sched/timed_action.m Wed Dec 19 22:46:05 2012 +0000 +++ b/sched/timed_action.m Sat Dec 22 16:17:51 2012 +0000 @@ -1,9 +1,20 @@ -function a0=timed_action(x) +% timed_action - return action function that checks the time +% +% timed_action :: +% (A1, A2, ... => B1, B2, ...) ~'some action', +% -> timed_action({A1,A2,...},{B1,B2,...}). +function a0=timed_action(x,varargin) + opts=prefs('print',0,'errorfn',@(t)nows-t,varargin{:}); + if opts.print, a0=@aa_print; else a0=@aa; end + errfn=opts.errorfn; - a0=@aa; - - function [a1,tt]=aa(t0,ta), - a1=x(t0); - tt=nows; + function varargout=aa_print(t0,varargin), + varargout{1}=errfn(t0); + [varargout{2:nargout}]=x(varargin{:}); + fprintf('timed_action: error=%g\n',varargout{1}); + end + function varargout=aa(t0,varargin), + varargout{1}=errfn(t0); + [varargout{2:nargout}]=x(varargin{:}); end end diff -r 289445d368a7 -r 7357e1dc2ad6 sched/timer_gc.m --- a/sched/timer_gc.m Wed Dec 19 22:46:05 2012 +0000 +++ b/sched/timer_gc.m Sat Dec 22 16:17:51 2012 +0000 @@ -1,4 +1,4 @@ % gctimers - Deletes all timers that have been released using releasetimer % -% gctimer :: unit -> action unit. +% gctimer :: void => void. function gctimers, delete(timerfindall('Tag','defunct')); diff -r 289445d368a7 -r 7357e1dc2ad6 sched/timer_release.m --- a/sched/timer_release.m Wed Dec 19 22:46:05 2012 +0000 +++ b/sched/timer_release.m Sat Dec 22 16:17:51 2012 +0000 @@ -1,5 +1,5 @@ % releasetimer - Mark timer for garbage collection % -% release :: timer -> action unit. +% release :: timer => void. function release(t), set(t,'Tag','defunct'); diff -r 289445d368a7 -r 7357e1dc2ad6 sched/timer_wait.m --- a/sched/timer_wait.m Wed Dec 19 22:46:05 2012 +0000 +++ b/sched/timer_wait.m Sat Dec 22 16:17:51 2012 +0000 @@ -1,11 +1,11 @@ -% waitfortimer - Wait for timer to stop running +% timer_wait - Wait for timer to stop running % -% waitfortimer :: timer -> action unit. -% waitfortimer :: timer, nonneg ~'time quantum for waiting' -> action unit. +% timer_wait :: timer => void. +% timer_wait :: timer, nonneg ~'time quantum for waiting' => void. % % If no wait quantum is supplied, the default is 50ms. -function waittimer(T,dt) +function timer_wait(T,dt) if nargin<2, dt=0.05; end while isvalid(T) && strcmp(T.Running,'on')