[fix](trx-frontend-http): measure auto squelch from the meter
Auto took the spectrum's noise floor and added 6 dB, but the threshold is compared against the channel level the meter reports, and the two sit a long way apart: the gap is set by the FFT size and window, the channel bandwidth, the decimation, and peak-versus-mean statistics. Measured on white noise it runs +22.1 dB at 48k/8k/3k, +18.7 dB at 240k/24k/12k and -1.2 dB at 1.92M/24k/12k — a 23 dB swing across ordinary configurations. Only the last of those is anywhere near right, so on a narrow span Auto set the gate some 20 dB below the noise and it never closed. It now reads the same number the DSP compares: the 20th percentile of the meter over the last ten seconds, plus 5 dB. The percentile keeps a burst of traffic inside the window from dragging the estimate up, and 5 dB clears the meter's own jitter, which measured 0.9-1.6 dB. Nothing in it converts between scales, so no part of the signal chain can put it out again. With no history yet — a fresh connection, a rig switch — it listens for a moment rather than refusing. The fixture gained a streaming /meter, without which there is nothing to measure, and the spectrum test pins auto to the meter it serves. Signed-off-by: Stan Grams <sjg@haxx.space>
This commit is contained in:
@@ -4727,6 +4727,7 @@ function render(update) {
|
||||
const sUnits = dbmToSUnits(update.status.rx.sig);
|
||||
sigLastSUnits = sUnits;
|
||||
sigLastDbm = update.status.rx.sig;
|
||||
recordSquelchMeterSample(update.status.rx.sig);
|
||||
const pct = sUnits <= 9 ? Math.max(0, Math.min(100, sUnits / 9 * 100)) : 100;
|
||||
signalBar.style.width = `${pct}%`;
|
||||
signalValue.innerHTML = formatSignal(sUnits);
|
||||
@@ -6369,12 +6370,28 @@ function setSdrSquelch(thresholdDb, enabled, options = {}) {
|
||||
renderSdrSquelch();
|
||||
if (options.submit !== false) submitSdrSquelch();
|
||||
}
|
||||
var SQUELCH_NOISE_WINDOW_MS = 1e4;
|
||||
var SQUELCH_NOISE_MARGIN_DB = 5;
|
||||
var SQUELCH_MEASURE_MS = 1500;
|
||||
var squelchMeterSamples = [];
|
||||
function recordSquelchMeterSample(db) {
|
||||
if (!isFiniteNumber(db)) return;
|
||||
const now = Date.now();
|
||||
squelchMeterSamples.push({ t: now, v: db });
|
||||
while (squelchMeterSamples[0] && now - squelchMeterSamples[0].t > SQUELCH_NOISE_WINDOW_MS) {
|
||||
squelchMeterSamples.shift();
|
||||
}
|
||||
}
|
||||
function squelchNoiseFloorDb() {
|
||||
const now = Date.now();
|
||||
const values = squelchMeterSamples.filter((sample) => now - sample.t <= SQUELCH_NOISE_WINDOW_MS).map((sample) => sample.v).sort((a, b) => a - b);
|
||||
if (values.length < 4) return null;
|
||||
return values[Math.floor((values.length - 1) * 0.2)] ?? null;
|
||||
}
|
||||
function autoSquelchThresholdDb() {
|
||||
const data = lastSpectrumData || window.lastSpectrumData;
|
||||
if (!data || !isNumericBins(data.bins) || data.bins.length === 0) return null;
|
||||
const noiseDb = estimateNoiseFloorDb(data.bins);
|
||||
if (noiseDb == null || !isFiniteNumber(noiseDb)) return null;
|
||||
return clampSdrSquelchDb(noiseDb + 6);
|
||||
const noiseDb = squelchNoiseFloorDb();
|
||||
if (noiseDb == null) return null;
|
||||
return clampSdrSquelchDb(noiseDb + SQUELCH_NOISE_MARGIN_DB);
|
||||
}
|
||||
function updateSdrSquelchControlVisibility() {
|
||||
if (!sdrSquelchWrapEl) return;
|
||||
@@ -6407,17 +6424,30 @@ if (sdrSquelchToggleBtn) {
|
||||
setSdrSquelch(sdrSquelchThresholdDb, !sdrSquelchEnabled);
|
||||
});
|
||||
}
|
||||
function applyAutoSquelch(threshold) {
|
||||
setSdrSquelch(threshold, true);
|
||||
showHint(`Squelch ${threshold} dB`, 1500);
|
||||
}
|
||||
var sdrSquelchAutoBtn = document.getElementById("sdr-squelch-auto");
|
||||
if (sdrSquelchAutoBtn) {
|
||||
sdrSquelchAutoBtn.addEventListener("click", () => {
|
||||
if (!sdrSquelchSupported) return;
|
||||
const threshold = autoSquelchThresholdDb();
|
||||
if (threshold == null) {
|
||||
showHint("No spectrum to measure the noise from", 1800);
|
||||
if (threshold != null) {
|
||||
applyAutoSquelch(threshold);
|
||||
return;
|
||||
}
|
||||
setSdrSquelch(threshold, true);
|
||||
showHint(`Squelch ${threshold} dB`, 1500);
|
||||
sdrSquelchAutoBtn.disabled = true;
|
||||
showHint("Measuring the noise…");
|
||||
setTimeout(() => {
|
||||
sdrSquelchAutoBtn.disabled = false;
|
||||
const measured = autoSquelchThresholdDb();
|
||||
if (measured == null) {
|
||||
showHint("No meter to measure the noise from", 1800);
|
||||
return;
|
||||
}
|
||||
applyAutoSquelch(measured);
|
||||
}, SQUELCH_MEASURE_MS);
|
||||
});
|
||||
}
|
||||
if (squelchGripEl) {
|
||||
@@ -8067,6 +8097,7 @@ function flushMeterDom() {
|
||||
const sUnits = dbmToSUnits(dbm);
|
||||
sigLastSUnits = sUnits;
|
||||
sigLastDbm = dbm;
|
||||
recordSquelchMeterSample(dbm);
|
||||
const pct = sUnits <= 9 ? Math.max(0, Math.min(100, sUnits / 9 * 100)) : 100;
|
||||
if (signalBar) signalBar.style.width = `${pct}%`;
|
||||
if (signalValue) signalValue.innerHTML = formatSignal(sUnits);
|
||||
|
||||
Reference in New Issue
Block a user