Time for action – type of step (walking, jogging, and running) detection using the accelerometer sensor

This section is dedicated to the implementation of the type of step detection algorithm discussed in the previous section. Our implementation for the algorithm will consist of four major components of android: first is android service, which will stay in the background, second is a set of two threads using the  ScheduledExecutorService, and third is the activity to show the pedometer application data. The last component is the SQLite database to store the steps' information. The following is the high-level class diagram of the application; we will discuss each class in detail in their own sections. Now, let's explore the first component in detail:

Time for action – type of step (walking, jogging, and running) detection using the accelerometer sensor

  1. The first component of the algorithm is the StepsTrackerService service, which will remain in the background and provide a container for execution. Inside this service, we create the StepDetectorListener and AccelerometerListener classes and implement them with the SensorEventListener interface so that they can receive the sensor events. In the onCreate() method of the service, we initiate SensorManager and the step detector sensor object after checking its availability. We register the StepDetectorListener with SensorManager as soon as the service is created. As discussed earlier, in order to make the algorithm battery and CPU efficient, we will only register the accelerometer listener and start processing the data when any step is detected using the step detector sensor.

    Hence, we only create the accelerometer Sensor object in the onCreate() method and wait for any step detection before creating the object of the AccelerometerListener and registering it with the SensorManager. We also create the object of the StepsTrackerDBHelper class, which is our SQLite database utility for handling all the database operations:

            public class StepsTrackerService extends Service{ 
     
              private SensorManager mSensorManager; 
              private Sensor mStepDetectorSensor; 
              private Sensor mAccelerometerSensor; 
              private AccelerometerListener 
              mAccelerometerListener; 
              private StepDetectorListener mStepDetectorListener; 
              StepsTrackerDBHelper mStepsTrackerDBHelper;
    
              @Override 
              public void onCreate() { 
                super.onCreate(); 
     
                mSensorManager = (SensorManager) 
                this.getSystemService(Context.SENSOR_SERVICE); 
                if(mSensorManager.getDefaultSensor
                (Sensor.TYPE_STEP_DETECTOR) != null) 
                { 
                  mStepDetectorSensor = 
                  mSensorManager.getDefaultSensor
                  (Sensor.TYPE_STEP_DETECTOR); 
                  mStepDetectorListener = new 
                  StepDetectorListener(); 
                  mSensorManager.registerListener
                  (mStepDetectorListener, mStepDetectorSensor, 
                  SensorManager.SENSOR_DELAY_FASTEST); 
                } 
                if(mSensorManager.getDefaultSensor
                (Sensor.TYPE_ACCELEROMETER) != null) 
                { 
                  mAccelerometerSensor = 
                  mSensorManager.getDefaultSensor
                  (Sensor.TYPE_ACCELEROMETER); 
                } 
                mStepsTrackerDBHelper = new 
                StepsTrackerDBHelper(this); 
              }   
    
  2. The second component that plays an important part in developing the algorithm is a set of two threads using the ScheduledExecutorService. It is the system level Android thread executor service that provides the required number of threads from its available pool of threads for execution. We initiate the object of the ScheduledExecutorService with only two threads, using the Executors.newScheduledThreadPool(2) method, and we use it to schedule the execution of our two threads. The ScheduledFuture class instance is used to store the reference of the scheduled thread obtained by scheduling it using the ScheduledExecutorService.schedule()method.

    The ScheduledFuture instance will also be used to cancel the execution of a scheduled thread using the ScheduledFuture.cancel() method. For the step detection algorithm, we use two threads: first is the UnregisterAcceleromterTask thread, which implements the runnable interface and is responsible for unregistering the accelerometer sensor, and second is ProcessDataTask, which also implements the runnable interface and is used to process the accelerometer data periodically. We use the mScheduledUnregisterAccelerometerTask instance of the ScheduledFuture class to store the reference of scheduled execution of the UnregisterAcceleromterTask thread, and similarly, we store the reference of the scheduled execution of the ProcessDataTask thread in mScheduledProcessDataTask, which is also the instance of the ScheduledFuture class.

    As the first part to the logic, we want to only register the accelerometer listener and start processing the accelerometer data when we detect any steps and this is achieved inside the onSensorChanged() method of the StepDetectorListener class, where we create the object of AccelerometerListener and register it with SensorManager. We only register AccelerometerListener if it is has not been registered earlier, and we check this by using a Boolean variable called isAccelerometerRegistered. When the AccelerometerListener is registered, we make it true and when it unregisters inside the run() method UnregisterAcceleromterTask thread, we make it false.

    Before registering, we also make sure that the mAccelerometerSensor is not null, that is, the accelerometer sensor is present on the device. Now, the second part of the logic is to unregister the accelerometer listener and stop processing the accelerometer data when no steps have been detected for the last 20 seconds. This is achieved by scheduling the execution of a new instance of the UnregisterAcceleromterTask thread after 20 seconds every time that a new step is detected, and cancelling the last scheduled execution of instance of the UnregisterAcceleromterTask thread, if present. This keeps on postponing the un-registration of the accelerometer listener until no step is detected for the last 20 seconds.

    We use the isScheduleUnregistered Boolean variable to check whether there is any old scheduled execution instances of the UnregisterAcceleromterTask thread pending for execution; if yes, then we cancel its execution using the cancel() method of the mScheduledUnregisterAccelerometerTask. As soon as the UnregisterAcceleromterTask thread is scheduled for future execution, we make the isScheduleUnregistered Boolean variable true, and after the successful execution of the UnregisterAcceleromterTask thread, we make it false. Inside the run() method of the UnregisterAcceleromterTask thread, we also stop the processing of accelerometer data by stopping the execution of the ProcessDataTask thread using the cancel() method of mScheduledProcessDataTask, which stores its scheduled execution reference:

              ScheduledExecutorService mScheduledExecutorService = 
              Executors.newScheduledThreadPool(2); 
              private ScheduledFuture 
              mScheduledUnregisterAccelerometerTask; 
              private ScheduledFuture mScheduledProcessDataTask; 
              private UnregisterAcceleromterTask 
              mUnregisterAcceleromterTask; 
              private ProcessDataTask mProcessDataTask; 
              private boolean isScheduleUnregistered = false; 
              private boolean isAccelerometerRegistered = false; 
     
              class StepDetectorListener implements 
              SensorEventListener{ 
     
                @Override 
                public void onSensorChanged(SensorEvent event) { 
     
                  if(!isAccelerometerRegistered && 
                  mAccelerometerSensor!=null) 
                  { 
                    mAccelerometerListener = new 
                    AccelerometerListener(); 
                    mSensorManager.registerListener
                    (mAccelerometerListener, mAccelerometerSensor, 
                    SensorManager.SENSOR_DELAY_FASTEST); 
                    isAccelerometerRegistered = true; 
                  } 
                  if(isScheduleUnregistered) 
                  { 
                    mScheduledUnregisterAccelerometerTask
                    .cancel(true); 
                  } 
                  mUnregisterAcceleromterTask = new 
                  UnregisterAcceleromterTask(); 
                  mScheduledUnregisterAccelerometerTask = 
                  mScheduledExecutorService.schedule
                  (mUnregisterAcceleromterTask, 20000, 
                  TimeUnit.MILLISECONDS); 
                  isScheduleUnregistered = true; 
                } 
              } 
     
              class UnregisterAcceleromterTask implements Runnable 
              { 
     
                @Override 
                public void run() { 
                  isAccelerometerRegistered = false; 
                  mSensorManager.unregisterListener
                  (mAccelerometerListener); 
                  isScheduleUnregistered = false; 
                  mScheduledProcessDataTask.cancel(false); 
                } 
              } 
    
  3. We create an AccelerometerData (POJO) class to hold the accelerometer data together. It has the float xy, and z variables to hold the acceleration values on the xy, and z axes. A double value holds the square root of the sum of the squares of the acceleration acting on all the three axes, and long time is used for storing the timestamp of the event. The Boolean variable isTruePeak is initiated to true and is helpful in finding the peak values corresponding to each step. (More on this in the next section). We create four instances of ArrayList on the AccelerometerData objects to process the accelerometer data.

    Each of them will be discussed as we use them. In the AccelerometerListener constructor, we schedule the periodic execution of the ProcessDataTask thread to execute every 10 seconds with an initial delay of 10 seconds using the scheduleWithFixedDelay() method of ScheduledExecutorService. The ProcessDataTask thread contains all the logic needed to process the raw accelerometer data and find the type of steps through it. Going back to AccelerometerListener, it's only responsible for two tasks: the first is scheduling the period execution of the ProcessDataTask thread, and the second is collecting the raw accelerometer data in the onSensorChanged() method and storing it in mAccelerometerDataList, which is the ArrayList of the AccelerometerData objects.

    Now, let's discuss the ProcessDataTask thread in detail, which executes every 10 seconds and processes the last 10 seconds of accelerometer data, which is stored in the mAccelerometerDataList. Inside the run() method of the ProcessDataTask thread, the first task we do is copy all the elements of the mAccelerometerDataList into a new ArrayList of AccelerometerData objects called mRawDataList. After coping, we empty the mAccelerometerDataList by calling its clear() method. This is done to avoid the concurrent access of mAccelerometerDataList from the OnSensorChanged() method of AccelerometerListener, which tries to add values to it, and from the ProcessDataTask thread's run() method, which reads values from it.

    After this step, we calculate the square root of the sum of the squares of the acceleration on the xy, and z axes and store it in the double value variable using a for loop, and we also update the SensorEvent timestamp to the current epoch timestamp in milliseconds in the same loop. By default, the timestamp of any SensorEvent is the time in nanoseconds from the system's boot time and not the epoch time (also called the Unix timestamp). A simple way to convert it to epoch time is to first divide the SensorEvent timestamp by 1,000,000 (to convert from nanoseconds to milliseconds) and then add the offset value. The offset value is the time in milliseconds from the start of the epoch time until the phone boot up time, and this can be calculated by subtracting the SystemClock.elapsedtime() from System.currentTimeMillis(). The remaining steps are discussed in the next section:

              class AccelerometerData { 
                public double value; 
                public float x; 
                public float y; 
                public float z; 
                public long time; 
                public boolean isTruePeak = true; 
              }
    
              private long timeOffsetValue; 
              ArrayList<AccelerometerData> mAccelerometerDataList 
              = new ArrayList<AccelerometerData>(); 
              ArrayList<AccelerometerData> mRawDataList = new 
              ArrayList<AccelerometerData>();  
              ArrayList<AccelerometerData> 
              mAboveThresholdValuesList = new 
              ArrayList<AccelerometerData>();  
              ArrayList<AccelerometerData> mHighestPeakList 
              = new ArrayList<AccelerometerData>();
    
              class AccelerometerListener implements 
              SensorEventListener{ 
     
                public AccelerometerListener() 
                { 
                  mProcessDataTask = new ProcessDataTask(); 
                  mScheduledProcessDataTask = 
                  mScheduledExecutorService.scheduleWithFixedDelay
                  (mProcessDataTask, 10000, 10000, 
                  TimeUnit.MILLISECONDS); 
                } 
     
                @Override 
                public void onSensorChanged(SensorEvent event) { 
     
                  AccelerometerData mAccelerometerData = new 
                  AccelerometerData(); 
                  mAccelerometerData.x = event.values[0]; 
                  mAccelerometerData.y = event.values[1]; 
                  mAccelerometerData.z = event.values[2]; 
                  mAccelerometerData.time = event.timestamp; 
                  mAccelerometerDataList.add(mAccelerometerData); 
                } 
              } 
     
              class ProcessDataTask implements Runnable { 
     
                @Override 
                public void run() { 
           
                //Copy accelerometer data from main sensor array 
                in separate array for processing 
                mRawDataList.addAll(mAccelerometerDataList); 
                mAccelerometerDataList.clear(); 
     
                //Calculating the magnitude (Square root of sum of 
                squares of x, y, z) & converting time from nano 
                seconds from boot time to epoc time 
                timeOffsetValue = System.currentTimeMillis() - 
                SystemClock.elapsedRealtime(); 
                int dataSize = mRawDataList.size();
     
                for (int i = 0; i < dataSize; i++) { 
     
                  mRawDataList.get(i).value = 
                  Math.sqrt(Math.pow(mRawDataList.get(i).x, 2) + 
                  Math.pow(mRawDataList.get(i).y, 2) + 
                  Math.pow(mRawDataList.get(i).z, 2)); 
                  mRawDataList.get(i).time = 
                  (mRawDataList.get(i).time/1000000L) + 
                  timeOffsetValue; 
                } 
     
                //Calculating the High Peaks 
                findHighPeaks(); 
                //Remove high peaks close to each other which are 
                within range of 0.4 seconds 
                removeClosePeaks(); 
                //Find the type of step (Running, jogging, 
                walking) & store in Database 
                findStepTypeAndStoreInDB(); 
     
                mRawDataList.clear(); 
                mAboveThresholdValuesList.clear(); 
                mHighestPeakList.clear(); 
              }   
    
  4. Until now, we have just been preparing the infrastructure required to write the step detection algorithm. In this section, we will write the core logic of the algorithm. As derived in the analysis of the step detection algorithm, we have to count the highest peaks above the walking threshold value, which corresponds to the highest acceleration registered on the accelerometer sensor when an individual step hits the ground. We can easily observe in the following graph that for every individual step, there is a corresponding highest peak, which is coupled with a lowest trough value. The algorithm simply boils down to counting the unique highest peak values (marked with a green circle), which are above the threshold value (plotted with the orange line), and coupling them with the lowest trough values (marked with a red circle).

    In the algorithm, we also have to deal with the false positive values (marked with a yellow circle). These false positive values are registered very close to the highest peak values.

    Time for action – type of step (walking, jogging, and running) detection using the accelerometer sensor

    The first step in the algorithm is to find all the values that are above the walking threshold value and store them in mAboveThresholdValuesListArrayList of the AccelerometerData objects. We do this inside a for loop using the if condition, which is executed over the entire 10 seconds of accelerometer data stored in mRawDataList. The second step in the algorithm is to find all the potential highest peak values from the preceding threshold values. This is achieved by only adding above the threshold values in the mAboveThresholdValuesList until any value lower than the threshold value is received. As soon as any value lower than the threshold is found, we take the values collected thus far in the mAboveThresholdValuesList and sort them to find the highest potential peak among the values collected until that point that are above the threshold.

    We use a custom DataSorter collection comparator class to sort the mAboveThresholdValuesList values. Now, this highest potential peak found among the above threshold values could be either a false positive (marked with a yellow circle in the figure), or a true highest peak value (marked with a green circle) corresponding to one step. We save this highest potential peak value in a separate mHighestPeakListArrayList. Before moving to the next group of above-threshold values, we clear all the data from the mAboveThresholdValuesListArrayList. After executing the for loop over the entire mRawDataList, we will get all the highest potential peaks, which consist of both false positive peak values (marked with the yellow circle in the figure) and the true highest peak values (marked with the green circle) in the mHighestPeakList.

    Now, the third step of the algorithm is to filter out the false positive peak values from the true highest peak values. We know from the analysis of the data that these false positive values are pretty close to the true highest peak values, and also, their magnitude is less than the true highest peak's magnitude. We use the same logic in for loop to filter out the false positive values. We assume, by default, that all the values collected in the mHighestPeakList are true peaks. We do this using the Boolean variable, isTruePeak, in the AccelerometerData model class and initializing it to true. Inside for loop, we check the time difference between the two consecutive values; if the time difference is less than 0.4 seconds, then we assume that they are pretty close to each other and one of them is a false positive, and after this, we further compare their magnitude of length of vector, and whichever is smaller, we mark that value as a false positive.

    After executing the for loop over the entire mHighestPeakList, we are able to filter out all the false positive values that have a lower magnitude and are near the highest peak values by setting their isTruePeak Boolean variable to false. Now, the final step in the algorithm is to detect the type of steps and save it in the database. This is done using a for loop over the mHighestPeakList inside the findStepTypeAndStoreInDB() method. Each type of step (running, jogging, and walking) is assigned a peak constant threshold value, which we have derived from the experimental data. For our algorithm, we are using the running peak value as 30, jogging peak value as 25, and walking peak value as 15. We execute a for loop only on those values of the mHighestPeakList that have the isTruePeak Boolean value as true. We categorize the elements of the mHighestPeakList by comparing the magnitude (length of vector) with the peak values of running, jogging, and walking, and also, we use the mStepsTrackerDBHelper.createStepsEntry() method to save the type and time of the step in the SQLite database:

    public void findHighPeaks(){ 
          //Calculating the High Peaks 
          boolean isAboveMeanLastValueTrue = false; 
          int dataSize = mRawDataList.size(); 
          for (int i = 0; i < dataSize; i++)  
          { 
            if(mRawDataList.get(i).value > WALKINGPEAK) 
            { 
                mAboveThresholdValuesList.add
                (mRawDataList.get(i)); 
                isAboveMeanLastValueTrue = false; 
            } 
            else 
            { 
              if(!isAboveMeanLastValueTrue && 
              mAboveThresholdValuesList.size()>0) 
              { 
                Collections.sort(mAboveThresholdValuesList,
                new DataSorter()); 
                mHighestPeakList.add(mAboveThresholdValuesList
                .get(mAboveThresholdValuesList.size()-1)); 
                mAboveThresholdValuesList.clear(); 
              } 
              isAboveMeanLastValueTrue = true; 
            } 
          } 
        } 
     
        public void removeClosePeaks() 
        { 
          int dataSize = mHighestPeakList.size(); 
            for (int i = 0; i < dataSize-1; i++) { 
     
              if(mHighestPeakList.get(i).isTruePeak) 
              { 
                if(mHighestPeakList.get(i+1).time - 
                mHighestPeakList.get(i).time < 400) 
              { 
                if(mHighestPeakList.get(i+1).value > 
                mHighestPeakList.get(i).value) 
                { 
                  mHighestPeakList.get(i).isTruePeak = false; 
                } 
                else 
                { 
                  mHighestPeakList.get(i+1).isTruePeak = false; 
                } 
              } 
            } 
          } 
        } 
     
        public void findStepTypeAndStoreInDB() 
        { 
          int size = mHighestPeakList.size(); 
          for (int i = 0; i < size; i++)  
          { 
            if(mHighestPeakList.get(i).isTruePeak) 
            { 
              if(mHighestPeakList.get(i).value > RUNNINGPEAK) 
              { 
                mStepsTrackerDBHelper.createStepsEntry
                (mHighestPeakList.get(i).time, RUNNING); 
              } 
              else 
              { 
                if(mHighestPeakList.get(i).value > JOGGINGPEAK) 
                { 
                  mStepsTrackerDBHelper.createStepsEntry
                  (mHighestPeakList.get(i).time, JOGGING); 
                } 
                else 
                { 
                  mStepsTrackerDBHelper.createStepsEntry
                  (mHighestPeakList.get(i).time, WALKING); 
                } 
              } 
            } 
          } 
        } 
     
        public class DataSorter implements 
        Comparator<AccelerometerData>{ 
     
          public int compare(AccelerometerData obj1, 
          AccelerometerData obj2){ 
            int returnVal = 0; 
     
            if(obj1.value < obj2.value){ 
              returnVal =  -1; 
            }else if(obj1.value > obj2.value){ 
              returnVal =  1; 
            } 
            return returnVal; 
          } 
        } 
    
  5. We create a StepsTrackerDBHelper class to handle all the database operations and extend it from the Android SQLite built in the SQLiteOpenHelper utility class, which provides access to the database. Inside the class, we create a database called StepsTrackerDatabase, and it has only one table StepsTrackerSummary, which consists of the following four columns (idsteptypesteptime, and stepdate):
    • The first column, id, is the unique integer identifier for each row of the table and is incremented automatically on the creation of every new row.
    • The second column, steptype, is used to store the type of step (running, jogging, or walking).
    • The third column is steptime, which is used to store the time in milliseconds.
    • The fourth column is stepdate, which is used to store the date in the mm/dd/yyyy string format.

    This class has a createStepsEntry() method that saves every step's information (the type, time, and date of every step) in a new row of the table. This method is called from StepTrackerService every time a new step is processed. There is another method of this class called getStepsByDate(), which is responsible for reading the total count of each type of step taken on a particular date, provided as the input parameter. This getStepsByDate()is called from the CustomAlgoResultsActivity, to display the pedometer data matrix. More on this in the next section:

    public class StepsTrackerDBHelper extends SQLiteOpenHelper 
    { 
      private static final String DATABASE_NAME = 
      "StepsTrackerDatabase"; 
      private static final String TABLE_STEPS_SUMMARY = 
      "StepsTrackerSummary"; 
      private static final String ID = "id"; 
      private static final String STEP_TYPE = "steptype"; 
      private static final String STEP_TIME = "steptime";//time is 
      in milliseconds Epoch Time 
      private static final String STEP_DATE = "stepdate";//Date 
      format is mm/dd/yyyy 
     
      private static final String CREATE_TABLE_STEPS_SUMMARY = 
      "CREATE TABLE " + TABLE_STEPS_SUMMARY + "(" + ID + " INTEGER 
      PRIMARY KEY AUTOINCREMENT," + STEP_DATE + " TEXT,"+ 
      STEP_TIME + " INTEGER,"+ STEP_TYPE + " TEXT"+")"; 
     
      public boolean createStepsEntry(long timeStamp, int 
      stepType) 
      { 
     
        boolean createSuccessful = false; 
        Calendar mCalendar = Calendar.getInstance();  
        String todayDate = 
        String.valueOf(mCalendar.get(Calendar.MONTH)+1)+"/" + 
        String.valueOf(mCalendar.get(Calendar.DAY_OF_MONTH))+"/" + 
        String.valueOf(mCalendar.get(Calendar.YEAR)); 
        try { 
          SQLiteDatabase db = this.getWritableDatabase(); 
          ContentValues values = new ContentValues(); 
          values.put(STEP_TIME, timeStamp); 
          values.put(STEP_DATE, todayDate); 
          values.put(STEP_TYPE, stepType); 
          long row = db.insert(TABLE_STEPS_SUMMARY, null, values); 
          if(row!=-1) 
          { 
            createSuccessful = true; 
          } 
          db.close(); 
     
        } catch (Exception e) { 
          e.printStackTrace(); 
        } 
        return createSuccessful; 
      } 
     
      public int [] getStepsByDate(String date) 
      { 
        int stepType[] = new int[3]; 
        String selectQuery = "SELECT " + STEP_TYPE + " FROM " + 
        TABLE_STEPS_SUMMARY +" WHERE " + STEP_DATE +" = '"+
        date + "'"; 
        try { 
          SQLiteDatabase db = this.getReadableDatabase(); 
          Cursor c = db.rawQuery(selectQuery, null); 
          if (c.moveToFirst()) { 
            do { 
              switch(c.getInt((c.getColumnIndex(STEP_TYPE)))) 
              { 
              case WALKING: ++stepType[0]; 
              break; 
              case JOGGING: ++stepType[1]; 
              break; 
              case RUNNING: ++stepType[2]; 
              break; 
              } 
            } while (c.moveToNext()); 
          } 
          db.close(); 
        } catch (Exception e) { 
          e.printStackTrace(); 
        } 
        return stepType; 
      } 
     
    } 
    
  6. The last component of our application is to derive and display the pedometer data matrix (total steps, distance, duration, average speed, average step frequency, calories burned, and type of steps) on the user interface. We do this inside the CustomAlgoResultsActivity class. In the onCreate() method of the activity, we initiate seven instances of TextView to display the seven data matrix points (total steps, distance, duration, average speed, average step frequency, calorie burned, and type of steps). We also initiate the object of the StepsTrackerDBHelper class, which is used to read the steps' data from the database. The calculateDataMatrix() method, which is called from the onCreate() method and is responsible for calculating the data matrix and assigning the values to respective TextView on the user interface. Now, let's discuss how we can calculate each data point in the data matrix.
  • The total numbers of steps are calculated by adding all the three types of steps (running, jogging, and walking) for a particular date. For our example, we use the current date for all the calculations. We use the getStepsByDate()method of the StepsTrackerDBHelper class to get the number of each type of step.
  • The total number of each type of step is directly given by the getStepsByDate()method of the StepsTrackerDBHelper class.which returns the total number of each type of step in an integer array of capacity 3.
  • The total distance is calculated by adding the distance travelled by all the three types of steps (running, jogging, and walking). With our experimental data, we found that walking a single step covers 0.5 meters, jogging a single step covers 1 meter, and running a single step covers close to 1.5 meters. Hence, we multiply the distance covered by each type of step with their respective numbers to get the total distance travelled.
  • The total duration is also calculated by adding the time taken by all the three types of steps (running, jogging, and walking), and with the experimental sample data, we found that on average, a single walking step takes 1 second, a single jogging step takes 0.75 second, and a single running step takes 0.5 second. We multiply their individual step timings with the number of each type of step and add them together to get the total duration.
  • We found that, on average, 1 calorie is burned by walking 20 steps (from various health resources such as  http://www.livestrong.com/article/320124-how-many-calories-does-the-average-person-use-per-step/). Similarly, 1 calorie is burned by jogging 10 steps, and for running, 1 calorie is burned by five steps. Now, by multiplying the respective number of each step with the inverse of each number of steps required to burn 1 calorie, will give us the total number of calories burned.
  • The average speed is calculated by dividing the total distance travelled by the total duration.
  • The average step frequency is calculated by dividing the total number of steps taken by the total duration in minutes:
         public class CustomAlgoResultsActivity extends
         Activity{
           private TextView mTotalStepsTextView;
           private TextView mTotalDistanceTextView;
           private TextView mTotalDurationTextView;
           private TextView mAverageSpeedTextView;
           private TextView mAveragFrequencyTextView;
           private TextView mTotalCalorieBurnedTextView;
           private TextView mPhysicalActivityTypeTextView;
           StepsTrackerDBHelper mStepsTrackerDBHelper;
          @Override
          protected void onCreate(Bundle savedInstanceState)
          {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.capability_layout);
            mTotalStepsTextView =
            (TextView)findViewById(R.id.total_steps);
            mTotalDistanceTextView =
            (TextView)findViewById(R.id.total_distance);
            mTotalDurationTextView =
            (TextView)findViewById(R.id.total_duration);
            mAverageSpeedTextView =
            (TextView)findViewById(R.id.average_speed);
            mAveragFrequencyTextView =
            (TextView)findViewById(R.id.average_frequency);
            mTotalCalorieBurnedTextView =
            (TextView)findViewById(R.id.calories_burned);
            mPhysicalActivityTypeTextView =
            (TextView)findViewById
            (R.id.physical_activitytype);
            mStepsTrackerDBHelper = new
            StepsTrackerDBHelper(this);
            Intent mStepsAnalysisIntent = new
            Intent(getApplicationContext(),
            StepsTrackerService.class);
            startService(mStepsAnalysisIntent);
            calculateDataMatrix();
          }
          public void calculateDataMatrix()
          {
            Calendar mCalendar = Calendar.getInstance();
            String todayDate =
            String.valueOf(mCalendar.get(Calendar.MONTH))
            +"/" + String.valueOf(mCalendar.get
            (Calendar.DAY_OF_MONTH)+1) +"/"+ String.valueOf
            (mCalendar.get(Calendar.YEAR));
            int stepType[] =
            mStepsTrackerDBHelper.getStepsByDate(todayDate);
            int walkingSteps = stepType[0];
            int joggingSteps = stepType[1];
            int runningSteps = stepType[2];
            //Calculating total steps
            int totalStepTaken = walkingSteps + joggingSteps
            + runningSteps;
            mTotalStepsTextView.setText
            (String.valueOf(totalStepTaken)+ " Steps");
            //Calculating total distance travelled
            float totalDistance = walkingSteps*0.5f +
            joggingSteps * 1.0f + runningSteps * 1.5f;
            mTotalDistanceTextView.setText
            (String.valueOf(totalDistance)+" meters");
            //Calculating total duration
            float totalDuration = walkingSteps*1.0f +
            joggingSteps * 0.75f + runningSteps * 0.5f;
            float hours = totalDuration / 3600;
            float minutes = (totalDuration % 3600) / 60;
            float seconds = totalDuration % 60;
            mTotalDurationTextView.setText
            (String.format("%.0f",hours) + " hrs " +
            String.format("%.0f",minutes) + " mins " +
            String.format("%.0f",seconds)+ " secs");
            //Calculating average speed
            if(totalDistance>0)
            {
              mAverageSpeedTextView.setText
              (String.format("%.2f",
              totalDistance/totalDuration)+" meter per
              seconds");
            }
            else
            {
              mAverageSpeedTextView.setText
              ("0 meter per seconds");
            }
            //Calculating average step frequency
            if(totalStepTaken>0)
            {
              mAveragFrequencyTextView.setText
              (String.format("%.0f",totalStepTaken/minutes)+"
              steps per minute");
            }
            else
            {
              mAveragFrequencyTextView.setText
              ("0 steps per minute");
            }
            //Calculating total calories burned
            float totalCaloriesBurned = walkingSteps * 0.05f
            + joggingSteps * 0.1f + runningSteps * 0.2f;
            mTotalCalorieBurnedTextView.setText
            (String.format("%.0f",totalCaloriesBurned)+"
            Calories");

            //Calculating type of physical activity
            mPhysicalActivityTypeTextView.setText
            (String.valueOf(walkingSteps) + " Walking Steps "
            + "
"+String.valueOf(joggingSteps) + " Jogging
            Steps " + "
"+String.valueOf(runningSteps)+ "
            Running Steps");
          }

The following is the screenshot of the pedometer app showing the pedometer data matrix (total steps, distance, duration, average speed, average step frequency, calories burned, and type of steps (walking, jogging, running)) active since the app was installed on the phone:

Time for action – type of step (walking, jogging, and running) detection using the accelerometer sensor

What just happened?

We did some really interesting developmental work for this pedometer application. First, we analyzed the accelerometer data to figure out the common pattern in the signatures of the walking, jogging, and running accelerometer sensor data. After that, we developed the type of step detection algorithm using this analysis. We also figured out how to use threads inside the service to process the accelerometer data in the background, and how to combine the step detector sensor with the accelerometer sensor to achieve battery optimization. We used our experimental data to derive the pedometer data matrix (total steps, distance, duration, average speed, average step frequency, calories burned, and type of steps) using the total number of each type of step detected by our algorithm.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset