Sleep stages are not the same across devices

Four vocabularies, one of which overlaps the others. Merging them naively double-counts sleep, and the usual fix — trusting whichever device you trust more — throws away the finer measurement about half the time.

What each source actually emits

SourceVocabularyShape
Apple, iOS 16+Core, Deep, REM, Awake, InBedFour stages plus a containing interval
Apple, pre-iOS 16InBed, AsleepNo staging at all
Ouradeep, light, REM, awake, unmeasurableFixed 5-minute grid
Fitbit stagesdeep, light, rem, wakeVariable-length intervals
Fitbit classicasleep, restless, awakeCoarser; older devices and short sleeps
WHOOPPer-stage totalsNot a hypnogram — no ordering
FIT sleep_levelFive values definedSee below

Apple's InBed is the one that breaks arithmetic

InBed is not a fifth stage. It is the interval that contains the stages. Summing InBed with Core, Deep and REM counts the same minutes twice and produces a night longer than the time between going to bed and getting up.

It is modelled as a session boundary rather than a stage — which is also why an export from before iOS 16, containing only InBed and Asleep, contains no staging at all rather than a two-stage hypnogram.

"Restless" is asleep

Fitbit's classic vocabulary has no light stage. restless means asleep and moving. Two wrong readings are common: counting it as awake, which shortens every classic night; and mapping it to light sleep, which asserts a stage the device never determined. Asleep-unspecified, flagged as estimated, is what it actually is.

Disagreeing about resolution is not disagreeing about facts

This is the part that has no standard answer, so here is ours. When a four-stage source and a two-state source both cover one night, they are not two claims about the same thing where one is wrong. The coarse source is making a weaker claim that the fine one is consistent with.

Resolving that by source priority — "I trust my ring more than my watch" — discards the finer staging whenever the trusted device happens to be the coarser one, which is a real and silent loss. So the rule is: prefer the source with more distinct resolved stages, and fall back to priority only when they tie — with an override so that a fine source covering twenty minutes cannot beat a coarse one that staged the whole night. Unknown and InBed contribute no resolution.

The FIT corpus contains no hypnogram at all

Worth knowing before assuming a Garmin file will contest an Apple night. The FIT profile defines five sleep_level values. Across 19,068 files and 443,374 sleep records in our corpus, exactly 2 appear: awake (404,450) and unmeasurable (38,924). Light, deep and REM: zero.

So those files are daily-wellness recordings whose sleep message only ever says "awake" or "could not tell". Whether Garmin devices that do stage sleep write it in sleep_level or somewhere else is a question 68,068 files do not answer.

What this means for you