use clap::Parser; use std::io::{Read, Write, Result}; use std::net::{TcpListener, TcpStream}; use std::thread; use std::time::Duration; // Framing tags const TAG_CONTROL_OUT: u8 = 0x10; const TAG_CONTROL_IN: u8 = 0x11; const TAG_BULK: u8 = 0x20; const ADSB_MESSAGE: [u8; 14] = [ 0x8D, 0x48, 0x40, 0xD6, 0x20, 0x2C, 0xC3, 0x71, 0xC3, 0x2C, 0xE0, 0x57, 0x60, 0x98, ]; #[derive(Parser, Debug)] #[command(author, version, about, long_about = None)] struct SimulationArgs { /// Host/IP to bind the TCP listener on #[arg(long, default_value = "127.0.0.1")] host: String, /// TCP port to bind the listener on #[arg(long, default_value = "9999")] port: u16, } fn main() { env_logger::init_from_env( env_logger::Env::default().filter_or("RUST_LOG", "warn,squawk_sim=info"), ); let args = SimulationArgs::parse(); // Build the bind address, e.g. "127.0.0.1:9999" let bind_address = format!("{}:{}", args.host, args.port); log::info!("Listening on {}", bind_address); // Start listening for incoming TCP connections let listener = TcpListener::bind(&bind_address) .unwrap_or_else(|err| panic!("failed to bind {}: {}", bind_address, err)); // Accept connections in a loop for incoming_connection in listener.incoming() { match incoming_connection { Ok(client_stream) => { // Spawn a thread per client thread::spawn(move || { if let Err(err) = handle_client_connection(client_stream) { log::error!("connection error: {}", err); } }); } Err(err) => log::error!("error accepting connection: {}", err), } } } /// Handle a single client connection fn handle_client_connection(mut connection: TcpStream) -> Result<()> { log::info!("Connection established"); loop { // Read the 4-byte header: [tag:1][bRequest:1][length:2 LE] let mut header_buffer = [0u8; 4]; if connection.read_exact(&mut header_buffer).is_err() { // Client closed on error break; } let message_tag = header_buffer[0]; let _b_request = header_buffer[1]; let payload_length = u16::from_le_bytes([header_buffer[2], header_buffer[3]]) as usize; log::trace!( "Received message '{:02x}' with payload length {}", message_tag, payload_length ); // Read the optional payload // let mut payload_buffer = vec![0u8; payload_length]; // if payload_length > 0 { // if connection.read(&mut payload_buffer).is_err() { // log::error!("error reading payload buffer"); // break; // } // } // Dispatch based on the framing tag match message_tag { TAG_CONTROL_OUT => { log::trace!("Received control out"); // Acknowledge with a status OK connection.write_all(&[0u8])?; } TAG_CONTROL_IN => { log::trace!("Received control in"); // STATUS(1) + LENGTH(2) + dummy payload connection.write_all(&[0x00])?; connection.write_all(&(payload_length as u16).to_le_bytes())?; connection.write_all(&vec![0x42; payload_length])?; } TAG_BULK => { log::trace!("Received bulk message"); let iq = generate_adsb_iq(); // STATUS(1) + LENGTH(4) + IQ data connection.write_all(&[0x00])?; connection.write_all(&(iq.len() as u32).to_le_bytes())?; connection.write_all(&iq)?; // Throttle a bit to simulate real USB/bulk behavior thread::sleep(Duration::from_millis(10)); } _unknown_tag => { log::warn!("Unknown message tag {}", _unknown_tag); break; } } } log::info!("Connection closed"); Ok(()) } fn generate_adsb_iq() -> Vec { let mut v = Vec::with_capacity(ADSB_MESSAGE.len() * 2); for &b in &ADSB_MESSAGE { v.push(b); // I v.push(0x80); // Q fixed } v } /// Build one preamble (8 bits) + 112 data bits /// Sampled at 2 Mhz (1 sample per half-bit). Interleaved I/Q bytes fn _generate_adsb_iq_samples() -> Vec { // Preamble bits (1us per bit at 2 Mhz -> 2 samples per bit) // Preamble is 8 bits: 1,0,1,0,1,0,0,0 let preamble_bits = [1, 0, 1, 0, 1, 0, 0, 0]; // Manchester encode the 112 data bits // bit=0 -> [1,0], bit=1 -> [0,1] (half-bit intervals) let mut manchester_bits = Vec::with_capacity(112 * 2); for &byte in ADSB_MESSAGE.iter() { for bit_idx in (0..8).rev() { let bit = (byte >> bit_idx) & 1; if bit == 0 { manchester_bits.push(1); manchester_bits.push(0); } else { manchester_bits.push(0); manchester_bits.push(1); } } } // Concatenate preamble + data let mut full_bitstream = Vec::with_capacity(preamble_bits.len() * 2 + manchester_bits.len()); // Preamble: each '1' or '0' is one microsecond = 2 samples for &pb in preamble_bits.iter() { // Push two identical half-bits = 2 samples full_bitstream.push(pb); full_bitstream.push(pb); } // Data: already in half-bit units = 1 sample per element full_bitstream.extend(manchester_bits); // Build interleaved I/Q samples // I = 128 + 127*(bit), Q = 128 let mut iq = Vec::with_capacity(full_bitstream.len() * 2); for &level in full_bitstream.iter() { let i_sample = if level == 1 { 255u8 } else { 128u8 }; let q_sample = 128u8; iq.push(i_sample); iq.push(q_sample); } iq }