Skip to content

Architecture

github-actions[bot] edited this page Nov 6, 2026 · 2 revisions

Architecture — how it works

FIX-Gateway is deliberately simple: one in-memory database in the middle, and plugins around the edge. Everything else follows from that.

        ┌──────────────── plugins load from the config file ────────────────┐
        │                                                                    │
   source plugins                     THE DATABASE                     consumer plugins
   (write keys) ───────────▶   key → value + quality + aux   ───────────▶  (read / subscribe)
                                    │      ▲                                  │
                                    │      └── compute plugin derives         │
                                    └────────  new keys from existing ones ───┘

There is no message routing to configure, no wiring between plugins. A plugin only ever talks to the database; the database notifies whoever is interested.

1. The database in the middle

The heart of FIX-Gateway is a single process-wide database of items, one per aircraft data point (IAS, ALT, OILP1, NAV1CRS, …). Each item carries far more than a number — see The FIX Database for the full model — but the essentials are:

  • a typed, bounded value (float / int / bool / str, clamped to min/max);
  • quality flags — old (stale), bad (suspect), fail (do not use), secfail (redundant source failed);
  • a tolerance (tol) timer — if nobody writes the item within its window, it is automatically marked old. This is how a display knows a sensor went quiet without any explicit "sensor lost" message;
  • aux values — named side-parameters (e.g. IAS carries Vs, Vno, Vne; a gauge carries lowWarn/highAlarm) that drive instrument arcs and bands.

Writing a value updates the timestamp (clearing old) and notifies every registered callback synchronously. That callback mechanism is the entire event system: consumers subscribe, compute functions recompute, the network plugin pushes updates to clients.

2. Plugins around the edge

A plugin is a Python class (Plugin, extending plugin.PluginBase) that the server loads at startup based on the config. It gets a clean API to the database and does one job:

Role Examples What it does
Source xplane, gpsd, stratux, canfix, rpi_bno055, mavlink reads an external protocol/sensor, writes keys
Consumer netfix (to pyEfis/clients), canfix (to bus nodes) reads/subscribes and forwards data out
Compute compute derives keys from other keys (TAS, density altitude, nav-source routing…)
Infrastructure quorum, annunciate, data_recorder, state_persist leader election, alerting, logging, persistence

The lifecycle is minimal: the server calls each plugin's run() (which returns immediately, spawning a thread if it needs one) and later stop(). See Writing a Plugin. Which plugins load, and their settings, is entirely a config decision — Configuration.

3. One key, one writer (the most important rule)

The database is a shared namespace and, by default, has no source arbitration: a write simply overwrites the value and notifies. So if two plugins write the same key, they fight — last-writer-wins, producing a flickering mix.

Enable exactly one source per key. Running both xplane and stratux attitude, for example, means they overwrite each other. This is the single most common integration mistake.

When you genuinely need more than one source for a value, give each source its own key and choose between them with a compute select, driven by a selector key — the way NAVSRC picks NAV1, NAV2 or GPS (section 4). The chosen value lands in one canonical key with its source's quality flags. Deliberate selection, not accidental collision.

For redundant gateways, the quorum plugin elects one leader (LEADER) from the QVOTE<n> keys the gateways exchange. Plugins that command equipment, such as mavlink and canfix outputs, act only on the leader.

4. The compute plugin — first-class derived data

compute is worth calling out because a lot of avionics "logic" lives there rather than in a source. It runs small functions wired in the config, each taking input keys and writing an output key, re-running whenever an input changes. Examples in use:

  • altp / altd — pressure altitude PALT from BARO and ALT, then density altitude DALT from PALT, TALT and OAT;
  • wrap360 for magnetic ground track (TRACKM = TRACK + MAGVAR);
  • select — an EFIS nav-source selector: COURSE/CDI/GSI = the source chosen by NAVSRC (0 = NAV1, 1 = NAV2, 2 = GPS), so the display and autopilot read one canonical key regardless of which radio is active. A second select on GPSSRC picks the GPS source itself — see Flight Plan Engine.

Because compute functions consume and produce ordinary keys, they compose freely and can be added without touching any source or consumer. See the compute plugin doc: doc/plugins/compute.rst.

5. Talking to the outside — NetFIX

Consumers that live in another process (pyEfis, a moving map, a control-head, a debug client) connect over the NetFIX ASCII protocol — a line-based TCP protocol on port 3490. A client can read a key, write a key, subscribe to updates, and query metadata, all over one socket. The server abstracts away whether a value came from a simulator, a real sensor, or a manual test. Full details: NetFIX Protocol.

6. Two contracts, kept separate

FIX-Gateway is the runtime flight-data bus — the values that change in flight. It deliberately does not manage reference data (terrain, charts, panel layouts, aircraft-parameter profiles) — that changes on a cycle, at the hangar, and is handled by the separate makerplane-data system. Keeping the two apart means the thing that updates over WiFi is never in the path of the thing that runs in the air.

Where to go next

Clone this wiki locally