Step detection XML
The WorkDag contains the description of the dataflow. Its immediate sub-elements are "mills," logical steps in the computation. The DAG may specify many inputs (sensor mills) but has only one output mill, specified by the WorkDag attribute resultCollectionName. <xml> <?xml version="1.0" standalone="no"?>
<WorkDag resultCollectionName="features"
maxCheckupPeriod="1000000" >
</xml> We instantiate the accelerometer sensor mill. All sensor mills use only the UPDATE_UNCONDITIONAL update policy. The sample period is given in microseconds. Note that this sample period is only a cross-check; it is ultimately determined by msb-server from /msp/msb.ini. 1953 μs corresponds to 512 Hz. <xml>
<Sensor name="accel" updatePolicy="UPDATE_UNCONDITIONAL" sampPeriod="1953" />
</xml> The accelerometer sensor mill is a MillMo with three outputs per sample—the x- y- and z-components of a 3-dimensional accelerometer reading. The first step of the algorithm takes the 1-dimensional magnitude of each reading. UPDATE_BY_SRC_PIGGYBACK causes a mill to be updated whenever its source mill is. <xml>
<ShortMag name="accelmag" srcName="accel" updatePolicy="UPDATE_BY_SRC_PIGGYBACK" />
</xml> An FFT is applied over a sliding window to operate on the signal in the frequency domain. Together, UPDATE_BY_NUM_NEW_SRC_SAMP and numSrcSampPerUpdate="1024" specify that accelmag_fft is updated once for every 1024 samples of input. (At every update, it produces one output record.) However, numSrcSampRqd="2048" indicates to accelmag_fft that the FFT is to be performed over the last 2048 samples. Thus its output windows overlap by half. This is required later for smoothing out the inverse FFT after a dynamic filter is applied.
This mill is sensitive to the sample rate. For example, to cut the sample rate in half from 512 Hz to 256 Hz and have this mill operate over the same window length (in time), numSrcSampPerUpdate and numSrcSampRqd would also need to be cut in half. <xml>
<Fft16 name="accelmag_fft" srcName="accelmag" updatePolicy="UPDATE_BY_NUM_NEW_SRC_SAMP" numSrcSampPerUpdate="1024" numSrcSampRqd="2048" />
</xml> The energy threshold is a simple mill which zeros its output if the energy of its input is not above the given threshold. Since the energy of a discrete-time function is not time-normalized, this mill too is sensitive to the sample rate. If the sample rate were halved, so should threshold be.
The purpose of this mill is to avoid detecting steps when very little is detected on the accelerometer. However, to keep the mean of the signal steady, the DC is still passed through. This may be a mistake—it means variations at frequencies lower than the window size still get through, raising the need for still another mechanism to suppress detection when idle (peaks_max_sane). <xml>
<EnergyThreshold name="accelmag_fft_thresholded" srcName="accelmag_fft" updatePolicy="UPDATE_BY_SRC_PIGGYBACK" threshold="250000" />
</xml> accelmag_fft_filtered operates on the signal in the frequency domain based on the relative energy content of the bins. I'm not really sure how threshold will scale with changes to the sample rate. Note that as with accelmag_fft_thresholded, accelmag_fft_filtered is updated once with every update of its source. <xml>
<EnergyFilter name="accelmag_fft_filtered" srcName="accelmag_fft_thresholded" updatePolicy="UPDATE_BY_SRC_PIGGYBACK" threshold=".16" />
</xml> accelmag_smooth returns the signal to the time domain. The attribute overlap="1" indicates that it expects each window to be twice the size of the number of outputs; that is, it is relying on accelmag_fft's 1:2 ratio of numSrcSampPerUpdate to numSrcSampRqd. Fft16I does something no other mill in LIRA has done yet: output more than one sample per update. So while the mill itself is updated here once every 2 seconds, at each update it produces 1024 samples of output. This means that if a downstream mill wants to see every sample, it must request to be updated for every sample, rather than by piggyback. <xml>
<Fft16I name="accelmag_smooth" srcName="accelmag_fft_filtered" updatePolicy="UPDATE_BY_SRC_PIGGYBACK" overlap="1" />
</xml> The integer FIR filter simply takes the derivative of the signal. It comes directly from Ying et al's "Automatic Step Detection in the Accelerometer Signal." <xml>
<IntegerFirFilter name="deriv" srcName="accelmag_smooth" updatePolicy="UPDATE_BY_NUM_NEW_SRC_SAMP" numSrcSampPerUpdate="1" > 2 1 0 -1 -2 </IntegerFirFilter>
</xml> The zero crossing mill flags output records where the signal crosses zero in the direction specified by edge. <xml>
<ZeroCross name="peaks_max" srcName="deriv" updatePolicy="UPDATE_BY_SRC_PIGGYBACK" edge="falling" />
</xml> peaks_max_sane is used to suppress very low-frequency variation, the final component in stopping idle detection. It has no effect when a signal is being detected at at least 1 Hz. (It is sensitive to the sample rate.) <xml>
<TimeDiffLim name="peaks_max_sane" srcName="peaks_max" updatePolicy="UPDATE_BY_SRC_PIGGYBACK" minSamps="16" maxSamps="512" />
</xml> This configuration computes a couple simple features. steps_05s is the number of detected steps in the last 5 seconds. The window length is sensitive to the sample rate. stridelength computes the mean and standard deviation of the last windowLength=16 strides. It has some simple logic that handles large gaps which, again, is sensitive to the sample rate. <xml>
<WinSum name="steps_05s" srcName="peaks_max_sane" updatePolicy="UPDATE_BY_NUM_NEW_SRC_SAMP" numSrcSampPerUpdate="128" windowLength="2560" />
<TimeDiffShort name="stridelength" srcName="peaks_max_sane" updatePolicy="UPDATE_BY_SRC_PIGGYBACK" windowLength="16" deltaMax="512" />
<SplitShort name="stridelength_mean" srcName="stridelength" updatePolicy="UPDATE_BY_SRC_PIGGYBACK" componentName="mean" />
<SplitShort name="stridelength_stddev" srcName="stridelength" updatePolicy="UPDATE_BY_SRC_PIGGYBACK" componentName="stddev" />
<Collection name="debug" updatePolicy="UPDATE_BY_PERIOD" sampPeriod="1953" > <component name="accelmag" /> <component name="accelmag_smooth" /> <component name="deriv" /> <component name="peaks_max_sane" /> </Collection>
<Collection name="features" updatePolicy="UPDATE_BY_PERIOD" sampPeriod="250000" > <component name="steps_05s" /> <component name="stridelength_mean" /> <component name="stridelength_stddev" /> </Collection>
</WorkDag> </xml>