## Summary
Adds asymmetric overlap percentages to the existing observer compare
page so it can be used as a **reference observer comparison** tool
(Uncle Lit's request, #671).
## What changed
`public/compare.js` (frontend only — no backend changes)
- New `computeOverlapStats(cmp)` helper that turns a
`comparePacketSets()` result into two-way coverage:
- `aSeesOfB` — % of B's packets that A also saw
- `bSeesOfA` — % of A's packets that B also saw
- plus shared / onlyA / onlyB / totalA / totalB
- Two callout cards on the compare summary view:
- `<A> saw N of <B>'s X packets` (Y%)
- `<B> saw N of <A>'s X packets` (Y%)
- Existing "Only A / Only B / Both" tabs already identify unique
packets; that's the second half of the issue and is left intact.
## Operator workflow
Pick a known-good observer (LOS to key nodes) as the reference. Pair it
with a candidate. If the candidate's overlap with the reference is high
→ healthy. If low → investigate antenna, obstruction, or RF deafness.
## Out of scope (future work)
Issue lists several follow-on milestones — full Analytics sub-tab with
reference-vs-many table, SNR delta, geographic proximity filter,
server-side `/api/analytics/observer-comparison` endpoint. Those are
larger and tracked by the issue's M1-M4 milestones; this PR closes the
core ask (asymmetric overlap on the existing compare page) and leaves
the rest for follow-ups.
## Tests
`test-compare-overlap.js` — 6 unit tests via vm sandbox:
- exposes `computeOverlapStats` on `window`
- basic asymmetric scenario (8/10 vs 8/12)
- zero packets — no division by zero
- one observer empty — both percentages 0
- perfect overlap — 100% both ways
- disjoint observers — 0% both ways
TDD: red commit landed first with stub returning zeros (assertions
failed), green commit added the math.
Closes#671
---------
Co-authored-by: bot <bot@corescope.local>
## Summary
Adds a **Flood / Direct packet filter** dropdown to the observer
comparison page. This addresses the issue that direct packets (heard by
only one observer) skew the comparison percentages.
## Changes
- **`public/compare.js`**: Added `filterPacketsByRoute(packets, mode)`
function and a "Packet Type" dropdown (All / Flood only / Direct only)
to the comparison controls. Changing the filter re-runs the comparison
with filtered packets.
- **`test-compare-flood-filter.js`**: Unit tests for the filter
function.
## Route type mapping (from firmware)
| Route Type | Value | Filter |
|---|---|---|
| TransportFlood | 0 | Flood |
| Flood | 1 | Flood |
| Direct | 2 | Direct |
| TransportDirect | 3 | Direct |
## TDD
- Red commit: `484fa72` (test only, fails)
- Green commit: `5661f71` (implementation, tests pass)
Fixes#928
---------
Co-authored-by: you <you@example.com>
#210: Add role="img" aria-label to 9 Chart.js canvases in node-analytics.js
and observer-detail.js with descriptive labels.
#211: Add scope="col" to all <th> elements across analytics.js, audio-lab.js,
compare.js, node-analytics.js, nodes.js, observer-detail.js, observers.js,
and packets.js (40+ headers).
#212: Add aria-label to packet filter input and time window select in
packets.js. Add for/id associations to all customize.js inputs: branding,
theme colors, node/type colors, heatmap sliders, onboarding fields, and
export controls.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Add #/compare page that lets users select two observers and compare
which packets each sees. Fetches last 24h of packets per observer,
computes set diff client-side using O(n) Set lookups. Shows summary
cards (both/only-A/only-B), stacked bar, type breakdown, and tabbed
detail tables. URL is shareable via ?a=ID1&b=ID2 query params.
- New file: public/compare.js (comparePacketSets + page module)
- Added compare button to observers page header
- 11 new tests for comparePacketSets (87 total frontend tests)
- Cache busters bumped
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>