Skip to content
9 changes: 4 additions & 5 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,15 +13,14 @@

## Architecture & Code Organization
- **Library-first design:** Core logic in `src/lib.rs` and CLI entry point in `src/main.rs`.
- **Shared code:** Parser modules (`src/parser/`) are used by both library and CLI (`parse_frames`, `parse_headers_from_text`).
- **Duplicated code:** Export implementations exist separately in `src/export.rs` (library) and `src/main.rs` (CLI). CLI does NOT use library export functions.
- **Current state:** **Partial unification** — parsing shared, export duplicated (~1800 lines in `src/main.rs`).
- **Shared code:** Parser modules (`src/parser/`) and export functions (`src/export.rs`) are shared by both library and CLI.
- **CLI as thin wrapper:** The CLI (`src/main.rs`) uses library export functions (`export_to_csv`, `export_to_gpx`, `export_to_event`) with CLI-specific status messages.
- **Current state:** **Full unification complete** — parsing and export layers unified, CLI reduced from ~1800 to ~1400 lines.
- **Decision criteria:** "Is this needed by crate consumers?" determines placement — shared logic in library, CLI-only logic in `src/main.rs`.
- **Feature flags:** `csv`, `cli`, `json`, `serde` control optional dependencies; default: `csv` + `cli`.
- **CRATE_USAGE.md reference:** See `CRATE_USAGE.md` for library API examples with feature flags.
- **Code quality goals:** Reduce duplication by migrating CLI export logic to use library `export_to_csv()`, `export_to_gpx()`, `export_to_event()` functions.
- **Testing:** Comprehensive tests distributed across `src/main.rs`, `src/conversion.rs`, `src/parser/stream.rs`, and `src/parser/helpers.rs`.
- **Public API:** `parse_bbl_file()`, `parse_bbl_bytes()`, `BBLLog`, `ExportOptions`, conversion utilities, parser helpers.
- **Public API:** `parse_bbl_file()`, `parse_bbl_bytes()`, `BBLLog`, `ExportOptions`, `export_to_csv()`, `export_to_gpx()`, `export_to_event()`, conversion utilities, parser helpers.

## Algorithms
- **Method Selection:**
Expand Down
1 change: 1 addition & 0 deletions examples/export_demo.rs
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,7 @@ fn main() -> Result<()> {
&log.gps_coordinates,
&log.home_coordinates,
&export_opts,
log.header.log_start_datetime.as_deref(),
)?;
println!("✓ GPX export complete");
} else {
Expand Down
1 change: 1 addition & 0 deletions examples/gpx_export.rs
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ fn main() -> anyhow::Result<()> {
&log.gps_coordinates,
&log.home_coordinates,
&export_opts,
log.header.log_start_datetime.as_deref(),
)?;
println!("✓ GPX export complete");
println!(" Exported {} GPS coordinates", log.gps_coordinates.len());
Expand Down
1 change: 1 addition & 0 deletions examples/multi_export.rs
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,7 @@ fn main() -> anyhow::Result<()> {
&log.gps_coordinates,
&log.home_coordinates,
&export_opts,
log.header.log_start_datetime.as_deref(),
)?;
println!(
"✓ GPX export complete ({} coordinates)",
Expand Down
30 changes: 20 additions & 10 deletions src/export.rs
Original file line number Diff line number Diff line change
Expand Up @@ -288,13 +288,28 @@ fn export_flight_data_to_csv(log: &BBLLog, output_path: &Path) -> Result<()> {
}

/// Export GPS data to GPX format
///
/// # Arguments
/// * `input_path` - Path to the input BBL file (used for output naming)
/// * `log_index` - Index of the current log (0-based)
/// * `total_logs` - Total number of logs in the file
/// * `gps_coordinates` - GPS coordinate data to export
/// * `_home_coordinates` - Home coordinates (reserved for future use)
/// * `export_options` - Export configuration options
/// * `log_start_datetime` - Optional log start datetime from header for accurate timestamps
///
/// # Performance Notes
/// For very large GPS traces, the `log_start_datetime` is parsed via `generate_gpx_timestamp()`
/// on each trackpoint. Future optimization: consider caching the parsed base epoch once per log
/// to avoid repeated parsing overhead when exporting thousands of GPS points.
pub fn export_to_gpx(
input_path: &Path,
log_index: usize,
total_logs: usize,
gps_coordinates: &[GpsCoordinate],
_home_coordinates: &[GpsHomeCoordinate],
export_options: &ExportOptions,
log_start_datetime: Option<&str>,
) -> Result<()> {
if gps_coordinates.is_empty() {
return Ok(());
Expand Down Expand Up @@ -339,19 +354,14 @@ pub fn export_to_gpx(
}
}

// Convert timestamp to ISO format
let total_seconds = coord.timestamp_us / 1_000_000;
let microseconds = coord.timestamp_us % 1_000_000;

// Use March 26, 2025 as base date
let hours = 5 + (total_seconds / 3600) % 24;
let minutes = (total_seconds % 3600) / 60;
let seconds = total_seconds % 60;
// Generate GPX timestamp from log_start_datetime + frame timestamp
// Following blackbox_decode approach: dateTime + (gpsFrameTime / 1000000)
let timestamp_str = generate_gpx_timestamp(log_start_datetime, coord.timestamp_us);

writeln!(
gpx_file,
r#" <trkpt lat="{:.7}" lon="{:.7}"><ele>{:.2}</ele><time>2025-03-26T{:02}:{:02}:{:02}.{:06}Z</time></trkpt>"#,
coord.latitude, coord.longitude, coord.altitude, hours, minutes, seconds, microseconds
r#" <trkpt lat="{:.7}" lon="{:.7}"><ele>{:.2}</ele><time>{}</time></trkpt>"#,
coord.latitude, coord.longitude, coord.altitude, timestamp_str
)?;
}

Expand Down
Loading