Skip to content

fix: relativeOdi gap segmentation - #69

Merged
abdulsaheel merged 2 commits into
mainfrom
fix/relative-odi-gap-segmentation
Sep 19, 2026
Merged

abdulsaheel merged 2 commits into
mainfrom
fix/relative-odi-gap-segmentation

Conversation

@abdulsaheel

@abdulsaheel abdulsaheel commented Sep 19, 2026

Copy link
Copy Markdown
Contributor

relativeOdi was missing the same gap-segmentation fix cvhr_apnea already has. analyzedHours used the raw first-to-last ts span, so a charging/off-wrist gap dilutes odiPerHour and burdenPct — same failure mode a 2h charging break already caused once in cvhr_apnea (80.0/h -> 53.3/h on identical beats).

worse here: the rolling AC/DC and baseline windows are index-windowed with zero ts awareness, so they could blend samples across a real gap and score a perfusion-baseline jump as a fake desaturation right at the boundary.

fix mirrors cvhr_apnea.dart: split at gaps > maxGapSec (default 30s), window and score each segment on its own, sum only observed spans into analyzedHours, fix burdenPct's denominator the same way.

no callers yet (dormant, confirmed via grep), so no kAlgoVersion bump needed.

test plan:

  • gap-free existing tests unaffected (single segment = same math as before)
  • new: two clean segments across a 2h gap match the summed-segment-hours rate, not the diluted raw-span rate
  • new: a DC/perfusion baseline jump across a gap scores zero dips at the boundary
  • new: default maxGapSec is a no-op on gap-free input

Summary by Sourcery

Segment relative ODI recordings at substantial timestamp gaps and base scoring on trusted observed time.

New Features:

  • Add configurable gap segmentation to relative ODI analysis so discontinuous recordings are scored as independent gap-free segments.

Bug Fixes:

  • Prevent charging, off-wrist, and perfusion-baseline gaps from diluting ODI rates or burden percentages or creating false boundary desaturations.

Enhancements:

  • Calculate analyzed hours from trusted observed segment time and preserve per-segment rolling-window and baseline isolation.

Tests:

  • Add regression coverage for charging-gap denominator handling, baseline jumps across gaps, and gap-free default behavior.

same bug cvhr_apnea already fixed once: analyzedHours used the raw
tsSec.last-first span, so a charging/off-wrist gap in the middle
dilutes odiPerHour and burdenPct. the rolling AC/DC/baseline windows
were also plain index windows with no gap awareness, so they could
blend pre-gap and post-gap samples across a hole (perfusion jump
scored as a fake dip at the boundary).

ported the same fix: split at gaps > maxGapSec, window/score each
segment on its own, sum only observed spans into analyzedHours, fix
burdenPct's denominator the same way. no callers yet so no wiring/
kAlgoVersion change.

@sourcery-ai sourcery-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry @abdulsaheel, you've used your own review budget of 250,000 diff characters for the last 7 days.

You can request another review in 6 days and 7 hours by commenting @sourcery-ai review. Upgrade to get a review now.

@coderabbitai

coderabbitai Bot commented Sep 19, 2026

Copy link
Copy Markdown

Warning

Review limit reached

Next included review available in 31 minutes.

Check out review usage here.

View limit details

Limit details: You’ve used the included review currently available.

You've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository.

Learn how review limits work.

Review configuration:

⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Advanced

Run ID: 5e43695a-6ec5-4b6b-a846-8d5dd1cc0bea

📥 Commits

Reviewing files that changed from the base of the PR and between 9f82ccf and 52f8d27.

📒 Files selected for processing (2)
  • lib/src/onehz/respiration/relative_odi.dart
  • test/onehz/respiration_test.dart

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@sourcery-ai

sourcery-ai Bot commented Sep 19, 2026

Copy link
Copy Markdown

Reviewer's Guide

Fixes relativeOdi to mirror cvhr_apnea’s gap-aware processing: recording gaps are segmented before windowing and scoring, observed segment spans drive ODI and burden denominators, and regression tests cover dilution, boundary artifacts, and gap-free compatibility.

Flow diagram for gap-aware relative ODI processing

flowchart TD
    A["Input red, IR, and timestamps"] --> B["Split at gaps greater than maxGapSec"]
    B --> C["Process each gap-free segment"]
    C --> D["Compute segment-scoped AC/DC windows"]
    D --> E["Compute segment-scoped rolling baseline"]
    E --> F["Detect qualifying dips"]
    F --> G["Accumulate dip metrics and observed span"]
    G --> H["Calculate ODI per hour and burdenPct"]
Loading

File-Level Changes

Change Details Files
Segment relative ODI processing at recording gaps and aggregate only observed, gap-free durations.
  • Added configurable maxGapSec segmentation with a 30-second default.
  • Runs AC/DC rolling windows and baseline dip detection independently within each segment.
  • Sums segment spans for analyzedHours and uses the same observed-time denominator for burdenPct.
  • Preserves NaN/degenerate-segment handling and aggregates dip metrics across segments.
lib/src/onehz/respiration/relative_odi.dart
Added regression coverage for gap-aware rates, boundary isolation, and default behavior.
  • Verifies a two-hour charging gap does not dilute ODI or burden calculations.
  • Verifies a perfusion/DC baseline shift across a gap produces no boundary dip.
  • Verifies default segmentation leaves gap-free input mathematically unchanged.
test/onehz/respiration_test.dart

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

…nflict

combined #68's per-sample trustedCoverage scaling with #69's gap
segmentation: each segment now computes its own trustedCoverage and
scales its own hours contribution before summing into analyzedHours,
instead of one global scale applied after the fact. odiPerHour and
burdenPct both key off that combined analyzedHours.
@abdulsaheel
abdulsaheel merged commit 9db819b into main Sep 19, 2026
4 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant