Introduction
Welcome to the Strapdown-rs documentation! This guide will help you understand and use the Strapdown-rs library, a high-performance Rust implementation of strapdown inertial navigation system (INS) algorithms.
What is Strapdown-rs?
Strapdown-rs is a straightforward strapdown inertial navigation system (INS) implementation in Rust. It is designed to be simple, performant, and easy to understand, making it an excellent starting point for those interested in learning about or implementing strapdown INS algorithms.
The project provides:
- A source library of strapdown INS algorithms and utilities (
strapdown-core) - A simulation program for simulating INS performance in various GNSS conditions (
strapdown-sim) - Experimental geophysical navigation capabilities for GNSS-denied environments (
strapdown-geonav)
Key Features
- Modern Rust Implementation: Memory-safe, high-performance code with cross-platform support
- Multiple Navigation Filters: Extended Kalman Filter (EKF), Unscented Kalman Filter (UKF), and Particle Filters
- GNSS Degradation Scenarios: Simulate dropouts, reduced update rates, and measurement corruption
- Geophysical Navigation: Experimental work on gravity and magnetic anomaly navigation
- Comprehensive Documentation: Well-documented code with examples and tutorials
- Research-Focused: Designed for research, teaching, and development purposes
Target Audience
This library is intended for:
- Researchers working on navigation systems and sensor fusion
- Engineers developing autonomous systems, robotics, or aerospace applications
- Students learning about inertial navigation and state estimation
- Developers needing a high-performance INS implementation in Rust
Project Status
Strapdown-rs is under active development as part of ongoing PhD research. The project prioritizes correctness, numerical stability, and performance while maintaining extensibility for researchers and engineers.
Getting Help
- User Guide: Start with the Quick Start guide
- API Documentation: See the API Reference for detailed function documentation
- Examples: Check out the Examples and Tutorials
- Issues: Report bugs or request features on GitHub
Citation
If you use Strapdown-rs in your research, please cite:
License
Strapdown-rs is licensed under the MIT License. See the LICENSE file for details.
Installation
This page provides detailed instructions for installing Strapdown-rs on your system.
Prerequisites
Before installing Strapdown-rs, ensure you have the following:
- Rust: Version 1.70 or higher (install from rustup.rs)
- System Libraries: Required for building certain dependencies
System Dependencies
Strapdown-rs requires several system libraries for HDF5 and NetCDF support:
Ubuntu/Debian
sudo apt update
sudo apt install -y pkg-config \
libhdf5-dev \
libhdf5-openmpi-dev \
libnetcdf-dev \
zlib1g-dev
Fedora/RHEL
sudo dnf install -y pkg-config \
hdf5-devel \
hdf5-openmpi-devel \
netcdf-devel \
zlib-devel
macOS
brew install pkg-config hdf5 netcdf
Windows
For Windows users, we recommend using vcpkg to install dependencies:
vcpkg install hdf5 netcdf zlib
Installation Methods
Method 1: Install from Crates.io (Recommended)
The easiest way to use Strapdown-rs is to add it as a dependency in your project:
cargo add strapdown-core
Or add manually to your Cargo.toml:
[dependencies]
strapdown-core = "0.1"
To install the simulation binary:
cargo install strapdown-sim
Method 2: Build from Source
Clone the repository and build locally:
# Clone the repository
git clone https://github.com/jbrodovsky/strapdown-rs.git
cd strapdown-rs
# Build the entire workspace
cargo build --workspace --all-features --release
# Install the simulation binary
cargo install --path sim
# Optionally, install the geonav binary
cargo install --path geonav
Method 3: Using Pixi (Experimental)
The project includes a pixi.toml for environment management:
# Install pixi
curl -fsSL https://pixi.sh/install.sh | bash
# Activate the environment
pixi install
pixi shell
# Build and run
cargo build --release
Verifying Installation
After installation, verify everything works:
# Check strapdown-sim version
strapdown-sim --version
# Run a simple test
cargo test -p strapdown-core
Troubleshooting
HDF5/NetCDF Linking Issues
If you encounter linking errors:
-
Ensure
pkg-configcan find the libraries:pkg-config --modversion hdf5 pkg-config --modversion netcdf -
Set environment variables if needed:
export PKG_CONFIG_PATH=/usr/local/lib/pkgconfig:$PKG_CONFIG_PATH
Rust Version Issues
Ensure you're using a recent Rust version:
rustc --version
rustup update stable
Next Steps
- Continue to the Quick Start guide
- Learn about System Requirements
- Explore Building from Source in detail
System Requirements
This page outlines the system requirements for running Strapdown-rs.
Hardware Requirements
Minimum
- CPU: Any modern 64-bit processor (x86_64 or ARM64)
- RAM: 2 GB
- Storage: 100 MB for binaries, plus space for data files
Recommended
- CPU: Multi-core processor (4+ cores) for parallel processing
- RAM: 8 GB or more for large simulations
- Storage: SSD with sufficient space for trajectory data and results
Software Requirements
Operating Systems
Strapdown-rs supports all major operating systems:
- Linux: Ubuntu 20.04+, Debian 11+, Fedora 35+, or equivalent
- macOS: 11.0 (Big Sur) or later
- Windows: Windows 10/11 with MSVC or MinGW
Rust Toolchain
- Minimum Rust version: 1.70.0
- Recommended: Latest stable release
- Required components:
rustc,cargo,clippy,rustfmt
Install via rustup:
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
System Libraries
Required Libraries
- pkg-config: For library detection
- HDF5: Version 1.10 or later
- NetCDF: Version 4.7 or later (for geophysical navigation)
- zlib: Standard compression library
Optional Libraries
- OpenMPI: For parallel HDF5 support (optional but recommended)
Platform-Specific Notes
Linux
Most distributions include the required libraries in their package repositories. Use your package manager to install dependencies.
Performance Note: For best performance, consider building with native CPU optimizations:
RUSTFLAGS="-C target-cpu=native" cargo build --release
macOS
Apple Silicon (M1/M2/M3) is fully supported. The ARM64 architecture provides excellent performance for navigation algorithms.
Note: You may need to set additional environment variables for Homebrew-installed libraries:
export CPATH=/opt/homebrew/include
export LIBRARY_PATH=/opt/homebrew/lib
Windows
Windows support is available but requires additional setup for HDF5 and NetCDF. Using vcpkg is recommended for managing C/C++ dependencies.
Alternatively, use WSL2 (Windows Subsystem for Linux) for a native Linux environment.
Development Requirements
If you plan to contribute to Strapdown-rs development:
- Git: Version control
- clippy: Rust linter (
rustup component add clippy) - rustfmt: Code formatter (
rustup component add rustfmt) - cargo-edit: For managing dependencies (
cargo install cargo-edit)
Testing Your Environment
After installing dependencies, verify your environment:
# Check Rust version
rustc --version
# Verify pkg-config
pkg-config --version
# Check for HDF5
pkg-config --modversion hdf5
# Check for NetCDF
pkg-config --modversion netcdf
# Test compilation
cargo build --workspace
Next Steps
- Proceed to Installing from Crates.io
- Or learn about Building from Source
Installing from Crates.io
Detailed instructions for installing from Crates.io.
Content coming soon.
Building from Source
Detailed instructions for building from source.
Content coming soon.
Quick Start
This guide will get you up and running with Strapdown-rs in minutes.
Installation
Add Strapdown-rs to your Cargo.toml:
cargo add strapdown-core
Or install the simulation binary:
cargo install strapdown-sim
Running Your First Simulation
1. Prepare Input Data
Strapdown-sim expects CSV files with IMU and GNSS data. The format follows the Sensor Logger app convention:
timestamp,gyro_x,gyro_y,gyro_z,accel_x,accel_y,accel_z,latitude,longitude,altitude
1234567890.0,0.01,-0.02,0.03,0.5,-0.2,9.81,45.5,-122.6,100.0
...
2. Run a Simulation
Dead Reckoning (Open-Loop):
strapdown-sim open-loop -i data/input.csv -o results/output.csv
Closed-Loop with EKF:
strapdown-sim closed-loop -i data/input.csv -o results/output.csv --filter ekf
Closed-Loop with UKF:
strapdown-sim closed-loop -i data/input.csv -o results/output.csv --filter ukf
Particle Filter:
strapdown-sim particle-filter -i data/input.csv -o results/output.csv --particles 100
3. View Results
The output CSV contains the estimated navigation state:
timestamp,latitude,longitude,altitude,velocity_north,velocity_east,velocity_down,roll,pitch,yaw
1234567890.0,45.50001,-122.59999,100.1,1.2,0.5,-0.1,0.01,0.02,90.5
...
Using Configuration Files
For more complex scenarios, use TOML configuration files:
# config.toml
[simulation]
mode = "closed-loop"
filter = "ekf"
[data]
input_file = "data/trajectory.csv"
output_file = "results/navigation.csv"
[gnss]
enabled = true
dropout_probability = 0.1
update_rate = 1.0
[filter]
use_biases = true
initial_position_uncertainty = 10.0
initial_velocity_uncertainty = 1.0
initial_attitude_uncertainty = 0.1
Run with:
strapdown-sim --config config.toml
Next Steps
- Read the User Guide for detailed usage instructions
- Explore Example Configurations
- Learn about Navigation Filters
- Check the API Reference for programmatic usage
User Guide Overview
Welcome to the Strapdown-rs User Guide! This section provides comprehensive information on using the library and simulation tools.
What You'll Learn
This guide covers:
- Core Concepts: Understanding strapdown INS, coordinate frames, and state representation
- Running Simulations: How to use the
strapdown-simbinary - Data Formats: Preparing and formatting your input data
- Configuration: Setting up simulations with TOML config files
- Logging and Debugging: Monitoring simulation progress
Quick Navigation
For Beginners
If you're new to strapdown INS or this library:
- Start with Core Concepts to understand the fundamentals
- Learn about Coordinate Frames used in the library
- Review State Representation to understand the 9-state and 15-state models
- Try the Quick Start tutorial
For Experienced Users
If you're familiar with INS and want to jump in:
- Check Input Data Format to prepare your data
- Review Configuration Files for advanced options
- Explore Running Simulations for different modes
- See Logging for debugging and monitoring
Simulation Modes
Strapdown-rs supports three main simulation modes:
Open-Loop (Dead Reckoning)
Pure inertial navigation without corrections. Useful for:
- Understanding INS error growth
- Baseline comparisons
- Testing IMU data quality
See: Open-Loop Mode
Closed-Loop (Kalman Filtering)
INS with GNSS corrections using EKF or UKF. Best for:
- Realistic navigation scenarios
- GNSS degradation studies
- Production-like simulations
See: Closed-Loop Mode
Particle Filter
Non-parametric Bayesian filtering for non-Gaussian distributions. Useful for:
- Multimodal uncertainty
- Highly nonlinear scenarios
- Research applications
See: Particle Filter Mode
Navigation Filters
The library provides multiple filter implementations:
- Extended Kalman Filter (EKF): Fast, efficient, works well for mildly nonlinear systems
- Unscented Kalman Filter (UKF): Better accuracy for nonlinear systems, 2-3x slower
- Particle Filter: Handles non-Gaussian distributions, computationally intensive
- Rao-Blackwellized Particle Filter (RBPF): Hybrid approach combining particles and EKF
Learn more: Navigation Filters
State Models
9-State Model
The basic navigation-only model:
- Position: latitude, longitude, altitude
- Velocity: north, east, down
- Attitude: roll, pitch, yaw
15-State Model
Extended model with IMU bias estimation:
- 9 navigation states (as above)
- Accelerometer biases: 3 states
- Gyroscope biases: 3 states
The 15-state model provides better long-term accuracy by estimating and correcting sensor biases.
Typical Workflow
- Collect or prepare IMU/GNSS data in CSV format
- Create a configuration file specifying simulation parameters
- Run the simulation using
strapdown-sim - Analyze results from the output CSV
- Iterate by adjusting parameters as needed
Common Use Cases
Research and Development
- Testing new navigation algorithms
- Comparing filter performance
- Studying error characteristics
- Publishing research results
Education
- Teaching INS fundamentals
- Demonstrating sensor fusion
- Illustrating error sources
- Hands-on learning
System Development
- Prototyping navigation systems
- Evaluating sensor requirements
- Testing GNSS-denied scenarios
- Performance benchmarking
Getting Help
- FAQ: Check the Frequently Asked Questions
- Examples: Browse Example Configurations
- API Docs: See API Reference for detailed documentation
- Issues: Report problems on GitHub
Next Steps
Choose your path:
- New to INS? → Core Concepts
- Ready to simulate? → Running Simulations
- Need data format info? → Input Data Format
- Want advanced features? → Configuration Files
Core Concepts
Strapdown Mechanization
Coordinate Frames
State Representation
Running Simulations
Open-Loop Mode
Closed-Loop Mode
Particle Filter Mode
Input Data Format
Overview
The strapdown-rs project uses CSV data files for simulation and testing. The primary data format is compatible with the Sensor Logger mobile application, which records IMU, GNSS, and other sensor measurements.
CSV Column Descriptions
The data files contain the following columns (not all columns may be present in every file):
Timestamp
- time - ISO UTC timestamp of the form
YYYY-MM-DD HH:mm:ss.ssss+HH:MM(where the+HH:MMis the timezone offset)
GNSS Measurements
- latitude - Latitude measurement in degrees
- longitude - Longitude measurement in degrees
- altitude - Altitude measurement in meters
- speed - Speed measurement in meters per second
- bearing - Bearing measurement in degrees
IMU Measurements (Body Frame)
- acc_x - Acceleration in the X direction (meters per second squared)
- acc_y - Acceleration in the Y direction (meters per second squared)
- acc_z - Acceleration in the Z direction (meters per second squared)
- gyro_x - Angular velocity around the X axis (radians per second)
- gyro_y - Angular velocity around the Y axis (radians per second)
- gyro_z - Angular velocity around the Z axis (radians per second)
Attitude (Orientation)
- roll - Roll angle in degrees
- pitch - Pitch angle in degrees
- yaw - Yaw angle in degrees
- qw - Quaternion scalar component
- qx - Quaternion X component
- qy - Quaternion Y component
- qz - Quaternion Z component
Magnetometer
- mag_x - Magnetic field strength in the X direction (micro teslas)
- mag_y - Magnetic field strength in the Y direction (micro teslas)
- mag_z - Magnetic field strength in the Z direction (micro teslas)
Barometric Pressure
- pressure - Atmospheric pressure measurement (milli bar)
- relativeAltitude - Relative altitude measurement (meters)
Gravity Vector (Computed)
- grav_x - Gravitational acceleration in the X direction (meters per second squared)
- grav_y - Gravitational acceleration in the Y direction (meters per second squared)
- grav_z - Gravitational acceleration in the Z direction (meters per second squared)
Data Directory Structure
The project organizes data files into several directories based on processing type and GNSS condition:
Input Data
input/: Contains pre-processed input files ready for processing, along with route visualization images. Raw recordings are available upon request but not stored in version control due to size.
Ground Truth
truth/: Contains ground truth data files of processed trajectories used for validation and testing. These files serve as reference data for comparing alternative processing methods and navigation algorithms.
GNSS Degradation Scenarios
The following directories contain processed trajectory data that simulate various degraded GNSS conditions:
degraded/: Simulates degraded GPS conditions with reduced accuracyspoofed/: Simulates GPS spoofing with fixed position offsets to mislead the navigation systemintermittent/: Simulates intermittent GPS with periodic outagescombo/: Simulates a combination of degraded, spoofed, and intermittent GPS conditions
Usage
Loading Data
The strapdown-core library provides functions to load CSV data:
#![allow(unused)] fn main() { use strapdown_core::sim::load_test_data; let data = load_test_data("path/to/data.csv")?; }
Testing with Degraded GNSS
To test alternative processing methods and navigation algorithms under degraded conditions:
- Design your navigation algorithm to leverage the available data in the input files
- Configure your experiment's
GnssDegradationConfigurationto match the specific characteristics of the degraded condition you want to simulate - Process your experiment accordingly
Example Usage
# Run closed-loop simulation with input data
strapdown-sim closed-loop -i data/input/trajectory.csv -o results/output.csv
# Test with degraded GNSS
strapdown-sim closed-loop -i data/degraded/trajectory.csv -o results/degraded_output.csv
Data Collection
Data can be collected using the Sensor Logger mobile application:
- Available for iOS and Android
- Records synchronized IMU and GNSS data
- Exports in CSV format compatible with this project
- Website: https://www.tszheichoi.com/sensorlogger
Notes
- Not all columns need to be present in every file
- Missing values are typically represented as empty strings or
NaN - The simulation functions will skip rows with invalid or missing critical data (e.g., IMU measurements)
- Timestamps should be monotonically increasing
- IMU data is typically recorded at ~100 Hz
- GNSS data is typically recorded at ~1 Hz
Configuration Files
Logging Guide
This project uses the Rust log crate with env_logger as the backend, providing a Python-like logging experience.
Usage
Command-Line Options
Both strapdown-sim and geonav-sim executables support the following logging options:
--log-level <LEVEL>: Set the log level (off, error, warn, info, debug, trace)- Default:
info
- Default:
--log-file <PATH>: Write logs to a file instead of stderr- If not specified, logs are written to stderr
Examples
Basic usage with default settings (info level to stderr):
strapdown-sim closed-loop -i input.csv -o output.csv
Set log level to debug:
strapdown-sim closed-loop -i input.csv -o output.csv --log-level debug
Write logs to a file:
strapdown-sim closed-loop -i input.csv -o output.csv --log-file simulation.log
Combine log level and file output:
strapdown-sim closed-loop -i input.csv -o output.csv --log-level debug --log-file debug.log
Disable logging:
strapdown-sim closed-loop -i input.csv -o output.csv --log-level off
Log Levels
The following log levels are available, from least to most verbose:
- off: No logging output
- error: Only errors that prevent operation
- warn: Warnings about potentially problematic situations
- info: General informational messages (default)
- debug: Detailed information useful for debugging
- trace: Very detailed trace information
Log Format
Log messages are formatted as:
YYYY-MM-DD HH:MM:SS.mmm [LEVEL] - message
Example:
2025-12-01 14:30:45.123 [INFO] - Read 1000 records from data/input.csv
2025-12-01 14:30:45.456 [INFO] - Running particle filter with 100 particles
2025-12-01 14:30:50.789 [INFO] - Results written to output.csv
Environment Variable Override
You can also control logging using the RUST_LOG environment variable, which follows the env_logger syntax. Command-line options take precedence over environment variables.
# Set log level via environment variable
RUST_LOG=debug strapdown-sim closed-loop -i input.csv -o output.csv
# Module-specific logging
RUST_LOG=strapdown=debug,strapdown_sim=trace strapdown-sim closed-loop -i input.csv -o output.csv
Using Logging in Code
For developers extending the project, use the logging macros from the log crate:
#![allow(unused)] fn main() { use log::{trace, debug, info, warn, error}; // Informational messages info!("Processing {} records", count); // Warnings warn!("Skipping row {} due to parse error", row_num); // Errors error!("Failed to read config file: {}", err); // Debug information debug!("UKF state: {:?}", ukf.get_mean()); // Detailed traces trace!("Entering function with params: {:?}", params); }
Python Logger Comparison
This logging system provides a similar experience to Python's logging module:
| Python | Rust |
|---|---|
logging.info("message") | info!("message") |
logging.warning("message") | warn!("message") |
logging.error("message") | error!("message") |
logging.debug("message") | debug!("message") |
--log-level info | --log-level info |
| Writing to file with FileHandler | --log-file path/to/file.log |
Notes
- Log files are opened in append mode, so multiple runs will append to the same log file
- Timestamps use the local system timezone
- The logger is initialized once at program startup
- Log messages from the core library (
strapdown-core) are also captured and formatted consistently
Kalman Filters
Extended Kalman Filter (EKF)
The Extended Kalman Filter (EKF) is one of the primary navigation filters available in Strapdown-rs. It provides efficient state estimation for the INS through linearization of nonlinear system and measurement models.
Overview
The EKF linearizes the nonlinear navigation equations using Jacobian matrices (first-order Taylor series expansion). While this approximation is less accurate than the UKF's unscented transform for highly nonlinear systems, it is computationally more efficient and works well for most practical navigation scenarios.
Mathematical Foundation
State Vector
The EKF supports two state configurations:
9-State Model (Navigation Only):
- Latitude, longitude, altitude
- North, east, down velocities
- Roll, pitch, yaw angles
15-State Model (Navigation + Biases):
- 9 navigation states (as above)
- Accelerometer biases (3)
- Gyroscope biases (3)
Prediction Step
The prediction step propagates the state and covariance forward using IMU measurements using the strapdown mechanization and computed Jacobian matrices.
Update Step
When GNSS or other measurements are available, the Kalman gain is computed and the state is updated with the measurement innovation.
Features
Advantages
- Computational Efficiency: 3-5x faster than UKF
- Memory Efficient: Stores only mean and covariance
- Well-Understood: Extensive literature and proven track record
- Analytic Jacobians: Uses pre-computed derivatives for accuracy
Limitations
- Linearization Error: Less accurate for highly nonlinear systems
- First-Order Approximation: May miss higher-order effects
- Gaussian Assumption: Cannot handle multimodal distributions
Usage
Basic Initialization
#![allow(unused)] fn main() { use strapdown::kalman::{ExtendedKalmanFilter, InitialState, NavigationFilter}; use nalgebra::{DMatrix, DVector}; // Define initial state let initial_state = InitialState { latitude: 45.0, longitude: -122.0, altitude: 100.0, northward_velocity: 0.0, eastward_velocity: 0.0, vertical_velocity: 0.0, roll: 0.0, pitch: 0.0, yaw: 0.0, in_degrees: true, is_enu: true, }; // Initialize 9-state EKF (no biases) let mut ekf = ExtendedKalmanFilter::new( initial_state, vec![], // No biases for 9-state vec![1e-6; 9], // Initial covariance diagonal DMatrix::from_diagonal(&DVector::from_vec(vec![1e-9; 9])), // Process noise false, // use_biases = false for 9-state ); }
15-State with Bias Estimation
#![allow(unused)] fn main() { // Initialize 15-state EKF with bias estimation let mut ekf = ExtendedKalmanFilter::new( initial_state, vec![0.0; 6], // Initial bias estimates (3 accel + 3 gyro) vec![1e-6; 15], // Initial covariance diagonal DMatrix::from_diagonal(&DVector::from_vec(vec![1e-9; 15])), // Process noise true, // use_biases = true for 15-state ); }
Prediction with IMU Data
#![allow(unused)] fn main() { use strapdown::IMUData; let imu_data = IMUData { timestamp: 1234567890.0, gyro: [0.01, -0.02, 0.03], // rad/s accel: [0.5, -0.2, 9.81], // m/s² }; let dt = 0.01; // Time step in seconds ekf.predict(&imu_data, dt); }
Update with GNSS
#![allow(unused)] fn main() { use strapdown::measurements::GPSPositionMeasurement; let gps_measurement = GPSPositionMeasurement { latitude: 45.00012, longitude: -122.00015, altitude: 101.5, std_dev: [5.0, 5.0, 10.0], // Measurement uncertainty }; ekf.update(&gps_measurement); }
Performance
Computational Complexity
- Typical update rate: 10,000-20,000 updates/second on modern hardware
- 3-5x faster than UKF
- Memory usage: ~2 KB for 15-state
Comparison with UKF
| Aspect | EKF | UKF |
|---|---|---|
| Speed | Faster (3-5x) | Slower |
| Accuracy | Good for mildly nonlinear | Better for highly nonlinear |
| Implementation | Requires Jacobians | No Jacobians needed |
| Memory | Lower | Higher |
| Best For | Real-time systems | Research/offline processing |
See EKF vs UKF Comparison for detailed analysis.
Best Practices
- Start with 9-state unless you need bias estimation
- Tune conservatively: Start with larger uncertainties and reduce
- Monitor innovation: Check measurement residuals for divergence
- Use 15-state for long-duration missions or low-quality IMUs
- Validate with dead reckoning: Compare against open-loop results
Next Steps
- Try the UKF for comparison
- Learn about Measurement Models
- See Example: Closed-Loop Simulation
Unscented Kalman Filter (UKF)
EKF vs UKF Comparison
Particle Filters
Overview
This document outlines the design for a particle filter-based Inertial Navigation System (INS) following the architecture established in the UKF implementation (kalman.rs). The design addresses vertical channel instability through two alternative approaches: third-order damping and 2.5D navigation.
Executive Summary: Key Architectural Decisions
1. Custom Forward Propagation Function Required
Critical insight: For 2.5D navigation, we cannot reuse the standard forward() function from lib.rs. A new forward_2_5d() function must be implemented in particle.rs.
Why?
- Standard strapdown mechanization (Groves Eq 5.54) fully integrates vertical acceleration into vertical velocity
- This creates deterministic coupling:
v_v(t+dt) = v_v(t) + ∫[a_z + g - Coriolis] dt - In 2.5D navigation, we want vertical velocity to be a random walk constrained by measurements, not deterministically coupled to accelerometer readings
Solution: forward_2_5d() implements:
- Full strapdown for: attitude, horizontal velocities (v_n, v_e), horizontal position (lat, lon)
- Simplified for: vertical velocity (random walk), altitude (integration of v_v only)
2. Vertical Channel Treatment
Two approaches are provided:
| Approach | States | Vertical Velocity | Complexity | Recommended |
|---|---|---|---|---|
| 2.5D Simplified | 15 | Random walk with large process noise | Low | Yes - Start here |
| Third-Order Damping | 17 | Damped feedback control | High | Optional - if 2.5D insufficient |
3. Implementation Priority
- First: Implement
forward_2_5d()- this is the foundation - Second: Implement particle filter with 2.5D mode
- Later: Optionally add third-order damping if needed
State Representation
Standard 15-State Model
Following the UKF implementation, the baseline particle filter uses a 15-state model:
Navigation states (9):
- Position:
[latitude, longitude, altitude](rad, rad, m) - Velocity:
[v_north, v_east, v_vertical](m/s) - Attitude:
[roll, pitch, yaw](rad) - represented internally as DCM
Bias states (6):
- Accelerometer biases:
[b_ax, b_ay, b_az](m/s²) - Gyroscope biases:
[b_gx, b_gy, b_gz](rad/s)
Extended State for Vertical Channel Damping
For third-order vertical channel damping, add optional states:
- Altitude error estimate:
δh(m) - Altitude error rate:
δh_dot(m/s)
Total state dimension: 15 + 2 = 17 states (when using damping)
Implementation note: Use Particle.other_states: Option<DVector<f64>> for the damping states.
Vertical Channel Approaches
Approach A: Third-Order Vertical Channel Damping
Motivation: The vertical channel in INS is inherently unstable due to coupling between altitude and vertical velocity errors. Third-order damping provides feedback to stabilize this channel.
State augmentation:
x = [lat, lon, h, v_n, v_e, v_v, φ, θ, ψ, b_a, b_g, δh, δh_dot]
└─────────────────────────────────────┘ └─────┘ └────────┘
15 standard states biases damping
Dynamics model:
- Altitude error dynamics:
δh_dot = δv_v - Altitude error rate dynamics:
δh_ddot = -k₁·δh - k₂·δh_dot + w_hk₁,k₂: Damping coefficients (tunable)w_h: Process noise on altitude error
Feedback mechanism:
During propagation (predict step):
- Propagate particles through standard strapdown equations
- Update altitude using damping feedback:
h_corrected = h_propagated - k_fb · δh v_v_corrected = v_v_propagated - k_fb · δh_dot - During measurement update with GPS altitude:
- Innovation:
z - h_expectedupdates bothhandδh - Damping states receive weighted update based on particle importance
- Innovation:
Pros:
- Theoretically rigorous approach based on observability analysis
- Provides smooth vertical channel behavior
- Well-suited for long GNSS outages
Cons:
- Additional states increase computational cost
- Requires careful tuning of damping coefficients
- Increased complexity in implementation
Approach B: 2.5D Navigation (Simplified Vertical Treatment)
Motivation: Treat vertical acceleration as primarily noise-driven rather than deterministic, simplifying the vertical channel dynamics. This is the recommended approach based on past success.
State representation:
x = [lat, lon, h, v_n, v_e, v_v, φ, θ, ψ, b_a, b_g]
└────────────────────────────────────────────┘
Standard 15 states only
Modified dynamics:
- Horizontal navigation: Full 6-DOF strapdown mechanization for position, velocity, and attitude
- Vertical channel: Simplified treatment:
- Altitude propagation:
h(t+dt) = h(t) + v_v(t)·dt - Vertical velocity: Integrate vertical acceleration with high process noise
- Vertical acceleration noise: Model as white noise with large variance
Q_vertical = [Q_h, Q_v_v] where Q_v_v >> Q_v_n, Q_v_e
- Altitude propagation:
Process noise tuning:
- Position (lat/lon):
1e-6(tight) - Altitude:
1e-3to1e-2(loose, allows drift) - Horizontal velocity:
1e-3 - Vertical velocity:
1e-2to1e-1(very loose, absorbs vertical uncertainty) - Biases:
1e-6to1e-8(standard)
Measurement handling:
- GPS altitude measurements directly constrain
hwith measurement noise - Barometric altitude (if available) provides additional vertical constraint
- Vertical velocity measurements (if available) constrain
v_v - During GNSS outages: Altitude drifts according to high process noise
Pros:
- Simpler implementation (no additional states)
- Computationally efficient
- Leverages particle filter's ability to handle non-Gaussian distributions
- Works well with intermittent GPS
- Avoids complex tuning of damping coefficients
Cons:
- Less theoretically rigorous
- Vertical channel accumulates uncertainty faster during outages
- Requires careful process noise tuning to balance stability and responsiveness
Particle Filter Architecture
Core Components
Particle struct (already defined in particle.rs)
#![allow(unused)] fn main() { pub struct Particle { pub nav_state: StrapdownState, // 9 nav states pub accel_bias: DVector<f64>, // 3 accel biases pub gyro_bias: DVector<f64>, // 3 gyro biases pub other_states: Option<DVector<f64>>, // Optional: damping states pub state_size: usize, // Total dimension pub weight: f64, // Importance weight } }
ParticleFilter struct
#![allow(unused)] fn main() { pub struct ParticleFilter { particles: Vec<Particle>, num_particles: usize, process_noise: ProcessNoise, resampling_strategy: ResamplingStrategy, averaging_strategy: AveragingStrategy, effective_particle_threshold: f64, // e.g., 0.5 * num_particles rng: StdRng, // Seeded RNG for reproducibility is_enu: bool, // Vertical channel specific vertical_channel_mode: VerticalChannelMode, // ThirdOrder or Simplified damping_coefficients: Option<(f64, f64)>, // (k1, k2) if using damping } }
ProcessNoise struct
#![allow(unused)] fn main() { pub struct ProcessNoise { pub position_std: Vector3<f64>, // [σ_lat, σ_lon, σ_h] pub velocity_std: Vector3<f64>, // [σ_vn, σ_ve, σ_vv] pub attitude_std: Vector3<f64>, // [σ_φ, σ_θ, σ_ψ] pub accel_bias_std: Vector3<f64>, // [σ_ba_x, σ_ba_y, σ_ba_z] pub gyro_bias_std: Vector3<f64>, // [σ_bg_x, σ_bg_y, σ_bg_z] pub damping_states_std: Option<Vector2<f64>>, // [σ_δh, σ_δh_dot] } }
Enums
#![allow(unused)] fn main() { pub enum VerticalChannelMode { ThirdOrderDamping { k1: f64, k2: f64 }, Simplified, // 2.5D navigation } pub enum ResamplingStrategy { Systematic, Stratified, Residual, Multinomial, } pub enum AveragingStrategy { WeightedMean, // Standard weighted average MaximumWeight, // Use highest weight particle MeanWithTrimming, // Remove outliers then average } }
Core Algorithm
Initialization
#![allow(unused)] fn main() { impl ParticleFilter { pub fn new( initial_state: InitialState, imu_biases: Vec<f64>, covariance_diagonal: Vec<f64>, process_noise: ProcessNoise, num_particles: usize, vertical_mode: VerticalChannelMode, seed: Option<u64>, ) -> Self; } }
Initialization steps:
- Create
num_particlesparticles from initial state - Sample each particle's state from
N(μ₀, P₀)where:μ₀= initial mean stateP₀= diag(covariance_diagonal)
- Initialize all weights to
1.0 / num_particles - Seed RNG for reproducibility
Predict Step
Key architectural decision: For 2.5D navigation, we cannot use the standard forward() function from lib.rs. The standard strapdown mechanization fully integrates vertical acceleration to update vertical velocity (Equation 5.54 from Groves), which creates deterministic coupling we want to avoid in 2.5D.
Why a new forward function is needed:
The standard forward() computes vertical velocity as:
#![allow(unused)] fn main() { // velocity_update() in lib.rs, line 532 velocity + (specific_force + gravity - r * (transport_rate + 2.0 * rotation_rate) * velocity) * dt }
This tightly couples vertical acceleration to vertical velocity. In 2.5D navigation, we want:
- Horizontal dynamics: Full strapdown (deterministic acceleration → velocity → position)
- Vertical dynamics: Decoupled or weakly coupled (vertical velocity as random walk, altitude from velocity integration only)
Solution: Implement forward_2_5d() that modifies vertical channel propagation.
Update Step
#![allow(unused)] fn main() { impl NavigationFilter for ParticleFilter { fn update<M: MeasurementModel + ?Sized>(&mut self, measurement: &M) { // 1. Compute weights based on measurement likelihood for particle in &mut self.particles { let predicted_meas = measurement.get_expected_measurement( &particle.to_state_vector() ); let innovation = measurement.get_vector() - predicted_meas; let meas_cov = measurement.get_noise(); // Likelihood: p(z|x) ∝ exp(-0.5 * innovation^T * R^-1 * innovation) let likelihood = self.compute_likelihood(&innovation, &meas_cov); particle.weight *= likelihood; } // 2. Normalize weights self.normalize_weights(); // 3. Check effective particle count let n_eff = self.effective_particle_count(); // 4. Resample if needed if n_eff < self.effective_particle_threshold { self.resample(); } } } }
Resampling
Systematic resampling is the recommended strategy:
#![allow(unused)] fn main() { fn systematic_resample(&mut self) { let n = self.num_particles; let mut new_particles = Vec::with_capacity(n); // Cumulative sum of weights let cumsum: Vec<f64> = self.particles.iter() .scan(0.0, |sum, p| { *sum += p.weight; Some(*sum) }) .collect(); // Systematic sampling let step = 1.0 / n as f64; let start = self.rng.gen_range(0.0..step); for i in 0..n { let u = start + i as f64 * step; let idx = cumsum.iter().position(|&cs| cs >= u).unwrap_or(n - 1); let mut new_particle = self.particles[idx].clone(); new_particle.weight = 1.0 / n as f64; new_particles.push(new_particle); } self.particles = new_particles; } }
State Estimation
#![allow(unused)] fn main() { fn weighted_mean_estimate(&self) -> DVector<f64> { let mut mean_state = DVector::zeros(self.particles[0].state_size); for particle in &self.particles { let state_vec = particle.to_state_vector(); mean_state += particle.weight * state_vec; } // Special handling for circular quantities (angles) self.wrap_angles(&mut mean_state); mean_state } }
The forward_2_5d() Function
This function implements modified strapdown mechanization for 2.5D navigation. It should be added to particle.rs as a module-level function.
Mathematical Formulation
Full strapdown components (unchanged from Groves Ch 5.4-5.5):
-
Attitude update (Equation 5.46):
C(t+dt) = C(t) * [I + Ω_ib * dt] - [Ω_ie + Ω_el] * C(t) * dt- Full implementation using gyros, transport rate, Earth rate
-
Horizontal velocity update (Equation 5.54, horizontal components only):
v_n(t+dt) = v_n(t) + [f_n + g_n - Coriolis_n - transport_n] * dt v_e(t+dt) = v_e(t) + [f_e + g_e - Coriolis_e - transport_e] * dt- Full mechanization for northward and eastward velocities
-
Horizontal position update (Equation 5.56, lat/lon only):
lat(t+dt) = lat(t) + [v_n / (R_N + h)] * dt lon(t+dt) = lon(t) + [v_e / ((R_E + h) * cos(lat))] * dt
Simplified vertical components (2.5D modification):
-
Vertical velocity: Random walk model (NOT integrated from acceleration)
v_v(t+dt) = v_v(t) + w_v * sqrt(dt)w_v ~ N(0, σ_vv²)- large process noise- No deterministic integration of vertical specific force
- Vertical velocity drifts according to process noise only
-
Altitude update: Simple integration of vertical velocity
h(t+dt) = h(t) + v_v(t) * dt- Uses trapezoidal rule:
h(t+dt) = h(t) + 0.5 * [v_v(t) + v_v(t+dt)] * dt
- Uses trapezoidal rule:
Implementation Details
#![allow(unused)] fn main() { /// Modified forward propagation for 2.5D navigation. /// /// Implements full strapdown mechanization for horizontal navigation (position, /// velocity, attitude) while treating the vertical channel with simplified dynamics. /// Vertical acceleration is NOT integrated into vertical velocity; instead, vertical /// velocity follows a random walk model constrained by measurements and process noise. /// /// # Arguments /// * `state` - Mutable reference to the navigation state /// * `imu_data` - Bias-corrected IMU measurements /// * `dt` - Time step in seconds /// /// # Mathematical Basis /// - Horizontal: Full Groves Equations 5.46, 5.54, 5.56 /// - Vertical: Simplified dynamics, vertical velocity as random walk pub fn forward_2_5d(state: &mut StrapdownState, imu_data: IMUData, dt: f64) { // See full implementation in source code } }
Key Differences from Standard forward()
| Component | Standard forward() | forward_2_5d() |
|---|---|---|
| Attitude | Full mechanization (Eq 5.46) | Same - Full mechanization |
| Specific force transform | Full (Eq 5.47) | Same - Full transform |
| Horizontal velocity | Full mechanization (Eq 5.54) | Same - Full mechanization |
| Vertical velocity | Integrated from vertical accel | NOT integrated - random walk |
| Horizontal position | Full mechanization (Eq 5.56) | Same - Full mechanization |
| Altitude | Integrated from vertical velocity | Same - Simple integration |
Critical insight: The ONLY difference is that v_v(t+dt) ≈ v_v(t) in the propagation step. Changes to vertical velocity come entirely from:
- Process noise injection (large
σ_vv) - Measurement updates (GPS altitude/velocity)
This makes vertical velocity behave like a "free parameter" that is constrained by measurements rather than deterministic dynamics.
Integration with Existing Codebase
Mirroring UKF Structure
The particle filter implements the same NavigationFilter trait as UKF:
#![allow(unused)] fn main() { pub trait NavigationFilter { fn predict(&mut self, imu_data: IMUData, dt: f64); fn update<M: MeasurementModel + ?Sized>(&mut self, measurement: &M); fn get_estimate(&self) -> DVector<f64>; fn get_certainty(&self) -> DMatrix<f64>; // Empirical covariance from particles } }
Simulation Integration
In sim.rs, add particle filter option to closed_loop function:
#![allow(unused)] fn main() { pub enum FilterType { UKF { alpha: f64, beta: f64, kappa: f64 }, ParticleFilter { num_particles: usize, vertical_mode: VerticalChannelMode, resampling: ResamplingStrategy, }, } }
Testing Strategy
Unit Tests
- Test particle initialization from
InitialState - Test process noise injection
- Test resampling algorithms (verify particle diversity)
- Test weight computation and normalization
- Test vertical channel damping (if used)
- Test state estimation with different averaging strategies
Integration Tests
- Compare PF vs UKF on same trajectory
- Test with GNSS dropout scenarios
- Test vertical channel stability (hover, climb, descent scenarios)
- Verify deterministic results with seeded RNG
Vertical Channel Tests
Create tests similar to UKF tests in kalman.rs:
pf_free_fall_motion()- vertical dynamics with no supportpf_hover_motion()- stationary altitudepf_climb_descent()- vertical motion profilepf_altitude_drift_during_outage()- verify controlled drift with 2.5D
Recommended Implementation Order
- ✅ Define
Particlestruct (already done) - Implement
forward_2_5d()function inparticle.rs- Copy
attitude_update()fromlib.rsor make it public - Implement
velocity_update_horizontal()helper - Reuse
position_update()fromlib.rs(make public if needed)
- Copy
- Implement
ProcessNoisestruct - Implement
ParticleFilterstruct with basic initialization - Implement
predict()usingforward_2_5d() - Implement
update()with systematic resampling - Implement
get_estimate()with weighted mean - Write unit tests for each component
- Integration with
sim.rs - Test on real datasets
- (Optional) Implement third-order damping if 2.5D is insufficient
Tuning Guidance
Number of Particles
- Start with 500-1000 particles for smartphone IMU
- Increase to 2000-5000 for high-precision applications
- Monitor computational cost vs. accuracy tradeoff
Process Noise (2.5D)
Conservative (high uncertainty, smooth vertical):
σ_h = 0.1 m,σ_vv = 0.2 m/s
Aggressive (low uncertainty, responsive vertical):
σ_h = 0.01 m,σ_vv = 0.05 m/s
Recommended starting point:
σ_h = 0.05 m,σ_vv = 0.1 m/s
Resampling Threshold
- Effective particle threshold:
0.5 * num_particles - Resample more frequently if seeing particle degeneracy
- Monitor
n_effover time to diagnose issues
Expected Performance
Advantages over UKF
- Better handling of non-Gaussian distributions
- More robust to outliers in measurements
- Can represent multimodal posteriors (e.g., during ambiguous scenarios)
- 2.5D approach naturally handles vertical channel uncertainty
Computational Cost
- PF with N=1000 particles ≈ 10-50× slower than UKF
- Use Rayon for parallel particle propagation if needed
- Profile and optimize hot paths (propagation, resampling)
Memory Requirements
- Each particle: ~200 bytes (15 states × 8 bytes + overhead)
- 1000 particles ≈ 200 KB (negligible for modern systems)
References
- Groves, P. D. (2013). Principles of GNSS, Inertial, and Multisensor Integrated Navigation Systems, 2nd Edition. Chapters 14-15 (INS error models, vertical channel instability).
- Gustafsson, F., et al. (2002). "Particle filters for positioning, navigation, and tracking." IEEE Transactions on Signal Processing.
- Existing
kalman.rsimplementation for architectural patterns.
Rao-Blackwellized Particle Filter
Measurement Models
Geophysical Navigation Overview
Gravity Anomaly Navigation
Magnetic Anomaly Navigation
Data Sources and Maps
Fault Simulation
Dropout Scenarios
Reduced Update Rates
Measurement Corruption
API Reference
earth Module
kalman Module
measurements Module
particles Module
sim Module
strapdown-sim Binary
strapdown-geonav
Example Configurations
Tutorial: Basic INS Simulation
Tutorial: GPS Degradation
Tutorial: Particle Filter
Contributing
Building and Testing
Building the Project
System Dependencies
The project requires the following system libraries:
pkg-configlibhdf5-devandlibhdf5-openmpi-dev(for HDF5 support)libnetcdf-dev(for NetCDF geophysical data)zlib1g-dev(for compression support)
On Ubuntu/Debian systems:
sudo apt update
sudo apt install -y pkg-config libhdf5-dev libhdf5-openmpi-dev libnetcdf-dev zlib1g-dev
Rust Toolchain
- Minimum Rust version: 1.70+ (stable channel)
- Required components:
clippy,rustfmt
Building
Build the entire workspace:
cargo build --workspace --all-features
Build a specific crate:
cargo build -p strapdown-core
cargo build -p strapdown-sim
cargo build -p strapdown-geonav
Build with optimizations (release mode):
cargo build --workspace --all-features --release
Installing Binaries
Install the simulation binaries to your system:
# Install strapdown-sim
cargo install --path sim
# Install geonav-sim
cargo install --path geonav
Testing
Overview
The strapdown-rs project includes comprehensive unit tests, integration tests, and module-specific tests to validate the correctness of the navigation algorithms.
Running Tests
Run all tests in the workspace:
cargo test --workspace --all-features --verbose
Run tests for a specific crate:
cargo test -p strapdown-core
cargo test -p strapdown-sim
cargo test -p strapdown-geonav
Run with output visible:
cargo test -- --nocapture
Run tests sequentially (single-threaded):
cargo test -- --test-threads=1
Test Organization
Tests are organized into:
- Unit tests: Inline in source files using
#[cfg(test)]modules - Integration tests: Located in
core/tests/integration_tests.rs - Module tests: Specific to individual modules
Integration Tests for INS Filters
The integration tests validate the entire INS filter pipeline using real sensor data. These tests are comprehensive and take longer to run than unit tests.
Test Data
The integration tests use real data collected from the Sensor Logger mobile application:
- IMU measurements (accelerometer and gyroscope) at ~100 Hz
- GNSS position and velocity measurements at ~1 Hz
- Approximately 90 minutes of data with ~5366 samples
- Dataset location:
sim/data/test_data.csv
Error Metrics
Horizontal Position Error
- Metric: Haversine distance between estimated and GNSS positions (meters)
- Formula: Great-circle distance on Earth's surface
- Purpose: Measures planar navigation accuracy
Altitude Error
- Metric: Simple absolute difference (meters)
- Purpose: Measures vertical navigation accuracy
Velocity Error
- Metric: Component-wise absolute differences for north, east, and down velocities (m/s)
- Purpose: Measures velocity estimation accuracy
Test Suite
1. test_dead_reckoning_on_real_data
Purpose: Establish baseline performance for pure INS dead reckoning without GNSS corrections.
What it tests:
- Dead reckoning completes without crashes or errors
- Navigation solution remains finite (no NaN or Inf values)
- Provides baseline drift metrics for comparison
Expected behavior:
- Significant drift over time (this is normal for MEMS IMUs)
- All state values remain finite
- Errors grow unbounded (no error thresholds enforced)
Typical results:
- RMS horizontal error: ~7,000 km (expected drift for 90 minutes without corrections)
- Test serves as a baseline to demonstrate the value of GNSS-aided navigation
2. test_ukf_closed_loop_on_real_data
Purpose: Validate UKF performance with full-rate GNSS measurements.
What it tests:
- Closed-loop UKF completes successfully
- Position errors remain bounded
- Velocity and attitude estimates are stable
- All values remain finite
Error thresholds:
- RMS horizontal error < 30 m
- RMS altitude error < 20 m
- Maximum horizontal error < 100 m
Typical results:
- RMS horizontal error: ~24 m
- RMS altitude error: ~4 m
- Mean velocity errors: <1 m/s
These are realistic values for consumer-grade MEMS IMU with smartphone GNSS.
3. test_ukf_with_degraded_gnss
Purpose: Validate UKF performance under degraded GNSS conditions (reduced update rate).
What it tests:
- UKF handles reduced GNSS update rate (5-second intervals)
- Errors are higher than full-rate but still bounded
- Filter doesn't diverge between GNSS updates
Error thresholds:
- RMS horizontal error < 50 m
- Maximum horizontal error < 600 m
Typical results:
- RMS horizontal error: ~28 m
- Maximum horizontal error: ~553 m
- Errors are larger due to drift between 5-second updates
4. test_ukf_outperforms_dead_reckoning
Purpose: Demonstrate that GNSS-aided UKF provides significant improvement over dead reckoning.
What it tests:
- UKF with GNSS has lower errors than dead reckoning
- GNSS corrections effectively bound error growth
Typical results:
- Dead reckoning RMS error: ~7,100 km
- UKF RMS error: ~24 m
- Improvement: >99.99%
Running Integration Tests
Run all integration tests:
cd core
cargo test --test integration_tests
Run a specific test:
cd core
cargo test --test integration_tests test_ukf_closed_loop_on_real_data -- --nocapture
Run with output visible:
cd core
cargo test --test integration_tests -- --nocapture
Run with single thread (sequential execution):
cd core
cargo test --test integration_tests -- --test-threads=1
Expected Runtime
The integration tests process real sensor data and run complex filters:
test_dead_reckoning_on_real_data: ~5 secondstest_ukf_closed_loop_on_real_data: ~60-90 secondstest_ukf_with_degraded_gnss: ~60-90 secondstest_ukf_outperforms_dead_reckoning: ~120-180 seconds
Total runtime: ~4-5 minutes
Implementation Details
Error Metric Calculation
The compute_error_metrics() function:
- Matches navigation results to GNSS measurements by timestamp
- Skips invalid GNSS data (NaN values)
- Computes error for each matched sample
- Filters out non-finite errors
- Calculates mean, max, and RMS statistics
Filter Initialization
Tests use realistic initialization based on first GNSS measurement:
- Position: First GNSS lat/lon/alt
- Velocity: Computed from GNSS speed and bearing
- Attitude: From phone orientation sensors
- Covariances: Conservative initial uncertainties
- Process noise: Tuned for MEMS IMU characteristics
Event Stream Generation
Tests use the build_event_stream() function to create a sequence of IMU propagation and GNSS update events from the raw data, with configurable scheduling and fault injection.
Future Test Enhancements
Potential improvements for the test suite:
-
Additional test scenarios:
- GNSS outages (DutyCycle scheduler)
- Measurement corruption (fault models)
- Different motion profiles
-
More filters:
- Particle filter integration tests
- Extended Kalman Filter (when implemented)
-
Performance benchmarks:
- Execution time metrics
- Memory usage tracking
- Scalability tests
-
Shorter test datasets:
- Create focused test datasets for faster CI/CD
- Keep full dataset for comprehensive validation
Linting and Formatting
Running Clippy
Clippy provides lint checks for Rust code:
cargo clippy --workspace --all-features
Address warnings and errors before committing.
Running rustfmt
Format code with rustfmt:
cargo fmt --all
Check formatting without making changes:
cargo fmt --all -- --check
Documentation
Building API Documentation
Generate documentation for all crates:
cargo doc --workspace --all-features --no-deps
Open the documentation in a browser:
cargo doc --workspace --all-features --no-deps --open
Building the Book
This user guide is built using mdBook. Install mdBook:
cargo install mdbook
Build the book:
cd book
mdbook build
Serve the book locally for development:
cd book
mdbook serve
Then open http://localhost:3000 in your browser.
Continuous Integration
The project uses GitHub Actions for continuous integration. See .github/workflows/ for workflow definitions:
rust.yml: Builds and tests the projectdeploy-book.yml: Builds and deploys this documentation to GitHub Pagespublish.yml: Publishes crates to crates.io
References
- Groves, P. D. (2013). Principles of GNSS, Inertial, and Multisensor Integrated Navigation Systems, 2nd ed.
- Sensor Logger app: https://www.tszheichoi.com/sensorlogger
Architecture
Project Structure
Frequently Asked Questions
General Questions
What is Strapdown-rs?
Strapdown-rs is a Rust library for implementing strapdown inertial navigation systems (INS). It provides core functionality for processing IMU data to estimate position, velocity, and orientation.
Who should use Strapdown-rs?
Strapdown-rs is designed for:
- Researchers working on navigation systems
- Engineers developing autonomous systems
- Students learning about inertial navigation
- Anyone needing a high-performance INS implementation in Rust
What coordinate frames are supported?
The library primarily uses the North-East-Down (NED) local-level frame. The 9-state vector includes:
- Position: latitude, longitude, altitude
- Velocity: northward, eastward, downward
- Attitude: roll, pitch, yaw
Is this production-ready?
Strapdown-rs is primarily intended for research and development. While the code is well-tested and prioritizes correctness, it is still under active development as part of ongoing PhD research.
Installation and Setup
What are the system requirements?
See the System Requirements page for detailed information. In summary:
- Rust 1.70 or later
- System libraries: HDF5, NetCDF, zlib
- Supported on Linux, macOS, and Windows
Why do I need HDF5 and NetCDF?
These libraries are required for:
- HDF5: Data storage and processing
- NetCDF: Geophysical map data for geonav features
If you only need the core INS functionality, you can disable these features in your Cargo.toml.
How do I install on Windows?
Windows support requires additional setup. We recommend either:
- Using vcpkg to install dependencies
- Using WSL2 for a native Linux environment
See Installation for details.
Usage Questions
What data format does strapdown-sim accept?
The simulation expects CSV files with IMU and GNSS data following the Sensor Logger app format. See Input Data Format for details.
Which filter should I use: EKF, UKF, or Particle Filter?
- EKF: Fastest, works well for mildly nonlinear systems
- UKF: Better accuracy for highly nonlinear systems, 2-3x slower than EKF
- Particle Filter: Best for non-Gaussian distributions and multimodal scenarios
See Filter Comparison for detailed analysis.
Can I use my own sensor data?
Yes! You'll need to convert your data to the expected CSV format. The library is designed to work with standard IMU (gyroscope and accelerometer) and GNSS (position) measurements.
How do I simulate GNSS outages?
Use the GNSS fault simulation features in the configuration file:
[gnss]
dropout_probability = 0.1 # 10% chance of dropout
reduced_update_rate = 0.5 # Half the normal rate
See GNSS Degradation Scenarios for more options.
Performance Questions
How fast is Strapdown-rs?
Performance varies by filter type and configuration:
- EKF: ~10,000-20,000 updates/second
- UKF: ~3,000-5,000 updates/second
- Particle Filter: Depends on particle count (100 particles: ~500 updates/second)
These are approximate figures on modern hardware and will vary based on your system.
Can I run simulations in parallel?
The current implementation processes data sequentially as navigation is inherently a sequential process. However, the particle filter implementation can utilize multiple cores for particle processing.
How much memory does it use?
Memory usage is modest:
- Core library: ~10-50 MB
- Simulations: Depends on data size and filter configuration
- Particle filters: Linear with particle count
Development Questions
How can I contribute?
We welcome contributions! Please see the Contributing Guide and reach out to the project maintainer before starting major work.
Where is the API documentation?
Full API documentation is available at docs.rs/strapdown-core. This book focuses on high-level concepts and usage patterns.
Can I use this in my commercial project?
Yes! Strapdown-rs is licensed under the MIT License, which allows commercial use. See the LICENSE file for details.
How do I cite this work?
If you use Strapdown-rs in your research, please cite the JOSS paper:
Troubleshooting
I'm getting linking errors during build
This usually means HDF5 or NetCDF libraries aren't found. Ensure:
- Libraries are installed:
pkg-config --modversion hdf5 PKG_CONFIG_PATHis set correctly- Development headers are installed (
-devpackages on Linux)
See Installation Troubleshooting for more help.
My simulation produces NaN values
Common causes:
- Invalid initial conditions
- IMU data with unrealistic values
- Numerical instability in filter
Check your input data and initial state. Enable debug logging with --log-level debug to investigate.
The particle filter is very slow
This is expected with high particle counts. Consider:
- Reducing the number of particles
- Using the RBPF (Rao-Blackwellized) variant
- Using EKF/UKF instead if appropriate
Where can I get help?
- Check this FAQ and the user guide
- Search existing GitHub Issues
- Open a new issue if you've found a bug
- Contact the maintainer for research collaborations
Additional Questions?
If your question isn't answered here, please:
- Check the User Guide
- Review the API Reference
- Open an issue on GitHub