Accelerometer logger: experiencing occasional long

2020-07-25 23:14发布

问题:

I am writing an app that logs the accelerations of the mobile phone every 40ms (at 25Hz). This frame rate can be held on average, but sometimes I am experiencing delays of 5'000ms - 50'000ms between timeframes. I am wondering why this happens.

Here you have a graph of the delays where you can see that they occur quite often:

Here's what I am doing (which might be bad):

  • The activity points to a accelerometer logger class (singleton, pure java, no android class extensions).
  • The accelerometer logger singleton continues to log in the background.
  • The accelerometer logger saves every log directly to the sqlite db.
  • I am also logging GPS data in the background.
  • The DAO (Data Access Object) assigns every Log to a LinkedBlockingQueue and saves them in a separate thread.

Here's what I think might be the problem:

  • Maybe i have to implement further lifecycle methods, or extend a specific android class, so that the accererometer logger gains priority (or just set the priority somewhere).
  • I might use the event.timestamp instead of System.currentTimeMills(). (I would prefer not to do this, as some sensors have different timezones, thats why i use System.currentTimeMillis(), but if necessary I'd switch.)

Do you have any experience with this or suggestions where the problem could probably lie?

Here's my code:

@SuppressLint("NewApi")
public class AccelerometerLogger implements SensorEventListener {

    private static AccelerometerLogger singleton = new AccelerometerLogger();

    private LoggerDao loggerDao;

    private SensorManager sensorManager;

    private Sensor accelerometer;

    private double acceleorometerRate = 25; // Hz

    int accelerometerDelayMicroseconds = (int) (Math.round(((1/this.acceleorometerRate)*1000000.0)));

    private AccelerometerLogger()
    {
        this.loggerDao = LoggerDao.getInstance();
    }

    public static AccelerometerLogger getInstance()
    {
        return singleton;
    }

    public void start(Context context)
    {
        this.sensorManager = (SensorManager) context.getSystemService(Context.SENSOR_SERVICE);
        this.accelerometer = this.sensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);

        int accelerometerMinDelay = this.accelerometer.getMinDelay();

        //Log.d("lggr-r", "desired delay: "+this.accelerometerDelayMicroseconds+" microseconds");
        //Log.d("lggr-r", "provided min delay: "+accelerometerMinDelay+" microseconds");

        if(accelerometerMinDelay < this.accelerometerDelayMicroseconds)
        {
            this.sensorManager.registerListener(this, this.accelerometer, this.accelerometerDelayMicroseconds);
            //Log.d("lggr-r", "listener registered for desired rate: "+this.acceleorometerRate+"Hz (delay of "+this.accelerometerDelayMicroseconds+" microseconds).");
        } 
        else if(accelerometerMinDelay==0)
        {           
            this.sensorManager.registerListener(this, this.accelerometer, SensorManager.SENSOR_DELAY_FASTEST);
            // Log.d("lggr-r", "listener registered for streaming api. only changes will be notified (interrupt).");
        }
        else
        {
            int providedRate = (int) Math.round(1 / (accelerometerMinDelay / 1000000.0));
            this.sensorManager.registerListener(this, this.accelerometer, SensorManager.SENSOR_DELAY_FASTEST);
            // Log.d("lggr-r", "can't read at the desired rate ("+this.acceleorometerRate+"Hz), app will read at "+providedRate+"Hz instead (delay of "+accelerometerMinDelay+" microseconds).");
        }
    }

    public void stop()
    {
        this.sensorManager.unregisterListener(this);
    }

    @Override
    public void onAccuracyChanged(Sensor sensor, int accuracy)
    {
        // String name = sensor.getName();
        // Log.d("lggr", "the accurracy of "+name+" changed to "+accuracy+".");
    }

    @Override
    public void onSensorChanged(SensorEvent event)
    {
        // lazy load loggerDao (TODO: fix all of those)
        if(this.loggerDao == null)
        {
            this.loggerDao = LoggerDao.getInstance();
        }


        String values = "";
        for(float value : event.values) values += value+",";
        values = values.substring(0,values.length()-2);

        // long timestamp = System.currentTimeMillis();
        // Log.d("lggr", "acc = {time:"+timestamp+", data: ["+values+"]}");

        AccelerometerSample accelerometerSample = new AccelerometerSample();
        accelerometerSample.setTimestamp(System.currentTimeMillis());
        accelerometerSample.setValues(event.values);

        this.loggerDao.save(accelerometerSample);
    }

}

Apparently the problem only happens on the Samsung Galaxy SIII mini. I've tested it with a Samsung Galaxy SII (custom ROM) and the delays were always about 0.04s (ranging between 0.005 and 0.12s - much better).

Do you have any suggestions why this happens on the Samsung Galaxy SIII mini?

UPDATE:

Ben Voigts answer which purposed to use the event.timestamp has improved the delays significantly. Still, i am experiencing sometimes some longer delays. Do you know how I can further improve them?

回答1:

You absolutely should be using event.timestamp. If you want local time, calculate the adjustment factor between event.timestamp and System.currentTimeMills() on the first event, and apply the same adjustment to subsequent samples.

The whole point of a hardware-provided timestamp attached to the sample is that it isn't messed up by thread scheduling delays.



回答2:

As Ben Voigt said, it's necessary to use the event.timestamp in order to get accurate timestamps for the sensor measurements. Here's a code sample I've used myself and worked for me:

@Override
public void onSensorChanged(SensorEvent event) {
    if (sampleCounter == 0) {
        long miliTime = System.currentTimeMillis();

        long nanoTime = event.timestamp;

        timeDiff = miliTime - nanoTime / 1000000;
        log.info("Synchornizing sensor clock. Current time= " + miliTime
                + ", difference between clocks = " + timeDiff);
    }

    float x = event.values[0];
    float y = event.values[1];
    float z = event.values[2];
    long ts = event.timestamp / 1000000 + timeDiff;

    //Do your stuff

    sampleCounter++;
}