diff --git a/src/decoders/trx-ftx/src/callsign_hash.rs b/src/decoders/trx-ftx/src/callsign_hash.rs index ee9af72..44d8176 100644 --- a/src/decoders/trx-ftx/src/callsign_hash.rs +++ b/src/decoders/trx-ftx/src/callsign_hash.rs @@ -106,10 +106,12 @@ impl CallsignHashTable { /// The `hash` parameter is the full 22-bit hash value. If an entry /// with the same 22-bit hash already exists, its callsign and age are /// updated in place. Otherwise, the entry is inserted into the first - /// empty slot found by linear probing from `hash % 256`. + /// empty slot found by linear probing from `hash % 256`. If the table + /// is full, the probe-start slot is evicted to make room. pub fn add(&mut self, callsign: &str, hash: u32) { let hash22 = hash & HASH22_MASK; - let mut idx = (hash22 as usize) % CALLSIGN_HASHTABLE_SIZE; + let start_idx = (hash22 as usize) % CALLSIGN_HASHTABLE_SIZE; + let mut idx = start_idx; loop { match &self.entries[idx] { @@ -124,6 +126,14 @@ impl CallsignHashTable { Some(_) => { // Collision — linear probe to next slot. idx = (idx + 1) % CALLSIGN_HASHTABLE_SIZE; + if idx == start_idx { + // Table is full; evict the start slot. + self.entries[idx] = Some(CallsignEntry { + hash: hash22, + callsign: callsign.to_string(), + }); + return; + } } None => { // Empty slot — insert here. @@ -142,12 +152,12 @@ impl CallsignHashTable { /// determine which bits to compare. /// /// Returns `Some(callsign)` if a matching entry is found, or `None` - /// if the probe sequence reaches an empty slot without finding a - /// match. + /// if no match is found within a full probe cycle. pub fn lookup(&self, hash_type: HashType, hash: u32) -> Option { let (shift, mask) = hash_type.shift_and_mask(); let target = hash & mask; - let mut idx = (hash as usize) % CALLSIGN_HASHTABLE_SIZE; + let start_idx = (hash as usize) % CALLSIGN_HASHTABLE_SIZE; + let mut idx = start_idx; loop { match &self.entries[idx] { @@ -157,6 +167,9 @@ impl CallsignHashTable { return Some(entry.callsign.clone()); } idx = (idx + 1) % CALLSIGN_HASHTABLE_SIZE; + if idx == start_idx { + return None; + } } None => return None, } @@ -391,6 +404,38 @@ mod tests { assert!(hash <= 0x3F_FFFF, "hash should fit in 22 bits"); } + #[test] + fn add_full_table_does_not_hang() { + // Fill the table to capacity with distinct hashes, then add one more. + // This must terminate (no infinite loop) and must not panic. + let mut table = CallsignHashTable::new(); + for i in 0..CALLSIGN_HASHTABLE_SIZE { + table.entries[i] = Some(CallsignEntry { + hash: i as u32, + callsign: format!("C{}", i), + }); + } + table.size = CALLSIGN_HASHTABLE_SIZE; + // This hash won't match any existing entry — must not infinite-loop. + table.add("W1AW", 0x3F_FFFF); + } + + #[test] + fn lookup_full_table_does_not_hang() { + // Fill the table with entries that won't match the target, then look + // up a hash that is absent. Must return None without looping forever. + let mut table = CallsignHashTable::new(); + for i in 0..CALLSIGN_HASHTABLE_SIZE { + table.entries[i] = Some(CallsignEntry { + hash: i as u32, + callsign: format!("C{}", i), + }); + } + table.size = CALLSIGN_HASHTABLE_SIZE; + let result = table.lookup(HashType::Hash22Bits, 0x3F_FFFF); + assert!(result.is_none()); + } + #[test] fn compute_hash_invalid_char_returns_none() { // Lowercase letters are not in the AlphanumSpaceSlash table. diff --git a/src/decoders/trx-ftx/src/constants.rs b/src/decoders/trx-ftx/src/constants.rs index 3708c98..9d65bda 100644 --- a/src/decoders/trx-ftx/src/constants.rs +++ b/src/decoders/trx-ftx/src/constants.rs @@ -8,12 +8,8 @@ use crate::protocol::{FTX_LDPC_K_BYTES, FTX_LDPC_M, FTX_LDPC_N}; pub const FT8_COSTAS_PATTERN: [u8; 7] = [3, 1, 4, 0, 6, 5, 2]; /// Costas sync tone patterns for FT4 (4 groups of 4 tones). -pub const FT4_COSTAS_PATTERN: [[u8; 4]; 4] = [ - [0, 1, 3, 2], - [1, 0, 2, 3], - [2, 3, 1, 0], - [3, 2, 0, 1], -]; +pub const FT4_COSTAS_PATTERN: [[u8; 4]; 4] = + [[0, 1, 3, 2], [1, 0, 2, 3], [2, 3, 1, 0], [3, 2, 0, 1]]; /// Gray code map for FT8 (8 symbols, 3 bits). pub const FT8_GRAY_MAP: [u8; 8] = [0, 1, 3, 2, 5, 6, 4, 7]; @@ -22,95 +18,259 @@ pub const FT8_GRAY_MAP: [u8; 8] = [0, 1, 3, 2, 5, 6, 4, 7]; pub const FT4_GRAY_MAP: [u8; 4] = [0, 1, 3, 2]; /// XOR sequence for FT4 encoding (prevents long zero runs on CQ). -pub const FT4_XOR_SEQUENCE: [u8; 10] = [ - 0x4A, 0x5E, 0x89, 0xB4, 0xB0, 0x8A, 0x79, 0x55, 0xBE, 0x28, -]; +pub const FT4_XOR_SEQUENCE: [u8; 10] = [0x4A, 0x5E, 0x89, 0xB4, 0xB0, 0x8A, 0x79, 0x55, 0xBE, 0x28]; /// Parity generator matrix for (174,91) LDPC code, stored in bitpacked format (MSB first). pub const FTX_LDPC_GENERATOR: [[u8; FTX_LDPC_K_BYTES]; FTX_LDPC_M] = [ - [0x83, 0x29, 0xce, 0x11, 0xbf, 0x31, 0xea, 0xf5, 0x09, 0xf2, 0x7f, 0xc0], - [0x76, 0x1c, 0x26, 0x4e, 0x25, 0xc2, 0x59, 0x33, 0x54, 0x93, 0x13, 0x20], - [0xdc, 0x26, 0x59, 0x02, 0xfb, 0x27, 0x7c, 0x64, 0x10, 0xa1, 0xbd, 0xc0], - [0x1b, 0x3f, 0x41, 0x78, 0x58, 0xcd, 0x2d, 0xd3, 0x3e, 0xc7, 0xf6, 0x20], - [0x09, 0xfd, 0xa4, 0xfe, 0xe0, 0x41, 0x95, 0xfd, 0x03, 0x47, 0x83, 0xa0], - [0x07, 0x7c, 0xcc, 0xc1, 0x1b, 0x88, 0x73, 0xed, 0x5c, 0x3d, 0x48, 0xa0], - [0x29, 0xb6, 0x2a, 0xfe, 0x3c, 0xa0, 0x36, 0xf4, 0xfe, 0x1a, 0x9d, 0xa0], - [0x60, 0x54, 0xfa, 0xf5, 0xf3, 0x5d, 0x96, 0xd3, 0xb0, 0xc8, 0xc3, 0xe0], - [0xe2, 0x07, 0x98, 0xe4, 0x31, 0x0e, 0xed, 0x27, 0x88, 0x4a, 0xe9, 0x00], - [0x77, 0x5c, 0x9c, 0x08, 0xe8, 0x0e, 0x26, 0xdd, 0xae, 0x56, 0x31, 0x80], - [0xb0, 0xb8, 0x11, 0x02, 0x8c, 0x2b, 0xf9, 0x97, 0x21, 0x34, 0x87, 0xc0], - [0x18, 0xa0, 0xc9, 0x23, 0x1f, 0xc6, 0x0a, 0xdf, 0x5c, 0x5e, 0xa3, 0x20], - [0x76, 0x47, 0x1e, 0x83, 0x02, 0xa0, 0x72, 0x1e, 0x01, 0xb1, 0x2b, 0x80], - [0xff, 0xbc, 0xcb, 0x80, 0xca, 0x83, 0x41, 0xfa, 0xfb, 0x47, 0xb2, 0xe0], - [0x66, 0xa7, 0x2a, 0x15, 0x8f, 0x93, 0x25, 0xa2, 0xbf, 0x67, 0x17, 0x00], - [0xc4, 0x24, 0x36, 0x89, 0xfe, 0x85, 0xb1, 0xc5, 0x13, 0x63, 0xa1, 0x80], - [0x0d, 0xff, 0x73, 0x94, 0x14, 0xd1, 0xa1, 0xb3, 0x4b, 0x1c, 0x27, 0x00], - [0x15, 0xb4, 0x88, 0x30, 0x63, 0x6c, 0x8b, 0x99, 0x89, 0x49, 0x72, 0xe0], - [0x29, 0xa8, 0x9c, 0x0d, 0x3d, 0xe8, 0x1d, 0x66, 0x54, 0x89, 0xb0, 0xe0], - [0x4f, 0x12, 0x6f, 0x37, 0xfa, 0x51, 0xcb, 0xe6, 0x1b, 0xd6, 0xb9, 0x40], - [0x99, 0xc4, 0x72, 0x39, 0xd0, 0xd9, 0x7d, 0x3c, 0x84, 0xe0, 0x94, 0x00], - [0x19, 0x19, 0xb7, 0x51, 0x19, 0x76, 0x56, 0x21, 0xbb, 0x4f, 0x1e, 0x80], - [0x09, 0xdb, 0x12, 0xd7, 0x31, 0xfa, 0xee, 0x0b, 0x86, 0xdf, 0x6b, 0x80], - [0x48, 0x8f, 0xc3, 0x3d, 0xf4, 0x3f, 0xbd, 0xee, 0xa4, 0xea, 0xfb, 0x40], - [0x82, 0x74, 0x23, 0xee, 0x40, 0xb6, 0x75, 0xf7, 0x56, 0xeb, 0x5f, 0xe0], - [0xab, 0xe1, 0x97, 0xc4, 0x84, 0xcb, 0x74, 0x75, 0x71, 0x44, 0xa9, 0xa0], - [0x2b, 0x50, 0x0e, 0x4b, 0xc0, 0xec, 0x5a, 0x6d, 0x2b, 0xdb, 0xdd, 0x00], - [0xc4, 0x74, 0xaa, 0x53, 0xd7, 0x02, 0x18, 0x76, 0x16, 0x69, 0x36, 0x00], - [0x8e, 0xba, 0x1a, 0x13, 0xdb, 0x33, 0x90, 0xbd, 0x67, 0x18, 0xce, 0xc0], - [0x75, 0x38, 0x44, 0x67, 0x3a, 0x27, 0x78, 0x2c, 0xc4, 0x20, 0x12, 0xe0], - [0x06, 0xff, 0x83, 0xa1, 0x45, 0xc3, 0x70, 0x35, 0xa5, 0xc1, 0x26, 0x80], - [0x3b, 0x37, 0x41, 0x78, 0x58, 0xcc, 0x2d, 0xd3, 0x3e, 0xc3, 0xf6, 0x20], - [0x9a, 0x4a, 0x5a, 0x28, 0xee, 0x17, 0xca, 0x9c, 0x32, 0x48, 0x42, 0xc0], - [0xbc, 0x29, 0xf4, 0x65, 0x30, 0x9c, 0x97, 0x7e, 0x89, 0x61, 0x0a, 0x40], - [0x26, 0x63, 0xae, 0x6d, 0xdf, 0x8b, 0x5c, 0xe2, 0xbb, 0x29, 0x48, 0x80], - [0x46, 0xf2, 0x31, 0xef, 0xe4, 0x57, 0x03, 0x4c, 0x18, 0x14, 0x41, 0x80], - [0x3f, 0xb2, 0xce, 0x85, 0xab, 0xe9, 0xb0, 0xc7, 0x2e, 0x06, 0xfb, 0xe0], - [0xde, 0x87, 0x48, 0x1f, 0x28, 0x2c, 0x15, 0x39, 0x71, 0xa0, 0xa2, 0xe0], - [0xfc, 0xd7, 0xcc, 0xf2, 0x3c, 0x69, 0xfa, 0x99, 0xbb, 0xa1, 0x41, 0x20], - [0xf0, 0x26, 0x14, 0x47, 0xe9, 0x49, 0x0c, 0xa8, 0xe4, 0x74, 0xce, 0xc0], - [0x44, 0x10, 0x11, 0x58, 0x18, 0x19, 0x6f, 0x95, 0xcd, 0xd7, 0x01, 0x20], - [0x08, 0x8f, 0xc3, 0x1d, 0xf4, 0xbf, 0xbd, 0xe2, 0xa4, 0xea, 0xfb, 0x40], - [0xb8, 0xfe, 0xf1, 0xb6, 0x30, 0x77, 0x29, 0xfb, 0x0a, 0x07, 0x8c, 0x00], - [0x5a, 0xfe, 0xa7, 0xac, 0xcc, 0xb7, 0x7b, 0xbc, 0x9d, 0x99, 0xa9, 0x00], - [0x49, 0xa7, 0x01, 0x6a, 0xc6, 0x53, 0xf6, 0x5e, 0xcd, 0xc9, 0x07, 0x60], - [0x19, 0x44, 0xd0, 0x85, 0xbe, 0x4e, 0x7d, 0xa8, 0xd6, 0xcc, 0x7d, 0x00], - [0x25, 0x1f, 0x62, 0xad, 0xc4, 0x03, 0x2f, 0x0e, 0xe7, 0x14, 0x00, 0x20], - [0x56, 0x47, 0x1f, 0x87, 0x02, 0xa0, 0x72, 0x1e, 0x00, 0xb1, 0x2b, 0x80], - [0x2b, 0x8e, 0x49, 0x23, 0xf2, 0xdd, 0x51, 0xe2, 0xd5, 0x37, 0xfa, 0x00], - [0x6b, 0x55, 0x0a, 0x40, 0xa6, 0x6f, 0x47, 0x55, 0xde, 0x95, 0xc2, 0x60], - [0xa1, 0x8a, 0xd2, 0x8d, 0x4e, 0x27, 0xfe, 0x92, 0xa4, 0xf6, 0xc8, 0x40], - [0x10, 0xc2, 0xe5, 0x86, 0x38, 0x8c, 0xb8, 0x2a, 0x3d, 0x80, 0x75, 0x80], - [0xef, 0x34, 0xa4, 0x18, 0x17, 0xee, 0x02, 0x13, 0x3d, 0xb2, 0xeb, 0x00], - [0x7e, 0x9c, 0x0c, 0x54, 0x32, 0x5a, 0x9c, 0x15, 0x83, 0x6e, 0x00, 0x00], - [0x36, 0x93, 0xe5, 0x72, 0xd1, 0xfd, 0xe4, 0xcd, 0xf0, 0x79, 0xe8, 0x60], - [0xbf, 0xb2, 0xce, 0xc5, 0xab, 0xe1, 0xb0, 0xc7, 0x2e, 0x07, 0xfb, 0xe0], - [0x7e, 0xe1, 0x82, 0x30, 0xc5, 0x83, 0xcc, 0xcc, 0x57, 0xd4, 0xb0, 0x80], - [0xa0, 0x66, 0xcb, 0x2f, 0xed, 0xaf, 0xc9, 0xf5, 0x26, 0x64, 0x12, 0x60], - [0xbb, 0x23, 0x72, 0x5a, 0xbc, 0x47, 0xcc, 0x5f, 0x4c, 0xc4, 0xcd, 0x20], - [0xde, 0xd9, 0xdb, 0xa3, 0xbe, 0xe4, 0x0c, 0x59, 0xb5, 0x60, 0x9b, 0x40], - [0xd9, 0xa7, 0x01, 0x6a, 0xc6, 0x53, 0xe6, 0xde, 0xcd, 0xc9, 0x03, 0x60], - [0x9a, 0xd4, 0x6a, 0xed, 0x5f, 0x70, 0x7f, 0x28, 0x0a, 0xb5, 0xfc, 0x40], - [0xe5, 0x92, 0x1c, 0x77, 0x82, 0x25, 0x87, 0x31, 0x6d, 0x7d, 0x3c, 0x20], - [0x4f, 0x14, 0xda, 0x82, 0x42, 0xa8, 0xb8, 0x6d, 0xca, 0x73, 0x35, 0x20], - [0x8b, 0x8b, 0x50, 0x7a, 0xd4, 0x67, 0xd4, 0x44, 0x1d, 0xf7, 0x70, 0xe0], - [0x22, 0x83, 0x1c, 0x9c, 0xf1, 0x16, 0x94, 0x67, 0xad, 0x04, 0xb6, 0x80], - [0x21, 0x3b, 0x83, 0x8f, 0xe2, 0xae, 0x54, 0xc3, 0x8e, 0xe7, 0x18, 0x00], - [0x5d, 0x92, 0x6b, 0x6d, 0xd7, 0x1f, 0x08, 0x51, 0x81, 0xa4, 0xe1, 0x20], - [0x66, 0xab, 0x79, 0xd4, 0xb2, 0x9e, 0xe6, 0xe6, 0x95, 0x09, 0xe5, 0x60], - [0x95, 0x81, 0x48, 0x68, 0x2d, 0x74, 0x8a, 0x38, 0xdd, 0x68, 0xba, 0xa0], - [0xb8, 0xce, 0x02, 0x0c, 0xf0, 0x69, 0xc3, 0x2a, 0x72, 0x3a, 0xb1, 0x40], - [0xf4, 0x33, 0x1d, 0x6d, 0x46, 0x16, 0x07, 0xe9, 0x57, 0x52, 0x74, 0x60], - [0x6d, 0xa2, 0x3b, 0xa4, 0x24, 0xb9, 0x59, 0x61, 0x33, 0xcf, 0x9c, 0x80], - [0xa6, 0x36, 0xbc, 0xbc, 0x7b, 0x30, 0xc5, 0xfb, 0xea, 0xe6, 0x7f, 0xe0], - [0x5c, 0xb0, 0xd8, 0x6a, 0x07, 0xdf, 0x65, 0x4a, 0x90, 0x89, 0xa2, 0x00], - [0xf1, 0x1f, 0x10, 0x68, 0x48, 0x78, 0x0f, 0xc9, 0xec, 0xdd, 0x80, 0xa0], - [0x1f, 0xbb, 0x53, 0x64, 0xfb, 0x8d, 0x2c, 0x9d, 0x73, 0x0d, 0x5b, 0xa0], - [0xfc, 0xb8, 0x6b, 0xc7, 0x0a, 0x50, 0xc9, 0xd0, 0x2a, 0x5d, 0x03, 0x40], - [0xa5, 0x34, 0x43, 0x30, 0x29, 0xea, 0xc1, 0x5f, 0x32, 0x2e, 0x34, 0xc0], - [0xc9, 0x89, 0xd9, 0xc7, 0xc3, 0xd3, 0xb8, 0xc5, 0x5d, 0x75, 0x13, 0x00], - [0x7b, 0xb3, 0x8b, 0x2f, 0x01, 0x86, 0xd4, 0x66, 0x43, 0xae, 0x96, 0x20], - [0x26, 0x44, 0xeb, 0xad, 0xeb, 0x44, 0xb9, 0x46, 0x7d, 0x1f, 0x42, 0xc0], - [0x60, 0x8c, 0xc8, 0x57, 0x59, 0x4b, 0xfb, 0xb5, 0x5d, 0x69, 0x60, 0x00], + [ + 0x83, 0x29, 0xce, 0x11, 0xbf, 0x31, 0xea, 0xf5, 0x09, 0xf2, 0x7f, 0xc0, + ], + [ + 0x76, 0x1c, 0x26, 0x4e, 0x25, 0xc2, 0x59, 0x33, 0x54, 0x93, 0x13, 0x20, + ], + [ + 0xdc, 0x26, 0x59, 0x02, 0xfb, 0x27, 0x7c, 0x64, 0x10, 0xa1, 0xbd, 0xc0, + ], + [ + 0x1b, 0x3f, 0x41, 0x78, 0x58, 0xcd, 0x2d, 0xd3, 0x3e, 0xc7, 0xf6, 0x20, + ], + [ + 0x09, 0xfd, 0xa4, 0xfe, 0xe0, 0x41, 0x95, 0xfd, 0x03, 0x47, 0x83, 0xa0, + ], + [ + 0x07, 0x7c, 0xcc, 0xc1, 0x1b, 0x88, 0x73, 0xed, 0x5c, 0x3d, 0x48, 0xa0, + ], + [ + 0x29, 0xb6, 0x2a, 0xfe, 0x3c, 0xa0, 0x36, 0xf4, 0xfe, 0x1a, 0x9d, 0xa0, + ], + [ + 0x60, 0x54, 0xfa, 0xf5, 0xf3, 0x5d, 0x96, 0xd3, 0xb0, 0xc8, 0xc3, 0xe0, + ], + [ + 0xe2, 0x07, 0x98, 0xe4, 0x31, 0x0e, 0xed, 0x27, 0x88, 0x4a, 0xe9, 0x00, + ], + [ + 0x77, 0x5c, 0x9c, 0x08, 0xe8, 0x0e, 0x26, 0xdd, 0xae, 0x56, 0x31, 0x80, + ], + [ + 0xb0, 0xb8, 0x11, 0x02, 0x8c, 0x2b, 0xf9, 0x97, 0x21, 0x34, 0x87, 0xc0, + ], + [ + 0x18, 0xa0, 0xc9, 0x23, 0x1f, 0xc6, 0x0a, 0xdf, 0x5c, 0x5e, 0xa3, 0x20, + ], + [ + 0x76, 0x47, 0x1e, 0x83, 0x02, 0xa0, 0x72, 0x1e, 0x01, 0xb1, 0x2b, 0x80, + ], + [ + 0xff, 0xbc, 0xcb, 0x80, 0xca, 0x83, 0x41, 0xfa, 0xfb, 0x47, 0xb2, 0xe0, + ], + [ + 0x66, 0xa7, 0x2a, 0x15, 0x8f, 0x93, 0x25, 0xa2, 0xbf, 0x67, 0x17, 0x00, + ], + [ + 0xc4, 0x24, 0x36, 0x89, 0xfe, 0x85, 0xb1, 0xc5, 0x13, 0x63, 0xa1, 0x80, + ], + [ + 0x0d, 0xff, 0x73, 0x94, 0x14, 0xd1, 0xa1, 0xb3, 0x4b, 0x1c, 0x27, 0x00, + ], + [ + 0x15, 0xb4, 0x88, 0x30, 0x63, 0x6c, 0x8b, 0x99, 0x89, 0x49, 0x72, 0xe0, + ], + [ + 0x29, 0xa8, 0x9c, 0x0d, 0x3d, 0xe8, 0x1d, 0x66, 0x54, 0x89, 0xb0, 0xe0, + ], + [ + 0x4f, 0x12, 0x6f, 0x37, 0xfa, 0x51, 0xcb, 0xe6, 0x1b, 0xd6, 0xb9, 0x40, + ], + [ + 0x99, 0xc4, 0x72, 0x39, 0xd0, 0xd9, 0x7d, 0x3c, 0x84, 0xe0, 0x94, 0x00, + ], + [ + 0x19, 0x19, 0xb7, 0x51, 0x19, 0x76, 0x56, 0x21, 0xbb, 0x4f, 0x1e, 0x80, + ], + [ + 0x09, 0xdb, 0x12, 0xd7, 0x31, 0xfa, 0xee, 0x0b, 0x86, 0xdf, 0x6b, 0x80, + ], + [ + 0x48, 0x8f, 0xc3, 0x3d, 0xf4, 0x3f, 0xbd, 0xee, 0xa4, 0xea, 0xfb, 0x40, + ], + [ + 0x82, 0x74, 0x23, 0xee, 0x40, 0xb6, 0x75, 0xf7, 0x56, 0xeb, 0x5f, 0xe0, + ], + [ + 0xab, 0xe1, 0x97, 0xc4, 0x84, 0xcb, 0x74, 0x75, 0x71, 0x44, 0xa9, 0xa0, + ], + [ + 0x2b, 0x50, 0x0e, 0x4b, 0xc0, 0xec, 0x5a, 0x6d, 0x2b, 0xdb, 0xdd, 0x00, + ], + [ + 0xc4, 0x74, 0xaa, 0x53, 0xd7, 0x02, 0x18, 0x76, 0x16, 0x69, 0x36, 0x00, + ], + [ + 0x8e, 0xba, 0x1a, 0x13, 0xdb, 0x33, 0x90, 0xbd, 0x67, 0x18, 0xce, 0xc0, + ], + [ + 0x75, 0x38, 0x44, 0x67, 0x3a, 0x27, 0x78, 0x2c, 0xc4, 0x20, 0x12, 0xe0, + ], + [ + 0x06, 0xff, 0x83, 0xa1, 0x45, 0xc3, 0x70, 0x35, 0xa5, 0xc1, 0x26, 0x80, + ], + [ + 0x3b, 0x37, 0x41, 0x78, 0x58, 0xcc, 0x2d, 0xd3, 0x3e, 0xc3, 0xf6, 0x20, + ], + [ + 0x9a, 0x4a, 0x5a, 0x28, 0xee, 0x17, 0xca, 0x9c, 0x32, 0x48, 0x42, 0xc0, + ], + [ + 0xbc, 0x29, 0xf4, 0x65, 0x30, 0x9c, 0x97, 0x7e, 0x89, 0x61, 0x0a, 0x40, + ], + [ + 0x26, 0x63, 0xae, 0x6d, 0xdf, 0x8b, 0x5c, 0xe2, 0xbb, 0x29, 0x48, 0x80, + ], + [ + 0x46, 0xf2, 0x31, 0xef, 0xe4, 0x57, 0x03, 0x4c, 0x18, 0x14, 0x41, 0x80, + ], + [ + 0x3f, 0xb2, 0xce, 0x85, 0xab, 0xe9, 0xb0, 0xc7, 0x2e, 0x06, 0xfb, 0xe0, + ], + [ + 0xde, 0x87, 0x48, 0x1f, 0x28, 0x2c, 0x15, 0x39, 0x71, 0xa0, 0xa2, 0xe0, + ], + [ + 0xfc, 0xd7, 0xcc, 0xf2, 0x3c, 0x69, 0xfa, 0x99, 0xbb, 0xa1, 0x41, 0x20, + ], + [ + 0xf0, 0x26, 0x14, 0x47, 0xe9, 0x49, 0x0c, 0xa8, 0xe4, 0x74, 0xce, 0xc0, + ], + [ + 0x44, 0x10, 0x11, 0x58, 0x18, 0x19, 0x6f, 0x95, 0xcd, 0xd7, 0x01, 0x20, + ], + [ + 0x08, 0x8f, 0xc3, 0x1d, 0xf4, 0xbf, 0xbd, 0xe2, 0xa4, 0xea, 0xfb, 0x40, + ], + [ + 0xb8, 0xfe, 0xf1, 0xb6, 0x30, 0x77, 0x29, 0xfb, 0x0a, 0x07, 0x8c, 0x00, + ], + [ + 0x5a, 0xfe, 0xa7, 0xac, 0xcc, 0xb7, 0x7b, 0xbc, 0x9d, 0x99, 0xa9, 0x00, + ], + [ + 0x49, 0xa7, 0x01, 0x6a, 0xc6, 0x53, 0xf6, 0x5e, 0xcd, 0xc9, 0x07, 0x60, + ], + [ + 0x19, 0x44, 0xd0, 0x85, 0xbe, 0x4e, 0x7d, 0xa8, 0xd6, 0xcc, 0x7d, 0x00, + ], + [ + 0x25, 0x1f, 0x62, 0xad, 0xc4, 0x03, 0x2f, 0x0e, 0xe7, 0x14, 0x00, 0x20, + ], + [ + 0x56, 0x47, 0x1f, 0x87, 0x02, 0xa0, 0x72, 0x1e, 0x00, 0xb1, 0x2b, 0x80, + ], + [ + 0x2b, 0x8e, 0x49, 0x23, 0xf2, 0xdd, 0x51, 0xe2, 0xd5, 0x37, 0xfa, 0x00, + ], + [ + 0x6b, 0x55, 0x0a, 0x40, 0xa6, 0x6f, 0x47, 0x55, 0xde, 0x95, 0xc2, 0x60, + ], + [ + 0xa1, 0x8a, 0xd2, 0x8d, 0x4e, 0x27, 0xfe, 0x92, 0xa4, 0xf6, 0xc8, 0x40, + ], + [ + 0x10, 0xc2, 0xe5, 0x86, 0x38, 0x8c, 0xb8, 0x2a, 0x3d, 0x80, 0x75, 0x80, + ], + [ + 0xef, 0x34, 0xa4, 0x18, 0x17, 0xee, 0x02, 0x13, 0x3d, 0xb2, 0xeb, 0x00, + ], + [ + 0x7e, 0x9c, 0x0c, 0x54, 0x32, 0x5a, 0x9c, 0x15, 0x83, 0x6e, 0x00, 0x00, + ], + [ + 0x36, 0x93, 0xe5, 0x72, 0xd1, 0xfd, 0xe4, 0xcd, 0xf0, 0x79, 0xe8, 0x60, + ], + [ + 0xbf, 0xb2, 0xce, 0xc5, 0xab, 0xe1, 0xb0, 0xc7, 0x2e, 0x07, 0xfb, 0xe0, + ], + [ + 0x7e, 0xe1, 0x82, 0x30, 0xc5, 0x83, 0xcc, 0xcc, 0x57, 0xd4, 0xb0, 0x80, + ], + [ + 0xa0, 0x66, 0xcb, 0x2f, 0xed, 0xaf, 0xc9, 0xf5, 0x26, 0x64, 0x12, 0x60, + ], + [ + 0xbb, 0x23, 0x72, 0x5a, 0xbc, 0x47, 0xcc, 0x5f, 0x4c, 0xc4, 0xcd, 0x20, + ], + [ + 0xde, 0xd9, 0xdb, 0xa3, 0xbe, 0xe4, 0x0c, 0x59, 0xb5, 0x60, 0x9b, 0x40, + ], + [ + 0xd9, 0xa7, 0x01, 0x6a, 0xc6, 0x53, 0xe6, 0xde, 0xcd, 0xc9, 0x03, 0x60, + ], + [ + 0x9a, 0xd4, 0x6a, 0xed, 0x5f, 0x70, 0x7f, 0x28, 0x0a, 0xb5, 0xfc, 0x40, + ], + [ + 0xe5, 0x92, 0x1c, 0x77, 0x82, 0x25, 0x87, 0x31, 0x6d, 0x7d, 0x3c, 0x20, + ], + [ + 0x4f, 0x14, 0xda, 0x82, 0x42, 0xa8, 0xb8, 0x6d, 0xca, 0x73, 0x35, 0x20, + ], + [ + 0x8b, 0x8b, 0x50, 0x7a, 0xd4, 0x67, 0xd4, 0x44, 0x1d, 0xf7, 0x70, 0xe0, + ], + [ + 0x22, 0x83, 0x1c, 0x9c, 0xf1, 0x16, 0x94, 0x67, 0xad, 0x04, 0xb6, 0x80, + ], + [ + 0x21, 0x3b, 0x83, 0x8f, 0xe2, 0xae, 0x54, 0xc3, 0x8e, 0xe7, 0x18, 0x00, + ], + [ + 0x5d, 0x92, 0x6b, 0x6d, 0xd7, 0x1f, 0x08, 0x51, 0x81, 0xa4, 0xe1, 0x20, + ], + [ + 0x66, 0xab, 0x79, 0xd4, 0xb2, 0x9e, 0xe6, 0xe6, 0x95, 0x09, 0xe5, 0x60, + ], + [ + 0x95, 0x81, 0x48, 0x68, 0x2d, 0x74, 0x8a, 0x38, 0xdd, 0x68, 0xba, 0xa0, + ], + [ + 0xb8, 0xce, 0x02, 0x0c, 0xf0, 0x69, 0xc3, 0x2a, 0x72, 0x3a, 0xb1, 0x40, + ], + [ + 0xf4, 0x33, 0x1d, 0x6d, 0x46, 0x16, 0x07, 0xe9, 0x57, 0x52, 0x74, 0x60, + ], + [ + 0x6d, 0xa2, 0x3b, 0xa4, 0x24, 0xb9, 0x59, 0x61, 0x33, 0xcf, 0x9c, 0x80, + ], + [ + 0xa6, 0x36, 0xbc, 0xbc, 0x7b, 0x30, 0xc5, 0xfb, 0xea, 0xe6, 0x7f, 0xe0, + ], + [ + 0x5c, 0xb0, 0xd8, 0x6a, 0x07, 0xdf, 0x65, 0x4a, 0x90, 0x89, 0xa2, 0x00, + ], + [ + 0xf1, 0x1f, 0x10, 0x68, 0x48, 0x78, 0x0f, 0xc9, 0xec, 0xdd, 0x80, 0xa0, + ], + [ + 0x1f, 0xbb, 0x53, 0x64, 0xfb, 0x8d, 0x2c, 0x9d, 0x73, 0x0d, 0x5b, 0xa0, + ], + [ + 0xfc, 0xb8, 0x6b, 0xc7, 0x0a, 0x50, 0xc9, 0xd0, 0x2a, 0x5d, 0x03, 0x40, + ], + [ + 0xa5, 0x34, 0x43, 0x30, 0x29, 0xea, 0xc1, 0x5f, 0x32, 0x2e, 0x34, 0xc0, + ], + [ + 0xc9, 0x89, 0xd9, 0xc7, 0xc3, 0xd3, 0xb8, 0xc5, 0x5d, 0x75, 0x13, 0x00, + ], + [ + 0x7b, 0xb3, 0x8b, 0x2f, 0x01, 0x86, 0xd4, 0x66, 0x43, 0xae, 0x96, 0x20, + ], + [ + 0x26, 0x44, 0xeb, 0xad, 0xeb, 0x44, 0xb9, 0x46, 0x7d, 0x1f, 0x42, 0xc0, + ], + [ + 0x60, 0x8c, 0xc8, 0x57, 0x59, 0x4b, 0xfb, 0xb5, 0x5d, 0x69, 0x60, 0x00, + ], ]; /// LDPC parity check matrix Nm: each row describes one parity check. @@ -204,49 +364,185 @@ pub const FTX_LDPC_NM: [[u8; 7]; FTX_LDPC_M] = [ /// Mn: each row corresponds to a codeword bit. /// The numbers indicate which three parity checks refer to the codeword bit (1-origin). pub const FTX_LDPC_MN: [[u8; 3]; FTX_LDPC_N] = [ - [16, 45, 73], [25, 51, 62], [33, 58, 78], [1, 44, 45], [2, 7, 61], - [3, 6, 54], [4, 35, 48], [5, 13, 21], [8, 56, 79], [9, 64, 69], - [10, 19, 66], [11, 36, 60], [12, 37, 58], [14, 32, 43], [15, 63, 80], - [17, 28, 77], [18, 74, 83], [22, 53, 81], [23, 30, 34], [24, 31, 40], - [26, 41, 76], [27, 57, 70], [29, 49, 65], [3, 38, 78], [5, 39, 82], - [46, 50, 73], [51, 52, 74], [55, 71, 72], [44, 67, 72], [43, 68, 78], - [1, 32, 59], [2, 6, 71], [4, 16, 54], [7, 65, 67], [8, 30, 42], - [9, 22, 31], [10, 18, 76], [11, 23, 82], [12, 28, 61], [13, 52, 79], - [14, 50, 51], [15, 81, 83], [17, 29, 60], [19, 33, 64], [20, 26, 73], - [21, 34, 40], [24, 27, 77], [25, 55, 58], [35, 53, 66], [36, 48, 68], - [37, 46, 75], [38, 45, 47], [39, 57, 69], [41, 56, 62], [20, 49, 53], - [46, 52, 63], [45, 70, 75], [27, 35, 80], [1, 15, 30], [2, 68, 80], - [3, 36, 51], [4, 28, 51], [5, 31, 56], [6, 20, 37], [7, 40, 82], - [8, 60, 69], [9, 10, 49], [11, 44, 57], [12, 39, 59], [13, 24, 55], - [14, 21, 65], [16, 71, 78], [17, 30, 76], [18, 25, 80], [19, 61, 83], - [22, 38, 77], [23, 41, 50], [7, 26, 58], [29, 32, 81], [33, 40, 73], - [18, 34, 48], [13, 42, 64], [5, 26, 43], [47, 69, 72], [54, 55, 70], - [45, 62, 68], [10, 63, 67], [14, 66, 72], [22, 60, 74], [35, 39, 79], - [1, 46, 64], [1, 24, 66], [2, 5, 70], [3, 31, 65], [4, 49, 58], - [1, 4, 5], [6, 60, 67], [7, 32, 75], [8, 48, 82], [9, 35, 41], - [10, 39, 62], [11, 14, 61], [12, 71, 74], [13, 23, 78], [11, 35, 55], - [15, 16, 79], [7, 9, 16], [17, 54, 63], [18, 50, 57], [19, 30, 47], - [20, 64, 80], [21, 28, 69], [22, 25, 43], [13, 22, 37], [2, 47, 51], - [23, 54, 74], [26, 34, 72], [27, 36, 37], [21, 36, 63], [29, 40, 44], - [19, 26, 57], [3, 46, 82], [23, 54, 74], [33, 52, 53], [30, 43, 52], - [6, 9, 52], [27, 33, 65], [25, 69, 73], [38, 55, 83], [20, 39, 77], - [18, 29, 56], [32, 48, 71], [42, 51, 59], [28, 44, 79], [34, 60, 62], - [31, 45, 61], [46, 68, 77], [6, 24, 76], [8, 10, 78], [40, 41, 70], - [17, 50, 53], [42, 66, 68], [4, 22, 72], [36, 64, 81], [13, 29, 47], - [2, 8, 81], [56, 67, 73], [5, 38, 50], [12, 38, 64], [59, 72, 80], - [3, 26, 79], [45, 76, 81], [1, 65, 74], [7, 18, 77], [11, 56, 59], - [14, 39, 54], [16, 37, 66], [10, 28, 55], [15, 60, 70], [17, 25, 82], - [20, 30, 31], [12, 67, 68], [23, 75, 80], [27, 32, 62], [24, 69, 75], - [19, 21, 71], [34, 53, 61], [35, 46, 47], [33, 59, 76], [40, 43, 83], - [41, 42, 63], [49, 75, 83], [20, 44, 48], [42, 49, 57], + [16, 45, 73], + [25, 51, 62], + [33, 58, 78], + [1, 44, 45], + [2, 7, 61], + [3, 6, 54], + [4, 35, 48], + [5, 13, 21], + [8, 56, 79], + [9, 64, 69], + [10, 19, 66], + [11, 36, 60], + [12, 37, 58], + [14, 32, 43], + [15, 63, 80], + [17, 28, 77], + [18, 74, 83], + [22, 53, 81], + [23, 30, 34], + [24, 31, 40], + [26, 41, 76], + [27, 57, 70], + [29, 49, 65], + [3, 38, 78], + [5, 39, 82], + [46, 50, 73], + [51, 52, 74], + [55, 71, 72], + [44, 67, 72], + [43, 68, 78], + [1, 32, 59], + [2, 6, 71], + [4, 16, 54], + [7, 65, 67], + [8, 30, 42], + [9, 22, 31], + [10, 18, 76], + [11, 23, 82], + [12, 28, 61], + [13, 52, 79], + [14, 50, 51], + [15, 81, 83], + [17, 29, 60], + [19, 33, 64], + [20, 26, 73], + [21, 34, 40], + [24, 27, 77], + [25, 55, 58], + [35, 53, 66], + [36, 48, 68], + [37, 46, 75], + [38, 45, 47], + [39, 57, 69], + [41, 56, 62], + [20, 49, 53], + [46, 52, 63], + [45, 70, 75], + [27, 35, 80], + [1, 15, 30], + [2, 68, 80], + [3, 36, 51], + [4, 28, 51], + [5, 31, 56], + [6, 20, 37], + [7, 40, 82], + [8, 60, 69], + [9, 10, 49], + [11, 44, 57], + [12, 39, 59], + [13, 24, 55], + [14, 21, 65], + [16, 71, 78], + [17, 30, 76], + [18, 25, 80], + [19, 61, 83], + [22, 38, 77], + [23, 41, 50], + [7, 26, 58], + [29, 32, 81], + [33, 40, 73], + [18, 34, 48], + [13, 42, 64], + [5, 26, 43], + [47, 69, 72], + [54, 55, 70], + [45, 62, 68], + [10, 63, 67], + [14, 66, 72], + [22, 60, 74], + [35, 39, 79], + [1, 46, 64], + [1, 24, 66], + [2, 5, 70], + [3, 31, 65], + [4, 49, 58], + [1, 4, 5], + [6, 60, 67], + [7, 32, 75], + [8, 48, 82], + [9, 35, 41], + [10, 39, 62], + [11, 14, 61], + [12, 71, 74], + [13, 23, 78], + [11, 35, 55], + [15, 16, 79], + [7, 9, 16], + [17, 54, 63], + [18, 50, 57], + [19, 30, 47], + [20, 64, 80], + [21, 28, 69], + [22, 25, 43], + [13, 22, 37], + [2, 47, 51], + [23, 54, 74], + [26, 34, 72], + [27, 36, 37], + [21, 36, 63], + [29, 40, 44], + [19, 26, 57], + [3, 46, 82], + [23, 54, 74], + [33, 52, 53], + [30, 43, 52], + [6, 9, 52], + [27, 33, 65], + [25, 69, 73], + [38, 55, 83], + [20, 39, 77], + [18, 29, 56], + [32, 48, 71], + [42, 51, 59], + [28, 44, 79], + [34, 60, 62], + [31, 45, 61], + [46, 68, 77], + [6, 24, 76], + [8, 10, 78], + [40, 41, 70], + [17, 50, 53], + [42, 66, 68], + [4, 22, 72], + [36, 64, 81], + [13, 29, 47], + [2, 8, 81], + [56, 67, 73], + [5, 38, 50], + [12, 38, 64], + [59, 72, 80], + [3, 26, 79], + [45, 76, 81], + [1, 65, 74], + [7, 18, 77], + [11, 56, 59], + [14, 39, 54], + [16, 37, 66], + [10, 28, 55], + [15, 60, 70], + [17, 25, 82], + [20, 30, 31], + [12, 67, 68], + [23, 75, 80], + [27, 32, 62], + [24, 69, 75], + [19, 21, 71], + [34, 53, 61], + [35, 46, 47], + [33, 59, 76], + [40, 43, 83], + [41, 42, 63], + [49, 75, 83], + [20, 44, 48], + [42, 49, 57], ]; /// Number of entries per row in FTX_LDPC_NM. pub const FTX_LDPC_NUM_ROWS: [u8; FTX_LDPC_M] = [ - 7, 6, 6, 6, 7, 6, 7, 6, 6, 7, 6, 6, 7, 7, 6, 6, - 6, 7, 6, 7, 6, 7, 6, 6, 6, 7, 6, 6, 6, 7, 6, 6, - 6, 6, 7, 6, 6, 6, 7, 7, 6, 6, 6, 6, 7, 7, 6, 6, - 6, 6, 7, 6, 6, 6, 7, 6, 6, 6, 6, 7, 6, 6, 6, 7, - 6, 6, 6, 7, 7, 6, 6, 7, 6, 6, 6, 6, 6, 6, 6, 7, - 6, 6, 6, + 7, 6, 6, 6, 7, 6, 7, 6, 6, 7, 6, 6, 7, 7, 6, 6, 6, 7, 6, 7, 6, 7, 6, 6, 6, 7, 6, 6, 6, 7, 6, 6, + 6, 6, 7, 6, 6, 6, 7, 7, 6, 6, 6, 6, 7, 7, 6, 6, 6, 6, 7, 6, 6, 6, 7, 6, 6, 6, 6, 7, 6, 6, 6, 7, + 6, 6, 6, 7, 7, 6, 6, 7, 6, 6, 6, 6, 6, 6, 6, 7, 6, 6, 6, ]; diff --git a/src/decoders/trx-ftx/src/decode.rs b/src/decoders/trx-ftx/src/decode.rs index 27a1761..6041c20 100644 --- a/src/decoders/trx-ftx/src/decode.rs +++ b/src/decoders/trx-ftx/src/decode.rs @@ -9,7 +9,7 @@ use num_complex::Complex32; use crate::constants::*; -use crate::monitor::{WfElem, Waterfall}; +use crate::monitor::{Waterfall, WfElem}; use crate::protocol::*; /// Candidate position in time and frequency. @@ -55,15 +55,25 @@ fn wf_mag_at(wf: &Waterfall, base: usize, idx: isize) -> &WfElem { if i < wf.mag.len() { &wf.mag[i] } else { - &WfElem { mag: -120.0, phase: 0.0 } + &WfElem { + mag: -120.0, + phase: 0.0, + } } } // Leaked reference for out-of-bounds default -static DEFAULT_WF_ELEM: WfElem = WfElem { mag: -120.0, phase: 0.0 }; +static DEFAULT_WF_ELEM: WfElem = WfElem { + mag: -120.0, + phase: 0.0, +}; fn wf_mag_safe(wf: &Waterfall, idx: usize) -> &WfElem { - if idx < wf.mag.len() { &wf.mag[idx] } else { &DEFAULT_WF_ELEM } + if idx < wf.mag.len() { + &wf.mag[idx] + } else { + &DEFAULT_WF_ELEM + } } fn ft8_sync_score(wf: &Waterfall, cand: &Candidate) -> i32 { @@ -75,8 +85,12 @@ fn ft8_sync_score(wf: &Waterfall, cand: &Candidate) -> i32 { for k in 0..FT8_LENGTH_SYNC { let block = FT8_SYNC_OFFSET * m + k; let block_abs = cand.time_offset as i32 + block as i32; - if block_abs < 0 { continue; } - if block_abs >= wf.num_blocks as i32 { break; } + if block_abs < 0 { + continue; + } + if block_abs >= wf.num_blocks as i32 { + break; + } let p_offset = base + block * wf.block_stride; let sm = FT8_COSTAS_PATTERN[k] as usize; @@ -96,7 +110,11 @@ fn ft8_sync_score(wf: &Waterfall, cand: &Candidate) -> i32 { if k > 0 && block_abs > 0 { let a = wf_mag_safe(wf, p_offset + sm).mag_int(); let b_idx = (p_offset + sm).wrapping_sub(wf.block_stride); - let b = if b_idx < wf.mag.len() { wf.mag[b_idx].mag_int() } else { 0 }; + let b = if b_idx < wf.mag.len() { + wf.mag[b_idx].mag_int() + } else { + 0 + }; score += a - b; num_average += 1; } @@ -109,7 +127,11 @@ fn ft8_sync_score(wf: &Waterfall, cand: &Candidate) -> i32 { } } - if num_average > 0 { score / num_average } else { 0 } + if num_average > 0 { + score / num_average + } else { + 0 + } } fn ft4_sync_score(wf: &Waterfall, cand: &Candidate) -> i32 { @@ -121,8 +143,12 @@ fn ft4_sync_score(wf: &Waterfall, cand: &Candidate) -> i32 { for k in 0..FT4_LENGTH_SYNC { let block = 1 + FT4_SYNC_OFFSET * m + k; let block_abs = cand.time_offset as i32 + block as i32; - if block_abs < 0 { continue; } - if block_abs >= wf.num_blocks as i32 { break; } + if block_abs < 0 { + continue; + } + if block_abs >= wf.num_blocks as i32 { + break; + } let p_offset = base + block * wf.block_stride; let sm = FT4_COSTAS_PATTERN[m][k] as usize; @@ -142,7 +168,11 @@ fn ft4_sync_score(wf: &Waterfall, cand: &Candidate) -> i32 { if k > 0 && block_abs > 0 { let a = wf_mag_safe(wf, p_offset + sm).mag_int(); let b_idx = (p_offset + sm).wrapping_sub(wf.block_stride); - let b = if b_idx < wf.mag.len() { wf.mag[b_idx].mag_int() } else { 0 }; + let b = if b_idx < wf.mag.len() { + wf.mag[b_idx].mag_int() + } else { + 0 + }; score += a - b; num_average += 1; } @@ -155,7 +185,11 @@ fn ft4_sync_score(wf: &Waterfall, cand: &Candidate) -> i32 { } } - if num_average > 0 { score / num_average } else { 0 } + if num_average > 0 { + score / num_average + } else { + 0 + } } fn ft2_sync_score(wf: &Waterfall, cand: &Candidate) -> i32 { @@ -178,12 +212,16 @@ fn ft2_sync_score(wf: &Waterfall, cand: &Candidate) -> i32 { let elem = *wf_mag_safe(wf, sym_offset + tone); sum += wf_elem_to_complex(elem); } - if !complete { continue; } + if !complete { + continue; + } score_f += sum.norm(); groups += 1; } - if groups == 0 { return 0; } + if groups == 0 { + return 0; + } (score_f / groups as f32 * 8.0).round() as i32 } @@ -194,9 +232,15 @@ fn heapify_down(heap: &mut [Candidate], size: usize) { let left = 2 * current + 1; let right = left + 1; let mut smallest = current; - if left < size && heap[left].score < heap[smallest].score { smallest = left; } - if right < size && heap[right].score < heap[smallest].score { smallest = right; } - if smallest == current { break; } + if left < size && heap[left].score < heap[smallest].score { + smallest = left; + } + if right < size && heap[right].score < heap[smallest].score { + smallest = right; + } + if smallest == current { + break; + } heap.swap(current, smallest); current = smallest; } @@ -206,14 +250,20 @@ fn heapify_up(heap: &mut [Candidate], size: usize) { let mut current = size - 1; while current > 0 { let parent = (current - 1) / 2; - if heap[current].score >= heap[parent].score { break; } + if heap[current].score >= heap[parent].score { + break; + } heap.swap(current, parent); current = parent; } } /// Find candidate signals in the waterfall. Returns sorted candidates (best first). -pub fn ftx_find_candidates(wf: &Waterfall, max_candidates: usize, min_score: i32) -> Vec { +pub fn ftx_find_candidates( + wf: &Waterfall, + max_candidates: usize, + min_score: i32, +) -> Vec { let is_ft2 = wf.protocol == FtxProtocol::Ft2; let num_tones = if wf.protocol.uses_ft4_layout() { 4 } else { 8 }; @@ -325,7 +375,13 @@ fn ft4_extract_likelihood(wf: &Waterfall, cand: &Candidate, log174: &mut [f32; F let base = get_cand_offset(wf, cand); for k in 0..FT4_ND { - let sym_idx = k + if k < 29 { 5 } else if k < 58 { 9 } else { 13 }; + let sym_idx = k + if k < 29 { + 5 + } else if k < 58 { + 9 + } else { + 13 + }; let bit_idx = 2 * k; let block = cand.time_offset as i32 + sym_idx as i32; @@ -358,7 +414,9 @@ fn ft2_extract_likelihood(wf: &Waterfall, cand: &Candidate, log174: &mut [f32; F for frame_sym in 0..frame_syms { let sym_idx = frame_sym + 1; // skip ramp-up let block = cand.time_offset as i32 + sym_idx as i32; - if block < 0 || block >= wf.num_blocks as i32 { continue; } + if block < 0 || block >= wf.num_blocks as i32 { + continue; + } let sym_offset = base + sym_idx * wf.block_stride; for tone in 0..4 { let elem = *wf_mag_safe(wf, sym_offset + tone); @@ -399,7 +457,14 @@ fn ft2_extract_likelihood(wf: &Waterfall, cand: &Candidate, log174: &mut [f32; F // Map to 174 data bits, selecting max-magnitude metric for data_sym in 0..FT2_ND { - let frame_sym = data_sym + if data_sym < 29 { 4 } else if data_sym < 58 { 8 } else { 12 }; + let frame_sym = data_sym + + if data_sym < 29 { + 4 + } else if data_sym < 58 { + 8 + } else { + 12 + }; let src_bit = 2 * frame_sym; let dst_bit = 2 * data_sym; @@ -418,7 +483,12 @@ fn ft2_extract_likelihood(wf: &Waterfall, cand: &Candidate, log174: &mut [f32; F } } -fn ft2_extract_logl_seq(symbols: &[[Complex32; 103]; 4], start_sym: usize, n_syms: usize, metrics: &mut [f32]) { +fn ft2_extract_logl_seq( + symbols: &[[Complex32; 103]; 4], + start_sym: usize, + n_syms: usize, + metrics: &mut [f32], +) { let n_bits = 2 * n_syms; let n_sequences = 1 << n_bits; @@ -438,7 +508,9 @@ fn ft2_extract_logl_seq(symbols: &[[Complex32; 103]; 4], start_sym: usize, n_sym let strength = sum.norm(); let mask_bit = n_bits - bit - 1; if (seq >> mask_bit) & 1 != 0 { - if strength > max_one { max_one = strength; } + if strength > max_one { + max_one = strength; + } } else if strength > max_zero { max_zero = strength; } @@ -470,7 +542,9 @@ fn ftx_normalize_logl(log174: &mut [f32; FTX_LDPC_N]) { /// Pack bits into bytes (MSB first). pub fn pack_bits(bit_array: &[u8], num_bits: usize, packed: &mut [u8]) { let num_bytes = num_bits.div_ceil(8); - for b in packed[..num_bytes].iter_mut() { *b = 0; } + for b in packed[..num_bytes].iter_mut() { + *b = 0; + } let mut mask: u8 = 0x80; let mut byte_idx = 0; for i in 0..num_bits { @@ -560,7 +634,9 @@ pub fn ftx_post_decode_snr(wf: &Waterfall, cand: &Candidate, message: &FtxMessag for sym in 0..nn { let block_abs = cand.time_offset as i32 + sym as i32; - if block_abs < 0 || block_abs >= wf.num_blocks as i32 { continue; } + if block_abs < 0 || block_abs >= wf.num_blocks as i32 { + continue; + } let p_offset = base + sym * wf.block_stride; let sig_db = wf_mag_safe(wf, p_offset + tones[sym] as usize).mag; @@ -568,7 +644,9 @@ pub fn ftx_post_decode_snr(wf: &Waterfall, cand: &Candidate, message: &FtxMessag let mut noise_min = 0.0f32; let mut found_noise = false; for t in 0..num_tones { - if t == tones[sym] as usize { continue; } + if t == tones[sym] as usize { + continue; + } let db = wf_mag_safe(wf, p_offset + t).mag; if !found_noise || db < noise_min { noise_min = db; diff --git a/src/decoders/trx-ftx/src/decoder.rs b/src/decoders/trx-ftx/src/decoder.rs index 3738345..b64b6ce 100644 --- a/src/decoders/trx-ftx/src/decoder.rs +++ b/src/decoders/trx-ftx/src/decoder.rs @@ -138,6 +138,7 @@ impl Ft8Decoder { /// Reset the decoder state for a new decode cycle. pub fn reset(&mut self) { self.monitor.reset(); + self.callsign_hash.cleanup(10); if let Some(ref mut pipe) = self.ft2_pipeline { pipe.reset(); } diff --git a/src/decoders/trx-ftx/src/encode.rs b/src/decoders/trx-ftx/src/encode.rs index 46ce54d..0a28455 100644 --- a/src/decoders/trx-ftx/src/encode.rs +++ b/src/decoders/trx-ftx/src/encode.rs @@ -7,9 +7,7 @@ use crate::constants::{ FTX_LDPC_GENERATOR, }; use crate::crc::ftx_add_crc; -use crate::protocol::{ - FT4_NN, FT8_NN, FTX_LDPC_K, FTX_LDPC_K_BYTES, FTX_LDPC_M, FTX_LDPC_N_BYTES, -}; +use crate::protocol::{FT4_NN, FT8_NN, FTX_LDPC_K, FTX_LDPC_K_BYTES, FTX_LDPC_M, FTX_LDPC_N_BYTES}; /// Returns 1 if an odd number of bits are set in `x`, zero otherwise. fn parity8(x: u8) -> u8 { @@ -29,11 +27,7 @@ fn parity8(x: u8) -> u8 { fn encode174(message: &[u8], codeword: &mut [u8]) { // Fill the codeword with message and zeros for j in 0..FTX_LDPC_N_BYTES { - codeword[j] = if j < FTX_LDPC_K_BYTES { - message[j] - } else { - 0 - }; + codeword[j] = if j < FTX_LDPC_K_BYTES { message[j] } else { 0 }; } // Compute the byte index and bit mask for the first checksum bit @@ -230,8 +224,9 @@ mod tests { // The codeword should start with the message bytes (systematic code). // Byte 11 shares bits between the last 3 message bits and the first // parity bits, so only check bytes 0..10 for exact match. - let message: [u8; FTX_LDPC_K_BYTES] = - [0x12, 0x34, 0x56, 0x78, 0x9A, 0xBC, 0xDE, 0xF0, 0x11, 0x22, 0x33, 0x40]; + let message: [u8; FTX_LDPC_K_BYTES] = [ + 0x12, 0x34, 0x56, 0x78, 0x9A, 0xBC, 0xDE, 0xF0, 0x11, 0x22, 0x33, 0x40, + ]; let mut codeword = [0u8; FTX_LDPC_N_BYTES]; encode174(&message, &mut codeword); @@ -246,8 +241,9 @@ mod tests { #[test] fn encode174_nonzero_parity() { // A non-zero message should produce non-zero parity bits - let message: [u8; FTX_LDPC_K_BYTES] = - [0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0xE0]; + let message: [u8; FTX_LDPC_K_BYTES] = [ + 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0xE0, + ]; let mut codeword = [0u8; FTX_LDPC_N_BYTES]; encode174(&message, &mut codeword); @@ -255,7 +251,10 @@ mod tests { let parity_nonzero = codeword[FTX_LDPC_K_BYTES..FTX_LDPC_N_BYTES] .iter() .any(|&b| b != 0); - assert!(parity_nonzero, "Parity bits should be non-zero for non-zero input"); + assert!( + parity_nonzero, + "Parity bits should be non-zero for non-zero input" + ); } #[test] diff --git a/src/decoders/trx-ftx/src/ldpc.rs b/src/decoders/trx-ftx/src/ldpc.rs index 945a9ef..8051e90 100644 --- a/src/decoders/trx-ftx/src/ldpc.rs +++ b/src/decoders/trx-ftx/src/ldpc.rs @@ -65,10 +65,8 @@ pub fn ldpc_decode( plain: &mut [u8; FTX_LDPC_N], ) -> i32 { // Allocate m[][] and e[][] on the heap (~60 kB each) to avoid stack overflow. - let mut m_matrix: Vec> = - vec![vec![0.0f32; FTX_LDPC_N]; FTX_LDPC_M]; - let mut e_matrix: Vec> = - vec![vec![0.0f32; FTX_LDPC_N]; FTX_LDPC_M]; + let mut m_matrix: Vec> = vec![vec![0.0f32; FTX_LDPC_N]; FTX_LDPC_M]; + let mut e_matrix: Vec> = vec![vec![0.0f32; FTX_LDPC_N]; FTX_LDPC_M]; // Initialize m[][] with the channel LLRs. for j in 0..FTX_LDPC_M { diff --git a/src/decoders/trx-ftx/src/lib.rs b/src/decoders/trx-ftx/src/lib.rs index a1b3c6d..c6bf0d1 100644 --- a/src/decoders/trx-ftx/src/lib.rs +++ b/src/decoders/trx-ftx/src/lib.rs @@ -2,21 +2,14 @@ // // SPDX-License-Identifier: BSD-2-Clause -pub mod protocol; +pub mod callsign_hash; pub mod constants; pub mod crc; -pub mod text; -#[allow(clippy::manual_memcpy, clippy::needless_range_loop)] -pub mod ldpc; -#[allow(clippy::needless_range_loop)] -pub mod encode; -pub mod callsign_hash; -#[allow(clippy::explicit_counter_loop, clippy::needless_range_loop)] -pub mod message; -#[allow(dead_code)] -pub mod monitor; #[allow(dead_code, clippy::needless_range_loop)] pub mod decode; +mod decoder; +#[allow(clippy::needless_range_loop)] +pub mod encode; #[allow( dead_code, clippy::manual_memcpy, @@ -24,6 +17,13 @@ pub mod decode; clippy::too_many_arguments )] pub mod ft2; -mod decoder; +#[allow(clippy::manual_memcpy, clippy::needless_range_loop)] +pub mod ldpc; +#[allow(clippy::explicit_counter_loop, clippy::needless_range_loop)] +pub mod message; +#[allow(dead_code)] +pub mod monitor; +pub mod protocol; +pub mod text; -pub use decoder::{Ft8Decoder, Ft8DecodeResult}; +pub use decoder::{Ft8DecodeResult, Ft8Decoder}; diff --git a/src/decoders/trx-ftx/src/message.rs b/src/decoders/trx-ftx/src/message.rs index 6201080..b7c0222 100644 --- a/src/decoders/trx-ftx/src/message.rs +++ b/src/decoders/trx-ftx/src/message.rs @@ -178,9 +178,7 @@ fn is_space(c: u8) -> bool { /// whitespace). fn copy_token(input: &str) -> (&str, String) { let input = input.trim_start(); - let end = input - .find(' ') - .unwrap_or(input.len()); + let end = input.find(' ').unwrap_or(input.len()); let token = &input[..end]; let rest = &input[end..].trim_start(); (rest, token.to_string()) @@ -307,11 +305,7 @@ pub fn pack_basecall(callsign: &str) -> Option { c6[i + 3] = b; } } - } else if starts_with(callsign, "3X") - && length > 2 - && is_letter(bytes[2]) - && length <= 7 - { + } else if starts_with(callsign, "3X") && length > 2 && is_letter(bytes[2]) && length <= 7 { // Guinea prefix: 3XA0XYZ -> QA0XYZ c6[0] = b'Q'; for (i, &b) in bytes[2..].iter().enumerate() { @@ -361,10 +355,7 @@ pub fn pack_basecall(callsign: &str) -> Option { /// Pack a special token, a 22-bit hash code, or a valid base call into a /// 28-bit integer. Returns `(n28, ip)` on success, or `None` on error. -fn pack28( - callsign: &str, - hash_table: Option<&mut CallsignHashTable>, -) -> Option<(i32, u8)> { +fn pack28(callsign: &str, hash_table: Option<&mut CallsignHashTable>) -> Option<(i32, u8)> { let mut ip: u8 = 0; // Check for special tokens @@ -452,11 +443,7 @@ fn unpack28( let n28_adj = n28 - NTOKENS; if n28_adj < MAX22 { // 22-bit hashed callsign - let call = lookup_callsign( - hash_table.as_deref(), - HashType::Hash22Bits, - n28_adj, - ); + let call = lookup_callsign(hash_table.as_deref(), HashType::Hash22Bits, n28_adj); return Some((call, FtxFieldType::Call)); } @@ -519,10 +506,7 @@ fn unpack28( // --------------------------------------------------------------------------- /// Pack a non-standard callsign into a 58-bit integer. -fn pack58( - hash_table: Option<&mut CallsignHashTable>, - callsign: &str, -) -> Option { +fn pack58(hash_table: Option<&mut CallsignHashTable>, callsign: &str) -> Option { let src = callsign.trim_start_matches('<').trim_end_matches('>'); let mut result: u64 = 0; @@ -545,10 +529,7 @@ fn pack58( } /// Unpack a non-standard callsign from a 58-bit integer. -fn unpack58( - n58: u64, - hash_table: Option<&mut CallsignHashTable>, -) -> Option { +fn unpack58(n58: u64, hash_table: Option<&mut CallsignHashTable>) -> Option { let mut c11 = [0u8; 11]; let mut n = n58; @@ -643,11 +624,7 @@ fn unpackgrid(igrid4: u16, ir: u8) -> Option<(String, FtxFieldType)> { (b'0' + d3) as char, ); - let result = if ir > 0 { - format!("R {}", grid) - } else { - grid - }; + let result = if ir > 0 { format!("R {}", grid) } else { grid }; Some((result, FtxFieldType::Grid)) } else { @@ -776,10 +753,7 @@ pub fn ftx_message_encode_std( let icq = call_to == "CQ" || starts_with(call_to, "CQ "); if let Some(slash_pos) = call_de.find('/') { - if slash_pos >= 2 - && icq - && !(call_de.ends_with("/P") || call_de.ends_with("/R")) - { + if slash_pos >= 2 && icq && !(call_de.ends_with("/P") || call_de.ends_with("/R")) { return FtxMessageRc::ErrorCallsign2; } } @@ -952,30 +926,26 @@ pub fn ftx_message_decode( let msg_type = msg.get_type(); let (field1, field2, field3, rc) = match msg_type { - FtxMessageType::Standard => { - match ftx_message_decode_std(msg, hash_table) { - (Some(f1), Some(f2), Some(f3), types, rc) => { - offsets.types = types; - (Some(f1), Some(f2), Some(f3), rc) - } - (f1, f2, f3, types, rc) => { - offsets.types = types; - (f1, f2, f3, rc) - } + FtxMessageType::Standard => match ftx_message_decode_std(msg, hash_table) { + (Some(f1), Some(f2), Some(f3), types, rc) => { + offsets.types = types; + (Some(f1), Some(f2), Some(f3), rc) } - } - FtxMessageType::NonstdCall => { - match ftx_message_decode_nonstd(msg, hash_table) { - (Some(f1), Some(f2), Some(f3), types, rc) => { - offsets.types = types; - (Some(f1), Some(f2), Some(f3), rc) - } - (f1, f2, f3, types, rc) => { - offsets.types = types; - (f1, f2, f3, rc) - } + (f1, f2, f3, types, rc) => { + offsets.types = types; + (f1, f2, f3, rc) } - } + }, + FtxMessageType::NonstdCall => match ftx_message_decode_nonstd(msg, hash_table) { + (Some(f1), Some(f2), Some(f3), types, rc) => { + offsets.types = types; + (Some(f1), Some(f2), Some(f3), rc) + } + (f1, f2, f3, types, rc) => { + offsets.types = types; + (f1, f2, f3, rc) + } + }, FtxMessageType::FreeText => { let text = ftx_message_decode_free(msg); (Some(text), None, None, FtxMessageRc::Ok) @@ -1053,7 +1023,15 @@ pub fn ftx_message_decode_std( let (call_de, ft1) = match unpack28(n29b >> 1, (n29b & 1) as u8, i3, Some(hash_table)) { Some(v) => v, - None => return (Some(call_to), None, None, field_types, FtxMessageRc::ErrorCallsign2), + None => { + return ( + Some(call_to), + None, + None, + field_types, + FtxMessageRc::ErrorCallsign2, + ) + } }; field_types[1] = ft1; @@ -1479,10 +1457,10 @@ mod tests { ftx_message_encode_telemetry(&mut msg, &telemetry); // Set i3.n3 to 0.5 for telemetry type msg.payload[9] = (msg.payload[9] & 0xC0) | (5 << 3); // n3=5 requires special handling - // Actually, telemetry is i3=0 n3=5: need bits 71..73 = 5, bits 74..76 = 0 - // n3 is in bits 71..73: payload[8] bit0 -> n3 bit2, payload[9] bits 7..6 -> n3 bits 1..0 - // i3 is in bits 74..76: payload[9] bits 5..3 - // For i3=0, n3=5 (binary 101): bit2=1, bit1=0, bit0=1 + // Actually, telemetry is i3=0 n3=5: need bits 71..73 = 5, bits 74..76 = 0 + // n3 is in bits 71..73: payload[8] bit0 -> n3 bit2, payload[9] bits 7..6 -> n3 bits 1..0 + // i3 is in bits 74..76: payload[9] bits 5..3 + // For i3=0, n3=5 (binary 101): bit2=1, bit1=0, bit0=1 msg.payload[8] = (msg.payload[8] & 0xFE) | 1; // n3 bit2 = 1 msg.payload[9] = 0b01 << 6; // n3 bits 1..0 = 01, i3 = 0 diff --git a/src/decoders/trx-ftx/src/monitor.rs b/src/decoders/trx-ftx/src/monitor.rs index 0882110..24aa6d5 100644 --- a/src/decoders/trx-ftx/src/monitor.rs +++ b/src/decoders/trx-ftx/src/monitor.rs @@ -38,7 +38,13 @@ pub struct Waterfall { } impl Waterfall { - pub fn new(max_blocks: usize, num_bins: usize, time_osr: usize, freq_osr: usize, protocol: FtxProtocol) -> Self { + pub fn new( + max_blocks: usize, + num_bins: usize, + time_osr: usize, + freq_osr: usize, + protocol: FtxProtocol, + ) -> Self { let block_stride = time_osr * freq_osr * num_bins; let mag = vec![WfElem::default(); max_blocks * block_stride]; Self { @@ -115,7 +121,13 @@ impl Monitor { let num_bins = max_bin - min_bin; let max_blocks = (slot_time / symbol_period) as usize; - let wf = Waterfall::new(max_blocks, num_bins, cfg.time_osr as usize, cfg.freq_osr as usize, cfg.protocol); + let wf = Waterfall::new( + max_blocks, + num_bins, + cfg.time_osr as usize, + cfg.freq_osr as usize, + cfg.protocol, + ); let mut real_planner = RealFftPlanner::::new(); let real_fft = real_planner.plan_fft_forward(nfft); @@ -168,7 +180,8 @@ impl Monitor { for _time_sub in 0..self.wf.time_osr { // Shift new data into analysis frame let shift = self.nfft - self.subblock_size; - self.last_frame.copy_within(self.subblock_size..self.nfft, 0); + self.last_frame + .copy_within(self.subblock_size..self.nfft, 0); for pos in shift..self.nfft { self.last_frame[pos] = if frame_pos < frame.len() { frame[frame_pos] @@ -183,7 +196,11 @@ impl Monitor { self.fft_input[pos] = self.window[pos] * self.last_frame[pos]; } self.real_fft - .process_with_scratch(&mut self.fft_input, &mut self.fft_output, &mut self.fft_scratch) + .process_with_scratch( + &mut self.fft_input, + &mut self.fft_output, + &mut self.fft_scratch, + ) .expect("FFT process failed"); // Extract magnitude and phase for each frequency sub-bin @@ -206,7 +223,10 @@ impl Monitor { } } else { if offset < self.wf.mag.len() { - self.wf.mag[offset] = WfElem { mag: -120.0, phase: 0.0 }; + self.wf.mag[offset] = WfElem { + mag: -120.0, + phase: 0.0, + }; } offset += 1; } diff --git a/src/decoders/trx-ftx/src/protocol.rs b/src/decoders/trx-ftx/src/protocol.rs index bd73463..9353325 100644 --- a/src/decoders/trx-ftx/src/protocol.rs +++ b/src/decoders/trx-ftx/src/protocol.rs @@ -36,32 +36,56 @@ impl FtxProtocol { /// Number of data symbols. pub fn nd(self) -> usize { - if self.uses_ft4_layout() { FT4_ND } else { FT8_ND } + if self.uses_ft4_layout() { + FT4_ND + } else { + FT8_ND + } } /// Total channel symbols. pub fn nn(self) -> usize { - if self.uses_ft4_layout() { FT4_NN } else { FT8_NN } + if self.uses_ft4_layout() { + FT4_NN + } else { + FT8_NN + } } /// Length of each sync group. pub fn sync_length(self) -> usize { - if self.uses_ft4_layout() { FT4_LENGTH_SYNC } else { FT8_LENGTH_SYNC } + if self.uses_ft4_layout() { + FT4_LENGTH_SYNC + } else { + FT8_LENGTH_SYNC + } } /// Number of sync groups. pub fn num_sync(self) -> usize { - if self.uses_ft4_layout() { FT4_NUM_SYNC } else { FT8_NUM_SYNC } + if self.uses_ft4_layout() { + FT4_NUM_SYNC + } else { + FT8_NUM_SYNC + } } /// Offset between sync groups. pub fn sync_offset(self) -> usize { - if self.uses_ft4_layout() { FT4_SYNC_OFFSET } else { FT8_SYNC_OFFSET } + if self.uses_ft4_layout() { + FT4_SYNC_OFFSET + } else { + FT8_SYNC_OFFSET + } } /// Number of FSK tones. pub fn num_tones(self) -> usize { - if self.uses_ft4_layout() { 4 } else { 8 } + if self.uses_ft4_layout() { + 4 + } else { + 8 + } } } diff --git a/src/decoders/trx-ftx/src/text.rs b/src/decoders/trx-ftx/src/text.rs index a9ab78d..47da494 100644 --- a/src/decoders/trx-ftx/src/text.rs +++ b/src/decoders/trx-ftx/src/text.rs @@ -108,16 +108,14 @@ pub fn nchar(c: char, table: CharTable) -> Option { // Extra symbols match table { - CharTable::Full => { - match c { - '+' => return Some(n), - '-' => return Some(n + 1), - '.' => return Some(n + 2), - '/' => return Some(n + 3), - '?' => return Some(n + 4), - _ => {} - } - } + CharTable::Full => match c { + '+' => return Some(n), + '-' => return Some(n + 1), + '.' => return Some(n + 2), + '/' => return Some(n + 3), + '?' => return Some(n + 4), + _ => {} + }, CharTable::AlphanumSpaceSlash => { if c == '/' { return Some(n); @@ -240,11 +238,7 @@ mod tests { let expected = " 0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ+-./?"; for (i, ch) in expected.chars().enumerate() { assert_eq!(charn(i as i32, CharTable::Full), ch, "charn({i})"); - assert_eq!( - nchar(ch, CharTable::Full), - Some(i as i32), - "nchar('{ch}')" - ); + assert_eq!(nchar(ch, CharTable::Full), Some(i as i32), "nchar('{ch}')"); } } @@ -269,11 +263,7 @@ mod tests { fn alphanum_space_round_trip() { let expected = " 0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"; for (i, ch) in expected.chars().enumerate() { - assert_eq!( - charn(i as i32, CharTable::AlphanumSpace), - ch, - "charn({i})" - ); + assert_eq!(charn(i as i32, CharTable::AlphanumSpace), ch, "charn({i})"); assert_eq!( nchar(ch, CharTable::AlphanumSpace), Some(i as i32), @@ -286,11 +276,7 @@ mod tests { fn letters_space_round_trip() { let expected = " ABCDEFGHIJKLMNOPQRSTUVWXYZ"; for (i, ch) in expected.chars().enumerate() { - assert_eq!( - charn(i as i32, CharTable::LettersSpace), - ch, - "charn({i})" - ); + assert_eq!(charn(i as i32, CharTable::LettersSpace), ch, "charn({i})"); assert_eq!( nchar(ch, CharTable::LettersSpace), Some(i as i32), diff --git a/src/decoders/trx-wspr/src/decoder.rs b/src/decoders/trx-wspr/src/decoder.rs index 43967f3..6bfca16 100644 --- a/src/decoders/trx-wspr/src/decoder.rs +++ b/src/decoders/trx-wspr/src/decoder.rs @@ -290,7 +290,12 @@ mod tests { let base_hz = 1496.0_f32; let start = EXPECTED_SIGNAL_START_SAMPLES; - for (sym, sync_tone) in SYNC_VECTOR.iter().copied().enumerate().take(WSPR_SYMBOL_COUNT) { + for (sym, sync_tone) in SYNC_VECTOR + .iter() + .copied() + .enumerate() + .take(WSPR_SYMBOL_COUNT) + { let tone = sync_tone + 2 * ((sym % 2) as u8); let freq = base_hz + tone as f32 * TONE_SPACING_HZ; let begin = start + sym * WSPR_SYMBOL_SAMPLES; @@ -347,7 +352,12 @@ mod tests { // Generate a synthetic WSPR-like signal using the sync vector let mut signal = vec![0.0_f32; WSPR_SIGNAL_SAMPLES]; - for (sym, sync_tone) in SYNC_VECTOR.iter().copied().enumerate().take(WSPR_SYMBOL_COUNT) { + for (sym, sync_tone) in SYNC_VECTOR + .iter() + .copied() + .enumerate() + .take(WSPR_SYMBOL_COUNT) + { let freq = base_hz + sync_tone as f32 * TONE_SPACING_HZ; let begin = sym * WSPR_SYMBOL_SAMPLES; for i in 0..WSPR_SYMBOL_SAMPLES {