@@ -4,10 +4,12 @@ use std::time::Duration;
44
55use anyhow:: { Context , Result } ;
66use chrono:: { DateTime , Timelike , Utc } ;
7- use log:: { debug, trace} ;
7+ use log:: { debug, trace, warn } ;
88use serde:: { Deserialize , Deserializer , Serialize , Serializer } ;
99
1010const 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
1214static 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)>>> =
2123static 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 ) ]
2529pub enum GnssResult {
2630 TimeSinceGpsEpoch ( GnssTimeSinceGpsEpoch ) ,
2731 DateTime ( GnssDateTime ) ,
2832 Location ( GnssLocation ) ,
2933}
3034
31- #[ derive( Debug , Clone , PartialEq ) ]
35+ #[ derive( Debug , Clone ) ]
3236pub struct GnssTimeSinceGpsEpoch {
3337 pub time_since_gps_epoch : Duration ,
3438}
3539
36- #[ derive( Debug , Clone , PartialEq ) ]
40+ #[ derive( Debug , Clone ) ]
3741pub struct GnssDateTime {
3842 pub timestamp : DateTime < Utc > ,
3943}
4044
41- #[ derive( Debug , Clone , PartialEq ) ]
45+ #[ derive( Debug , Clone ) ]
4246pub 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 ) ]
4960pub 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+
194270pub 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+
270354fn 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