S1005403_RisCC/doc/report_metadata.md

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.