From 1885d07aaf5b373910f8bc49bc178150425b606a Mon Sep 17 00:00:00 2001 From: Bill Mallard Date: Sat, 18 Apr 2026 06:36:00 -0500 Subject: [PATCH 1/7] =?UTF-8?q?Add=20comprehensive=20README.md=20=E2=80=94?= =?UTF-8?q?=20describe=20protocol=20spec,=20parameter=20coverage,=20and=20?= =?UTF-8?q?MAOS=20integration?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 78 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) create mode 100644 README.md diff --git a/README.md b/README.md new file mode 100644 index 0000000..7ec0f3e --- /dev/null +++ b/README.md @@ -0,0 +1,78 @@ +# CAN-FIX Protocol Specification + +**Status:** Open Source Specification — Creative Commons Licensed +**Format:** Sphinx documentation source (builds to PDF and HTML) +**Scope:** Experimental Amateur-Built (E-AB) Aircraft Avionics + +--- + +## What This Is + +This repository contains the **authoritative specification** for the CAN-FIX communication protocol — the CAN bus implementation of the Flight Information eXchange (FIX) protocol family. CAN-FIX defines how avionics nodes on an aircraft CAN network identify themselves, publish flight data parameters, and consume data from other nodes. + +The specification is licensed under Creative Commons, meaning anyone can read, implement, modify, and distribute compliant devices without licensing fees or vendor lock-in. + +## What CAN-FIX Defines + +- **Parameter namespace:** Every flight data value (airspeed, altitude, heading, GPS position, engine temperatures, control surface positions, etc.) has a named identifier. Parameters are organized by type and support multiple simultaneous sources (e.g., two independent EGT sensors for two engines). +- **Frame structure:** How 11-bit CAN identifiers are allocated across parameter types, node IDs, and index bytes for multi-instance data. +- **Node types and device IDs:** Standard device type identifiers so nodes can announce their role on the network. +- **Multi-source design:** The protocol explicitly accommodates redundant nodes publishing the same parameter type using separate identifier ranges, avoiding CAN arbitration conflicts. +- **Data types:** float, int, bool, string with defined units and ranges per parameter. + +## Parameter Coverage + +The specification covers the full avionics parameter set for a modern aircraft: + +| Domain | Example Parameters | +|---|---| +| Navigation | LAT, LONG, ALT, IAS, TAS, GS, heading, track | +| AHRS | Pitch, roll, yaw rate, accelerations | +| Engine (×2) | RPM, MAP, EGT (×N cylinders), CHT, oil temp/pressure, fuel flow | +| Control surfaces | Pitch, roll, yaw, flap, trim positions | +| Electrical | Bus voltage, current, alternator status | +| Fuel | Quantity (×N tanks), flow, pressure | +| COM/NAV radios | Active/standby frequencies | +| Systems | Gear position, door status, general annunciators | + +## Repository Contents + +| Path | Description | +|---|---| +| `src/canfix.json` | Machine-readable parameter definitions (primary reference) | +| `src/canfix.xml` | XML form of parameter definitions | +| `src/parameters.rst` | Human-readable parameter documentation source | +| `src/framedef.rst` | CAN frame format specification | +| `src/datatypes.rst` | Data type definitions | +| `src/physical.rst` | Physical layer requirements (wiring, termination, bit rate) | +| `src/requirements.rst` | Protocol requirements | +| `src/practices.rst` | Recommended implementation practices | + +## Building the Documentation + +Requires Python with Sphinx and supporting packages: + +```bash +sudo pip install pyexcel pyexcel-ods sphinx +sudo apt install texlive-latex-base texlive-fonts-recommended texlive-fonts-extra texlive-latex-extra texlive-xetex latexmk +cd src +make latexpdf # builds PDF +make html # builds HTML +``` + +## Role in the MakerPlane / MAOS Ecosystem + +This specification is the **shared language** for the entire avionics stack: + +- [can-fix-arduinolib](../can-fix-arduinolib) — Arduino implementation of this protocol +- [fix-gateway](../fix-gateway) — Software data broker that speaks CAN-FIX (among many other protocols) +- [pyEfis](../pyEfis) — EFIS display that consumes FIX parameter data +- [pyAvMap](../pyAvMap) — Moving map that consumes LAT/LONG/heading from the FIX data stream + +Any MAOS subsystem that needs to publish or consume avionics data should reference the `canfix.json` parameter list as the authoritative source of parameter names, units, and ranges before defining custom data interfaces. + +## Important Disclaimer + +> This specification is developed for Experimental Amateur-Built aircraft use only. +> It is not FAA-approved avionics software or a certified communication standard. +> All implementations are the builder's responsibility. From 6545202a17ed31741efd4c5111d2a43811f44302 Mon Sep 17 00:00:00 2001 From: Bill Mallard Date: Sat, 18 Apr 2026 07:20:42 -0500 Subject: [PATCH 2/7] Revert README: Remove MAOS-specific ecosystem sections for upstream PR compatibility --- README.md | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/README.md b/README.md index 7ec0f3e..85d7851 100644 --- a/README.md +++ b/README.md @@ -60,17 +60,6 @@ make latexpdf # builds PDF make html # builds HTML ``` -## Role in the MakerPlane / MAOS Ecosystem - -This specification is the **shared language** for the entire avionics stack: - -- [can-fix-arduinolib](../can-fix-arduinolib) — Arduino implementation of this protocol -- [fix-gateway](../fix-gateway) — Software data broker that speaks CAN-FIX (among many other protocols) -- [pyEfis](../pyEfis) — EFIS display that consumes FIX parameter data -- [pyAvMap](../pyAvMap) — Moving map that consumes LAT/LONG/heading from the FIX data stream - -Any MAOS subsystem that needs to publish or consume avionics data should reference the `canfix.json` parameter list as the authoritative source of parameter names, units, and ranges before defining custom data interfaces. - ## Important Disclaimer > This specification is developed for Experimental Amateur-Built aircraft use only. From b0c91853ed9fbb3873de3517cf4b208304d3e19d Mon Sep 17 00:00:00 2001 From: Bill Mallard Date: Mon, 20 Apr 2026 07:26:55 -0500 Subject: [PATCH 3/7] Add JSON schema validation test suite (30 tests, all passing) Validates canfix.json structural integrity: 230 parameters with required fields, unique IDs in 256-1759 range, valid compound/array types, parseable min/max with comma-formatted number handling, group coverage of 0-2047, status entries, and known-parameter spot checks. Co-Authored-By: Claude Sonnet 4.6 --- tests/test_canfix_spec.py | 252 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 252 insertions(+) create mode 100644 tests/test_canfix_spec.py diff --git a/tests/test_canfix_spec.py b/tests/test_canfix_spec.py new file mode 100644 index 0000000..41c165f --- /dev/null +++ b/tests/test_canfix_spec.py @@ -0,0 +1,252 @@ +""" +Structural validation tests for canfix.json. + +Run from the repo root: + python -m pytest tests/ -v +""" +import json +import os +import pytest + +SPEC_PATH = os.path.join(os.path.dirname(__file__), "..", "src", "canfix.json") + +# Base types per the CAN-FIX spec; compound types (e.g. INT[2],BYTE) are split on ',' +# and array suffixes (e.g. CHAR[2]) are stripped before lookup. +VALID_BASE_TYPES = {"BYTE", "WORD", "SHORT", "USHORT", "FLOAT", "DOUBLE", "CHAR", "BOOL", + "INT", "UINT", "LONG", "ULONG", "DINT", ""} + + +@pytest.fixture(scope="module") +def spec(): + with open(SPEC_PATH, encoding="utf-8") as f: + return json.load(f) + + +@pytest.fixture(scope="module") +def parameters(spec): + return spec["parameters"] + + +@pytest.fixture(scope="module") +def groups(spec): + return spec["groups"] + + +@pytest.fixture(scope="module") +def status(spec): + return spec["status"] + + +# ── Top-level structure ──────────────────────────────────────────────────────── + +class TestTopLevelStructure: + def test_has_parameters_key(self, spec): + assert "parameters" in spec + + def test_has_groups_key(self, spec): + assert "groups" in spec + + def test_has_status_key(self, spec): + assert "status" in spec + + def test_parameter_count(self, parameters): + assert len(parameters) == 230, ( + f"Expected 230 parameters, got {len(parameters)}" + ) + + def test_group_count(self, groups): + assert len(groups) == 17 + + def test_status_count(self, status): + assert len(status) == 10 + + +# ── Parameter required fields ───────────────────────────────────────────────── + +class TestParameterRequiredFields: + def test_all_have_id(self, parameters): + missing = [p.get("name", "?") for p in parameters if "id" not in p] + assert not missing, f"Parameters missing 'id': {missing}" + + def test_all_have_name(self, parameters): + missing = [p.get("id", "?") for p in parameters if "name" not in p or not p["name"]] + assert not missing, f"Parameters missing 'name': {missing}" + + def test_all_have_count(self, parameters): + missing = [p.get("id", "?") for p in parameters if "count" not in p] + assert not missing, f"Parameters missing 'count': {missing}" + + def test_all_have_type(self, parameters): + missing = [p.get("id", "?") for p in parameters if "type" not in p] + assert not missing, f"Parameters missing 'type': {missing}" + + def test_all_have_index_key(self, parameters): + missing = [p.get("id", "?") for p in parameters if "index" not in p] + assert not missing, f"Parameters missing 'index' key (may be null): {missing}" + + def test_all_have_metadata_key(self, parameters): + missing = [p.get("id", "?") for p in parameters if "metadata" not in p] + assert not missing, f"Parameters missing 'metadata' key: {missing}" + + +# ── Parameter ID constraints ────────────────────────────────────────────────── + +class TestParameterIds: + def test_ids_are_integers(self, parameters): + non_int = [p["name"] for p in parameters if not isinstance(p["id"], int)] + assert not non_int, f"Non-integer IDs: {non_int}" + + def test_ids_are_unique(self, parameters): + ids = [p["id"] for p in parameters] + seen = set() + dupes = [] + for i in ids: + if i in seen: + dupes.append(i) + seen.add(i) + assert not dupes, f"Duplicate parameter IDs: {dupes}" + + def test_ids_in_parameter_range(self, parameters): + # Parameters live in IDs 256–1759 per the CAN-FIX spec + out_of_range = [p["id"] for p in parameters if not (256 <= p["id"] <= 1759)] + assert not out_of_range, f"Parameter IDs outside 256–1759: {out_of_range}" + + def test_ids_sorted_ascending(self, parameters): + ids = [p["id"] for p in parameters] + assert ids == sorted(ids), "Parameter list is not sorted by ID" + + +# ── Parameter type constraints ──────────────────────────────────────────────── + +class TestParameterTypes: + def test_types_are_strings(self, parameters): + non_str = [p["id"] for p in parameters if not isinstance(p["type"], str)] + assert not non_str, f"Non-string types: {non_str}" + + def test_types_are_valid(self, parameters): + import re + def base_types(type_str): + # Split compound types on ',' then strip array notation e.g. CHAR[2] → CHAR + parts = [t.strip() for t in type_str.split(",")] + return [re.sub(r"\[\d+\]$", "", p) for p in parts] + + invalid = [] + for p in parameters: + for bt in base_types(p["type"]): + if bt not in VALID_BASE_TYPES: + invalid.append((p["id"], p["type"], bt)) + assert not invalid, f"Unrecognized base types: {invalid}" + + def test_count_is_positive_integer(self, parameters): + bad = [p["id"] for p in parameters + if not isinstance(p["count"], int) or p["count"] < 1] + assert not bad, f"Parameters with invalid count: {bad}" + + +# ── Min/max constraints ─────────────────────────────────────────────────────── + +class TestMinMax: + @staticmethod + def _parse_number(s): + # Some values use comma-formatted numbers e.g. "60,000" → 60000 + return float(str(s).replace(",", "")) + + def test_min_max_parseable_as_float(self, parameters): + bad = [] + for p in parameters: + for key in ("min", "max"): + if key in p and p[key] is not None: + try: + self._parse_number(p[key]) + except (TypeError, ValueError): + bad.append((p["id"], key, p[key])) + assert not bad, f"Non-numeric min/max values: {bad}" + + def test_min_less_than_max_where_present(self, parameters): + violations = [] + for p in parameters: + if "min" in p and "max" in p and p["min"] is not None and p["max"] is not None: + try: + mn = self._parse_number(p["min"]) + mx = self._parse_number(p["max"]) + if mn >= mx: + violations.append((p["id"], p["name"], mn, mx)) + except (TypeError, ValueError): + violations.append((p["id"], p["name"], p["min"], p["max"])) + assert not violations, f"min >= max: {violations}" + + def test_multiplier_parseable_as_float(self, parameters): + bad = [] + for p in parameters: + if "multiplier" in p and p["multiplier"] is not None: + try: + float(p["multiplier"]) + except (TypeError, ValueError): + bad.append((p["id"], p["multiplier"])) + assert not bad, f"Non-numeric multipliers: {bad}" + + +# ── Group constraints ───────────────────────────────────────────────────────── + +class TestGroups: + def test_groups_have_required_fields(self, groups): + for g in groups: + assert "name" in g, f"Group missing 'name': {g}" + assert "startid" in g, f"Group missing 'startid': {g}" + assert "endid" in g, f"Group missing 'endid': {g}" + + def test_group_startid_lte_endid(self, groups): + violations = [ + (g["name"], g["startid"], g["endid"]) + for g in groups + if g["startid"] > g["endid"] + ] + assert not violations, f"Groups with startid > endid: {violations}" + + def test_group_ids_cover_0_to_2047(self, groups): + covered = set() + for g in groups: + for i in range(g["startid"], g["endid"] + 1): + covered.add(i) + assert 0 in covered + assert 2047 in covered + + def test_every_parameter_id_falls_in_a_group(self, parameters, groups): + def in_any_group(pid): + return any(g["startid"] <= pid <= g["endid"] for g in groups) + + orphans = [p["id"] for p in parameters if not in_any_group(p["id"])] + assert not orphans, f"Parameter IDs not covered by any group: {orphans}" + + +# ── Status entries ──────────────────────────────────────────────────────────── + +class TestStatus: + def test_status_have_required_fields(self, status): + for s in status: + assert "type" in s, f"Status entry missing 'type': {s}" + assert "description" in s, f"Status entry missing 'description': {s}" + assert "datatype" in s, f"Status entry missing 'datatype': {s}" + + def test_status_descriptions_are_nonempty(self, status): + empty = [s.get("type") for s in status if not s.get("description")] + assert not empty, f"Status entries with empty description: {empty}" + + +# ── Spot-check well-known parameters ───────────────────────────────────────── + +class TestKnownParameters: + def _get(self, parameters, pid): + return next((p for p in parameters if p["id"] == pid), None) + + def test_flap_control_switches_id_256(self, parameters): + p = self._get(parameters, 256) + assert p is not None + assert p["name"] == "Flap Control Switches" + assert p["type"] == "BYTE" + + def test_trim_switches_id_258(self, parameters): + p = self._get(parameters, 258) + assert p is not None + assert p["name"] == "Trim Switches" + assert p["type"] == "WORD" From 0ac85d5ad350272db134a2160330bf7c942bdfab Mon Sep 17 00:00:00 2001 From: Bill Mallard Date: Tue, 30 Jun 2026 11:35:40 -0500 Subject: [PATCH 4/7] ci: build the spec to HTML and publish to GitHub Pages Docs-only workflow: installs Sphinx + pyexcel(-ods), runs the existing generators (canfix_json/canfix_xml/parameters) against CAN-FIX.ods, builds the HTML with sphinx-build, and deploys to GitHub Pages. Adds a root index.html redirect to toc.html (the master_doc). The schema (CAN-FIX.ods) is read only, never modified. Co-Authored-By: Claude Opus 4.8 (1M context) --- .github/workflows/docs.yml | 57 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 57 insertions(+) create mode 100644 .github/workflows/docs.yml diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml new file mode 100644 index 0000000..9f8f9bc --- /dev/null +++ b/.github/workflows/docs.yml @@ -0,0 +1,57 @@ +name: docs + +# Build the CAN-FIX specification (Sphinx) and publish the HTML to GitHub Pages. +# Docs-only: this reads src/CAN-FIX.ods + the .rst sources to build the site and +# never modifies the schema. Mirrors src/buildall.sh (minus the PDF). + +on: + push: + branches: [master] + workflow_dispatch: + +permissions: + contents: read + pages: write + id-token: write + +# One Pages deployment at a time. +concurrency: + group: pages + cancel-in-progress: true + +jobs: + build: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-python@v5 + with: + python-version: "3.12" + - name: Install Sphinx + spreadsheet readers + run: pip install sphinx pyexcel pyexcel-ods + - name: Generate parameter data from CAN-FIX.ods + working-directory: src + run: | + python canfix_json.py + python canfix_xml.py + python parameters.py + - name: Build HTML + working-directory: src + run: sphinx-build -b html . _build/html + - name: Field-list cleanup + root redirect to the TOC + run: | + find src/_build/html -name '*.html' -exec sed -i 's/::/:/g' {} + + printf '\n' > src/_build/html/index.html + - uses: actions/upload-pages-artifact@v3 + with: + path: src/_build/html + + deploy: + needs: build + runs-on: ubuntu-latest + environment: + name: github-pages + url: ${{ steps.deployment.outputs.page_url }} + steps: + - id: deployment + uses: actions/deploy-pages@v4 From 8af032ea9a5cbc590a9a2853313863e816db464b Mon Sep 17 00:00:00 2001 From: Bill Mallard Date: Tue, 30 Jun 2026 11:48:49 -0500 Subject: [PATCH 5/7] Add wiki companion: parameter quick-reference generator + pages Adds a GitHub-wiki companion to the published specification: - tools/gen_param_reference.py generates a parameter quick-reference table from src/canfix.json (grouped by CAN ID range), so it always matches the spec without hand maintenance. - wiki/ holds the canonical wiki source: Home, Frame-Format, Data-Types, Implementing-a-Node, the generated Parameter-Reference, and the _Sidebar/_Footer navigation. - tools/sync_wiki.sh regenerates the table and mirrors wiki/ to a local clone of the GitHub wiki for review and publishing. Docs-only: nothing here touches the master spreadsheet (CAN-FIX.ods) or the generated schema. Co-Authored-By: Claude Opus 4.8 (1M context) --- tools/gen_param_reference.py | 146 +++++++++++++++++ tools/sync_wiki.sh | 57 +++++++ wiki/Data-Types.md | 47 ++++++ wiki/Frame-Format.md | 137 ++++++++++++++++ wiki/Home.md | 85 ++++++++++ wiki/Implementing-a-Node.md | 82 ++++++++++ wiki/Parameter-Reference.md | 306 +++++++++++++++++++++++++++++++++++ 7 files changed, 860 insertions(+) create mode 100644 tools/gen_param_reference.py create mode 100644 tools/sync_wiki.sh create mode 100644 wiki/Data-Types.md create mode 100644 wiki/Frame-Format.md create mode 100644 wiki/Home.md create mode 100644 wiki/Implementing-a-Node.md create mode 100644 wiki/Parameter-Reference.md diff --git a/tools/gen_param_reference.py b/tools/gen_param_reference.py new file mode 100644 index 0000000..bcba362 --- /dev/null +++ b/tools/gen_param_reference.py @@ -0,0 +1,146 @@ +#!/usr/bin/env python3 +"""Generate the Parameter Quick-Reference wiki page from src/canfix.json. + +canfix.json is itself generated from the master spreadsheet (src/CAN-FIX.ods) +by src/canfix_json.py, so this script never touches the schema -- it only +reformats the already-generated parameter table into a Markdown quick reference +for the GitHub wiki. Paths are resolved relative to the repo root, so it can be +run from anywhere: + + python tools/gen_param_reference.py +""" +import json +import os + +REPO = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) +SRC = os.path.join(REPO, "src", "canfix.json") +OUT = os.path.join(REPO, "wiki", "Parameter-Reference.md") +PAGES = "https://billmallard.github.io/canfix-spec" +JSON_URL = "https://github.com/billmallard/canfix-spec/blob/master/src/canfix.json" + + +def esc(text): + """Escape a value for use inside a Markdown table cell.""" + return str(text).replace("|", "\\|").replace("\n", " ").strip() + + +def id_label(p): + """A single ID, or a 'start-end' range when the parameter spans several.""" + count = p.get("count", 1) or 1 + start = p["id"] + if count > 1: + return f"{start}-{start + count - 1}" + return str(start) + + +def range_label(p): + lo = p.get("min") + hi = p.get("max") + has_lo = lo not in (None, "") + has_hi = hi not in (None, "") + if not has_lo and not has_hi: + return "" + return f"{lo if has_lo else ''} to {hi if has_hi else ''}" + + +def notes(p): + bits = [] + idx = p.get("index") + if idx: + bits.append(f"Indexed by {idx}") + fmt = p.get("format") + if fmt: + bits.append(fmt) + for remark in (p.get("remarks") or []): + bits.append(remark) + return "; ".join(esc(b) for b in bits) + + +def main(): + with open(SRC, encoding="utf-8") as f: + spec = json.load(f) + + groups = spec["groups"] + params = spec["parameters"] + version = spec.get("version", "") + + out = [] + out.append("# Parameter Quick-Reference") + out.append("") + out.append( + f"Every CAN-FiX parameter (protocol version **{version}**), grouped by " + f"CAN identifier range. This page is generated from " + f"[`src/canfix.json`]({JSON_URL}), which is built from the master " + f"spreadsheet `src/CAN-FIX.ods` -- so it always matches the specification." + ) + out.append("") + out.append( + f"For the full normative definitions (data encodings, frame formats, " + f"meta data and bit fields) see the [complete specification]({PAGES}/), " + f"in particular the [Parameters chapter]({PAGES}/parameters.html)." + ) + out.append("") + out.append("**Reading this table**") + out.append("") + out.append( + "- **ID** -- the 11-bit CAN frame identifier. A range (e.g. `256-257`) " + "means the parameter occupies several consecutive IDs for multiple " + "instances." + ) + out.append( + f"- **Type** -- the [data type]({PAGES}/datatypes.html) of the value " + "(see also [Data Types](Data-Types))." + ) + out.append("- **Units / Range / Mult** -- engineering units, min/max, and the " + "multiplier (the value each least-significant bit represents).") + out.append( + "- **Notes** -- the index dimension (for indexed parameters), the data " + "format, and any per-parameter remarks." + ) + out.append("") + + total_groups = 0 + for g in groups: + gparams = sorted( + (p for p in params if g["startid"] <= p["id"] <= g["endid"]), + key=lambda x: x["id"], + ) + if not gparams: + continue + total_groups += 1 + out.append(f"## {esc(g['name'])} ({g['startid']}-{g['endid']})") + out.append("") + out.append("| ID | Parameter | Type | Units | Range | Mult | Notes |") + out.append("|---|---|---|---|---|---|---|") + for p in gparams: + row = [ + id_label(p), + esc(p.get("name", "")), + esc(p.get("type", "")), + esc(p.get("units", "") or ""), + esc(range_label(p)), + esc(p.get("multiplier", "") or ""), + notes(p), + ] + out.append("| " + " | ".join(row) + " |") + out.append("") + + out.append("---") + out.append("") + out.append( + f"_{len(params)} parameters across {total_groups} groups. Generated by " + f"`tools/gen_param_reference.py` from `src/canfix.json` (version " + f"{version}). Do not edit by hand -- edit the master spreadsheet and " + f"regenerate._" + ) + out.append("") + + os.makedirs(os.path.dirname(OUT), exist_ok=True) + with open(OUT, "w", encoding="utf-8", newline="\n") as f: + f.write("\n".join(out)) + print(f"wrote {OUT}") + print(f" {len(params)} parameters, {total_groups} groups") + + +if __name__ == "__main__": + main() diff --git a/tools/sync_wiki.sh b/tools/sync_wiki.sh new file mode 100644 index 0000000..66c9974 --- /dev/null +++ b/tools/sync_wiki.sh @@ -0,0 +1,57 @@ +#!/usr/bin/env bash +# +# Mirror the canonical wiki source (wiki/) to a local clone of the GitHub wiki. +# +# This regenerates the parameter quick-reference from src/canfix.json, then +# copies the wiki pages into a sibling ../canfix-spec.wiki working copy. It does +# NOT push -- review the result and push it yourself (publishing is outward-facing). +# +# Usage: +# tools/sync_wiki.sh [wiki-remote-url] [wiki-clone-dir] +# +# Defaults: +# wiki-remote-url = https://github.com/billmallard/canfix-spec.wiki.git +# wiki-clone-dir = ../canfix-spec.wiki (relative to repo root) +# +set -euo pipefail + +REPO_ROOT="$(cd "$(dirname "$0")/.." && pwd)" +SRC="$REPO_ROOT/wiki" +WIKI_URL="${1:-https://github.com/billmallard/canfix-spec.wiki.git}" +WIKI_DIR="${2:-$REPO_ROOT/../canfix-spec.wiki}" + +# 1. Regenerate the parameter quick-reference from the spec data. +echo "Regenerating Parameter-Reference.md from src/canfix.json" +python "$REPO_ROOT/tools/gen_param_reference.py" + +if [ ! -d "$SRC" ]; then + echo "error: $SRC not found" >&2 + exit 1 +fi + +# 2. Clone or update the wiki working copy. +if [ -d "$WIKI_DIR/.git" ]; then + echo "Updating existing wiki clone at $WIKI_DIR" + git -C "$WIKI_DIR" pull --ff-only +else + echo "Cloning wiki from $WIKI_URL into $WIKI_DIR" + echo " (the wiki must exist: open the repo's Wiki tab and create one page first," + echo " or push an initial commit to $WIKI_URL)" + git clone "$WIKI_URL" "$WIKI_DIR" +fi + +# 3. Copy each page. Inter-page links already use the extension-less GitHub wiki +# form ([text](Page-Name)); the sed is a harmless safety net for any that use +# a .md suffix. +shopt -s nullglob +for f in "$SRC"/*.md; do + base="$(basename "$f")" + sed -e 's#\](\([A-Za-z0-9_-]*\)\.md\([)#]\)#](\1\2#g' \ + "$f" > "$WIKI_DIR/$base" + echo " page: $base" +done + +echo +echo "Mirror staged in $WIKI_DIR (NOT pushed)." +echo "Review and publish:" +echo " cd \"$WIKI_DIR\" && git add -A && git commit -m 'Sync wiki from wiki/' && git push" diff --git a/wiki/Data-Types.md b/wiki/Data-Types.md new file mode 100644 index 0000000..7f309fa --- /dev/null +++ b/wiki/Data-Types.md @@ -0,0 +1,47 @@ +# Data Types + +How the 1-5 data bytes of a [Normal Data frame](Frame-Format#normal-data-frame) +are encoded. The full text is in the +[Data Types chapter](https://billmallard.github.io/canfix-spec/datatypes.html). + +Each parameter has a **fixed** data type -- there is no type indicator in the +message itself (this is deliberate, to keep small 8-bit nodes simple). Fixed-point +integers with an assumed multiplier are strongly preferred over floating point. + +| Type | Description | +|---|---| +| `CHAR` | 8-bit ASCII character | +| `BYTE` | 8 bits (discrete states, switch positions, commands) | +| `WORD` | 16 bits (discrete states, 16 bit wide) | +| `SHORT` | 8-bit signed integer | +| `USHORT` | 8-bit unsigned integer | +| `INT` | 16-bit signed integer | +| `UINT` | 16-bit unsigned integer | +| `DINT` | 32-bit signed integer | +| `UDINT` | 32-bit unsigned integer | +| `FLOAT` | 32-bit IEEE 754 floating point | + +## Key rules + +- **Little-endian.** Multi-byte values are sent least-significant byte first + (Byte 3 of the frame is the LSB). +- **Assumed decimal places.** Most quantities are integers scaled by a + **multiplier**. The engineering value is `raw x multiplier`. Example: a + parameter with units `%`, multiplier `0.01` and a raw `INT` of `5000` means + `50.00 %`. The multiplier for each parameter is in the + [Parameter Quick-Reference](Parameter-Reference). +- **Arrays and mixed types.** A parameter may pack several values into the five + data bytes. Time, for example, is three `USHORT`s (hours, minutes, seconds); + a date could be `UINT, USHORT[2]` (year, then month and day). The packing is + given in each parameter's definition -- the array/mixed forms show up in the + **Type** column of the quick-reference (e.g. `INT[2],BYTE`). +- **64-bit floats** are not a type; when extra precision is needed a parameter is + sent across two messages, defined per parameter (rarely needed). +- **Discrete bits.** `BYTE`/`WORD` parameters often carry independent bit flags + (switch up/down, mode engaged). The bit meanings are in the parameter's + **Notes** in the [Parameter Quick-Reference](Parameter-Reference) and in the + [Parameters chapter](https://billmallard.github.io/canfix-spec/parameters.html). + +--- + +See also: **[Frame Format](Frame-Format)** · **[Parameter Quick-Reference](Parameter-Reference)** diff --git a/wiki/Frame-Format.md b/wiki/Frame-Format.md new file mode 100644 index 0000000..10a1a0f --- /dev/null +++ b/wiki/Frame-Format.md @@ -0,0 +1,137 @@ +# Frame Format + +A quick reference for the CAN-FiX frame layouts. The full, authoritative text is +in the [Frame Definitions chapter](https://billmallard.github.io/canfix-spec/framedef.html) +of the specification. + +CAN-FiX uses the **CAN 2.0B base frame** -- an **11-bit identifier** and up to +**8 data bytes**. The identifier both names the message *and* sets its priority: +**lower ID = higher priority** (it wins bus arbitration). All multi-byte values +in the data field are **little-endian** (least-significant byte first). + +## How the 11-bit ID space is divided + +The identifier tells you which of the four frame formats you are looking at: + +| ID range | Frame type | Format | +|---|---|---| +| 1 - 255 | **Node Alarms** (highest priority) | [Node Alarm](#node-alarm-frame) | +| 256 - 1759 | **Parameter data** (the bulk of traffic) | [Normal Data](#normal-data-frame) | +| 1760 - 2015 | **Node-Specific messages** (config, ID, firmware...) | [Node-Specific](#node-specific-frame) | +| 2016 - 2047 | **Two-way connection channels** | (point-to-point payloads) | + +Within the parameter-data range, IDs are grouped by category and priority. These +are the groups used by the [Parameter Quick-Reference](Parameter-Reference): + +| ID range | Group | +|---|---| +| 256 - 319 | High Priority Pilot Control Inputs | +| 320 - 383 | High Priority Measured Positions | +| 384 - 447 | High Priority Flight Data | +| 448 - 511 | High Priority Navigation Data | +| 512 - 639 | High Priority Engine / Aircraft System Data | +| 640 - 767 | High Priority Auxiliary Data | +| 768 - 895 | Normal Priority Pilot Control Inputs | +| 896 - 1023 | Normal Priority Measured Positions | +| 1024 - 1151 | Normal Priority Flight Data | +| 1152 - 1279 | Normal Priority Navigation Data | +| 1280 - 1407 | Normal Priority Engine / Aircraft System Data | +| 1408 - 1535 | Normal Priority Auxiliary Data | + +## Normal Data frame + +The heart of the protocol -- a parameter value update. Frame ID = the parameter +ID (see [Parameter Quick-Reference](Parameter-Reference)). + +| Byte 0 | Byte 1 | Byte 2 | Byte 3 | Byte 4 | Byte 5 | Byte 6 | Byte 7 | +|---|---|---|---|---|---|---|---| +| Node | Index | Function Code | Data LSB | Data | Data | Data | Data MSB | + +- **Node** -- the Node ID of the sender (1-255, unique on the network). +- **Index** -- selects an instance of an indexed parameter (e.g. EGT per + cylinder: 0, 1, 2...). Zero when the parameter is not indexed. +- **Function Code** -- meta data selector + quality flags (see below). +- **Data** -- 1 to 5 bytes, little-endian, encoded per the parameter's + [data type](Data-Types). + +### Function / Status Code byte + +A value of **0** means "ordinary value update, good quality" -- the common case. + +| Bit 7 | Bit 6 | Bit 5 | Bit 4 | Bit 3 | Bit 2 | Bit 1 | Bit 0 | +|---|---|---|---|---|---|---|---| +| Meta Data 3 | Meta Data 2 | Meta Data 1 | Meta Data 0 | Future | Failure | Quality | Annunciate | + +- **Annunciate (bit 0)** -- the value is out of range and should be annunciated + (e.g. an alarm set point was exceeded). +- **Quality (bit 1)** -- the sender suspects the data; show it flagged (like an + attitude-indicator flag). +- **Failure (bit 2)** -- the sender knows the data is bad; do not show it, + indicate the failure. +- **Meta Data (bits 4-7)** -- when non-zero, the data field carries one of 15 + meta-data values for this parameter (alarm set points, V-speeds, scaling) + instead of the parameter value itself. Meta data shares the parameter's units, + range and data type. Value 0 = the parameter itself. + +> If Quality or Failure is set, use an alternate source for the value if one is +> available. + +## Node Alarm frame + +Identifiers **1-255**: a real-time alarm from the node whose ID equals the frame +identifier. These are the highest-priority messages on the bus -- use them for +millisecond-critical notifications (e.g. coordinating a fly-by-wire failover), +**not** for annunciating a low oil pressure to the pilot. + +| Byte 0 | Byte 1 | Byte 2 | Byte 3 | Byte 4 | Byte 5 | Byte 6 | Byte 7 | +|---|---|---|---|---|---|---|---| +| Alarm Code LSB | Alarm Code MSB | Data LSB | Data | Data | Data | Data | Data MSB | + +The 16-bit alarm code and the six data bytes are **not** defined by the +specification -- their meaning is left to the implementer. + +## Node-Specific frame + +Identifiers **1760-2015**: configuration and management messages. The sender's +Node ID is implied by the identifier: + +``` +Node ID = Frame ID - 1760 +``` + +| Byte 0 | Byte 1 | Byte 2 | Byte 3 | Byte 4 | Byte 5 | Byte 6 | Byte 7 | +|---|---|---|---|---|---|---|---| +| Control Code | Data LSB | Data | Data | Data | Data | Data | Data MSB | + +For most of these, **Byte 1 is the destination node** (0 = broadcast to all). + +### Control codes + +| Code | Message | Mandatory | Responds to broadcast | +|---|---|---|---| +| 0 | Node Identification | Yes | Yes | +| 1 | Bit Rate Set | Yes | Yes | +| 2 | Node ID Set | Yes | No | +| 3 | Disable Parameter | Yes | Yes | +| 4 | Enable Parameter | Yes | No | +| 5 | Node Report | Yes | Yes | +| 6 | Node Status Information | No | N/A | +| 7 | Update Firmware | No | No | +| 8 | Two-Way Connection Request | No | No | +| 9 | Node Configuration Set | No | No | +| 10 | Node Configuration Query | No | No | +| 11 | Node Description | No | No | +| 12 - 19 | Parameter Set (8 codes cover index ranges 0-255) | No | N/A | +| 20 - 127 | Reserved for future use | | | +| 128 - 255 | User defined | No | | + +**Bit rates** (Bit Rate Set, byte 2): `1` = 125 kbps, `2` = 250 kbps, +`3` = 500 kbps, `4` = 1 Mbps. + +See the [Frame Definitions chapter](https://billmallard.github.io/canfix-spec/framedef.html) +for the per-message payloads (Node Identification response, Parameter Set bit +layout, firmware/channel handshakes, etc.). + +--- + +See also: **[Data Types](Data-Types)** · **[Parameter Quick-Reference](Parameter-Reference)** · **[Implementing a Node](Implementing-a-Node)** diff --git a/wiki/Home.md b/wiki/Home.md new file mode 100644 index 0000000..05d542e --- /dev/null +++ b/wiki/Home.md @@ -0,0 +1,85 @@ +# CAN-FiX + +**CAN-FiX** is the CAN bus implementation of **FIX** (*Flight Information +eXchange*), an open, vendor-neutral protocol family for exchanging data between +aircraft avionics and flight systems. It is aimed at the Experimental Amateur +Built (E-AB) community: the specification and protocols are released under a +Creative Commons license, so anyone can build a device or write software that +talks to other CAN-FiX equipment without paying for a license. + +This wiki is a **friendly companion** to the formal specification. It gives you +quick-reference tables and a get-started guide. The **full, normative +specification** -- the document that actually defines the protocol -- is +published here: + +- **Specification (web):** +- **Specification (PDF):** built by `make latexpdf` (see [Building the docs](#building-the-spec-yourself)) +- **Source repository:** + +> If the wiki and the specification ever disagree, **the specification wins.** +> The wiki is generated/curated from the same source and is meant for speed of +> lookup, not as the authority. + +## Start here + +| If you want to... | Go to | +|---|---| +| Understand what CAN-FiX is and why | [Introduction chapter](https://billmallard.github.io/canfix-spec/intro.html) | +| Look up a parameter (ID, units, range) | **[Parameter Quick-Reference](Parameter-Reference)** | +| Decode or build a CAN frame | **[Frame Format](Frame-Format)** | +| Know how a value is encoded on the wire | **[Data Types](Data-Types)** | +| Build your own CAN-FiX node | **[Implementing a Node](Implementing-a-Node)** | +| Read the complete, normative text | [Full specification](https://billmallard.github.io/canfix-spec/) | + +## What CAN-FiX is in one minute + +A CAN-FiX network is made of **nodes**, each with a unique **Node ID** (1-255; +0 is the broadcast address). The normal pattern is *producer / consumer*: a node +that measures something (airspeed, altitude, engine data, GPS position) puts that +value on the bus, and any other node that cares (a display, a recorder, an +autopilot) reads it. No node addresses another to receive data -- data is simply +produced for all to consume. + +CAN-FiX is built on CAN 2.0B but uses only the **base frame format** (an 11-bit +identifier), giving 2048 message types. The identifier *is* the priority: lower +ID wins arbitration, so the most critical data (node alarms, pilot control +inputs) sits at the low end of the ID space and is delivered first. The +[Frame Format](Frame-Format) page shows how that ID space is divided up. + +Beyond live data, the protocol also defines node configuration, identification, +firmware update, and point-to-point parameter setting -- see +[Implementing a Node](Implementing-a-Node). + +## About this fork + +This is **[billmallard/canfix-spec](https://github.com/billmallard/canfix-spec)**, +a fork of the MakerPlane CAN-FiX specification, maintained and published under +the project's open-source license. The web specification and this wiki are built +and kept current from this fork. The master copy of the parameter data is the +spreadsheet `src/CAN-FIX.ods`; everything else (the JSON/XML exports, the +parameter list, this wiki's quick-reference) is **generated** from it. + +## Building the spec yourself + +The HTML and PDF are produced with [Sphinx](https://www.sphinx-doc.org/). From +the repository: + +```bash +cd src +pip install sphinx pyexcel pyexcel-ods # one-time +python canfix_json.py # regenerate canfix.json from the .ods +python canfix_xml.py # regenerate canfix.xml +python parameters.py # regenerate parameter_list.rst +make html # -> src/_build/html +make latexpdf # -> src/_build/latex (needs LaTeX) +``` + +The web spec at the link above is rebuilt automatically by GitHub Actions on +every push to `master`. The [Parameter Quick-Reference](Parameter-Reference) in +this wiki is regenerated by `tools/gen_param_reference.py`. + +--- + +*CAN is a registered trademark of Robert Bosch GmbH. CAN-FiX and the FIX +protocols are licensed under Creative Commons -- see the +[License chapter](https://billmallard.github.io/canfix-spec/license.html).* diff --git a/wiki/Implementing-a-Node.md b/wiki/Implementing-a-Node.md new file mode 100644 index 0000000..354b50e --- /dev/null +++ b/wiki/Implementing-a-Node.md @@ -0,0 +1,82 @@ +# Implementing a Node + +A practical get-started checklist for building a CAN-FiX node. It summarizes the +specification; the binding rules are in the +[Requirements](https://billmallard.github.io/canfix-spec/requirements.html) and +[Best Practices](https://billmallard.github.io/canfix-spec/practices.html) +chapters, with frame details in +[Frame Definitions](https://billmallard.github.io/canfix-spec/framedef.html). + +## 1. Pick a Node ID and bit rate + +- Choose a **Node ID** in **1-255**, unique on the network (0 is the broadcast + address, never a real node). +- Use the **CAN 2.0B base frame** (11-bit identifier) only. +- Default to a network **bit rate** the installation agrees on -- the protocol + defines `125k / 250k / 500k / 1M` and a [Bit Rate Set](Frame-Format#control-codes) + message to change it. +- Read the bus as you transmit (standard CAN arbitration); a lower identifier + always wins, so your most critical parameters belong at lower IDs. + +## 2. Produce your parameters + +For each thing your node measures: + +1. Find (or choose) its parameter ID in the + [Parameter Quick-Reference](Parameter-Reference). The **frame ID is the + parameter ID**. +2. Build the [Normal Data frame](Frame-Format#normal-data-frame): + - Byte 0 = your Node ID + - Byte 1 = Index (0 if the parameter is not indexed) + - Byte 2 = Function Code (0 for a good-quality value) + - Bytes 3-7 = the value, **little-endian**, in the parameter's + [data type](Data-Types) (`raw = engineering / multiplier`) +3. Transmit it periodically. Send only what your node is responsible for. + +**Quality matters.** When a reading is suspect or failed, set the **Quality** or +**Failure** bit in the Function Code rather than sending a silently-wrong value. +Set **Annunciate** when a value crosses an alarm threshold. See +[the Function/Status code](Frame-Format#function--status-code-byte). + +**Send your meta data.** If a parameter has alarm set points or ranges (e.g. oil +pressure limits, V-speeds), the *measuring* node should transmit them as meta data +(Function Code meta-data bits 1-15) so every display stays consistent without +separate configuration. This is a core design principle of CAN-FiX. + +## 3. Handle the mandatory node-specific messages + +Every node **must** respond to these [control codes](Frame-Format#control-codes) +(Node-Specific frames, ID = `1760 + your Node ID`): + +| Code | Message | What your node must do | +|---|---|---| +| 0 | Node Identification | Reply with the spec revision you implement (`0x01` for this spec); optionally follow with a [Node Description](https://billmallard.github.io/canfix-spec/framedef.html#node-description) string | +| 1 | Bit Rate Set | Change CAN bit rate immediately and permanently | +| 2 | Node ID Set | Change your Node ID, then announce success on the new ID | +| 3 | Disable Parameter | Stop broadcasting the named parameter | +| 4 | Enable Parameter | Resume broadcasting the named parameter | +| 5 | Node Report | Immediately send every parameter you own, plus its meta data | + +Optional messages (firmware update, two-way channels, node configuration, +node status, point-to-point Parameter Set) are described in the +[Frame Definitions chapter](https://billmallard.github.io/canfix-spec/framedef.html). + +## 4. Consume what you need + +To use data from the bus, just listen for the relevant parameter IDs and decode +per [Data Types](Data-Types). Respect the **Quality / Failure** flags -- fall +back to an alternate source when set. Remember that **one parameter should have +one producer**: if two nodes broadcast the same parameter they will conflict, so +use Disable/Enable Parameter (or a Node Report at startup) to resolve duplicates. + +## Reference implementations + +- **C / Arduino:** the [can-fix-arduinolib](https://github.com/makerplane/can-fix-arduinolib) + library implements the node-specific protocol and parameter framing. +- **Python:** the [python-canfix](https://github.com/birkelbach/python-canfix) + package (and MakerPlane's FIX-Gateway, which bridges CAN-FiX to other + transports) are useful references for decode/encode. + +--- + +See also: **[Frame Format](Frame-Format)** · **[Data Types](Data-Types)** · **[Parameter Quick-Reference](Parameter-Reference)** diff --git a/wiki/Parameter-Reference.md b/wiki/Parameter-Reference.md new file mode 100644 index 0000000..009f782 --- /dev/null +++ b/wiki/Parameter-Reference.md @@ -0,0 +1,306 @@ +# Parameter Quick-Reference + +Every CAN-FiX parameter (protocol version **0.7**), grouped by CAN identifier range. This page is generated from [`src/canfix.json`](https://github.com/billmallard/canfix-spec/blob/master/src/canfix.json), which is built from the master spreadsheet `src/CAN-FIX.ods` -- so it always matches the specification. + +For the full normative definitions (data encodings, frame formats, meta data and bit fields) see the [complete specification](https://billmallard.github.io/canfix-spec/), in particular the [Parameters chapter](https://billmallard.github.io/canfix-spec/parameters.html). + +**Reading this table** + +- **ID** -- the 11-bit CAN frame identifier. A range (e.g. `256-257`) means the parameter occupies several consecutive IDs for multiple instances. +- **Type** -- the [data type](https://billmallard.github.io/canfix-spec/datatypes.html) of the value (see also [Data Types](Data-Types)). +- **Units / Range / Mult** -- engineering units, min/max, and the multiplier (the value each least-significant bit represents). +- **Notes** -- the index dimension (for indexed parameters), the data format, and any per-parameter remarks. + +## High Priority Pilot Control Inputs (256-319) + +| ID | Parameter | Type | Units | Range | Mult | Notes | +|---|---|---|---|---|---|---| +| 256-257 | Flap Control Switches | BYTE | | | | Discrete Bits; b0 = Up; b1 = Down; b2 - b7 Can be used for predefined notches | +| 258-259 | Trim Switches | WORD | | | | Discrete Bits; b0 = Pitch Up; b1 = Pitch Down; b2 = Pitch Center; b3 = Roll Left; b4 = Roll Right; b5 = Roll Center; b6 = Yaw Left; b7 = Yaw Right; b8 = Yaw Center; b9 = Collective Up; b10 = Collective Dn; b11 = AT Pedals Left; b12 = AT Pedals Right; b13 = AT Pedals Center | +| 260-261 | Electrical Bus Control Switches | BYTE | | | | Discrete Bits | +| 262-263 | Lighting Control Switches | BYTE | | | | Discrete Bits | +| 264-265 | Fuel Pump System Switches | BYTE | | | | Discrete Bits | +| 266-267 | Fuel Valve System Switches | BYTE | | | | Discrete Bits | +| 268-269 | Auto Pilot Commands | WORD | | | | Discrete Bits; b0 = Engage; b1 = Disconnect; b2 = VS Mode; b3 = Alt Hold Mode; b4 = Alt Select Mode; b5 = Heading Increment; b6 = Heading Decrement | +| 270-271 | VHF Control Commands | BYTE | | | | Indexed by Radio; Discrete Bits; b0 = PTT; b1 = Flip; b2 = Next Saved Freq; b3 = Prev Saved Freq | +| 272-273 | VOR/LOC Control Commands | BYTE | | | | Indexed by Radio; Discrete Bits; b1 = Flip; b2 = Next Saved Freq; b3 = Prev Saved Freq | +| 274-275 | Transponder Commands | BYTE | | | | Discrete Bits; b0 = IDENT; b1 = ALT; b2 = STBY; b3 = VFR; b4 = OFF; b5 = Squat | +| 276-277 | Starter / Magneto Commands | BYTE | | | | Discrete Bits | +| 278-279 | Landing Gear Control Position | BYTE | | | | Discrete Bits; 0=Down; 1=Up; b0=Nose; b1=Left; b2=Right | +| 280-281 | Keypad Input | CHAR[2] | | | | Key, Function Key | +| 282-283 | Encoder Input (High Priority) | INT[2],BYTE | | | | Indexed by Unit; Steps Moved; X,Y and Switch Positions; Less than 0 = CCW, Greater than 0 = CW | +| 284-291 | Generic Switches (High Priority) | BYTE[5] | | | | Indexed by Unit; Discrete Bits; User Defined For Multiplexing Switches | +| 292 | Pitch Control Position | INT | % | -100 to 100 | 0.01 | Greater Than 0 = Nose Up | +| 293 | Roll Control Position | INT | % | -100 to 100 | 0.01 | Greater Than 0 = Right | +| 294 | Yaw Control Position | INT | % | -100 to 100 | 0.01 | Greater Than 0 = Right | +| 295 | Collective Control Position | INT | % | -100 to 100 | 0.01 | Greater Than 0 = Up | +| 296 | Anti-Torque Pedals Position | INT | % | -100 to 100 | 0.01 | Greater Than 0 = Right | +| 297 | Flap Control Position | INT | % | -100 to 100 | 0.01 | Greater Than 0 = Down | +| 298 | Left Brake Control Position | UINT | % | 0 to 100 | 0.01 | | +| 299 | Right Brake Control Position | UINT | % | 0 to 100 | 0.01 | | +| 300-301 | Engine Throttle Control Position | UINT | % | 0 to 100 | 0.01 | | +| 302-303 | Engine Prop Control Position | UINT | % | 0 to 100 | 0.01 | | +| 304-305 | Engine Mixture Control Position | UINT | % | 0 to 100 | 0.01 | | +| 306-307 | Generic Analog Control (High Priority) | UINT | % | 0 to 100 | 0.01 | Indexed by Unit; User Defined | + +## High Priority Measured Positions (320-383) + +| ID | Parameter | Type | Units | Range | Mult | Notes | +|---|---|---|---|---|---|---| +| 320 | Elevator Position | INT | ° | -90 to 90 | 0.01 | Greater Than 0 = Nose Up | +| 321 | Aileron Position | INT | ° | -90 to 90 | 0.01 | Greater Than 0 = Right | +| 322 | Rudder Position | INT | ° | -90 to 90 | 0.01 | Greater Than 0 = Right | +| 323 | Collective Position | INT | ° | -90 to 90 | 0.01 | Greater Than 0 = Up | +| 324 | Tail Rotor Angle | INT | ° | -90 to 90 | 0.01 | Greater Than 0 = Right | +| 325 | Flap Position | INT | ° | -90 to 90 | 0.01 | Greater Than 0 = Down | +| 326-328 | Landing Gear Position Switches | BYTE | | | | Discrete Bits; b0=Nose Up; b1=Nose Down; b2=Left Up; b3=Left Down; b4=Right Up; b5=Right Down | + +## High Priority Flight Data (384-447) + +| ID | Parameter | Type | Units | Range | Mult | Notes | +|---|---|---|---|---|---|---| +| 384 | Pitch Angle | INT | ° | -90 to 90 | 0.01 | Greater Than 0 = Nose Up | +| 385 | Roll Angle | INT | ° | -180 to 180 | 0.01 | Greater Than 0 = Right | +| 386 | Angle of Attack | INT | ° | -90 to 90 | 0.01 | | +| 387 | Indicated Airspeed | UINT | knots | 0 to 999.9 | 0.1 | | +| 388 | Indicated Altitude | DINT | ft | -1000 to 60,000 | | | +| 389 | Heading | UINT | ° | 0 to 359.9 | 0.1 | Magnetic Heading | +| 390 | Vertical Speed | INT | ft/min | -30,000 to 30,000 | | | +| 391 | TE Variometer Vertical Speed | INT | knots | -300 to 300 | 0.01 | | +| 392 | Radar Altitude | UINT | ft | 0 to 60,000 | | | +| 393 | Yaw Angle | INT | ° | -180 to 180 | 0.01 | | +| 394 | Normal Acceleration | INT | g | -30 to 30 | 0.001 | | +| 395 | Lateral Acceleration | INT | g | -30 to 30 | 0.001 | | +| 396 | Longitudinal Acceleration | INT | g | -30 to 30 | 0.001 | | +| 397 | True Airspeed | UINT | knots | 0 to 999.9 | 0.1 | | +| 398 | Calibrated Airspeed | UINT | knots | 0 to 999.9 | 0.1 | | +| 399 | Mach Number | UINT | Mach | 0 to 100 | 0.01 | | +| 400 | Altimeter Setting | UINT | inHg | 0 to 35 | 0.001 | | +| 401 | Pressure Altitude | DINT | ft | -1,000 to 60,000 | | | + +## High Priority Navigation Data (448-511) + +| ID | Parameter | Type | Units | Range | Mult | Notes | +|---|---|---|---|---|---|---| +| 448 | VOR/LOC Deviation | UINT | ° | 0 to 359.9 | 0.1 | | +| 449 | Glideslope Deviation | INT | ° | -45 to 45 | 0.01 | | +| 450 | OBI Flags | WORD | | | | b0 = To/From (To = 1); b1:b2 = Input (00=NAV1, 01=NAV2, 10=GPS1, 11=GPS2); b3 = GS; b4 = LOC/NAV | +| 451 | Aircraft Position Latitude | FLOAT | ° | -90 to 90 | | | +| 452 | Aircraft Position Longitude | FLOAT | ° | -180 to 180 | | | +| 453 | Groundspeed | UINT | knots | 0 to 2000 | 0.1 | | +| 454 | True Ground Track | UINT | ° | 0 to 359.9 | 0.1 | | +| 455 | Magnetic Ground Track | UINT | ° | 0 to 359.9 | 0.1 | | +| 456 | Cross Track Error | INT | nm | | 0.01 | | +| 457 | Selected Course | UINT | ° | 0 to 359.9 | 0.1 | | +| 458 | Selected Glidepath Angle | UINT | ° | 0 to 90 | 0.1 | | +| 459 | Selected Vertical Speed | INT | ft/min | -30,000 to 30,000 | | | +| 460 | Selected Airspeed | UINT | knots | 0 to 999.9 | 0.1 | | +| 461 | Selected Altitude | DINT | ft | -1000 to 60,000 | | | +| 462 | RAIM Status | USHORT | | | | 0 if Good; Otherwise the ID of the most likely failed satellite | +| 463 | RAIM Horizontal Error | UINT | ft | | | | +| 464 | RAIM Vertical Error | UINT | ft | | | | +| 465 | ADS-B ES Airborne Position Latitude | FLOAT | ° | -90 to 90 | | Indexed by Aircraft | +| 466 | ADS-B ES Airborne Position Longitude | FLOAT | ° | -180 to 180 | | Indexed by Aircraft | +| 467 | ADS-B ES Airborne Position Altitude | DINT | ft | -1000 to 60,000 | | Indexed by Aircraft | +| 468 | ADS-B ES Surface Position Latitude | FLOAT | ° | -90 to 90 | | Indexed by Aircraft | +| 469 | ADS-B ES Surface Position Longitude | FLOAT | ° | -180 to 180 | | Indexed by Aircraft | +| 470 | ADS-B ES Surface Position Altitude | DINT | ft | -1000 to 60,000 | | Indexed by Aircraft | +| 471 | ADS-B ES Status | | | | | Indexed by Aircraft | +| 472 | ADS-B ES Identification | | | | | Indexed by Aircraft | +| 473 | ADS-B ES Type | | | | | Indexed by Aircraft | +| 474 | ADS-B ES Airborne Velocity | UINT | knots | 0 to 2000 | 0.1 | Indexed by Aircraft | +| 475 | ADS-B ES Airborne Bearing | UINT | ° | 0 to 359.9 | 0.1 | Indexed by Aircraft | +| 476 | ADS-B ES Airborne Rate of Climb | INT | ft/min | -30,000 to 30,000 | | Indexed by Aircraft | +| 477 | ADS-B ES Emergency Priority Status | | | | | Indexed by Aircraft; Event Driven Information | +| 478 | ADS-B ES Current Trajectory Change Point | | | | | Indexed by Aircraft; Event Driven Information | +| 479 | ADS-B ES Next Trajectory Change Point | | | | | Indexed by Aircraft; Event Driven Information | +| 480 | ADS-B ES Operation Coord. Message | | | | | Indexed by Aircraft; Event Driven Information | +| 481 | ADS-B ES Operational Status | | | | | Indexed by Aircraft; Event Driven Information | + +## High Priority Engine / Aircraft System Data (512-639) + +| ID | Parameter | Type | Units | Range | Mult | Notes | +|---|---|---|---|---|---|---| +| 512-513 | N1 or Engine RPM | UINT | RPM | | | N1 for Turbines | +| 514-515 | N2, Prop RPM or Rotor RPM | UINT | RPM | | | N2 for Turbines | +| 516-517 | Torque | INT | | | | | +| 518-519 | Turbine Inlet Temperature | UINT | °C | | 0.1 | | +| 520-521 | Inter-turbine Temperature | UINT | °C | | 0.1 | | +| 522-523 | Turbine Outlet Temperature | UINT | °C | | 0.1 | | +| 524-525 | Fuel Pressure Switch | SHORT | | | | 0 = Normal; -1 = Low; 1 = High | +| 526-527 | Oil Pressure Switch | SHORT | | | | 0 = Normal; -1 = Low; 1 = High | +| 528-529 | Oil Temperature Switch | SHORT | | | | 0 = Normal; -1 = Low; 1 = High | +| 530-531 | Coolant Temperature Switch | SHORT | | | | 0 = Normal; -1 = Low; 1 = High | +| 532-533 | Fuel Quantity Switch | SHORT | | | | 0 = Normal; -1 = Low; 1 = High | +| 534-535 | Oil Quantity Switch | SHORT | | | | 0 = Normal; -1 = Low; 1 = High | +| 536-537 | Coolant Quantity Switch | SHORT | | | | 0 = Normal; -1 = Low; 1 = High | +| 538-539 | Fuel Flow | UINT | gal/hr | | 0.01 | | +| 540-541 | Fuel Pressure | UINT | psi | | 0.01 | | +| 542-543 | Manifold Pressure | UINT | inHg | | 0.01 | | +| 544-545 | Oil Pressure | UINT | psi | | 0.01 | | +| 546-547 | Oil Temperature | UINT | °C | | 0.1 | | +| 548-549 | Coolant Temperature | UINT | °C | | 0.1 | | +| 550-553 | Fuel Quantity | UINT | gal | | 0.01 | Indexed by Aux Tank | +| 554-555 | Fuel Pump Pressure | UINT | psi | | 0.01 | | +| 556-557 | Oil Quantity | UINT | gal | | 0.01 | | +| 558-559 | Coolant Quantity | UINT | gal | | 0.01 | | +| 560-561 | Electric Propulsion Motor Current | UINT | A | | | | +| 562-563 | Main Propulsion Bus Voltage | UINT | V | | 0.1 | | +| 564-565 | Main Battery Current | INT | A | | | | +| 566-567 | Main Battery Temperature | UINT | °C | | 0.1 | | +| 568-569 | Main Battery Charge | UINT | % | 0 to 100 | 0.1 | | +| 570-571 | Hybrid System Status | WORD | | | | | +| 572-573 | Upper Deck Pressure | UINT | inHg | | 0.01 | | + +## High Priority Auxiliary Data (640-767) + +| ID | Parameter | Type | Units | Range | Mult | Notes | +|---|---|---|---|---|---|---| +| 640 | Cabin Pressure | UINT | inHg | 0 to 35 | 0.001 | | +| 641 | Cabin Altitude | INT | ft | -1,000 to 30,000 | | | + +## Normal Priority Pilot Control Inputs (768-895) + +| ID | Parameter | Type | Units | Range | Mult | Notes | +|---|---|---|---|---|---|---| +| 768-775 | Encoder Input | INT[2],BYTE | | | | Indexed by Unit; Steps Moved; X,Y and Switch Positions; Less than 0 = CCW, Greater than 0 = CW | +| 776-783 | Generic Switches | BYTE[5] | | | | Indexed by Unit; Discrete Bits; User Defined For Multiplexing Switches | +| 784 | Speedbrake Control Position | UINT | % | 0 to 100 | 0.01 | | +| 785 | Cowl Flaps Control Position | UINT | % | 0 to 100 | 0.01 | | +| 786 | Pitch Trim Control Position | INT | % | -100 to 100 | 0.01 | | +| 787 | Roll Trim Control Position | INT | % | -100 to 100 | 0.01 | | +| 788 | Yaw Trim Control Position | INT | % | -100 to 100 | 0.01 | | +| 789 | Collective Trim Control Position | INT | % | -100 to 100 | 0.01 | | +| 790 | Anti-Torque Pedals Trim Position | INT | % | -100 to 100 | 0.01 | | +| 791-798 | Generic Analog Control | UINT | % | 0 to 100 | 0.01 | Indexed by Unit; User Defined | + +## Normal Priority Measured Positions (896-1023) + +| ID | Parameter | Type | Units | Range | Mult | Notes | +|---|---|---|---|---|---|---| +| 896 | Speedbrake Position | INT | ° | -90 to 90 | 0.01 | Less than 0 = Down; Greater than 0 = Up | +| 897 | Cowl Flaps Position | UINT | % | 0 to 100 | 0.01 | 100% = Open | +| 898 | Pitch Trim Position | INT | ° | -90 to 90 | 0.01 | Less than 0 = Down; Greater than 0 = Up | +| 899 | Roll Trim Position | INT | ° | -90 to 90 | 0.01 | Less than 0 = Left; Greater than 0 = Right | +| 900 | Yaw Trim Position | INT | ° | -90 to 90 | 0.01 | Less than 0 = Left; Greater than 0 = Right | +| 901 | Pitch Trim Motor Speed | INT | % | -100 to 100 | 0.01 | Less than 0 = Down; Greater than 0 = Up | +| 902 | Roll Trim Motor Speed | INT | % | -100 to 100 | 0.01 | Less than 0 = Left; Greater than 0 = Right | +| 903 | Yaw Trim Motor Speed | INT | % | -100 to 100 | 0.01 | Less than 0 = Left; Greater than 0 = Right | +| 904 | Collective Trim Motor Speed | INT | % | -100 to 100 | 0.01 | Less than 0 = Down; Greater than 0 = Up | +| 905 | Anti-Torque Pedals Trim Motor Speed | INT | % | -100 to 100 | 0.01 | Less than 0 = Left; Greater than 0 = Right | +| 906 | Light Status | BYTE | | | | Discrete Bits | +| 907-910 | Fuel Pump Status | BYTE | | | | Discrete Bits | +| 911 | Fuel Valve Status | BYTE | | | | Discrete Bits | +| 912-919 | Generic Analog Measurement | UINT | % | 0 to 100 | 0.01 | Indexed by Unit; User Defined | + +## Normal Priority Flight Data (1024-1151) + +| ID | Parameter | Type | Units | Range | Mult | Notes | +|---|---|---|---|---|---|---| +| 1024 | Pitch Rate | INT | °/sec | -3000 to 3000 | 0.1 | Less than 0 = Down; Greater than 0 = Up | +| 1025 | Roll Rate | INT | °/sec | -3000 to 3000 | 0.1 | Less than 0 = Left; Greater than 0 = Right | +| 1026 | Yaw Rate | INT | °/sec | -3000 to 3000 | 0.1 | Less than 0 = Left; Greater than 0 = Right | +| 1027 | Turn Rate | INT | °/sec | -3000 to 3000 | 0.1 | Less than 0 = Left; Greater than 0 = Right | +| 1028 | Static Pressure | UINT | inHg | | 0.001 | | +| 1029 | Pitot Pressure | UINT | inHg | | 0.001 | | +| 1030 | Total Air Temperature | INT | °C | -300 to 300 | 0.01 | | +| 1031 | Static Air Temperature | INT | °C | -300 to 300 | 0.01 | | +| 1032 | Density Altitude | DINT | ft | -1,000 to 60,000 | | | +| 1033 | True Altitude | DINT | ft | -1,000 to 60,000 | | | +| 1034 | GPS Altitude | DINT | ft | -1,000 to 60,000 | | | +| 1035 | Wind Speed | UINT | knots | 0 to 2000 | 0.1 | | +| 1036 | Wind Direction | UINT | ° | 0 to 360 | 0.01 | Magnetic | + +## Normal Priority Navigation Data (1152-1279) + +| ID | Parameter | Type | Units | Range | Mult | Notes | +|---|---|---|---|---|---|---| +| 1152 | Next Waypoint Identifier | CHAR[5] | | | | | +| 1153 | Next Waypoint Latitude | FLOAT | ° | -90 to 90 | | | +| 1154 | Next Waypoint Longitude | FLOAT | ° | -180 to 180 | | | +| 1155 | Next Waypoint Altitude | DINT | ft | -1000 to 60,000 | | | +| 1156 | Next Waypoint ETA | USHORT[3] | UTC | | | Hour, Min, Sec | +| 1157 | Next Waypoint ETE | USHORT[3] | | | | Hour, Min, Sec | +| 1158 | Waypoint Identifier | CHAR[5] | | | | Indexed by Waypoint | +| 1159 | Waypoint Latitude | FLOAT | ° | -90 to 90 | | Indexed by Waypoint | +| 1160 | Waypoint Longitude | FLOAT | ° | -180 to 180 | | Indexed by Waypoint | +| 1161 | Waypoint Altitude | DINT | ft | -1000 to 60,000 | | Indexed by Waypoint | +| 1162 | Waypoint ETA | USHORT[3] | UTC | | | Indexed by Waypoint; Hour, Min, Sec | +| 1163 | Waypoint ETE | USHORT[3] | | | | Indexed by Waypoint; Hour, Min, Sec | +| 1164 | Waypoint, Distance To | UINT | nm | | | Indexed by Waypoint | +| 1165 | Waypoint Minimum Altitude | DINT | ft | -1000 to 60,000 | | Indexed by Waypoint | +| 1166 | Waypoint Minimum Flight Level | UINT | | | | Indexed by Waypoint | +| 1167 | Waypoint Minimum Radar Level | UINT | | | | Indexed by Waypoint | +| 1168 | Waypoint Maximum Altitude | DINT | ft | -1000 to 60,000 | | Indexed by Waypoint | +| 1169 | Waypoint Maximum Flight Level | UINT | | | | Indexed by Waypoint | +| 1170 | Waypoint Maximum Radar Level | UINT | | | | Indexed by Waypoint | +| 1171 | Waypoint Planned Altitude | DINT | ft | -1000 to 60,000 | | Indexed by Waypoint | +| 1172 | Waypoint Reserved | | | | | Indexed by Waypoint | +| 1173 | Destination Identifier | CHAR[5] | | | | | +| 1174 | Destination Latitude | FLOAT | ° | -90 to 90 | | | +| 1175 | Destination Longitude | FLOAT | ° | -180 to 180 | | | +| 1176 | Destination Altitude | DINT | ft | -1000 to 60,000 | | | +| 1177 | Destination ETA | USHORT[3] | UTC | | | Hour, Min, Sec | +| 1178 | Destination ETE | USHORT[3] | | | | Hour, Min, Sec | +| 1179 | Track Error Angle | | ° | | | | +| 1180-1215 | Reserved | | | | | | +| 1216-1219 | VHF Com Frequency | UINT | MHz | | 0.01 | Indexed by 0=Current, 1=Standby, >1 = Memory Locations | +| 1220-1223 | VOR/ILS Frequency | UINT | ° | 0 to 359 | | Indexed by 0=Current, 1=Standby, >1 = Memory Locations | +| 1224-1227 | VOR/ILS Identifier | CHAR[5] | | | | | +| 1228-1231 | Actual VOR Radial | UINT | ° | 0 to 359.9 | 0.1 | | +| 1232-1235 | Selected VOR Radial | UINT | ° | 0 to 359.9 | 0.1 | | +| 1236 | Transponder Code | USHORT[4] | | | | | +| 1240 | Auto Pilot / FD Mode | WORD | | | | Indexed by 0=AP Mode, 1=Horizontal Mode, 2=Vertical Mode, 255=Status | + +## Normal Priority Engine / Aircraft System Data (1280-1407) + +| ID | Parameter | Type | Units | Range | Mult | Notes | +|---|---|---|---|---|---|---| +| 1280-1281 | Cylinder Head Temperature | UINT | °C | | 0.1 | Indexed by Cylinder | +| 1282-1283 | Exhaust Gas Temperature | UINT | °C | | 0.1 | Indexed by Cylinder | +| 1284-1285 | Cylinder Head Temp. Rate of Change | UINT | °C/Min | | 0.1 | Indexed by Cylinder | +| 1286-1287 | Cylinder Head Temp. Deviation | UINT | °C | | 0.1 | | +| 1288-1289 | Exhaust Gas Temp. Rate of Change | UINT | °C/Min | | 0.1 | Indexed by Cylinder | +| 1290-1291 | Exhaust Gas Temp. Deviation | UINT | °C | | 0.1 | | +| 1292-1293 | Carburetor Temperature | UINT | °C | | 0.1 | | +| 1294-1297 | Electrical Bus Voltage | UINT | V | | 0.1 | | +| 1298-1301 | Electrical Bus Current | UINT | A | | 0.1 | | +| 1302-1305 | Generator / Alternator Voltage | UINT | V | | 0.1 | | +| 1306-1309 | Generator / Alternator Current | UINT | A | | 0.1 | | +| 1310-1311 | Engine Power | UINT | % | | 0.1 | | +| 1312-1313 | Total Engine Time | UINT | Hours | | 0.1 | Indexed by Flight; Index 0 = Total, 1 = last flight, reverse chronological order from there | +| 1314-1315 | Total Engine Time (Tach) | UINT | Hours | | 0.1 | Indexed by Flight; Index 0 = Total, 1 = last flight, reverse chronological order from there | +| 1316-1317 | Gearbox Speed | UINT | RPM | | | | +| 1318-1319 | Gearbox Oil Pressure Switch | BYTE | | | | 0 = Normal; -1 = Low; 1 = High | +| 1320-1321 | Gearbox Oil Temperature Switch | BYTE | | | | 0 = Normal; -1 = Low; 1 = High | +| 1322-1323 | Gearbox Oil Quantity Switch | BYTE | | | | 0 = Normal; -1 = Low; 1 = High | +| 1324-1325 | Hydraulic Pressure Switch | BYTE | | | | 0 = Normal; -1 = Low; 1 = High | +| 1326-1327 | Hydraulic Temperature Switch | BYTE | | | | 0 = Normal; -1 = Low; 1 = High | +| 1328-1329 | Hydraulic Fluid Quantity Switch | BYTE | | | | 0 = Normal; -1 = Low; 1 = High | +| 1330-1331 | Gearbox Oil Pressure | UINT | psi | | 0.01 | | +| 1332-1333 | Gearbox Oil Temperature | UINT | °C | | 0.1 | | +| 1334-1335 | Gearbox Oil Quantity | UINT | % | 0 to 100 | 0.01 | | +| 1336-1337 | Hydraulic Pressure | UINT | psi | | 0.01 | | +| 1338-1339 | Hydraulic Temperature | UINT | °C | | 0.1 | | +| 1340-1341 | Hydraulic Fluid Quantity | UINT | % | 0 to 100 | 0.01 | | +| 1342-1345 | Tire Pressure | UINT | psi | | 0.01 | | +| 1346-1349 | Strut Pressure | UINT | psi | | 0.01 | | +| 1350 | Flight Time | UINT | Hours | | 0.1 | Indexed by Flight; Index 0 = last flight, reverse chronological order from there | + +## Normal Priority Auxiliary Data (1408-1535) + +| ID | Parameter | Type | Units | Range | Mult | Notes | +|---|---|---|---|---|---|---| +| 1408 | Time | USHORT[3],UINT | UTC | | | Hour, Min, Sec, mSec | +| 1409 | Date | UINT,USHORT[2] | | | | Year, Month, Day | +| 1410 | Time Zone | SHORT | Hours | -12 to 12 | 0.1 | | +| 1411 | Cabin Temperature | UINT | °C | | 0.1 | | +| 1412 | Panel Dimmer Level | USHORT | % | 0 to 100 | | | +| 1413 | Longitudinal Center of Gravity | UINT | %MAC | 0 to 100 | 0.1 | | +| 1414 | Lateral Center of Gravity | INT | % | -100 to 100 | 0.1 | | +| 1415 | Aircraft Identifier | CHAR[5] | | | | | +| 1416 | Aircraft Type | CHAR[5] | | | | | + +--- + +_230 parameters across 12 groups. Generated by `tools/gen_param_reference.py` from `src/canfix.json` (version 0.7). Do not edit by hand -- edit the master spreadsheet and regenerate._ From f9c8a31580314aac090dd6d65276eaacd0a2fb57 Mon Sep 17 00:00:00 2001 From: Bill Mallard Date: Tue, 30 Jun 2026 11:48:49 -0500 Subject: [PATCH 6/7] README: link the online spec + wiki, mark CAN-FIX.ods as the master - Add a "Read the Specification Online" section pointing to the live GitHub Pages spec and the new wiki quick-references. - Clarify that canfix.json/xml and parameters.rst are generated from the master spreadsheet CAN-FIX.ods (not hand-edited), and add the regenerate step plus the GitHub Actions auto-publish note to the build instructions. Co-Authored-By: Claude Opus 4.8 (1M context) --- README.md | 30 +++++++++++++++++++++++++----- 1 file changed, 25 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 85d7851..92524fe 100644 --- a/README.md +++ b/README.md @@ -12,6 +12,12 @@ This repository contains the **authoritative specification** for the CAN-FIX com The specification is licensed under Creative Commons, meaning anyone can read, implement, modify, and distribute compliant devices without licensing fees or vendor lock-in. +## Read the Specification Online + +- **Full specification (web):** — the complete document, rebuilt automatically from this repository on every push. +- **Wiki (quick reference):** — a friendly companion with a [parameter quick-reference](https://github.com/billmallard/canfix-spec/wiki/Parameter-Reference) table, a [frame-format](https://github.com/billmallard/canfix-spec/wiki/Frame-Format) cheat-sheet, and an [implementer's quick-start](https://github.com/billmallard/canfix-spec/wiki/Implementing-a-Node). +- **PDF:** build it locally with `make latexpdf` (see [Building the Documentation](#building-the-documentation)). + ## What CAN-FIX Defines - **Parameter namespace:** Every flight data value (airspeed, altitude, heading, GPS position, engine temperatures, control surface positions, etc.) has a named identifier. Parameters are organized by type and support multiple simultaneous sources (e.g., two independent EGT sensors for two engines). @@ -39,14 +45,21 @@ The specification covers the full avionics parameter set for a modern aircraft: | Path | Description | |---|---| -| `src/canfix.json` | Machine-readable parameter definitions (primary reference) | -| `src/canfix.xml` | XML form of parameter definitions | -| `src/parameters.rst` | Human-readable parameter documentation source | +| `src/CAN-FIX.ods` | **Master parameter spreadsheet** — the source of truth for all parameter definitions | +| `src/canfix.json` | Machine-readable parameter definitions (*generated* from `CAN-FIX.ods`) | +| `src/canfix.xml` | XML form of parameter definitions (*generated* from `CAN-FIX.ods`) | +| `src/parameters.rst` | Human-readable parameter list (*generated* from `CAN-FIX.ods`) | | `src/framedef.rst` | CAN frame format specification | | `src/datatypes.rst` | Data type definitions | | `src/physical.rst` | Physical layer requirements (wiring, termination, bit rate) | | `src/requirements.rst` | Protocol requirements | | `src/practices.rst` | Recommended implementation practices | +| `tools/gen_param_reference.py` | Generates the wiki parameter quick-reference from `canfix.json` | +| `tools/sync_wiki.sh` | Mirrors `wiki/` to the GitHub wiki clone for publishing | + +> **Editing parameters:** change `src/CAN-FIX.ods` (the master), then regenerate +> the derived files with the commands below. Do not hand-edit `canfix.json`, +> `canfix.xml`, or `parameters.rst` — they are overwritten on every build. ## Building the Documentation @@ -56,10 +69,17 @@ Requires Python with Sphinx and supporting packages: sudo pip install pyexcel pyexcel-ods sphinx sudo apt install texlive-latex-base texlive-fonts-recommended texlive-fonts-extra texlive-latex-extra texlive-xetex latexmk cd src -make latexpdf # builds PDF -make html # builds HTML +python canfix_json.py # regenerate canfix.json from CAN-FIX.ods +python canfix_xml.py # regenerate canfix.xml from CAN-FIX.ods +python parameters.py # regenerate parameter_list from CAN-FIX.ods +make latexpdf # builds PDF -> src/_build/latex +make html # builds HTML -> src/_build/html ``` +The web specification at is built +and published automatically by GitHub Actions (`.github/workflows/docs.yml`) on +every push to `master`, so the online copy always matches the repository. + ## Important Disclaimer > This specification is developed for Experimental Amateur-Built aircraft use only. From ce37953f1397d072a683ceb2a62269d41e395a97 Mon Sep 17 00:00:00 2001 From: Bill Mallard Date: Sun, 5 Jul 2026 09:25:14 -0500 Subject: [PATCH 7/7] wiki: Background page -- CANaerospace history and why CAN-FiX exists Researched 2026-07-05: Stock Flight Systems (1998), NASA AGATE (2001), Rotax iS deployment, ARINC 825 lineage, current status (spec frozen at V1.7/2006), and the 2013 Birkelbach 'What's wrong with CANaerospace?' forum thread quoted as the primary source. Mirrors pyEfis/docs/canaerospace_background.md. Also force-add wiki/_Sidebar.md + _Footer.md: the Sphinx '_*' ignore was silently excluding them from the canonical wiki source that tools/sync_wiki.sh mirrors. Co-Authored-By: Claude Fable 5 Signed-off-by: Bill Mallard --- wiki/Background-CANaerospace.md | 111 ++++++++++++++++++++++++++++++++ wiki/_Footer.md | 4 ++ wiki/_Sidebar.md | 20 ++++++ 3 files changed, 135 insertions(+) create mode 100644 wiki/Background-CANaerospace.md create mode 100644 wiki/_Footer.md create mode 100644 wiki/_Sidebar.md diff --git a/wiki/Background-CANaerospace.md b/wiki/Background-CANaerospace.md new file mode 100644 index 0000000..6da6180 --- /dev/null +++ b/wiki/Background-CANaerospace.md @@ -0,0 +1,111 @@ +# Background: CANaerospace and why CAN-FiX exists + +CAN-FiX was born from Phil Birkelbach's frustrations with +**CANaerospace**, the 1998 aviation CAN protocol it most resembles. +This page gives the history: what CANaerospace is, where it went, and +what CAN-FiX deliberately did differently. (Researched 2026-07-05; +sources at the bottom.) + +## What CANaerospace is + +CANaerospace is an application-layer protocol over CAN, created in +**1998 by Michael Stock** of Stock Flight Systems — an aerospace +engineering firm in Farchach (Berg, Bavaria) specialized in flight-test +instrumentation. Design highlights: + +- Big-endian, **self-identifying messages**: 8-byte payload = 4-byte + header (Node-ID, data type, service code, message code) + 4 bytes of + data — half of every frame is metadata. +- Priority-ordered logical channels (emergency events, node services, + normal operation data, user-defined, debug) over a standard + identifier distribution (IDs 300–1799). +- The same parameter may be sent with **different data types**, and + **alternative identifier distributions may coexist** on one bus. +- Free to download and use — genuinely open for its era. + +The flexibility was deliberate: in a flight-test lab you want to put +arbitrary new parameters on the bus quickly. + +## Its pedigree + +- **NASA AGATE (2001):** published by NASA as the databus standard of + the Advanced General Aviation Transport Experiments consortium, which + carried it into research aircraft, simulators and UAVs worldwide. +- **Rotax iS engines (2012–present):** the Rotax 912iS/915iS ECUs speak + CANaerospace — it ships in essentially every new injected-Rotax + installation today. +- **ARINC 825 (2007–present):** an AEEC working group (Airbus, Boeing, + GE, Rockwell Collins, Vector, and Stock Flight Systems itself) used + CANaerospace as the basis for ARINC 825, the certified-aviation CAN + standard — still actively revised, with CAN FD in later revisions. + +## Current status: frozen, not failed + +The open spec's last revision is **V1.7 (2006)** — two decades +untouched, with no active steward. stockflightsystems.com survives as a +legacy brochure site; the commercial products (Rotax iS EMU, MT +propeller control, CANaerospace loggers) are sold today by RS Flight +Systems GmbH of Berg — likely a Reiser/Stock venture carrying the +product line forward (probable, not confirmed). The certified world +absorbed CANaerospace into ARINC 825; the protocol itself keeps flying +inside Rotax ECUs. + +## Why CAN-FiX — Birkelbach, August 2013 + +The primary source is the MakerPlane forum thread +[*"What's wrong with CANaerospace?"*](http://makerplane.org/forum/viewtopic.php?t=216): + +> "With it you can send the same parameter over the bus with different +> data types. This is a great feature if what you are after is +> flexibility. **The problem with flexibility in a communication +> protocol is that it forces complexity at the end points.**" + +> "CA also allows for multiple identifier distributions to exist on the +> bus at the same time. It would be up to the end points to determine +> if the data that they are receiving is what they think it is." + +> "CAN-FIX is a much more rigid protocol… **There is only one way to +> send airspeed on CAN-FIX.**" + +Concretely, CAN-FiX: + +- replaced the data-type byte with an **index byte** — up to 256 + instances of a parameter (all your EGTs) instead of type negotiation + (see [Frame Format](Frame-Format)); +- pinned **one canonical encoding per parameter** (see + [Data Types](Data-Types)), so a $2 microcontroller needs no + type-handling layer; +- put **metadata on the bus** (V-speeds, ranges, alarm setpoints); +- targeted the Experimental/Amateur-Built ecosystem under a Creative + Commons license. + +A fair framing: CANaerospace optimizes for the flight-test lab +(flexibility first); CAN-FiX optimizes for a fixed avionics ecosystem +of heterogeneous cheap nodes (rigidity first). Both are rational in +their own domains. + +## Family tree + +``` +Bosch CAN (1986, automotive) + ├─ CANopen / DeviceNet (industrial) + ├─ NMEA 2000 (marine) + └─ CANaerospace (1998, GA / research / flight test) + ├─ ARINC 825 (2007, certified transport; CAN FD later) + ├─ UAVCAN (2014) ─┬─ DroneCAN (2022) + │ └─ Cyphal (2022) + └─ CAN-FiX (~2012, MakerPlane — experimental aviation) +``` + +## Sources + +- [Wikipedia — CANaerospace](https://en.wikipedia.org/wiki/CANaerospace) +- [MakerPlane forum — "What's wrong with CANaerospace?" (Aug 2013)](http://makerplane.org/forum/viewtopic.php?t=216) +- [CAN-FiX Overview (makerplane.org)](https://makerplane.org/can-fix-overview/) +- [CANaerospace V1.7 specification (PDF)](https://www.stockflightsystems.com/tl_files/downloads/canaerospace/canas_17.pdf) +- [Stock Flight Systems — ARINC 825 presentation (PDF)](https://files.stockflightsystems.com/_5_Arinc_825/ARINC825_Presentation.pdf) +- [arinc-825.com — The ARINC825 Standard](https://www.arinc-825.com/the-arinc825-standard/) +- [Vector application note — CAN-based protocols in Avionics (PDF)](https://cdn.vector.com/cms/content/know-how/_application-notes/canopen/AN-ION-1-0104_CAN-based_protocols_in_Avionics.pdf) +- [RS Flight Systems](https://www.rs-flightsystems.com/) +- [Wikipedia — Cyphal](https://en.wikipedia.org/wiki/Cyphal) +- [Zubax — Cyphal vs DroneCAN](https://zubax.com/blog/on-the-key-differences-between-cyphal-and-dronecan-formerly-uavcan/2038) diff --git a/wiki/_Footer.md b/wiki/_Footer.md new file mode 100644 index 0000000..a44acbc --- /dev/null +++ b/wiki/_Footer.md @@ -0,0 +1,4 @@ +[CAN-FiX specification](https://billmallard.github.io/canfix-spec/) · +[Repository](https://github.com/billmallard/canfix-spec) · +Wiki quick-references are generated/curated from `src/CAN-FIX.ods` -- the +specification is authoritative. diff --git a/wiki/_Sidebar.md b/wiki/_Sidebar.md new file mode 100644 index 0000000..11458c0 --- /dev/null +++ b/wiki/_Sidebar.md @@ -0,0 +1,20 @@ +### CAN-FiX Wiki + +- [Home](Home) +- [Parameter Quick-Reference](Parameter-Reference) +- [Frame Format](Frame-Format) +- [Data Types](Data-Types) +- [Implementing a Node](Implementing-a-Node) +- [Background: CANaerospace](Background-CANaerospace) + +### Full specification + +- [Specification (web)](https://billmallard.github.io/canfix-spec/) +- [Introduction](https://billmallard.github.io/canfix-spec/intro.html) +- [Frame Definitions](https://billmallard.github.io/canfix-spec/framedef.html) +- [Parameters](https://billmallard.github.io/canfix-spec/parameters.html) +- [License](https://billmallard.github.io/canfix-spec/license.html) + +### Project + +- [Repository](https://github.com/billmallard/canfix-spec)