{"id":51595203,"url":"https://github.com/hyperized/modes","last_synced_at":"2026-07-11T18:01:45.815Z","repository":{"id":355733867,"uuid":"1229366316","full_name":"hyperized/modes","owner":"hyperized","description":"Pure Go decoder for ICAO Annex 10 Vol IV / Mode S + RTCA DO-260B / ADS-B Extended Squitter.","archived":false,"fork":false,"pushed_at":"2026-05-21T11:44:28.000Z","size":160,"stargazers_count":0,"open_issues_count":2,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-05-21T19:58:15.830Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"Go","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"other","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/hyperized.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-05-05T01:09:57.000Z","updated_at":"2026-05-21T11:39:51.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/hyperized/modes","commit_stats":null,"previous_names":["hyperized/modes"],"tags_count":1,"template":false,"template_full_name":null,"purl":"pkg:github/hyperized/modes","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/hyperized%2Fmodes","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/hyperized%2Fmodes/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/hyperized%2Fmodes/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/hyperized%2Fmodes/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/hyperized","download_url":"https://codeload.github.com/hyperized/modes/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/hyperized%2Fmodes/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":35370428,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-07-11T02:00:05.354Z","response_time":104,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2026-07-11T18:01:44.968Z","updated_at":"2026-07-11T18:01:45.808Z","avatar_url":"https://github.com/hyperized.png","language":"Go","funding_links":[],"categories":[],"sub_categories":[],"readme":"# modes\n\n[![Go Reference](https://pkg.go.dev/badge/github.com/hyperized/modes.svg)](https://pkg.go.dev/github.com/hyperized/modes)\n\nA pure-Go implementation of **ICAO Annex 10 Volume IV** (the Mode S transponder downlink protocol) and **RTCA DO-260B** (its ADS-B Extended Squitter overlay). Decodes validated Mode S frames into typed messages — aircraft identification, airborne and surface position via Compact Position Reporting (CPR), velocity, surveillance altitude / identity, ACAS coordination, Comm-A / Comm-B / Comm-D ELM channels.\n\nSpec-faithful, allocation-free on the hot path, zero third-party dependencies. Any source of CRC-validated `[]byte` Mode S frames works the same.\n\n## Status\n\nWorking coverage of every Downlink Format defined in the spec. Tests: 100 % statement coverage; lint clean against `golangci-lint v2.12` (`default: all`, `revive` enable-all-rules at line-length 120).\n\nThe decoded surface is broad rather than deep — every DF and most ME Type Codes have a typed decoded message; the long tail of subtype-specific sub-decoders (TC 29 selected-altitude sub-decoding, TC 31 airborne / surface sub-fields, the dozen-plus Comm-B BDS registers beyond BDS 2,0) is filled in commit-by-commit as downstream consumers ask for them. Real-frame regression vectors land alongside as captured ADS-B replay data becomes available.\n\n## Coverage\n\n| Downlink Format  | Decoder                            | Notes |\n|------------------|------------------------------------|-------|\n| DF 0             | `DecodeACASShortReply`             | Air-to-air ACAS short |\n| DF 4             | `DecodeSurveillanceAltitude`       | Altitude reply |\n| DF 5             | `DecodeSurveillanceIdentity`       | Identity / squawk reply |\n| DF 11            | `DecodeAllCallReply`               | All-call reply (II 0..15) |\n| DF 16            | `DecodeACASLongReply`              | Air-to-air ACAS long; MV exposed raw |\n| DF 17 / 18       | `DecodeExtendedSquitter`           | ADS-B Extended Squitter; ME dispatched per TC |\n| DF 20 / 21       | `DecodeCommBAltitude` / `Identity` | Comm-B; MB exposed raw, BDS 2,0 callsign decoded |\n| DF 24..31        | `DecodeCommDExtendedLength`        | Comm-D ELM segment; reassembly is the caller's job |\n\nME Type Codes (DF 17 / 18):\n\n| TC range | Message                       | Coverage |\n|----------|-------------------------------|----------|\n| 1..4     | Aircraft Identification       | full (callsign + emitter category set) |\n| 5..8     | Surface Position              | full structure (movement / heading / CPR); resolve CPR via `DecodeCPRGlobal` or `DecodeCPRLocal` |\n| 9..18    | Airborne Position (barometric)| full structure; same CPR resolution helpers |\n| 19       | Airborne Velocity             | subtypes 1 (subsonic ground-speed) and 2 (supersonic ground-speed, ×4 multiplier) full; subtypes 3/4 (airspeed) structural fields only |\n| 20..22   | Airborne Position (GNSS)      | structure; per-subtype altitude lands as a follow-up |\n| 28       | Aircraft Status               | subtype 1 (Emergency / Priority Status) full; subtype 2 (TCAS RA) raw |\n| 29       | Target State and Status       | structural (subtype + raw) |\n| 31       | Aircraft Operational Status   | structural (subtype + raw) |\n\n## Quickstart\n\n```go\npackage main\n\nimport (\n\t\"fmt\"\n\n\t\"github.com/hyperized/modes\"\n)\n\nfunc main() {\n\tframe := modes.Frame{ /* 7 or 14 bytes from your demodulator */ }\n\n\tswitch frame.DF() {\n\tcase modes.DFExtendedSquitter, modes.DFNonTransponderES:\n\t\tsquitter, err := modes.DecodeExtendedSquitter(frame)\n\t\tif err != nil {\n\t\t\tfmt.Println(\"decode:\", err)\n\n\t\t\treturn\n\t\t}\n\n\t\tswitch msg := squitter.Message.(type) {\n\t\tcase modes.IdentificationMessage:\n\t\t\tfmt.Printf(\"ICAO=%06X callsign=%s category=%c%d\\n\",\n\t\t\t\tuint32(squitter.ICAO), msg.Callsign, msg.CategorySet, msg.EmitterCategory)\n\t\tcase modes.AirbornePositionMessage:\n\t\t\tfmt.Printf(\"ICAO=%06X alt=%dft cpr=(%d, %d, %v)\\n\",\n\t\t\t\tuint32(squitter.ICAO), msg.AltitudeFeet,\n\t\t\t\tmsg.CPR.Latitude, msg.CPR.Longitude, msg.CPR.Format)\n\t\tcase modes.AirborneVelocityMessage:\n\t\t\tif msg.GroundSpeedAvailable {\n\t\t\t\tfmt.Printf(\"ICAO=%06X gs=%.0fkt track=%.0f° vr=%dft/min\\n\",\n\t\t\t\t\tuint32(squitter.ICAO), msg.GroundSpeedKnots,\n\t\t\t\t\tmsg.TrackDegrees, msg.VerticalRateFeetMin)\n\t\t\t}\n\t\t}\n\t}\n}\n```\n\n## Architecture\n\nThe public surface is two layers:\n\n```\n                       Frame ([]byte)\n                          │\n         ┌────────────────┴───────────────────┐\n         ▼                                    ▼\n   per-DF decoders                   standalone helpers\n   (DecodeAllCallReply,              (CRC24, AltitudeFeet,\n   DecodeSurveillanceAlt,             SquawkFromIdentityCode,\n   DecodeACASShortReply,              DecodeCPRGlobal,\n   DecodeExtendedSquitter,            DecodeCPRLocal,\n   DecodeCommBAltitude, …)            DecodeBDS20Callsign)\n         │\n         ▼  (DF 17/18 only)\n   decodeMEPayload\n   (TC dispatch)\n         │\n         ▼\n   Message interface\n   (IdentificationMessage,\n    AirbornePositionMessage,\n    AirborneVelocityMessage,\n    SurfacePositionMessage,\n    AircraftStatusMessage,\n    TargetStateMessage,\n    OperationalStatusMessage)\n```\n\nThe `Message` interface is sealed (callers can't add their own implementations), so type-switching on `squitter.Message` is exhaustive against the documented set. Adding a new ME Type Code is one new file — declare a struct, satisfy `isModesMessage()`, extend the dispatch chain. No public API breakage.\n\n## Errors\n\nStatic sentinels for every recoverable failure mode; branch with `errors.Is`:\n\n| Sentinel                          | Meaning |\n|-----------------------------------|---------|\n| `ErrFrameTooShort`                | Frame buffer is shorter than the DF's wire length. |\n| `ErrWrongDF`                      | Decoder was handed a frame whose DF doesn't match. |\n| `ErrUnsupportedTypeCode`          | ES dispatcher saw a TC without a registered decoder. Structural fields still populated. |\n| `ErrCPRZoneCrossing`              | Globally-unambiguous CPR pair straddles an NL boundary; discard the older frame and retry. |\n| `ErrAltitudeMSet`                 | 13-bit altitude field has M=1 (metric encoding); spec leaves the value to-be-defined. |\n| `ErrGillhamUnsupported`           | Q=0 Gillham/Gray-coded altitude path; lookup table lands as a follow-up. |\n| `ErrGNSSAltitudeUnsupported`      | TC 20..22 GNSS-altitude per-subtype decoding lands as a follow-up. |\n\n## CLI — `modes-decode`\n\nA small reference binary that pipes one hex frame per line through the library and prints one decoded line per frame. Designed to compose with `demod1090` output.\n\n```sh\ngo install github.com/hyperized/modes/cmd/modes-decode@latest\n\ndemod1090 | modes-decode\n# or replay a capture:\nrtl-probe -capture cap.iq \u0026\u0026 demod1090 -replay-iq cap.iq | modes-decode\n```\n\nInput lines are case-insensitive hex with an optional `0x` prefix; blank lines and lines starting with `#` are skipped. Decoding errors go to stderr along with a final `modes-decode: decoded=N errors=N` summary so a wrapping shell can count outcomes. Decoded frames go to stdout, e.g.\n\n```\n4840D6 df=17 tc=4 ident callsign=KLM1023 set=A category=0\n40621D df=4 surv-alt alt=38000ft fs=0\n```\n\nExit codes:\n\n| Code | Meaning |\n|------|---------|\n| 0    | Clean shutdown. Per-line decode failures count on stderr but don't change the exit. |\n| 1    | Scanner / IO failure on stdin. |\n| 2    | Bad CLI flag. |\n\n**Caveat — `--crc-residual`.** Mode S DFs 0/4/5/11/16/20/21 overlay parity with the addressed aircraft's ICAO; the receiver recovers it from the CRC residual at validation time. `--crc-residual` is the only way to pass that context to `modes-decode`, but the flag is global and applies to every frame in the stream. It's only meaningful when the input carries a single ICAO's traffic; for mixed-ICAO streams, accept the ICAO=0 default (the rest of the decoded fields are still correct) or pre-split the input by ICAO. DF 17 / DF 18 carry the broadcasting AA in the message body and never need this flag.\n\nRun `modes-decode -h` for the full reference.\n\n## Build \u0026 test\n\n```sh\nmake           # fmt + vet + test (race + cover)\nmake lint      # golangci-lint run ./...\nmake cover     # produces coverage.html\n```\n\n## License\n\nBusiness Source License 1.1. See `LICENSE`. Free for non-commercial use; commercial integration requires a paid license. Converts to Apache-2.0 on the change date (2036-05-05).\n\n## References\n\n- **ICAO Annex 10 Vol IV** — paywalled; [Junzi Sun's \"ADS-B Decoding Guide\"](https://mode-s.org/) (free PDF) is the practical reference.\n- **RTCA DO-260B** — paywalled; same set of free references covers ADS-B specifics.\n- [`flightaware/dump1090`](https://github.com/flightaware/dump1090) and [`wiedehopf/readsb`](https://github.com/wiedehopf/readsb) — GPL-2 reference implementations. Read for understanding; do not copy.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fhyperized%2Fmodes","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fhyperized%2Fmodes","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fhyperized%2Fmodes/lists"}