Question

I'm learning the Basics of Android programming.

I have a simple android test application in which i log the accelerometer,magnetometer and the orientation data to an external file while also displaying it. I initiate the logging process on click of a Start button (registerListener for relevant sensors) by calling a method initLogger.

Which looks something similar to this...

public void initLogger(View view)
{
    boolean bFlag = false;

    Button btnStart = (Button)findViewById(R.id.btnStartLog);
    Button btnStop = (Button)findViewById(R.id.btnStopLog);

    btnStart.setEnabled(bFlag);
    btnStop.setEnabled(!bFlag);

    bEnableLogging = true;
    //Start reading the sensor values
    sensorManager.registerListener(this, sensorManager.getDefaultSensor(Sensor.TYPE_MAGNETIC_FIELD), SensorManager.SENSOR_DELAY_UI);
    sensorManager.registerListener(this, sensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER), SensorManager.SENSOR_DELAY_UI);

   //so on.... 

There is also a Stop button, which shall stop the logging process (and finally unregister by calling unregisterListener for each sensor)

The data retrieval process happens inside the onSensorChanged handler which shall retrieve the data from the relevant sensors, sets the value to the respective UI elements and finally log the data to an external .csv file.

onSensorChanged eventhandler looks something like this ...

public void onSensorChanged(SensorEvent event) {


    // TODO Auto-generated method stub
    // accelerometer
    TextView tAX = (TextView) findViewById(R.id.txtViewAxValue);
    TextView tAY = (TextView) findViewById(R.id.txtViewAyValue);
    TextView tAZ = (TextView) findViewById(R.id.txtViewAzValue);

    // magnetic field
    TextView tMX = (TextView) findViewById(R.id.txtViewMx);
    TextView tMY = (TextView) findViewById(R.id.txtViewMy);
    TextView tMZ = (TextView) findViewById(R.id.txtViewMz);

    if (bEnableLogging) {
        if (event.sensor.getType() == Sensor.TYPE_ACCELEROMETER) {

            accelerometerdata = event.values.clone();

            tAX.setText(Double.toString(accelerometerdata[0]));
            tAY.setText(Double.toString(accelerometerdata[1]));
            tAZ.setText(Double.toString(accelerometerdata[2]));


        }

        if (event.sensor.getType() == Sensor.TYPE_MAGNETIC_FIELD) {

            magneticmatrixdata = event.values.clone();

            tMX.setText(Double.toString(magneticmatrixdata[0]));
            tMY.setText(Double.toString(magneticmatrixdata[1]));
            tMZ.setText(Double.toString(magneticmatrixdata[2]));

        }

               // so on ....

Although i receive the data from all the configured sensors, i do not have the control over the rate at which the data is received. i.e

I know SensorChanged event is fired as and when the Sensor data is changed. However i want this event to be fired at a fixed rate. For ex: every 40ms

Question:

  1. How to ensure the SensorChanged event is fired at constant rate ?
  2. Is the class TimerTask in Java of any help in this case ?

Experts out here in SO.Please help me :)

Was it helpful?

Solution

Since you know that if there was no SensorChanged Event fired, there was no change, you can just use your old value. As you asked for LOG data in specific intervals, i would not do any output in the onSensorChanged Method just clone the new data to your accelerometerdata variable. And than log the value of accelerometerdata every 40ms. This way you are logging the actual value every 40ms even if the data didnt change....

Note: According to Ridcullys Answer it also seem to be possible to get Sensor data "delivered" in specific time intervals. But since there is an delay on these "Deliveries" as always with sensor-data on Android, with my solution you will be more exactly on the 40ms interval. On the other hand it could happen that if the sensor data changes in the moment you log, it might happen that you delay the new data for one interval. And i guess (not sure about this point) - since its just about logging and not about sth like "get it as fast as possible in realtime", so this is not an requirement - the Timer-Solution causes less CPU-Load.

OTHER TIPS

You can change the interval by changing the delay when registering for the sensor.

int SENSOR_DELAY_FASTEST    get sensor data as fast as possible 
int SENSOR_DELAY_GAME       rate suitable for games 
int SENSOR_DELAY_NORMAL     rate (default) suitable for screen orientation changes
int SENSOR_DELAY_UI         rate suitable for the user interface

According to this, the Fastest delay is what you would need and if it doesn't changes fast enough for you is because there were no changes. There is no getSensorData method.

You can specify other data delays, such as SENSOR_DELAY_GAME (20,000 microsecond delay), SENSOR_DELAY_UI (60,000 microsecond delay), or SENSOR_DELAY_FASTEST (0 microsecond delay). As of Android 3.0 (API Level 11) you can also specify the delay as an absolute value (in microseconds).

The delay that you specify is only a suggested delay. The Android system and other applications can alter this delay. As a best practice, you should specify the largest delay that you can because the system typically uses a smaller delay than the one you specify (that is, you should choose the slowest sampling rate that still meets the needs of your application). Using a larger delay imposes a lower load on the processor and therefore uses less power.

When registering your Listener with the SensorManager using registerListener, instead of the fixed constants SENSOR_DELAY_... you can pass the interval in microseconds as described in the JavaDocs:

rate The rate sensor events are delivered at. This is only a hint to the system. Events may be received faster or slower than the specified rate. Usually events are received faster. The value must be one of SENSOR_DELAY_NORMAL, SENSOR_DELAY_UI, SENSOR_DELAY_GAME, or SENSOR_DELAY_FASTEST or, the desired delay between events in microsecond.

So, just pass 40000 to match your example request, also, as stated, this in only a hint so I would not rely on getting the values in intervals exact to the microseconds.

I have been trying to figure out how can I have very long delays with sensors, and so far I have been successful. The documentation on the developer.android talks that the delay time can be specified in microseconds, below is a large number I tried:

// Initialize in onCreate
mSensorManager = (SensorManager) getSystemService(Context.SENSOR_SERVICE)
mLight = mSensorManager.getDefaultSensor(Sensor.TYPE_LIGHT);

// Register the listener, and play around with delay
mSensorManager.registerListener(this, mLight, 200000000);

However note the LogCat dump below - the onSensorChanged() - gets called as many as 4 times a second... I am trying to figure out getting larger delays with no success so far!!!

01-14 06:58:07.088: D/IOE Sensors(20933): onSensorChanged called, the event is: android.hardware.SensorEvent@42849f70 01-14 06:58:07.268: D/IOE Sensors(20933): onSensorChanged called, the event is: android.hardware.SensorEvent@42849f70 01-14 06:58:07.448: D/IOE Sensors(20933): onSensorChanged called, the event is: android.hardware.SensorEvent@42849f70 01-14 06:58:07.628: D/IOE Sensors(20933): onSensorChanged called, the event is: android.hardware.SensorEvent@42849f70 01-14 06:58:07.808: D/IOE Sensors(20933): onSensorChanged called, the event is: android.hardware.SensorEvent@42849f70 01-14 06:58:07.989: D/IOE Sensors(20933): onSensorChanged called, the event is: android.hardware.SensorEvent@42849f70 01-14 06:58:08.169: D/IOE Sensors(20933): onSensorChanged called, the event is: android.hardware.SensorEvent@42849f70

It seems that the rate at which onSensorChanged is called has to be one of the 4 suggested values.

What you can do for 'slow reads' is use SENSOR_DELAY_UI and (in onSensorChanged) measure the time that has passed since the last time you read the sensordata:

long lastUpdate = System.currentTimeMillis();

// In onSensorChanged:
long curTime = System.currentTimeMillis();

        if ((curTime - lastUpdate) > 500){ // only reads data twice per second
            lastUpdate = curTime;
            // do stuff
        }

The only disadvantage I see here is that the if statement will be called many times with a false outcome.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top