Skip to content

Add synthetic trajectory generation and code formatting#251

Merged
jbrodovsky merged 2 commits into
mainfrom
imu-sim-tools
Apr 2, 2026
Merged

Add synthetic trajectory generation and code formatting#251
jbrodovsky merged 2 commits into
mainfrom
imu-sim-tools

Conversation

@jbrodovsky

Copy link
Copy Markdown
Owner

Introduce synthetic trajectory generation functionality and apply linting and formatting improvements across the codebase.

Copilot AI review requested due to automatic review settings April 2, 2026 19:02
@jbrodovsky jbrodovsky merged commit c8bad94 into main Apr 2, 2026
1 of 2 checks passed
@jbrodovsky jbrodovsky deleted the imu-sim-tools branch April 2, 2026 19:02

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR introduces a new synthetic trajectory generation capability for the simulation tool and applies a set of formatting/linting cleanups, along with a few dependency and environment updates.

Changes:

  • Add a syn CLI subcommand and SimulationMode::Synthetic support to generate synthetic IMU/GNSS/baro CSV outputs from an initial kinematic state.
  • Extend core simulation configuration/types to support synthetic generation and make IMUQuality serializable/CLI-selectable.
  • Apply formatting/lint fixes and bump a few dependencies/tooling settings (netcdf/hdf5, pixi env, assorted Python/Rust formatting).

Reviewed changes

Copilot reviewed 14 out of 17 changed files in this pull request and generated 8 comments.

Show a summary per file
File Description
sim/src/main.rs Adds syn subcommand and config-path handling for synthetic generation.
sim/Cargo.toml Adds rand dependency for seeded synthetic generation in the sim binary.
core/src/sim.rs Adds SimulationMode::Synthetic, synthetic config/state types, and synthetic data generation implementation.
core/src/lib.rs Makes IMUQuality serializable and clap-friendly for CLI/config usage.
core/Cargo.toml Bumps hdf5-metno/netcdf and retains rand dependencies used by core.
geonav/Cargo.toml Bumps netcdf version.
geonav/src/lib.rs Formatting-only changes.
core/src/messages.rs Formatting-only changes in tests.
core/src/measurements.rs Replaces numeric literals with std constants in tests (format/readability).
core/tests/integration_tests.rs Suppresses unused-variable warnings in a test (format/lint).
pixi.toml Adjusts linker/env paths for pixi environment activation.
Cargo.lock Updates lockfile for bumped/added deps.
papers/anomaly_ukf/generate_figures.py Import reordering/formatting.
analysis_rbpf_results.py Import reordering/formatting.
data.ipynb Import ordering/formatting within notebook cells.

Comment thread core/src/sim.rs
Comment on lines +3865 to +3871
// Accelerometer: specific force to hold v_nav constant (v_dot = 0)
// From velocity_update: v_dot = f_nav + g_nav - (Ω_en + 2*Ω_ie)*v = 0
// → f_nav = (Ω_en + 2*Ω_ie)*v - g_nav
let omega_en_skew = earth::vector_to_skew_symmetric(&omega_en_nav);
let omega_ie_skew = earth::vector_to_skew_symmetric(&omega_ie_nav);
let coriolis = (omega_en_skew + 2.0 * omega_ie_skew) * velocity;

Copilot AI Apr 2, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

compute_perfect_imu’s accelerometer specific-force term doesn’t match the library’s velocity_update model. velocity_update applies - r * (transport_rate + 2*rotation_rate) * v (where r = earth::ecef_to_lla(lat, lon)), but here coriolis is computed as (Ω_en + 2Ω_ie) * v without the r rotation (and without using longitude). This will prevent the “perfect IMU” from actually producing constant nav-frame velocity when propagated with forward. Align the inverse mechanization with the exact term used in velocity_update (including the ecef_to_lla factor and longitude, if that’s truly intended in the forward model).

Copilot uses AI. Check for mistakes.
Comment thread core/src/sim.rs
Comment on lines +3981 to +3982
latitude: state.latitude.to_degrees(),
longitude: state.longitude.to_degrees(),

Copilot AI Apr 2, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The synthetic truth NavigationResult writes latitude/longitude in degrees, but NavigationResult documents these fields as radians. This will make --no-noise output incompatible with consumers expecting radians. Store lat/lon in radians (or change the NavigationResult field docs/types if degrees are intended, but that would be a broader breaking change).

Suggested change
latitude: state.latitude.to_degrees(),
longitude: state.longitude.to_degrees(),
latitude: state.latitude,
longitude: state.longitude,

Copilot uses AI. Check for mistakes.
Comment thread core/src/sim.rs
Comment on lines +4082 to +4084
roll: roll.to_degrees(),
pitch: pitch.to_degrees(),
yaw: yaw.to_degrees(),

Copilot AI Apr 2, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

TestDataRecord.roll/pitch/yaw are documented as radians, but the synthetic generator stores them in degrees. This will break initialization paths that directly call Rotation3::from_euler_angles(first_record.roll, ...) on these fields. Keep these angles in radians in TestDataRecord output (or adjust all downstream parsing/usage, but that would be a larger change).

Suggested change
roll: roll.to_degrees(),
pitch: pitch.to_degrees(),
yaw: yaw.to_degrees(),
roll,
pitch,
yaw,

Copilot uses AI. Check for mistakes.
Comment thread core/src/sim.rs
Comment on lines +4085 to +4088
qw: state.attitude.euler_angles().2.cos(),
qx: 0.0,
qy: 0.0,
qz: 0.0,

Copilot AI Apr 2, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The quaternion fields in synthetic TestDataRecord output are not a valid quaternion (qw is set to cos(yaw) and qx/qy/qz are zero). If these fields are used anywhere (or by downstream tooling), this will be incorrect. Compute the quaternion from state.attitude and populate all four components consistently (and keep them normalized).

Copilot uses AI. Check for mistakes.
Comment thread core/src/sim.rs
Comment on lines +4051 to +4057
let true_pressure =
earth::expected_barometric_pressure(state.altitude, earth::SEA_LEVEL_PRESSURE);
let out_pressure = if config.no_noise {
true_pressure
} else {
true_pressure + rng.sample(baro_dist)
};

Copilot AI Apr 2, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

TestDataRecord.pressure is documented/used in tests with values like 1013.25 (hPa/mbar), but synthetic generation uses earth::expected_barometric_pressure(..., SEA_LEVEL_PRESSURE) which returns Pascals (101325 at sea level). This introduces a 100x unit mismatch. Convert the computed pressure to the same units used by TestDataRecord (or clearly standardize units across the codebase).

Suggested change
let true_pressure =
earth::expected_barometric_pressure(state.altitude, earth::SEA_LEVEL_PRESSURE);
let out_pressure = if config.no_noise {
true_pressure
} else {
true_pressure + rng.sample(baro_dist)
};
let true_pressure_pa =
earth::expected_barometric_pressure(state.altitude, earth::SEA_LEVEL_PRESSURE);
let measured_pressure_pa = if config.no_noise {
true_pressure_pa
} else {
true_pressure_pa + rng.sample(baro_dist)
};
const PASCALS_PER_HECTOPASCAL: f64 = 100.0;
let out_pressure = measured_pressure_pa / PASCALS_PER_HECTOPASCAL;

Copilot uses AI. Check for mistakes.
Comment thread core/src/sim.rs
Vector3::new(rng.sample(dist), rng.sample(dist), rng.sample(dist))
};
let gyro_bias = {
let sigma = config.imu_quality.gyro_bias_instability_dph();

Copilot AI Apr 2, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

gyro_bias_instability_dph() appears to return a bias instability in radians per hour, but the sampled gyro_bias is added directly to IMUData.gyro (radians/second). This will overstate the bias by a factor of 3600. Convert the bias instability to radians/second before sampling/applying (or rename the helper to reflect the units it returns and adjust usage accordingly).

Suggested change
let sigma = config.imu_quality.gyro_bias_instability_dph();
// `gyro_bias_instability_dph()` returns a rate bias in radians per hour,
// while IMU gyro samples are represented in radians per second.
// Convert here so the sampled constant bias has consistent units.
let sigma = config.imu_quality.gyro_bias_instability_dph() / 3600.0_f64;

Copilot uses AI. Check for mistakes.
Comment thread core/src/sim.rs
Comment on lines 3253 to 3258
/// Input CSV file path (relative or absolute)
#[serde(default = "default_input")]
pub input: String,
/// Output CSV file path (relative or absolute)
#[serde(default = "default_output")]
pub output: String,

Copilot AI Apr 2, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Adding #[serde(default = "default_input")] / default_output makes input/output silently optional in config deserialization for all modes. For non-synthetic modes this can hide a misconfigured config file and lead to confusing attempts to read/write input.csv/output.csv. Consider keeping these required for modes that need them (e.g., by making them Option<String> and validating per-mode, or by using a custom deserializer/enum per mode).

Copilot uses AI. Check for mistakes.
Comment thread core/src/sim.rs
Comment on lines +3898 to +3904
pub fn generate_synthetic(
config: &SyntheticConfig,
rng: &mut rand::rngs::StdRng,
) -> (Vec<NavigationResult>, Vec<TestDataRecord>) {
use crate::earth;
use rand::Rng;
use rand_distr::Normal;

Copilot AI Apr 2, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Synthetic trajectory generation adds substantial new behavior (generate_synthetic, compute_perfect_imu, unit conversions, noise/bias modeling) but there are no unit tests validating basic invariants (e.g., constant-velocity propagation with perfect IMU, correct units in NavigationResult vs TestDataRecord, deterministic output with fixed seed). Add focused tests in this module to prevent regressions and to lock down units/frames.

Copilot generated this review using guidance from repository custom instructions.
@jbrodovsky

Copy link
Copy Markdown
Owner Author

@copilot apply changes based on the comments in this thread

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants