7.1 KiB
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).
- Wall-clock UTC timestamp when the archive was saved (e.g.
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.0means one update per second.
- The update interval the client used when sending updates to the server (from UI
client_update_rate_hz(number, Hz) — optional- The computed send rate (1 / client_update_interval_s). Example:
1.0Hz.
- The computed send rate (1 / client_update_interval_s). Example:
estimated_latency_ms(number, milliseconds) — optional- One-way estimated latency (server → client) computed by the
ClockSynchronizermodel, rounded to 2 decimal places.
- One-way estimated latency (server → client) computed by the
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)
- Summary statistics over recent latency samples (milliseconds). Keys:
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_msformat are no longer supported.
- Array of
-
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.
- The scenario data returned by
-
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 simulationaltitude_ft(number)velocity_xy_fps(array[vx_east_fps, vy_north_fps]) — feet per secondheading_deg(number) — degrees (0 = North)latitude(number) — decimal degrees (WGS84)longitude(number) — decimal degrees (WGS84)
Note:
latitude/longitudemay be absent for some ownship samples if the upstream source doesn't provide them. - Time-ordered list of ownship samples recorded during the run. Each entry is an object with fields such as:
-
simulation_results(object)- A mapping of
target_id→ object with two arrays:simulatedandreal. - 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 assimulatedbut represents states reported by the radar (reception time used fortimestamp).
- A mapping of
-
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 samplelat(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_trajectorysample in time that includeslatitude,longitudeandposition_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).
- The archive looks for the nearest
- A mapping of
Extra notes and best practices
-
Units and coordinate conventions
- Positions reported in
simulation_resultsare in feet (ft). Velocities in feet/second (fps). Time values are seconds (monotonic timestamps used internally);start_timestamp_utcis wall-clock UTC ISO string. - Georeferenced coordinates are decimal degrees (WGS84) and altitude remains in feet.
- Positions reported in
-
Sample timestamps
timestampvalues saved with target states are the application's monotonic timestamps (used consistently for relative timing). When correlating with wall-clock time, usestart_timestamp_utcandduration_secondsas anchors.
-
Size considerations
latency_samples_msandsimulation_geoposare 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)
{
"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_geoposcomputation for a controlled synthetic case (recommended), or - Replace the equirectangular approximation with
pyproj/geographiclibfor 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.