feat(sdr): local receivers — browser RTL-SDR, Local ADS-B layer and decoder feeds (#732)

Adds a Local ADS-B layer fed by your own receivers: a browser WebUSB
RTL-SDR (with FM listening) and local 1090/978 MHz decoder feeds
(LOCAL_RECEIVER_FEEDS, local and private addresses only). Local aircraft
draw in magenta with class, silhouettes, 3D models, trails and a position
sanity filter; the click card names the band and input that heard them.

Builds on Sameh Khamis's SDR prototype and the initial GEV port by
Mazeyar Moeini Feizabadi (#134); the decoder-feed design follows the
notes on #57. Closes #57.

Co-authored-by: Sameh Khamis <[email protected]>
Co-authored-by: Mazeyar Moeini Feizabadi <[email protected]>
This commit is contained in:
authored and GitHub committed 2026-09-23 16:00:53 -05:00
1 parent 143cffd7ca
commit 1fc795577a
78 files changed
+12267 -108

No files matched your search

+1
View File
@@ -138,6 +138,7 @@ const schemas = [
'telegeography-submarine-cables',
'local-firms',
'alpr-cameras',
'local-adsb',
],
},
enabled: {
+7 -2
View File
@@ -15,13 +15,14 @@ const stable = (value) =>
)
: value;
test('the complete Realtime tool payload pins the additive analyst and satellite release', () => {
test('the complete Realtime tool payload pins the additive analyst, satellite and Local ADS-B release', () => {
const digest = createHash('sha256')
.update(JSON.stringify(stable(GEV_REALTIME_TOOLS)))
.digest('hex');
assert.equal(
digest,
'9e33ac0fa5a860a17bb6d79f2c43646b6c36d0c932590bf114814e891a1c96de',
// Re-derived for the additive `local-adsb` set_layer_visibility value.
'07c2439e085cce9b9e1b36dfd01e2a688e66957db464c740627dd378536d904b',
);
});
@@ -86,6 +87,10 @@ test('all legacy action arguments are byte-identical after removing the delibera
layers.enum = layers.enum.filter(
(key) => !['satellites', 'local-datacenters', 'local-dams'].includes(key),
);
// Local ADS-B is an additive set_layer_visibility enum value.
const visibility = legacy.find((tool) => tool.name === 'set_layer_visibility')
.parameters.properties.layerId;
visibility.enum = visibility.enum.filter((key) => key !== 'local-adsb');
// Independently derived by executing trusted c9f9896 actionSchemas in the restricted container.
assert.equal(
createHash('sha256').update(JSON.stringify(legacy)).digest('hex'),
+6
View File
@@ -226,6 +226,12 @@ const LAYER_ALIASES = new Map([
['license plate readers', 'alpr-cameras'],
['license plate cameras', 'alpr-cameras'],
['plate readers', 'alpr-cameras'],
['local-adsb', 'local-adsb'],
['local adsb', 'local-adsb'],
['local ads-b', 'local-adsb'],
['my receiver', 'local-adsb'],
['my antenna', 'local-adsb'],
['my sdr', 'local-adsb'],
]);
const CITY_ALIASES = new Map([
+24
View File
@@ -3165,6 +3165,30 @@ test('ALPR common names toggle only the registered camera layer through the norm
}
});
test('Local ADS-B common names toggle only the receiver layer through the normal voice action', async () => {
globalThis.window = globalThis.window || { clearTimeout, setTimeout, requestIdleCallback: null };
const viewer = { clock: { onTick: { addEventListener: () => () => {} } },
scene: { canvas: { addEventListener() {}, removeEventListener() {} } },
camera: { moveEnd: { addEventListener() {} } } };
const calls = [];
let enabled = false;
const dataManager = {
layers: new Map([['local-adsb', { module: {} }]]),
getAll: () => [{ id: 'local-adsb', name: 'Local ADS-B' }],
isEnabled: () => enabled,
setEnabled: async (id, value) => { calls.push([id, value]); enabled = value; return true; },
};
const runner = createGevActionRunner({ viewer, styleManager: {}, dataManager });
for (const alias of ['local-adsb', 'local ADS-B', 'Local ADSB', 'my receiver', 'my antenna', 'my SDR']) {
for (const value of [true, false]) {
const result = await runner('set_layer_visibility', { layerId: alias, enabled: value });
assert.equal(result.ok, true);
assert.equal(result.layerId, 'local-adsb');
assert.deepEqual(calls.at(-1), ['local-adsb', value]);
}
}
});
test('ISS voice lookup uses the registered satellite instance', async () => {
const calls = [];
const viewer = {