gr-sarsat-modern/python/sarsat/sarp_msg_extract.py
Ryan Malloy 5867c54de3 feat: port gr-sarsat to GNU Radio 3.10+
Ported from zleffke/gr-sarsat (2018, GNU Radio 3.7):
- Updated Python 2 → Python 3
- Converted XML block definitions to YAML
- Added pyproject.toml for modern packaging
- Preserved original MIT license

Blocks:
- biphase_l_decode_bb: Manchester decoder (decim_block)
- pds_frame_sync: Frame synchronizer with PDU output
- sarp_msg_extract: SARP message splitter/validator
2026-02-11 02:22:28 -07:00

84 lines
2.7 KiB
Python

#!/usr/bin/env python3
# -*- coding: utf-8 -*-
# MIT License
# Copyright (c) 2018 zleffke
# Ported to GNU Radio 3.10+ by gr-mcp
"""
SARP (Search And Rescue Processor) Message Extractor.
Each 72-byte PDS frame contains three 24-byte SARP messages.
Each SARP message starts with a 12-bit sync word: 0xD60
This block:
1. Receives PDS frame PDUs
2. Splits into three 24-byte SARP messages
3. Validates sync word (0xD60)
4. Routes to 'valid' or 'invalid' output port
"""
import numpy as np
import pmt
import binascii
from gnuradio import gr
class sarp_msg_extract(gr.basic_block):
"""
SARP Message Extractor.
Input: PDU from PDS Frame Sync Block (72 bytes)
Output: Individual SARP messages (24 bytes each) on 'valid' or 'invalid' ports
A SARP message is considered valid if its sync word matches 0xD60.
Even invalid messages may contain recoverable beacon data via BCH codes.
"""
def __init__(self):
gr.basic_block.__init__(
self,
name="sarp_msg_extract",
in_sig=None,
out_sig=None,
)
self.message_port_register_in(pmt.intern("in"))
self.set_msg_handler(pmt.intern("in"), self.handle_msg)
self.message_port_register_out(pmt.intern("valid"))
self.message_port_register_out(pmt.intern("invalid"))
def _check_sync_word(self, msg: bytearray) -> bool:
"""Check if SARP message has valid sync word (0xD60)."""
# Sync word is first 12 bits: 0xD6 followed by upper nibble 0x0
return (msg[0] == 0xD6) and ((msg[1] & 0xF0) == 0x00)
def _publish_msg(self, msg: bytearray, valid: bool):
"""Publish SARP message to appropriate output port."""
port = "valid" if valid else "invalid"
pdu = pmt.cons(pmt.PMT_NIL, pmt.init_u8vector(len(msg), msg))
self.message_port_pub(pmt.intern(port), pdu)
def handle_msg(self, msg_pmt):
"""Process incoming PDS frame PDU."""
pds_msg = pmt.cdr(msg_pmt)
if not pmt.is_u8vector(pds_msg):
print("[ERROR] Received invalid message type. Expected u8vector")
return
buff = bytearray(pmt.u8vector_elements(pds_msg))
if len(buff) < 72:
print(f"[ERROR] PDS frame too short: {len(buff)} bytes (expected 72)")
return
# Extract three 24-byte SARP messages
sarp_msg_1 = bytearray(buff[0:24])
sarp_msg_2 = bytearray(buff[24:48])
sarp_msg_3 = bytearray(buff[48:72])
# Validate and publish each message
self._publish_msg(sarp_msg_1, self._check_sync_word(sarp_msg_1))
self._publish_msg(sarp_msg_2, self._check_sync_word(sarp_msg_2))
self._publish_msg(sarp_msg_3, self._check_sync_word(sarp_msg_3))