You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
The relational (two-tag) analysis dimension was opened by correlate(other) (#340), which returns a single scalar Pearson r at zero lag — instantaneous agreement. It answers "do A and B move together?" but structurally cannot answer "how far does channel B trail channel A?".
Transport delay and propagation lag are canonical sensor-analysis questions:
how long does an upstream temperature change take to reach a downstream sensor?
how far does a vibration wavefront trail its source?
does a control output lead a process variable, and by how much?
correlate (#340) only sees zero-lag agreement, so two perfectly-related-but-delayed channels look uncorrelated to it. The best-lag argmax of a cross-correlation is the standard toolbox-free answer. Today a FastSense user must drop to getXY on both tags, resample onto a uniform grid, hand-roll the lagged sums, and find the argmax — exactly the boilerplate the Tag analysis family exists to eliminate.
Proposed feature
A base-Tag convenience method returning the best time-lag (and the correlation at that lag) between two tags, toolbox-free:
dt =tagA.lagCorrelation(tagB); % scalar: best lag in TIME units (B lags A by +dt)
[dt, r] =tagA.lagCorrelation(tagB); % + Pearson r at that lag
[dt, r, lags, rr] =tagA.lagCorrelation(tagB); % + full lag axis (time) and r-vs-lag curve
... = tagA.lagCorrelation(tagB, 'MaxLag', 10); % cap the search window (time units); default = fraction of overlap
... = tagA.lagCorrelation(tagB, t0, t1); % over a time window (mirrors correlate/getStats range args)
Rough sketch
Single method on libs/SensorThreshold/Tag.m (base class → every subclass inherits it — same placement as correlate#340, getStats#223, spectrum#338, percentile#339). Builds on accessors already on every Tag: getXYRange (Tag.m:125) and valueAt (Tag.m:161, overridden per subclass):
[X, ~] = obj.getXYRange(t0, t1) for A over the overlapping window; establish a uniform grid tg = t0:dt:t1 (dt = median sample spacing of A, or explicit) so a lag index maps cleanly to a time delay — the one piece of real design surface.
Drop pairwise NaNs; guard n < 2 and zero-variance (constant channel → NaN, documented).
Cross-correlate toolbox-free over ±MaxLag samples (a short sliding-window loop of mean-removed normalized sums). Return best-lag dt = bestLagIndex * dt (+ optional r, lag axis, r-vs-lag curve).
Pure-function, read-only; no new property, notoStruct/fromStruct change, no Tag/DashboardWidget contract touched. The lag axis + r-vs-lag curve are a natural [x, y] pair the caller can hand to a standalone FastSense/ad-hoc plot.
Value
High — adds time-delay estimation, a core machinery/process-analysis capability that correlate (#340) structurally cannot provide; directly serves transport-delay, propagation, and lead/lag control-loop workflows that force the user out of FastSense today.
Constraints check
Toolbox-free: ✅ — the manual normalized cross-correlation sum is base MATLAB and Octave. Avoid xcorr / finddelay — those are Signal Processing Toolbox functions and must not be used.
Backward-compatible: ✅ — a brand-new read-only method cannot break existing scripts or serialized dashboards; no toStruct/fromStruct change.
Fit note (for triage): cross-correlation needs a uniform time grid to convert a lag index into a time delay — more design surface than the scalar correlate (#340). The method should state its policy in its header: ZOH-resample both tags onto a uniform grid (dt = median A-spacing or explicit) over the overlapping window, and default MaxLag to a fraction of that window. Documented design decision, not a blocker; reuses machinery already in the repo (valueAt ZOH; the intent of resampleUniform#308) and is toolbox-free either way.
Effort estimate
M — one method in a single file, plus a focused test: delayed copy of a signal → recovered lag ≈ known delay; zero-lag identical tags → lag ≈ 0, r ≈ 1; MaxLag clamping; ZOH alignment of mismatched timestamps; NaN and n < 2 / zero-variance guards; time-range path.
Dedup
Clean. No issue/PR mentions cross-correlation, xcorr, lag, time-delay, or coherence. correlate (#340) is the zero-lag scalar relational primitive — this is its time-delay sibling, orthogonal. DerivedTag/CompositeTag (arithmetic combination) and PR #206 (cross-machine comparison view) are different concerns. The entire unary analysis family (#223/#258/#306/#308/#312/#316/#326/#327/#328/#329/#338/#339) is orthogonal.
AI-proposed via /feature-scout — needs a human product decision before implementation.
Problem / motivation
The relational (two-tag) analysis dimension was opened by
correlate(other)(#340), which returns a single scalar Pearson r at zero lag — instantaneous agreement. It answers "do A and B move together?" but structurally cannot answer "how far does channel B trail channel A?".Transport delay and propagation lag are canonical sensor-analysis questions:
correlate(#340) only sees zero-lag agreement, so two perfectly-related-but-delayed channels look uncorrelated to it. The best-lag argmax of a cross-correlation is the standard toolbox-free answer. Today a FastSense user must drop togetXYon both tags, resample onto a uniform grid, hand-roll the lagged sums, and find the argmax — exactly the boilerplate the Tag analysis family exists to eliminate.Proposed feature
A base-
Tagconvenience method returning the best time-lag (and the correlation at that lag) between two tags, toolbox-free:Rough sketch
Single method on
libs/SensorThreshold/Tag.m(base class → every subclass inherits it — same placement ascorrelate#340,getStats#223,spectrum#338,percentile#339). Builds on accessors already on every Tag:getXYRange(Tag.m:125) andvalueAt(Tag.m:161, overridden per subclass):[X, ~] = obj.getXYRange(t0, t1)for A over the overlapping window; establish a uniform gridtg = t0:dt:t1(dt = median sample spacing of A, or explicit) so a lag index maps cleanly to a time delay — the one piece of real design surface.tgvia ZOH:a = arrayfun(@(t) obj.valueAt(t), tg),b = arrayfun(@(t) other.valueAt(t), tg)— reuses the existing per-subclassvalueAtalignment primitive (same conventioncorrelateTag: add correlate(other) — toolbox-free Pearson correlation between two tags (first relational/two-tag analysis primitive) #340 adopts).n < 2and zero-variance (constant channel →NaN, documented).±MaxLagsamples (a short sliding-window loop of mean-removed normalized sums). Return best-lagdt = bestLagIndex * dt(+ optional r, lag axis, r-vs-lag curve).Pure-function, read-only; no new property, no
toStruct/fromStructchange, no Tag/DashboardWidget contract touched. The lag axis + r-vs-lag curve are a natural[x, y]pair the caller can hand to a standaloneFastSense/ad-hoc plot.Value
High — adds time-delay estimation, a core machinery/process-analysis capability that
correlate(#340) structurally cannot provide; directly serves transport-delay, propagation, and lead/lag control-loop workflows that force the user out of FastSense today.Constraints check
xcorr/finddelay— those are Signal Processing Toolbox functions and must not be used.toStruct/fromStructchange.Tagcontract exactly as Tag: add correlate(other) — toolbox-free Pearson correlation between two tags (first relational/two-tag analysis primitive) #340/Tag: add spectrum() — toolbox-free single-sided amplitude spectrum (frequency-domain sibling to the Tag analysis family) #338/Tag: add percentile()/quantile() — toolbox-free order-statistics primitive (P50/P95/P99 & IQR, order-stat sibling to getStats #223) #339/Tag: add a public getStats() statistics primitive (N/Min/Max/Mean/Rms/Std over a series or time range) #223 do; reusesgetXYRange+valueAt.Fit note (for triage): cross-correlation needs a uniform time grid to convert a lag index into a time delay — more design surface than the scalar
correlate(#340). The method should state its policy in its header: ZOH-resample both tags onto a uniform grid (dt = median A-spacing or explicit) over the overlapping window, and defaultMaxLagto a fraction of that window. Documented design decision, not a blocker; reuses machinery already in the repo (valueAtZOH; the intent ofresampleUniform#308) and is toolbox-free either way.Effort estimate
M — one method in a single file, plus a focused test: delayed copy of a signal → recovered lag ≈ known delay; zero-lag identical tags → lag ≈ 0, r ≈ 1;
MaxLagclamping; ZOH alignment of mismatched timestamps; NaN andn < 2/ zero-variance guards; time-range path.Dedup
Clean. No issue/PR mentions cross-correlation, xcorr, lag, time-delay, or coherence.
correlate(#340) is the zero-lag scalar relational primitive — this is its time-delay sibling, orthogonal.DerivedTag/CompositeTag(arithmetic combination) and PR #206 (cross-machine comparison view) are different concerns. The entire unary analysis family (#223/#258/#306/#308/#312/#316/#326/#327/#328/#329/#338/#339) is orthogonal.AI-proposed via /feature-scout — needs a human product decision before implementation.