加速度计传感器事件时间戳

34

我目前正在开发一个安卓应用程序.. 我需要记录加速度传感器事件坐标以及事件时间。我已经获取了传感器事件的时间戳,像是"3497855005850",但我无法将事件时间戳转换为用户可读的日期时间格式。提前致谢。

如何将SensorEvent的时间戳转换为Unix时间戳?


呃!又是另一个行业标准 :)。确实,命名是计算机行业中最棘手的问题。我也曾陷入同样的困境。 - Bharat Bhagat
8个回答

41

传感器时间戳实际上是设备已运行时间的纳秒数,而不是系统时间的纳秒数。请参见SensorEvent.timestamp to absolute (utc) timestamp?.

理论上,你可以使用以下方法将传感器时间戳转换为毫秒级时间:

long timeInMillis = (new Date()).getTime() 
                    + (sensorEvent.timestamp - System.nanoTime()) / 1000000L;

你甚至可以使用两个System.nanoTime()调用来框定(new Date()).getTime()调用,并对它们进行平均以更接近实际偏移量。

然后,您可以将传感器时间格式化为日期和时间。


9
从Android 4.2.1(API级别17)开始,在我的Nexus 4上,sensorEvent.timestamp似乎包含了精确的时间戳(以纳秒为单位)。这似乎没有被记录在文档中,尽管它非常重要! - Romain Guidoux
1
我可以证明Romain所说的话。 - rbncrthms
3
时间戳的值似乎不幸地与Android的版本无关:https://code.google.com/p/android/issues/detail?id=56561 - Romain Guidoux
对于类型为STEP_COUNTER的SensorEvent,在搭载Android 5的三星S4上无法正常工作 - 因此基本上给出的答案对于该传感器类型并不是很有用或正确。我最初认为Boban S的答案有道理,但实际上在该设备上这种传感器也无法正常工作。基本上,您可以获得引发该类型传感器事件的时间 - 这可能是因为它是事件的聚合而不是单个事件。 - David Glance
2
这是一个不正确的答案。传感器时间戳可能不是以纳秒为单位的正常运行时间,这取决于制造商。 - Andrey Suvorov
显示剩余2条评论

5
你可以在onSensorChanged(SensorEvent)函数中设置参考时间变量。
当前时间和事件时间的参考值。当事件到达时,从传感器参考时间中减去事件时间,就可以得到以纳秒为单位的差值。你可以将该差值除以1,000,000后加到当前时间参考值中,以得到以毫秒为单位的事件时间。
对于一个事件来说,计算误差最大为0.5毫秒。你可以通过偶尔更改参考时间来将误差最小化。
private long sensorTimeReference = 0l;
private long myTimeReference = 0l;

public void onSensorChanged(SensorEvent event) {
    // set reference times
    if(sensorTimeReference == 0l && myTimeReference == 0l) {
        sensorTimeReference = event.timestamp;
        myTimeReference = System.currentTimeMillis();
    }
    // set event timestamp to current time in milliseconds
    event.timestamp = myTimeReference + 
        Math.round((event.timestamp - sensorTimeReference) / 1000000.0);
    // some code...
}

2
你如何处理传感器报告值时存在延迟的情况? - Michael

4

正确答案是这个,否则时间戳将会被移动。 - Mehran Sahandi Far
为什么我们要将 (event.timestamp - SystemClock.elapsedRealtimeNanos()) 添加到 System.currentTimeMillis() 中?这不会使实际事件时间超过当前时间吗? - Sagar Patel
SystemClock.elapsedRealtimeNanos()SensorEvent.timestamp的基准(自启动以来的相对时间)可能与System.currentTimeMillis()(挂钟时间)的基准不同。它仍然能正常工作吗? - Sagar Patel
即使它能够正常工作(或者不能),请问有人可以解释一下为什么以及如何工作吗? - Sagar Patel
1
@SagarPatel 传感器事件中的时间戳和elapsedRealtimeNanos都是基于“启动时间”的相同基准时间。 - chejdj
显示剩余2条评论

3
官方的SensorEvent#timestamp 文档表明:

[事件时间为] 事件发生时的纳秒级时间。 对于给定的传感器,每个新的传感器事件应该使用与SystemClock.elapsedRealtimeNanos()相同的时间基准单调递增。

挑战
  • 某些制造商/设备使用SystemClock.currentTimeNanos()(例如Nexus 4)。
设备无关的解决方案:
/**
 * Calculates the static offset (ms) which needs to
 *  be added to the `event.time` (ns) to get the Unix
 *  timestamp of the event.
 *
 * @param eventTimeNanos the {@code SensorEvent.time} to be used to determine the time offset
 * @return the offset in milliseconds
 */
private long eventTimeOffset(final long eventTimeNanos) {
    // Capture timestamps of event reporting time
    final long elapsedRealTimeMillis = SystemClock.elapsedRealtime();
    final long upTimeMillis = SystemClock.uptimeMillis();
    final long currentTimeMillis = System.currentTimeMillis();

    // Check which timestamp the event.time is closest to the event.time
    final long eventTimeMillis = eventTimeNanos / 1_000_000L;
    final long elapsedTimeDiff = elapsedRealTimeMillis - eventTimeMillis;
    final long upTimeDiff = upTimeMillis - eventTimeMillis;
    final long currentTimeDiff = currentTimeMillis - eventTimeMillis;

    // Default case (elapsedRealTime, following the documentation)
    if (Math.abs(elapsedTimeDiff) <= Math.min(Math.abs(upTimeDiff), Math.abs(currentTimeDiff))) {
        final long bootTimeMillis = currentTimeMillis - elapsedRealTimeMillis;
        return bootTimeMillis;
    }

    // Other seen case (currentTime, e.g. Nexus 4)
    if (Math.abs(currentTimeDiff) <= Math.abs(upTimeDiff)) {
        return 0;
    }

    // Possible case, but unknown if actually used by manufacturers (upTime)
    throw new IllegalStateException("The event.time seems to be upTime. In this case we cannot use a static offset to calculate the Unix timestamp of the event");
}

注册时间延迟

  • 该代码与事件时间和传感器事件监听器触发的时间之间的延迟(注册时间)无关,因为我们仅使用SystemClock来计算偏移量。
  • 如果多个设备的系统时间已同步(有应用程序可以实现此功能),则可以同步多个设备之间的传感器数据。

关于“可能的情况”(参见代码):

  • 目前不清楚是否有任何制造商实际上使用upTime作为event.time。因此,我们抛出一个异常来看看是否会发生这种情况。

  • 如果确实发生了这种情况,并且我们计算了currentTimeupTime之间的静态偏移量,这将导致设备再次进入(深度)睡眠时出现时间偏移。我们需要为每个事件动态地计算偏移量,这相当耗费资源。

  • 如果在设备上发生这种情况,则可以使用以下方法进行测试:https://developer.android.com/training/monitoring-device-state/doze-standby#testing_doze_and_app_standby


1

我看到三种获取毫秒的方法(Kotlin)

val millis = Date().getTime() + (event.timestamp - System.nanoTime()) / 1000000L

val millis = System.currentTimeMillis() + (event.timestamp - System.nanoTime()) / 1000000L

val millis = System.currentTimeMillis() + (event.timestamp - SystemClock.elapsedRealtimeNanos()) / 1000000L

这三种方法都能得到相同的结果,但是当我想要查看从计算值到当前时间的差异时

val diff  = System.currentTimeMillis() - millis

我看到'value'为-359704905的'diff'?

Log.d("diff", "" + event.timestamp + " - " + System.nanoTime())

差异: 541695268300000 - 181990403666592

差异: 541695277240000 - 181990405818592

差异: 541695286859000 - 181990411901592

差异: 541695296139000 - 181990412584592

差异: 541695305735000 - 181990415222592

因此,所有建议的解决方案都不正确

对我来说,这种简单的方法适合我的需求

override fun onSensorChanged(sensorEvent: SensorEvent?) {
    val millis = System.currentTimeMillis()
}

2
如果您在注册时设置了任何延迟,则此解决方案将无法正常工作。当延迟> 0时,您将会有多个读数同时到达,并且您将为所有读数分配相同的时间戳。 *使用以下方式进行注册 public boolean registerListener(SensorEventListener listener,Sensor sensor,             int samplingPeriodUs,int maxReportLatencyUs) - FabioC

1
SensorEvent.timestamp是事件发生时的经过时间,但它是自启动以来经过的时间。它不是事件发生的时间。我们需要从事件发生后经过的时间中获取事件实际发生的时间。

参考: https://developer.android.com/reference/android/hardware/SensorEvent#timestamp

  1. 我们需要将所有实体都放在相似的单位中。这里我们使用Millis
  2. 我们有三个实体可用。a) System.currentTimeMillis() b) SystemClock.elapsedRealtime() 和 c) SensorEvent.timestamp

enter image description here

  1. 如果我们使用SystemClock.elapsedRealtimeNanos(),那么我们需要将其转换为以下Millis
val systemCurrentTimeMillis = System.currentTimeMillis()
val systemClockElapsedRealtimeMillis = TimeUnit.NANOSECONDS.toMillis(SystemClock.elapsedRealtimeNanos())
val sensorEventTimeStampMillis = TimeUnit.NANOSECONDS.toMillis(sensorEvent.timestamp)
  1. 我们需要找出以下两者之间的差异:systemCurrentTimeMillissystemClockElapsedRealtimeMillis

enter image description here

val currentMinusElapsedRealtimeMillis = systemCurrentTimeMillis - systemClockElapsedRealtimeMillis

一旦我们找到差异,我们需要按下面的方式将 sensorEventTimeStampMillis 添加到它上面:

enter image description here 或者 enter image description here

val actualEventTimeMillis = currentMinusElapsedRealtimeMillis + sensorEventTimeStampMillis
  1. 接下来,我们需要将结果转换为UTC时间(我使用的是 Joda Time):
val actualEventTimeUtc = DateTime(actualEventTimeMillis, DateTimeZone.UTC)
actualEventTimeUtc是事件发生的绝对时间。
另一种理解方式是:假设传感器在onSensorChanged中报告了一个事件。我们发现当前时间是13点。换句话说,报告时间是下午1点,而不是实际事件发生的时间。 SystemClock.elapsedRealtime()显示它已经运行了30个小时。换句话说,它在当前时间30小时前启动。
因此,我们从当前时间减去30小时,以获取传感器启动的时间。所以,我们得到了前一天早上7点的时间。 表示事件发生在SystemClock.elapsedRealtime()后的28小时。
因此,我们将28小时添加到前一天上午7点。所以,我们得到了当天上午11点,这是事件发生的实际时间。

1

我遇到了同样的问题,使用了ehartwell的solution。但是我使用了System.currentTimeMillis()而不是new Date().getTime()

此外,我发现sensorEvent.timeStampSystem.nanoTime()之间存在最大300毫秒的偏移量,大多数情况下小于200毫秒。根据所需的精度,您可以忽略这种差异。

如果我使用Boban S.的solution,则差异在初始化时是正确的。然而,正确时间和测量时间会分歧。估计的测量时间在未来。


发散现象发生在什么时间范围内,程度如何?您是否在多个设备上看到这种情况? - Michael

1
每个设备都不同。它可能基于纪元、开机时间和CPU唤醒时间。最好的方法是读取所有三个,然后查看时间戳最接近哪一个,然后根据偏移量进行转换。

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