Adding base for adsb

This commit is contained in:
2025-04-21 19:15:11 -04:00
parent 06f9a96498
commit 95e4b8abf3
15 changed files with 1185 additions and 9 deletions

4
adsb/adsb_lib/Cargo.toml Normal file
View File

@@ -0,0 +1,4 @@
[package]
name = "adsb_lib"
version = "0.1.0"
edition = "2021"

119
adsb/adsb_lib/src/lib.rs Normal file
View File

@@ -0,0 +1,119 @@
use std::io::Result;
pub trait RtlDevice {
/// Send a control message to the device
fn control_send(&mut self, b_request: u8, data: &[u8]) -> Result<()>;
/// Receive a control message from a device
fn control_recv(&mut self, b_request: u8, length: usize) -> Result<Vec<u8>>;
/// Read a chunk of raw IQ samples from the bulk-in endpoint
///
/// # Arguments
/// * `buffer` - the slice to fill with received data
///
/// # Returns
/// Number of bytes actually read
fn read_bulk(&mut self, buffer: &mut [u8]) -> Result<usize>;
}
pub fn run<S: RtlDevice>(device: &mut S) -> Result<()> {
// RESET
device.control_send(0x00, &[])?;
// SET_FREQ
device.control_send(0x02, &1_090_000_000u32.to_le_bytes())?;
// SET_SR
device.control_send(0x03, &2_400_000u32.to_le_bytes())?;
// AGC on
device.control_send(0x04, &[1])?;
// Precompute the preamble pattern in “halfbit” units (16 samples)
let preamble_halfbit_pattern = [
1,1, 0,0, 1,1, 0,0,
1,1, 0,0, 0,0, 0,0
];
// Create a big buffer to hold raw I/Q bytes
let mut iq_buffer = [0u8; 16_384];
loop {
// Read one bulk transfer's worth of I/Q data
let bytes_read = device.read_bulk(&mut iq_buffer)?;
if bytes_read < 32 {
// Must be at least 16 I/Q pairs
continue;
}
let raw = &iq_buffer[..bytes_read];
// Build a vector of "bit-samples" by thresholding I
// raw is [I0,Q0,I1,Q1,...], so step by 2
let mut halfbit_samples = Vec::with_capacity(raw.len() / 2);
for pair in raw.chunks_exact(2) {
let i_sample = pair[0] as u16;
// Threshold at 200
halfbit_samples.push(if i_sample > 200 {
1
} else {
0
});
}
// Scan for the 16-sample preamble
let mut data_start_index = None;
for idx in 0..halfbit_samples.len().saturating_sub(16) {
if &halfbit_samples[idx..idx + 16] == preamble_halfbit_pattern {
data_start_index = Some(idx + 16);
break;
}
}
let data_start = match data_start_index {
Some(i) => i,
None => continue // No preamble found in this chunk
};
// Collect 112 ADS-B bits, each manchester-encoded into 2 half-bits
// 224 half-bit samples total
let required_samples = 112 * 2;
if data_start + required_samples > halfbit_samples.len() {
// Not enough in this buffer
continue;
}
let manchester_slice = &halfbit_samples[data_start..data_start + required_samples];
// Manchester-decode pairs back into plain bits
let mut adsb_bits = Vec::with_capacity(112);
for window in manchester_slice.chunks_exact(2) {
match window {
[1, 0] => adsb_bits.push(0),
[0, 1] => adsb_bits.push(1),
_ => {
// Failed manchester pattern
adsb_bits.clear();
break;
}
}
}
if adsb_bits.len() != 112 {
// Data is malformed
continue;
}
// Pack 112 bits into 14 bytes (MSB first in each byte)
let mut adsb_payload = [0u8; 14];
for (bit_index, &bit_value) in adsb_bits.iter().enumerate() {
let byte_index = bit_index / 8;
let bit_in_byte = 7 - (bit_index % 8);
if bit_value == 1 {
adsb_payload[byte_index] |= 1 << bit_in_byte;
}
}
// Print out the 14-byte payload in hex
print!("ADS-B payload: ");
for byte in &adsb_payload {
print!("{:02X} ", byte);
}
println!();
}
}