use num_complex::Complex;
use crate::unpack_iq_24bit;
#[derive(Debug, Clone, Default)]
pub struct P1Status {
pub ptt: bool,
pub adc_overflow: u8,
pub forward_power: u16,
pub reverse_power: u16,
pub exciter_power: u16,
pub supply_voltage: u16,
pub response_addr: u8,
}
pub struct P1RxFrame {
pub samples: Vec<Vec<Complex<f64>>>,
pub status: P1Status,
}
pub fn parse_rx_packet(data: &[u8], nddc: usize) -> Option<P1RxFrame> {
if data.len() < 1032 || nddc == 0 {
return None;
}
let mut samples: Vec<Vec<Complex<f64>>> = (0..nddc).map(|_| Vec::new()).collect();
let mut status = P1Status::default();
for frame_idx in 0..2 {
let base = 8 + frame_idx * 512; let sync_ok = data[base] == 0x7F && data[base + 1] == 0x7F && data[base + 2] == 0x7F;
if !sync_ok {
return None;
}
let c0 = data[base + 3];
let c1 = data[base + 4];
let c2 = data[base + 5];
let c3 = data[base + 6];
let c4 = data[base + 7];
parse_response_control_bytes(&mut status, c0, c1, c2, c3, c4);
let iq_start = base + 8;
let iq_end = base + 512;
let bytes_per_sample = 6; let bytes_per_row = bytes_per_sample * nddc + 2;
let mut offset = iq_start;
while offset + bytes_per_row <= iq_end {
for (ddc, ddc_samples) in samples.iter_mut().enumerate().take(nddc) {
let sample_offset = offset + ddc * bytes_per_sample;
if sample_offset + bytes_per_sample <= data.len() {
let iq = unpack_iq_24bit(data, sample_offset);
ddc_samples.push(iq);
}
}
offset += bytes_per_row;
}
}
Some(P1RxFrame { samples, status })
}
pub fn parse_response_control_bytes(status: &mut P1Status, c0: u8, c1: u8, c2: u8, c3: u8, c4: u8) {
let addr = c0 & 0x7E;
status.ptt = (c0 & 0x01) != 0;
status.response_addr = addr;
match addr {
0x00 => {
status.adc_overflow = c1;
}
0x08 => {
status.exciter_power = ((c1 as u16) << 8) | (c2 as u16);
status.forward_power = ((c3 as u16) << 8) | (c4 as u16);
}
0x10 => {
status.reverse_power = ((c1 as u16) << 8) | (c2 as u16);
}
0x18 => {
status.supply_voltage = ((c3 as u16) << 8) | (c4 as u16);
}
_ => {}
}
}
pub fn build_tx_packet(
seq: u32,
tx_iq: &[Complex<f64>],
control_bytes: [u8; 5],
control_bytes2: [u8; 5],
) -> Vec<u8> {
let mut packet = vec![0u8; 1032];
packet[0] = 0xEF;
packet[1] = 0xFE;
packet[2] = 0x01; packet[3] = 0x02;
packet[4] = ((seq >> 24) & 0xFF) as u8;
packet[5] = ((seq >> 16) & 0xFF) as u8;
packet[6] = ((seq >> 8) & 0xFF) as u8;
packet[7] = (seq & 0xFF) as u8;
for frame_idx in 0..2u32 {
let base = 8 + (frame_idx as usize) * 512;
packet[base] = 0x7F;
packet[base + 1] = 0x7F;
packet[base + 2] = 0x7F;
let cb = if frame_idx == 0 {
&control_bytes
} else {
&control_bytes2
};
packet[base + 3] = cb[0];
packet[base + 4] = cb[1];
packet[base + 5] = cb[2];
packet[base + 6] = cb[3];
packet[base + 7] = cb[4];
let iq_start = base + 8;
let iq_end = base + 512;
let bytes_per_block = 8; let max_samples = (iq_end - iq_start) / bytes_per_block;
let sample_offset = (frame_idx as usize) * max_samples;
for i in 0..max_samples {
let sample_idx = sample_offset + i;
let (iv, qv) = if sample_idx < tx_iq.len() {
let s = &tx_iq[sample_idx];
let i_val = (s.re.clamp(-1.0, 1.0) * 32767.0) as i16;
let q_val = ((-s.im).clamp(-1.0, 1.0) * 32767.0) as i16;
(i_val, q_val)
} else {
(0i16, 0i16)
};
let offset = iq_start + i * bytes_per_block;
packet[offset] = 0;
packet[offset + 1] = 0;
packet[offset + 2] = 0;
packet[offset + 3] = 0;
packet[offset + 4] = ((iv as u16) >> 8) as u8;
packet[offset + 5] = (iv as u16 & 0xFF) as u8;
packet[offset + 6] = ((qv as u16) >> 8) as u8;
packet[offset + 7] = (qv as u16 & 0xFF) as u8;
}
}
packet
}
pub fn build_control_bytes(
address: u8,
frequency: u64,
sample_rate_index: u8,
ptt: bool,
nddc: u8,
rx_attenuation: u8,
tx_drive: u8,
) -> [u8; 5] {
let mut cb = [0u8; 5];
let ptt_bit = if ptt { 0x01 } else { 0x00 };
cb[0] = address | ptt_bit;
match address {
0x00 => {
cb[1] = sample_rate_index & 0x03;
cb[4] = (nddc.saturating_sub(1) & 0x07) << 3 | 0x04;
}
0x02 | 0x04 | 0x06 | 0x08 | 0x0A | 0x0C | 0x0E => {
let freq = frequency.min(u32::MAX as u64) as u32;
cb[1] = ((freq >> 24) & 0xFF) as u8;
cb[2] = ((freq >> 16) & 0xFF) as u8;
cb[3] = ((freq >> 8) & 0xFF) as u8;
cb[4] = (freq & 0xFF) as u8;
}
0x12 => {
cb[1] = tx_drive;
}
0x14 => {
cb[4] = rx_attenuation & 0x1F;
}
_ => {}
}
cb
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn parse_rx_packet_too_short() {
let data = vec![0u8; 100];
assert!(parse_rx_packet(&data, 1).is_none());
}
#[test]
fn parse_rx_packet_zero_nddc() {
let data = vec![0u8; 1032];
assert!(parse_rx_packet(&data, 0).is_none());
}
#[test]
fn parse_rx_packet_bad_sync() {
let mut data = vec![0u8; 1032];
assert!(parse_rx_packet(&data, 1).is_none());
data[8] = 0x7F;
data[9] = 0x7F;
data[10] = 0x7F;
assert!(parse_rx_packet(&data, 1).is_none());
}
#[test]
fn parse_rx_packet_valid_frame() {
let mut data = vec![0u8; 1032];
data[8] = 0x7F;
data[9] = 0x7F;
data[10] = 0x7F;
data[520] = 0x7F;
data[521] = 0x7F;
data[522] = 0x7F;
let frame = parse_rx_packet(&data, 1).unwrap();
assert_eq!(frame.samples.len(), 1);
assert_eq!(frame.samples[0].len(), 126);
}
#[test]
fn build_tx_packet_structure() {
let tx_iq = vec![Complex::new(0.5, -0.5); 126];
let cb1 = [0x00, 0x01, 0x00, 0x00, 0x00];
let cb2 = [0x02, 0x00, 0x07, 0x07, 0x40];
let pkt = build_tx_packet(42, &tx_iq, cb1, cb2);
assert_eq!(pkt.len(), 1032);
assert_eq!(pkt[0], 0xEF);
assert_eq!(pkt[1], 0xFE);
assert_eq!(pkt[2], 0x01);
assert_eq!(pkt[3], 0x02);
assert_eq!(u32::from_be_bytes([pkt[4], pkt[5], pkt[6], pkt[7]]), 42);
assert_eq!(&pkt[8..11], &[0x7F, 0x7F, 0x7F]);
assert_eq!(&pkt[11..16], &cb1);
assert_eq!(&pkt[520..523], &[0x7F, 0x7F, 0x7F]);
assert_eq!(&pkt[523..528], &cb2);
}
#[test]
fn build_control_bytes_address_0x00() {
let cb = build_control_bytes(0x00, 7_074_000, 1, false, 2, 0, 0);
assert_eq!(cb[0], 0x00); assert_eq!(cb[1], 0x01); assert_eq!(cb[4], (1 << 3) | 0x04); }
#[test]
fn build_control_bytes_address_0x00_ptt() {
let cb = build_control_bytes(0x00, 7_074_000, 0, true, 1, 0, 0);
assert_eq!(cb[0], 0x01); }
#[test]
fn build_control_bytes_frequency() {
let freq: u64 = 14_200_000;
let cb = build_control_bytes(0x04, freq, 0, false, 1, 0, 0);
assert_eq!(cb[0], 0x04);
let parsed_freq = u32::from_be_bytes([cb[1], cb[2], cb[3], cb[4]]);
assert_eq!(parsed_freq, freq as u32);
}
#[test]
fn build_control_bytes_attenuation() {
let cb = build_control_bytes(0x14, 0, 0, false, 1, 20, 0);
assert_eq!(cb[0], 0x14);
assert_eq!(cb[4], 20);
}
#[test]
fn build_control_bytes_tx_drive() {
let cb = build_control_bytes(0x12, 0, 0, false, 1, 0, 200);
assert_eq!(cb[0], 0x12);
assert_eq!(cb[1], 200);
}
#[test]
fn parse_response_control_bytes_addr_0x08() {
let mut status = P1Status::default();
parse_response_control_bytes(&mut status, 0x08, 0x12, 0x34, 0x56, 0x78);
assert_eq!(status.response_addr, 0x08);
assert_eq!(status.exciter_power, 0x1234);
assert_eq!(status.forward_power, 0x5678);
assert!(!status.ptt);
}
#[test]
fn parse_response_control_bytes_ptt() {
let mut status = P1Status::default();
parse_response_control_bytes(&mut status, 0x01, 0, 0, 0, 0); assert!(status.ptt);
assert_eq!(status.response_addr, 0x00);
}
#[test]
fn roundtrip_tx_rx() {
let tx_iq = vec![Complex::new(0.0, 0.0); 126];
let cb1 = [0x00, 0x00, 0x00, 0x00, 0x00];
let cb2 = [0x00, 0x00, 0x00, 0x00, 0x00];
let pkt = build_tx_packet(0, &tx_iq, cb1, cb2);
assert_eq!(pkt.len(), 1032);
assert_eq!(&pkt[8..11], &[0x7F, 0x7F, 0x7F]);
assert_eq!(&pkt[520..523], &[0x7F, 0x7F, 0x7F]);
}
}