CMLogItem.timestamp: which clock is it on, and how to convert to AVCaptureSession.synchronizationClock / host time?

We receive live relative-altitude updates from CMAltimeter.startRelativeAltitudeUpdates(to:withHandler:) on iPhone. CMAltitudeData inherits CMLogItem.timestamp, which the documentation describes as the time when the item is valid and as seconds since device boot.

We need to determine whether a pressure measurement's event time falls inside an application operation bounded by a supported monotonic clock. Could Apple clarify the supported contract for CMAltitudeData.timestamp on iOS?

  1. Does CMAltitudeData.timestamp use the same epoch and rate as mach_absolute_time(), DispatchTime.uptimeNanoseconds, CACurrentMediaTime(), or ProcessInfo.systemUptime? If only some are compatible, which ones?

  2. Does it advance or pause during device sleep, device lock, application suspension, and background execution? In particular, are its sleep/suspension semantics guaranteed to match any of the clocks above?

  3. Is there a supported API for converting CMLogItem.timestamp to a host CMClock/Mach time, or for sampling “now” in the exact same clock domain used by CMAltitudeData.timestamp?

  4. Are the answers contractual across supported iPhone hardware and iOS releases, or are they implementation details that applications should not rely on?

  5. If direct comparison is not supported, what Apple-supported clock or conversion mechanism should an application use to compare a CMAltitudeData event time with two application-side monotonic operation boundaries?

The question concerns clock semantics only. A minimal reproducer can be supplied if requested, but no application identifier, production data, sensor values, or user data is required to answer it.

Answered by DTS Engineer in 906840022

Your point that the altimeter does not perform its own timestamping raises one remaining question: what event does CMAltitudeData.timestamp represent—receipt of a sensor reading by the system, creation of the Core Motion event, or an estimate of when pressure was measured?

locationd collects and stores the data from our various sensors, and that data store* is what then sends the motion events you receive. SO, generally speaking, its behavior is probably closer to this:

*Just to clarify, this doesn't mean that locationd specifically stores every reading it receives and then sends it to your app at some later point. The process of sending data to clients and storing that data all happens as part of the same process.

CMAltitudeData.timestamp represent—receipt of a sensor reading by the system

However, the problem here is that the details of exactly how our sensors generate and process data vary considerably across our full device range and, in fact, a huge part of locationd's "job" is to provide relatively consistent behavior across a very wide range of sensor implementations. Putting that in more concrete terms, if these differences ACTUALLY matter to your app’s implementation, then I think you're expecting a level of precision that the system was never really designed to provide.

Can a newly delivered event contain an older or filtered pressure measurement whose age is not represented by that timestamp?

No.

For implementation, is comparing that timestamp directly with operation boundaries sampled using ProcessInfo.systemUptime, while monitoring for unexpected divergence, a reasonable approach for this coarse association?

You'll need to test it for yourself, but yes, I expect it will work well. One suggestion on that point— if you're planning on integrating this into an existing, mature implementation, I would strongly suggest writing a VERY simple test app that does nothing but collect the sensor data you're interested in and correlate them with ProcessInfo.systemUptime (or any other source you want to try). I'd probably test "all" the time sources at once just to see how things compared, but the details there don't matter. Partly this is just to quickly confirm that this works the way you want, both so you can avoid wasted effort now and so you can validate things in the future. The "pro move" here would actually be to integrate this into your long-term testing process, so that you'll immediately notice if/when something were to change.

However, the bigger issue is that "real" app implementations often work very differently than people realize, and that disconnect can end up causing large amounts of wasted time and effort. The best way to counter that is to start with a clear understanding of what the "basic" behavior is, so that when something different happens, you'll start by looking at your app’s implementation, not the data.

That leads to here:

"Separately, does CMDeviceMotion (gyroscope/accelerometer) timestamping follow the same software-stamping path, or is IMU data timestamped closer to the sensor?"

Down this path lies madness. The problem here is that:

  1. There's no meaningful time difference between those two cases.

  2. Even if there were a meaningful time difference, there's no way your app can account for or compensate for that difference, so it still doesn't matter.

Since you mentioned CMDeviceMotion, a few other tips/tricks that might be useful:

  • If you're using motion data to drive your interface, the best way to do that is by directly using the latest sample property (CMMotionManager.deviceMotion) in your interface rendering code, NOT by receiving the data on a queue and routing it to your interface. Trying to collect and route the data yourself only introduces additional latency and complexity, which is why the properties exist in the first place.

  • If you're doing user activity tracking, I'd strongly recommend using APIs like CMSensorRecorder or CMBatchedSensorManager, NOT CMMotionManager. CMMotionManager delivers data at a far higher rate than meaningful user analysis requires (humans don't really move at 50hz), so a proper CMDeviceMotion implementation ends up needing to batch up events anyway. Even worse, most of the motion analysis issues I've seen with CMDeviceMotion are caused by the app interfering with its own event delivery, then failing to account for the distortion those delays created. All of those issues go away if you use our non-"real time" APIs.

__
Kevin Elliott
DTS Engineer, CoreOS/Hardware

We need to determine whether a pressure measurement's event time falls inside an application operation bounded by a supported monotonic clock. Could Apple clarify the supported contract for CMAltitudeData.timestamp on iOS?

The official contract is what the timestamp documentation says:

"The timestamp is the amount of time in seconds since the device booted."

...which, as you've noted, isn't all that strong. Informally, I believe it's actually mach_absolute_time, converted to a double of seconds.

Does CMAltitudeData.timestamp use the same epoch and rate as mach_absolute_time(),

We actually have relatively few time sources, with mach_absolute_time and mach_continuous_time being the primary underlying clocks.

If only some are compatible, which ones?

DispatchTime.uptimeNanoseconds or ProcessInfo.systemUptime?

Both of those derive from mach_absolute_time.

CACurrentMediaTime(),

I think this does too, but its implementation is a bit more complicated.

Does it advance or pause during device sleep, device lock, application suspension, and background execution? In particular, are its sleep/suspension semantics guaranteed to match any of the clocks above?

I believe all of the clocks above will behave identically.

Is there a supported API for converting CMLogItem.timestamp to a host CMClock/Mach time, or for sampling “now” in the exact same clock domain used by CMAltitudeData.timestamp?

The main issue here is how "accurate" you think the value will be. If you're trying to correlate events within a second or so, then the comparison is relatively straightforward; however, the more precise you try and make that comparison, the more "philosophical" this question becomes. Our altimeter isn't doing its own timestamping, so below a certain threshold, things like event processing jitter inside Locationd start having a significant effect.

Are the answers contractual across supported iPhone hardware and iOS releases, or are they implementation details that applications should not rely on?

Both? Strictly speaking, they're implementation details that could theoretically change at any time. Having said that, the implementation hasn't changed since iOS 4 (when CoreMotion was introduced), and I don't see any particular reason why it would.

If direct comparison is not supported, what Apple-supported clock or conversion mechanism should an application use to compare a CMAltitudeData event time with two application-side monotonic operation boundaries?

The paranoid implementation here would be for your app to regularly correlate the times returned by the various clock sources you were trying to correlate. Sampling time differences means the times would never align; however, I think you'd find that the actual divergence was always within a fairly narrow band, assuming you were careful not to introduce your own divergence [1].

[1] The main issue here is that thread activity can delay event delivery, which would push CMAltitudeData "back" in time. However, as long as you’re only trying to correlate user-relevant wall times, that divergence shouldn't be large enough to matter.

__
Kevin Elliott
DTS Engineer, CoreOS/Hardware

Thank you, Kevin—this helps considerably. We’re aiming for coarse association with an application operation, not sub-millisecond pressure timing.

Your point that the altimeter does not perform its own timestamping raises one remaining question: what event does CMAltitudeData.timestamp represent—receipt of a sensor reading by the system, creation of the Core Motion event, or an estimate of when pressure was measured? Can a newly delivered event contain an older or filtered pressure measurement whose age is not represented by that timestamp?

For implementation, is comparing that timestamp directly with operation boundaries sampled using ProcessInfo.systemUptime, while monitoring for unexpected divergence, a reasonable approach for this coarse association?

We understand that this would not establish the physical measurement instant or a guaranteed error bound.

"Separately, does CMDeviceMotion (gyroscope/accelerometer) timestamping follow the same software-stamping path, or is IMU data timestamped closer to the sensor?"

Accepted Answer

Your point that the altimeter does not perform its own timestamping raises one remaining question: what event does CMAltitudeData.timestamp represent—receipt of a sensor reading by the system, creation of the Core Motion event, or an estimate of when pressure was measured?

locationd collects and stores the data from our various sensors, and that data store* is what then sends the motion events you receive. SO, generally speaking, its behavior is probably closer to this:

*Just to clarify, this doesn't mean that locationd specifically stores every reading it receives and then sends it to your app at some later point. The process of sending data to clients and storing that data all happens as part of the same process.

CMAltitudeData.timestamp represent—receipt of a sensor reading by the system

However, the problem here is that the details of exactly how our sensors generate and process data vary considerably across our full device range and, in fact, a huge part of locationd's "job" is to provide relatively consistent behavior across a very wide range of sensor implementations. Putting that in more concrete terms, if these differences ACTUALLY matter to your app’s implementation, then I think you're expecting a level of precision that the system was never really designed to provide.

Can a newly delivered event contain an older or filtered pressure measurement whose age is not represented by that timestamp?

No.

For implementation, is comparing that timestamp directly with operation boundaries sampled using ProcessInfo.systemUptime, while monitoring for unexpected divergence, a reasonable approach for this coarse association?

You'll need to test it for yourself, but yes, I expect it will work well. One suggestion on that point— if you're planning on integrating this into an existing, mature implementation, I would strongly suggest writing a VERY simple test app that does nothing but collect the sensor data you're interested in and correlate them with ProcessInfo.systemUptime (or any other source you want to try). I'd probably test "all" the time sources at once just to see how things compared, but the details there don't matter. Partly this is just to quickly confirm that this works the way you want, both so you can avoid wasted effort now and so you can validate things in the future. The "pro move" here would actually be to integrate this into your long-term testing process, so that you'll immediately notice if/when something were to change.

However, the bigger issue is that "real" app implementations often work very differently than people realize, and that disconnect can end up causing large amounts of wasted time and effort. The best way to counter that is to start with a clear understanding of what the "basic" behavior is, so that when something different happens, you'll start by looking at your app’s implementation, not the data.

That leads to here:

"Separately, does CMDeviceMotion (gyroscope/accelerometer) timestamping follow the same software-stamping path, or is IMU data timestamped closer to the sensor?"

Down this path lies madness. The problem here is that:

  1. There's no meaningful time difference between those two cases.

  2. Even if there were a meaningful time difference, there's no way your app can account for or compensate for that difference, so it still doesn't matter.

Since you mentioned CMDeviceMotion, a few other tips/tricks that might be useful:

  • If you're using motion data to drive your interface, the best way to do that is by directly using the latest sample property (CMMotionManager.deviceMotion) in your interface rendering code, NOT by receiving the data on a queue and routing it to your interface. Trying to collect and route the data yourself only introduces additional latency and complexity, which is why the properties exist in the first place.

  • If you're doing user activity tracking, I'd strongly recommend using APIs like CMSensorRecorder or CMBatchedSensorManager, NOT CMMotionManager. CMMotionManager delivers data at a far higher rate than meaningful user analysis requires (humans don't really move at 50hz), so a proper CMDeviceMotion implementation ends up needing to batch up events anyway. Even worse, most of the motion analysis issues I've seen with CMDeviceMotion are caused by the app interfering with its own event delivery, then failing to account for the distortion those delays created. All of those issues go away if you use our non-"real time" APIs.

__
Kevin Elliott
DTS Engineer, CoreOS/Hardware

Thanks, Kevin—this answers our remaining questions and gives us a practical path forward. We’ll start with a minimal test comparing the sensor timestamps with systemUptime, then use that baseline to check our app’s behavior. We’ll keep the claim to coarse association with the capture operation, rather than precision the system wasn’t designed to provide.

Really appreciate the detailed explanations and implementation advice.

Thanks, Kevin—this answers our remaining questions and gives us a practical path forward. We’ll start with a minimal test comparing the sensor timestamps with systemUptime, then use that baseline to check our app’s behavior.

Briefly expanding on my previous answer, I've seen a lot of CMMotionManager issues which were actually caused by the app’s larger threading patterns distorting event delivery and the app then failing to account for that.

Putting that in more concrete terms, if you receive CMDeviceMotion events on the main thread and then process them as they arrive, you'll often find that detailed motion analysis doesn't really seem to work right. It will seem to be working "ok", but then regularly have glitches or defects that don't have any clear cause.

What's actually going on here is that other event activity on the main thread is introducing timing "gaps" in the data, so the system either drops events or delivers “bursts" of events instead of a steady stream. Regardless, if your engine simply processes events on arrival without accounting for timing, then those bursts end up throwing the entire analysis off.

Adding to the fun, simply moving the processing off the main thread solves that issue by immediately introducing new ones. Off the main thread, you generally want to avoid processing the data on the thread (so you don't end up blocking "yourself", recreating the problem you're trying to fix), but the naive approach of simply sending every event to a new thread then generates a lot of thread wakes, throwing away more performance. The solution to that is to collect a set of events and then send them for processing in batches...

...which is what DIRECTLY led to classes like CMSensorRecorder and CMBatchedSensorManager. They separate data collection from data processing, sidestepping all of these issues. Those are the APIs most user motion* analysis apps should be using.

*If you're tracking device motion to implement things like motion-based controls, then CMDeviceMotion is a great option, but building that around the event delivery system is probably a mistake. For motion interfaces, the better option is to build out your interface render engine, then use the property accessors to directly retrieve the device’s "current" position during rendering. This use case is EXACTLY why those property accessors were created.

We’ll keep the claim to coarse association with the capture operation, rather than precision the system wasn’t designed to provide.

FYI, ironically, the reason we're somewhat vague about the timestamps anchoring is actually that it DOES need to be precise. That is, the key role of the timestamp is to establish the timing gap between individual events, as accurate event "spacing" is one of the most critical factors in accurate motion analysis. However, the tricky part of that is that what matters for that is the timing of collection relative to each other, NOT relative to the absolute event itself.

Putting that in concrete terms, if you were to compare these three cases:

  1. Timestamp occurs immediately "before" event.

  2. Timestamp occurs immediately "after" event.

  3. Timestamp collection is inconsistent, sometimes before, sometimes after.

...You'd find that the motion analysis of 1 & 2 is essentially identical, aside from a TINY time shift that’s small enough to not really be relevant. You'd find that the analysis from #3 is... not great, as the inconsistent event spacing distorts the overall analysis.

THAT dynamic is why CoreMotion is vague about what its timestamp actually "means.” It needs to ensure that the timestamp of a given event stream is consistent within that time stream, but different sensor designs or architectural changes mean that different collection "points" may work better for different event streams. The vagueness of the definition lets them pick the best approach without worrying about breaking the API contract.

Really appreciate the detailed explanations and implementation advice.

You're very welcome.

__
Kevin Elliott
DTS Engineer, CoreOS/Hardware

CMLogItem.timestamp: which clock is it on, and how to convert to AVCaptureSession.synchronizationClock / host time?
 
 
Q