/** * Funf: Open Sensing Framework * Copyright (C) 2010-2011 Nadav Aharony, Wei Pan, Alex Pentland. * Acknowledgments: Alan Gardner * Contact: nadav@media.mit.edu * * This file is part of Funf. * * Funf is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation, either version 3 of * the License, or (at your option) any later version. * * Funf is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with Funf. If not, see <http://www.gnu.org/licenses/>. */ package edu.mit.media.funf.probe.builtin; import android.content.Intent; import android.os.Bundle; import android.os.Handler; import android.util.Log; import edu.mit.media.funf.Utils; import edu.mit.media.funf.probe.Probe; import edu.mit.media.funf.probe.ProbeScheduler; import edu.mit.media.funf.probe.builtin.ProbeKeys.ActivityKeys; public class ActivityProbe extends Probe implements ActivityKeys { private static final long DEFAULT_DURATION = 15L; private static final long DEFAULT_PERIOD = 120L; private static final long INTERVAL = 1L; private static final String DELEGATE_PROBE_NAME = AccelerometerSensorProbe.class.getName(); //private long duration = 0L; private Handler handler; private long startTime; private int intervalCount; private int lowActivityIntervalCount; private int highActivityIntervalCount; private ActivityCounter activityCounter; @Override public Parameter[] getAvailableParameters() { return new Parameter[] { new Parameter(Parameter.Builtin.DURATION, DEFAULT_DURATION), new Parameter(Parameter.Builtin.PERIOD, DEFAULT_PERIOD), new Parameter(Parameter.Builtin.START, 0L), new Parameter(Parameter.Builtin.END, 0L) }; } @Override public String[] getRequiredFeatures() { return new String[]{ "android.hardware.sensor.accelerometer" }; } @Override public String[] getRequiredPermissions() { return new String[]{}; } @Override protected String getDisplayName() { return "Activity Level Probe"; } @Override protected void onHandleCustomIntent(Intent intent) { if (Probe.ACTION_DATA.equals(intent.getAction()) && DELEGATE_PROBE_NAME.equals(intent.getStringExtra(PROBE))) { if (handler == null) { handler = new Handler(); // Make sure handler is created on message thread } if (activityCounter == null) { activityCounter = new ActivityCounter(); } if (!activityCounter.isRunning()) { // Ensure probe stays alive run(); } activityCounter.handleAccelerometerData(intent.getExtras()); } } private class ActivityCounter { private long intervalStartTime; private float varianceSum; private float avg; private float sum; private int count; private Runnable disableRunnable; public boolean isRunning() { return disableRunnable != null; } private void reset(long timestamp) { Log.d(TAG, "RESET:" + timestamp); // If more than an INTERVAL away, start a new scan startTime = intervalStartTime = timestamp; varianceSum = avg = sum = count = 0; intervalCount = 1; lowActivityIntervalCount = 0; highActivityIntervalCount = 0; } private void resetDisableTimer() { if (disableRunnable == null) { Log.d(TAG, "CREATING SEND DATA RUNNABLE"); disableRunnable = new Runnable() { public void run() { Log.d(TAG, "SENDING DATA"); sendProbeData(); disableRunnable = null; disable(); } }; } handler.removeCallbacks(disableRunnable); handler.postDelayed(disableRunnable, Utils.secondsToMillis(2 * INTERVAL)); } private void intervalReset() { Log.d(TAG, "INTERVAL RESET"); // Calculate activity and reset intervalCount++; if (varianceSum >= 10.0f) { highActivityIntervalCount++; } else if (varianceSum < 10.0f && varianceSum > 3.0f) { lowActivityIntervalCount++; } intervalStartTime += INTERVAL; // Ensure 1 second intervals varianceSum = avg = sum = count = 0; } private void update(float x, float y, float z) { //Log.d(TAG, "UPDATE:(" + x + "," + y + "," + z + ")"); // Iteratively calculate variance sum count++; float magnitude = (float)Math.sqrt(x*x + y*y + z*z); float newAvg = (count - 1)*avg/count + magnitude/count; float deltaAvg = newAvg - avg; varianceSum += (magnitude - newAvg) * (magnitude - newAvg) - 2*(sum - (count-1)*avg) + (count - 1) *(deltaAvg * deltaAvg); sum += magnitude; avg = newAvg; //Log.d(TAG, "UPDATED VALUES:(count, varianceSum, sum, avg) " + count + ", " + varianceSum+ ", " + sum+ ", " + avg); } public void handleAccelerometerData(Bundle data) { long timestamp = data.getLong(TIMESTAMP, 0L); Log.d(TAG, "Starttime: " + startTime + " IntervalStartTime: " + intervalStartTime); Log.d(TAG, "RECEIVED:" + timestamp); if (!this.isRunning() //|| timestamp > startTime + duration // we don't know how to determine duration anymore, since onRun is never called || timestamp >= intervalStartTime + 2 * INTERVAL) { Log.d(TAG, "RESET:" + timestamp); reset(timestamp); } else if (timestamp >= intervalStartTime + INTERVAL) { Log.d(TAG, "Interval Reset:" + timestamp); intervalReset(); } resetDisableTimer(); long[] eventTimestamp = data.getLongArray("EVENT_TIMESTAMP"); //int[] accuracy = data.getIntArray("ACCURACY"); float[] x = data.getFloatArray("X"); float[] y = data.getFloatArray("Y"); float[] z = data.getFloatArray("Z"); for (int i=0; i<eventTimestamp.length; i++) { update(x[i], y[i], z[i]); } } } @Override protected void onEnable() { // Nothing } @Override protected void onDisable() { // Nothing } @Override public void onRun(Bundle params) { // Nothing } @Override public void onStop() { // Nothing } @Override public void sendProbeData() { Bundle data = new Bundle(); data.putInt(TOTAL_INTERVALS, intervalCount); data.putInt(LOW_ACTIVITY_INTERVALS, lowActivityIntervalCount); data.putInt(HIGH_ACTIVITY_INTERVALS, highActivityIntervalCount); Log.d(TAG, "(" + lowActivityIntervalCount + " Low Active / " + intervalCount + "Total)"); Log.d(TAG, "(" + highActivityIntervalCount + " High Active / " + intervalCount + "Total)"); sendProbeData(startTime, data); // Timestamp already in seconds } @Override protected ProbeScheduler getScheduler() { return new DelegateProbeScheduler(AccelerometerSensorProbe.class); } @Override public boolean isEnabled() { return activityCounter != null && activityCounter.isRunning(); } @Override public boolean isRunning() { return activityCounter != null && activityCounter.isRunning(); } }