diff --git a/docs/plans/2026-07-02-signal-identification.md b/docs/plans/2026-07-02-signal-identification.md new file mode 100644 index 0000000..6c0ab20 --- /dev/null +++ b/docs/plans/2026-07-02-signal-identification.md @@ -0,0 +1,1568 @@ +# Signal Identification Implementation Plan + +> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking. + +**Goal:** Build a bundled JSON signal database (~500 entries) with a scored match API route and a standalone modal overlay accessible from the waterfall and global nav. + +**Architecture:** `utils/signal_db.py` loads `data/signals.json` once at startup (lazy, cached). `routes/signalid.py` gains `POST /signalid/match` which calls the pure match function. `static/js/components/signal-id-modal.js` is a standalone IIFE modal called from both `waterfall.js` and a new nav button in `templates/partials/nav.html`. + +**Tech Stack:** Python 3.11+, Flask blueprints, vanilla JS (IIFE), JSON data file, existing CSS variables. + +## Global Constraints + +- All modulation tokens in `signals.json` must be uppercase strings (`WFM`, `FM`, `AM`, `USB`, `LSB`, `NFM`, `FSK`, `OOK`, `PSK`, etc.) +- All frequencies in `signals.json` stored as integers in Hz +- Match scores are 0–100 integer points +- Route added to existing `signalid_bp` blueprint in `routes/signalid.py`; no new blueprint +- No new Python dependencies — stdlib only +- JS follows IIFE pattern consistent with other components: `window.SignalIdModal = (function() { ... })()` +- Modal styled with existing CSS variables only (`--bg-card`, `--accent-cyan`, `--font-mono`, `--text-primary`, `--text-muted`, `--text-secondary`, `--accent-red`, `--bg-input`) +- `signal-id-modal.js` goes in `static/js/components/` (eagerly loaded, not lazy-loaded per mode) + +--- + +## File Map + +| Action | File | Responsibility | +|---|---|---| +| Create | `data/signals.json` | Bundled signal database | +| Create | `utils/signal_db.py` | Load + cache signals.json; pure `match_signals()` function | +| Create | `tests/test_signals_json.py` | Schema validation for every entry in signals.json | +| Create | `tests/test_signalid_match.py` | Unit tests for matching algorithm | +| Create | `static/js/components/signal-id-modal.js` | Signal ID modal IIFE component | +| Modify | `routes/signalid.py` | Add `POST /signalid/match` route | +| Modify | `config.py` | Add `REGION = _get_env("REGION", "GLOBAL")` | +| Modify | `templates/index.html` | Add eager ` +``` + +- [ ] **Step 3: Manually verify the modal renders correctly** + +Start the dev server: `sudo -E venv/bin/python intercept.py` + +Open the browser console and run: +```javascript +SignalIdModal.open({ frequency_mhz: 98.5, modulation: 'WFM' }) +``` + +Verify: +- Modal and backdrop appear +- Frequency field shows `98.5000` +- Modulation shows `WFM` +- Clicking "Search" fires a network request to `/signalid/match` +- Results render with score bars and match reasons +- FM Broadcast Radio appears as top result +- Clicking × or backdrop closes the modal + +Then run: `SignalIdModal.open({})` and verify the frequency field is blank and focused. + +- [ ] **Step 4: Commit** + +```bash +git add static/js/components/signal-id-modal.js templates/index.html +git commit -m "feat: add SignalIdModal IIFE component with scored results" +``` + +--- + +## Task 5: Nav button + waterfall integration + +**Files:** +- Modify: `templates/partials/nav.html` +- Modify: `templates/partials/modes/waterfall.html` +- Modify: `static/js/modes/waterfall.js` + +**Interfaces:** +- Consumes: `window.SignalIdModal.open(opts)` (Task 4) + +- [ ] **Step 1: Add "Signal ID" button to nav.html** + +In `templates/partials/nav.html`, find the Intel group `