【问题标题】:SensorEvent.timestamp inconsistencySensorEvent.timestamp 不一致
【发布时间】:2014-11-04 07:00:06
【问题描述】:

我的应用程序使用 android 4.4.X 中引入的step detector sensor API's 在后台计步。

对于我的应用来说,知道每个步骤事件产生的确切时间(至少精确到一秒)是很重要的。

因为我执行了sensor batching,所以调用onSensorChanged(SensorEvent event)的时间与step事件发生的时间不同-我必须使用event.timestamp字段来获取事件时间。

关于这个领域的文档是:

事件发生的时间(以纳秒为单位)

问题:

在某些设备(例如 Moto X 2013)中,此时间戳似乎是自启动后以纳秒为单位的时间,而在某些设备(例如 Nexus 5)中,它实际上以纳秒为单位返回通用系统时间,与 System.currentTimeMills() / 1000 相同。

我了解,已经有一个 old open issue 与此相关,但由于引入了传感器批处理 - 使用此字段 了解事件时间变得很重要,并且不再可能依赖System.currentTimeMills()

我的问题:

我应该怎么做才能在所有设备上始终以系统毫秒为单位获取事件时间?

【问题讨论】:

    标签: android android-sensors nanotime


    【解决方案1】:

    您可以检查event.timestamp 是否小于例如,而不是您的“2 天”比较。 1262304000000000000 - 如果用户的时钟设置在过去,或者他们的手机已经运行了 40 年,那么你只会遇到问题......

    除了this issue 上的评论表明有时它甚至是毫秒而不是纳秒。其他 cmets 表示应用了偏移量,在这种情况下,它既不是基于系统时间,也不是基于正常运行时间。

    如果您真的必须准确,我能看到的唯一方法是最初捕获一个事件(或两个,用于比较),将 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;
    

    最后一个警告 - 即使您做了所有这些,如果系统时间在您捕获期间发生变化,它可能会影响您的时间戳。祝你好运!

    【讨论】:

    • 我相信您的解决方案是我能得到的最佳答案。谢谢
    • 哇,这太不可思议了。
    【解决方案2】:

    我找到了解决问题的变通解决方案。该解决方案假定时间戳只能是以下两者之一:系统时间戳或启动时间:

    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】:

      根据您问题中的link

      事实上,这是“按预期工作”。时间戳不是 定义为 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。
      • 是的,通读您的回答,我意识到这基本上是相同的想法。限制值肯定可以解决漂移问题,但是根据需要,对几个样本进行平均,并结合定期重新计算是可以考虑的替代方案。
      • 这太恶心了!
      猜你喜欢
      • 2017-02-06
      • 1970-01-01
      • 2016-12-13
      • 1970-01-01
      • 2014-06-14
      • 1970-01-01
      • 2017-01-16
      • 2019-09-15
      相关资源
      最近更新 更多