///|
/// A rolling analysis window with quality and headline metrics.
pub(all) struct WindowedAnalysis {
start : Int
end : Int
mean_rr : Double
mean_hr : Double
sdnn : Double
rmssd : Double
quality_ratio : Double
readiness_proxy : Double
} derive(FromJson, ToJson, Debug, Eq)
///|
/// Analyze overlapping windows without computing expensive spectral features.
pub fn analyze_windows(
intervals : Array[Double],
window_size : Int,
hop_size : Int,
config : HrvConfig,
) -> Array[WindowedAnalysis] {
let result = []
let ranges = make_segment_ranges(intervals.length(), window_size, hop_size)
for segment_range in ranges {
let window = slice_segment(intervals, segment_range)
let validation = validate_intervals(window, config)
let cleaned = valid_intervals(window, config)
let mean_rr = mean_value(cleaned)
let rmssd = calculate_rmssd(cleaned)
let quality = if window.length() == 0 {
0.0
} else {
validation.valid.to_double() / window.length().to_double()
}
let mean_scale = if mean_rr == 0.0 { 1.0 } else { mean_rr }
let proxy = (quality * 70.0 +
(rmssd / mean_scale * 100.0).clamp(min=0.0, max=30.0)).clamp(
min=0.0,
max=100.0,
)
result.push({
start: segment_range.start,
end: segment_range.end,
mean_rr,
mean_hr: if mean_rr <= 0.0 {
0.0
} else {
60000.0 / mean_rr
},
sdnn: calculate_sdnn(cleaned),
rmssd,
quality_ratio: quality,
readiness_proxy: proxy,
})
}
result
}
///|
/// Return the window with the highest readiness proxy.
pub fn best_window(windows : Array[WindowedAnalysis]) -> WindowedAnalysis? {
if windows.length() == 0 {
return None
}
let mut best = windows[0]
for window in windows {
if window.readiness_proxy > best.readiness_proxy {
best = window
}
}
Some(best)
}
///|
/// Return the window with the lowest quality.
pub fn worst_quality_window(
windows : Array[WindowedAnalysis],
) -> WindowedAnalysis? {
if windows.length() == 0 {
return None
}
let mut worst = windows[0]
for window in windows {
if window.quality_ratio < worst.quality_ratio {
worst = window
}
}
Some(worst)
}
///|
/// Calculate the median and spread of window RMSSD values.
pub fn window_rmssd_summary(
windows : Array[WindowedAnalysis],
) -> DistributionStats {
let values = []
for window in windows {
values.push(window.rmssd)
}
summarize_distribution(values)
}
///|
/// Compare two adjacent windows for a meaningful quality change.
pub fn window_quality_change(
left : WindowedAnalysis,
right : WindowedAnalysis,
) -> Double {
right.quality_ratio - left.quality_ratio
}
///|
/// Return the average recovery proxy across windows.
pub fn average_window_readiness(windows : Array[WindowedAnalysis]) -> Double {
let values = []
for window in windows {
values.push(window.readiness_proxy)
}
mean_value(values)
}