[fix](trx-vdes): normalize geo boxes for map decoding

Co-authored-by: OpenAI Codex <codex@openai.com>
Signed-off-by: Stan Grams <sjg@haxx.space>
This commit is contained in:
2026-03-03 20:10:02 +01:00
parent 295a395999
commit 2eb3c4f66f
+59 -11
View File
@@ -600,17 +600,15 @@ fn parse_msg_6(bits: &[u8], mut parsed: ParsedPayload) -> ParsedPayload {
if let (Some(ne_lon), Some(ne_lat), Some(sw_lon), Some(sw_lat)) = if let (Some(ne_lon), Some(ne_lat), Some(sw_lon), Some(sw_lat)) =
(ne_lon, ne_lat, sw_lon, sw_lat) (ne_lon, ne_lat, sw_lon, sw_lat)
{ {
let ne_lon_deg = ne_lon as f64 / 600.0; let first_lon_deg = ne_lon as f64 / 600.0;
let ne_lat_deg = ne_lat as f64 / 600.0; let first_lat_deg = ne_lat as f64 / 600.0;
let sw_lon_deg = sw_lon as f64 / 600.0; let second_lon_deg = sw_lon as f64 / 600.0;
let sw_lat_deg = sw_lat as f64 / 600.0; let second_lat_deg = sw_lat as f64 / 600.0;
let valid_box = valid_geo_coord(sw_lat_deg, sw_lon_deg) if let Some((sw_lat_deg, sw_lon_deg, ne_lat_deg, ne_lon_deg)) =
&& valid_geo_coord(ne_lat_deg, ne_lon_deg) normalize_geo_box(first_lat_deg, first_lon_deg, second_lat_deg, second_lon_deg)
&& ne_lat_deg >= sw_lat_deg {
&& ne_lon_deg >= sw_lon_deg; parsed.lon = Some((sw_lon_deg + ne_lon_deg) * 0.5);
if valid_box { parsed.lat = Some((sw_lat_deg + ne_lat_deg) * 0.5);
parsed.lon = Some((ne_lon_deg + sw_lon_deg) * 0.5);
parsed.lat = Some((ne_lat_deg + sw_lat_deg) * 0.5);
parsed.summary = Some(format!( parsed.summary = Some(format!(
"Geo ASM {} · {} data bits · box {:.3},{:.3} to {:.3},{:.3}", "Geo ASM {} · {} data bits · box {:.3},{:.3} to {:.3},{:.3}",
parsed.asm_identifier.unwrap_or(0), parsed.asm_identifier.unwrap_or(0),
@@ -746,6 +744,23 @@ fn valid_geo_coord(lat: f64, lon: f64) -> bool {
(-90.0..=90.0).contains(&lat) && (-180.0..=180.0).contains(&lon) (-90.0..=90.0).contains(&lat) && (-180.0..=180.0).contains(&lon)
} }
fn normalize_geo_box(
first_lat: f64,
first_lon: f64,
second_lat: f64,
second_lon: f64,
) -> Option<(f64, f64, f64, f64)> {
if !valid_geo_coord(first_lat, first_lon) || !valid_geo_coord(second_lat, second_lon) {
return None;
}
let south = first_lat.min(second_lat);
let north = first_lat.max(second_lat);
let west = first_lon.min(second_lon);
let east = first_lon.max(second_lon);
Some((south, west, north, east))
}
fn viterbi_decode_rate_half(coded_bits: &[u8]) -> Vec<u8> { fn viterbi_decode_rate_half(coded_bits: &[u8]) -> Vec<u8> {
if coded_bits.len() < 2 { if coded_bits.len() < 2 {
return Vec::new(); return Vec::new();
@@ -1082,6 +1097,22 @@ mod tests {
Complex::new(angle.cos(), angle.sin()) Complex::new(angle.cos(), angle.sin())
} }
fn write_bits(bits: &mut [u8], start: usize, len: usize, value: u32) {
for idx in 0..len {
let shift = len - idx - 1;
bits[start + idx] = ((value >> shift) & 1) as u8;
}
}
fn write_signed_bits(bits: &mut [u8], start: usize, len: usize, value: i32) {
let mask = if len >= 32 {
u32::MAX
} else {
(1u32 << len) - 1
};
write_bits(bits, start, len, (value as u32) & mask);
}
#[test] #[test]
fn packs_dibits_msb_first() { fn packs_dibits_msb_first() {
assert_eq!( assert_eq!(
@@ -1167,4 +1198,21 @@ mod tests {
let decoded = viterbi_decode_rate_half(&coded); let decoded = viterbi_decode_rate_half(&coded);
assert_eq!(decoded, input); assert_eq!(decoded, input);
} }
#[test]
fn parses_geo_box_even_when_corners_arrive_reversed() {
let mut bits = vec![0u8; 160];
write_bits(&mut bits, 0, 4, 6);
write_bits(&mut bits, 13, 32, 12345);
write_signed_bits(&mut bits, 45, 18, (10.0_f64 * 600.0) as i32);
write_signed_bits(&mut bits, 63, 17, (20.0_f64 * 600.0) as i32);
write_signed_bits(&mut bits, 80, 18, (-5.0_f64 * 600.0) as i32);
write_signed_bits(&mut bits, 98, 17, (15.0_f64 * 600.0) as i32);
let parsed = parse_vdes_payload(&bits);
assert_eq!(parsed.message_id, Some(6));
assert_eq!(parsed.lat, Some(17.5));
assert_eq!(parsed.lon, Some(2.5));
}
} }