package edu.ucla.iBeaconNav; import java.util.LinkedList; import java.util.Queue; import android.app.Notification; import android.app.Activity; import android.app.Service; import android.content.Context; import android.content.Intent; import android.hardware.Sensor; import android.hardware.SensorEvent; import android.hardware.SensorEventListener; import android.hardware.SensorManager; import android.os.Handler; import android.os.IBinder; import android.os.Message; import android.os.Messenger; import android.os.SystemClock; import android.widget.EditText; import edu.ucla.iBeaconNav.R; public class Sensors extends Service implements SensorEventListener { /* Private data */ private SensorManager sensorManager; private Sensor accSensor; private Sensor grvSensor; private Sensor magSensor; private Sensor gyrSensor; private Messenger messenger; private boolean active; private long lastTime_ms = 0; private float snsInterval_ms = 0; private long lastTime_ns = 0; private long snsInterval_ns = 0; /* Sensor data */ private float[] accValues = new float[3]; private float[] magValues = new float[3]; private float[] gyrValues = new float[3]; private float[] rotationMatrix_R = new float[9]; private float[] rtMatrixStable_R = new float[9]; private float[] rotationMatrix_I = new float[9]; private float[] orientValues_rd = new float[3]; private float[] orientValues = new float[3]; private float[] accWorldCoord = {0,0,0}; private float[] gyrWorldCoord = {0,0,0}; /* Auxiliary Variables for Sensor Processing */ private final int cycle = 32; private LinkedList accBuffers = new LinkedList(); private LinkedList magBuffers = new LinkedList(); private LinkedList gyrBuffers = new LinkedList(); private LinkedList orientBuffers = new LinkedList(); private LinkedList rtMtrxBuffers = new LinkedList(); private float[] data = new float[4]; private float[] accSum = {0,0,0}; private float[] accAvg = {0,0,0}; private float[] magSum = {0,0,0}; private float[] gyrSum = {0,0,0}; private float[] gyrOffset = {0,0,0}; private float[] orientSum = {0,0,0}; private float[] rtMtrxSum = {0,0,0, 0,0,0, 0,0,0}; private float[] accBuffer = {0,0,0}; private float[] magBuffer = {0,0,0}; private float[] orientBuffer= {0,0,0}; private int cnt = 0; private int accCnt = 0; private int magCnt = 0; private int gyrCnt = 0; private int orientCnt = 0; private int rtMtrxCnt = 0; private boolean ifGyrOffset = false; private boolean ifGrvOffset = false; private boolean ifStable = false; private final float EPSILON = (float)0.01; private float accValueTotal = 0; private float calculatedGravity = SensorManager.GRAVITY_EARTH; private float gravityRef = 0; private float gyroscopeRef = 0; private boolean ifSetGrvRef = false; /* Position Related Stuff */ //private float startPosX = 0; //private float startPos = 0; private float currentPosX = 0; private float currentPosY = 0; // rotate around gravity direction, positive when counterclock-wise, initially align with user's first step private float currentHeading = 0; private float headingOffset = 0; private float stepLength = (float)0.5; // in m private int stepCount = 0; private boolean stepStart = false; /* Private methods */ private void tellMain(CMD.Response cmd, Object value) { try { android.os.Message msg = android.os.Message.obtain(); msg.what = cmd.ordinal(); msg.obj = value; this.messenger.send(msg); } catch (Exception e) { Util.debug("Sensors: error sending message", e); } } private void notify(String text, int icon) { // Notify Main this.tellMain(CMD.Response.NOTIFY, text); // Notification bar //Notification note = new Notification(icon, null, 0); //Intent intent = new Intent(this, Main.class); //PendingIntent pend = PendingIntent.getActivity(this, 0, intent, 0); //note.setLatestEventInfo(this, "iBeaconNav!", text, pend); //PendingIntent pend = PendingIntent.getActivity(this, 0, intent, 0); Notification note = new Notification.Builder(this) .setContentTitle("iBeaconNav!") .setContentText("iBeaconNav!") .setSmallIcon(icon) .build(); this.startForeground(1, note); } private void handle(CMD.Command cmd, Messenger mgr) { // Validate messenger if (cmd != CMD.Command.REGISTER && mgr != null && mgr != this.messenger) Util.debug("Sensors: handle - invalid messenger"); // Handle the command switch (cmd) { // Setup communication with Main case REGISTER: Util.debug("Sensors: handle - register"); this.messenger = mgr; break; // Create client thread case CONNECT: Util.debug("Sensors: handle - connect"); sensorManager.registerListener(this, accSensor, SensorManager.SENSOR_DELAY_FASTEST); sensorManager.registerListener(this, grvSensor, SensorManager.SENSOR_DELAY_FASTEST); sensorManager.registerListener(this, magSensor, SensorManager.SENSOR_DELAY_FASTEST); sensorManager.registerListener(this, gyrSensor, SensorManager.SENSOR_DELAY_FASTEST); break; // Stop client thread case DISCONNECT: Util.debug("Sensors: handle - register"); // TODO break; // Reset heading case RSTHEAD: Util.debug("Sensors: handle - reset heading"); currentHeading = 0; displayData(CMD.Data.HEADING); break; // Reset distance case RSTDST: Util.debug("Sensors: handle - reset distance"); currentPosX = 0; currentPosY = 0; displayData(CMD.Data.POSITION); break; } } /* Public methods */ public boolean isRunning() { return true; // TODO } /* Service Methods */ @Override public void onCreate() { Util.debug("Sensors: onCreate"); super.onCreate(); sensorManager = (SensorManager) getSystemService(Context.SENSOR_SERVICE); accSensor = sensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER); grvSensor = sensorManager.getDefaultSensor(Sensor.TYPE_GRAVITY); magSensor = sensorManager.getDefaultSensor(Sensor.TYPE_MAGNETIC_FIELD); gyrSensor = sensorManager.getDefaultSensor(Sensor.TYPE_GYROSCOPE); } @Override public void onDestroy() { Util.debug("Sensors: onDestroy"); //this.handle(CMD.Response.DISCONNECT, null); } @Override public int onStartCommand(Intent intent, int flags, int startId) { Util.debug("Sensors: onStartCommand"); int rval = super.onStartCommand(intent, flags, startId); CMD.Command cmd = (CMD.Command)intent.getExtras().get("Command"); Messenger mgr = (Messenger)intent.getExtras().get("Messenger"); this.handle(cmd, mgr); return rval; } @Override public IBinder onBind(Intent intent) { Util.debug("Sensors: onBind"); return messenger.getBinder(); } class IncomingHandler extends Handler{ @Override public void handleMessage(Message msg) { //Util.debug("Sensors: MSG HANDLERRRRRRRRR"); super.handleMessage(msg); } } @Override public void onAccuracyChanged(Sensor sensor, int accuracy) { // TODO Auto-generated method stub Util.debug("Sensors: onAccuracyChanged"); } @Override public void onSensorChanged(SensorEvent event) { // TODO Auto-generated method stub switch(event.sensor.getType()){ case Sensor.TYPE_ACCELEROMETER: accCnt++; System.arraycopy(event.values, 0, accValues, 0, 3); //accCnt = everyAveragedBuffer(accBuffer, accValues, 3, accCnt, cycle, CMD.Data.ACC); forwardAveragedBuffer(accBuffers, accSum, accAvg, accValues, 3, accCnt, cycle, CMD.Data.ACC); toWorldCoordinates(accWorldCoord, accValues, rotationMatrix_R, accCnt, CMD.Data.WRDACC); accValueTotal = accWorldCoord[2]; if (ifSetGrvRef && !ifGrvOffset && Math.abs((accValueTotal-gravityRef)/gravityRef)<0.05){ rtMatrixStable_R = rotationMatrix_R; calculatedGravity = accValueTotal; ifGrvOffset = true; ifGyrOffset = false; } else if(!ifSetGrvRef && accCnt>400){ ifSetGrvRef = true; gravityRef = accValueTotal; } updateOrientation(); break; case Sensor.TYPE_MAGNETIC_FIELD: magCnt++; System.arraycopy(event.values, 0, magValues, 0, 3); //magCnt = everyAveragedBuffer(magBuffer, magValues, 3, magCnt, cycle, CMD.Data.MAG); forwardAveragedBuffer(magBuffers, magSum, null, magValues, 3, magCnt, cycle, CMD.Data.MAG); updateOrientation(); // This maybe useless break; case Sensor.TYPE_GYROSCOPE: gyrCnt++; float gyrTemp[] = new float[3]; long currentTime_ns = event.timestamp; snsInterval_ns = currentTime_ns-lastTime_ns; lastTime_ns = currentTime_ns; int chkIntCnt = 8; //check Interval Count if (gyrCnt%chkIntCnt==0){ //long currentTime_ms = SystemClock.elapsedRealtime(); //snsInterval_ms = (float)(currentTime_ms-lastTime_ms)/chkIntCnt; if(ifSetGrvRef){ ifStable = isStable(accBuffers, 3, cycle/2) && isStable(gyrBuffers, 3, cycle/2); } if (ifStable){ ifGrvOffset = false; } //lastTime_ms = SystemClock.elapsedRealtime(); //Util.debug("Interal in ms: "+Float.toString(snsInterval_ms)); } System.arraycopy(event.values, 0, gyrValues, 0, 3); System.arraycopy(gyrValues, 0, gyrTemp, 0, 3); forwardAveragedBuffer(gyrBuffers, gyrSum, null, gyrValues, 3, gyrCnt, 2*cycle, CMD.Data.GYR); System.arraycopy(gyrTemp, 0, gyrValues, 0, 3); if (!ifGyrOffset && ifGrvOffset){ //Util.debug("[GYR1] Reset "+Float.toString(gyrValues[2])); gyrOffset[0] = gyrValues[0]; //x gyrOffset[1] = gyrValues[1]; //y gyrOffset[2] = gyrValues[2]; //z ifGyrOffset = true; } depleteOffset(gyrValues, gyrOffset, 3); //cycleFloatArray(gyrValues, 3); toWorldCoordinates(gyrWorldCoord, gyrValues, rtMatrixStable_R, gyrCnt, CMD.Data.WRDGYR); float gyrZ = gyrWorldCoord[2]; //if (!ifStable && ifGyrOffset && Math.abs(gyrZ)>0.05){ if (ifGyrOffset){ //currentHeading+=gyrZ*snsInterval_ms/1000*180/Math.PI; currentHeading += gyrZ*snsInterval_ns/1000000000*180/Math.PI; currentHeading%=360; displayData(CMD.Data.HEADING); } break; } processSensorInfo(); } private void cycleFloatArray(float[] array, int length){ float temp = array[length-1]; for(int i=1; i buffer, int length, int cycle){ int len = buffer.size(); float[] avrg = new float[length]; float[] devSum = new float[length]; float ttDev = 0; for(int i=0; i0.1){ //Util.debug("[DEV] unStable"); displayData(CMD.Data.STABLE); return false; }else{ //Util.debug("[DEV] Stable"); displayData(CMD.Data.STABLE); return true; } } private void processSensorInfo(){ displayData(CMD.Data.STPCNT); float epsl = (float)0.6; // on step count???? if (stepStart){ //judge if stop if(accValueTotal-calculatedGravity>epsl){ stepStart = false; stepCount++; currentPosX += stepLength*Math.sin(currentHeading); currentPosY += stepLength*Math.cos(currentHeading); displayData(CMD.Data.POSITION); } }else{ //judge if start if (calculatedGravity-accValueTotal>epsl ){ stepStart = true; } } } private void updateOrientation(){ if (accValues == null || magValues == null){ return; } float R[] = new float[9]; float I[] = new float[9]; boolean success; success = SensorManager.getRotationMatrix(R, I, accValues, magValues); if(success){ orientCnt++; rtMtrxCnt++; rotationMatrix_R = R; rotationMatrix_I = I; SensorManager.getOrientation(rotationMatrix_R, orientValues_rd); } // to degree orientValues[0] = (float) (orientValues_rd[0]*180/Math.PI); orientValues[1] = (float) (orientValues_rd[1]*180/Math.PI); orientValues[2] = (float) (orientValues_rd[2]*180/Math.PI); //orientCnt=everyAveragedBuffer(orientBuffer, orientValues, 3, orientCnt, 2*cycle, CMD.Data.ORIENT); forwardAveragedBuffer(rtMtrxBuffers,rtMtrxSum, null, rotationMatrix_R, 9, rtMtrxCnt, 2*cycle, null); forwardAveragedBuffer(orientBuffers,orientSum, null, orientValues, 3, orientCnt, 2*cycle, CMD.Data.ORIENT); //displayData(CMD.Data.ORIENT); } private void toWorldCoordinates(float[] worldCoord, float[] values, float[] rotationMatrix, int cnt, CMD.Data cmd){ float result[] = new float[3]; result = new Matrix(rotationMatrix).multipleV(values); worldCoord[0] = result[0]; worldCoord[1] = result[1]; worldCoord[2] = result[2]; if(cnt%32==0){ displayData(cmd); } } private void displayData(CMD.Data dataType){ //Util.debug("Sensors: displayData"); float data[] = new float[4]; data[0] = dataType.ordinal(); switch(dataType) { case ACC: data[1] = accValues[0]; data[2] = accValues[1]; data[3] = accValues[2]; break; case MAG: data[1] = magValues[0]; data[2] = magValues[1]; data[3] = magValues[2]; break; case ORIENT: data[1] = orientValues[0]; data[2] = orientValues[1]; data[3] = orientValues[2]; break; case WRDACC: data[1] = accWorldCoord[0]; data[2] = accWorldCoord[1]; data[3] = accWorldCoord[2]; break; case STPCNT: data[1] = stepCount; data[2] = calculatedGravity; break; case POSITION: data[1] = currentPosX; data[2] = currentPosY; break; case GYR: data[1] = gyrValues[0]; data[2] = gyrValues[1]; data[3] = gyrValues[2]; break; case HEADING: data[1] = currentHeading; break; case WRDGYR: data[1] = gyrWorldCoord[0]; break; case STABLE: data[1] = ifStable?1:0; break; default: Util.debug("Bad Data Sending Command!"); return; } this.tellMain(CMD.Response.SHOWDATA, data); } private int everyAveragedBuffer(float[] buffer, float[] values, int length, int cnt, int cycle, CMD.Data cmd){ for(int i=0; i buffer, float[] sum, float[] avg, float[] values, int length, int cnt, int cycle, CMD.Data cmd){ if (buffer==null||buffer.size()