xRIT — LRIT/HRIT file layer (GOES, Meteosat, GK-2A, Elektro-L)
The file-transfer layer of the geostationary weather birds: images travel as xRIT files — a typed header chain (primary header, image structure, …) followed by the data field — fragmented across CCSDS Space Packets using the 2-bit sequence flags, and reassembled per-APID on receive. Sits directly on the CCSDS core this repo already runs.
Rafe project · app/radio/sat/xrit.py, test_sat_xrit.py · build + parse + fragment + reassemble · uncompressed and JPEG (Pillow) image fields; lossless-wavelet LRIT is a follow-on
Abstract
Geostationary imagery (GOES GRB/HRIT, Meteosat, GK-2A, Elektro-L) reaches the ground as xRIT files — "LRIT" at low rate, "HRIT" at high — each a self-describing container: a chain of typed headers, then the payload. The transport beneath is the familiar CCSDS stack (CADUs → VCDUs → Space Packets); the xRIT layer's job is exactly two things, and this module implements both directions of each:
- files ↔︎ packets — fragment a file across Space Packets and reassemble, using the packet header's 2-bit sequence flags;
- file ↔︎ image — build and parse the header chain and decode the image data field.
1. The file format
1.1 Header chain
Headers are [type(1) | length(2, BE) | fields…], concatenated; the primary header states where the chain ends and the data begins.
Primary header (type 0, 16 bytes) — must be first:
| Offset | Field |
|---|---|
| 0 | type = 0 |
| 1–2 | header length = 16 |
| 3 | file type code (0 = image) |
| 4–7 | total header length (BE32) — offset of the data field |
| 8–15 | data field length in bits (BE64) |
Image structure header (type 1, 9 bytes):
| Offset | Field |
|---|---|
| 0 | type = 1 |
| 1–2 | header length = 9 |
| 3 | bits per pixel (8 supported) |
| 4–5 | columns (BE16) |
| 6–7 | lines (BE16) |
| 8 | compression: 0 none · 1 lossless · 2 JPEG |
parse_xrit walks the chain by (type, length) until the total header length, collecting what it recognises and skipping unknown types — the forward-compatibility property the format was designed for.
1.2 Data field
- Compression 0: raw 8-bpp pixels, reshaped to
(lines, cols). - Compression 2 (JPEG): decoded via Pillow — this is what the real lossy LRIT services do; the round-trip test bounds the loss (mean abs error < 4 at quality 90).
- Compression 1 (lossless wavelet/Rice): not yet implemented — named follow-on.
2. Packet fragmentation and reassembly
Space Packet header: version/type/APID(2) · flags+seq(2) · length(2), then data. The sequence flags (top 2 bits of byte 2) tell the reassembler how fragments compose:
| Flags | Meaning |
|---|---|
01 |
first segment of a file |
00 |
continuation |
10 |
last segment |
11 |
unsegmented (whole file in one packet) |
fragment_to_packets(file, apid, seg=800) cuts the file into ≤ seg-byte segments and numbers them; reassemble_files(packets) keeps one buffer per APID, so interleaved files on different APIDs (the normal situation — a virtual channel carries several image channels at once) reassemble correctly; the test interleaves two images across two APIDs and recovers both exactly.
3. The full chain
The GOES-shaped end-to-end test drives: image → xRIT file → Space Packet → VCDU/M-PDU (892-byte frame) → 4 × RS(255,223) → CADU → QPSK symbols → CCSDS decode → packet extraction → file reassembly → header parse → pixel-exact image. Everything below the packet layer is the LRPT/CCSDS core, already specified there.
4. Validation
| Test | Asserts |
|---|---|
test_xrit_file_roundtrip |
header fields + pixel-exact image |
test_packet_fragment_reassemble |
genuinely multi-segment file survives |
test_interleaved_apids |
two files interleaved on two APIDs both recovered |
test_full_goes_chain_through_ccsds |
image → … → CADU → … → image, pixel-exact |
test_xrit_jpeg_compressed |
JPEG field genuinely smaller; loss bounded |
test_xrit_jpeg_via_packets |
JPEG file through fragmentation too |
5. Limitations
- Header types beyond 0/1 (annotation, time stamp, key/encryption, segmentation) are skipped on parse and never emitted on build — enough for image work, not for the full dissemination catalogue (text products, DCS).
- Lossless compression (flag 1) unimplemented (§1.2).
- Multi-segment image products (one image split across several files with a segmentation header) are not recombined — one file = one image here.
- Encrypted HRIT (GK-2A) is out of scope without keys.
Related: LRPT (the CCSDS transport beneath), HRPT/AHRPT (the polar siblings), sat.md (how the decoders wire into live SDR collectors).