I do a step counter.
I read in a one book a method, how to calculate user's steps using an accelerometer. I have Samsung Grand Premium (2014) which has only an accelerometer, so why I am designing the algorithm.
The problem is I need to visualize some results in a graphview. I created 3 classes: Accelerometer, Hub and MainScreen.
In the Accelerometer.class, I get the new value of the X,Y,Z in a certain period of time. After that I transmit data to Hub. The method IncomingData() receives data from Accelerometer. Then it has to analyze in the future, but now it just receives. This method also counts Samples. If we have 10 seconds window analysis, every point is known every 20ms. Therefore, the max of the samples is 10 000 ms / 20 ms = 500 samples. When the current sample == MaxSamples, I represent data in the graph calling the setDataGraph in the MainScreen activity.
Normally it works fine. But if I am waiting some of the time, f.e. 5 minutes, and using my own profiler I can see that the DrawTime increases, and the problem I don't know why, and how I can fix it. It works, but it is going to eat a lot of resources, and the graph starts lagging.
I want to use GraphView because it is the View, and it can be easily implemented in the layout.
If you have any idea please Help Me.
Thank you.
Accelerometer.class()
import android.hardware.Sensor;
import android.hardware.SensorEvent;
import android.hardware.SensorEventListener;
import android.hardware.SensorManager;
public class Accelerometer implements SensorEventListener{
private String ACCELEROMETER_NAME = "ACCELEROMETER";
private double[] XYZ = new double[3];
private SensorManager AclManager;
private Hub hub;
private MainScreen mainScreen;
private int ACCELEROMETER_DELAY = SensorManager.SENSOR_DELAY_GAME;//SENSOR_DELAY_GAME
Accelerometer(Hub hub, SensorManager Manager, Sensor SensorType){
this.hub = hub;
this.mainScreen = mainScreen;
AclManager = Manager;
AclManager.registerListener(this, SensorType, ACCELEROMETER_DELAY); //SENSOR_DELAY_GAME = 20 ms
}
@Override
public void onSensorChanged(SensorEvent event) {
if(event.sensor.getType() == Sensor.TYPE_ACCELEROMETER)
for(int i = 0 ; i < event.values.length; i++){
XYZ[i] = event.values[i];
}
Hub.IncomingData(ACCELEROMETER_NAME,XYZ,ACCELEROMETER_DELAY);
// mainScreen.test_view.setText(XYZ[0]+" "+XYZ[1]+" "+XYZ[2]+" ");
}
@Override
public void onAccuracyChanged(Sensor sensor, int accuracy) {
}
}
Hub.class()
import android.content.Context;
import android.hardware.Sensor;
import android.hardware.SensorManager;
import android.os.Bundle;
import androidx.appcompat.app.AppCompatActivity;
import com.google.android.gms.common.util.ArrayUtils;
import java.util.ArrayList;
import java.util.stream.Stream;
public class Hub
{
private static SensorManager StepsManager;
private Sensor AccelerometerSensor;
private Sensor StepsCounterSensor;
private Sensor StepsDetectorSensor;
private double StepsFromReboot = 0;
private double StepsDetectorCurrentDate = 0;
private static double StepsAccelerometerCurrentDate = 0;
private Accelerometer acl;
private static double[] AccelerometerCoords = new double[3];
private static double AccelerometerVectorLength = 0;
private static double AccelerometerVectorLengthPrevious = AccelerometerVectorLength;
private static double VectorsDiff = AccelerometerVectorLength - AccelerometerVectorLengthPrevious;
private static final int STEP_THRESHOLD = 6;
private static boolean DateStarted = true;
private ArrayList<Boolean> AreSensorsPresented = new ArrayList<Boolean>(3){
{
add(false);
add(false);
add(false);
}
};
private ArrayList<Sensor> SensorsList = new ArrayList<Sensor>(3);
private ArrayList<String> SensorsNames = new ArrayList<String>(3){
{
add("Accelerometer sensor");
add("Steps counter sensor");
add("Steps detector sensor");
}
};
public void CheckSensorsAvailability(){
for(int i = 0; i < SensorsList.size(); i++){
if(SensorsList.get(i) == null){
AreSensorsPresented.set(i,false);
}
else{
AreSensorsPresented.set(i,true);
}
}
}
public void TypeSensorsInfo(){
System.out.println("\nThe sensors are available:\n");
for(int i = 0; i < AreSensorsPresented.size(); i++){
System.out.println(SensorsNames.get(i)+": is "+(AreSensorsPresented.get(i) ? "presented":"unavailable"));
}
}
private static final int WindowAnalyzeSec = 10 * 1000;
public static int MaxSamples = 500;
public static int Samples = 0;
public static ArrayList<Double> ValueHistory = new ArrayList<Double>(MaxSamples);
public static ArrayList<Double> Time = new ArrayList<>(MaxSamples);
public static long TimeStart;
public static long TimeEnd;
public static void IncomingData(String From, double[] values, int DelayType){
switch (From){
case "ACCELEROMETER":
{
if (Samples == 0) TimeStart = System.currentTimeMillis();
//there comes x,y,z coordinates
AccelerometerCoords[0] = values[0];
AccelerometerCoords[1] = values[1];
AccelerometerCoords[2] = values[2];
AccelerometerVectorLength = Math.sqrt(Math.pow(AccelerometerCoords[0],2) + Math.pow(AccelerometerCoords[1],2) + Math.pow(AccelerometerCoords[2],2));
VectorsDiff = AccelerometerVectorLength - AccelerometerVectorLengthPrevious;
if(VectorsDiff>STEP_THRESHOLD){
StepsAccelerometerCurrentDate+=1;
if(DateStarted){StepsAccelerometerCurrentDate = 0; DateStarted = false;}
MainScreen.test_view.setText(
"Steps from Accelerometer are: " + StepsAccelerometerCurrentDate +
"\n\n"+AccelerometerVectorLength+"\nprev: " +AccelerometerVectorLengthPrevious +"\ndiff: "+VectorsDiff+'^'
);
}
else{
String Info = MainScreen.test_view.getText().toString();
Info = Info.substring(0,Info.indexOf('^')+1);
Info += "\n\nCurrent difference:\nVectors difference: " + VectorsDiff + "\nX: " + AccelerometerCoords[0] + "\nY: " + AccelerometerCoords[1] + "\nZ: " + AccelerometerCoords[2] +"\nSamples are "+Samples;
MainScreen.test_view.setText(Info);
}
MaxSamplesCount(DelayType);
if (Samples >= MaxSamples){
ThisMainScreen.setDataGraph(Time,ValueHistory);
ValueHistory.clear();
Samples = 0;
TimeEnd = System.currentTimeMillis() - TimeStart;
System.out.println("TimeEnd: "+TimeEnd/1000+" s");
System.out.println("MaxSamples: "+MaxSamples+"\n\n");
}
else{
++Samples;
ValueHistory.add(AccelerometerVectorLength);
}
}
AccelerometerVectorLengthPrevious = AccelerometerVectorLength;
break;
}
}
private static MainScreen ThisMainScreen;
public Hub(MainScreen ThisMainScreen){
this.ThisMainScreen = ThisMainScreen;
StepsManager = (SensorManager) ThisMainScreen.getSystemService(Context.SENSOR_SERVICE);
AccelerometerSensor = StepsManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
StepsCounterSensor = StepsManager.getDefaultSensor(Sensor.TYPE_STEP_COUNTER);
StepsDetectorSensor = StepsManager.getDefaultSensor(Sensor.TYPE_STEP_DETECTOR);
SensorsList.add(AccelerometerSensor);
SensorsList.add(StepsCounterSensor);
SensorsList.add(StepsDetectorSensor);
CheckSensorsAvailability();
acl = new Accelerometer(Hub.this,StepsManager,AccelerometerSensor);
SetTimeValues();
}
public static float getDelayInMs(int DelayType){
/*
SENSOR_DELAY_FASTEST: delay = 0; (200Hz)
SENSOR_DELAY_GAME: delay = 20000;(20ms)
SENSOR_DELAY_UI: delay = 66667; (66,667 ms)
SENSOR_DELAY_NORMAL: delay = 200000; (200ms)
*/
float delay = -1f;
switch (DelayType){
case SensorManager.SENSOR_DELAY_FASTEST: {
delay = 1/200f*1000f; //5
break;
}
case SensorManager.SENSOR_DELAY_GAME:{
delay = 20f;
break;
}
case SensorManager.SENSOR_DELAY_UI:{
delay = 66.667f;
break;
}
case SensorManager.SENSOR_DELAY_NORMAL: {
delay = 200f;
break;
}
}
return delay;
}
private static void MaxSamplesCount(int DelayType){
//updates every SENSOR_DELAY_GAME = 20 ms
//freq = 1/20ms = 50Hz
//window is = 10s = 10 000 ms
//N = 10 000/20 = 500
float UpdateEveryMs = getDelayInMs(DelayType);
MaxSamples = (int) Math.ceil((float)WindowAnalyzeSec/UpdateEveryMs);
// SetTimeValues();
}
private static void SetTimeValues(){
Time.clear();
double step = ((double) WindowAnalyzeSec/1000 - 0)/(double) MaxSamples;
for (int i = 0; i < MaxSamples; i++){
Time.add(i*step);
}
}
public ArrayList<Sensor> getSensorsList(){
return SensorsList;
}
public ArrayList<String> getSensorsNames(){
return SensorsNames;
}
public ArrayList<Boolean> getSensorAvaialble(){
return AreSensorsPresented;
}
}
MainScreen.class()
import android.graphics.Canvas;
import android.graphics.Color;
import android.hardware.Sensor;
import android.hardware.SensorEvent;
import android.hardware.SensorEventListener;
import android.os.Bundle;
import android.text.Html;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;
import androidx.appcompat.app.AppCompatActivity;
import com.jjoe64.graphview.GraphView;
import com.jjoe64.graphview.series.DataPoint;
import com.jjoe64.graphview.series.LineGraphSeries;
import java.util.ArrayList;
import java.util.Collections;
public class MainScreen extends AppCompatActivity implements SensorEventListener {
public static GraphView graph;
private Hub hub;
private Button check;
public static TextView test_view;
private ArrayList<String> sNames;
private ArrayList<Sensor> sList;
private ArrayList<Boolean> sAvailable;
public static double GetMax(double[] arr) {
double max = arr[0];
for (int i = 0; i < arr.length; i++) {
if (arr[i] > max) {
max = arr[i];
}
}
return max;
}
public double GetMin(double[] arr) {
double min = arr[0];
for (int i = 0; i < arr.length; i++) {
if (arr[i] < min) {
min = arr[i];
}
}
return min;
}
// static LineGraphSeries<DataPoint>series = new LineGraphSeries<DataPoint>();
// static LineGraphSeries<DataPoint>series2 = new LineGraphSeries<DataPoint>();
public static LineGraphSeries<DataPoint> series = new LineGraphSeries<>();
public static void setDataGraph(ArrayList<Double> time, ArrayList<Double> magnitude) {
long TimerStart = System.currentTimeMillis();
// graph.removeAllSeries();
series.resetData(new DataPoint[]{});
for (int i = 0; i < magnitude.size(); i++) {
try {
series.appendData(new DataPoint(time.get(i), magnitude.get(i)), true, magnitude.size());
} catch (Exception e) {
System.out.println("EXCEPTION IS : " + e.getMessage());
}
}
graph.addSeries(series);
double magmax = Collections.max(magnitude) + 2; // Math.ceil(GetMax(magnitude))+2;//extra space
graph.getViewport().setMaxY(magmax);
long TimerEnd = System.currentTimeMillis() - TimerStart;
System.out.println("\nDraw time is " + TimerEnd + " ms = " + TimerEnd / 1000 + " s");
System.out.println("magnitude.size = " + magnitude.size() + " time.size = " + time.size());
System.out.println("magmax = " + (magmax - 2));
System.out.println("ymax = " + series.getHighestValueY() + " ymin = " + series.getLowestValueY());
if (TimerEnd > 200) {
System.gc();
System.runFinalization();
}
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.coordinator);
hub = new Hub(this);
graph = (GraphView) findViewById(R.id.graph);
test_view = (TextView) findViewById(R.id.test_view);
check = (Button) findViewById(R.id.btn_test);
check.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
sNames = hub.getSensorsNames();
sList = hub.getSensorsList();
sAvailable = hub.getSensorAvaialble();
String Info = "";
for (int i = 0; i < sNames.size(); i++) {
Info += "Name of sensor: " + sNames.get(i) + " status: " + (sAvailable.get(i) ? "<font color = #7C07>true</font>" : "<font color = #D10C0C>false</font>") + "<br>";
}
Info += "<br><br>More details:<br>";
for (int i = 0; i < sList.size(); i++) {
Info += (i + 1) + ")" + sList.get(i) + ";<br>";
}
test_view.setText(Html.fromHtml(Info));
}
});
//form series
int tmin = 0;//time[0]
int tmax = 10;
graph.getViewport().setMinX(tmin);
graph.getViewport().setMaxX(tmax);
int magmin = 0;
graph.getViewport().setMinY(magmin);
graph.getViewport().setYAxisBoundsManual(true);
graph.getViewport().setXAxisBoundsManual(false);
}
@Override
public void onSensorChanged(SensorEvent event) {
// test_view.setText(event.values[0]+" " + event.values[1]+" "+event.values[2]);
}
@Override
public void onAccuracyChanged(Sensor sensor, int accuracy) {
}
}
Profiler says:
Initial:
I/System.out: Draw time is 2 ms = 0 s
I/System.out: magnitude.size = 500 time.size = 500
I/System.out: magmax = 9.596914380508396
I/System.out: ymax = 9.596914380508396 ymin = 9.563461644595941
I/System.out: TimeEnd: 10 s
I/System.out: MaxSamples: 500
After 6 minutes of work:
I/System.out: Draw time is 207 ms = 0 s
I/System.out: magnitude.size = 500 time.size = 500
I/System.out: magmax = 9.596914380508396
I/System.out: ymax = 9.596914380508396 ymin = 9.563461644595941
I/art: Explicit concurrent mark sweep GC freed 27109(1809KB) AllocSpace objects, 0(0B) LOS objects, 24% free, 7MB/9MB, paused 579us total 43.945ms
I/System.out: TimeEnd: 10 s
I/System.out: MaxSamples: 500