Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 15 additions & 13 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -39,26 +39,28 @@ iMotions.xml
**/.venv/
**/__pycache__/

# Generated ML pipeline outputs (regenerated by test_model.py / train_model.py)
# Generated ML pipeline outputs (regenerated by evaluate_model.py / train_model.py)
inference/predictions_output.csv
inference/evaluation_metrics.json
inference/Models/*.pkl
inference/dataset/test.csv
inference/models/*.pkl
inference/data/prepared/test.csv

# Generated clustering inputs and outputs (regenerated by the scripts under inference/)
inference/Data/synthetic_pilot_telemetry.csv
inference/Data/synthetic_pilot_tiers.csv
inference/Data/flight_features.csv
inference/Data/pilot_features.csv
inference/Data/pilot_clusters.csv
inference/Data/group_centres.csv
inference/Data/client/*
inference/data/synthetic_flight_data.csv
inference/data/synthetic_pilot_telemetry.csv
inference/data/synthetic_pilot_tiers.csv
inference/data/flight_features.csv
inference/data/pilot_features.csv
inference/data/pilot_clusters.csv
inference/data/group_centres.csv
# Collected telemetry (one file per collection session, written by data_logger.py)
inference/Data/session_flight_data.csv
inference/data/session_flight_data.csv

# Local build archives
/xplane_plugin.zip
/src.zip

## Client Data
inference/data/Client/*
## Client Data (restricted, never commit)
# Both spellings, because the folder name is case-sensitive on Linux and macOS
inference/data/Client/*
inference/data/client/*
6 changes: 3 additions & 3 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ Data flow: `xplane_plugin` (C++) → `baton` (Rust library, compiled to C++) →
- `xplane_plugin/`: X-Plane plugin, built with Meson. `baton` lives in `xplane_plugin/subprojects/baton`.
- `relay/`: Rust relay with a GUI.
- `src/server/`: mock iMotions TCP server for testing without iMotions.
- `inference/`: logging, labeling, training. See `inference/README.md`.
- `inference/`: logging, pilot performance groups, and an out-of-scope event classification pipeline. One package, `inference/pilot_inference/`. See `inference/README.md`.

## Build and test

Expand All @@ -18,14 +18,14 @@ Run from the repo root, in order, per component:
- Plugin (also builds baton): `cd xplane_plugin && meson setup build && meson compile -C build && meson test -C build`
- Relay: `cd relay && cargo test`
- Baton: `cd xplane_plugin/subprojects/baton && cargo test`
- Inference: `cd inference && uv sync`, then `uv run python <script>`. `uv run pytest` runs the tests in `inference/tests/`; most scripts are untested.
- Inference: `cd inference && uv sync`, then `uv run python -m pilot_inference.<group>.<module>`. `uv run pytest` runs the tests in `inference/tests/`; the event classification scripts are mostly untested.

CI is in `.github/workflows/` (`meson-build.yml`, `super-linter.yml`). Run `meson setup` with the cross-file for your OS if not on native Windows (see README).

## Gotchas

- Hardware-bound: anything needing live X-Plane or iMotions cannot be verified by an agent. Say so rather than claiming it works.
- Data in `inference/Data/` was collected before #196, so `altitude` and `velocity` are mis-scaled (divide by 3.28084 and 1.94384). See `docs/telemetry_schema.md`.
- Data in `inference/data/` was collected before #196, so `altitude` and `velocity` are mis-scaled (divide by 3.28084 and 1.94384). See `docs/telemetry_schema.md`.
- `yaw` is true heading, effectively a duplicate of `heading`.
- The 20 Hz send on Windows and Linux lives in the plugin window's draw callback.
- Heading deviation needs wrapped angular difference, and floored modulo is Python-only; do not port the formula to C++ or Rust as written.
Expand Down
47 changes: 33 additions & 14 deletions GLOSSARY.md
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,18 @@ _Avoid_: Core metrics, KPIs
A set of pilots whose **Deviation features** resemble each other. Groups are not ranked.
_Avoid_: Tier (a **Synthetic pilot**'s known level is its **Hidden tier**, a different thing), skill level, class

**Segment**:
One of the seven phases of a **Flight**, from takeoff through landing.
_Avoid_: Phase, section, leg (the five-value **Flight phase** in the out-of-scope section and a **Leg** are different things)

**Leg**:
A stretch of the route flown on one heading, such as the 190° climb or the 220° approach.
_Avoid_: Segment (the client's seven phases), phase

**Instruction**:
The timestamped moment a pilot is told a new **Target**, such as "change heading to 20". Instructions are standardized across pilots.
_Avoid_: Command, prompt, target change

### Pilots and flights

**Pilot**:
Expand All @@ -74,37 +86,44 @@ _Avoid_: Session, run, trip
Facts about a pilot that are not telemetry: certification level, flight hours and pilot rating.
_Avoid_: Pilot profile, demographics

### Classification
### Synthetic pilots

**Synthetic pilot**:
A generated pilot with generated flights, built to deviate by a known amount, used to check that **Performance groups** find real differences before real pilots are available.
_Avoid_: Test pilot, fake pilot, mock pilot

**Hidden tier**:
The known deviation level a **Synthetic pilot** is generated at, kept apart from its telemetry so **Performance groups** can be compared against it. Real pilots have none.
_Avoid_: True group, skill level

### Out of scope: event classification

The client has not asked for these terms; they are kept apart from the model above.

**Flight event label**:
One of the 13 classes assigned to a sample, naming what the aircraft is doing at that moment.
One of the 13 classes assigned to a **Sample**, naming what the aircraft is doing at that moment.
_Avoid_: Class, tag, category

**Flight phase**:
A flight event label that names a stage of a flight: taxi, takeoff, cruise, approach or landing.
A **Flight event label** that names a stage of a flight: taxi, takeoff, cruise, approach or landing. There are five, and they are not the client's seven **Segments**.
_Avoid_: Stage, segment

**Synthetic data**:
Generated telemetry, balanced across flight event labels, used when real flights are not available.
Generated telemetry, balanced across **Flight event labels**, used when real flights are not available.
_Avoid_: Fake data, dummy data, mock data

**Synthetic pilot**:
A generated pilot with generated flights, built to deviate by a known amount, used to check that **Performance groups** find real differences before real pilots are available.
_Avoid_: Test pilot, fake pilot, mock pilot

**Hidden tier**:
The known deviation level a **Synthetic pilot** is generated at, kept apart from its telemetry so **Performance groups** can be compared against it. Real pilots have none.
_Avoid_: True group, skill level

## Relationships

- A **Pilot** flies many **Flights**; a **Flight** produces many **Samples**.
- A **Sample** has one value per telemetry field, and one **Deviation** per **Flight dynamics factor** once a **Target** exists.
- A **Sample** receives exactly one **Flight event label**; a **Flight phase** is a kind of flight event label.
- A **Flight** is divided into **Segments**, and is also flown as a sequence of **Legs**. An **Instruction** sets a new **Target** from that moment on.
- A **Pilot** falls into exactly one **Performance group**, decided by their **Deviation features** alone.
- **Pilot metadata** is compared against **Performance groups** after they are formed; it does not form them.
- Out of scope: a **Sample** receives exactly one **Flight event label**, and a **Flight phase** is a kind of **Flight event label**.

## Flagged ambiguities

- **Pilot rating** is the type of certification, not a performance score (client, see `docs/client_meeting_notes.md`). Whether it means the same as certification level, or an extra rating such as instrument, is still unclear.
- **Target** source is most likely instructor-assigned: the client gives standardized, timestamped instructions during the flight. No targets are captured yet, and the heading reference (true vs. magnetic) is unresolved.
- **Target** source is the **Instruction**: the client gives standardized, timestamped instructions during the flight, and iMotions holds the timestamps. None have been shared and no targets are captured yet, and the heading reference (true vs. magnetic) is unresolved.
- **Leg** and **Segment** may or may not line up. The client's seven **Segments** are unknown until she shares them, so how they map to our **Legs** is open.
- **Segment** names and boundaries are unknown until the client shares her seven-phase segmentation, which she marks by hand from the iMotions audio and video. Detecting **Instructions** automatically, for example from the eye tracker's audio, is later work; today the client segments by hand.
8 changes: 4 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,15 @@
- `baton`: C++ library for communicating with `relay`. Provides an object that handles inter-process communication (IPC) and abstracts over it for ease-of-use. Currently located in `xplane_plugin/subprojects`

Additional Components:
- `inference`: Python-based module for logging, labeling, and training a machine learning model using the collected flight data
- `inference`: Python package for logging telemetry and forming pilot performance groups from the collected flight data

# New Contributor? Start Here

Pilot Data Synchronization spans three languages (C++, Rust, Python) and two external programs (X-Plane, iMotions). You do **not** need all of them to contribute. Pick the path that matches the part you want to work on:

| You want to work on | You need | Start with |
|---|---|---|
| ML / inference (`inference/`) | Python 3 only, no simulator | [`inference/README.md`](inference/README.md). Sample data lives in `inference/Data/` |
| ML / inference (`inference/`) | Python 3 only, no simulator | [`inference/README.md`](inference/README.md). Sample data lives in `inference/data/` |
| Relay GUI (`relay/`) | Rust | [Setup → Relay](#2-relay), then test against the [mock iMotions server](#no-imotions-use-the-mock-server) |
| X-Plane plugin / baton | Meson, Ninja, mingw-w64, Rust | [Setup → X-Plane plugin](#1-x-plane-plugin) |
| Docs / data schema | Nothing to install | [`docs/telemetry_schema.md`](docs/telemetry_schema.md) and [`GLOSSARY.md`](GLOSSARY.md) |
Expand Down Expand Up @@ -191,7 +191,7 @@ The data flow is as follows:
- `xplane_plugin` extracts the data
- `xplane_plugin` uses the `baton` library to send the data to the `relay` program
- `relay` sends the data to iMotions
- *(optional)* `inference` can log the same TCP data stream for dataset creation and model training
- *(optional)* `inference` can log the same TCP data stream and form pilot performance groups from it

`relay` and `baton` are developed by this team and are not external programs/libraries.

Expand Down Expand Up @@ -237,7 +237,7 @@ Component-level detail lives next to the code it describes, so it stays in sync:
|---|---|
| Project vocabulary | [`GLOSSARY.md`](GLOSSARY.md) |
| Telemetry fields, units and deviation metrics (the data contract) | [`docs/telemetry_schema.md`](docs/telemetry_schema.md) |
| ML pipeline: logging, labeling, training | [`inference/README.md`](inference/README.md) |
| ML: logging, performance groups | [`inference/README.md`](inference/README.md) |
| X-Plane DataRefs used by the plugin | [`xplane_plugin/docs/key_datarefs.md`](xplane_plugin/docs/key_datarefs.md) |
| Issue labels and triage | [`docs/agents/triage-labels.md`](docs/agents/triage-labels.md) |
| Product roadmap framework | [`Documents/PRODUCT_ROADMAP_FRAMEWORK.md`](Documents/PRODUCT_ROADMAP_FRAMEWORK.md) |
Expand Down
2 changes: 1 addition & 1 deletion docs/adr/0002-cluster-on-std-and-absolute-bias.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Cluster on the log of std and |bias|, not mad or signed bias

`inference/features.py` emits `mad`, `std`, signed `bias` and `abs_bias` per flight dynamics factor. Pilot clustering (#216) uses only `std` (how much the pilot wobbles) and `|bias|` (how far the pilot sits off target), 8 features in total. `|bias|` is the mean of each flight's own `|bias|`, not the absolute value of the mean signed bias, which would cancel a pilot's flights on opposite sides of the target. These are the two independent parts of deviation: mean squared deviation is exactly `std² + bias²`. Each feature is clustered on a log scale, `log(x + floor)`, and then standardized over the cohort. The floors are 1 ft, 0.1°, 2 ft/min and 0.1 kt.
`inference/pilot_inference/performance/features.py` emits `mad`, `std`, signed `bias` and `abs_bias` per flight dynamics factor. Pilot clustering (#216) uses only `std` (how much the pilot wobbles) and `|bias|` (how far the pilot sits off target), 8 features in total. `|bias|` is the mean of each flight's own `|bias|`, not the absolute value of the mean signed bias, which would cancel a pilot's flights on opposite sides of the target. These are the two independent parts of deviation: mean squared deviation is exactly `std² + bias²`. Each feature is clustered on a log scale, `log(x + floor)`, and then standardized over the cohort. The floors are 1 ft, 0.1°, 2 ft/min and 0.1 kt.

## Considered Options

Expand Down
14 changes: 7 additions & 7 deletions docs/telemetry_schema.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,11 @@ and the DataRef reference in [xplane_plugin/docs/key_datarefs.md](../xplane_plug
## 1. What the plugin outputs today

The plugin reads pilot-side cockpit DataRefs at up to 20 Hz (throttled to one
send per 50ms) and streams them through Baton → Relay → `data_logger.py`, which
send per 50ms) and streams them through Baton → Relay → `inference/pilot_inference/capture/data_logger.py`, which
writes one CSV row per complete sample.

Current CSV output (`inference/Data/session_flight_data.csv`, one file per
collection session). The CSVs committed under `inference/Data/` were logged
Current CSV output (`inference/data/session_flight_data.csv`, one file per
collection session). The CSVs committed under `inference/data/` were logged
before `pilot_id` and `flight_id` existed and do not have those two columns:

| Field | Type | Units | Source (DataRef) | Notes |
Expand All @@ -43,7 +43,7 @@ error), not the aircraft's raw physical state — see note 2 in
#196, the plugin multiplied `altitude_ft_pilot` (already feet) by 3.28084 and
`airspeed_kts_pilot` (already knots) by 1.94384 before sending. Data collected
with the fixed plugin is in feet and knots. The CSVs committed under
`inference/Data/` were collected before the fix, so their `altitude` and
`inference/data/` were collected before the fix, so their `altitude` and
`velocity` columns, and the labels derived from them, are still scaled by
those factors. Divide `altitude` by 3.28084 and `velocity` by 1.94384, or
re-collect the data, before using it.
Expand Down Expand Up @@ -171,15 +171,15 @@ return a negative remainder for a negative left-hand side — both the plugin
and the relay are implemented in those languages, so a direct port of this
line would be wrong there. The formula's output range is also `[-180, 180)`:
a difference of exactly +180° maps to -180°. The shared helper is
`angular_diff` in `inference/angles.py`; both `features.py` and
`label_generator.py`'s `_calculate_heading_change` (a related turn-rate
`angular_diff` in `inference/pilot_inference/angles.py`; both `performance/features.py` and
`event_classification/label_generator.py`'s `_calculate_heading_change` (a related turn-rate
quantity) call it, so they agree at that boundary. Point any new heading
computation at the helper rather than re-deriving this formula.

The airspeed columns are named `airspeed_*` to match the glossary, while the
raw telemetry column keeps its name, `velocity`.

`inference/features.py` summarises deviation per flight and per pilot in raw
`inference/pilot_inference/performance/features.py` summarises deviation per flight and per pilot in raw
units: mean absolute deviation (`_mad`), standard deviation of the signed
deviation (`_std`), mean signed deviation (`_bias`) and the absolute value of
each flight's `_bias` (`_abs_bias`) for each metric, plus the number of samples
Expand Down
Binary file removed inference/Models/bestModel.pkl
Binary file not shown.
Binary file removed inference/Models/finalModel.pkl
Binary file not shown.
Loading
Loading