mirror of
https://github.com/bilawalsidhu/gods-eye-view.git
synced 2026-11-05 19:04:02 +00:00
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:
78 files changed
+12267
-108
No files matched your search
@@ -0,0 +1,155 @@
|
||||
import test from 'node:test';
|
||||
import assert from 'node:assert/strict';
|
||||
|
||||
import {
|
||||
internetRadioAudioActive,
|
||||
localSdrCardView,
|
||||
localSdrFeedLine,
|
||||
localSdrFmAudioActive,
|
||||
} from './localSdrPresentation.js';
|
||||
|
||||
const idle = Object.freeze({
|
||||
webUsbSupported: true,
|
||||
connected: false,
|
||||
status: 'idle',
|
||||
message: 'Connect an RTL-SDR to begin.',
|
||||
mode: 'fm',
|
||||
frequencyHz: 98_500_000,
|
||||
volume: 0.8,
|
||||
gain: 'auto',
|
||||
seeking: false,
|
||||
seekMessage: '',
|
||||
decodedMessages: 0,
|
||||
messagesPerSecond: null,
|
||||
aircraftHeard: 0,
|
||||
aircraftPositioned: 0,
|
||||
deviceLabel: null,
|
||||
locationStatus: 'unknown',
|
||||
audioState: 'idle',
|
||||
samplesPerSecond: 0,
|
||||
workerBlocks: 0,
|
||||
iqLevelDbfs: null,
|
||||
audioLevelDbfs: null,
|
||||
});
|
||||
|
||||
test('an idle card invites a connection and hides receiver stats', () => {
|
||||
const view = localSdrCardView(idle);
|
||||
assert.equal(view.statusText, 'Connect an RTL-SDR to begin.');
|
||||
assert.equal(view.connectLabel, 'CONNECT');
|
||||
assert.equal(view.connectDisabled, false);
|
||||
assert.equal(view.connectionLabel, 'IDLE');
|
||||
assert.equal(view.gainValue, 'auto');
|
||||
assert.equal(view.statsHidden, true);
|
||||
assert.equal(view.tuneDisabled, true);
|
||||
assert.equal(view.seekDisabled, true);
|
||||
assert.equal(view.frequencyValue, '98.5');
|
||||
const unsupported = localSdrCardView({
|
||||
...idle,
|
||||
webUsbSupported: false,
|
||||
status: 'unsupported',
|
||||
message: 'WebUSB is unavailable in this browser',
|
||||
});
|
||||
assert.equal(unsupported.connectDisabled, true);
|
||||
assert.equal(unsupported.changeDeviceDisabled, true);
|
||||
assert.equal(unsupported.statusError, true);
|
||||
});
|
||||
|
||||
test('streaming ADS-B shows message rate, heard, positioned and IQ level', () => {
|
||||
const view = localSdrCardView({
|
||||
...idle,
|
||||
connected: true,
|
||||
status: 'streaming',
|
||||
mode: 'adsb',
|
||||
message: 'Receiving 1090 MHz ADS-B',
|
||||
gain: 20.7,
|
||||
messagesPerSecond: 13.8,
|
||||
aircraftHeard: 4,
|
||||
aircraftPositioned: 2,
|
||||
iqLevelDbfs: -21.44,
|
||||
samplesPerSecond: 2_000_000,
|
||||
decodedMessages: 276,
|
||||
deviceLabel: 'FlyCatcher_ADS_B',
|
||||
});
|
||||
assert.equal(view.statsHidden, false);
|
||||
assert.deepEqual(view.stats, {
|
||||
rate: '13.8',
|
||||
heard: '4',
|
||||
positioned: '2',
|
||||
iq: '-21.4 dBFS',
|
||||
});
|
||||
assert.equal(view.gainValue, '20.7');
|
||||
assert.equal(view.connectionLabel, '4 HEARD');
|
||||
assert.equal(view.connectLabel, 'DISCONNECT');
|
||||
assert.equal(view.frequencyDisabled, true);
|
||||
assert.equal(view.volumeDisabled, true);
|
||||
assert.equal(
|
||||
view.statusText,
|
||||
'Receiving 1090 MHz ADS-B · FlyCatcher_ADS_B · 2.00 MS/s IQ · 276 messages',
|
||||
);
|
||||
});
|
||||
|
||||
test('local FM and internet radio audio states drive the one-listener rule', () => {
|
||||
assert.equal(localSdrFmAudioActive(idle), false);
|
||||
assert.equal(
|
||||
localSdrFmAudioActive({ ...idle, connected: true, status: 'streaming' }),
|
||||
true,
|
||||
);
|
||||
assert.equal(
|
||||
localSdrFmAudioActive({
|
||||
...idle,
|
||||
connected: true,
|
||||
status: 'streaming',
|
||||
mode: 'adsb',
|
||||
}),
|
||||
false,
|
||||
'ADS-B produces no audio',
|
||||
);
|
||||
for (const audioState of ['loading', 'buffering', 'playing'])
|
||||
assert.equal(internetRadioAudioActive({ audioState }), true);
|
||||
assert.equal(internetRadioAudioActive({ audioState: 'paused' }), false);
|
||||
assert.equal(internetRadioAudioActive(null), false);
|
||||
});
|
||||
|
||||
test('the decoder-feed line appears only when feeds are configured', () => {
|
||||
assert.equal(localSdrFeedLine(null), null);
|
||||
assert.equal(
|
||||
localSdrFeedLine({ configured: false, feeds: [], polling: false }),
|
||||
null,
|
||||
);
|
||||
const feeds = [
|
||||
{ band: '1090', label: '1090 MHz', status: 'live' },
|
||||
{ band: '978', label: '978 MHz UAT', status: 'unreachable' },
|
||||
];
|
||||
assert.equal(
|
||||
localSdrFeedLine({ configured: true, polling: true, feeds }),
|
||||
'Decoder feeds: 1090 live · 978 unreachable',
|
||||
);
|
||||
assert.equal(
|
||||
localSdrFeedLine({ configured: true, polling: false, feeds }),
|
||||
'Decoder feeds: 1090 · 978 · read while Local ADS-B is on',
|
||||
);
|
||||
assert.equal(
|
||||
localSdrFeedLine({
|
||||
configured: true,
|
||||
polling: false,
|
||||
feeds: [{ band: null, label: 'entry 2', status: 'invalid' }],
|
||||
}),
|
||||
'Decoder feeds: entry 2 invalid · read while Local ADS-B is on',
|
||||
);
|
||||
});
|
||||
|
||||
test('two feeds on one band are told apart by their ordinal labels', () => {
|
||||
const feeds = [
|
||||
{ band: '1090', label: '1090 MHz', status: 'live' },
|
||||
{ band: '978', label: '978 MHz UAT #1', status: 'live' },
|
||||
{ band: '978', label: '978 MHz UAT #2', status: 'stale' },
|
||||
];
|
||||
assert.equal(
|
||||
localSdrFeedLine({ configured: true, polling: true, feeds }),
|
||||
'Decoder feeds: 1090 live · 978 MHz UAT #1 live · 978 MHz UAT #2 stale',
|
||||
);
|
||||
assert.equal(
|
||||
localSdrFeedLine({ configured: true, polling: false, feeds }),
|
||||
'Decoder feeds: 1090 · 978 MHz UAT #1 · 978 MHz UAT #2 · read while Local ADS-B is on',
|
||||
);
|
||||
});
|
||||
Reference in new issue
Block a user