package org.spin.gaitlib;
import android.util.Log;
import org.spin.gaitlib.cadence.CadenceDetector;
import org.spin.gaitlib.cadence.DefaultCadenceDetector;
import org.spin.gaitlib.core.GaitData;
import org.spin.gaitlib.core.IGaitUpdateListener;
import org.spin.gaitlib.core.ILoggable;
import org.spin.gaitlib.filter.FilterNotSetException;
import org.spin.gaitlib.filter.IFilter;
import org.spin.gaitlib.gait.DefaultGaitClassifier;
import org.spin.gaitlib.gait.GaitClassifier;
import org.spin.gaitlib.gait.IClassifierModelLoadingListener;
import org.spin.gaitlib.sensor.SignalListener;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.Timer;
import java.util.TimerTask;
/**
* GaitAnalysis is the main class for using GaitLib. GaitUpdateListeners can be registered here to
* receive notification when new information is computed. Sampling window size and sampling interval
* can be set when starting gait analysis.
*
* @author Mike
*/
public class GaitAnalysis {
private static final String TAG = "GaitAnalysis";
/**
* Default size of the window used for sensor data processing, measured in milliseconds.
*/
public static final int DEFAULT_WINDOW_SIZE_MS = 4000;
/**
* Default interval between instances of data processing to update cadence and gait.
*/
public static final int DEFAULT_SAMPLING_INTERVAL_MS = 1000;
private final CadenceDetector cadenceDetector;
private final GaitClassifier gaitClassifier;
private final SignalListener signalListener = new SignalListener();
private final Set<IGaitUpdateListener> gaitUpdateListeners = new HashSet<IGaitUpdateListener>();
private final Set<ILoggable> loggables = new HashSet<ILoggable>();
private boolean isGaitAnalysisRunning = false;
private boolean isLoggingEnabled = false;
private Timer mTimer;
/**
* Set up GaitAnalysis with the default Cadence Detector, the default Gait Classifier using the
* default model file.
*/
public GaitAnalysis() {
this(getDefaultCadenceDetector(), getDefaultGaitClassifier());
}
/**
* Set up GaitAnalysis with defined Cadence Detector and Gait Classifier.
*
* @param cadenceDetector the CadenceDetector to use; <code>null</code> if not to detect
* cadence.
* @param gaitClassifier the GaitClassifier to use; <code>null</code> if not to classify gait.
*/
public GaitAnalysis(CadenceDetector cadenceDetector, GaitClassifier gaitClassifier) {
this.cadenceDetector = cadenceDetector;
this.gaitClassifier = gaitClassifier;
if (cadenceDetector != null) {
cadenceDetector.setSignalListener(signalListener);
loggables.add(cadenceDetector);
}
if (gaitClassifier != null) {
gaitClassifier.setSignalListener(signalListener);
loggables.add(gaitClassifier);
}
loggables.add(signalListener);
}
public static CadenceDetector getDefaultCadenceDetector() {
return new DefaultCadenceDetector();
}
public static GaitClassifier getDefaultGaitClassifier() {
return new DefaultGaitClassifier();
}
/**
* @param modelFileLocation
* @param modelXMLFileLocation
* @return a new instance of the default gait classifier with a defined model.
*/
public static GaitClassifier getDefaultGaitClassifier(String modelFileLocation,
String modelXMLFileLocation) {
return new DefaultGaitClassifier(modelFileLocation, modelXMLFileLocation);
}
/**
* @param filtered whether to apply defined filter for measuring cadence
* @return the current cadence value
* @throws FilterNotSetException if trying to get filtered result without setting a filter first
* by calling {@link #setCadenceFilter(IFilter)}.
*/
public float getCadence(boolean filtered) throws FilterNotSetException {
return cadenceDetector.getCadence(filtered);
}
/**
* @param filtered whether to apply defined filter to cadence value during calculation.
* @return the current stride length
* @throws FilterNotSetException if trying to get filtered result without setting a filter first
* by calling {@link #setCadenceFilter(IFilter)}.
*/
public float getStrideLength(boolean filtered) throws FilterNotSetException {
return cadenceDetector.getStrideLength(filtered);
}
/**
* @return the speed at which the user is moving
*/
public float getSpeed() {
return cadenceDetector.getSpeed();
}
/**
* @return the confidence level of the latest cadence calculation
*/
public float getCadenceConfidence() {
return cadenceDetector.getCadenceConfidence();
}
/**
* @return the latest result of gait classification
*/
public String getCurrentGait() {
return gaitClassifier.getCurrentGait();
}
/**
* @return a list of all gaits among which to classify
*/
public List<String> getDefinedGaits() {
return gaitClassifier.getAllGaits();
}
public SignalListener getSignalListener() {
return signalListener;
}
/**
* @param filter the filter to be applied to cadence measurements
*/
public void setCadenceFilter(IFilter filter) {
cadenceDetector.setFilter(filter);
}
public boolean shouldDetectCadence() {
return cadenceDetector != null;
}
public boolean shouldClassifyGait() {
return gaitClassifier != null && gaitClassifier.isModelLoaded();
}
/**
* Register a GaitUpdateListener to receive gait update events.
*
* @param listener implements <code>IGaitUpdateListener</code>
* @return <code>true</code> if the listener is added successfully; <code>false</code>
* otherwise.
*/
public boolean registerGaitUpdateListener(IGaitUpdateListener listener) {
return gaitUpdateListeners.add(listener);
}
/**
* Remove a GaitUpdateListener to stop receiving gait update events.
*
* @param listener the listener to be removed
* @return <code>true</code> if the listener is removed successfully; <code>false</code>
* otherwise.
*/
public boolean removeGaitUpdateListener(IGaitUpdateListener listener) {
return gaitUpdateListeners.remove(listener);
}
private void notifyGaitUpdateListeners() {
GaitData data = new GaitData(cadenceDetector.getCurrentCadenceState(),
gaitClassifier.getCurrentGait(), cadenceDetector.getCurrentCadenceState()
.getTimestamp());
for (IGaitUpdateListener listener : gaitUpdateListeners) {
listener.onGaitUpdated(data);
}
}
/**
* Start gait analysis with default sampling window size and default sampling interval.
*/
public void startGaitAnalysis() {
startGaitAnalysis(DEFAULT_WINDOW_SIZE_MS, DEFAULT_SAMPLING_INTERVAL_MS);
}
/**
* Start gait analysis with user defined sampling window size and sampling interval.
*
* @param windowSize the length of time in which data is used for gait analysis, measured in
* milliseconds.
* @param samplingInterval the length of time between consecutive updates, measured in
* milliseconds.
*/
public void startGaitAnalysis(int windowSize, int samplingInterval) {
signalListener.setWindowSize(windowSize);
mTimer = new Timer();
mTimer.schedule(new TimerTask() {
@Override
public void run() {
boolean shouldClassifyGait = shouldClassifyGait();
boolean shouldDetectCadence = shouldDetectCadence();
if (shouldDetectCadence) {
cadenceDetector.updateCadenceState();
}
if (shouldClassifyGait) {
try {
gaitClassifier.classifyGait();
}
catch (Exception e) {
Log.v(TAG, e.toString());
}
}
if (shouldClassifyGait || shouldDetectCadence) {
notifyGaitUpdateListeners();
}
}
}, windowSize, samplingInterval);
setGaitAnalysisRunning(true);
}
/**
* Stop gait analysis
*/
public void stopGaitAnalysis() {
if (isGaitAnalysisRunning()) {
mTimer.cancel();
mTimer.purge();
setGaitAnalysisRunning(false);
setLoggingEnabled(false);
}
}
/**
* Enable or disable logging for GaitLib.
*
* @param enabled <code>true</code> to enable, <code>false</code> to disable.
*/
public void setLoggingEnabled(boolean enabled) {
for (ILoggable loggable : loggables) {
if (loggable != null) {
loggable.setLoggingEnabled(enabled);
}
}
isLoggingEnabled = enabled;
}
/**
* @return <code>true</code> if user has enabled logging, <code>false</code> otherwise.
*/
public boolean isLoggingEnabled() {
return isLoggingEnabled;
}
/**
* @return <code>true</code> if all logging service is working properly, <code>false</code>
* otherwise.
*/
public boolean isLogging() {
boolean isLogging = true;
for (ILoggable loggable : loggables) {
isLogging &= loggable.isLogging();
}
return isLogging;
}
public boolean isGaitAnalysisRunning() {
return isGaitAnalysisRunning;
}
/**
* Set a flag to determine whether gait analysis is running at the moment.
*
* @param isGaitAnalysisRunning
*/
private void setGaitAnalysisRunning(boolean isGaitAnalysisRunning) {
this.isGaitAnalysisRunning = isGaitAnalysisRunning;
}
/**
* Register listener to receive updates during the gait classifier model loading process,
* including start and end, success or fail.
*
* @param listener
*/
public void addGaitClassifierModelLoadingListener(IClassifierModelLoadingListener listener) {
gaitClassifier.addModelLoadingListener(listener);
}
public void removeClassifierModelLoadingListener(IClassifierModelLoadingListener listener) {
gaitClassifier.removeModelLoadingListener(listener);
}
}