SensorEvent.timestamp inconsistency
Asked Answered
D

3

16

my application performs in background step counting using the step detector sensor API's introduced in android 4.4.X.

It's essential to my app to know the exact time (at least accuracy of a second) each step event has accrued.

because I perform sensor batching , the time onSensorChanged(SensorEvent event) been called is not the same time when the step event took place - I must use the event.timestampfield to get the event time.

the documentation about this field is:

The time in nanosecond at which the event happened

The problem:

In some devices (such Moto X 2013) seems like this timestamp is time in nano seconds since boot, while in some devices (such Nexus 5) it's actually returns universal system time in nano seconds same as System.currentTimeMills() / 1000.

I understand, there's already an old open issue about that, but since sensor batching is introduced - it becomes important to use this field to know the event time, and it's not possible to rely anymore on the System.currentTimeMills()

My question:

What should I do to get always the event time in system milliseconds across all devices?

Degrade answered 4/11, 2014 at 7:0 Comment(0)
F
8

Instead of your "2-day" comparison, you could just check if event.timestamp is less than e.g. 1262304000000000000 - that way you'd only have a problem if the user's clock is set in the past, or their phone has been running for 40 years...

Except that a comment on this issue indicates that sometimes it's even milliseconds instead of nanoseconds. And other comments indicate that there's an offset applied, in which case it won't be either system time or uptime-based.

If you really have to be accurate, the only way I can see is to initially capture an event (or two, for comparison) with max_report_latency_ns set to 0 (i.e. non-batched) and compare the timestamp to the system time and/or elapsedRealtime. Then use that comparison to calculate an offset (and potentially decide whether you need to compensate for milliseconds vs nanoseconds) and use that offset for your batched events.

E.g. grab a couple of events, preferably a couple of seconds apart, recording the System.currentTimeMillis() each time and then do something like this:

long timestampDelta = event2.timestamp - event1.timestamp;
long sysTimeDelta = sysTimeMillis2 - sysTimeMillis1;
long divisor; // to get from timestamp to milliseconds
long offset; // to get from event milliseconds to system milliseconds

if (timestampDelta/sysTimeDelta > 1000) { // in reality ~1 vs ~1,000,000
    // timestamps are in nanoseconds
    divisor = 1000000;
} else {
    // timestamps are in milliseconds
    divisor = 1;
}

offset = sysTimeMillis1 - (event1.timestamp / divisor);

And then for your batched events

long eventTimeMillis = (event.timestamp / divisor) + offset;

One final caveat - even if you do all that, if the system time changes during your capture, it may affect your timestamps. Good luck!

Flournoy answered 7/4, 2015 at 23:57 Comment(4)
I believe your solution is the best answer I could get. thank youDegrade
Wow, this is unbelievable.Arliearliene
:O Useful APIs ... Is this still present in 2022 with more recent API levels?Domain
@StevenJeuris Interesting question! I have no idea, haven't touched this stuff in a long time.Flournoy
D
4

I found a work-around solution that solving the problem. the solution assumes that the timestamp can be only one of the two: system timestamp, or boot time:

protected long getEventTimestampInMills(SensorEvent event) {
    long timestamp = event.timestamp / 1000 / 1000;

    /**
     * work around the problem that in some devices event.timestamp is
     * actually returns nano seconds since last boot.
     */
    if (System.currentTimeMillis() - timestamp > Consts.ONE_DAY * 2) {
        /**
         * if we getting from the original event timestamp a value that does
         * not make sense(it is very very not unlikely that will be batched
         * events of two days..) then assume that the event time is actually
         * nano seconds since boot
         */
        timestamp = System.currentTimeMillis()
                + (event.timestamp - System.nanoTime()) / 1000000L;
    }

    return timestamp;
}
Degrade answered 11/11, 2014 at 6:17 Comment(0)
C
3

According to the link in your question:

This is, in fact, "working as intended". The timestamps are not defined as being the Unix time; they're just "a time" that's only valid for a given sensor. This means that timestamps can only be compared if they come from the same sensor.

So, the timestamp-field could be completely unrelated to the current system time.

However; if at startup you were to take two sensor samples, without batching, you could calculate the difference between the System.currentTimeMillis() and the timestamp, as well as the quotient to the differences between the different times you should be able to convert between the different times:

//receive event1:
long t1Sys = System.currentTimeMillis();
long t1Evt = event.timestamp;

//receive event2:
long t2Sys = System.currentTimeMillis();
long t2Evt = event.timestamp;

//Unregister sensor


long startoffset = t1Sys - t1Evt; //not exact, but should definitely be less than a second, possibly use an averaged value.
long rateoffset = (t2Sys - t1Sys) / (t2Evt - t1Evt);

Now any timestamp from that sensor can be converted

long sensorTimeMillis = event.timestamp * rateoffset + startoffset;
Continuator answered 8/4, 2015 at 9:12 Comment(3)
I see we've been thinking along very similar lines. I would imagine your rateoffset calculation will result in drift over time though (the further apart the sample events the less of an issue this should be, but it will never be perfect, particularly because there will be an unpredictable delay between the event time and the associated system time). That's why in my answer I clamped it either to 1 or to 1,000,000.Flournoy
Yes, reading through your anwer I realize it is basically the same idea. Clamping the value definitely solves the problem with drift, but an average of several samples, combined with periodic recalculations is an alternative to be considered, depending on what is needed.Continuator
this is revolting!Arliearliene

© 2022 - 2024 — McMap. All rights reserved.