changeset 2:7357e1dc2ad6

Simplified scheduler library with new schedule representation.
author samer
date Sat, 22 Dec 2012 16:17:51 +0000
parents 289445d368a7
children 3f77126f7b5f
files arrows/aica.m arrows/audio_bench1.m arrows/stats/aica.m audio/playaudio_async.m sched/HRTimer.java sched/README sched/isrunning.m sched/iterate_sched.m sched/lsched.m sched/msched.m sched/now_ns_hr.m sched/nows.m sched/nows_hr.m sched/rlsched.m sched/rsched.m sched/rsched2.m sched/sch_wait.m sched/sched.m sched/sched_at.m sched/sched_rec.m sched/schedabs.m sched/schedabs_hr.m sched/sleeptill_hr.m sched/ssched.m sched/statuscb.m sched/timed_action.m sched/timer_gc.m sched/timer_release.m sched/timer_wait.m
diffstat 29 files changed, 165 insertions(+), 597 deletions(-) [+]
line wrap: on
line diff
--- 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
--- 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'.
 %
--- /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
--- 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
--- 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; }
--- 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
+
--- 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');
--- 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
-
--- 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
--- 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
--- 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
 
--- 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
--- 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
 
--- 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
--- 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
--- 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
--- 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
--- 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
-	
--- 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);
--- 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
-
--- 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
-
--- 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);
--- 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;
--- 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
--- 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);
--- 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
--- 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'));
--- 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');
 	
--- 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')