diff --git a/app/build.gradle b/app/build.gradle index bda3c73..81c2bfd 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -25,16 +25,15 @@ android { dependencies { compile files('libs/mpandroidchartlibrary-2-0-7.jar') compile 'com.getpebble:pebblekit:3.1.0@aar' - // Unit testing dependencies testCompile 'junit:junit:4.12' // Set this dependency if you want to use Mockito testCompile 'org.mockito:mockito-core:1.10.19' // Set this dependency if you want to use Hamcrest matching testCompile 'org.hamcrest:hamcrest-library:1.1' - compile 'com.android.support:appcompat-v7:22.2.1' compile 'com.android.support:support-v4:22.2.1' + compile files('libs/JTransforms-3.1-with-dependencies.jar') } repositories { diff --git a/app/libs/JTransforms-3.1-with-dependencies.jar b/app/libs/JTransforms-3.1-with-dependencies.jar new file mode 100644 index 0000000..c6d5e1a Binary files /dev/null and b/app/libs/JTransforms-3.1-with-dependencies.jar differ diff --git a/app/src/main/assets/pebble_sd.pbw b/app/src/main/assets/pebble_sd.pbw index 101fbfa..b4679f9 100644 Binary files a/app/src/main/assets/pebble_sd.pbw and b/app/src/main/assets/pebble_sd.pbw differ diff --git a/app/src/main/java/uk/org/openseizuredetector/AccelData.java b/app/src/main/java/uk/org/openseizuredetector/AccelData.java index 33828ee..2eabed3 100644 --- a/app/src/main/java/uk/org/openseizuredetector/AccelData.java +++ b/app/src/main/java/uk/org/openseizuredetector/AccelData.java @@ -62,6 +62,22 @@ public class AccelData { return timestamp; } + public int getX() { + return x; + } + + public int getY() { + return y; + } + + public int getZ() { + return z; + } + + public int getMagnitude() { + return (int)Math.sqrt(x*x + y*y + z*z); + } + public void applyTimezone(TimeZone tz) { timestamp -= tz.getOffset(timestamp); } diff --git a/app/src/main/java/uk/org/openseizuredetector/CircularArrayList.java b/app/src/main/java/uk/org/openseizuredetector/CircularArrayList.java new file mode 100644 index 0000000..08d7d10 --- /dev/null +++ b/app/src/main/java/uk/org/openseizuredetector/CircularArrayList.java @@ -0,0 +1,107 @@ +package uk.org.openseizuredetector; + +import java.util.AbstractList; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.RandomAccess; + +/** + * Created by graham on 28/06/16. + */ +public class CircularArrayList + extends AbstractList implements RandomAccess { + /** + * If you use this code, please consider notifying isak at du-preez dot com + * with a brief description of your application. + * + * This is free and unencumbered software released into the public domain. + * Anyone is free to copy, modify, publish, use, compile, sell, or + * distribute this software, either in source code form or as a compiled + * binary, for any purpose, commercial or non-commercial, and by any + * means. + */ + + private final int n; // buffer length + private final List buf; // a List implementing RandomAccess + private int head = 0; + private int tail = 0; + + public CircularArrayList(int capacity) { + n = capacity + 1; + buf = new ArrayList(Collections.nCopies(n, (E) null)); + } + + public int capacity() { + return n - 1; + } + + private int wrapIndex(int i) { + int m = i % n; + if (m < 0) { // java modulus can be negative + m += n; + } + return m; + } + + // This method is O(n) but will never be called if the + // CircularArrayList is used in its typical/intended role. + private void shiftBlock(int startIndex, int endIndex) { + assert (endIndex > startIndex); + for (int i = endIndex - 1; i >= startIndex; i--) { + set(i + 1, get(i)); + } + } + + @Override + public int size() { + return tail - head + (tail < head ? n : 0); + } + + @Override + public E get(int i) { + if (i < 0 || i >= size()) { + throw new IndexOutOfBoundsException(); + } + return buf.get(wrapIndex(head + i)); + } + + @Override + public E set(int i, E e) { + if (i < 0 || i >= size()) { + throw new IndexOutOfBoundsException(); + } + return buf.set(wrapIndex(head + i), e); + } + + @Override + public void add(int i, E e) { + int s = size(); + if (s == n - 1) { + throw new IllegalStateException("Cannot add element." + + " CircularArrayList is filled to capacity."); + } + if (i < 0 || i > s) { + throw new IndexOutOfBoundsException(); + } + tail = wrapIndex(tail + 1); + if (i < s) { + shiftBlock(i, s); + } + set(i, e); + } + + @Override + public E remove(int i) { + int s = size(); + if (i < 0 || i >= s) { + throw new IndexOutOfBoundsException(); + } + E e = get(i); + if (i > 0) { + shiftBlock(0, i); + } + head = wrapIndex(head + 1); + return e; + } +} diff --git a/app/src/main/java/uk/org/openseizuredetector/OsdUtil.java b/app/src/main/java/uk/org/openseizuredetector/OsdUtil.java index e7b5d6e..77e46ea 100644 --- a/app/src/main/java/uk/org/openseizuredetector/OsdUtil.java +++ b/app/src/main/java/uk/org/openseizuredetector/OsdUtil.java @@ -42,7 +42,12 @@ import org.apache.http.conn.util.InetAddressUtils; import java.net.InetAddress; import java.net.NetworkInterface; +import java.util.AbstractList; +import java.util.ArrayList; +import java.util.Collections; import java.util.Enumeration; +import java.util.List; +import java.util.RandomAccess; /** * OsdUtil - OpenSeizureDetector Utilities diff --git a/app/src/main/java/uk/org/openseizuredetector/SdDataSourcePebble.java b/app/src/main/java/uk/org/openseizuredetector/SdDataSourcePebble.java index ab726a1..0a9d53b 100644 --- a/app/src/main/java/uk/org/openseizuredetector/SdDataSourcePebble.java +++ b/app/src/main/java/uk/org/openseizuredetector/SdDataSourcePebble.java @@ -40,6 +40,7 @@ import com.getpebble.android.kit.util.PebbleDictionary; import org.json.JSONException; import org.json.JSONObject; +import org.jtransforms.fft.DoubleFFT_1D; import java.io.File; import java.io.FileOutputStream; @@ -118,6 +119,12 @@ public class SdDataSourcePebble extends SdDataSource { private int DATA_TYPE_SETTINGS = 2; // Settings private int DATA_TYPE_SPEC = 3; // FFT Spectrum (or part of a spectrum) private int DATA_TYPE_RAW = 4; // raw accelerometer data. + + // Values for SD_MODE + private int SD_MODE_FFT = 0; // The original OpenSeizureDetector mode (FFT based) + private int SD_MODE_RAW = 1; // Send raw, unprocessed data to the phone. + private int SD_MODE_FILTER = 2; // Use digital filter rather than FFT. + private short mDataUpdatePeriod; private short mMutePeriod; private short mManAlarmPeriod; @@ -134,6 +141,11 @@ public class SdDataSourcePebble extends SdDataSource { private short mFallThreshMax; private short mFallWindow; + // raw data storage for SD_MODE_RAW + private int MAX_RAW_DATA = 500; + private double[] rawData = new double[MAX_RAW_DATA]; + private int nRawData = 0; + public SdDataSourcePebble(Context context, SdDataReceiver sdDataReceiver) { super(context, sdDataReceiver); mName = "Pebble"; @@ -390,12 +402,19 @@ public class SdDataSourcePebble extends SdDataSource { if (data.getUnsignedIntegerAsLong(KEY_DATA_TYPE) == DATA_TYPE_RAW) { Log.v(TAG, "DATA_TYPE = Raw"); - long numSamples = data.getUnsignedIntegerAsLong(KEY_NUM_RAW_DATA); + long numSamples; + numSamples = data.getUnsignedIntegerAsLong(KEY_NUM_RAW_DATA); Log.v(TAG, "numSamples = " + numSamples); - byte[] rawData = data.getBytes(KEY_RAW_DATA); - for (AccelData reading : AccelData.fromDataArray(rawData)) { - Log.i(TAG, "reading ts " + reading.getTimestamp()); + byte[] rawDataBytes = data.getBytes(KEY_RAW_DATA); + for (AccelData reading : AccelData.fromDataArray(rawDataBytes)) { + if (nRawData < MAX_RAW_DATA) { + rawData[nRawData] = reading.getMagnitude(); + nRawData++; + } else { + Log.i(TAG, "WARNING - rawData Buffer Full"); + } } + } } }; @@ -615,6 +634,19 @@ public class SdDataSourcePebble extends SdDataSource { getPebbleSdSettings(); getPebbleData(); } + + if (mPebbleSdMode == SD_MODE_RAW) { + analyseRawData(); + } + } + + + private void analyseRawData() { + Log.v(TAG,"analyserawData()"); + DoubleFFT_1D fft = new DoubleFFT_1D(MAX_RAW_DATA); + fft.realForward(rawData); + // FIXME - rawData should really be a circular buffer. + nRawData = 0; } /**