From 691a967083374348ab9a802a8bab19bf06d96535 Mon Sep 17 00:00:00 2001 From: LelsersLasers Date: Thu, 2 Jul 2026 21:51:48 -0700 Subject: [PATCH 1/9] Add a fallback slope of 1 when there is only 1 GPS point --- src/daq_log_parse/correlate.rs | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/src/daq_log_parse/correlate.rs b/src/daq_log_parse/correlate.rs index 2271d3c..61b0a19 100644 --- a/src/daq_log_parse/correlate.rs +++ b/src/daq_log_parse/correlate.rs @@ -3,6 +3,12 @@ use chrono::TimeZone as _; use crate::daq_log_parse::parse::ParsedMessage; +// If there is only one GPS point, it is not possible to do a linear regression. +// In that case, we will use this fallback slope value. It is in general safe to +// assume a slope of 1.0, since DAQ runs at 1 tick = 1 ms, but in post processing, +// there is not a reason to not run proper linear regression if there are multiple GPS points. +const REGRESSION_FALLBACK_SLOPE: f64 = 1.0; + pub struct CorrelationFunction { /// real_time ~= slope * log_time_ms + intercept_ms /// @@ -206,14 +212,23 @@ struct Point { /// Least squares linear regression. /// -/// Fits: +/// Note: on if there is only 1 point, uses REGRESSION_FALLBACK_SLOPE /// +/// Fits: /// y = slope * x + intercept fn linear_regression(points: &[Point]) -> Option<(f64, f64)> { - if points.len() < 2 { + if points.is_empty() { return None; } + // Fallback for single point: use a default slope and calculate intercept based on that point + if points.len() == 1 { + let p = &points[0]; + let slope = REGRESSION_FALLBACK_SLOPE; + let intercept = p.y - slope * p.x; + return Some((slope, intercept)); + } + let n = points.len() as f64; let mut sum_x = 0.0; From d6568bc4f748c9a6f46c95062ff1f30af245758f Mon Sep 17 00:00:00 2001 From: LelsersLasers Date: Thu, 2 Jul 2026 22:02:27 -0700 Subject: [PATCH 2/9] More useful information in correlate --- src/daq_log_parse/correlate.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/daq_log_parse/correlate.rs b/src/daq_log_parse/correlate.rs index 61b0a19..b91367c 100644 --- a/src/daq_log_parse/correlate.rs +++ b/src/daq_log_parse/correlate.rs @@ -155,6 +155,7 @@ pub fn time_correlate_chunk(chunk: Vec) -> CorrelationChunkResult if gps_points.is_empty() { // No GPS points found, can't correlate + log::warn!("No GPS points found in chunk, cannot correlate timestamps"); return CorrelationChunkResult::uncorrelated_new(chunk); } @@ -169,7 +170,7 @@ pub fn time_correlate_chunk(chunk: Vec) -> CorrelationChunkResult let (slope, intercept) = match linear_regression(&points) { Some(v) => v, None => { - log::error!("Failed to refit correlation line"); + log::error!("Failed to refit correlation line. Points: {:?}", points); return CorrelationChunkResult::uncorrelated_new(chunk); } }; @@ -205,6 +206,7 @@ pub fn time_correlate_chunk(chunk: Vec) -> CorrelationChunkResult ) } +#[derive(Debug)] struct Point { x: f64, // log timestamp ms y: f64, // unix timestamp ms From be228172b8efb08e9a912da9c9ce3b5a9afcfeae Mon Sep 17 00:00:00 2001 From: LelsersLasers Date: Thu, 2 Jul 2026 22:03:19 -0700 Subject: [PATCH 3/9] Log error (but continue) if slope is not 1 Allows insight on if the 1 point fallback is dangerous (some chunks with more than 1 point log error, and then fallback happens = bad) --- src/daq_log_parse/correlate.rs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/daq_log_parse/correlate.rs b/src/daq_log_parse/correlate.rs index b91367c..1086cb6 100644 --- a/src/daq_log_parse/correlate.rs +++ b/src/daq_log_parse/correlate.rs @@ -197,6 +197,11 @@ pub fn time_correlate_chunk(chunk: Vec) -> CorrelationChunkResult points.len() ); + // Log error (but continue) if slope is not ~1.0 + if (slope - 1.0).abs() > 0.001 { + log::error!("GPS correlation slope is not ~1.0"); + } + CorrelationChunkResult::correlated_new( chunk, CorrelationFunction { From 8cf5af0925dc8fb73255680ee5321f831edfb97a Mon Sep 17 00:00:00 2001 From: LelsersLasers Date: Thu, 2 Jul 2026 22:03:39 -0700 Subject: [PATCH 4/9] Log warning on fallback --- src/daq_log_parse/correlate.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/daq_log_parse/correlate.rs b/src/daq_log_parse/correlate.rs index 1086cb6..d385066 100644 --- a/src/daq_log_parse/correlate.rs +++ b/src/daq_log_parse/correlate.rs @@ -230,6 +230,7 @@ fn linear_regression(points: &[Point]) -> Option<(f64, f64)> { // Fallback for single point: use a default slope and calculate intercept based on that point if points.len() == 1 { + log::warn!("Only one GPS point found, using fallback slope of {}", REGRESSION_FALLBACK_SLOPE); let p = &points[0]; let slope = REGRESSION_FALLBACK_SLOPE; let intercept = p.y - slope * p.x; From 541cbb2420df72ae9c2a52330ed5233629ec8d66 Mon Sep 17 00:00:00 2001 From: LelsersLasers Date: Thu, 2 Jul 2026 22:03:51 -0700 Subject: [PATCH 5/9] fmt --- src/daq_log_parse/correlate.rs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/daq_log_parse/correlate.rs b/src/daq_log_parse/correlate.rs index d385066..3719fa3 100644 --- a/src/daq_log_parse/correlate.rs +++ b/src/daq_log_parse/correlate.rs @@ -230,7 +230,10 @@ fn linear_regression(points: &[Point]) -> Option<(f64, f64)> { // Fallback for single point: use a default slope and calculate intercept based on that point if points.len() == 1 { - log::warn!("Only one GPS point found, using fallback slope of {}", REGRESSION_FALLBACK_SLOPE); + log::warn!( + "Only one GPS point found, using fallback slope of {}", + REGRESSION_FALLBACK_SLOPE + ); let p = &points[0]; let slope = REGRESSION_FALLBACK_SLOPE; let intercept = p.y - slope * p.x; From 1a8d56f3d9dad39b4c7369ea69de405140e99124 Mon Sep 17 00:00:00 2001 From: LelsersLasers Date: Thu, 2 Jul 2026 22:09:58 -0700 Subject: [PATCH 6/9] Clean up wording in comment --- src/daq_log_parse/correlate.rs | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/daq_log_parse/correlate.rs b/src/daq_log_parse/correlate.rs index 3719fa3..a23d8d3 100644 --- a/src/daq_log_parse/correlate.rs +++ b/src/daq_log_parse/correlate.rs @@ -3,10 +3,9 @@ use chrono::TimeZone as _; use crate::daq_log_parse::parse::ParsedMessage; -// If there is only one GPS point, it is not possible to do a linear regression. -// In that case, we will use this fallback slope value. It is in general safe to -// assume a slope of 1.0, since DAQ runs at 1 tick = 1 ms, but in post processing, -// there is not a reason to not run proper linear regression if there are multiple GPS points. +// In that case we use this fallback slope value. It is generally safe to assume a +// slope of 1.0 since the DAQ runs at 1 tick = 1 ms, but in post-processing there +// is no reason not to run a proper linear regression when multiple GPS points exist. const REGRESSION_FALLBACK_SLOPE: f64 = 1.0; pub struct CorrelationFunction { From f7562cc1b1040a9420a223509d66d1899b8f2abf Mon Sep 17 00:00:00 2001 From: LelsersLasers Date: Thu, 2 Jul 2026 22:11:29 -0700 Subject: [PATCH 7/9] Logging the points count, not the points on failure Logging the points could lead to logging like a bazillion points --- src/daq_log_parse/correlate.rs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/daq_log_parse/correlate.rs b/src/daq_log_parse/correlate.rs index a23d8d3..69ee595 100644 --- a/src/daq_log_parse/correlate.rs +++ b/src/daq_log_parse/correlate.rs @@ -169,7 +169,10 @@ pub fn time_correlate_chunk(chunk: Vec) -> CorrelationChunkResult let (slope, intercept) = match linear_regression(&points) { Some(v) => v, None => { - log::error!("Failed to refit correlation line. Points: {:?}", points); + log::error!( + "Failed to refit correlation line. Points count: {}", + points.len() + ); return CorrelationChunkResult::uncorrelated_new(chunk); } }; @@ -210,7 +213,6 @@ pub fn time_correlate_chunk(chunk: Vec) -> CorrelationChunkResult ) } -#[derive(Debug)] struct Point { x: f64, // log timestamp ms y: f64, // unix timestamp ms From 06929d78983c55f58f59d184f9c1d51fc362d5d0 Mon Sep 17 00:00:00 2001 From: LelsersLasers Date: Thu, 2 Jul 2026 22:13:08 -0700 Subject: [PATCH 8/9] Replace hardset 1 with REGRESSION_FALLBACK_SLOPE Also logs REGRESSION_FALLBACK_SLOPE for easier debugging --- src/daq_log_parse/correlate.rs | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/src/daq_log_parse/correlate.rs b/src/daq_log_parse/correlate.rs index 69ee595..6006a00 100644 --- a/src/daq_log_parse/correlate.rs +++ b/src/daq_log_parse/correlate.rs @@ -199,9 +199,13 @@ pub fn time_correlate_chunk(chunk: Vec) -> CorrelationChunkResult points.len() ); - // Log error (but continue) if slope is not ~1.0 - if (slope - 1.0).abs() > 0.001 { - log::error!("GPS correlation slope is not ~1.0"); + // Log error (but continue) if slope is not ~REGRESSION_FALLBACK_SLOPE + if (slope - REGRESSION_FALLBACK_SLOPE).abs() > 0.001 { + log::error!( + "GPS correlation slope ({:.9}) deviates from expected {:.3}.", + slope, + REGRESSION_FALLBACK_SLOPE, + ); } CorrelationChunkResult::correlated_new( From 1eb4b7b41573665a59391a85635838180f75039e Mon Sep 17 00:00:00 2001 From: LelsersLasers Date: Thu, 2 Jul 2026 22:13:31 -0700 Subject: [PATCH 9/9] Fix typo --- src/daq_log_parse/correlate.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/daq_log_parse/correlate.rs b/src/daq_log_parse/correlate.rs index 6006a00..72ddc54 100644 --- a/src/daq_log_parse/correlate.rs +++ b/src/daq_log_parse/correlate.rs @@ -224,7 +224,7 @@ struct Point { /// Least squares linear regression. /// -/// Note: on if there is only 1 point, uses REGRESSION_FALLBACK_SLOPE +/// Note: if there is only 1 point, uses REGRESSION_FALLBACK_SLOPE /// /// Fits: /// y = slope * x + intercept