124 lines
7.1 KiB
Markdown
124 lines
7.1 KiB
Markdown
# Simulation Report (archive) — Metadata & Data Format
|
|
|
|
This document describes the JSON report files created by the application and stored in the `archive_simulations/` folder. The goal is to provide a stable reference for post-analysis tools and map visualisation.
|
|
|
|
Filename and location
|
|
- Folder: `archive_simulations/`
|
|
- Filename pattern: `YYYYMMDD_HHMMSS_<scenario_name>.json` (example: `20251105_102118_test.json`).
|
|
- Encoding: UTF-8, pretty-printed JSON (indent=4).
|
|
|
|
Top-level JSON structure
|
|
|
|
- `metadata` (object)
|
|
- `scenario_name` (string)
|
|
- Name of the scenario used for the run.
|
|
- `start_timestamp_utc` (string, ISO-8601)
|
|
- Wall-clock UTC timestamp when the archive was saved (e.g. `2025-11-05T10:21:18.123456`).
|
|
- `duration_seconds` (number, seconds)
|
|
- Duration of the recorded session (end_time - start_time), in seconds.
|
|
- `client_update_interval_s` (number, seconds) — optional
|
|
- The update interval the client used when sending updates to the server (from UI `update_time`). Example: `1.0` means one update per second.
|
|
- `client_update_rate_hz` (number, Hz) — optional
|
|
- The computed send rate (1 / client_update_interval_s). Example: `1.0` Hz.
|
|
- `estimated_latency_ms` (number, milliseconds) — optional
|
|
- One-way estimated latency (server → client) computed by the `ClockSynchronizer` model, rounded to 2 decimal places.
|
|
- `prediction_offset_ms` (number, milliseconds) — optional
|
|
- Optional manual prediction offset read from connection settings.
|
|
- `latency_summary` (object) — optional
|
|
- Summary statistics over recent latency samples (milliseconds). Keys:
|
|
- `mean_ms` (number)
|
|
- `std_ms` (number)
|
|
- `var_ms` (number)
|
|
- `min_ms` (number)
|
|
- `max_ms` (number)
|
|
- `count` (integer)
|
|
- `latency_samples` (array of arrays) — optional
|
|
- Array of `[timestamp, latency_ms]` pairs where timestamp is the monotonic time (seconds) and latency_ms is the measured latency in milliseconds. Contains all available samples collected during the simulation (up to 10000 samples, sufficient for ~2.7 hours at 1Hz sampling).
|
|
- Example: `[[3255.123, 1.2], [3255.456, 1.5], ...]`
|
|
- Note: Old archives using `latency_samples_ms` format are no longer supported.
|
|
|
|
- `scenario_definition` (object)
|
|
- The scenario data returned by `Scenario.to_dict()`. Structure depends on the scenario model (targets, durations, etc.). Keep in mind some fields may be ctypes-derived values when taken from live packets.
|
|
|
|
- `ownship_trajectory` (array of objects)
|
|
- Time-ordered list of ownship samples recorded during the run. Each entry is an object with fields such as:
|
|
- `timestamp` (number) — monotonic clock timestamp used by the app (seconds)
|
|
- `position_xy_ft` (array `[x_east_ft, y_north_ft]`) — position in feet relative to the local origin used by the simulation
|
|
- `altitude_ft` (number)
|
|
- `velocity_xy_fps` (array `[vx_east_fps, vy_north_fps]`) — feet per second
|
|
- `heading_deg` (number) — degrees (0 = North)
|
|
- `latitude` (number) — decimal degrees (WGS84)
|
|
- `longitude` (number) — decimal degrees (WGS84)
|
|
|
|
Note: `latitude`/`longitude` may be absent for some ownship samples if the upstream source doesn't provide them.
|
|
|
|
- `simulation_results` (object)
|
|
- A mapping of `target_id` → object with two arrays: `simulated` and `real`.
|
|
- For each target id (string keys in JSON but integer in code):
|
|
- `simulated` — array of tuples/lists: `[timestamp, x_ft, y_ft, z_ft]`
|
|
- `timestamp`: monotonic timestamp (seconds)
|
|
- `x_ft`, `y_ft`, `z_ft`: positions in feet. Convention used: `x` = East, `y` = North, `z` = altitude (feet).
|
|
- `real` — same format as `simulated` but represents states reported by the radar (reception time used for `timestamp`).
|
|
|
|
- `simulation_geopos` (object)
|
|
- A mapping of `target_id` → array of geo-position samples computed for each recorded real state when possible.
|
|
- Each geo sample object contains:
|
|
- `timestamp` (number) — monotonic timestamp associated with the real sample
|
|
- `lat` (number) — decimal degrees (WGS84)
|
|
- `lon` (number) — decimal degrees (WGS84)
|
|
- `alt_ft` (number | null) — altitude in feet (if available)
|
|
- How geopos are computed (implementation notes):
|
|
- The archive looks for the nearest `ownship_trajectory` sample in time that includes `latitude`, `longitude` and `position_xy_ft`.
|
|
- It computes the east/north offsets between the target and ownship (units: feet), converts them to meters, and then converts meters to degrees using a simple equirectangular approximation:
|
|
- lat offset = delta_north_m / R
|
|
- lon offset = delta_east_m / (R * cos(lat_rad))
|
|
- R used: 6,378,137 m (WGS84 sphere approximation)
|
|
- The result is suitable for visualization on maps at local/regional scales. For high-precision geodesy or very large distances, replace this with a proper geodetic library (e.g. `pyproj` / `geographiclib`).
|
|
|
|
Extra notes and best practices
|
|
- Units and coordinate conventions
|
|
- Positions reported in `simulation_results` are in feet (ft). Velocities in feet/second (fps). Time values are seconds (monotonic timestamps used internally); `start_timestamp_utc` is wall-clock UTC ISO string.
|
|
- Georeferenced coordinates are decimal degrees (WGS84) and altitude remains in feet.
|
|
|
|
- Sample timestamps
|
|
- `timestamp` values saved with target states are the application's monotonic timestamps (used consistently for relative timing). When correlating with wall-clock time, use `start_timestamp_utc` and `duration_seconds` as anchors.
|
|
|
|
- Size considerations
|
|
- `latency_samples_ms` and `simulation_geopos` are intentionally limited in length to avoid huge files. If you need full-resolution traces, consider enabling raw persistence (the application can persist raw SFP packets separately) or adjust retention in the router/archive code.
|
|
|
|
Example (excerpt)
|
|
|
|
```json
|
|
{
|
|
"metadata": {
|
|
"scenario_name": "test",
|
|
"start_timestamp_utc": "2025-11-05T10:21:18.123456",
|
|
"duration_seconds": 12.34,
|
|
"client_update_interval_s": 1.0,
|
|
"client_update_rate_hz": 1.0,
|
|
"estimated_latency_ms": 12.34,
|
|
"latency_summary": {"mean_ms": 12.3, "std_ms": 0.5, "count": 100},
|
|
"latency_samples_ms": [11.2, 12.1, 12.4]
|
|
},
|
|
"ownship_trajectory": [
|
|
{"timestamp": 1.0, "position_xy_ft": [0.0, 0.0], "latitude": 45.0, "longitude": 9.0}
|
|
],
|
|
"simulation_results": {
|
|
"1": {
|
|
"simulated": [[1.0, 100.0, 200.0, 30.0]],
|
|
"real": [[1.1, 100.0, 200.0, 30.0]]
|
|
}
|
|
},
|
|
"simulation_geopos": {
|
|
"1": [{"timestamp": 1.1, "lat": 45.0009, "lon": 9.0012, "alt_ft": 30.0}]
|
|
}
|
|
}
|
|
```
|
|
|
|
If you want, I can:
|
|
- Add a small unit test that verifies `simulation_geopos` computation for a controlled synthetic case (recommended), or
|
|
- Replace the equirectangular approximation with `pyproj`/`geographiclib` for improved accuracy (adds dependency), or
|
|
- Add this document text into another doc or README section.
|
|
|
|
---
|
|
Document created by the development tooling. If you'd like this expanded with a field-level table or example scripts that load and plot the archive on a map (GeoJSON output), tell me which format you prefer and I will add it. |