Metadata-Version: 2.4
Name: lib3-aerospace-telemetry
Version: 0.1.0
Summary: Zero-copy aerospace telemetry frame parsing and stream reassembly
Author: Aero Frame Contributors
License: MIT
Classifier: Development Status :: 4 - Beta
Classifier: Programming Language :: Python :: 3
Classifier: Topic :: Scientific/Engineering
Requires-Python: >=3.10
Description-Content-Type: text/markdown
Provides-Extra: test
Requires-Dist: pytest>=8; extra == "test"

# aero-frame

`lib3-aerospace-telemetry` is a dependency-free Python library for bounded, zero-copy decoding of common aerospace telemetry containers. It provides parsers for VITA 49/VRT, CCSDS Space Packets, IRIG 106 Chapter 10/11 packet headers, and MIL-STD-1553B words, plus an incremental stream tokenizer for the byte-oriented protocols.

## Design

The parsers accept `bytes`, `bytearray`, or `memoryview`. Header fields are extracted with explicit big-endian masks and shifts. Packet payloads are `memoryview` slices and therefore do not allocate when a complete packet is already present in the caller's buffer. Only a partial packet retained between `feed()` calls is copied into the decoder's bounded carry buffer.

Every operation returns one of these statuses:

| Status | Meaning |
| --- | --- |
| `AERO_VALID_PACKET` | A complete packet or word was decoded. |
| `AERO_INCOMPLETE_STREAM` | More bytes are required before decoding can continue. |
| `AERO_CORRUPTED_PACKET` | A sync, reserved field, or length contract failed. |

## Install

```bash
python -m pip install lib3-aerospace-telemetry
```

For development:

```bash
python -m pip install -e ".[test]"
python -m pytest
```

## Streaming CCSDS

The CCSDS primary header is six octets. Version is bits 15-13, packet type bit 12, secondary-header flag bit 11, APID bits 10-0; the second word contains sequence flags bits 15-14 and sequence count bits 13-0; packet length is the final 16-bit field plus one.

```python
from aero_frame import AeroStatus, StreamDecoder

stream = StreamDecoder("ccsds")
for datagram in udp_socket:
    for event in stream.feed(datagram):
        if event.status is AeroStatus.VALID_PACKET:
            packet = event.value
            consume(packet.apid, packet.payload)
        elif event.status is AeroStatus.CORRUPTED_PACKET:
            log_protocol_fault(event.error)
stream.flush()  # reports a truncated final frame, if any
```

## VITA 49 / VRT

The implementation reads the VRT packet type, stream-ID and class-ID presence flags, integer/fractional timestamp format codes, and 16-bit packet-size-in-words field. Integer timestamps are exposed as raw GPS/POSIX epoch ticks; epoch interpretation is a mission-level policy. The payload remains raw so applications can decode 4-, 8-, 16-, 24-, or 32-bit I/Q samples according to the stream's context packet.

```python
from aero_frame import parse_vita49
result = parse_vita49(frame_memoryview)
if result.status.value == "AERO_VALID_PACKET":
    iq_bytes = result.value.payload
```

## IRIG 106

The fixed header parser validates sync `0xEB25`, reads channel ID, packet/data lengths, data type, sequence counter, and time-format flags, then returns a zero-copy data slice. Packet-length semantics are the IRIG convention: the stored value is the final zero-based byte offset, so total bytes are `packet_length + 1`.

```python
from aero_frame import parse_irig106
result = parse_irig106(recording_buffer)
```

## MIL-STD-1553B

A 1553 word is passed as an integer because the electrical Manchester waveform and parity are below this library's frame-matrix layer. The parser exposes command, data, and status structures and validates the two sync bits.

| Word | Bits | Fields |
| --- | ---: | --- |
| Command | 15-14 sync, 13-9 | terminal address |
| Command | 10 | transmit/receive |
| Command | 9-5 | subaddress or mode |
| Command | 4-0 | word count or mode code |
| Status | 13-9 | terminal address |
| Status | 8..0 | message/error and service flags |
| Data | 13-0 | 16-bit bus payload in the word model |

```python
from aero_frame import parse_1553_word
command = parse_1553_word(0x8401, "command")
status = parse_1553_word(0x4984, "status")
```

## Safety and performance boundaries

No parser indexes beyond a checked input boundary, trusts an unbounded length, or allocates a payload copy. Length fields are validated before slicing. For production ingest, use one `StreamDecoder` per protocol and feed it immutable network buffers; for a complete datagram, call the parser directly to retain the caller's buffer ownership.

The package intentionally leaves mission-specific secondary-header epochs, VRT context sample encodings, IRIG channel payload semantics, and Manchester decoding to policy modules. Those formats vary by configuration and cannot be inferred safely from a generic packet header.
