Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions rustfmt.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
max_width = 100
wrap_comments = true
comment_width = 100
format_strings = true
edition = "2024"
imports_granularity = "Crate"
group_imports = "StdExternalCrate"
style_edition = "2024"
4 changes: 2 additions & 2 deletions swiftnav/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -12,14 +12,14 @@ rust-version = "1.85.0"
[dependencies]
bon = "3.8.1"
rustversion = "1.0.22"
chrono = { version = "0.4.42", optional = true }
chrono = { version = "0.4.42" }
strum = { version = "0.27.2", features = ["derive"] }
nalgebra = "0.34.1"
thiserror = "2.0.17"

[dev-dependencies]
float_eq = "1.0.1"
proptest = "1.8.0"
proptest = "1.9.0"

# This tells docs.rs to include the katex header for math formatting
# To do this locally
Expand Down
3 changes: 2 additions & 1 deletion swiftnav/src/coords/ecef.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
use nalgebra::Vector3;
use std::ops::{Add, AddAssign, Mul, MulAssign, Sub, SubAssign};

use nalgebra::Vector3;

use crate::{
coords::{AzimuthElevation, Ellipsoid, LLHDegrees, LLHRadians, NED, WGS84},
math,
Expand Down
12 changes: 7 additions & 5 deletions swiftnav/src/coords/llh.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
use crate::coords::{LatitudinalHemisphere, LongitudinalHemisphere};

use super::{Ellipsoid, ECEF, WGS84};
use nalgebra::Vector3;

use super::{ECEF, Ellipsoid, WGS84};
use crate::coords::{LatitudinalHemisphere, LongitudinalHemisphere};

/// WGS84 geodetic coordinates (Latitude, Longitude, Height), with angles in degrees.
///
/// Internally stored as an array of 3 [f64](std::f64) values: latitude, longitude, and height above the ellipsoid in meters
/// Internally stored as an array of 3 [f64](std::f64) values: latitude, longitude, and height above
/// the ellipsoid in meters
#[derive(Copy, Clone, Debug, PartialEq, PartialOrd, Default)]
pub struct LLHDegrees(Vector3<f64>);

Expand Down Expand Up @@ -157,7 +158,8 @@ impl AsMut<Vector3<f64>> for LLHDegrees {

/// WGS84 geodetic coordinates (Latitude, Longitude, Height), with angles in radians.
///
/// Internally stored as an array of 3 [f64](std::f64) values: latitude, longitude, and height above the ellipsoid in meters
/// Internally stored as an array of 3 [f64](std::f64) values: latitude, longitude, and height above
/// the ellipsoid in meters
#[derive(Copy, Clone, Debug, PartialEq, PartialOrd, Default)]
pub struct LLHRadians(Vector3<f64>);

Expand Down
25 changes: 13 additions & 12 deletions swiftnav/src/coords/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -44,11 +44,10 @@
//! the more common algortihms based on the Newton-Raphson method.
//!
//! ## References
//! * "A comparison of methods used in rectangular to Geodetic Coordinates
//! Transformations", Burtch R. R. (2006), American Congress for Surveying
//! and Mapping Annual Conference. Orlando, Florida.
//! * "Transformation from Cartesian to Geodetic Coordinates Accelerated by
//! Halley’s Method", T. Fukushima (2006), Journal of Geodesy.
//! * "A comparison of methods used in rectangular to Geodetic Coordinates Transformations", Burtch
//! R. R. (2006), American Congress for Surveying and Mapping Annual Conference. Orlando, Florida.
//! * "Transformation from Cartesian to Geodetic Coordinates Accelerated by Halley’s Method", T.
//! Fukushima (2006), Journal of Geodesy.
//!
//! # Examples
//!
Expand Down Expand Up @@ -84,16 +83,19 @@ pub use ecef::*;
pub use ellipsoid::*;
pub use hemisphere::*;
pub use llh::*;
use nalgebra::Vector2;
pub use ned::*;

use crate::{reference_frame::ReferenceFrame, time::GpsTime};
use nalgebra::Vector2;

/// WGS84 local horizontal coordinates consisting of an Azimuth and Elevation, with angles stored as radians
/// WGS84 local horizontal coordinates consisting of an Azimuth and Elevation, with angles stored as
/// radians
///
/// Azimuth can range from $0$ to $2\pi$. North has an azimuth of $0$, east has an azimuth of $\frac{\pi}{2}$
/// Azimuth can range from $0$ to $2\pi$. North has an azimuth of $0$, east has an azimuth of
/// $\frac{\pi}{2}$
///
/// Elevation can range from $-\frac{\pi}{2}$ to $\frac{\pi}{2}$. Up has an elevation of $\frac{\pi}{2}$, down an elevation of $-\frac{\pi}{2}$
/// Elevation can range from $-\frac{\pi}{2}$ to $\frac{\pi}{2}$. Up has an elevation of
/// $\frac{\pi}{2}$, down an elevation of $-\frac{\pi}{2}$
#[derive(Copy, Clone, Debug, PartialEq, PartialOrd, Default)]
pub struct AzimuthElevation(Vector2<f64>);

Expand Down Expand Up @@ -292,11 +294,10 @@ impl Coordinate {
#[cfg(test)]
mod tests {
use float_eq::assert_float_eq;

use crate::time::UtcTime;
use proptest::prelude::*;

use super::*;
use proptest::prelude::*;
use crate::time::UtcTime;

/* Maximum allowable error in quantities with units of length (in meters). */
const MAX_DIST_ERROR_M: f64 = 1e-6;
Expand Down
3 changes: 2 additions & 1 deletion swiftnav/src/edc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -326,9 +326,10 @@ pub fn compute_crc24q(buf: &[u8], initial_value: u32) -> u32 {

#[cfg(test)]
mod tests {
use super::*;
use proptest::prelude::*;

use super::*;

const TEST_DATA: &[u8] = "123456789".as_bytes();

/// Helper function to append a CRC-24Q value as 3 bytes (big-endian) to a buffer
Expand Down
15 changes: 7 additions & 8 deletions swiftnav/src/math.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,7 @@

/// We define a `const` max function since [`std::cmp::max`] isn't `const`
pub(crate) const fn compile_time_max_u16(a: u16, b: u16) -> u16 {
if b < a {
a
} else {
b
}
if b < a { a } else { b }
}

/// Computes the square root of a given number at compile time using the Newton-Raphson method.
Expand All @@ -35,7 +31,8 @@ pub(crate) const fn compile_time_max_u16(a: u16, b: u16) -> u16 {
/// # Notes
///
/// - This function is marked as `const`, allowing it to be evaluated at compile time.
/// - The algorithm iteratively refines the approximation of the square root until the result stabilizes.
/// - The algorithm iteratively refines the approximation of the square root until the result
/// stabilizes.
#[expect(clippy::many_single_char_names, reason = "It's math, whatyagonnado?")]
pub(crate) const fn compile_time_sqrt(s: f64) -> f64 {
assert!(
Expand All @@ -57,7 +54,8 @@ pub(crate) const fn compile_time_sqrt(s: f64) -> f64 {
x
}

/// Calculate the rotation matrix for rotating between an [`crate::coords::ECEF`] and [`crate::coords::NED`] frames
/// Calculate the rotation matrix for rotating between an [`crate::coords::ECEF`] and
/// [`crate::coords::NED`] frames
#[must_use]
pub(crate) fn ecef2ned_matrix(llh: crate::coords::LLHRadians) -> nalgebra::Matrix3<f64> {
let sin_lat = llh.latitude().sin();
Expand All @@ -80,10 +78,11 @@ pub(crate) fn ecef2ned_matrix(llh: crate::coords::LLHRadians) -> nalgebra::Matri

#[cfg(test)]
mod tests {
use super::*;
use float_eq::assert_float_eq;
use proptest::prelude::*;

use super::*;

proptest! {
#![proptest_config(ProptestConfig::with_cases(1000))]

Expand Down
3 changes: 2 additions & 1 deletion swiftnav/src/nmea/checksum.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,8 @@ fn u8_to_ascii_char(nibble: u8) -> char {
pub fn calculate_checksum(sentence: &str) -> String {
let mut checksum = 0;

// this flag indicates if we have reached the '*' character or anything beyond that such as the actual checksum value or the end sequence (<CR><LF>)
// this flag indicates if we have reached the '*' character or anything beyond that such as the
// actual checksum value or the end sequence (<CR><LF>)
let mut at_or_past_checksum_field = false;

for (i, byte) in sentence.bytes().enumerate() {
Expand Down
36 changes: 11 additions & 25 deletions swiftnav/src/nmea/gga.rs
Original file line number Diff line number Diff line change
Expand Up @@ -99,10 +99,13 @@ impl GGA {

let hdop = self.hdop.map_or(String::new(), |hdop| format!("{hdop:.1}"));

// NOTE(ted): This is actually not the right value to use, however, we don't really use height for finding information like nearest station so it's ok to use for now
// NOTE(ted): This is actually not the right value to use, however, we don't really use
// height for finding information like nearest station so it's ok to use for now
let height = "0.0";

let age_dgps = self.age_dgps.map_or(0.0, |age| age.as_secs_f64());
let age_dgps = self
.age_dgps
.map_or(String::new(), |age| format!("{:.1}", age.as_secs_f64()));

let geoidal_separation = self
.geoidal_separation
Expand All @@ -113,7 +116,9 @@ impl GGA {
.map_or(String::new(), |id| id.to_string());

let sentence = format!(
"GPGGA,{timestamp},{latitude:.6},{latitudinal_hemisphere},{longitude:.6},{longitudinal_hemisphere},{gps_quality},{sat_in_use},{hdop},{height:.6},M,{geoidal_separation},{age_dgps:.1},{reference_station_id}",
"GPGGA,{timestamp},{latitude:.6},{latitudinal_hemisphere},{longitude:.6},\
{longitudinal_hemisphere},{gps_quality},{sat_in_use},{hdop},{height:.6},M,\
{geoidal_separation},{age_dgps:.1},{reference_station_id}",
);

let checksum = nmea::calculate_checksum(&sentence);
Expand All @@ -133,6 +138,7 @@ mod test {
let gga = GGA::builder()
.sat_in_use(12)
.time(DateTime::from_timestamp(1_761_351_489, 0).unwrap())
.gps_quality(GPSQuality::GPS)
.hdop(0.9)
.llh(super::LLHDegrees::new(37.7749, -122.4194, 10.0))
.build();
Expand Down Expand Up @@ -166,30 +172,10 @@ mod test {
);
}

#[test]
fn gga_with_dgps_fields_that_is_not_dgps_is_ignored() {
let gga = GGA::builder()
.sat_in_use(8)
.time(DateTime::from_timestamp(1_761_351_489, 0).unwrap())
.hdop(1.2)
.llh(super::LLHDegrees::new(34.0522, -118.2437, 15.0))
.gps_quality(GPSQuality::GPS)
.age_dgps(Duration::from_secs_f64(2.5))
.geoidal_separation(1.0)
.reference_station_id(42)
.build();

let sentence = gga.to_sentence();

assert_eq!(
sentence,
"$GPGGA,0189.00,34.052200,N,-118.243700,W,1,8,1.2,0.0,M,1.00,,*00\r\n"
);
}

#[test]
fn gga_sentence_is_always_less_than_82_characters() {
// we are going to set some very large decimal places and the highest possible values in terms of character count to ensure our sentence is always below 82 characters
// we are going to set some very large decimal places and the highest possible values in
// terms of character count to ensure our sentence is always below 82 characters
let gga = GGA::builder()
.sat_in_use(12)
.time(DateTime::from_timestamp(1_761_351_489, 0).unwrap())
Expand Down
3 changes: 2 additions & 1 deletion swiftnav/src/nmea/source.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,8 @@ pub enum Source {
Gallileo = 0b100,
/// China's Beidou
Beidou = 0b1000,
/// Global Navigation Sattelite System. Some combination of other systems. Depends on receiver model, receiver settings, etc..
/// Global Navigation Sattelite System. Some combination of other systems. Depends on receiver
/// model, receiver settings, etc..
GNSS = 0b10000,
/// `MediaTek` NMEA packet protocol
MTK = 0b10_0000,
Expand Down
25 changes: 15 additions & 10 deletions swiftnav/src/reference_frame/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,9 @@
// WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR PURPOSE.
//! Geodetic reference frame transformations
//!
//! Transform coordinates between geodetic reference frames using time-dependent Helmert transformations.
//! Supports both global frames (ITRF series) and regional frames (ETRF, NAD83, etc.) with runtime
//! parameter loading.
//! Transform coordinates between geodetic reference frames using time-dependent Helmert
//! transformations. Supports both global frames (ITRF series) and regional frames (ETRF, NAD83,
//! etc.) with runtime parameter loading.
//!
//! # Key Concepts
//!
Expand Down Expand Up @@ -68,14 +68,16 @@
//! repo.add_transformation(custom_transform);
//! ```

use crate::coords::{Coordinate, ECEF};
use nalgebra::{Matrix3, Vector3};
use std::{
collections::{HashMap, HashSet, VecDeque},
fmt,
};

use nalgebra::{Matrix3, Vector3};
use strum::{Display, EnumIter, EnumString};

use crate::coords::{Coordinate, ECEF};

mod params;

/// Geodetic reference frame identifiers
Expand Down Expand Up @@ -195,10 +197,10 @@ impl PartialEq<ReferenceFrame> for &ReferenceFrame {
/// # Parameter Units
///
/// Input parameters are stored in standard geodetic units:
/// - **Translation** (`tx`, `ty`, `tz`): millimeters (mm)
/// - **Translation** (`tx`, `ty`, `tz`): millimeters (mm)
/// - **Translation rates** (`tx_dot`, `ty_dot`, `tz_dot`): mm/year
/// - **Scale** (`s`): parts per billion (ppb)
/// - **Scale rate** (`s_dot`): ppb/year
/// - **Scale rate** (`s_dot`): ppb/year
/// - **Rotation** (`rx`, `ry`, `rz`): milliarcseconds (mas)
/// - **Rotation rates** (`rx_dot`, `ry_dot`, `rz_dot`): mas/year
/// - **Reference epoch** (`epoch`): decimal years
Expand Down Expand Up @@ -258,7 +260,8 @@ impl TimeDependentHelmertParams {
}
}

/// Reverses the transformation. Since this is a linear transformation we simply negate all terms
/// Reverses the transformation. Since this is a linear transformation we simply negate all
/// terms
#[must_use]
pub fn invert(mut self) -> Self {
self.t *= -1.0;
Expand Down Expand Up @@ -574,10 +577,12 @@ fn builtin_transformations() -> Vec<Transformation> {

#[cfg(test)]
mod tests {
use super::*;
use std::str::FromStr;

use float_eq::assert_float_eq;
use params::TRANSFORMATIONS;
use std::str::FromStr;

use super::*;

#[expect(clippy::too_many_lines)]
#[test]
Expand Down
2 changes: 1 addition & 1 deletion swiftnav/src/signal/code.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
// EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE IMPLIED
// WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR PURPOSE.

use super::{consts, Constellation};
use super::{Constellation, consts};

/// Code identifiers
#[derive(
Expand Down
6 changes: 4 additions & 2 deletions swiftnav/src/signal/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,8 @@
//! This module provides:
//! - [`Constellation`] - Representing the supporting GNSS constellations
//! - [`Code`] - Representing the codes broadcast from the GNSS satellites
//! - [`GnssSignal`] - Represents a [`Code`] broadcast by a specific satellite, using the satellite PRN as the identifier
//! - [`GnssSignal`] - Represents a [`Code`] broadcast by a specific satellite, using the satellite
//! PRN as the identifier
//!
//! # Examples
//!
Expand All @@ -37,9 +38,10 @@ mod code;
mod constellation;
pub mod consts;

use std::fmt;

pub use code::*;
pub use constellation::*;
use std::fmt;

/// GNSS Signal identifier
#[derive(Debug, Copy, Clone, Ord, PartialOrd, Eq, PartialEq, Hash)]
Expand Down
5 changes: 3 additions & 2 deletions swiftnav/src/time/gnss.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,13 @@
// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND,
// EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE IMPLIED
// WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR PURPOSE.
use crate::time::{consts, UtcParams, UtcTime, MJD, UTC_LEAPS, WEEK};
use std::{
ops::{Add, AddAssign, Sub, SubAssign},
time::Duration,
};

use crate::time::{MJD, UTC_LEAPS, UtcParams, UtcTime, WEEK, consts};

/// Representation of GPS Time
#[derive(Debug, Copy, Clone)]
pub struct GpsTime {
Expand Down Expand Up @@ -189,7 +190,7 @@ impl GpsTime {
assert!(utc_time.hour() == 23);
assert!(utc_time.minute() == 59);
assert!(utc_time.seconds_int() == 59);
/* add the extra second back in*/
/* add the extra second back in */
utc_time.add_second();
}

Expand Down
Loading