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

+48
View File
@@ -0,0 +1,48 @@
class GevSdrAudioPlayer extends AudioWorkletProcessor {
constructor() {
super();
this.queue = [];
this.offset = 0;
this.volume = 0.8;
this.port.onmessage = (event) => {
if (
event.data?.type === 'samples' &&
event.data.samples instanceof Float32Array
) {
this.queue.push(event.data.samples);
if (this.queue.length > 48)
this.queue.splice(0, this.queue.length - 48);
} else if (event.data?.type === 'volume') {
this.volume = Math.max(0, Math.min(1, Number(event.data.value) || 0));
} else if (event.data?.type === 'clear') {
this.queue.length = 0;
this.offset = 0;
}
};
}
process(_inputs, outputs) {
const output = outputs[0]?.[0];
if (!output) return true;
output.fill(0);
let outputOffset = 0;
while (outputOffset < output.length && this.queue.length) {
const samples = this.queue[0];
const available = samples.length - this.offset;
const count = Math.min(output.length - outputOffset, available);
for (let index = 0; index < count; index += 1) {
output[outputOffset + index] =
samples[this.offset + index] * this.volume;
}
outputOffset += count;
this.offset += count;
if (this.offset >= samples.length) {
this.queue.shift();
this.offset = 0;
}
}
return true;
}
}
registerProcessor('gev-sdr-audio-player', GevSdrAudioPlayer);