传感器事件时间戳不一致性

16
我的应用程序使用Android 4.4.X中引入的步数检测器传感器API进行后台步数计数。对于我的应用程序来说,知道每个步骤事件发生的确切时间(至少精确到一秒)非常重要。
由于我执行了传感器批处理,因此调用onSensorChanged(SensorEvent event)的时间并不是步骤事件发生的时间。我必须使用event.timestamp字段来获取事件时间。
关于此字段的文档如下:

事件发生时的纳秒时间

问题: 在某些设备(例如Moto X 2013),这个时间戳似乎是自启动以来的纳秒时间,而在某些设备(例如Nexus 5)中,它实际上返回的是通用系统时间(纳秒),与System.currentTimeMills() / 1000相同。
我知道,已经有一个旧的未解决问题与此相关,但是自从引入了传感器批处理 - 使用这个字段来知道事件时间变得更加重要,不能再依赖System.currentTimeMills()了。 我的问题: 为了在所有设备上始终以系统毫秒获取事件时间,我应该怎么做?
3个回答

7

不要用“2天”这个比较,你可以检查event.timestamp是否小于例如1262304000000000000。这样,只有当用户的时钟设置在过去或他们的手机运行了40年时才会出现问题……

但是,在此问题的评论中指出,有时候甚至是毫秒而不是纳秒。其他评论指出,存在应用的偏移量,那么它既不是系统时间也不是运行时间。

如果你确实需要准确的结果,我能想到唯一的方法是最初使用max_report_latency_ns设置为0(即非批处理)捕获一个或两个事件,并将其时间戳与系统时间和/或elapsedRealtime进行比较。然后使用该比较来计算偏移量(并可能决定您是否需要补偿毫秒或纳秒),并将该偏移量用于批处理事件。

例如,抓取几个事件,最好相隔几秒钟,在每次记录System.currentTimeMillis(),然后像这样做:

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);

然后针对您的批处理事件

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

最后一个注意事项 - 即使您做了所有这些,如果系统时间在捕获期间发生更改,可能会影响您的时间戳。祝好运!


我相信你的解决方案是我能得到的最佳答案。谢谢。 - Tal Kanel
2
哇,这太不可思议了。 - Michael
有用的API...在2022年的更高API级别中,这个还存在吗? - Steven Jeuris
1
@StevenJeuris 有趣的问题!我不知道,已经很久没有接触这些东西了。 - CupawnTae

4

我找到了一个解决问题的方法。这个方法假设时间戳只能是以下两种之一:系统时间戳或开机时间戳:

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;
}

3
根据您提供的问题中的链接
这实际上是“按预期工作”。时间戳未被定义为Unix时间;它们只是在给定传感器上有效的“时间”。这意味着,只有来自同一传感器的时间戳才能进行比较。
因此,timestamp字段可能与当前系统时间完全无关。
然而,如果在启动时采取两个传感器样本(不使用批处理),可以计算出System.currentTimeMillis()和时间戳之间的差以及不同时间之间差的商,从而能够在不同时间之间进行转换。
//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);

现在,可以将来自该传感器的任何时间戳进行转换。
long sensorTimeMillis = event.timestamp * rateoffset + startoffset;

我看到我们的想法非常相似。我想你的rateoffset计算会随着时间的推移而产生漂移(样本事件之间的距离越远,这个问题就越不明显,但它永远不会完美,特别是因为事件时间和相关系统时间之间会有不可预测的延迟)。这就是为什么在我的答案中,我将其夹紧到1或1,000,000。 - CupawnTae
是的,阅读您的答案后,我意识到基本上是相同的想法。夹紧值确实解决了漂移问题,但是考虑到需要什么,几个样本的平均值结合定期重新计算是另一种可行的选择。 - Jave
这太令人反感了! - Michael

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接