Skip to content

Commit d283398

Browse files
committed
formatting pass
1 parent d670bad commit d283398

5 files changed

Lines changed: 256 additions & 190 deletions

File tree

core/src/earth.rs

Lines changed: 21 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -77,43 +77,50 @@ pub const DEGREES_TO_METERS: f64 = 60.0 * 1852.0;
7777
/// Atmospheric pressure at sea level in Pascals ($P_0$)
7878
pub const SEA_LEVEL_PRESSURE: f64 = 101325.0;
7979
/// Standard temperature at sea level in Kelvin ($T_0$)
80-
pub const SEA_LEVEL_TEMPERATURE: f64 = 288.15;
80+
pub const SEA_LEVEL_TEMPERATURE: f64 = 288.15;
8181
/// Molar mass of dry air in kg/mol ($M$)
82-
pub const MOLAR_MASS_DRY_AIR: f64 = 0.0289644;
82+
pub const MOLAR_MASS_DRY_AIR: f64 = 0.0289644;
8383
/// Universal gas constant in J/(mol·K) ($R_0$)
8484
pub const UNIVERSAL_GAS_CONSTANT: f64 = 8.314462618;
8585
/// Standard lapse rate in K/m ($L$)
8686
pub const STANDARD_LAPSE_RATE: f64 = 0.0065;
8787
/// Calculate a barometric altitude from a measured pressure
88-
///
88+
///
8989
/// This function calculates the altitude above sea level based on the measured pressure
9090
/// using the barometric formula. The formula assumes a standard atmosphere and uses the
9191
/// universal gas constant, standard lapse rate, and molar mass of dry air.
92-
///
92+
///
9393
/// # Parameters
9494
/// - `pressure` - The measured pressure in Pascals
9595
/// # Returns
9696
/// The calculated altitude in meters above sea level
9797
pub fn barometric_altitude(pressure: &f64) -> f64 {
98-
let exponent: f64 = - (UNIVERSAL_GAS_CONSTANT * STANDARD_LAPSE_RATE) / (G0 * MOLAR_MASS_DRY_AIR);
99-
(SEA_LEVEL_PRESSURE / STANDARD_LAPSE_RATE) * (pressure / SEA_LEVEL_PRESSURE - 1.0) * exponent.exp()
98+
let exponent: f64 = -(UNIVERSAL_GAS_CONSTANT * STANDARD_LAPSE_RATE) / (G0 * MOLAR_MASS_DRY_AIR);
99+
(SEA_LEVEL_PRESSURE / STANDARD_LAPSE_RATE)
100+
* (pressure / SEA_LEVEL_PRESSURE - 1.0)
101+
* exponent.exp()
100102
}
101103
/// Calculate the relative barometric altitude from a measured pressure
102-
///
104+
///
103105
/// This function calculates the relative altitude from a reference pressure using the
104-
/// barometric formula. The formula assumes a standard atmosphere and uses the universal
106+
/// barometric formula. The formula assumes a standard atmosphere and uses the universal
105107
/// gas constant, standard lapse rate, and molar mass of dry air.
106-
///
108+
///
107109
/// # Parameters
108110
/// - `pressure` - The measured pressure in Pascals
109111
/// - `initial_pressure` - The reference pressure in Pascals
110112
/// - `average_temperature` - Optional average temperature in Kelvin (defaults to standard sea level temperature)
111-
///
113+
///
112114
/// # Returns
113115
/// The calculated relative altitude in meters above the reference pressure
114-
pub fn relative_barometric_altitude(pressure: f64, initial_pressure: f64, average_temperature: Option<f64>) -> f64 {
116+
pub fn relative_barometric_altitude(
117+
pressure: f64,
118+
initial_pressure: f64,
119+
average_temperature: Option<f64>,
120+
) -> f64 {
115121
let average_temperature = average_temperature.unwrap_or(SEA_LEVEL_TEMPERATURE);
116-
((UNIVERSAL_GAS_CONSTANT * average_temperature) / (G0 * MOLAR_MASS_DRY_AIR)) * (initial_pressure / pressure).ln()
122+
((UNIVERSAL_GAS_CONSTANT * average_temperature) / (G0 * MOLAR_MASS_DRY_AIR))
123+
* (initial_pressure / pressure).ln()
117124
}
118125
/// Calculates the expected barometric pressure at a given altitude
119126
///
@@ -138,7 +145,8 @@ pub fn expected_barometric_pressure(altitude: f64, sea_level_pressure: f64) -> f
138145
// Barometric formula (assuming isothermal atmosphere):
139146
// P = P0 * exp(- (g0 * M * h) / (R * T0))
140147
// Here we use the same constants as in barometric_altitude
141-
let exponent = - (G0 * MOLAR_MASS_DRY_AIR * altitude) / (UNIVERSAL_GAS_CONSTANT * SEA_LEVEL_TEMPERATURE);
148+
let exponent =
149+
-(G0 * MOLAR_MASS_DRY_AIR * altitude) / (UNIVERSAL_GAS_CONSTANT * SEA_LEVEL_TEMPERATURE);
142150
sea_level_pressure * exponent.exp()
143151
}
144152
/// Convert a three-element vector to a skew-symmetric matrix

core/src/filter.rs

Lines changed: 34 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,8 @@ pub trait MeasurementModel {
4343
}
4444
/// GPS position measurement model
4545
#[derive(Clone, Debug, Default)]
46-
pub struct GPSPositionMeasurement { // <-- Check this model for degree/radian consistency
46+
pub struct GPSPositionMeasurement {
47+
// <-- Check this model for degree/radian consistency
4748
/// latitude in degrees
4849
pub latitude: f64,
4950
/// longitude in degrees
@@ -183,9 +184,9 @@ impl MeasurementModel for GPSPositionAndVelocityMeasurement {
183184
}
184185
}
185186

186-
/// A relative relative altitude measurement derived from barometric pressure.
187-
/// Note that this measurement model is an altitude measurement derived from
188-
/// a barometric altimeter and not a direct calculation of altitude from the
187+
/// A relative relative altitude measurement derived from barometric pressure.
188+
/// Note that this measurement model is an altitude measurement derived from
189+
/// a barometric altimeter and not a direct calculation of altitude from the
189190
/// barometric pressure.
190191
#[derive(Clone, Debug, Default)]
191192
pub struct RelativeAltitudeMeasurement {
@@ -205,7 +206,8 @@ impl MeasurementModel for RelativeAltitudeMeasurement {
205206
DMatrix::from_diagonal(&DVector::from_vec(vec![5.0])) // 1 mm noise
206207
}
207208
fn get_sigma_points(&self, state_sigma_points: &DMatrix<f64>) -> DMatrix<f64> {
208-
let mut measurement_sigma_points = DMatrix::<f64>::zeros(self.get_dimension(), state_sigma_points.ncols());
209+
let mut measurement_sigma_points =
210+
DMatrix::<f64>::zeros(self.get_dimension(), state_sigma_points.ncols());
209211
for (i, sigma_point) in state_sigma_points.column_iter().enumerate() {
210212
measurement_sigma_points[(0, i)] = sigma_point[2];
211213
}
@@ -244,7 +246,7 @@ pub struct StrapdownParams {
244246
/// Note that, internally, angles are always stored in radians (both for the attitude and the position),
245247
/// however, the user can choose to convert them to degrees when retrieving the state vector and the UKF
246248
/// and underlying strapdown state can be constructed from data in degrees by using the boolean `in_degrees`
247-
/// toggle where applicable. Generally speaking, the design of this crate is such that methods that expect
249+
/// toggle where applicable. Generally speaking, the design of this crate is such that methods that expect
248250
/// a WGS84 coordinate (e.g. latitude or longitude) will expect the value in degrees, whereas trigonometric
249251
/// functions (e.g. sine, cosine, tangent) will expect the value in radians.
250252
pub struct UKF {
@@ -370,7 +372,7 @@ impl UKF {
370372
lambda,
371373
state_size,
372374
weights_mean,
373-
weights_cov
375+
weights_cov,
374376
}
375377
}
376378
/// Predicts the state using the strapdown equations and IMU measurements.
@@ -389,20 +391,20 @@ impl UKF {
389391
let mut sigma_points = self.get_sigma_points();
390392
for i in 0..sigma_points.ncols() {
391393
let mut sigma_point_vec = sigma_points.column(i).clone_owned();
392-
let mut state = StrapdownState {
393-
latitude: sigma_point_vec[0],
394-
longitude: sigma_point_vec[1],
395-
altitude: sigma_point_vec[2],
396-
velocity_north: sigma_point_vec[3],
397-
velocity_east: sigma_point_vec[4],
398-
velocity_down: sigma_point_vec[5],
394+
let mut state = StrapdownState {
395+
latitude: sigma_point_vec[0],
396+
longitude: sigma_point_vec[1],
397+
altitude: sigma_point_vec[2],
398+
velocity_north: sigma_point_vec[3],
399+
velocity_east: sigma_point_vec[4],
400+
velocity_down: sigma_point_vec[5],
399401
attitude: Rotation3::from_euler_angles(
400-
sigma_point_vec[6],
401-
sigma_point_vec[7],
402-
sigma_point_vec[8]
402+
sigma_point_vec[6],
403+
sigma_point_vec[7],
404+
sigma_point_vec[8],
403405
),
404406
coordinate_convention: true,
405-
};
407+
};
406408
forward(&mut state, imu_data, dt);
407409
// Update the sigma point with the new state
408410
sigma_point_vec[0] = state.latitude;
@@ -478,10 +480,7 @@ impl UKF {
478480
/// # Arguments
479481
/// * `measurement` - The measurement vector to update the state with.
480482
/// * `measurement_sigma_points` - The measurement sigma points to use for the update.
481-
pub fn update<M: MeasurementModel>(
482-
&mut self,
483-
measurement: M,
484-
) {
483+
pub fn update<M: MeasurementModel>(&mut self, measurement: M) {
485484
let measurement_sigma_points = measurement.get_sigma_points(&self.get_sigma_points());
486485
// Calculate expected measurement
487486
let mut z_hat = DVector::<f64>::zeros(measurement.get_dimension());
@@ -498,7 +497,8 @@ impl UKF {
498497
s += measurement.get_noise();
499498
// Calculate the cross-covariance
500499
let sigma_points = self.get_sigma_points();
501-
let mut cross_covariance = DMatrix::<f64>::zeros(self.state_size, measurement.get_dimension());
500+
let mut cross_covariance =
501+
DMatrix::<f64>::zeros(self.state_size, measurement.get_dimension());
502502
for (i, measurement_sigma_point) in measurement_sigma_points.column_iter().enumerate() {
503503
let measurement_diff = measurement_sigma_point - &z_hat;
504504
let state_diff = sigma_points.column(i) - &self.mean_state;
@@ -525,7 +525,9 @@ impl UKF {
525525
let I = DMatrix::<f64>::identity(self.state_size, self.state_size);
526526
let P = self.covariance.clone();
527527
//self.covariance -= &k * s * &k.transpose();
528-
self.covariance = (&I - &k * &cross_covariance) * &P * (&I - &k * &cross_covariance).transpose() + &k * measurement.get_noise() * &k.transpose();
528+
self.covariance =
529+
(&I - &k * &cross_covariance) * &P * (&I - &k * &cross_covariance).transpose()
530+
+ &k * measurement.get_noise() * &k.transpose();
529531
}
530532
}
531533
#[derive(Clone, Debug, Default)]
@@ -667,8 +669,8 @@ impl ParticleFilter {
667669
/// Tests
668670
#[cfg(test)]
669671
mod tests {
670-
use crate::earth;
671672
use super::*;
673+
use crate::earth;
672674
use assert_approx_eq::assert_approx_eq;
673675
use nalgebra::Vector3;
674676

@@ -706,10 +708,7 @@ mod tests {
706708
BETA,
707709
KAPPA,
708710
);
709-
assert_eq!(
710-
ukf.mean_state.len(),
711-
18
712-
);
711+
assert_eq!(ukf.mean_state.len(), 18);
713712
let wms = ukf.weights_mean;
714713
let wcs = ukf.weights_cov;
715714
assert_eq!(wms.len(), (2 * ukf.state_size) + 1);
@@ -777,12 +776,12 @@ mod tests {
777776
ukf.mean_state.len() == 15 //+ measurement_bias.len()
778777
);
779778
let measurement = GPSPositionMeasurement {
780-
latitude: 0.0,
781-
longitude: 0.0,
782-
altitude: 0.0,
783-
horizontal_noise_std: 1e-3,
784-
vertical_noise_std: 1e-3,
785-
};
779+
latitude: 0.0,
780+
longitude: 0.0,
781+
altitude: 0.0,
782+
horizontal_noise_std: 1e-3,
783+
vertical_noise_std: 1e-3,
784+
};
786785
ukf.update(measurement);
787786
// Check that the state has not changed
788787
assert_approx_eq!(ukf.mean_state[0], 0.0, 1e-3);

core/src/main.rs

Lines changed: 39 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,29 +2,61 @@ use clap::Parser;
22
use csv::ReaderBuilder;
33
use std::error::Error;
44
use std::path::PathBuf;
5-
use strapdown::sim::{NavigationResult, TestDataRecord, closed_loop, dead_reckoning, degrade_measurements};
5+
use strapdown::sim::{
6+
NavigationResult, TestDataRecord, closed_loop, dead_reckoning, degrade_measurements,
7+
};
68

79
/// Command line arguments
810
#[derive(Parser, Debug)]
911
#[clap(author, version, about, long_about = None)]
1012
struct Args {
1113
/// Mode of operation, either open-loop or closed-loop
12-
#[clap(short, long, value_parser, default_value = "open-loop", help = "Mode of operation: 'open-loop' or 'closed-loop'")]
14+
#[clap(
15+
short,
16+
long,
17+
value_parser,
18+
default_value = "open-loop",
19+
help = "Mode of operation: 'open-loop' or 'closed-loop'"
20+
)]
1321
mode: String,
1422
/// Input CSV file path
15-
#[clap(short, long, value_parser, help = "Path to the input CSV file containing IMU and (optionally) GPS data")]
23+
#[clap(
24+
short,
25+
long,
26+
value_parser,
27+
help = "Path to the input CSV file containing IMU and (optionally) GPS data"
28+
)]
1629
input: PathBuf,
1730
/// Output CSV file path
18-
#[clap(short, long, value_parser, help = "Path to the output CSV file for navigation results")]
31+
#[clap(
32+
short,
33+
long,
34+
value_parser,
35+
help = "Path to the output CSV file for navigation results"
36+
)]
1937
output: PathBuf,
2038
/// Optional: GPS measurement interval in seconds (for simulating intermittent GPS outages)
21-
#[clap(long, value_parser, help = "Interval (in seconds) between GPS measurements, used to simulate GPS outages. Default is 0.0 which updates at every iteration.")]
39+
#[clap(
40+
long,
41+
value_parser,
42+
help = "Interval (in seconds) between GPS measurements, used to simulate GPS outages. Default is 0.0 which updates at every iteration."
43+
)]
2244
gps_interval: Option<f64>,
2345
/// Optional: GPS degradation factor
24-
#[clap(long, value_parser, default_value = "1.0", help = "Factor by which GPS accuracy is degraded. 1.0 is no degradation. >= 1.0 is a degradation factor. This factor is applied as a scalar multiplier to the recorded GPS accuracy.")]
46+
#[clap(
47+
long,
48+
value_parser,
49+
default_value = "1.0",
50+
help = "Factor by which GPS accuracy is degraded. 1.0 is no degradation. >= 1.0 is a degradation factor. This factor is applied as a scalar multiplier to the recorded GPS accuracy."
51+
)]
2552
gps_degradation: Option<f64>,
2653
/// Optional: GPS spoofing offset
27-
#[clap(long, value_parser, default_value = "0.0", help = "Offset to apply to GPS coordinates (in meters)")]
54+
#[clap(
55+
long,
56+
value_parser,
57+
default_value = "0.0",
58+
help = "Offset to apply to GPS coordinates (in meters)"
59+
)]
2860
gps_spoofing_offset: Option<f64>,
2961
}
3062
fn main() -> Result<(), Box<dyn Error>> {

0 commit comments

Comments
 (0)