Updated adsb stuff
This commit is contained in:
10
adsb/squawk_sim/Cargo.toml
Normal file
10
adsb/squawk_sim/Cargo.toml
Normal file
@@ -0,0 +1,10 @@
|
||||
[package]
|
||||
name = "squawk_sim"
|
||||
version = "0.1.0"
|
||||
edition = "2024"
|
||||
|
||||
[dependencies]
|
||||
adsb_lib = { path = "../adsb_lib" }
|
||||
clap = { version = "4.5.37", features = ["derive"] }
|
||||
log = "0.4.27"
|
||||
env_logger = "0.11.8"
|
||||
180
adsb/squawk_sim/src/main.rs
Normal file
180
adsb/squawk_sim/src/main.rs
Normal file
@@ -0,0 +1,180 @@
|
||||
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<u8> {
|
||||
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<u8> {
|
||||
// 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
|
||||
}
|
||||
Reference in New Issue
Block a user