Skip to content

Commit b36b814

Browse files
committed
libconcentratord: Implement xtal correction calculation.
1 parent e8415ee commit b36b814

File tree

1 file changed

+89
-5
lines changed

1 file changed

+89
-5
lines changed

libconcentratord/src/gnss.rs

Lines changed: 89 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,12 @@ use std::time::Duration;
44

55
use anyhow::{Context, Result};
66
use chrono::{DateTime, Timelike, Utc};
7-
use log::{debug, trace};
7+
use log::{debug, trace, warn};
88
use serde::{Deserialize, Deserializer, Serialize, Serializer};
99

1010
const GNSS_MAX_AGE: u32 = 30_000_000; // 30 seconds
11+
const XERR_INIT_AVG: usize = 16;
12+
const XERR_FILT_COEF: f64 = 256.0;
1113

1214
static TIME_SINCE_GPS_EPOCH: LazyLock<Mutex<Option<(GnssTimeSinceGpsEpoch, u32)>>> =
1315
LazyLock::new(|| Mutex::new(None));
@@ -21,30 +23,39 @@ static GNSS_LOCATION: LazyLock<Mutex<Option<(GnssLocation, u32)>>> =
2123
static STATIC_GNSS_LOCATION: LazyLock<Mutex<Option<GnssLocation>>> =
2224
LazyLock::new(|| Mutex::new(None));
2325

24-
#[derive(Debug, Clone, PartialEq)]
26+
static XTAL_CORRECT: LazyLock<Mutex<Option<XtalCorrect>>> = LazyLock::new(|| Mutex::new(None));
27+
28+
#[derive(Debug, Clone)]
2529
pub enum GnssResult {
2630
TimeSinceGpsEpoch(GnssTimeSinceGpsEpoch),
2731
DateTime(GnssDateTime),
2832
Location(GnssLocation),
2933
}
3034

31-
#[derive(Debug, Clone, PartialEq)]
35+
#[derive(Debug, Clone)]
3236
pub struct GnssTimeSinceGpsEpoch {
3337
pub time_since_gps_epoch: Duration,
3438
}
3539

36-
#[derive(Debug, Clone, PartialEq)]
40+
#[derive(Debug, Clone)]
3741
pub struct GnssDateTime {
3842
pub timestamp: DateTime<Utc>,
3943
}
4044

41-
#[derive(Debug, Clone, PartialEq)]
45+
#[derive(Debug, Clone)]
4246
pub struct GnssLocation {
4347
pub lat: f64,
4448
pub lon: f64,
4549
pub alt: f32,
4650
}
4751

52+
#[derive(Debug, Clone)]
53+
struct XtalCorrect {
54+
init_cpt: usize,
55+
init_acc: f64,
56+
xtal_correct: f64,
57+
}
58+
4859
#[derive(Debug, Default, Clone, PartialEq)]
4960
pub enum Device {
5061
#[default]
@@ -154,7 +165,9 @@ pub fn sync(result: &GnssResult, count_us_at_pps: u32) -> Result<()> {
154165
.ok_or_else(|| anyhow!("Strip nanoseconds error"))?;
155166

156167
let mut dt = GNSS_DATE_TIME.lock().unwrap();
168+
let prev_dt = dt.clone();
157169
*dt = Some((v, count_us_at_pps));
170+
calculate_xtal_error(prev_dt, dt.clone().unwrap());
158171
}
159172
GnssResult::Location(v) => {
160173
debug!(
@@ -191,6 +204,69 @@ pub fn sync(result: &GnssResult, count_us_at_pps: u32) -> Result<()> {
191204
Ok(())
192205
}
193206

207+
fn calculate_xtal_error(prev_dt: Option<(GnssDateTime, u32)>, current_dt: (GnssDateTime, u32)) {
208+
if let Some(prev_dt) = prev_dt {
209+
let prev_count_us_at_pps = prev_dt.1;
210+
let current_count_us_at_pps = current_dt.1;
211+
let count_us_delta =
212+
(current_dt.0.timestamp - prev_dt.0.timestamp).as_seconds_f64() * 1_000_000f64;
213+
if count_us_delta == 0.0 {
214+
// Avoid divide by
215+
trace!("count_us_delta == 0, skipping xtal error correction");
216+
return;
217+
}
218+
219+
let (count_us_diff, _) = current_count_us_at_pps.overflowing_sub(prev_count_us_at_pps);
220+
if count_us_diff == 0 {
221+
// PPS has not been triggered yet
222+
trace!("count_us_diff == 0, skipping xtal error correction");
223+
return;
224+
}
225+
226+
let xtal_error = count_us_diff as f64 / count_us_delta;
227+
debug!(
228+
"xtal error calculated, error: {:.12}, prev_count_us: {}, current_count_us: {}",
229+
xtal_error, prev_count_us_at_pps, current_count_us_at_pps
230+
);
231+
if xtal_error > 1.00001 || xtal_error < 0.99999 {
232+
warn!(
233+
"xtal error out of expected range, xtal_error: {:.6}",
234+
xtal_error
235+
);
236+
return;
237+
}
238+
239+
let mut xtal_correct = XTAL_CORRECT.lock().unwrap();
240+
if let Some(xtal_correct) = xtal_correct.as_mut() {
241+
if xtal_correct.init_cpt < XERR_INIT_AVG {
242+
xtal_correct.init_cpt += 1;
243+
xtal_correct.init_acc += xtal_error;
244+
} else if xtal_correct.init_cpt == XERR_INIT_AVG {
245+
xtal_correct.xtal_correct = XERR_INIT_AVG as f64 / xtal_correct.init_acc;
246+
debug!(
247+
"xtal correction calculated, xtal_correct: {:.12}",
248+
xtal_correct.xtal_correct
249+
);
250+
} else {
251+
let x = 1.0 / xtal_error;
252+
xtal_correct.xtal_correct = xtal_correct.xtal_correct
253+
- xtal_correct.xtal_correct / XERR_FILT_COEF
254+
+ x / XERR_FILT_COEF;
255+
debug!(
256+
"xtal correction calculated, xtal_correct: {:.12}",
257+
xtal_correct.xtal_correct
258+
);
259+
}
260+
} else {
261+
*xtal_correct = Some(XtalCorrect {
262+
init_cpt: 1,
263+
init_acc: xtal_error,
264+
xtal_correct: 1.0,
265+
})
266+
}
267+
}
268+
}
269+
194270
pub fn set_static_location(lat: f64, lon: f64, alt: f32) {
195271
if lat == 0.0 && lon == 0.0 && alt == 0.0 {
196272
return;
@@ -267,6 +343,14 @@ pub fn get_location_last_updated_at() -> Option<DateTime<Utc>> {
267343
}
268344
}
269345

346+
pub fn get_xtal_correct() -> f64 {
347+
if let Some(xtal_correct) = XTAL_CORRECT.lock().unwrap().as_ref() {
348+
xtal_correct.xtal_correct
349+
} else {
350+
1.0
351+
}
352+
}
353+
270354
fn parse_nmea(b: &[u8]) -> Result<Option<GnssResult>> {
271355
let v = nmea::parse_bytes(b).map_err(|e| anyhow!("NMEA parse error: {:?}", e))?;
272356

0 commit comments

Comments
 (0)