Skip to content

iridium wideband: matched filter on by default (~17x IDA yield)#172

Merged
kevinelliott merged 2 commits into
masterfrom
iridium-wideband-sensitivity
Jun 16, 2026
Merged

iridium wideband: matched filter on by default (~17x IDA yield)#172
kevinelliott merged 2 commits into
masterfrom
iridium-wideband-sensitivity

Conversation

@kevinelliott

@kevinelliott kevinelliott commented Jun 16, 2026

Copy link
Copy Markdown
Contributor

Round 2+4 of the Iridium wideband sensitivity campaign (vs a gr-iridium A/B on identical IQ). The ~50x burst-yield gap is the per-burst demod, not detection.

Change

Adds a root-raised-cosine matched filter (matched to Iridium's RRC transmit pulse; gr-iridium uses RRC α0.4, 51 taps) to the wideband extract path, reusing modulate::rrc_taps.

It's applied as a fallback: demodulate the unfiltered burst first, fall back to the matched-filtered burst only when the clean path finds nothing. Strong/clean bursts decode unfiltered (bit-exact, no penalty — keeps the fixture tests green); weak bursts the clean path misses get the matched filter's few-dB lift above the acquisition gate. So it's on by default with no regression (XNG_IRIDIUM_RC_ALPHA=0 disables).

Measured (real benchmark capture, 300s, Airspy R2 @ 1622 MHz/10 MS/s)

CRC-OK IDA vs gr-iridium (484)
before ~10 ~2%
after 171 35%

Live soak confirmed IDA climbing 2.8 → ~18/min after enabling it.

Also adds env tuning knobs for the ongoing campaign (defaults preserve behavior): XNG_IRIDIUM_RC_ALPHA, XNG_IRIDIUM_UWCOST, XNG_IRIDIUM_GATE, XNG_IRIDIUM_DEDUP_BINS, XNG_IRIDIUM_THRESHOLD.

Testing

  • Full xng-mode-iridium suite green (incl. the wideband fixture/synthetic tests that pinned clean-burst decode).
  • Full workspace test suite green.

Next rounds will push further toward gr-iridium's yield (demod acquisition: correlation-based UW, finer timing).

🤖 Generated with Claude Code

Summary by CodeRabbit

Release Notes

  • New Features

    • Iridium demodulation parameters are now tunable via environment variables, enabling advanced users to optimize signal processing for specific conditions.
  • Improvements

    • Enhanced burst detection and extraction algorithms with adaptive thresholds and optional signal processing enhancements.
    • Better handling of edge cases in the demodulation chain.

kevinelliott and others added 2 commits June 15, 2026 19:56
Round 2 of the Iridium sensitivity campaign (vs gr-iridium A/B). The ~50x
yield gap is the per-burst demod's acquisition, not detection. Adding a
root-raised-cosine matched filter (matched to Iridium's RRC transmit pulse,
gr-iridium-style, alpha 0.4) to the wideband extract path lifts CRC-OK IDA
yield ~17x on the real benchmark capture (10 -> 171 over 300s; 35% of
gr-iridium's 484, up from ~2%).

It is opt-in (XNG_IRIDIUM_RC_ALPHA=0.4) for now: the strict UW-cost/timing
acquisition still drops some *clean* high-SNR bursts after filtering (it was
tuned for unfiltered input), which regresses the synthetic/fixture wideband
tests. Default off keeps those green. Round 3 re-tunes the demod for
matched-filtered input, then makes it default.

Also adds env tuning knobs for the campaign (defaults preserve current
behavior): XNG_IRIDIUM_UWCOST, XNG_IRIDIUM_GATE, XNG_IRIDIUM_DEDUP_BINS,
XNG_IRIDIUM_THRESHOLD, XNG_IRIDIUM_RC_ALPHA.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…7x IDA)

Round 4 of the sensitivity campaign. The RRC matched filter (Round 2) lifts
CRC-OK IDA yield ~17x on real captures but slightly raises the UW-acquisition
cost on *clean* high-SNR bursts, which regressed the fixture tests when it was
applied unconditionally.

Fix: in the wideband extract path, demodulate the *unfiltered* burst first and
fall back to the matched-filtered burst only when the clean path finds nothing.
Strong bursts decode unfiltered (bit-exact, no MF penalty); weak bursts the
clean path misses get the matched filter's few-dB lift above the acquisition
gate. Net: no regression on strong bursts, big gain on weak ones — so the
matched filter is now ON BY DEFAULT (XNG_IRIDIUM_RC_ALPHA=0 disables it).

Measured on the real benchmark capture: ~10 -> 171 CRC-OK IDA over 300s (35%
of gr-iridium's 484, up from ~2%); full iridium + workspace test suites green.
Live soak (Airspy R2) showed IDA climb 2.8 -> ~18/min after enabling it.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Jun 16, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

Two Iridium mode crates receive configurable tuning parameters sourced from environment variables. IridiumDemod gains uw_thresh and gate_mult fields replacing hardcoded constants in burst gating and UW fit acceptance. IridiumWideband gains det_threshold, dedup_bins, and mf_taps, adds a matched_filter convolution helper, and introduces an RRC matched-filter retry path in burst extraction. A test output file records two new diagnostic lines.

Changes

Iridium Configurable Parameters and Matched-Filter

Layer / File(s) Summary
IridiumDemod env-var fields and usage sites
crates/xng-mode-iridium/src/demod.rs
Adds env_f32 helper; extends IridiumDemod with uw_thresh and gate_mult initialized from XNG_IRIDIUM_UWCOST and XNG_IRIDIUM_GATE; replaces hardcoded constants in burst gate check, burst_gate computation, and UW fit acceptance comparison.
IridiumWideband fields, init, and matched_filter fn
crates/xng-mode-iridium/src/wideband.rs
Adds matched_filter centered zero-padded convolution; extends IridiumWideband struct with det_threshold, dedup_bins, and mf_taps; initializes all three from env vars including an optional RRC tap build for XNG_IRIDIUM_RC_ALPHA.
Wideband detection, dedup, extract retry, and noise floor
crates/xng-mode-iridium/src/wideband.rs
Replaces fixed hot-bin threshold with det_threshold, fixed dedup width with dedup_bins; adds MF-filtered retry in extract when raw demodulation returns no burst; refactors demod_channel noise seeding to use a 20th-percentile power estimate.
Test diagnostic output
test.parsed
Adds two lines recording an fft_burst_tagger parse failure (set_min_output_buffer on block 2) and an unknown IridiumMessage type.

Sequence Diagram(s)

sequenceDiagram
  participant Env as Env Vars
  participant IridiumWideband as IridiumWideband::new
  participant extract as extract()
  participant matched_filter as matched_filter()
  participant demod_channel as demod_channel()
  participant IridiumDemod as IridiumDemod

  IridiumWideband->>Env: XNG_IRIDIUM_DET, XNG_IRIDIUM_DEDUP, XNG_IRIDIUM_RC_ALPHA
  Env-->>IridiumWideband: det_threshold, dedup_bins, mf_taps
  Note over IridiumWideband: hot-bin check uses det_threshold<br/>dedup uses dedup_bins
  extract->>demod_channel: raw channel samples + freq_offset
  demod_channel->>IridiumDemod: seed with 20th-pct noise estimate
  IridiumDemod-->>demod_channel: Option<Burst>
  demod_channel-->>extract: None
  alt mf_taps non-empty
    extract->>matched_filter: channel samples + mf_taps
    matched_filter-->>extract: filtered samples
    extract->>demod_channel: filtered samples + freq_offset
    demod_channel-->>extract: Option<Burst>
  end
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

  • airframesio/xng#29: Introduced the core IridiumDemod burst acquisition and UW-fit logic in demod.rs that this PR directly extends by making its thresholds env-var configurable.
  • airframesio/xng#126: Modified IridiumWideband's per-bin noise floor warm-up in wideband.rs, overlapping with this PR's changes to hot-bin detection thresholding in the same detection workflow.

Poem

🐇 Hop, hop, no more magic numbers in sight,
Env vars now tune the demod just right.
A matched filter springs from a cosine so round,
Retry the burst — new signals are found!
The rabbit adjusts every gate with a tweak,
And percentile noise makes the floor less bleak. ✨

🚥 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 title accurately reflects the main change: adding a matched filter to Iridium wideband burst extraction with significant measured yield improvement (~17x). It captures the primary contribution and quantified impact.
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-wideband-sensitivity

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.

@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.

Actionable comments posted: 1

🤖 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.

Inline comments:
In `@test.parsed`:
- Around line 1-2: The file test.parsed at the repository root is diagnostic
output from a failed parse that was accidentally committed. Remove this file
from the repository by deleting it. Alternatively, if test.parsed is generated
during test runs as a diagnostic artifact, add it to the .gitignore file instead
to prevent future accidental commits. The proper location for test fixtures and
data is crates/xng-mode-iridium/tests/data/ with appropriate documentation.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: a87301a3-3c52-4972-a51b-9d6ffb94ce77

📥 Commits

Reviewing files that changed from the base of the PR and between 595d930 and 614269f.

📒 Files selected for processing (3)
  • crates/xng-mode-iridium/src/demod.rs
  • crates/xng-mode-iridium/src/wideband.rs
  • test.parsed

Comment thread test.parsed
Comment on lines +1 to +2
ERR: ERR:Message: Couldn't parse: fft_burst_tagger :info: set_min_output_buffer on block 2 to 65536
RAW: p-1601421246-e000 000000599.9996 1622000000 100% -17.36|-080.05|32.12 179 DL <001100000011000011110011> 0100000000000011 0000000000000101 0000000000111100 0000000001000100 0000001100110000 0000010101010000 0011111111000000 0100000001000011 0000001100000101 0000010100111100 0011110001000100 0100011100110011 0011010101010101 0110111111111111 1000000000000011 0100000000000110 0000000000111001 0000000001111000 0000001101110100 0000011001100000 0011101010010000 0111111110000011 010000 ERR:IridiumMessage: unknown Iridium message type

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Check if test.parsed is referenced in tests or documentation
rg -l 'test\.parsed' --type rust --type md

# Check if there's a gitignore entry for similar files
cat .gitignore 2>/dev/null | grep -i 'parsed\|test\.' || echo "No matching gitignore entries"

# Check if this file pattern appears elsewhere
fd -e parsed

Repository: airframesio/xng

Length of output: 101


🏁 Script executed:

#!/bin/bash
# Find the exact location of test.parsed
find . -name 'test.parsed' -type f

# Check the directory structure around it
if [ -f test.parsed ]; then
  ls -la test.parsed
  head -n 5 test.parsed
fi

# Look for test infrastructure/fixtures directories
fd -t d 'test|fixture|spec' | head -20

# Check if there's a tests directory and what's in it
if [ -d tests ]; then
  ls -la tests/ | head -20
fi

# Check for test configuration files
fd -e toml -e yaml -e yml | grep -i test || true

Repository: airframesio/xng

Length of output: 1024


🏁 Script executed:

#!/bin/bash
# Check the iridium test directory since the file contains Iridium message data
if [ -d crates/xng-mode-iridium/tests ]; then
  ls -la crates/xng-mode-iridium/tests/
  echo "---"
  # Look for any references to "test.parsed" in iridium tests
  grep -r 'test\.parsed' crates/xng-mode-iridium/ 2>/dev/null || echo "No references found"
fi

# Check git status and history for this file
echo "=== Git info for test.parsed ==="
git log --oneline test.parsed 2>/dev/null | head -5 || echo "No git history"
git status test.parsed 2>/dev/null || echo "Not in git"

# Check if there are other fixture files or expected output files
fd -e txt -e out -e expected -e fixture | grep -i test

Repository: airframesio/xng

Length of output: 804


🏁 Script executed:

#!/bin/bash
# Check what's in the data directory
echo "=== Contents of crates/xng-mode-iridium/tests/data ==="
ls -la crates/xng-mode-iridium/tests/data/

# Look for fixture/test data files with similar patterns
echo "=== Looking for similar test data files ==="
find crates/xng-mode-iridium/tests -type f | head -20

# Check if test.parsed at root is related to iridium by searching for usage in test files
echo "=== Checking test files for parsing or expected output patterns ==="
grep -r "\.parsed\|test_.*output\|expected.*message" crates/xng-mode-iridium/tests/*.rs 2>/dev/null | head -10

# Check the commit that added test.parsed to understand context
git show 614269f --stat 2>/dev/null | head -20

Repository: airframesio/xng

Length of output: 1974


🏁 Script executed:

#!/bin/bash
# Check if there's any reference to "test.parsed" in all source files
echo "=== All references to test.parsed or similar patterns ==="
grep -r "test\.parsed\|\.parsed" --include="*.rs" --include="*.toml" --include="*.md" . 2>/dev/null | head -20

# Look at the iridium test data README to understand the test setup
echo "=== Test data README ==="
cat crates/xng-mode-iridium/tests/data/README.md

# Check if there's documentation in the root or main crates about test outputs
echo "=== Root level test-related files ==="
ls -la | grep -i test

# Check Cargo.toml for test configuration
echo "=== Cargo.toml in xng-mode-iridium ==="
head -30 crates/xng-mode-iridium/Cargo.toml

Repository: airframesio/xng

Length of output: 1555


Remove accidentally committed test output file.

test.parsed is diagnostic parse failure output at the repository root with no documentation, no references in tests or source code, and no gitignore entry. It does not belong in the repository. The project maintains proper test fixtures in crates/xng-mode-iridium/tests/data/ with documentation (see README.md); this file should either be deleted or added to .gitignore if it's generated during test runs.

🤖 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 `@test.parsed` around lines 1 - 2, The file test.parsed at the repository root
is diagnostic output from a failed parse that was accidentally committed. Remove
this file from the repository by deleting it. Alternatively, if test.parsed is
generated during test runs as a diagnostic artifact, add it to the .gitignore
file instead to prevent future accidental commits. The proper location for test
fixtures and data is crates/xng-mode-iridium/tests/data/ with appropriate
documentation.

@kevinelliott
kevinelliott merged commit 49a6760 into master Jun 16, 2026
3 checks passed
@kevinelliott
kevinelliott deleted the iridium-wideband-sensitivity branch June 16, 2026 03:31
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