Skip to content

nathan-casabieille/ASTERIXCodec

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

38 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

ASTERIXCodec Logo

Comprehensive ASTERIX protocol toolkit for Air Traffic Management (ATM)

CI Status

CAT01 Tests CAT02 Tests CAT04 Tests CAT07 Tests
CAT08 Tests CAT09 Tests CAT10 Tests CAT11 Tests
CAT15 Tests CAT16 Tests CAT17 Tests CAT18 Tests
CAT19 Tests CAT20 Tests CAT21 Tests CAT23 Tests
CAT25 Tests CAT32 Tests CAT34 Tests CAT48 Tests
CAT62 Tests CAT63 Tests CAT65 Tests CAT150 Tests
CAT205 Tests CAT240 Tests CAT247 Tests

A high-performance, spec-driven C++20 library for encoding and decoding ASTERIX (All Purpose Structured EUROCONTROL Surveillance Information Exchange) messages.

The category structure is loaded at runtime from an XML file, making it straightforward to add new categories without recompiling the library.


Features

  • XML-driven data dictionary — category definitions (items, encodings, UAP) are parsed from specs/CATXX.xml via pugixml; no hardcoded category logic.
  • All standard item types — Fixed (group), Extended (FX-bit chaining), Repetitive (FX-bit list), RepetitiveGroup (count-prefixed structured groups), RepetitiveGroupFX (FX-terminated structured groups), Compound (PSF-driven optional sub-items), and Explicit/SP.
  • Dynamic UAP selection — for CAT01 the plot/track variant is auto-detected from I001/020 TYP on a per-record basis.
  • Multi-record blocks — a single Data Block can carry any number of Data Records; the decode loop handles them correctly.
  • Strict bounds checkingBitReader and BitWriter throw on any out-of-bounds access; mandatory-item violations are flagged on the DecodedRecord.
  • Zero-copy decode — hot paths use std::span<const uint8_t>; no intermediate buffer copies.
  • Pretty-printer — physical values (NM, °, FL, kt) and table lookups are rendered in the test executable for visual inspection.

Supported Categories

Category Description Edition
CAT001 Transmission of Monoradar Data Target Reports 1.4
CAT002 Transmission of Monoradar Service Messages 1.2
CAT004 Safety Net Messages 1.13
CAT007 Transmission of Directed Interrogation Messages 1.12
CAT008 Monoradar Derived Weather Information 1.3
CAT009 Composite Weather Reports 2.1
CAT010 Transmission of Monosensor Surface Movement Data 1.1
CAT011 Transmission of A-SMGCS Data 1.3
CAT015 Independent Non-Cooperative Surveillance System Target Reports 1.2
CAT016 Transmission of Data Link Flight Messages 1.0
CAT017 Mode S Surveillance Coordination Function Messages 1.3
CAT018 Mode S Datalink Function Messages 1.8
CAT019 Multilateration System Status Messages 1.3
CAT020 Multilateration Target Reports 1.11
CAT025 CNS/ATM Ground System Status Reports 1.6
CAT021 ADS-B Target Reports 2.7
CAT023 CNS/ATM Ground Station and Service Status Reports 1.3
CAT032 Miniplan Reports to an SDPS 1.2
CAT034 Transmission of Monoradar Service Messages 1.29
CAT048 Monoradar Target Reports 1.32
CAT062 SDPS Track Messages 1.21
CAT063 Sensor Status Reports 1.7
CAT065 SDPS Service Status Reports 1.6
CAT150 MADAP Plan Server - Flight Data Message 3.0
CAT205 Radio Direction Finder Reports 1.0
CAT240 Radar Video Transmission 1.3
CAT247 Version Number Exchange 1.3

Support for additional categories can be added by dropping a new XML spec into specs/ and calling codec.registerCategory(loadSpec("specs/CATXX.xml")).


Project Structure

ASTERIXCodec/
├── CMakeLists.txt                   # C++20 build; FetchContent(pugixml v1.14)
├── include/ASTERIXCodec/
│   ├── Types.hpp                    # Core metadata and decoded-value types
│   ├── BitStream.hpp                # MSB-first BitReader / BitWriter (header-only)
│   ├── SpecLoader.hpp               # loadSpec(path) → CategoryDef
│   └── Codec.hpp                    # Public API: decode() + encode()
├── src/
│   ├── SpecLoader.cpp               # pugixml → CategoryDef parser
│   └── Codec.cpp                    # FSPEC + item decode/encode engine
├── specs/
│   └── CAT*.xml                     # 27 XML category definitions (one per supported category)
└── tests/
    └── test_cat*.cpp                # One test file per category (27 total)

Requirements

Tool Minimum version
C++ compiler GCC 12 / Clang 15 / MSVC 19.34 (C++20 required)
CMake 3.20
Internet access Required once to fetch pugixml via CMake FetchContent

Build

# Configure (Debug)
cmake -B build -DCMAKE_BUILD_TYPE=Debug

# Build library + test executable
cmake --build build -j$(nproc)

# Run tests
./build/test_cat01  && ./build/test_cat02  && ./build/test_cat04  && ./build/test_cat07  && \
./build/test_cat08  && ./build/test_cat09  && ./build/test_cat10  && ./build/test_cat11  && \
./build/test_cat15  && ./build/test_cat16  && ./build/test_cat17  && ./build/test_cat18  && \
./build/test_cat19  && ./build/test_cat20  && ./build/test_cat25  && \
./build/test_cat21  && ./build/test_cat23  && ./build/test_cat32  && ./build/test_cat34  && \
./build/test_cat48  && ./build/test_cat62  && ./build/test_cat63  && ./build/test_cat65  && \
./build/test_cat150 && ./build/test_cat205 && ./build/test_cat240 && ./build/test_cat247

Expected output ends with ALL TESTS PASSED.


Quick API Example

#include "ASTERIXCodec/Codec.hpp"
#include "ASTERIXCodec/SpecLoader.hpp"

using namespace asterix;

// 1. Load category definition from XML
Codec codec;
codec.registerCategory(loadSpec("specs/CAT01.xml"));

// 2. Decode a raw frame received from the network
std::vector<uint8_t> raw = { /* ... */ };
DecodedBlock block = codec.decode(raw);

if (!block.valid) {
    std::cerr << "Error: " << block.error << '\n';
}

for (const auto& rec : block.records) {
    // Access I001/010 Data Source Identifier
    uint64_t sac = rec.items.at("010").fields.at("SAC");
    uint64_t sic = rec.items.at("010").fields.at("SIC");

    // Access I001/040 polar position (raw values — apply scale manually)
    // RHO [NM] = raw / 128.0
    // THETA [°] = raw * 360.0 / 65536.0
    uint64_t rho_raw   = rec.items.at("040").fields.at("RHO");
    uint64_t theta_raw = rec.items.at("040").fields.at("THETA");

    // Check UAP variant
    bool is_track = (rec.uap_variation == "track");
}

// 3. Encode a record back to bytes
DecodedRecord rec;
rec.uap_variation = "track";
// ... populate rec.items ...
std::vector<uint8_t> frame = codec.encode(1, { rec });

ASTERIX Wire Format (CAT01 reminder)

Data Block
  ├── CAT   [1 byte]   Category number (0x01)
  ├── LEN   [2 bytes]  Total block length in bytes (big-endian)
  └── Data Records (one or more)
        ├── FSPEC  [1–N bytes]  Presence bitmask; each byte: [I7|I6|I5|I4|I3|I2|I1|FX]
        │                        FX=1 → next FSPEC byte follows; FX=0 → last byte
        └── Items  [variable]   Present items in UAP order

Item encoding types:

Type Description
Fixed Fixed-length; sub-elements bit-packed MSB-first
Extended Variable octets; each = 7 data bits + 1 FX bit
Repetitive FX-terminated list; each octet = 7-bit value + FX
RepetitiveGroup 1-byte count prefix; then N × structured sub-element group
RepetitiveGroupFX FX-terminated list of structured groups; FX is the last bit of each group
Compound PSF indicator byte(s) select optional named sub-items (each a Fixed group)
Explicit (SP/RE) First byte = total length (inclusive); raw payload follows

CAT01 UAP selectionI001/020 field TYP acts as a discriminator: TYP=0 selects the plot UAP, TYP=1 selects the track UAP. Because both UAPs share the same first two FSPEC slots (I010, I020), a single decode pass is sufficient.


XML Spec Schema

The specs/CAT01.xml file follows this structure:

<Category cat="1" name="..." edition="1.4" date="2022-08-18">

  <DataItems>
    <!-- Fixed item -->
    <DataItem id="010" name="Data Source Identifier" presence="mandatory">
      <Fixed>
        <Element name="SAC" bits="8" encoding="raw"/>
        <Element name="SIC" bits="8" encoding="raw"/>
      </Fixed>
    </DataItem>

    <!-- Extended item (variable octets, each = 7 bits + FX) -->
    <DataItem id="020" name="Target Report Descriptor">
      <Extended>
        <Octet>   <!-- must sum to exactly 7 bits -->
          <Element name="TYP" bits="1" encoding="table">
            <Entry value="0" meaning="Plot"/>
            <Entry value="1" meaning="Track"/>
          </Element>
          <!-- ... -->
        </Octet>
      </Extended>
    </DataItem>

    <!-- Repetitive FX item -->
    <DataItem id="030" name="Warning/Error Conditions">
      <Repetitive type="fx">
        <Element bits="7" encoding="table"> <!-- ... --> </Element>
      </Repetitive>
    </DataItem>

    <!-- Repetitive count-prefixed structured group (e.g. CAT002/070) -->
    <DataItem id="070" name="Plot Count Values">
      <Repetitive type="count" count_bytes="1">
        <Group>
          <Element name="A"       bits="1"  encoding="table"/>
          <Element name="IDENT"   bits="5"  encoding="table"/>
          <Element name="COUNTER" bits="10" encoding="raw"/>
        </Group>
      </Repetitive>
    </DataItem>

    <!-- Compound item (e.g. CAT034/050 System Configuration and Status)        -->
    <!-- PSF byte: bit7=sub-item 0, …, bit1=sub-item 6, bit0=FX continuation  -->
    <!-- Only present sub-items follow the PSF byte(s) on the wire.            -->
    <DataItem id="050" name="System Configuration and Status">
      <Compound>
        <SubItem name="COM">           <!-- slot 0: PSF bit7 -->
          <Fixed>
            <Element name="NOGO" bits="1" encoding="table">
              <Entry value="0" meaning="System is released for operational use"/>
              <Entry value="1" meaning="Operational use of System is inhibited"/>
            </Element>
            <!---->
            <Spare bits="1"/>
          </Fixed>
        </SubItem>
        <SubItem name="-"/>            <!-- slot 1: PSF bit6 – unused -->
        <SubItem name="-"/>            <!-- slot 2: PSF bit5 – unused -->
        <SubItem name="PSR">           <!-- slot 3: PSF bit4 -->
          <Fixed> <!----> </Fixed>
        </SubItem>
        <!---->
      </Compound>
    </DataItem>

    <!-- Special Purpose Field -->
    <DataItem id="SP" name="Special Purpose Field">
      <Explicit type="sp"/>
    </DataItem>
  </DataItems>

  <UAPs default="plot">
    <Variation name="plot">
      <Item ref="010"/> <Item ref="020"/> <!-- ... -->
    </Variation>
    <Variation name="track">
      <Item ref="010"/> <Item ref="020"/> <Item ref="161"/> <!-- ... -->
    </Variation>
    <!-- UAP discriminator -->
    <Case item="020" field="TYP">
      <When value="0" use="plot"/>
      <When value="1" use="track"/>
    </Case>
  </UAPs>

</Category>

Supported encoding values: raw, table, unsigned_quantity, signed_quantity, string_octal.

Decoded Compound sub-fields are accessed via item.compound_sub_fields["COM"]["NOGO"].


References

About

A universal ASTERIX codec using XML specifications to encode and decode any data category.

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors