[fix](trx-vdes): soften decode plausibility gating

Keep geo sanity checks, but treat most marginal VDES decodes as low confidence instead of rejecting them outright.

Co-authored-by: Stan Grams <sjg@haxx.space>
Signed-off-by: Stan Grams <sjg@haxx.space>
This commit is contained in:
2026-03-03 01:39:32 +01:00
parent 8d06d73e60
commit 081d2d062d
+15 -13
View File
@@ -164,7 +164,8 @@ impl VdesDecoder {
.into_iter() .into_iter()
.filter(|bit| *bit == 0) .filter(|bit| *bit == 0)
.count(); .count();
if vdes_plausibility_score(&parsed, link_id, tail_zero_bits) < 15 { let plausibility = vdes_plausibility_score(&parsed, link_id, tail_zero_bits);
if plausibility < -35 {
return Some(build_unsynced_message( return Some(build_unsynced_message(
channel, channel,
&framed, &framed,
@@ -174,9 +175,10 @@ impl VdesDecoder {
)); ));
} }
let fec_state = format!( let fec_state = format!(
"Hard-decision 1/2 Viterbi, tail {} / {} zero bits", "Hard-decision 1/2 Viterbi, tail {} / {} zero bits{}",
tail_zero_bits, tail_zero_bits,
TER_MCS1_100_FEC_TAIL_BITS TER_MCS1_100_FEC_TAIL_BITS,
if plausibility < 15 { " · Low confidence" } else { "" }
); );
let destination = parsed.summary.clone().or_else(|| { let destination = parsed.summary.clone().or_else(|| {
Some(format!( Some(format!(
@@ -642,13 +644,13 @@ fn vdes_plausibility_score(parsed: &ParsedPayload, link_id: Option<u8>, tail_zer
match parsed.message_id { match parsed.message_id {
Some(0..=6) => score += 30, Some(0..=6) => score += 30,
Some(_) | None => score -= 40, Some(_) | None => score -= 10,
} }
if valid_station_id(parsed.source_id) { if valid_station_id(parsed.source_id) {
score += 20; score += 20;
} else { } else {
score -= 40; score -= 20;
} }
if link_id.is_some() { if link_id.is_some() {
@@ -658,7 +660,7 @@ fn vdes_plausibility_score(parsed: &ParsedPayload, link_id: Option<u8>, tail_zer
score += match tail_zero_bits { score += match tail_zero_bits {
4.. => 20, 4.. => 20,
2..=3 => 8, 2..=3 => 8,
_ => -20, _ => -8,
}; };
if !parsed.payload_bits.is_empty() { if !parsed.payload_bits.is_empty() {
@@ -677,14 +679,14 @@ fn vdes_plausibility_score(parsed: &ParsedPayload, link_id: Option<u8>, tail_zer
if valid_station_id(parsed.destination_id) { if valid_station_id(parsed.destination_id) {
score += 15; score += 15;
} else { } else {
score -= 20; score -= 8;
} }
} }
Some(5) => { Some(5) => {
if valid_station_id(parsed.destination_id) { if valid_station_id(parsed.destination_id) {
score += 15; score += 15;
} else { } else {
score -= 20; score -= 8;
} }
if parsed.ack_nack_mask.is_some() { if parsed.ack_nack_mask.is_some() {
score += 5; score += 5;
@@ -695,14 +697,14 @@ fn vdes_plausibility_score(parsed: &ParsedPayload, link_id: Option<u8>, tail_zer
if parsed.lat.is_some() && parsed.lon.is_some() { if parsed.lat.is_some() && parsed.lon.is_some() {
score += 20; score += 20;
} else { } else {
score -= 20; score -= 8;
} }
} }
_ => {} _ => {}
} }
if parsed.message_label == Some("Unknown") { if parsed.message_label == Some("Unknown") {
score -= 20; score -= 8;
} }
score score
@@ -710,15 +712,15 @@ fn vdes_plausibility_score(parsed: &ParsedPayload, link_id: Option<u8>, tail_zer
fn counted_payload_score(parsed: &ParsedPayload, payload_start_bit: usize) -> i32 { fn counted_payload_score(parsed: &ParsedPayload, payload_start_bit: usize) -> i32 {
let Some(data_count) = parsed.data_count else { let Some(data_count) = parsed.data_count else {
return -15; return -8;
}; };
let Some(expected_end) = payload_start_bit.checked_add(usize::from(data_count)) else { let Some(expected_end) = payload_start_bit.checked_add(usize::from(data_count)) else {
return -15; return -8;
}; };
if parsed.payload_bits.len() == usize::from(data_count) && expected_end >= payload_start_bit { if parsed.payload_bits.len() == usize::from(data_count) && expected_end >= payload_start_bit {
15 15
} else { } else {
-15 -8
} }
} }