Bug report: ResMed AirSense adapter (cpap-py) — 4 bugs found during SleepLab validation
## Summary Validation testing against a ResMed AirSense 10 DATALOG (`cam` dataset, 72 nights, 2026-02-03 – 2026-05-15) with OSCAR as ground truth revealed four bugs in the ResMed adapter. Results are from SleepLab integration at [joshuamyers-dev/sleeplab#44](https://github.com/joshuamyers-dev/sleeplab/pull/44). Note: `MANUFACTURER_VALIDATED[\"ResMed\"] = False` is correctly set — these bugs explain why. --- ## Bug 1 — Metrics timestamps anchored to Unix epoch (BLOCKER) All `session_metrics` rows returned by `_extract_metrics_from_timeseries` have timestamps starting at `1970-01-01T00:00:00Z`. The values are correct but the timestamps are relative offsets (elapsed seconds from session start), not absolute timestamps. **Observed:** ``` ts | mask_pressure | leak 1970-01-01 00:00:00+00 | 2.52 | 0.0000 1970-01-01 00:00:02+00 | 3.34 | 0.4400 ``` **Expected:** ``` ts | mask_pressure | leak 2026-02-10 21:30:42+00 | 2.52 | 0.0000 2026-02-10 21:30:44+00 | 3.34 | 0.4400 ``` **Root cause:** `timestamps_low` contains elapsed seconds since session start (0, 2, 4, …). The SleepLab integration stores these directly as unix timestamps. The fix requires adding `session.start_time.timestamp()` to each `ts` value — either in the adapter's output contract (document that `timestamps_low` is relative), or by having `map_timeseries_to_metrics` return absolute timestamps as all other adapters do. **Downstream impact:** Charts are unusable (all metrics displayed at 1970). The Event Inspector window query (`WHERE sm.ts >= event_datetime - N seconds`) returns zero rows because metrics are 56 years before any event timestamp — event detail views are completely broken for cpap-parser ResMed sessions. --- ## Bug 2 — Duration accuracy: STR.edf summary vs PLD EDF header cpap-parser reads session duration from `STR.edf` daily summaries; OSCAR and SleepLab's native path read from the PLD EDF header (`num_records × duration_per_record`). **Accuracy vs OSCAR (n=72 shared nights):** | Metric | cpap-parser vs OSCAR | |---|---| | Median offset | +60 seconds | | Max absolute error | **+30,060 seconds (8.4 hours)** | | Within ±60 s | 40% of nights | The +60s median is a systematic bias. The 30,060s outlier occurred on a night where the DATALOG had a 4-minute fragment (interrupted session) while STR.edf recorded the full sleep period — two different things being measured. **Reference:** 2026-03-06: cpap-parser=30,300s, native=240s, OSCAR=0s (same night, different interpretations of "session duration"). --- ## Bug 3 — Device serial returns "Unknown" `directory.machine.serial_number` returns the string `"Unknown"` instead of the actual device serial. The serial is available in `Identification.json` at the SD card root and in the EDF signal headers. **Observed:** ```python directory.machine.serial_number # → 'Unknown' ``` **Expected:** `'23233254908'` (value confirmed from `Identification.json` and OSCAR import of same SD card) --- ## Bug 4 — Ghost sessions from STR.edf history `directory.daily_summaries` contains **439 entries** spanning 2025-02-04 to 2026-05-15 (full STR.edf device history). `directory.sessions` contains only **119 blocks** for 2026-02-03 to 2026-05-15 (EDF files present in DATALOG). This means **367 daily summaries have no corresponding session data** — consuming systems that iterate `daily_summaries` to create session records will create empty "ghost" entries with AHI/duration but no metrics, events, or waveform for the historical dates. This is specific to the ResMed adapter's STR.edf-based architecture. The Löwenstein adapter has 6 summaries matching 6 actual session blocks (1:1 coverage, no ghosts). Other Rust-backed adapters are expected to behave like Löwenstein. **Suggestion:** Either filter `daily_summaries` to only dates with at least one corresponding entry in `directory.sessions`, or expose a `has_session_data: bool` flag per summary so consuming applications can distinguish summary-only dates. --- ## Test environment - cpap-parser version: from `main` (installed via `pip install git+https://gitlab.com/open-cpap/cpap-parser`) - cpap-py version: 1.0.0 - Device: ResMed AirSense 10 AutoSet - Dataset: 72 nights DATALOG + STR.edf - Ground truth: OSCAR v1.7.x summary export - Related SleepLab PR: https://github.com/joshuamyers-dev/sleeplab/pull/44
issue