1 package edu.ucla.iBeaconNav;
3 import java.util.LinkedList;
4 import java.util.Queue;
6 import android.app.Notification;
7 import android.app.Activity;
8 import android.app.Service;
9 import android.content.Context;
10 import android.content.Intent;
11 import android.hardware.Sensor;
12 import android.hardware.SensorEvent;
13 import android.hardware.SensorEventListener;
14 import android.hardware.SensorManager;
15 import android.os.Handler;
16 import android.os.IBinder;
17 import android.os.Message;
18 import android.os.Messenger;
19 import android.os.SystemClock;
20 import android.widget.EditText;
21 import edu.ucla.iBeaconNav.R;
23 public class Sensors extends Service implements SensorEventListener
26 private SensorManager sensorManager;
27 private Sensor accSensor;
28 private Sensor grvSensor;
29 private Sensor magSensor;
30 private Sensor gyrSensor;
32 private Messenger messenger;
33 private boolean active;
34 private long lastTime_ms = 0;
35 private float snsInterval_ms = 0;
36 private long lastTime_ns = 0;
37 private long snsInterval_ns = 0;
41 private float[] accValues = new float[3];
42 private float[] magValues = new float[3];
43 private float[] gyrValues = new float[3];
44 private float[] rotationMatrix_R = new float[9];
45 private float[] rtMatrixStable_R = new float[9];
46 private float[] rotationMatrix_I = new float[9];
47 private float[] orientValues_rd = new float[3];
48 private float[] orientValues = new float[3];
49 private float[] accWorldCoord = {0,0,0};
50 private float[] gyrWorldCoord = {0,0,0};
53 /* Auxiliary Variables for Sensor Processing */
55 private final int cycle = 32;
57 private LinkedList<Float> accBuffers = new LinkedList<Float>();
58 private LinkedList<Float> magBuffers = new LinkedList<Float>();
59 private LinkedList<Float> gyrBuffers = new LinkedList<Float>();
60 private LinkedList<Float> orientBuffers = new LinkedList<Float>();
61 private LinkedList<Float> rtMtrxBuffers = new LinkedList<Float>();
62 private float[] data = new float[4];
63 private float[] accSum = {0,0,0};
64 private float[] accAvg = {0,0,0};
65 private float[] magSum = {0,0,0};
66 private float[] gyrSum = {0,0,0};
67 private float[] gyrOffset = {0,0,0};
68 private float[] orientSum = {0,0,0};
69 private float[] rtMtrxSum = {0,0,0,
72 private float[] accBuffer = {0,0,0};
73 private float[] magBuffer = {0,0,0};
74 private float[] orientBuffer= {0,0,0};
76 private int accCnt = 0;
77 private int magCnt = 0;
78 private int gyrCnt = 0;
79 private int orientCnt = 0;
80 private int rtMtrxCnt = 0;
82 private boolean ifGyrOffset = false;
83 private boolean ifGrvOffset = false;
84 private boolean ifStable = false;
86 private final float EPSILON = (float)0.01;
88 private float accValueTotal = 0;
89 private float calculatedGravity = SensorManager.GRAVITY_EARTH;
90 private float gravityRef = 0;
91 private float gyroscopeRef = 0;
92 private boolean ifSetGrvRef = false;
95 /* Position Related Stuff */
96 //private float startPosX = 0;
97 //private float startPos = 0;
98 private float currentPosX = 0;
99 private float currentPosY = 0;
100 // rotate around gravity direction, positive when counterclock-wise, initially align with user's first step
101 private float currentHeading = 0;
102 private float headingOffset = 0;
103 private float stepLength = (float)0.5; // in m
104 private int stepCount = 0;
105 private boolean stepStart = false;
109 /* Private methods */
110 private void tellMain(CMD.Response cmd, Object value)
113 android.os.Message msg = android.os.Message.obtain();
114 msg.what = cmd.ordinal();
116 this.messenger.send(msg);
117 } catch (Exception e) {
118 Util.debug("Sensors: error sending message", e);
122 private void notify(String text, int icon)
125 this.tellMain(CMD.Response.NOTIFY, text);
128 //Notification note = new Notification(icon, null, 0);
129 //Intent intent = new Intent(this, Main.class);
130 //PendingIntent pend = PendingIntent.getActivity(this, 0, intent, 0);
131 //note.setLatestEventInfo(this, "iBeaconNav!", text, pend);
132 //PendingIntent pend = PendingIntent.getActivity(this, 0, intent, 0);
134 Notification note = new Notification.Builder(this)
135 .setContentTitle("iBeaconNav!")
136 .setContentText("iBeaconNav!")
140 this.startForeground(1, note);
143 private void handle(CMD.Command cmd, Messenger mgr)
145 // Validate messenger
146 if (cmd != CMD.Command.REGISTER && mgr != null && mgr != this.messenger)
147 Util.debug("Sensors: handle - invalid messenger");
149 // Handle the command
151 // Setup communication with Main
153 Util.debug("Sensors: handle - register");
154 this.messenger = mgr;
157 // Create client thread
159 Util.debug("Sensors: handle - connect");
160 sensorManager.registerListener(this, accSensor, SensorManager.SENSOR_DELAY_FASTEST);
161 sensorManager.registerListener(this, grvSensor, SensorManager.SENSOR_DELAY_FASTEST);
162 sensorManager.registerListener(this, magSensor, SensorManager.SENSOR_DELAY_FASTEST);
163 sensorManager.registerListener(this, gyrSensor, SensorManager.SENSOR_DELAY_FASTEST);
166 // Stop client thread
168 Util.debug("Sensors: handle - register");
174 Util.debug("Sensors: handle - reset heading");
176 displayData(CMD.Data.HEADING);
181 Util.debug("Sensors: handle - reset distance");
184 displayData(CMD.Data.POSITION);
190 public boolean isRunning()
195 /* Service Methods */
197 public void onCreate()
199 Util.debug("Sensors: onCreate");
201 sensorManager = (SensorManager) getSystemService(Context.SENSOR_SERVICE);
202 accSensor = sensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
203 grvSensor = sensorManager.getDefaultSensor(Sensor.TYPE_GRAVITY);
204 magSensor = sensorManager.getDefaultSensor(Sensor.TYPE_MAGNETIC_FIELD);
205 gyrSensor = sensorManager.getDefaultSensor(Sensor.TYPE_GYROSCOPE);
209 public void onDestroy()
211 Util.debug("Sensors: onDestroy");
212 //this.handle(CMD.Response.DISCONNECT, null);
216 public int onStartCommand(Intent intent, int flags, int startId)
218 Util.debug("Sensors: onStartCommand");
219 int rval = super.onStartCommand(intent, flags, startId);
220 CMD.Command cmd = (CMD.Command)intent.getExtras().get("Command");
221 Messenger mgr = (Messenger)intent.getExtras().get("Messenger");
222 this.handle(cmd, mgr);
227 public IBinder onBind(Intent intent)
229 Util.debug("Sensors: onBind");
230 return messenger.getBinder();
232 class IncomingHandler extends Handler{
234 public void handleMessage(Message msg) {
235 //Util.debug("Sensors: MSG HANDLERRRRRRRRR");
236 super.handleMessage(msg);
241 public void onAccuracyChanged(Sensor sensor, int accuracy) {
242 // TODO Auto-generated method stub
243 Util.debug("Sensors: onAccuracyChanged");
248 public void onSensorChanged(SensorEvent event) {
249 // TODO Auto-generated method stub
250 switch(event.sensor.getType()){
251 case Sensor.TYPE_ACCELEROMETER:
253 System.arraycopy(event.values, 0, accValues, 0, 3);
254 //accCnt = everyAveragedBuffer(accBuffer, accValues, 3, accCnt, cycle, CMD.Data.ACC);
255 forwardAveragedBuffer(accBuffers, accSum, accAvg, accValues, 3, accCnt, cycle, CMD.Data.ACC);
256 toWorldCoordinates(accWorldCoord, accValues, rotationMatrix_R, accCnt, CMD.Data.WRDACC);
257 accValueTotal = accWorldCoord[2];
258 if (ifSetGrvRef && !ifGrvOffset && Math.abs((accValueTotal-gravityRef)/gravityRef)<0.05){
259 rtMatrixStable_R = rotationMatrix_R;
260 calculatedGravity = accValueTotal;
264 else if(!ifSetGrvRef && accCnt>400){
266 gravityRef = accValueTotal;
270 case Sensor.TYPE_MAGNETIC_FIELD:
272 System.arraycopy(event.values, 0, magValues, 0, 3);
273 //magCnt = everyAveragedBuffer(magBuffer, magValues, 3, magCnt, cycle, CMD.Data.MAG);
274 forwardAveragedBuffer(magBuffers, magSum, null, magValues, 3, magCnt, cycle, CMD.Data.MAG);
275 updateOrientation(); // This maybe useless
277 case Sensor.TYPE_GYROSCOPE:
279 float gyrTemp[] = new float[3];
280 long currentTime_ns = event.timestamp;
281 snsInterval_ns = currentTime_ns-lastTime_ns;
282 lastTime_ns = currentTime_ns;
284 int chkIntCnt = 8; //check Interval Count
285 if (gyrCnt%chkIntCnt==0){
286 //long currentTime_ms = SystemClock.elapsedRealtime();
287 //snsInterval_ms = (float)(currentTime_ms-lastTime_ms)/chkIntCnt;
289 ifStable = isStable(accBuffers, 3, cycle/2) && isStable(gyrBuffers, 3, cycle/2);
294 //lastTime_ms = SystemClock.elapsedRealtime();
295 //Util.debug("Interal in ms: "+Float.toString(snsInterval_ms));
298 System.arraycopy(event.values, 0, gyrValues, 0, 3);
299 System.arraycopy(gyrValues, 0, gyrTemp, 0, 3);
300 forwardAveragedBuffer(gyrBuffers, gyrSum, null, gyrValues, 3, gyrCnt, 2*cycle, CMD.Data.GYR);
301 System.arraycopy(gyrTemp, 0, gyrValues, 0, 3);
302 if (!ifGyrOffset && ifGrvOffset){
303 //Util.debug("[GYR1] Reset "+Float.toString(gyrValues[2]));
304 gyrOffset[0] = gyrValues[0]; //x
305 gyrOffset[1] = gyrValues[1]; //y
306 gyrOffset[2] = gyrValues[2]; //z
309 depleteOffset(gyrValues, gyrOffset, 3);
310 //cycleFloatArray(gyrValues, 3);
311 toWorldCoordinates(gyrWorldCoord, gyrValues, rtMatrixStable_R, gyrCnt, CMD.Data.WRDGYR);
313 float gyrZ = gyrWorldCoord[2];
314 //if (!ifStable && ifGyrOffset && Math.abs(gyrZ)>0.05){
316 //currentHeading+=gyrZ*snsInterval_ms/1000*180/Math.PI;
317 currentHeading += gyrZ*snsInterval_ns/1000000000*180/Math.PI;
319 displayData(CMD.Data.HEADING);
328 private void cycleFloatArray(float[] array, int length){
329 float temp = array[length-1];
330 for(int i=1; i<length; i++){
331 array[i] = array[i-1];
336 private void depleteOffset(float[] values, float[] offset, int len){
337 for(int i=0; i<len; i++){
338 values[i]-=offset[i];
342 private boolean isStable(LinkedList<Float> buffer, int length, int cycle){
343 int len = buffer.size();
344 float[] avrg = new float[length];
345 float[] devSum = new float[length];
347 for(int i=0; i<length; i++){
349 for (int j=0; j<cycle; j++){
350 avrg[length-1-i]+=buffer.get(len-1-j*length-i);
352 avrg[length-1-i]/=cycle;
354 for (int i=0; i<length; i++){
356 for(int j=0; j<cycle; j++){
357 devSum[length-1-i]+=(float) Math.pow(buffer.get(len-1-j*length-i)-avrg[length-1-i],2);
359 devSum[length-1-i] = (float) Math.sqrt(devSum[length-1-i]/cycle);
362 for (int i=0; i<length; i++){
367 //Util.debug("[DEV] unStable");
368 displayData(CMD.Data.STABLE);
371 //Util.debug("[DEV] Stable");
372 displayData(CMD.Data.STABLE);
379 private void processSensorInfo(){
380 displayData(CMD.Data.STPCNT);
381 float epsl = (float)0.6;
385 if(accValueTotal-calculatedGravity>epsl){
388 currentPosX += stepLength*Math.sin(currentHeading);
389 currentPosY += stepLength*Math.cos(currentHeading);
390 displayData(CMD.Data.POSITION);
395 if (calculatedGravity-accValueTotal>epsl ){
403 private void updateOrientation(){
404 if (accValues == null || magValues == null){
407 float R[] = new float[9];
408 float I[] = new float[9];
410 success = SensorManager.getRotationMatrix(R, I, accValues, magValues);
414 rotationMatrix_R = R;
415 rotationMatrix_I = I;
416 SensorManager.getOrientation(rotationMatrix_R, orientValues_rd);
419 orientValues[0] = (float) (orientValues_rd[0]*180/Math.PI);
420 orientValues[1] = (float) (orientValues_rd[1]*180/Math.PI);
421 orientValues[2] = (float) (orientValues_rd[2]*180/Math.PI);
423 //orientCnt=everyAveragedBuffer(orientBuffer, orientValues, 3, orientCnt, 2*cycle, CMD.Data.ORIENT);
424 forwardAveragedBuffer(rtMtrxBuffers,rtMtrxSum, null, rotationMatrix_R, 9, rtMtrxCnt, 2*cycle, null);
425 forwardAveragedBuffer(orientBuffers,orientSum, null, orientValues, 3, orientCnt, 2*cycle, CMD.Data.ORIENT);
426 //displayData(CMD.Data.ORIENT);
429 private void toWorldCoordinates(float[] worldCoord, float[] values, float[] rotationMatrix, int cnt, CMD.Data cmd){
430 float result[] = new float[3];
431 result = new Matrix(rotationMatrix).multipleV(values);
432 worldCoord[0] = result[0];
433 worldCoord[1] = result[1];
434 worldCoord[2] = result[2];
440 private void displayData(CMD.Data dataType){
441 //Util.debug("Sensors: displayData");
442 float data[] = new float[4];
443 data[0] = dataType.ordinal();
446 data[1] = accValues[0];
447 data[2] = accValues[1];
448 data[3] = accValues[2];
451 data[1] = magValues[0];
452 data[2] = magValues[1];
453 data[3] = magValues[2];
456 data[1] = orientValues[0];
457 data[2] = orientValues[1];
458 data[3] = orientValues[2];
461 data[1] = accWorldCoord[0];
462 data[2] = accWorldCoord[1];
463 data[3] = accWorldCoord[2];
467 data[2] = calculatedGravity;
470 data[1] = currentPosX;
471 data[2] = currentPosY;
474 data[1] = gyrValues[0];
475 data[2] = gyrValues[1];
476 data[3] = gyrValues[2];
479 data[1] = currentHeading;
482 data[1] = gyrWorldCoord[0];
485 data[1] = ifStable?1:0;
488 Util.debug("Bad Data Sending Command!");
492 this.tellMain(CMD.Response.SHOWDATA, data);
495 private int everyAveragedBuffer(float[] buffer, float[] values, int length, int cnt, int cycle, CMD.Data cmd){
496 for(int i=0; i<length; i++){
497 buffer[i]+=values[i];
501 for(int i=0; i<length; i++){
502 values[i] = buffer[i]/cycle;
510 private void forwardAveragedBuffer(LinkedList<Float> buffer, float[] sum, float[] avg,
511 float[] values, int length, int cnt, int cycle, CMD.Data cmd){
512 if (buffer==null||buffer.size()<cycle*length){
513 for(int i=0; i<length; i++){
514 buffer.addLast(values[i]);
516 values[i] = sum[i]/buffer.size();
520 float[] discarded = new float[length];
521 for(int i=0; i<length; i++){
522 discarded[i]= buffer.removeFirst();
523 buffer.addLast(values[i]);
524 sum[i]-=discarded[i];
526 values[i]=sum[i]/cycle;
530 for(int i=0; i<length; i++){
534 if (cnt%32==0 && cmd != null){
539 public void printMatrix(String s, Matrix m){
540 Util.debug("Sensor: ["+s+"] "+m.mValue[0]+" "+m.mValue[1]+" "+m.mValue[2]);
541 Util.debug("Sensor: " +m.mValue[3]+" "+m.mValue[4]+" "+m.mValue[5]);
542 Util.debug("Sensor: " +m.mValue[6]+" "+m.mValue[7]+" "+m.mValue[8]);
545 public void printVector(String s, float[] v){
546 Util.debug("Sensor: ["+s+"] "+v[0]+" "+v[1]+" "+v[2]);