Skip to content

Iridium: IMS pager messages, voice/IP/sync tagging#50

Merged
kevinelliott merged 1 commit into
masterfrom
iridium-pager
Jun 11, 2026
Merged

Iridium: IMS pager messages, voice/IP/sync tagging#50
kevinelliott merged 1 commit into
masterfrom
iridium-pager

Conversation

@kevinelliott

@kevinelliott kevinelliott commented Jun 11, 2026

Copy link
Copy Markdown
Contributor

Gap series 3/6 (vs iridium-toolkit).

  • Pager messages (IMS): full port of the toolkit's IridiumMSMessage chain (BSD, attributed) — messaging header + 2-way interleave + BCH 1897, group/trailer handling, odd-bit stream, LSB-first RIC, format 5 ASCII (1023-mod-1024 block checksum, multi-part counters, ETX) and format 3 BCD. Multi-part pages reassemble by RIC into msg-complete frames. Console: IRIDIUM msg ric=1234567 | CALL OPS +14155550100.
  • Oracle-validated: a synthetic page from our TX helpers parses field-identically in bitsparser.py (IridiumMessagingAscii ... ric:1234567 fmt:05 seq:07 TXT: CALL OPS +14155550100), vendored as a CI vector; genframe --ims regenerates it for future cross-checks.
  • Duplex traffic tagging by LCW frame type: voice (AMBE candidate bytes extracted as hex — codec proprietary, same stance as the toolkit), ip-data, sync.
  • ITL deferred with rationale (orbital tooling, not frame parsing) — noted in PROVENANCE.

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features

    • Added IMS pager messaging support with multi-part message reassembly.
    • Enabled ASCII and BCD format decoding for pager messages.
    • Added duplex traffic class support for voice, IP-data, and sync frames.
    • Improved console output to display RIC and message text.
  • Documentation

    • Updated Iridium mode documentation with expanded feature descriptions.
    • Added provenance details for pager and duplex traffic decoding.
  • Tests

    • Added end-to-end tests for IMS pager decoding validation.

Gap series 3/6 (vs iridium-toolkit). The toolkit's headline trick —
plain-text pager messages — now decodes natively: MS frames (32-bit
messaging header, 2-way interleave, BCH poly 1897) parse through the
ported IridiumMSMessage chain (BSD, attributed): header block with
super-frame counters and group, acquisition-group extras, all-ones
trailers, odd-bit stream, LSB-first RIC, format 5 ASCII (block
checksum, multi-part counters, ETX) and format 3 BCD. Multi-part pages
reassemble by RIC into msg-complete frames; the console prints
'IRIDIUM msg ric=1234567 | CALL OPS ...'.

Oracle-validated: a synthetic page built by our TX helpers parses
field-identically in bitsparser.py (IridiumMessagingAscii,
ric/fmt/seq/text), vendored as a CI vector alongside a bit-level e2e
test and unit roundtrips.

Duplex traffic classes tag by LCW frame type (toolkit mapping): voice
(AMBE candidate payload extracted as hex — the codec is proprietary),
ip-data, sync. ITL deferred: the toolkit's itl.py is orbital tooling,
not a frame parser.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Jun 11, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

This PR extends the Iridium decoder with IMS pager messaging support, adding frame parsing and multi-part message reassembly to both single-channel and wideband decoders. It includes LCW duplex traffic class mapping for voice/IP-data/sync frames, console output formatting for RIC and message text, comprehensive tests, and updated documentation.

Changes

IMS Pager Messaging Decoder

Layer / File(s) Summary
IMS pager data types and parsing
crates/xng-mode-iridium/src/ms.rs
Defines MsFrame, MsBody, and MsContent (ASCII/BCD/Raw variants) with serde serialization. Implements parse() to validate block structure, extract header fields, decode RIC (LSB-first), and dispatch content decoding by format. Adds msg_checksum validation and PagerReassembler for time-based multi-part accumulation keyed by RIC, with 60s timeout and part concatenation. Includes unit tests for single-part parsing and multi-part reassembly.
Decoder struct and pager field initialization
crates/xng-mode-iridium/src/lib.rs
Exports ms module publicly. Adds pager: ms::PagerReassembler field to both IridiumChannelDecoder and IridiumWidebandDecoder, initialized in both constructors via ms::PagerReassembler::new().
MS frame decoding in decode_bits
crates/xng-mode-iridium/src/lib.rs
Implements FrameKind::Ms arm: de-interleaves 64-bit chunks, strips fill, performs ECC with MESSAGING_BCH_POLY, chunks to 21-bit blocks, calls ms::parse(), and returns IridiumFrame with kind: "msg" and JSON details.
MS message reassembly in handle_bits
crates/xng-mode-iridium/src/lib.rs
Extends handle_bits to accept pager reassembler. When frame kind is "msg", deserializes JSON details to ms::MsFrame, extracts body, feeds into pager, and conditionally emits "msg-complete" frame on reassembly completion. Bypasses ACARS/DA/LCW flow for MS frames.
LCW duplex traffic class mapping
crates/xng-mode-iridium/src/lib.rs
Introduces decode_lcw_bits() helper to extract post-access-code LCW data and determine frame type. Refactors decode_da_bits to use helper and only proceed when ft == 2. Maps non-DA frame types (0, 1, 7) to "voice", "ip-data", "sync" in lcw_traffic_frame with JSON details including payload_hex and bit length.
Console message formatting
src/outputs/console.rs
Extends MessageBody::Iridium pretty-printing to optionally include ric=... (from /body/ric or direct ric field) and pipe-delimited text (from /body/text or text field).
IMS pager tests and example code
crates/xng-mode-iridium/examples/genframe.rs, crates/xng-mode-iridium/tests/e2e.rs
Adds genframe ims mode to construct and decode fixed single-part ASCII pages. Provides ims_bits() helper (access prefix + messaging header + interleaved BCH blocks) and pager_blocks() helper (RIC + fixed fields + 7-bit ASCII text + 21-bit padding). Includes ims_pager_bits_decode and oracle_validated_ims_vector tests verifying decoded message kind, RIC, text, and metadata fields.
README and PROVENANCE documentation
README.md, crates/xng-mode-iridium/PROVENANCE.md
Updates README to list IMS pager multipart reassembly and voice/IP-data tagging capabilities. Adds IMS pager + duplex traffic classes section to PROVENANCE documenting frame structure, format/sequence/checksum handling, RIC decoding, ASCII/BCD content variants, toolkit-derived frame-type mapping, oracle validation with vendored CI vector, multi-part assembly, and scope (AMBE extraction only, ITL frames undecoded).

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

  • airframesio/xng#29: Core Iridium decoder implementation that this PR extends with IMS pager messaging support and MS frame decoding logic.

Poem

🐰 A messenger hops through the airwaves bright,
Pager parts reassembled, multi-part in flight!
RIC by RIC, through 60 seconds we wait,
LCW voices sing their duplex fate,
bzzt goes the Iridium, decoded and whole! 🛰️

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The PR title clearly and specifically describes the main changes: adding IMS pager message decoding and voice/IP/sync traffic tagging for Iridium mode, which aligns directly with the changeset scope across documentation, parsing, reassembly, and console output.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch iridium-pager

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 8b9d79bbd6

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +217 to +218
if *ctr_max == 0 {
return Some(text.clone()); // single-part page

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Avoid duplicating single-part IMS pages

For any single-part ASCII page (ctr_max == 0), this returns the text as if a reassembly just completed; handle_bits then emits a synthetic msg-complete frame and still pushes the original msg frame, so normal one-frame pager messages show up twice in downstream outputs. This should only complete multipart pages, or the caller should suppress the raw frame when creating a completion frame.

Useful? React with 👍 / 👎.

Comment on lines +221 to +224
let entry = self
.pending
.entry(body.ric)
.or_insert_with(|| (vec![None; *ctr_max as usize + 1], time));

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Include sequence in the multipart page key

When two multipart ASCII pages for the same RIC overlap within the timeout, this single body.ric key lets parts from different seq values overwrite each other and can emit a corrupted concatenation, e.g. part 0 of message B plus part 1 of message A. Since MsBody already carries seq, the pending map needs to distinguish at least (ric, seq) for multipart reassembly.

Useful? React with 👍 / 👎.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (1)
crates/xng-mode-iridium/src/lib.rs (1)

279-284: 💤 Low value

Partial byte at end of payload may have incorrect interpretation.

chunks(8) will yield a partial chunk if payload.len() % 8 != 0. The fold will left-shift those fewer bits, producing a value as if the missing bits were zeros on the right. This may misrepresent the actual bit pattern for the last byte.

Consider using chunks_exact(8) if trailing bits should be excluded, or document that the last hex byte may be partial.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@crates/xng-mode-iridium/src/lib.rs` around lines 279 - 284, The current
payload -> payload_hex logic uses payload.chunks(8) which yields a partial final
chunk and the fold treats missing bits as zeros; change the code to use
payload.chunks_exact(8) and handle the remainder explicitly (using
chunks_exact(8).remainder()) so you either exclude the trailing bits or compute
the final byte correctly (e.g., by computing the folded value for the remainder
with the intended padding/bit-alignment), or if you intend to keep the current
semantics, add a comment documenting that the last hex byte may be partial;
update the code around payload.chunks(8) and payload_hex accordingly.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@crates/xng-mode-iridium/src/lib.rs`:
- Around line 279-284: The current payload -> payload_hex logic uses
payload.chunks(8) which yields a partial final chunk and the fold treats missing
bits as zeros; change the code to use payload.chunks_exact(8) and handle the
remainder explicitly (using chunks_exact(8).remainder()) so you either exclude
the trailing bits or compute the final byte correctly (e.g., by computing the
folded value for the remainder with the intended padding/bit-alignment), or if
you intend to keep the current semantics, add a comment documenting that the
last hex byte may be partial; update the code around payload.chunks(8) and
payload_hex accordingly.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: aad9a8e6-e894-4e0d-91a4-1d96b0c862de

📥 Commits

Reviewing files that changed from the base of the PR and between 33f2d46 and 8b9d79b.

📒 Files selected for processing (7)
  • README.md
  • crates/xng-mode-iridium/PROVENANCE.md
  • crates/xng-mode-iridium/examples/genframe.rs
  • crates/xng-mode-iridium/src/lib.rs
  • crates/xng-mode-iridium/src/ms.rs
  • crates/xng-mode-iridium/tests/e2e.rs
  • src/outputs/console.rs

@kevinelliott
kevinelliott merged commit 9e9acf0 into master Jun 11, 2026
2 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant