@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.
- package/README.md +157 -3
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +924 -0
- package/dist/surveillance/adsb/index.d.ts +16 -0
- package/dist/surveillance/adsb/index.d.ts.map +1 -0
- package/dist/surveillance/ais/index.d.ts +17 -0
- package/dist/surveillance/ais/index.d.ts.map +1 -0
- package/dist/surveillance/asmgcs/index.d.ts +14 -0
- package/dist/surveillance/asmgcs/index.d.ts.map +1 -0
- package/dist/surveillance/asterix/cat10/index.d.ts +3 -0
- package/dist/surveillance/asterix/cat10/index.d.ts.map +1 -0
- package/dist/surveillance/asterix/cat21/index.d.ts +5 -0
- package/dist/surveillance/asterix/cat21/index.d.ts.map +1 -0
- package/dist/surveillance/asterix/cat48/index.d.ts +5 -0
- package/dist/surveillance/asterix/cat48/index.d.ts.map +1 -0
- package/dist/surveillance/asterix/cat62/index.d.ts +3 -0
- package/dist/surveillance/asterix/cat62/index.d.ts.map +1 -0
- package/dist/surveillance/asterix/decoder.d.ts +14 -0
- package/dist/surveillance/asterix/decoder.d.ts.map +1 -0
- package/dist/surveillance/asterix/fields.d.ts +4 -0
- package/dist/surveillance/asterix/fields.d.ts.map +1 -0
- package/dist/surveillance/asterix/index.d.ts +5 -0
- package/dist/surveillance/asterix/index.d.ts.map +1 -0
- package/dist/surveillance/asterix/parser.d.ts +16 -0
- package/dist/surveillance/asterix/parser.d.ts.map +1 -0
- package/dist/surveillance/asterix/parser.test.d.ts +2 -0
- package/dist/surveillance/asterix/parser.test.d.ts.map +1 -0
- package/dist/surveillance/asterix/resolvers.d.ts +17 -0
- package/dist/surveillance/asterix/resolvers.d.ts.map +1 -0
- package/dist/surveillance/asterix/types.d.ts +50 -0
- package/dist/surveillance/asterix/types.d.ts.map +1 -0
- package/dist/surveillance/asterix/values.d.ts +10 -0
- package/dist/surveillance/asterix/values.d.ts.map +1 -0
- package/dist/surveillance/asterix/xml-loader.d.ts +46 -0
- package/dist/surveillance/asterix/xml-loader.d.ts.map +1 -0
- package/dist/surveillance/index.d.ts +6 -0
- package/dist/surveillance/index.d.ts.map +1 -0
- package/dist/surveillance/mlat/index.d.ts +16 -0
- package/dist/surveillance/mlat/index.d.ts.map +1 -0
- package/package.json +38 -4
- package/src/config/asterix.dtd +78 -0
- package/src/config/asterix.ini +17 -0
- package/src/config/asterix_bds.xml +462 -0
- package/src/config/asterix_cat001_1_1.xml +872 -0
- package/src/config/asterix_cat001_1_2.xml +836 -0
- package/src/config/asterix_cat002_1_0.xml +258 -0
- package/src/config/asterix_cat004_1_12.xml +1713 -0
- package/src/config/asterix_cat008_1_0.xml +353 -0
- package/src/config/asterix_cat010_1_1.xml +791 -0
- package/src/config/asterix_cat011_1_2.xml +1691 -0
- package/src/config/asterix_cat019_1_2.xml +293 -0
- package/src/config/asterix_cat020_1_7.xml +1256 -0
- package/src/config/asterix_cat021_0_26.xml +902 -0
- package/src/config/asterix_cat021_1_4.xml +1594 -0
- package/src/config/asterix_cat021_1_8.xml +1594 -0
- package/src/config/asterix_cat021_2_4.xml +2191 -0
- package/src/config/asterix_cat023_1_2.xml +357 -0
- package/src/config/asterix_cat025_1_2.xml +306 -0
- package/src/config/asterix_cat030_6_2.xml +1387 -0
- package/src/config/asterix_cat031_6_2.xml +214 -0
- package/src/config/asterix_cat032_6_2.xml +465 -0
- package/src/config/asterix_cat032_7_0.xml +702 -0
- package/src/config/asterix_cat034_1_26.xml +586 -0
- package/src/config/asterix_cat034_1_27.xml +683 -0
- package/src/config/asterix_cat048_1_14.xml +1142 -0
- package/src/config/asterix_cat048_1_21.xml +1241 -0
- package/src/config/asterix_cat062_0_17.xml +1442 -0
- package/src/config/asterix_cat062_1_16.xml +3031 -0
- package/src/config/asterix_cat062_1_18.xml +3303 -0
- package/src/config/asterix_cat062_1_7.xml +2817 -0
- package/src/config/asterix_cat062_1_9.xml +737 -0
- package/src/config/asterix_cat063_1_3.xml +298 -0
- package/src/config/asterix_cat065_1_2.xml +168 -0
- package/src/config/asterix_cat065_1_3.xml +183 -0
- package/src/config/asterix_cat094_0_1.xml +1103 -0
- package/src/config/asterix_cat205_1_0.xml +385 -0
- package/src/config/asterix_cat240_1_3.xml +289 -0
- package/src/config/asterix_cat242_1_0.xml +644 -0
- package/src/config/asterix_cat247_1_2.xml +92 -0
- package/src/config/asterix_cat252_6_1.xml +1826 -0
- package/src/config/asterix_cat252_6_2.xml +1831 -0
- package/src/config/asterix_cat252_7_0.xml +1867 -0
- package/src/config/eurocontroll_version/asterix-spec.xsd +343 -0
- package/src/config/eurocontroll_version/asterix_cat094.xml +457 -0
package/README.md
CHANGED
|
@@ -1,3 +1,157 @@
|
|
|
1
|
-
#
|
|
2
|
-
|
|
3
|
-
|
|
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 |
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,yBAAyB,CAAC"}
|