mirror of
https://github.com/bilawalsidhu/gods-eye-view.git
synced 2026-11-04 10:24:02 +00:00
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]>
156 lines
4.7 KiB
JavaScript
156 lines
4.7 KiB
JavaScript
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',
|
|
);
|
|
});
|