Skip to content

feat(tag): add Tag.cumulativeIntegral() trapezoidal totalizer (#327)#331

Draft
HanSur94 wants to merge 3 commits into
mainfrom
claude/inspiring-dirac-00a0c7
Draft

feat(tag): add Tag.cumulativeIntegral() trapezoidal totalizer (#327)#331
HanSur94 wants to merge 3 commits into
mainfrom
claude/inspiring-dirac-00a0c7

Conversation

@HanSur94

Copy link
Copy Markdown
Owner

What was built

Adds a new additive public method cumulativeIntegral() to the base Tag class (libs/SensorThreshold/Tag.m), inherited by every Tag subclass through the existing getXY/getXYRange seam. Returns the running trapezoidal (area-under-curve) integral of Y w.r.t. X — the totalizer primitive (flow→volume, power→energy, current→charge, dose rate→accumulated dose).

[t, cum] = tag.cumulativeIntegral();                 % full series; cum(end) = grand total
[t, cum] = tag.cumulativeIntegral('Range',[t0 t1]);  % within a time window
total    = tag.cumulativeIntegral('Range',[t0 t1]);  % 1-out form → scalar grand total

Design:

  • Gap-robust NaN handling — per-segment trapezoid areas (0.5·dt·(Y_i+Y_{i+1})), non-finite segments zeroed, then cumsum with a leading 0. Algebraically identical to cumtrapz(X,Y) when no NaNs are present; an interior NaN zeroes only its two flanking segments instead of poisoning the entire tail.
  • Kind-aware — emits Tag:integralOnDiscrete warning on a 'state' (discrete/ZOH) channel, since area-under-staircase is rarely intended; still returns the value.
  • nargout dispatch — 2-out returns [X, cum], 1-out returns scalar cum(end). Empty data → 0 (scalar) / [] (series).
  • 'Range' parsed in the lib's existing inline name-value style; unknown keys → Tag:unknownOption.

Backward-compatible & additive only: no public signature, serialized format, toStruct/fromStruct, or DashboardWidget/Tag interface change. Toolbox-free, pure MATLAB/Octave, no MEX. The abstract-stub count stays at exactly 6 (the new method is concrete, contains no Tag:notImplemented).

How it was verified

  • TestTag suite: 32/32 pass in the live MATLAB session (8 new cumulativeIntegral cases: ramp area, non-uniform spacing vs cumtrapz, 'Range' windowing, scalar form, MockTag empty-guard, NaN-gap, discrete-channel warning, unknown-option error).
  • Smoke test (live): ramp Y=2 over 0:4[0 2 4 6 8], total 8; interior NaN → [0 0 0 1 2] (no NaN tail); empty → 0.
  • MISS_HIT mh_style + mh_lint: everything seems fine on both changed files.

Closes #327


AI-built via /feature-build — needs human review before merge.

HanSur94 added 3 commits June 29, 2026 21:17
…egrator

- Concrete public method on the Tag base class (not abstract, no Tag:notImplemented)
- Gap-robust: per-segment 0.5*dt*(Y_i+Y_{i+1}), non-finite segments zeroed
  so interior NaN does not poison the running tail; identical to cumtrapz
  when data is clean (cum(1)=0)
- Single 'Range',[t0 t1] name-value option dispatches to getXYRange()
- Empty series: 1-out -> 0, 2-out -> [],[]
- Single-sample series: cum=0 (no interval = no area)
- Emits Tag:integralOnDiscrete warning for StateTag kind, still returns value
- 1-out form: scalar cum(end); 2-out form: [X, cum] row vectors
- Abstract-stub count unchanged: 6 occurrences of Tag:notImplemented
- testCumulativeIntegralUniformRamp: constant Y=2 over 0:4 -> [0 2 4 6 8]
- testCumulativeIntegralNonUniform: X=[0 1 3 7] non-uniform spacing verified
- testCumulativeIntegralRangeWindow: 'Range' option derives expected from getXYRange
- testCumulativeIntegralScalarForm: 1-out total equals 2-out cum(end)
- testCumulativeIntegralEmptyData: MockTag returns 0 (1-out) and [] (2-out)
- testCumulativeIntegralNaNGap: interior NaN does not produce NaN tail
- testCumulativeIntegralDiscreteWarns: StateTag emits Tag:integralOnDiscrete
- testCumulativeIntegralUnknownOption: bogus key throws Tag:unknownOption
- Existing testAbstractMethodCount unchanged (still asserts 6 stubs)
@codecov

codecov Bot commented Jun 29, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 84.09091% with 7 lines in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
libs/SensorThreshold/Tag.m 84.09% 7 Missing ⚠️

📢 Thoughts on this report? Let us know!

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.

Tag: add cumulativeIntegral() — trapezoidal totalizer series (running ∫ over getXY)

1 participant