DISS Pipeline
Expert guide for the eLTS DISS binary-file processing pipeline (C-band uplink, G1 Galileo).
Architecture Overview
DISS binary file
└─ DissFilter (src/filter/diss_filter.py)
├─ _process_normal_band_block() ─► MsgSubfrParser ─► FNavParser / INavParser / CNavParser / GNavParser
│ ► IntSubfrParser
└─ _process_s_band_block() ─► SBandParser ─► FNavParser / INavParser / CNavParser / GNavParser
Each parser delegates per-word extraction to parser/nav_utils/{f_nav,i_nav,c_nav,g_nav}/ where the bitfield descriptors live.
Key Classes
| Class | File | Role |
|---|---|---|
DissFilter |
filter/diss_filter.py |
Top-level orchestrator; reads raw DISS binary, splits into normal-band and S-band blocks, dispatches to sub-parsers |
MsgSubfrParser |
parser/diss/msg_subfr_parser.py |
Normal-band Message sub-frame (Field 1 = FNAV/INAV/SAR/CNAV, Field 2 = GNAV) |
IntSubfrParser |
parser/diss/int_subfr_parser.py |
Normal-band Integrity sub-frame |
SBandParser |
parser/diss/s_band_parser.py |
S-band 26080-byte fixed packet; dispatches by nav type and batch number |
FNavParser |
parser/diss/fnav_parser.py |
Extracts F/NAV pages (normal-band + S-band) |
INavParser |
parser/diss/inav_parser.py |
Extracts I/NAV pages (normal-band + S-band) |
CNavParser |
parser/diss/cnav_parser.py |
Extracts C/NAV pages (S-band only) |
GNavParser |
parser/diss/gnav_parser.py |
Extracts G/NAV pages (normal-band Field 2 + S-band nav/almanac/PRS) |
NavParser |
parser/diss/nav_parser.py |
Abstract base class; provides _push_message() |
Config Files
| File | Purpose |
|---|---|
config/s_band_config.json |
All S-band bit sizes (HEADER, BATCH_START, G_NAV_WORD, G_NAV_BATCH, FNAV_BATCH, INAV_BATCH, …) and bit offsets (GNAV_OFFSET, GNAV_ALMANAC_OFFSET, GNAV_PRS_OFFSET, …) |
config/diss_config.json |
Service descriptors, INAV uplink message sizes, file header offsets |
config/frames/msg_subframe.json |
Normal-band Message sub-frame layout: 12 packet slots, each 264 bits. lookupValue field drives page_info routing. Field 2 packets (6-11, G/NAV) have no lookupValue. |
config/common_config.json |
Shared constants used across parsers |
config/{f_nav,i_nav,c_nav,g_nav}/ |
Per-nav-type bitfield descriptor JSON files; loaded by nav_utils uplink classes |
Service Descriptors (Normal-Band)
Defined in config/diss_config.json → SERVICE_DESCRIPTORS:
| Descriptor | Value | Packet(s) | Nav type |
|---|---|---|---|
| FNAV | 0 | Packets 0-1 | F/NAV |
| INAV | 1 | Packets 1-2 | I/NAV |
| CNAV | 2 | Packets 4-5 | C/NAV |
| SAR | 3 | Packet 3 | SAR |
| PRS(1/2) | 4 | Packets 6,8,10 | G/NAV first half |
| PRS(2/2) | 5 | Packets 7,9,11 | G/NAV second half |
| Dummy | 6 (110 binary) | — | Skip slot |
| Spare | 7 (111 binary) | — | Skip slot |
Normal-Band Packet Layout (§4.2.4.2, msg_subframe.json)
Each of the 12 packets in the Message sub-frame is 264 bits (33 bytes):
- Bits 0-2: 3-bit service descriptor
- Bits 3-263: 261 bits of payload data
Field 1 (packets 0-5): FNAV, INAV, SAR, CNAV pages — each has a lookupValue in msg_subframe.json, so collect_lookup_info() can build page_info.
Field 2 (packets 6-11): G/NAV PRS pairs — no lookupValue → page_info["GNAV"] never exists → msg_subfr_parser.extract_nav_page() must branch for GNAV before the page_info lookup block.
S-Band Packet Layout (§4.2.4.2, Figure 18)
Fixed packet size: 26080 bytes (S_BAND_PACKET_LEN in s_band_config.json).
All bit offsets below are data-relative (i.e. after the 184-bit HEADER).
| Section | Bit offset (data-relative) | Batch size | Slot count |
|---|---|---|---|
| F/NAV nav | 0 | 1000 bits | 8 slots |
| F/NAV almanac | 8000 bits | 5760 bits | 1 slot |
| I/NAV nav | 13760 bits | 816 bits | 8 slots |
| I/NAV almanac | 20288 bits | 6192 bits | 1 slot |
G/NAV nav (GNAV_OFFSET) |
26480 bits | 1464 bits | 9 slots |
G/NAV almanac (GNAV_ALMANAC_OFFSET) |
39656 bits | 2600 bits | 8 slots |
G/NAV PRS (GNAV_PRS_OFFSET) |
60456 bits | 7408 bits | 20 slots |
Access pattern (same for all nav types):
data_bits = bytes_to_bits(packet)
section = data_bits[HEADER:][slot_start : slot_start + batch_size]
BATCH_START Structure (48 bits)
Every nav/almanac/PRS batch begins with a 48-bit header:
bits 0-3: TYPE_NAV_SV_DATA (4 bits)
bits 4-9: LAST_VALID_WORD_IDX (6 bits)
bits 10-15: MSG_NUM (6 bits)
bits 16-47: TOA (32 bits)
Within TOA (bits 16-47 of BATCH_START):
TOW: bits 16-35 (20 bits)WN: bits 36-47 (12 bits)
subframe_id and Slot Selection
# src/parser/diss/common_utils.py
def generate_subframe_id(time_ms: int) -> int:
return ((time_ms // 1000) + 1) % 30 # → 0..29
Used by GNavParser.extract_gnav_page() to select the G/NAV word slot in Field 2:
slot_idx = subframe_id % 3 # → 0, 1, or 2
Symmetry Pattern for Nav Parsers
All nav parsers follow the same pattern:
class XNavParser(NavParser):
def extract_xnav_page(self, subframe, req_page_type, subframe_id, subframe_data, msg_filt):
"""Normal-band extraction."""
...
def extract_xnav_page_S_band(self, packet, batch_number, req_page_type, subframe_data, msg_filt):
"""S-band nav batch extraction."""
...
def extract_xnav_almanac_S_band(self, packet, batch_number, req_page_type, subframe_data, msg_filt):
"""S-band almanac batch extraction."""
...
When adding a new nav type or method, wire it in both:
msg_subfr_parser.MsgSubfrParser.extract_nav_page()— normal-band routings_band_parser.SBandParser.extract_nav_page()— S-band routing
And add headers in __init__ of both parsers.
_parse_packet() Return Type
SBandParser._parse_packet() returns a tuple (fieldIndex, number_of_mismatch) — never a bare int. Callers unpack with:
processed_count, _ = self.s_band_parser._parse_packet(...)
A bare return self.fieldIndex on early-exit paths is a bug; it must be return self.fieldIndex, self.number_of_mismatch.
Byte Alignment Rule
utils.bits_to_bytes(bit_string) requires len(bit_string) % 8 == 0. Always pad before converting:
pad = (-n_bits) % 8
word_bytes = bits_to_bytes(word_bits + "0" * pad)
G_NAV_WORD = 283 bits → pad 5 zeros → 288 bits = 36 bytes
Pre-encryption G/NAV = 164 bits → pad 4 zeros → 168 bits = 21 bytes