HRPT & AHRPT — full-resolution polar weather imagery
Two routes to the same instrument: NOAA HRPT is the raw one — Manchester chips at 1330.8 kchip/s, 10-bit words in fixed 11090-word minor frames, no FEC at all — while MetOp/FengYun AHRPT wraps the identical AVHRR data in the full CCSDS machinery (Viterbi + RS CADUs + Space Packets) this repo already runs for LRPT.
Rafe project · app/radio/sat/hrpt.py, app/radio/sat/ahrpt.py · test_sat_hrpt.py, test_sat_hrpt_iq.py, test_sat_ahrpt.py · RX (+ reference encoders for self-test) · HRPT minor-frame sync word is a self-consistent placeholder — transcribe the exact NOAA KLM value before an on-air capture (§2.2)
Abstract
The AVHRR imager on the NOAA POES and MetOp satellites produces 5 spectral channels × 2048 samples per scan line. NOAA's HRPT downlink (1698 MHz band, BPSK) is gloriously archaic: no error correction whatsoever, just bi-phase-L (Manchester) chips carrying 10-bit words in a rigid frame — if your S-band dish gives you clean bits you get a clean image, and every bit error is a speckle. AHRPT (MetOp, FengYun) is the modern re-plumbing: the same instrument words ride CCSDS Space Packets inside RS-protected, convolutionally-coded CADUs — so Rafe's implementation is deliberately thin, reusing the CCSDS core (ccsds.py) wholesale and adding only the AVHRR payload layer. This document pins both framings, the shared word/ de-interleave machinery, and the IQ front ends.
1. Shared machinery: 10-bit words and channel de-interleave
AVHRR words are 10-bit, MSB first (pack_words/unpack_words). The earth view of one scan line is 10 240 words = 5 channels × 2048 samples, interleaved word-by-word: word \(k\) belongs to channel \(k \bmod 5\), sample \(\lfloor k/5 \rfloor\) (deinterleave → a (5, 2048) array per line; channel_images stacks lines into 5 per-channel images, one row per frame).
2. NOAA HRPT
2.1 Physical layer
| Parameter | Value | Code |
|---|---|---|
| Bit rate | 665 400 bit/s | BIT_RATE |
| Line code | Manchester / bi-phase-L: 1 → 10, 0 → 01 |
manchester_encode |
| Chip rate | 2 × 665 400 = 1 330 800 chip/s | CHIP_RATE |
| Carrier | BPSK (≈1698/1702/1707 MHz) | decode_iq via qpsk_rx.demod_bpsk |
manchester_decode takes the first chip of each pair. decode_iq runs the shared BPSK receiver (AGC → RRC → Gardner → Costas) at chip rate, hard-slices, then tries the 2 BPSK polarities × 2 Manchester chip phases and keeps the combination in which the frame sync appears — the ambiguity-resolution-by- sync-word pattern used throughout the satellite stack.
2.2 Minor-frame structure
| Parameter | Value | Code |
|---|---|---|
| Words per minor frame | 11 090 | WORDS_PER_FRAME |
| Frame sync | 6 ten-bit words (60 bits) | HRPT_SYNC |
| Sync tolerance | ≤ 6 bit errors in 60 | find_sync(max_err=6) |
| Earth-view offset | word 750 | EARTH_START |
| Earth view | words 750 … 750+10239 | EARTH_WORDS |
One minor frame = one scan line (3 lines/second). The words before the earth view carry telemetry, calibration and space view — skipped (zero-filled by the reference encoder), listed in §4. The stream decoder (decode) hunts the sync from any bit offset (the test prepends 37 junk bits), extracts each frame's earth words, de-interleaves, and steps exactly one frame forward — frame after frame until the bits run out, no inter-frame state needed because there is no FEC to synchronise.
Interop caveat (deliberate):
HRPT_SYNC—1000010111 0011111010 0011000001 1110110001 0101101010 1001000000— is flagged in the source as a self-consistent constant. Transcribe the exact NOAA KLM minor-frame sync before trusting an on-air capture; everything else (word size, frame length, interleave, offsets) is standard HRPT geometry.
3. MetOp / FengYun AHRPT
AHRPT carries AVHRR as CCSDS Space Packets (APID 103) inside the same CADU pipeline as LRPT — so ahrpt.py is ~90 lines of glue:
Encode (reference/test): scan lines → 10-bit interleaved words (reusing hrpt.pack_words) → a payload prefixed by a 5-byte dimension header [nch, nsamp_lo, nsamp_hi, nlines_lo, nlines_hi] → 800-byte segments, each a Space Packet (sequence-numbered) → one packet per VCDU (892-byte frame, 6-byte header zone + 2-byte M-PDU pointer) → 4 × 223-byte RS chunks → ccsds.encode_cadu(…, interleave=4) (RS(255,223) ×4, PN randomiser, ASM, r=1/2 K=7 convolution).
Decode: ccsds.decode_stream (Viterbi → ASM hunt → derandomise → RS) → lrpt.extract_packets → filter APID 103 → reassemble segments by packet sequence number → parse the dimension header → unpack_avhrr back to per-line (nch, nsamp) arrays.
IQ front end: AHRPT is OQPSK; decode_ahrpt_iq demodulates with the shared receiver and tries the 4 phase rotations × I/Q swap, letting the CCSDS ASM arbitrate — identical strategy to LRPT.
Scope note: the 5-byte dimension header is Rafe's own serialisation for the payload layer (the real MetOp AVHRR packet layout carries its dimensions implicitly); APID, packet, CADU and FEC layers are the standard CCSDS ones, shared with and validated by the LRPT chain.
4. Validation
| Test | Asserts |
|---|---|
test_manchester_roundtrip |
chip encode/decode identity |
test_word_pack_roundtrip |
10-bit word pack/unpack identity |
test_frame_decode_and_deinterleave |
3 frames + 37 junk lead-in bits → all 5 channels of each recovered exactly |
test_sync_tolerates_bit_errors |
sync found with 4 errors in its 60 bits |
test_channel_images_shape |
5 stacked images, one row per frame |
test_hrpt_full_decode_iq |
raw BPSK IQ (120 Hz offset, timing/phase offsets, noise) → exact channels |
test_avhrr_pack_roundtrip |
AHRPT 10-bit payload pack/unpack identity |
test_ahrpt_over_ccsds_multicadu |
6 lines spanning many CADUs → exact recovery through the full CCSDS chain |
test_ahrpt_iq_impaired |
OQPSK IQ, 170 Hz offset + noise → exact lines |
5. Limitations
- HRPT telemetry ignored. Words 0–749 (TIP telemetry, calibration, space view) are skipped; only the earth view is extracted. Calibrated temperatures/albedo would start there.
- HRPT sync constant needs the KLM transcription (§2.2).
- AHRPT dimension header is Rafe-internal (§3); real-capture interop needs the MetOp AVHRR packet field layout.
- Real-time streaming decode wires through
sat/decode_file.pyand the collector roles described in sat.md.
Related: LRPT (the CCSDS core both reuse; Meteor's JPEG alternative to raw AVHRR), APT (the analog ancestor), xRIT (the geostationary file layer), and the maths guide on concatenated FEC.