[feat](trx-rs): remove MARINE mode (superseded by virtual channels)

MARINE was a composite mode that ran both AIS and VDES decoders
simultaneously. It is now fully replaced by allocating two virtual
channels — one tuned to the AIS frequencies and one to VDES — each
decoded independently.

- trx-core/state: remove RigMode::MARINE variant
- trx-protocol/codec: remove MARINE parse/serialize
- trx-backend-ft817: remove MARINE from unsupported-mode guard
- trx-backend-ft450d: remove MARINE from FM CAT code mapping
- trx-backend-soapysdr: remove MARINE from bandwidth table, supported
  modes list, AIS channel activity check, parse_rig_mode, vchan_impl
  bandwidth table, demod selection, dsp/channel bandwidth / sample-rate
  / IQ-tap guards
- trx-server/audio: remove MARINE from AIS and VDES decoder activation
- trx-server/rig_task: remove MARINE from audio-streaming mode list
- trx-server/main: remove MARINE from bandwidth table, mode parser,
  VDES channel subscription match
- app.js: remove isMarineMode(), MARINE entry in MODE_BW_SPECS, MARINE
  bandwidth specs block in visibleBandwidthSpecs(), MARINE from
  decoder status mode lists, MARINE BW-edge drag guard

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Signed-off-by: Stan Grams <sjg@haxx.space>
This commit is contained in:
2026-03-11 20:58:20 +01:00
parent 60267d450b
commit 19ab3b3931
12 changed files with 25 additions and 42 deletions
+8 -8
View File
@@ -1102,7 +1102,7 @@ fn downmix_if_needed(frame: Vec<f32>, channels: u16) -> Vec<f32> {
mono
}
/// Run the AIS decoder task. Only processes PCM when rig mode is AIS or MARINE.
/// Run the AIS decoder task. Only processes PCM when rig mode is AIS.
pub async fn run_ais_decoder(
sample_rate: u32,
channels: u16,
@@ -1118,7 +1118,7 @@ pub async fn run_ais_decoder(
let mut was_active = false;
let mut active = matches!(
state_rx.borrow().status.mode,
RigMode::AIS | RigMode::MARINE
RigMode::AIS
);
loop {
@@ -1126,7 +1126,7 @@ pub async fn run_ais_decoder(
match state_rx.changed().await {
Ok(()) => {
let state = state_rx.borrow();
active = matches!(state.status.mode, RigMode::AIS | RigMode::MARINE);
active = matches!(state.status.mode, RigMode::AIS);
if active {
pcm_a_rx = pcm_a_rx.resubscribe();
pcm_b_rx = pcm_b_rx.resubscribe();
@@ -1178,7 +1178,7 @@ pub async fn run_ais_decoder(
match changed {
Ok(()) => {
let state = state_rx.borrow();
active = matches!(state.status.mode, RigMode::AIS | RigMode::MARINE);
active = matches!(state.status.mode, RigMode::AIS);
if !active && was_active {
decoder_a.reset();
decoder_b.reset();
@@ -1196,7 +1196,7 @@ pub async fn run_ais_decoder(
}
}
/// Run the VDES decoder task. Only processes IQ when rig mode is VDES or MARINE.
/// Run the VDES decoder task. Only processes IQ when rig mode is VDES.
pub async fn run_vdes_decoder(
sample_rate: u32,
mut iq_rx: broadcast::Receiver<Vec<Complex<f32>>>,
@@ -1209,7 +1209,7 @@ pub async fn run_vdes_decoder(
let mut was_active = false;
let mut active = matches!(
state_rx.borrow().status.mode,
RigMode::VDES | RigMode::MARINE
RigMode::VDES
);
loop {
@@ -1217,7 +1217,7 @@ pub async fn run_vdes_decoder(
match state_rx.changed().await {
Ok(()) => {
let state = state_rx.borrow();
active = matches!(state.status.mode, RigMode::VDES | RigMode::MARINE);
active = matches!(state.status.mode, RigMode::VDES);
if active {
iq_rx = iq_rx.resubscribe();
}
@@ -1250,7 +1250,7 @@ pub async fn run_vdes_decoder(
match changed {
Ok(()) => {
let state = state_rx.borrow();
active = matches!(state.status.mode, RigMode::VDES | RigMode::MARINE);
active = matches!(state.status.mode, RigMode::VDES);
if !active && was_active {
decoder.reset();
was_active = false;
+2 -3
View File
@@ -246,7 +246,7 @@ fn default_audio_bandwidth_for_mode(mode: &trx_core::rig::state::RigMode) -> u32
RigMode::FM => 12_500,
RigMode::WFM => 180_000,
RigMode::AIS => 25_000,
RigMode::VDES | RigMode::MARINE => 100_000,
RigMode::VDES => 100_000,
RigMode::Other(_) => 3_000,
}
}
@@ -269,7 +269,6 @@ fn parse_rig_mode(
"FM" => RigMode::FM,
"AIS" => RigMode::AIS,
"VDES" => RigMode::VDES,
"MARINE" => RigMode::MARINE,
"DIG" => RigMode::DIG,
"PKT" => RigMode::PKT,
_ => initial_mode.clone(),
@@ -362,7 +361,7 @@ fn build_sdr_rig_from_instance(rig_cfg: &RigInstanceConfig) -> SdrRigBuildResult
// explicit VDES channel has been configured.
let vdes_channel_idx = channels
.iter()
.position(|(_, mode, _, _)| matches!(mode, trx_core::rig::state::RigMode::VDES | trx_core::rig::state::RigMode::MARINE))
.position(|(_, mode, _, _)| matches!(mode, trx_core::rig::state::RigMode::VDES))
.unwrap_or(0);
let vdes_iq = sdr_rig.subscribe_iq_channel(vdes_channel_idx);
// Extract the virtual channel manager before the rig is consumed by Box.
+1 -1
View File
@@ -845,7 +845,7 @@ fn map_signal_strength(mode: &RigMode, raw: u8) -> i32 {
// FT-817 returns 0-15 for signal strength
// Map to approximate dBm / S-units
match mode {
RigMode::FM | RigMode::WFM | RigMode::AIS | RigMode::VDES | RigMode::MARINE => {
RigMode::FM | RigMode::WFM | RigMode::AIS | RigMode::VDES => {
-120 + (raw as i32 * 6)
}
_ => -127 + (raw as i32 * 6),
@@ -513,7 +513,7 @@ fn encode_mode(mode: &RigMode) -> DynResult<char> {
RigMode::USB => Ok('2'),
RigMode::CW => Ok('3'),
RigMode::FM => Ok('4'),
RigMode::AIS | RigMode::VDES | RigMode::MARINE => Ok('4'),
RigMode::AIS | RigMode::VDES => Ok('4'),
RigMode::AM => Ok('5'),
RigMode::DIG => Ok('6'),
RigMode::CWR => Ok('7'),
@@ -603,7 +603,7 @@ fn encode_mode(mode: &RigMode) -> Option<u8> {
RigMode::FM => 0x08,
RigMode::DIG => 0x0A,
RigMode::PKT => 0x0C,
RigMode::AIS | RigMode::VDES | RigMode::MARINE | RigMode::Other(_) => return None,
RigMode::AIS | RigMode::VDES | RigMode::Other(_) => return None,
})
}
@@ -156,7 +156,7 @@ impl Demodulator {
RigMode::AM => Self::Am,
RigMode::FM => Self::Fm,
RigMode::WFM => Self::Wfm,
RigMode::AIS | RigMode::VDES | RigMode::MARINE => Self::Fm,
RigMode::AIS | RigMode::VDES => Self::Fm,
RigMode::CW | RigMode::CWR => Self::Cw,
RigMode::DIG => Self::Passthrough,
// VHF/UHF packet radio (APRS, AX.25) is FM-encoded AFSK.
@@ -127,7 +127,7 @@ fn default_bandwidth_for_mode(mode: &RigMode) -> u32 {
RigMode::FM => 12_500,
RigMode::WFM => 180_000,
RigMode::AIS => 25_000,
RigMode::VDES | RigMode::MARINE => 100_000,
RigMode::VDES => 100_000,
RigMode::Other(_) => 3_000,
}
}
@@ -199,7 +199,7 @@ impl ChannelDsp {
let target_rate = match mode {
RigMode::WFM => audio_bandwidth_hz.max(audio_sample_rate.saturating_mul(4)),
RigMode::VDES | RigMode::MARINE => audio_sample_rate.max(96_000),
RigMode::VDES => audio_sample_rate.max(96_000),
_ => audio_sample_rate.max(1),
};
let decim_factor = (sdr_sample_rate / target_rate.max(1)).max(1) as usize;
@@ -466,7 +466,7 @@ impl ChannelDsp {
self.scratch_decimated
.reserve(capacity - self.scratch_decimated.capacity());
}
if matches!(self.mode, RigMode::VDES | RigMode::MARINE) && self.iq_tx.receiver_count() > 0 {
if matches!(self.mode, RigMode::VDES) && self.iq_tx.receiver_count() > 0 {
self.scratch_iq_tap.clear();
if self.scratch_iq_tap.capacity() < capacity {
self.scratch_iq_tap
@@ -68,7 +68,7 @@ impl SoapySdrRig {
match mode {
RigMode::LSB | RigMode::USB | RigMode::DIG => 3_000,
RigMode::PKT | RigMode::AIS => 25_000,
RigMode::VDES | RigMode::MARINE => 100_000,
RigMode::VDES => 100_000,
RigMode::CW | RigMode::CWR => 500,
RigMode::AM => 9_000,
RigMode::FM => 12_500,
@@ -229,7 +229,6 @@ impl SoapySdrRig {
RigMode::FM,
RigMode::AIS,
RigMode::VDES,
RigMode::MARINE,
RigMode::DIG,
RigMode::PKT,
],
@@ -367,7 +366,7 @@ impl SoapySdrRig {
let Some((ais_a_idx, ais_b_idx)) = self.ais_channel_indices else {
return;
};
let enabled = matches!(self.mode, RigMode::AIS | RigMode::MARINE);
let enabled = matches!(self.mode, RigMode::AIS);
let dsps = self.pipeline.channel_dsps.read().unwrap();
for idx in [ais_a_idx, ais_b_idx] {
if let Some(dsp_arc) = dsps.get(idx) {
@@ -48,7 +48,7 @@ fn default_bandwidth_hz(mode: &RigMode) -> u32 {
RigMode::FM => 12_500,
RigMode::WFM => 180_000,
RigMode::PKT | RigMode::AIS => 25_000,
RigMode::VDES | RigMode::MARINE => 100_000,
RigMode::VDES => 100_000,
RigMode::Other(_) => 3_000,
}
}