[feat](trx-frontend-http): add LNA gain control UI and API

Add POST /set_sdr_lna_gain endpoint. Add an LNA Gain input+Set button
to the SDR settings row, styled identically to RF Gain. The control is
hidden until the server reports a sdr_lna_gain_db value (i.e. devices
that expose an LNA gain element). AGC disables it alongside RF Gain.

Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
Signed-off-by: Stanislaw Grams <stanislawgrams@gmail.com>
This commit is contained in:
2026-03-15 19:25:15 +01:00
parent 2ebd085aec
commit b7a4b8a1df
3 changed files with 51 additions and 3 deletions
@@ -2719,6 +2719,11 @@ function render(update) {
) { ) {
sdrGainEl.value = String(Math.round(update.filter.sdr_gain_db)); sdrGainEl.value = String(Math.round(update.filter.sdr_gain_db));
} }
if (sdrLnaGainEl && typeof update.filter.sdr_lna_gain_db === "number"
&& document.activeElement !== sdrLnaGainEl) {
sdrLnaGainEl.value = String(Math.round(update.filter.sdr_lna_gain_db));
if (sdrLnaGainControlsEl) sdrLnaGainControlsEl.style.display = "";
}
if (wfmDeemphasisEl && typeof update.filter.wfm_deemphasis_us === "number") { if (wfmDeemphasisEl && typeof update.filter.wfm_deemphasis_us === "number") {
wfmDeemphasisEl.value = String(update.filter.wfm_deemphasis_us); wfmDeemphasisEl.value = String(update.filter.wfm_deemphasis_us);
} }
@@ -6720,6 +6725,9 @@ const sdrSettingsRowEl = document.getElementById("sdr-settings-row");
const sdrGainControlsEl = document.getElementById("sdr-gain-controls"); const sdrGainControlsEl = document.getElementById("sdr-gain-controls");
const sdrGainEl = document.getElementById("sdr-gain-db"); const sdrGainEl = document.getElementById("sdr-gain-db");
const sdrGainSetBtn = document.getElementById("sdr-gain-set"); const sdrGainSetBtn = document.getElementById("sdr-gain-set");
const sdrLnaGainControlsEl = document.getElementById("sdr-lna-gain-controls");
const sdrLnaGainEl = document.getElementById("sdr-lna-gain-db");
const sdrLnaGainSetBtn = document.getElementById("sdr-lna-gain-set");
const sdrAgcEl = document.getElementById("sdr-agc-enabled"); const sdrAgcEl = document.getElementById("sdr-agc-enabled");
const wfmStFlagEl = document.getElementById("wfm-st-flag"); const wfmStFlagEl = document.getElementById("wfm-st-flag");
const sdrSquelchWrapEl = document.getElementById("sdr-squelch-wrap"); const sdrSquelchWrapEl = document.getElementById("sdr-squelch-wrap");
@@ -6896,10 +6904,12 @@ function submitSdrGain() {
postPath(`/set_sdr_gain?db=${encodeURIComponent(parsed)}`).catch(() => {}); postPath(`/set_sdr_gain?db=${encodeURIComponent(parsed)}`).catch(() => {});
} }
function updateSdrGainInputState() { function updateSdrGainInputState() {
if (!sdrAgcEl || !sdrGainEl || !sdrGainSetBtn) return; if (!sdrAgcEl) return;
const agcOn = sdrAgcEl.checked; const agcOn = sdrAgcEl.checked;
sdrGainEl.disabled = agcOn; if (sdrGainEl) sdrGainEl.disabled = agcOn;
sdrGainSetBtn.disabled = agcOn; if (sdrGainSetBtn) sdrGainSetBtn.disabled = agcOn;
if (sdrLnaGainEl) sdrLnaGainEl.disabled = agcOn;
if (sdrLnaGainSetBtn) sdrLnaGainSetBtn.disabled = agcOn;
} }
if (sdrAgcEl) { if (sdrAgcEl) {
sdrAgcEl.addEventListener("change", () => { sdrAgcEl.addEventListener("change", () => {
@@ -6918,6 +6928,23 @@ if (sdrGainEl) {
} }
}); });
} }
function submitSdrLnaGain() {
if (!sdrLnaGainEl) return;
const parsed = Number.parseFloat(sdrLnaGainEl.value);
if (!Number.isFinite(parsed) || parsed < 0) return;
postPath(`/set_sdr_lna_gain?db=${encodeURIComponent(parsed)}`).catch(() => {});
}
if (sdrLnaGainSetBtn) {
sdrLnaGainSetBtn.addEventListener("click", submitSdrLnaGain);
}
if (sdrLnaGainEl) {
sdrLnaGainEl.addEventListener("keydown", (ev) => {
if (ev.key === "Enter") {
ev.preventDefault();
submitSdrLnaGain();
}
});
}
function updateWfmControls() { function updateWfmControls() {
if (!wfmControlsCol) return; if (!wfmControlsCol) return;
const mode = (modeEl && modeEl.value ? modeEl.value : "").toUpperCase(); const mode = (modeEl && modeEl.value ? modeEl.value : "").toUpperCase();
@@ -268,6 +268,13 @@
</label> </label>
<button id="sdr-gain-set" type="button" class="wfm-inline-btn">Set</button> <button id="sdr-gain-set" type="button" class="wfm-inline-btn">Set</button>
</div> </div>
<div class="wfm-gain-group" id="sdr-lna-gain-controls">
<label class="wfm-control">
<span class="wfm-control-label">LNA Gain</span>
<input id="sdr-lna-gain-db" class="status-input" type="number" min="0" max="60" step="1" inputmode="decimal">
</label>
<button id="sdr-lna-gain-set" type="button" class="wfm-inline-btn">Set</button>
</div>
</div> </div>
</div> </div>
<div class="full-row label-below-row" id="vchan-row"> <div class="full-row label-below-row" id="vchan-row">
@@ -873,6 +873,19 @@ pub async fn set_sdr_gain(
send_command(&rig_tx, RigCommand::SetSdrGain(query.db)).await send_command(&rig_tx, RigCommand::SetSdrGain(query.db)).await
} }
#[derive(serde::Deserialize)]
pub struct SdrLnaGainQuery {
pub db: f64,
}
#[post("/set_sdr_lna_gain")]
pub async fn set_sdr_lna_gain(
query: web::Query<SdrLnaGainQuery>,
rig_tx: web::Data<mpsc::Sender<RigRequest>>,
) -> Result<HttpResponse, Error> {
send_command(&rig_tx, RigCommand::SetSdrLnaGain(query.db)).await
}
#[derive(serde::Deserialize)] #[derive(serde::Deserialize)]
pub struct SdrAgcQuery { pub struct SdrAgcQuery {
pub enabled: bool, pub enabled: bool,
@@ -1546,6 +1559,7 @@ pub fn configure(cfg: &mut web::ServiceConfig) {
.service(set_bandwidth) .service(set_bandwidth)
.service(set_fir_taps) .service(set_fir_taps)
.service(set_sdr_gain) .service(set_sdr_gain)
.service(set_sdr_lna_gain)
.service(set_sdr_agc) .service(set_sdr_agc)
.service(set_sdr_squelch) .service(set_sdr_squelch)
.service(set_wfm_deemphasis) .service(set_wfm_deemphasis)