feat: EOlife X phase-timed waveform reconstruction#291
Open
sorlob wants to merge 8 commits into
Open
Conversation
Add TimeSeriesBase.correct_clock_drift(anchor_time, drift_point, *, drift=..., true_time=...) for the common workflow of correcting a linear clock drift from one anchor and one drift observation. The drift can be expressed either as a signed Timedelta (device − true) or via the wall-clock time at the second observation; the two forms are mutually exclusive. The method computes the appropriate scale_factor and delegates to scale_time_index. Rework the scale_time_index docstring to name clock-drift correction as the primary use case, give the explicit mapping formula, and explain how to derive scale_factor from two sync points so the method is discoverable by users searching for "drift". Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Clock drift is a property of a clock, which typically governs more than one channel/label of a recording — and some devices have several clocks (e.g. via OEM modules). Correcting only some of the channels sharing a drifted clock would desynchronise them, and correcting unaffected channels would introduce new desync. Document that users should loop over all channels and labels driven by the affected clock.
Allow modifying the time index in-place by passing inplace=True, avoiding the copy overhead when the original object is no longer needed. Default remains inplace=False to preserve the existing copy-on-modify semantics.
The offset represents a deliberate alignment shift, not a clock-rate artefact — it should not be scaled along with the recording. Previously scale_time_index absorbed the offset into time_index and reset _offset to zero, making it impossible to recover the original alignment after the fact. Now the offset is un-baked before scaling and re-applied unchanged afterwards, so _offset is preserved on the returned series and the effective timestamps (as returned by get_data) remain consistent.
Pull in inplace parameter, offset preservation fix, and metadata trail for correct_clock_drift (PR #290) for local use during eolife development.
Adds `data_timed` to `EOLifeRecord`. The 9 columns of the EOlife export are split into discrete events and reconstructed channels: Labels (3 with distinct + 1 IntervalLabel): - cycle_start: - insp_end: - exp_end: + "Uncharacterised Breaths" IntervalLabel (global, when present) Channels (4 reconstructed waveforms): - Vi (displayed): screen-accurate inspiratory volume ramp - V_exp: expiratory volume ramp for leak comparison - Vt (displayed): step-function Vt readout - f (displayed): step-function respiratory rate readout The `f (displayed)` step function was reverse-engineered from screen-recording ground truth on an EOlife X (firmware v2.1.3) — rolling 3-breath mean of cycle durations, updated at next breath onset, set to NaN when rate > 60 /min. Contiguous uncharacterised breaths — those where the device could not determine Ti and Te, exported as literal "Na" (always absent together, never one without the other) — are exposed as an `IntervalLabel` (`eolife_uncharacterised_breaths`). The `source` metadata is derived from the serial number prefix (e.g. "EOlife X0001126" → "EOlife X"). `add_ventilatory_feedback` dispatches each group as discrete events or channels and applies `DEFAULT_PLOT_STYLE` entries automatically. Includes an EOlife X firmware v2.1.3 test fixture and new test classes covering uncharacterised-breath intervals and `f (displayed)` firmware behaviour. Co-Authored-By: dpurkarthofer <dpurkarthofer@users.noreply.github.com> Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Previously f was bundled in exp_end and anchored at onset + Ti + Te, before the Tp pause — too early for the device to have computed the cycle time. f is now a separate label group anchored at onset + Ti + Te + Tp for fully characterised breaths, falling back to next inspiration onset when any of Ti, Te, or Tp is missing. Leakage / Leakage ratio are split into their own leakage group so the label addition order becomes Vt → f → Leakage / Leakage ratio. Co-Authored-By: dpurkarthofer <dpurkarthofer@users.noreply.github.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
data_timedtoEOLifeRecordwith timing-adjusted views of the 9 columns in the EOlife exportcycle_start(Cycle number, Ti, Te, Tp at inspiration onset),insp_end(Vi at end of inspiration),exp_end(Vt, f, Leakage, Leakage ratio at end of expiration)f (displayed)reverse-engineered from screen-recording ground truth on EOlife X firmware v2.1.3: rolling 3-breath mean of cycle durations, updated at next breath onset, NaN when rate > 60 /mineolife_uncharacterised_breathsIntervalLabel ("Uncharacterised Breaths")sourcemetadata derived from serial number prefix ("EOlife X0001126" → "EOlife X")add_ventilatory_feedbackdispatches groups as discrete events or channels and auto-appliesDEFAULT_PLOT_STYLE