added gitingore

This commit is contained in:
toni
2017-12-19 20:41:29 +01:00
parent e8dbaec6c4
commit 70cf17c479
12 changed files with 332 additions and 223 deletions

9
android/ConductorsPhone/.gitignore vendored Normal file
View File

@@ -0,0 +1,9 @@
*.iml
.gradle
/local.properties
/.idea/workspace.xml
/.idea/libraries
.DS_Store
/build
/captures
.externalNativeBuild

View File

@@ -28,12 +28,16 @@ android {
} }
} }
compileOptions {
targetCompatibility 1.8
sourceCompatibility 1.8
}
} }
dependencies { dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar']) implementation fileTree(dir: 'libs', include: ['*.jar'])
implementation 'com.google.android.support:wearable:2.1.0' implementation 'com.google.android.support:wearable:2.1.0'
implementation 'com.google.android.gms:play-services-wearable:11.6.0' implementation 'com.google.android.gms:play-services-wearable:11.8.0'
implementation 'com.android.support:percent:26.1.0' implementation 'com.android.support:percent:26.1.0'
implementation 'com.android.support:animated-vector-drawable:26.1.0' implementation 'com.android.support:animated-vector-drawable:26.1.0'
implementation 'com.android.support:appcompat-v7:26.1.0' implementation 'com.android.support:appcompat-v7:26.1.0'

View File

@@ -19,10 +19,13 @@
<!-- <!--
Set to true if your app is Standalone, that is, it does not require the handheld Set to true if your app is Standalone, that is, it does not require the handheld
app to run. app to run.
-->
<meta-data <meta-data
android:name="com.google.android.wearable.standalone" android:name="com.google.android.wearable.standalone"
android:value="true" /> android:value="true" />
-->
<meta-data android:name="com.google.android.gms.version"
android:value="@integer/google_play_services_version" />
<activity <activity
android:name=".MainActivity" android:name=".MainActivity"

View File

@@ -399,7 +399,7 @@ public class Croller extends View {
@Override @Override
public boolean onTouchEvent(MotionEvent e) { public boolean onTouchEvent(MotionEvent e) {
float distancePointToMiddle = Utils.getDistance(e.getX(), e.getY(), midx, midy); double distancePointToMiddle = Utils.getDistance(e.getX(), e.getY(), midx, midy);
if ((distancePointToMiddle > touchCircleRadiusMax || distancePointToMiddle < touchCircleRadiusMin)) { if ((distancePointToMiddle > touchCircleRadiusMax || distancePointToMiddle < touchCircleRadiusMin)) {
if (startEventSent && mCrollerChangeListener != null) { if (startEventSent && mCrollerChangeListener != null) {
mCrollerChangeListener.onStopTrackingTouch(this); mCrollerChangeListener.onStopTrackingTouch(this);

View File

@@ -9,6 +9,9 @@ import android.os.Handler;
import java.util.List; import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList; import java.util.concurrent.CopyOnWriteArrayList;
import de.tonifetzer.conductorswatch.bpmEstimation.AccelerometerData;
import de.tonifetzer.conductorswatch.bpmEstimation.AccelerometerWindowBuffer;
import de.tonifetzer.conductorswatch.bpmEstimation.BpmEstimator;
import de.tonifetzer.conductorswatch.utilities.Utils; import de.tonifetzer.conductorswatch.utilities.Utils;
/** /**
@@ -21,7 +24,8 @@ public class Estimator implements SensorEventListener {
private SensorManager mSensorManager; private SensorManager mSensorManager;
private Sensor mAccelerometer; private Sensor mAccelerometer;
private Context mContext; private Context mContext;
private Utils.AccelerometerWindowBuffer mAccelerometerWindowBuffer; private AccelerometerWindowBuffer mAccelerometerWindowBuffer;
private BpmEstimator mBpmEstimator;
private Handler mHandler; private Handler mHandler;
private int mUpdaterate_ms = 100; private int mUpdaterate_ms = 100;
@@ -34,13 +38,10 @@ public class Estimator implements SensorEventListener {
if(mAccelerometerWindowBuffer.isNextWindowReady()){ if(mAccelerometerWindowBuffer.isNextWindowReady()){
//TODO: calculate average samplerate using the window - interpolation of sensordata would be better i think double bpm = mBpmEstimator.estimate();
//int randomNum = ThreadLocalRandom.current().nextInt(0, 240 + 1);
long diff = mAccelerometerWindowBuffer.getYongest().ts - mAccelerometerWindowBuffer.getOldest().ts;
for (OnBpmEstimatorListener listener:listeners) { for (OnBpmEstimatorListener listener:listeners) {
listener.onNewDataAvailable((float) diff); listener.onNewDataAvailable(bpm);
} }
} }
@@ -61,7 +62,8 @@ public class Estimator implements SensorEventListener {
mAccelerometer = mSensorManager.getDefaultSensor(Sensor.TYPE_LINEAR_ACCELERATION); mAccelerometer = mSensorManager.getDefaultSensor(Sensor.TYPE_LINEAR_ACCELERATION);
mSensorManager.registerListener(this, mAccelerometer, SensorManager.SENSOR_DELAY_FASTEST); mSensorManager.registerListener(this, mAccelerometer, SensorManager.SENSOR_DELAY_FASTEST);
mAccelerometerWindowBuffer = new Utils.AccelerometerWindowBuffer(4096, 256); mAccelerometerWindowBuffer = new AccelerometerWindowBuffer(1024, 256);
mBpmEstimator = new BpmEstimator(mAccelerometerWindowBuffer, 0, 5000);
mHandler = new Handler(); mHandler = new Handler();
mHandler.post(mProcessSensors); //start runnable mHandler.post(mProcessSensors); //start runnable
@@ -81,7 +83,7 @@ public class Estimator implements SensorEventListener {
//ca 200hz, every 5 to 6 ms we have an update //ca 200hz, every 5 to 6 ms we have an update
if (se.sensor.getType() == Sensor.TYPE_LINEAR_ACCELERATION) { if (se.sensor.getType() == Sensor.TYPE_LINEAR_ACCELERATION) {
mAccelerometerWindowBuffer.add(new Utils.AccelerometerData(System.currentTimeMillis(), se.values[0], se.values[1], se.values[2])); mAccelerometerWindowBuffer.add(new AccelerometerData(System.currentTimeMillis(), se.values[0], se.values[1], se.values[2]));
} }
// mSensorUpdateFlag = false; // mSensorUpdateFlag = false;
@@ -98,7 +100,7 @@ public class Estimator implements SensorEventListener {
* Interface for callback calculated bpm * Interface for callback calculated bpm
*/ */
public interface OnBpmEstimatorListener { public interface OnBpmEstimatorListener {
void onNewDataAvailable(float bpm); void onNewDataAvailable(double bpm);
} }
private List<OnBpmEstimatorListener> listeners = new CopyOnWriteArrayList<OnBpmEstimatorListener>(); private List<OnBpmEstimatorListener> listeners = new CopyOnWriteArrayList<OnBpmEstimatorListener>();

View File

@@ -7,18 +7,31 @@ import android.graphics.Point;
import android.os.Bundle; import android.os.Bundle;
import android.os.Handler; import android.os.Handler;
import android.os.Vibrator; import android.os.Vibrator;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.wearable.activity.WearableActivity; import android.support.wearable.activity.WearableActivity;
import android.util.Log; import android.util.Log;
import android.view.GestureDetector; import android.view.GestureDetector;
import android.view.MotionEvent; import android.view.MotionEvent;
import android.widget.TextView; import android.widget.TextView;
import com.google.android.gms.common.ConnectionResult;
import com.google.android.gms.common.api.GoogleApiClient;
import com.google.android.gms.common.api.ResultCallback;
import com.google.android.gms.wearable.MessageApi;
import com.google.android.gms.wearable.Node;
import com.google.android.gms.wearable.NodeApi;
import com.google.android.gms.wearable.Wearable;
import java.sql.Time; import java.sql.Time;
import java.util.List;
import java.util.Vector; import java.util.Vector;
import java.util.concurrent.TimeUnit;
import de.tonifetzer.conductorswatch.utilities.Utils; import de.tonifetzer.conductorswatch.utilities.Utils;
public class MainActivity extends WearableActivity implements WorkerFragment.OnFragmentInteractionListener, TapBpm.OnTapBpmListener{ public class MainActivity extends WearableActivity implements WorkerFragment.OnFragmentInteractionListener, TapBpm.OnTapBpmListener, GoogleApiClient.ConnectionCallbacks,
GoogleApiClient.OnConnectionFailedListener{
// member // member
private TextView mTextView; private TextView mTextView;
@@ -26,6 +39,13 @@ public class MainActivity extends WearableActivity implements WorkerFragment.OnF
private GestureDetector mDetector; private GestureDetector mDetector;
private boolean mModeRecord; private boolean mModeRecord;
// connection to phone stuff
private static final String START_ACTIVITY_PATH = "/start-activity";
public static final String TAG = "DataLayerListenerService";
private GoogleApiClient mGoogleApiClient;
private Node mNode;
private boolean mResolvingError = false;
// display center // display center
private int mDisplayWidth; private int mDisplayWidth;
private int mDisplayHeight; private int mDisplayHeight;
@@ -47,6 +67,7 @@ public class MainActivity extends WearableActivity implements WorkerFragment.OnF
private int mLongPressDelay = 1200; // in Milliseconds private int mLongPressDelay = 1200; // in Milliseconds
private boolean mLongPressHandlerActivated = false; private boolean mLongPressHandlerActivated = false;
private final Handler mHandler = new Handler(); private final Handler mHandler = new Handler();
private Runnable mLongPressed = new Runnable() { private Runnable mLongPressed = new Runnable() {
public void run() { public void run() {
Vibrator vibrator = (Vibrator) getSystemService(Context.VIBRATOR_SERVICE); Vibrator vibrator = (Vibrator) getSystemService(Context.VIBRATOR_SERVICE);
@@ -91,7 +112,7 @@ public class MainActivity extends WearableActivity implements WorkerFragment.OnF
Point currentPoint = new Point((int)ev.getX(), (int)ev.getY()); Point currentPoint = new Point((int)ev.getX(), (int)ev.getY());
//make sure the longPress only works within the maincircle of the scroller //make sure the longPress only works within the maincircle of the scroller
float distancePointToMiddle = Utils.getDistance(currentPoint.x, currentPoint.y, (float) (mDisplayWidth / 2.0f), (float) (mDisplayHeight / 2.0f)); double distancePointToMiddle = Utils.getDistance(currentPoint.x, currentPoint.y, (mDisplayWidth / 2.0f), (mDisplayHeight / 2.0f));
if ((distancePointToMiddle > mCroller.getMainCircleRadius())){ if ((distancePointToMiddle > mCroller.getMainCircleRadius())){
mHandler.removeCallbacks(mLongPressed); mHandler.removeCallbacks(mLongPressed);
return false; return false;
@@ -134,7 +155,7 @@ public class MainActivity extends WearableActivity implements WorkerFragment.OnF
Point currentPoint = new Point((int)ev.getX(), (int)ev.getY()); Point currentPoint = new Point((int)ev.getX(), (int)ev.getY());
//only works within the maincircle of the scroller //only works within the maincircle of the scroller
float distancePointToMiddle = Utils.getDistance(currentPoint.x, currentPoint.y, mDisplayCenter.x, mDisplayCenter.y); double distancePointToMiddle = Utils.getDistance(currentPoint.x, currentPoint.y, mDisplayCenter.x, mDisplayCenter.y);
if ((distancePointToMiddle > mCroller.getMainCircleRadius())){ if ((distancePointToMiddle > mCroller.getMainCircleRadius())){
//if we are outside the area of interest and the animation is already running, we need to reset //if we are outside the area of interest and the animation is already running, we need to reset
@@ -170,7 +191,7 @@ public class MainActivity extends WearableActivity implements WorkerFragment.OnF
Point currentPoint = new Point((int)ev.getX(), (int)ev.getY()); Point currentPoint = new Point((int)ev.getX(), (int)ev.getY());
//only works within the maincircle of the scroller //only works within the maincircle of the scroller
float distancePointToMiddle = Utils.getDistance(currentPoint.x, currentPoint.y, mDisplayCenter.x, mDisplayCenter.y); double distancePointToMiddle = Utils.getDistance(currentPoint.x, currentPoint.y, mDisplayCenter.x, mDisplayCenter.y);
if ((distancePointToMiddle > mCroller.getMainCircleRadius())){ if ((distancePointToMiddle > mCroller.getMainCircleRadius())){
mCroller.setProgress(mCroller.getProgress()); mCroller.setProgress(mCroller.getProgress());
@@ -204,6 +225,14 @@ public class MainActivity extends WearableActivity implements WorkerFragment.OnF
super.onCreate(savedInstanceState); super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main); setContentView(R.layout.activity_main);
//init GoogleApiClient and get nodeid (phone)
mGoogleApiClient = new GoogleApiClient.Builder(this)
.addApi(Wearable.API)
.addConnectionCallbacks(this)
.addOnConnectionFailedListener(this)
.build();
//get display infos
mDisplayWidth= this.getResources().getDisplayMetrics().widthPixels; mDisplayWidth= this.getResources().getDisplayMetrics().widthPixels;
mDisplayHeight= this.getResources().getDisplayMetrics().heightPixels; mDisplayHeight= this.getResources().getDisplayMetrics().heightPixels;
mDisplayCenter = new Point((mDisplayWidth / 2), (mDisplayHeight / 2)); mDisplayCenter = new Point((mDisplayWidth / 2), (mDisplayHeight / 2));
@@ -224,6 +253,7 @@ public class MainActivity extends WearableActivity implements WorkerFragment.OnF
public void onProgressChanged(int progress) { public void onProgressChanged(int progress) {
// use the progress // use the progress
mTextView.setText(String.valueOf(progress)); mTextView.setText(String.valueOf(progress));
sendMessage(Integer.toString(progress));
} }
}); });
@@ -242,6 +272,29 @@ public class MainActivity extends WearableActivity implements WorkerFragment.OnF
setAmbientEnabled(); setAmbientEnabled();
} }
private void sendMessage(String Key) {
if (mNode != null && mGoogleApiClient!= null && mGoogleApiClient.isConnected()) {
Log.d(TAG, "-- " + mGoogleApiClient.isConnected());
Wearable.MessageApi.sendMessage(
mGoogleApiClient, mNode.getId(), Key, new byte[0]).setResultCallback(
new ResultCallback<MessageApi.SendMessageResult>() {
@Override
public void onResult(MessageApi.SendMessageResult sendMessageResult) {
Log.d(TAG, "--- " + sendMessageResult.getStatus().getStatusCode());
if (!sendMessageResult.getStatus().isSuccess()) {
Log.e(TAG, "Failed to send message with status code: "
+ sendMessageResult.getStatus().getStatusCode());
}
}
}
);
}
}
@Override @Override
public boolean dispatchTouchEvent(MotionEvent ev) { public boolean dispatchTouchEvent(MotionEvent ev) {
@@ -265,14 +318,33 @@ public class MainActivity extends WearableActivity implements WorkerFragment.OnF
@Override @Override
public void onFragmentStopped(Vector<Float> bpmList) { public void onFragmentStopped(Vector<Double> bpmList) {
//TODO: save the bpmList into a file //TODO: save the bpmList into a file
//TODO: send bpmList and all other data to phone
Log.d("FragmentListener", "Received bpmList"); Log.d("FragmentListener", "Received bpmList");
} }
@Override
protected void onStart() {
super.onStart();
if (!mResolvingError) {
mGoogleApiClient.connect();
}
//start app on phone
//TODO: bissle cleverer machen. starten und auf antwort warten. falls er sagt "bin schon an" nicht mehr senden
// ansonsten nochmal versuchen.bzw. auch von anderen stellen (bei click etc) aufrufen.
//TODO: disconnect or own class for this stuff
}
@Override @Override
public void onNewEstimation(int bpm) { public void onNewEstimation(int bpm) {
mTapBpmEstimation = bpm; mTapBpmEstimation = bpm;
@@ -310,4 +382,45 @@ public class MainActivity extends WearableActivity implements WorkerFragment.OnF
e.printStackTrace(); e.printStackTrace();
} }
} }
@Override
public void onConnected(@Nullable Bundle bundle) {
resolveNode();
new Thread(new Runnable() {
@Override
public void run() {
while(mNode == null){
}
sendMessage(START_ACTIVITY_PATH);
}
}).start();
}
@Override
public void onConnectionSuspended(int i) {
}
@Override
public void onConnectionFailed(@NonNull ConnectionResult connectionResult) {
}
private void resolveNode() {
Wearable.NodeApi.getConnectedNodes(mGoogleApiClient)
.setResultCallback(new ResultCallback<NodeApi.GetConnectedNodesResult>() {
@Override
public void onResult(NodeApi.GetConnectedNodesResult nodes) {
for (Node node : nodes.getNodes()) {
mNode = node;
}
}
});
}
} }

View File

@@ -22,7 +22,7 @@ import java.util.Vector;
public class WorkerFragment extends Fragment implements Metronome.OnMetronomeListener, Estimator.OnBpmEstimatorListener{ public class WorkerFragment extends Fragment implements Metronome.OnMetronomeListener, Estimator.OnBpmEstimatorListener{
private OnFragmentInteractionListener mListener; private OnFragmentInteractionListener mListener;
private Vector<Float> mBpmList; //TODO save to file. private Vector<Double> mBpmList; //TODO save to file.
private Estimator mEstimator; private Estimator mEstimator;
private Metronome mMetronome; private Metronome mMetronome;
@@ -59,7 +59,7 @@ public class WorkerFragment extends Fragment implements Metronome.OnMetronomeLis
mEstimator = new Estimator(getContext()); mEstimator = new Estimator(getContext());
mEstimator.add(this); mEstimator.add(this);
//mBpmThread = new Thread(mEstimator, "estThread"); //mBpmThread = new Thread(mEstimator, "estThread");
mBpmList = new Vector<Float>(); mBpmList = new Vector<Double>();
// init metronome and listener // init metronome and listener
@@ -138,17 +138,22 @@ public class WorkerFragment extends Fragment implements Metronome.OnMetronomeLis
*/ */
@Override @Override
public void onNewClick() { public void onNewClick() {
mVibrator.vibrate(10); //mVibrator.vibrate(10);
} }
/** /**
* Callback function for Bpm estimation update * Callback function for Bpm estimation update
*/ */
@Override @Override
public void onNewDataAvailable(float bpm) { public void onNewDataAvailable(double bpm) {
//TODO: what if multiple threads access mBpmList? put into synchronized? @frank fragen :D //TODO: what if multiple threads access mBpmList? put into synchronized? @frank fragen :D
//TODO: send this to smartphone //TODO: send this to smartphone
if(bpm == -1){
//to stuff with UI. Write Text or make XX or something like that
}
mBpmList.add(bpm); mBpmList.add(bpm);
// we need this here, since ui elements can only be changed within activity thread and // we need this here, since ui elements can only be changed within activity thread and
@@ -160,7 +165,7 @@ public class WorkerFragment extends Fragment implements Metronome.OnMetronomeLis
public void run() { public void run() {
mTextView.setText(String.valueOf(mBpmList.lastElement())); mTextView.setText(String.valueOf(mBpmList.lastElement()));
mCroller.setProgress((Math.round(mBpmList.lastElement()))); mCroller.setProgress((int)(Math.round(mBpmList.lastElement())));
} }
}); });
} }
@@ -170,7 +175,7 @@ public class WorkerFragment extends Fragment implements Metronome.OnMetronomeLis
* Interface for connecting to WorkerFragment * Interface for connecting to WorkerFragment
*/ */
public interface OnFragmentInteractionListener { public interface OnFragmentInteractionListener {
void onFragmentStopped(Vector<Float> bpmList); void onFragmentStopped(Vector<Double> bpmList);
} }
} }

View File

@@ -3,14 +3,162 @@ package de.tonifetzer.conductorswatch.utilities;
import android.content.Context; import android.content.Context;
import android.content.res.Resources; import android.content.res.Resources;
import android.util.DisplayMetrics; import android.util.DisplayMetrics;
import org.jtransforms.fft.FloatFFT_1D;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Comparator;
import java.util.LinkedList;
//TODO: change from double to generic type
public class Utils { public class Utils {
public static float getDistance(float x1, float y1, float x2, float y2) { public static double getDistance(double x1, double y1, double x2, double y2) {
return (float) Math.sqrt((x1 - x2) * (x1 - x2) + (y1 - y2) * (y1 - y2)); return (double) Math.sqrt((x1 - x2) * (x1 - x2) + (y1 - y2) * (y1 - y2));
}
public static double sqr(double x) {
return x * x;
}
public static int nextPow2(int a){
return a == 0 ? 0 : 32 - Integer.numberOfLeadingZeros(a - 1);
}
public static double sum(double[] data){
double sum = 0;
for (int i = 0; i < data.length; i++) {
sum += data[i];
}
return sum;
}
public static long sum(long[] data){
long sum = 0;
for (int i = 0; i < data.length; i++) {
sum += data[i];
}
return sum;
}
public static double sum (LinkedList<Double> data){
double sum = 0;
for (int i = 0; i < data.size(); i++) {
sum += data.get(i).doubleValue();
}
return sum;
}
//TODO: Could be slow.. faster method?
public static double rms(double[] nums) {
double sum = 0.0f;
for (double num : nums)
sum += num * num;
return Math.sqrt(sum / nums.length);
}
public static double[] diff(double[] data){
double[] diff = new double[data.length - 1];
int i=0;
for(int j = 1; j < data.length; ++j){
diff[i] = data[j] - data[i];
++i;
}
return diff;
}
public static long[] diff(long[] data){
long[] diff = new long[data.length - 1];
int i=0;
for(int j = 1; j < data.length; ++j){
diff[i] = data[j] - data[i];
++i;
}
return diff;
}
public static double mean(double[] data){
return sum(data) / data.length;
}
public static double mean(long[] data){
return (double) sum(data) / (double) data.length;
}
public static double mean(LinkedList<Double> data){
return sum(data) / data.size();
}
public static double median(LinkedList<Double> data){
data.sort(Comparator.naturalOrder());
double median;
if (data.size() % 2 == 0)
median = (data.get(data.size()/2) + data.get(data.size()/2 - 1))/2;
else
median = data.get(data.size()/2);
return median;
}
//TODO: Could be slow.. faster method?
public static double geometricMean(double[] data) {
double sum = data[0];
for (int i = 1; i < data.length; i++) {
sum *= data[i];
}
return Math.pow(sum, 1.0 / data.length);
}
public static int intersectionNumber(double[] signal, double border){
int cnt = 0;
boolean isSmallerValue = false;
boolean isBiggerValue = false;
for(double value : signal){
if(value < border){
if(isBiggerValue){
cnt++;
}
isSmallerValue = true;
isBiggerValue = false;
}
else {
if(isSmallerValue){
cnt++;
}
isSmallerValue = false;
isBiggerValue = true;
}
}
return cnt;
}
public static double[] removeZero(double[] array){
int j = 0;
for( int i=0; i<array.length; i++ )
{
if (array[i] != 0){
array[j++] = array[i];
}
}
double[] newArray = new double[j];
System.arraycopy( array, 0, newArray, 0, j );
return newArray;
}
public static double[] greaterZero(double[] array){
int j = 0;
for( int i=0; i < array.length; i++ )
{
if (array[i] > 0){
array[j++] = array[i];
}
}
double[] newArray = new double[j];
System.arraycopy( array, 0, newArray, 0, j );
return newArray;
} }
public static float convertDpToPixel(float dp, Context context) { public static float convertDpToPixel(float dp, Context context) {
@@ -25,174 +173,4 @@ public class Utils {
return px / ((float) metrics.densityDpi / DisplayMetrics.DENSITY_DEFAULT); return px / ((float) metrics.densityDpi / DisplayMetrics.DENSITY_DEFAULT);
} }
public static class AccelerometerData {
public float x,y,z;
public long ts;
public AccelerometerData(long ts, float x, float y, float z){
this.ts = ts;
this.x = x;
this.y = y;
this.z = z;
}
}
public static class AccelerometerWindowBuffer extends ArrayList<AccelerometerData> {
private int mWindowSize;
private int mOverlapSize;
private int mOverlapCounter;
private float[] mX;
private float[] mY;
private float[] mZ;
private long[] mTs;
public AccelerometerWindowBuffer(int windowSize, int overlap){
this.mWindowSize = windowSize;
this.mOverlapSize = overlap;
mOverlapCounter = 1;
mX = new float[this.mWindowSize];
mY = new float[this.mWindowSize];
mZ = new float[this.mWindowSize];
mTs = new long[this.mWindowSize];
}
public boolean add(AccelerometerData ad){
boolean r = super.add(ad);
if (size() > mWindowSize){
removeRange(0, size() - mWindowSize);
}
//update the double arrays.
for (int i = 0; i < size(); ++i) {
mX[i] = get(i).x;
mY[i] = get(i).y;
mZ[i] = get(i).z;
mTs[i] = get(i).ts;
}
++mOverlapCounter;
return r;
}
public boolean isNextWindowReady(){
if(size() == mWindowSize && mOverlapCounter > mOverlapSize){
mOverlapCounter = 1;
return true;
}
return false;
}
public AccelerometerData getYongest() {
return get(size() - 1);
}
public AccelerometerData getOldest() {
return get(0);
}
public float[] getX(){
return mX;
}
public float[] getY(){
return mY;
}
public float[] getZ(){
return mZ;
}
public long[] getTs(){
return mTs;
}
}
public static float sqr(float x) {
return x * x;
}
public static int nextPow2(int a){
return a == 0 ? 0 : 32 - Integer.numberOfLeadingZeros(a - 1);
}
public static float mean(float[] data){
float sum = 0;
for (int i = 0; i < data.length; i++) {
sum += data[i];
}
return sum / data.length;
}
public static float[] removeZero(float[] array){
int j = 0;
for( int i=0; i<array.length; i++ )
{
if (array[i] != 0)
array[j++] = array[i];
}
float[] newArray = new float[j];
System.arraycopy( array, 0, newArray, 0, j );
return newArray;
}
//TODO: errorhandling maxLag = 0;
//TODO: größeren Testcase schreiben
public static float[] fftAutoCorrelation(float[] data, int maxLag) {
int n = data.length;
float[] x = Arrays.copyOf(data, n);
int mxl = Math.min(maxLag, n - 1);
int ceilLog2 = nextPow2(2*n -1);
int n2 = (int) Math.pow(2,ceilLog2);
// x - mean(x) (pointwise)
float x_mean = mean(x);
for(int i = 0; i < x.length; ++i){
x[i] -= x_mean;
}
// double the size of x and fill up with zeros. if x is not even, add additional 0
float[] x2 = new float[n2 * 2]; //need double the size for fft.realForwardFull (look into method description)
Arrays.fill(x2, 0);
System.arraycopy(x,0, x2, 0, x.length);
// x_fft calculate fft 1D
FloatFFT_1D fft = new FloatFFT_1D(n2);
fft.realForwardFull(x2);
// Cr = abs(x_fft).^2 (absolute with complex numbers is (r^2) + (i^2)
float[] Cr = new float[n2 * 2];
int j = 0;
for(int i = 0; i < x2.length; ++i){
Cr[j++] = sqr(x2[i]) + sqr(x2[i+1]);
++i; //skip the complex part
}
// ifft(Cr,[],1)
FloatFFT_1D ifft = new FloatFFT_1D(n2);
ifft.realInverseFull(Cr, true);
// remove complex part and scale/normalize
float[] c1 = new float[n2];
j = 0;
for(int i = 0; i < Cr.length; ++i){
c1[j++] = Cr[i] / Cr[0];
++i; //skip the complex part
}
// Keep only the lags we want and move negative lags before positive lags.
float[] c = new float[(mxl * 2) + 1];
System.arraycopy(c1, 0, c, mxl, mxl + 1); // +1 to place the 1.0 in the middle of correlation
System.arraycopy(c1, n2 - mxl, c, 0, mxl);
return c;
}
//TODO: findPeaks
} }

View File

@@ -1,11 +1,3 @@
<resources> <resources>
<string name="app_name">Conductor\'s Watch</string> <string name="app_name">Conductor\'s Watch</string>
<!--
This string is used for square devices and overridden by hello_world in
values-round/strings.xml for round devices.
-->
<string name="hello_world">Hello Square World!</string>
<!-- TODO: Remove or change this placeholder text -->
<string name="hello_blank_fragment">Hello blank fragment</string>
</resources> </resources>

View File

@@ -6,6 +6,8 @@ import java.io.BufferedReader;
import java.io.File; import java.io.File;
import java.io.FileReader; import java.io.FileReader;
import java.io.IOException; import java.io.IOException;
import java.util.LinkedList;
import java.util.stream.IntStream;
/** /**
@@ -16,19 +18,19 @@ public class Main {
public static void main(String [ ] args) { public static void main(String [ ] args) {
File folder = new File("/home/toni/Documents/programme/dirigent/measurements/wearR"); File folder = new File("/home/toni/Documents/programme/dirigent/measurements/wearR");
File[] listOfFiles = folder.listFiles(); File[] listOfFiles = folder.listFiles();
/*
Utils.ShowPNG windowRaw = new Utils.ShowPNG(); Utils.ShowPNG windowRaw = new Utils.ShowPNG();
Utils.ShowPNG windowAuto = new Utils.ShowPNG(); Utils.ShowPNG windowAuto = new Utils.ShowPNG();
Utils.ShowPNG windowPeaksX = new Utils.ShowPNG(); Utils.ShowPNG windowPeaksX = new Utils.ShowPNG();
Utils.ShowPNG windowPeaksY = new Utils.ShowPNG(); Utils.ShowPNG windowPeaksY = new Utils.ShowPNG();
Utils.ShowPNG windowPeaksZ = new Utils.ShowPNG(); Utils.ShowPNG windowPeaksZ = new Utils.ShowPNG();
*/
// iterate trough files in measurements folder // iterate trough files in measurements folder
for (File file : listOfFiles) { for (File file : listOfFiles) {
if (file.isFile() && file.getName().contains(".csv")) { if (file.isFile() && file.getName().contains(".csv")) {
AccelerometerWindowBuffer accWindowBuffer = new AccelerometerWindowBuffer(4096, 256); AccelerometerWindowBuffer accWindowBuffer = new AccelerometerWindowBuffer(1024, 256);
BpmEstimator bpmEstimator = new BpmEstimator(accWindowBuffer, 0, 5000); BpmEstimator bpmEstimator = new BpmEstimator(accWindowBuffer, 0, 5000);
//read the file line by line //read the file line by line
@@ -51,9 +53,9 @@ public class Main {
double curBpm = bpmEstimator.estimate(); double curBpm = bpmEstimator.estimate();
//System.out.println("BPM: " + curBpm); //System.out.println("BPM: " + curBpm);
/*
AccelerometerInterpolator acInterp = new AccelerometerInterpolator(accWindowBuffer, bpmEstimator.getSampleRate_ms());
AccelerometerInterpolator acInterp = new AccelerometerInterpolator(accWindowBuffer, 5);
//print raw x,y,z //print raw x,y,z
double[] dTs = IntStream.range(0, accWindowBuffer.getTs().length).mapToDouble(i -> accWindowBuffer.getTs()[i]).toArray(); double[] dTs = IntStream.range(0, accWindowBuffer.getTs().length).mapToDouble(i -> accWindowBuffer.getTs()[i]).toArray();
@@ -68,9 +70,9 @@ public class Main {
windowRaw.set(plotRaw.draw()); windowRaw.set(plotRaw.draw());
//auto corr //auto corr
double[] xAutoCorr = new AutoCorrelation(acInterp.getX(), 1024).getCorr(); double[] xAutoCorr = new AutoCorrelation(acInterp.getX(), 512).getCorr();
double[] yAutoCorr = new AutoCorrelation(acInterp.getY(), 1024).getCorr(); double[] yAutoCorr = new AutoCorrelation(acInterp.getY(), 512).getCorr();
double[] zAutoCorr = new AutoCorrelation(acInterp.getZ(), 1024).getCorr(); double[] zAutoCorr = new AutoCorrelation(acInterp.getZ(), 512).getCorr();
//print autocorr //print autocorr
int[] tmp = IntStream.rangeClosed(-((xAutoCorr.length - 1)/2), ((xAutoCorr.length - 1)/2)).toArray(); int[] tmp = IntStream.rangeClosed(-((xAutoCorr.length - 1)/2), ((xAutoCorr.length - 1)/2)).toArray();
@@ -87,7 +89,7 @@ public class Main {
Peaks pX = new Peaks(xAutoCorr, 50, 0.1f, 0, false); Peaks pX = new Peaks(xAutoCorr, 50, 0.1f, 0, false);
LinkedList<Integer> peaksX = pX.getPeaksIdx(); LinkedList<Integer> peaksX = pX.getPeaksIdx();
double[] dPeaksXX = IntStream.range(0, peaksX.size()).mapToDouble(i -> (peaksX.get(i) - 1024)).toArray();//peaks.stream().mapToDouble(i->i).toArray(); double[] dPeaksXX = IntStream.range(0, peaksX.size()).mapToDouble(i -> (peaksX.get(i) - 512)).toArray();//peaks.stream().mapToDouble(i->i).toArray();
double[] dPeaksXY = IntStream.range(0, peaksX.size()).mapToDouble(i -> (xAutoCorr[peaksX.get(i)])).toArray(); double[] dPeaksXY = IntStream.range(0, peaksX.size()).mapToDouble(i -> (xAutoCorr[peaksX.get(i)])).toArray();
Plot plotPeaksX = Plot.plot(Plot.plotOpts(). Plot plotPeaksX = Plot.plot(Plot.plotOpts().
title("Peak Detection on X"). title("Peak Detection on X").
@@ -101,7 +103,7 @@ public class Main {
Peaks pY = new Peaks(yAutoCorr, 50, 0.1f, 0, false); Peaks pY = new Peaks(yAutoCorr, 50, 0.1f, 0, false);
LinkedList<Integer> peaksY = pY.getPeaksIdx(); LinkedList<Integer> peaksY = pY.getPeaksIdx();
double[] dPeaksYX = IntStream.range(0, peaksY.size()).mapToDouble(i -> (peaksY.get(i) - 1024)).toArray();//peaks.stream().mapToDouble(i->i).toArray(); double[] dPeaksYX = IntStream.range(0, peaksY.size()).mapToDouble(i -> (peaksY.get(i) - 512)).toArray();//peaks.stream().mapToDouble(i->i).toArray();
double[] dPeaksYY = IntStream.range(0, peaksY.size()).mapToDouble(i -> (yAutoCorr[peaksY.get(i)])).toArray(); double[] dPeaksYY = IntStream.range(0, peaksY.size()).mapToDouble(i -> (yAutoCorr[peaksY.get(i)])).toArray();
Plot plotPeaksY = Plot.plot(Plot.plotOpts(). Plot plotPeaksY = Plot.plot(Plot.plotOpts().
title("Peak Detection on Y"). title("Peak Detection on Y").
@@ -115,7 +117,7 @@ public class Main {
Peaks pZ = new Peaks(zAutoCorr, 50, 0.1f, 0, false); Peaks pZ = new Peaks(zAutoCorr, 50, 0.1f, 0, false);
LinkedList<Integer> peaksZ = pZ.getPeaksIdx(); LinkedList<Integer> peaksZ = pZ.getPeaksIdx();
double[] dPeaksZX = IntStream.range(0, peaksZ.size()).mapToDouble(i -> (peaksZ.get(i) - 1024)).toArray();//peaks.stream().mapToDouble(i->i).toArray(); double[] dPeaksZX = IntStream.range(0, peaksZ.size()).mapToDouble(i -> (peaksZ.get(i) - 512)).toArray();//peaks.stream().mapToDouble(i->i).toArray();
double[] dPeaksZY = IntStream.range(0, peaksZ.size()).mapToDouble(i -> (zAutoCorr[peaksZ.get(i)])).toArray(); double[] dPeaksZY = IntStream.range(0, peaksZ.size()).mapToDouble(i -> (zAutoCorr[peaksZ.get(i)])).toArray();
Plot plotPeaksZ = Plot.plot(Plot.plotOpts(). Plot plotPeaksZ = Plot.plot(Plot.plotOpts().
title("Peak Detection on Z"). title("Peak Detection on Z").
@@ -134,7 +136,7 @@ public class Main {
//System.out.println("BPM-Z: " + pZ.getBPM(bpmEstimator.getSampleRate_ms())); //System.out.println("BPM-Z: " + pZ.getBPM(bpmEstimator.getSampleRate_ms()));
//todo: kleiner fenstergrößen testen. so ist doch etwas langsam auf der Uhr. //todo: kleiner fenstergrößen testen. so ist doch etwas langsam auf der Uhr.
*/
int dummyForBreakpoint = 0; int dummyForBreakpoint = 0;
} }
} }

View File

@@ -36,7 +36,7 @@ public class AccelerometerWindowBuffer extends ArrayList<AccelerometerData> {
} }
public boolean isNextWindowReady(){ public boolean isNextWindowReady(){
if((size() == mWindowSize || size() == mWindowSize / 2 || size() == mWindowSize / 4) && mOverlapCounter > mOverlapSize){ if((size() > mWindowSize / 2) && mOverlapCounter > mOverlapSize){
mOverlapCounter = 1; mOverlapCounter = 1;
return true; return true;

View File

@@ -77,6 +77,7 @@ public class BpmEstimator {
//kalman filter (lohnt dann, wenn wir konstantes tempo haben, mit startangabe!) //kalman filter (lohnt dann, wenn wir konstantes tempo haben, mit startangabe!)
mBpmHistory.add(estimatedBPM); mBpmHistory.add(estimatedBPM);
mResetCounter = 0;
} }
else { else {
int resetAfter = (int) Math.round(mResetLimit_ms / (mBuffer.getOverlapSize() * sampleRate)); int resetAfter = (int) Math.round(mResetLimit_ms / (mBuffer.getOverlapSize() * sampleRate));
@@ -102,9 +103,9 @@ public class BpmEstimator {
private double getBestBpmEstimation(Peaks peaksX, Peaks peaksY, Peaks peaksZ) throws IllegalArgumentException { private double getBestBpmEstimation(Peaks peaksX, Peaks peaksY, Peaks peaksZ) throws IllegalArgumentException {
int cntNumAxis = 0; int cntNumAxis = 0;
double sumCorr = 1; //to prevent division by zero double sumCorr = 0; //to prevent division by zero
double sumRms = 1; double sumRms = 0;
double sumNumInter = 1; double sumNumInter = 0;
double corrMeanX = 0, corrRmsX = 0; double corrMeanX = 0, corrRmsX = 0;
int corrNumInterX = 0; int corrNumInterX = 0;
@@ -167,7 +168,7 @@ public class BpmEstimator {
//values to low, reject //values to low, reject
//TODO: this is a pretty simple assumption. first shot! //TODO: this is a pretty simple assumption. first shot!
if(corrRmsX < 0.2 && corrRmsY < 0.2 && corrRmsZ < 0.2){ if(corrRmsX < 0.25 && corrRmsY < 0.25 && corrRmsZ < 0.25){
return -1; return -1;
} }