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
| Source | Vocabulary | Shape |
|---|---|---|
| Apple, iOS 16+ | Core, Deep, REM, Awake, InBed | Four stages plus a containing interval |
| Apple, pre-iOS 16 | InBed, Asleep | No staging at all |
| Oura | deep, light, REM, awake, unmeasurable | Fixed 5-minute grid |
Fitbit stages | deep, light, rem, wake | Variable-length intervals |
Fitbit classic | asleep, restless, awake | Coarser; older devices and short sleeps |
| WHOOP | Per-stage totals | Not a hypnogram — no ordering |
FIT sleep_level | Five values defined | See 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
- Do not add InBed to stage totals.
- Do not compare a stage total from a four-stage device with one from a two-state device and conclude that either is broken.
- Do check which generation your Apple export is — see what is in an export.zip.
- Do treat WHOOP's per-stage totals as totals: there is no ordering in them to compare interval by interval.