From 85967d6dbb5d0d8afb0a09ed25abbe9d067c12b7 Mon Sep 17 00:00:00 2001 From: bensynapse <118375461+bensynapse@users.noreply.github.com> Date: Thu, 17 Sep 2026 20:07:25 +0300 Subject: [PATCH] Preserve FIT records on the equator and prime meridian --- core/src/lib.rs | 12 ++++-- core/tests/fit_parser_tests.rs | 68 ++++++++++++++++++++++++++++++++++ 2 files changed, 76 insertions(+), 4 deletions(-) diff --git a/core/src/lib.rs b/core/src/lib.rs index 0709d7e..459364b 100644 --- a/core/src/lib.rs +++ b/core/src/lib.rs @@ -661,7 +661,7 @@ impl FitParser { self.read_u8().unwrap_or(0), self.read_u8().unwrap_or(0), ]; - if signature != [b'.', b'F', b'I', b'T'] { + if signature != *b".FIT" { return coordinates; } @@ -890,7 +890,7 @@ impl FitParser { // Latitude field if field.size == 4 { if let Some(lat_raw) = self.read_i32_le() { - if lat_raw != 0x7FFFFFFF && lat_raw != 0 { + if lat_raw != 0x7FFFFFFF { let lat_degrees = lat_raw as f64 * (180.0 / 2147483648.0); if lat_degrees.abs() <= 90.0 { lat = Some(lat_degrees); @@ -905,7 +905,7 @@ impl FitParser { // Longitude field if field.size == 4 { if let Some(lon_raw) = self.read_i32_le() { - if lon_raw != 0x7FFFFFFF && lon_raw != 0 { + if lon_raw != 0x7FFFFFFF { let lon_degrees = lon_raw as f64 * (180.0 / 2147483648.0); if lon_degrees.abs() <= 180.0 { lon = Some(lon_degrees); @@ -924,7 +924,11 @@ impl FitParser { } if let (Some(lat_val), Some(lon_val)) = (lat, lon) { - Some([round(lat_val), round(lon_val)]) + if is_valid_coordinate(lat_val, lon_val) { + Some([round(lat_val), round(lon_val)]) + } else { + None + } } else { None } diff --git a/core/tests/fit_parser_tests.rs b/core/tests/fit_parser_tests.rs index 008ce1e..3d03b40 100644 --- a/core/tests/fit_parser_tests.rs +++ b/core/tests/fit_parser_tests.rs @@ -253,6 +253,74 @@ mod fit_parser_tests { assert!(result.is_none()); } + #[test] + fn test_parse_record_message_equator() { + let definition = create_test_message_definition(20); + for longitude in [-45.0, 45.0] { + let mut data = Vec::new(); + data.extend_from_slice(&0i32.to_le_bytes()); + data.extend_from_slice(&((longitude * 2147483648.0 / 180.0) as i32).to_le_bytes()); + data.extend_from_slice(&1234u16.to_le_bytes()); + + let mut parser = create_test_parser(data); + assert_eq!( + parser.parse_record_message(&definition), + Some([0.0, longitude]) + ); + assert_eq!(parser.pos, parser.data.len()); + } + } + + #[test] + fn test_parse_record_message_prime_meridian() { + let definition = create_test_message_definition(20); + for latitude in [-45.0, 45.0] { + let mut data = Vec::new(); + data.extend_from_slice(&((latitude * 2147483648.0 / 180.0) as i32).to_le_bytes()); + data.extend_from_slice(&0i32.to_le_bytes()); + data.extend_from_slice(&1234u16.to_le_bytes()); + + let mut parser = create_test_parser(data); + assert_eq!( + parser.parse_record_message(&definition), + Some([latitude, 0.0]) + ); + assert_eq!(parser.pos, parser.data.len()); + } + } + + #[test] + fn test_parse_gps_coordinates_zero_axes() { + // A local record definition with signed latitude and longitude fields. + let mut records = vec![0x40, 0, 0, 20, 0, 2, 0, 4, 0x85, 1, 4, 0x85]; + let quarter_circle = 1i32 << 29; // 45 degrees in semicircles + for (lat, lon) in [ + (0, quarter_circle), + (0, -quarter_circle), + (quarter_circle, 0), + (-quarter_circle, 0), + (i32::MAX, quarter_circle), + (quarter_circle, i32::MAX), + (0, 0), + ] { + records.push(0); // Local record message header + records.extend_from_slice(&lat.to_le_bytes()); + records.extend_from_slice(&lon.to_le_bytes()); + } + + let mut data = vec![12, 0x10, 0, 0]; + data.extend_from_slice(&(records.len() as u32).to_le_bytes()); + data.extend_from_slice(b".FIT"); + data.extend_from_slice(&records); + data.extend_from_slice(&[0, 0]); // CRC is not checked by this parser + + let mut parser = create_test_parser(data); + assert_eq!( + parser.parse_gps_coordinates(), + vec![[0.0, 45.0], [0.0, -45.0], [45.0, 0.0], [-45.0, 0.0]] + ); + } + #[test] fn test_parse_record_message_out_of_range_coordinates() { let definition = create_test_message_definition(20);