@aeroswarm/codec 0.0.0-stage → 0.1.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (85) hide show
  1. package/README.md +157 -3
  2. package/dist/index.d.ts +2 -0
  3. package/dist/index.d.ts.map +1 -0
  4. package/dist/index.js +924 -0
  5. package/dist/surveillance/adsb/index.d.ts +16 -0
  6. package/dist/surveillance/adsb/index.d.ts.map +1 -0
  7. package/dist/surveillance/ais/index.d.ts +17 -0
  8. package/dist/surveillance/ais/index.d.ts.map +1 -0
  9. package/dist/surveillance/asmgcs/index.d.ts +14 -0
  10. package/dist/surveillance/asmgcs/index.d.ts.map +1 -0
  11. package/dist/surveillance/asterix/cat10/index.d.ts +3 -0
  12. package/dist/surveillance/asterix/cat10/index.d.ts.map +1 -0
  13. package/dist/surveillance/asterix/cat21/index.d.ts +5 -0
  14. package/dist/surveillance/asterix/cat21/index.d.ts.map +1 -0
  15. package/dist/surveillance/asterix/cat48/index.d.ts +5 -0
  16. package/dist/surveillance/asterix/cat48/index.d.ts.map +1 -0
  17. package/dist/surveillance/asterix/cat62/index.d.ts +3 -0
  18. package/dist/surveillance/asterix/cat62/index.d.ts.map +1 -0
  19. package/dist/surveillance/asterix/decoder.d.ts +14 -0
  20. package/dist/surveillance/asterix/decoder.d.ts.map +1 -0
  21. package/dist/surveillance/asterix/fields.d.ts +4 -0
  22. package/dist/surveillance/asterix/fields.d.ts.map +1 -0
  23. package/dist/surveillance/asterix/index.d.ts +5 -0
  24. package/dist/surveillance/asterix/index.d.ts.map +1 -0
  25. package/dist/surveillance/asterix/parser.d.ts +16 -0
  26. package/dist/surveillance/asterix/parser.d.ts.map +1 -0
  27. package/dist/surveillance/asterix/parser.test.d.ts +2 -0
  28. package/dist/surveillance/asterix/parser.test.d.ts.map +1 -0
  29. package/dist/surveillance/asterix/resolvers.d.ts +17 -0
  30. package/dist/surveillance/asterix/resolvers.d.ts.map +1 -0
  31. package/dist/surveillance/asterix/types.d.ts +50 -0
  32. package/dist/surveillance/asterix/types.d.ts.map +1 -0
  33. package/dist/surveillance/asterix/values.d.ts +10 -0
  34. package/dist/surveillance/asterix/values.d.ts.map +1 -0
  35. package/dist/surveillance/asterix/xml-loader.d.ts +46 -0
  36. package/dist/surveillance/asterix/xml-loader.d.ts.map +1 -0
  37. package/dist/surveillance/index.d.ts +6 -0
  38. package/dist/surveillance/index.d.ts.map +1 -0
  39. package/dist/surveillance/mlat/index.d.ts +16 -0
  40. package/dist/surveillance/mlat/index.d.ts.map +1 -0
  41. package/package.json +38 -4
  42. package/src/config/asterix.dtd +78 -0
  43. package/src/config/asterix.ini +17 -0
  44. package/src/config/asterix_bds.xml +462 -0
  45. package/src/config/asterix_cat001_1_1.xml +872 -0
  46. package/src/config/asterix_cat001_1_2.xml +836 -0
  47. package/src/config/asterix_cat002_1_0.xml +258 -0
  48. package/src/config/asterix_cat004_1_12.xml +1713 -0
  49. package/src/config/asterix_cat008_1_0.xml +353 -0
  50. package/src/config/asterix_cat010_1_1.xml +791 -0
  51. package/src/config/asterix_cat011_1_2.xml +1691 -0
  52. package/src/config/asterix_cat019_1_2.xml +293 -0
  53. package/src/config/asterix_cat020_1_7.xml +1256 -0
  54. package/src/config/asterix_cat021_0_26.xml +902 -0
  55. package/src/config/asterix_cat021_1_4.xml +1594 -0
  56. package/src/config/asterix_cat021_1_8.xml +1594 -0
  57. package/src/config/asterix_cat021_2_4.xml +2191 -0
  58. package/src/config/asterix_cat023_1_2.xml +357 -0
  59. package/src/config/asterix_cat025_1_2.xml +306 -0
  60. package/src/config/asterix_cat030_6_2.xml +1387 -0
  61. package/src/config/asterix_cat031_6_2.xml +214 -0
  62. package/src/config/asterix_cat032_6_2.xml +465 -0
  63. package/src/config/asterix_cat032_7_0.xml +702 -0
  64. package/src/config/asterix_cat034_1_26.xml +586 -0
  65. package/src/config/asterix_cat034_1_27.xml +683 -0
  66. package/src/config/asterix_cat048_1_14.xml +1142 -0
  67. package/src/config/asterix_cat048_1_21.xml +1241 -0
  68. package/src/config/asterix_cat062_0_17.xml +1442 -0
  69. package/src/config/asterix_cat062_1_16.xml +3031 -0
  70. package/src/config/asterix_cat062_1_18.xml +3303 -0
  71. package/src/config/asterix_cat062_1_7.xml +2817 -0
  72. package/src/config/asterix_cat062_1_9.xml +737 -0
  73. package/src/config/asterix_cat063_1_3.xml +298 -0
  74. package/src/config/asterix_cat065_1_2.xml +168 -0
  75. package/src/config/asterix_cat065_1_3.xml +183 -0
  76. package/src/config/asterix_cat094_0_1.xml +1103 -0
  77. package/src/config/asterix_cat205_1_0.xml +385 -0
  78. package/src/config/asterix_cat240_1_3.xml +289 -0
  79. package/src/config/asterix_cat242_1_0.xml +644 -0
  80. package/src/config/asterix_cat247_1_2.xml +92 -0
  81. package/src/config/asterix_cat252_6_1.xml +1826 -0
  82. package/src/config/asterix_cat252_6_2.xml +1831 -0
  83. package/src/config/asterix_cat252_7_0.xml +1867 -0
  84. package/src/config/eurocontroll_version/asterix-spec.xsd +343 -0
  85. package/src/config/eurocontroll_version/asterix_cat094.xml +457 -0
package/README.md CHANGED
@@ -1,3 +1,157 @@
1
- # Temporary Holding Version
2
-
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
1
+ # @aeroswarm/codec
2
+
3
+ Decoding library for aviation surveillance protocols. Converts raw wire-format data (ASTERIX UDP datagrams) into structured records that the rest of the AeroSwarm pipeline normalizes into track events.
4
+
5
+ **Node-only.** The decoder reads its XML format definitions from disk at runtime — never import this package from browser code. Frontends consume the already-normalized events via `@aeroswarm/client`.
6
+
7
+ ## Installation
8
+
9
+ ```bash
10
+ pnpm add @aeroswarm/codec
11
+ ```
12
+
13
+ ## Quick start
14
+
15
+ ```ts
16
+ import { parseAsterix, resolveAsterixPosition, resolveAsterixAltitudeFt } from "@aeroswarm/codec";
17
+
18
+ socket.on("message", (buf: Buffer) => {
19
+ // A single UDP datagram may contain multiple concatenated ASTERIX blocks —
20
+ // parseAsterix handles that and returns one record per block.
21
+ const records = parseAsterix(buf);
22
+
23
+ for (const rec of records) {
24
+ // rec.category → 21, 23, 48, …
25
+ // rec.edition → which edition actually decoded it, e.g. "0.26"
26
+ // rec.fields → decoded values (icao24, lat/lon, callsign, …)
27
+ const pos = resolveAsterixPosition(rec.fields, radarSite);
28
+ const altFt = resolveAsterixAltitudeFt(rec.fields);
29
+ }
30
+ });
31
+ ```
32
+
33
+ ### Forcing a specific edition
34
+
35
+ Auto-detection tries every known edition per category and keeps the **richest plausible decode** (a wrong-edition UAP can still "successfully" decode with fields landing in the wrong slots, so first-match is not safe). When you know your sensor's edition, pin it — deterministic and one decode per packet instead of several:
36
+
37
+ ```ts
38
+ // e.g. the kantor radar transmits CAT21 edition 0.26
39
+ const records = parseAsterix(buf, { 21: "0.26" });
40
+ ```
41
+
42
+ In the services this is wired to the `ASTERIX_EDITIONS` env var (format `"21:0.26,48:1.21"`).
43
+
44
+ ## Supported categories & editions
45
+
46
+ | Category | Content | Editions (newest first) |
47
+ |----------|---------|--------------------------|
48
+ | CAT010 | A-SMGCS monosensor surface movement (aircraft + vehicles) | 1.1 |
49
+ | CAT011 | A-SMGCS fused surface movement | 1.2 |
50
+ | CAT020 | Multilateration (MLAT) target reports | 1.7 |
51
+ | CAT021 | ADS-B target reports | 2.4, 1.8, 1.4, 0.26 |
52
+ | CAT023 | CNS/ATM ground station & service status | 1.2 |
53
+ | CAT034 | Radar service messages (north marker, sector crossing) — sensor id only | 1.27, 1.26 |
54
+ | CAT048 | Monoradar (PSR/SSR) target reports | 1.21, 1.14 |
55
+ | CAT062 | System/fused tracks from ATC (keyed by system track number; identity item 380 is compound and not yet unpacked) | 1.18, 1.16, 1.9, 1.7, 0.17 |
56
+ | CAT063 | Sensor status — sensor id only | 1.3 |
57
+
58
+ Definitions are standard `asterix` XML files in [`src/config/`](src/config/). More category XMLs are already present there (CAT001, 002, 004, …) but not yet registered — see "Adding a category" below.
59
+
60
+ ## Exposed API
61
+
62
+ ### `parseAsterix(buf, defaultEdition?)`
63
+
64
+ ```ts
65
+ function parseAsterix(
66
+ buf: Buffer,
67
+ defaultEdition?: Record<number, string>, // category → edition, skips auto-detect
68
+ ): AsterixRecord[]
69
+ ```
70
+
71
+ Parses one UDP datagram (possibly multiple concatenated blocks) into records. Never throws on malformed input — bad blocks are skipped and an empty array is a valid result. Records failing the plausibility check (lat/lon out of range) are dropped.
72
+
73
+ ### `resolveAsterixPosition(fields, radarSite?)`
74
+
75
+ Returns `{ lat, lon } | null`. Uses WGS-84 position when present; for radar categories reporting range/azimuth (`measuredPosition`) or cartesian offsets (`positionCartesian`), converts relative to `radarSite`.
76
+
77
+ ### `resolveAsterixAltitudeFt(fields)`
78
+
79
+ Returns the best available altitude in feet (`geometric → flight level` preference) or `undefined`.
80
+
81
+ ### Types
82
+
83
+ ```ts
84
+ interface AsterixRecord { category: number; edition: string; fields: AsterixFields; }
85
+
86
+ interface AsterixFields {
87
+ icao24?: string; // 6-char hex aircraft address
88
+ callsign?: string; // e.g. "MAS179"
89
+ squawk?: string; // 4-digit octal
90
+ lat?: number; lon?: number; // WGS-84 degrees
91
+ altitudeFt?: number;
92
+ timeOfDay?: number; // seconds since UTC midnight
93
+ velocity?: { groundSpeedKt?: number; headingDeg?: number };
94
+ positionCartesian?: { xNm?: number; yNm?: number };
95
+ measuredPosition?: { rangeNm?: number; azimuthDeg?: number };
96
+ dataSource?: { sac: number; sic: number };
97
+ // CAT23 ground-station status
98
+ reportType?: number; // 1 = ground station status
99
+ gsStatus?: { nogo: boolean; odp: boolean; oxt: boolean; msc: boolean; tsv: boolean; spo: boolean };
100
+ operationalRangeNm?: number;
101
+ }
102
+
103
+ interface RadarSite { lat: number; lon: number; }
104
+ ```
105
+
106
+ ### Per-category subpath re-exports
107
+
108
+ `cat10/`, `cat21/`, `cat48/`, `cat62/` re-export the same parser API for callers that want category-scoped imports.
109
+
110
+ ### Other protocol entry points (stubs)
111
+
112
+ `decodeAdsbFrame`, `decodeAis`, `decodeMlatFrame`, `decodeAsmgcsFrame` exist as placeholders and currently return `null`. Raw ADS-B/AIS decoding is not implemented — ADS-B arrives via ASTERIX CAT21.
113
+
114
+ ## How decoding works (architecture)
115
+
116
+ ```
117
+ XML definition (src/config/asterix_catXXX_Y_Z.xml)
118
+ ├─ UAP → which FSPEC bit maps to which DataItem
119
+ ├─ formats → fixed/variable/repetitive/compound/explicit item lengths
120
+ └─ <Bits> → named bit-fields: range, signedness, encoding, scale
121
+ ↓
122
+ decoder.ts walks FSPEC + items, slices each present item's bytes
123
+ values.ts extractBitFields() turns bytes into named raw values per the XML
124
+ fields.ts semantic mapper: raw names → canonical AsterixFields
125
+ (XML-first; falls back to manual byte decoding when a
126
+ definition lacks usable <Bits> specs)
127
+ parser.ts edition selection: forced, or richest-plausible-decode wins
128
+ ```
129
+
130
+ Value extraction is **config-driven**: bit positions, two's-complement handling, scale factors (`180/2²⁵` vs `180/2²³` per edition, etc.), 6-bit ICAO callsign chars, octal squawk — all read from the XML. The semantic layer stays as a thin, reviewable mapping because "which item is *the* position" and altitude-preference rules are policy, not format.
131
+
132
+ ### Adding a category or edition
133
+
134
+ 1. Drop the standard `asterix` XML into `src/config/` (many are already there).
135
+ 2. Register it in `FILE_MAP` (xml-loader.ts) and `EDITIONS` (parser.ts).
136
+ 3. If the category introduces new semantic fields, extend `AsterixFields` (types.ts) and add a small mapping in `fields.ts`.
137
+ 4. Add a regression test with a real captured packet in `parser.test.ts`.
138
+
139
+ ## Testing
140
+
141
+ ```bash
142
+ pnpm test
143
+ ```
144
+
145
+ Tests use **real packets captured from the live radar feed** as fixtures (a CAT21/0.26 MAS179 report and a CAT23 ground-station status), plus multi-block and malformed-input cases. When touching the decoder, always keep these green — they pin exact decoded values.
146
+
147
+ ## Low-level API (advanced)
148
+
149
+ Exposed for tooling/tests; not needed for normal decoding:
150
+
151
+ | Export | Purpose |
152
+ |--------|---------|
153
+ | `loadDef(cat, edition)` | Load + cache one XML definition (`AsterixDef`: UAP + item formats + bit-fields) |
154
+ | `decodeBlock(block, def, onField)` | Walk one ASTERIX block, invoking `onField(id, bytes)` per present item |
155
+ | `parseFspec(buf, start?)` | Parse an FX-extended FSPEC into a bit array |
156
+ | `extractBitFields(data, defs)` | Apply `BitFieldDef[]` to raw item bytes → named values |
157
+ | `BitFieldDef`, `ItemDef`, `AsterixDef`, `UAPEntry`, `RawValues` | Supporting types |
@@ -0,0 +1,2 @@
1
+ export * from "./surveillance/index.js";
2
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,yBAAyB,CAAC"}