Working on adsb

This commit is contained in:
2025-05-03 16:01:29 -04:00
parent 1d4b8338cc
commit dc1a6de6c6
6 changed files with 369 additions and 169 deletions

45
adsb/src/constants.rs Normal file
View File

@@ -0,0 +1,45 @@
use std::fmt::Display;
use std::time::Duration;
#[derive(Debug)]
pub struct DeviceInfo {
/// Vendor ID
pub vid: u16,
/// Product ID
pub pid: u16,
}
impl Display for DeviceInfo {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "VID: 0x{:04X} PID: 0x{:04X}", self.vid, self.pid)
}
}
// Devices
pub const DEVICE_RTL2832U: DeviceInfo = DeviceInfo {
vid: 0x0BDA,
pid: 0x2832,
};
// Timeout
pub const TIMEOUT: Duration = Duration::from_secs(1);
// pub const DEFAULT_BUFFER_LENGTH: usize = 4096;
pub const DEFAULT_BUFFER_LENGTH: usize = 64;
// Request Types
pub const REQ_CTRL_OUT: u8 =
rusb::constants::LIBUSB_ENDPOINT_OUT | rusb::constants::LIBUSB_REQUEST_TYPE_VENDOR;
// Blocks
pub const BLOCK_USB: u16 = 1;
// USB
pub const USB_EPA_CTL: u16 = 0x2148;
pub const USB_SYSCTL: u16 = 0x2000;
/// ADS-B downlink frequency (1090 MHz)
pub const ADSB_FREQUENCY_HZ: u32 = 1_090_000_000;
/// RTL-SDR sample rate in samples/second.
pub const SAMPLE_RATE_HZ: u32 = 2_048_000;

View File

@@ -1,23 +1,27 @@
use std::borrow::Cow;
use std::fmt::Display;
use std::sync::Arc;
use std::sync::atomic::{AtomicBool, Ordering};
use rusb::{
Context, Device, DeviceDescriptor, DeviceHandle, DeviceList, Direction, TransferType, UsbContext,
};
use crate::error::{Error, Result};
use std::time::Duration;
const TIMEOUT: Duration = Duration::from_secs(1);
use crate::constants::{
ADSB_FREQUENCY_HZ, BLOCK_USB, DEFAULT_BUFFER_LENGTH, REQ_CTRL_OUT, SAMPLE_RATE_HZ, TIMEOUT,
USB_EPA_CTL, USB_SYSCTL,
};
/// rusb/libusb implementation of `RtlSdrDevice`
pub struct RtlSdrDevice {
/// Device handle
handle: DeviceHandle<Context>,
device_desc: DeviceDescriptor,
device: Device<Context>,
endpoint: Endpoint,
frequency: u32,
rate: u32,
}
impl RtlSdrDevice {
/// Display dongle information
/// Display RTL SDR information
pub fn info(vid: u16, pid: u16) {
let device_list = match DeviceList::new() {
Ok(d) => d,
@@ -29,7 +33,7 @@ impl RtlSdrDevice {
for device in device_list.iter() {
match device.device_descriptor() {
Ok(device_desc) => {
if vid != device_desc.vendor_id() && pid != device_desc.product_id() {
if vid != device_desc.vendor_id() || pid != device_desc.product_id() {
continue;
}
println!(
@@ -43,7 +47,7 @@ impl RtlSdrDevice {
match device.open() {
Ok(handle) => {
println!("{}", device_info(&handle, &device_desc, " ", true));
},
}
Err(err) => {
eprintln!(" Unable to open device: {:?}", err);
continue;
@@ -58,7 +62,7 @@ impl RtlSdrDevice {
}
}
/// Open the USB device and return a wrapper
/// Open the RTL SDR device and return a wrapper
pub fn open(vid: u16, pid: u16) -> Result<Self> {
// Create a new libusb context
let ctx = Context::new().map_err(|_| Error::new("Unable to create libusb context"))?;
@@ -71,33 +75,226 @@ impl RtlSdrDevice {
if device_desc.vendor_id() == vid && device_desc.product_id() == pid {
let handle = device.open()?;
return Ok(Self {
handle,
device_desc,
device,
});
log::debug!("{}", device_info(&handle, &device_desc, "", false));
// Find the endpoint
let endpoint = match Endpoint::find(&device_desc, &device, TransferType::Bulk) {
Some(e) => e,
None => return Err(Error::new("Unable to find endpoint on device")),
};
log::debug!("Found readable endpoint: {:?}", endpoint.to_string());
let mut sdr = Self::new(handle, endpoint);
sdr.initialize()?;
return Ok(sdr);
}
}
Err(Error::new("No valid device found"))
}
pub fn read(&mut self, transfer_type: TransferType) -> Result<()> {
/// Close the RTL SDR device
pub fn close(&self) -> Result<()> {
log::debug!("Closing device...");
self.attach_kernel_driver(self.endpoint.interface);
self.handle.release_interface(self.endpoint.interface)?;
Ok(())
}
/// Process the USB data
pub fn process(&mut self, running: Arc<AtomicBool>) -> Result<()> {
log::debug!(
"Reading active configuration: {} ({:?})",
self.handle.active_configuration()?,
transfer_type
"Reading from active configuration: {}",
self.handle.active_configuration()?
);
log::debug!("{}", device_info(&self.handle, &self.device_desc, "", false));
// Read endpoint
match Endpoint::find_readable(&self.device, &self.device_desc, transfer_type) {
Some(endpoint) => endpoint.read(&mut self.handle)?,
None => log::warn!("No readable {:?} endpoint", transfer_type),
let mut buffer = [0u8; DEFAULT_BUFFER_LENGTH];
while running.load(Ordering::SeqCst) {
let s = self.read(&mut buffer)?;
log::debug!("Read: {}", s);
}
self.close()
}
fn new(handle: DeviceHandle<Context>, endpoint: Endpoint) -> Self {
Self {
handle,
endpoint,
frequency: 0,
rate: 0,
}
}
fn read(&self, buffer: &mut [u8; DEFAULT_BUFFER_LENGTH]) -> Result<String> {
let length = match self.endpoint.transfer_type {
TransferType::Interrupt => self
.handle
.read_interrupt(self.endpoint.address, buffer, TIMEOUT)
.map_err(|err| Error::new(format!("Unable to read interrupt from endpoint: {:?}", err)))?,
TransferType::Bulk => self
.handle
.read_bulk(self.endpoint.address, buffer, TIMEOUT)
.map_err(|err| Error::new(format!("Unable to read bulk from endpoint: {:?}", err)))?,
_ => 0,
};
log::trace!("Received {} bytes", length);
let s = match String::from_utf8_lossy(&buffer[..length]) {
Cow::Borrowed(s) => s.to_string(),
Cow::Owned(s) => s,
};
Ok(s.to_string())
}
fn initialize(&mut self) -> Result<()> {
// Configure the device for the endpoint
self.set_active_configuration(self.endpoint.config)?;
self.claim_interface(self.endpoint.interface)?;
self.set_alternate_setting(self.endpoint.interface, self.endpoint.setting)?;
self.detach_kernel_driver(self.endpoint.interface);
self.test_write()?;
// Reset the internal USB buffer
self.reset_buffer()?;
// Set the center-frequency in Hz
self.set_center_frequency(ADSB_FREQUENCY_HZ)?;
// Set the sample rate
self.set_sample_rate(SAMPLE_RATE_HZ)?;
Ok(())
}
fn set_active_configuration(&self, configuration: u8) -> Result<()> {
self
.handle
.set_active_configuration(configuration)
.map_err(|err| Error::new(format!("Failed to set active configuration: {:?}", err)))
}
fn claim_interface(&self, interface: u8) -> Result<()> {
self
.handle
.claim_interface(interface)
.map_err(|err| Error::new(format!("Failed to claim interface: {:?}", err)))
}
fn set_alternate_setting(&self, interface: u8, setting: u8) -> Result<()> {
self
.handle
.set_alternate_setting(interface, setting)
.map_err(|err| Error::new(format!("Failed to set alternate setting: {:?}", err)))
}
fn test_write(&self) -> Result<()> {
log::trace!("Testing write to device...");
let length = ctrl_write_register(&self.handle, BLOCK_USB, USB_SYSCTL, 0x89, 1)?;
if length == 0 {
log::info!("Resetting device");
self
.handle
.reset()
.map_err(|err| Error::new(format!("Failed to reset device: {:?}", err)))?;
} else {
log::trace!("Test write was successful");
}
Ok(())
}
fn reset_buffer(&self) -> Result<()> {
log::trace!("Resetting buffer...");
ctrl_write_register(&self.handle, BLOCK_USB, USB_EPA_CTL, 0x1002, 2)
.map_err(|err| Error::new(format!("Failed to reset the internal buffer: {:?}", err)))?;
ctrl_write_register(&self.handle, BLOCK_USB, USB_EPA_CTL, 0x0000, 2)
.map_err(|err| Error::new(format!("Failed to reset the internal buffer: {:?}", err)))?;
Ok(())
}
fn set_center_frequency(&mut self, frequency: u32) -> Result<()> {
log::trace!("Setting center_frequency to {}Hz", frequency);
self.frequency = frequency;
Ok(())
}
fn set_sample_rate(&mut self, rate: u32) -> Result<()> {
log::trace!("Setting sample_rate to {}Hz", rate);
self.rate = rate;
Ok(())
}
fn detach_kernel_driver(&self, interface: u8) {
// Detach the kernel driver if applicable
if let Ok(true) = self.handle.kernel_driver_active(interface) {
log::trace!("Detaching active kernel driver");
self.handle.detach_kernel_driver(interface).ok();
}
}
fn attach_kernel_driver(&self, interface: u8) {
// Attach the kernel driver if applicable
if let Ok(true) = self.handle.kernel_driver_active(interface) {
log::trace!("Attaching active kernel driver");
self.handle.attach_kernel_driver(interface).ok();
}
}
}
#[derive(Debug)]
pub struct Endpoint {
config: u8,
interface: u8,
setting: u8,
address: u8,
transfer_type: TransferType,
}
impl Display for Endpoint {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(
f,
"Config: {}, Interface: {}, Setting: {}, Address: 0x{:04X}, Transfer Type: {:?}",
self.config, self.interface, self.setting, self.address, self.transfer_type
)
}
}
impl Endpoint {
fn find<T: UsbContext>(
device_desc: &DeviceDescriptor,
device: &Device<T>,
transfer_type: TransferType,
) -> Option<Self> {
for n in 0..device_desc.num_configurations() {
let config_desc = match device.config_descriptor(n) {
Ok(c) => c,
Err(_) => continue,
};
for interface in config_desc.interfaces() {
for interface_desc in interface.descriptors() {
for endpoint_desc in interface_desc.endpoint_descriptors() {
if endpoint_desc.direction() == Direction::In
&& endpoint_desc.transfer_type() == transfer_type
{
return Some(Endpoint {
config: config_desc.number(),
interface: interface_desc.interface_number(),
setting: interface_desc.setting_number(),
address: endpoint_desc.address(),
transfer_type,
});
}
}
}
}
}
None
}
}
fn device_info<T: UsbContext>(
@@ -110,20 +307,23 @@ fn device_info<T: UsbContext>(
Ok(l) => l,
Err(err) => {
return format!("{} Unable to get languages: {:?}", offset, err);
},
}
};
let descriptor_type = device_desc.descriptor_type();
let mut output = String::new();
if full {
output = format!("{}Device Descriptor ({})\n", offset, descriptor_type).to_string();
output = format!("{}Device Descriptor ({})\n", offset, descriptor_type);
}
if !languages.is_empty() {
for language in languages {
let manufacturer = handle.read_manufacturer_string(language, device_desc, TIMEOUT)
let manufacturer = handle
.read_manufacturer_string(language, device_desc, TIMEOUT)
.unwrap_or_else(|err| err.to_string());
let product = handle.read_product_string(language, device_desc, TIMEOUT)
let product = handle
.read_product_string(language, device_desc, TIMEOUT)
.unwrap_or_else(|err| err.to_string());
let serial_number = handle.read_serial_number_string(language, device_desc, TIMEOUT)
let serial_number = handle
.read_serial_number_string(language, device_desc, TIMEOUT)
.unwrap_or_else(|err| err.to_string());
output.push_str(&format!(
"{}{}Manufacturer: {}, Product: {}, Serial Number: {}",
@@ -147,7 +347,7 @@ fn device_info<T: UsbContext>(
let protocol = device_desc.protocol_code();
let max_packet_size = device_desc.max_packet_size();
output.push_str(&format!(
"{}{}Class: {:#04x}, Subclass: {:#04x}, Protocol: {:#04x}, Max Packet Size: {}\n",
"{}{}Class: {:#04x}, Subclass: {:#04x}, Protocol: {:#04x}, Max Packet Size: {}",
offset, offset, class, sub_class, protocol, max_packet_size
))
}
@@ -156,119 +356,46 @@ fn device_info<T: UsbContext>(
output
}
#[derive(Debug)]
struct Endpoint {
config: u8,
interface: u8,
setting: u8,
address: u8,
transfer_type: TransferType,
fn ctrl_write_register<T: UsbContext>(
handle: &DeviceHandle<T>,
block: u16,
address: u16,
value: u16,
length: usize,
) -> rusb::Result<usize> {
assert!(length == 1 || length == 2);
let data: [u8; 2] = value.to_be_bytes();
let buffer = if length == 1 { &data[1..2] } else { &data };
let index = (block << 8) | 0x10;
log::trace!(
"Received block {}, address 0x{:04X}, value 0x{:04X}, length {} \
- writing control register: {} 0x{:04X} 0x{:04X} {:?}",
block,
address,
value,
length,
REQ_CTRL_OUT,
address,
index,
buffer
);
handle.write_control(REQ_CTRL_OUT, 0x00, address, index, buffer, TIMEOUT)
}
impl Endpoint {
pub fn find_readable<T: UsbContext>(
device: &Device<T>,
device_desc: &DeviceDescriptor,
transfer_type: TransferType,
) -> Option<Self> {
for n in 0..device_desc.num_configurations() {
let config_desc = match device.config_descriptor(n) {
Ok(c) => c,
Err(_) => continue,
};
fn demod_ctrl_write_register<T: UsbContext>(
handle: &DeviceHandle<T>,
page: u16,
address: u16,
value: u16,
length: usize,
) -> rusb::Result<usize> {
assert!(length == 1 || length == 2);
for interface in config_desc.interfaces() {
for interface_desc in interface.descriptors() {
for endpoint_desc in interface_desc.endpoint_descriptors() {
if endpoint_desc.direction() == Direction::In
&& endpoint_desc.transfer_type() == transfer_type
{
return Some(Self {
config: config_desc.number(),
interface: interface_desc.interface_number(),
setting: interface_desc.setting_number(),
address: endpoint_desc.address(),
transfer_type,
});
}
}
}
}
}
None
}
fn read<T: UsbContext>(&self, handle: &mut DeviceHandle<T>) -> Result<()> {
log::debug!("Reading from endpoint: {:?}", self);
let running = Arc::new(AtomicBool::new(true));
{
let running = running.clone();
ctrlc::set_handler(move || {
running.store(false, Ordering::SeqCst);
})?;
}
// Detach the kernel driver if applicable
let has_kernel_driver = match handle.kernel_driver_active(self.interface) {
Ok(true) => {
log::debug!("Detaching active kernel driver");
handle.detach_kernel_driver(self.interface).ok();
true
}
_ => false,
};
self.configure_endpoint(handle)?;
let mut buffer = [0u8; 4096];
while running.load(Ordering::SeqCst) {
let length = match self.transfer_type {
TransferType::Interrupt => handle
.read_interrupt(self.address, &mut buffer, TIMEOUT)
.map_err(|err| {
Error::new(format!("Unable to read interrupt from endpoint: {:?}", err))
})?,
TransferType::Bulk => handle
.read_bulk(self.address, &mut buffer, TIMEOUT)
.map_err(|err| Error::new(format!("Unable to read bulk from endpoint: {:?}", err)))?,
_ => 0,
};
log::debug!("Received: {:?}", &buffer[..length]);
}
// Attach the kernel driver if applicable
if has_kernel_driver {
log::debug!("Attaching active kernel driver");
handle.attach_kernel_driver(self.interface).ok();
}
log::debug!("Exiting USB read");
Ok(())
}
fn configure_endpoint<T: UsbContext>(&self, handle: &mut DeviceHandle<T>) -> Result<()> {
log::debug!("Configuring endpoint: {:?}", self);
// Switch to ADS-B mode
// let request_type = request_type(Direction::Out, RequestType::Vendor, Recipient::Interface);
// handle.write_control(
// request_type,
// 0x42,
// 0x0002,
// 0,
// &[],
// TIMEOUT,
// )?;
handle
.set_active_configuration(self.config)
.map_err(|err| Error::new(format!("Failed to set active configuration: {:?}", err)))?;
handle
.claim_interface(self.interface)
.map_err(|err| Error::new(format!("Failed to claim interface: {:?}", err)))?;
handle
.set_alternate_setting(self.interface, self.setting)
.map_err(|err| Error::new(format!("Failed to set alternate setting: {:?}", err)))?;
Ok(())
}
let data: [u8; 2] = value.to_be_bytes();
let buffer = if length == 1 { &data[1..2] } else { &data };
let index = 0x10 | page;
let address = (address << 8) | 0x20;
handle.write_control(REQ_CTRL_OUT, 0x00, address, index, buffer, TIMEOUT)
}

View File

@@ -36,3 +36,9 @@ impl From<ctrlc::Error> for Error {
Error::Other(err.to_string())
}
}
impl From<std::str::Utf8Error> for Error {
fn from(err: std::str::Utf8Error) -> Self {
Error::Other(err.to_string())
}
}

View File

@@ -1,12 +1,14 @@
mod constants;
mod device;
mod error;
mod frame;
mod hex;
use error::Result;
use std::sync::Arc;
use std::sync::atomic::{AtomicBool, Ordering};
use crate::device::RtlSdrDevice;
use clap::Parser;
use rusb::TransferType;
use crate::constants::DEVICE_RTL2832U;
use crate::frame::ADSBFrame;
use crate::hex::hex_to_bytes;
@@ -26,47 +28,71 @@ struct ReceiverArgs {
info: bool,
/// Enable debug logging
#[arg(short = 'D', long, action)]
debug: bool,
#[arg(short = 'D', long, action = clap::ArgAction::Count)]
debug: u8,
}
fn main() -> Result<()> {
fn main() {
let args = ReceiverArgs::parse();
let default_filter = if args.debug {
"warn,adsb=debug"
} else {
"warn,adsb=info"
let default_filter = match args.debug {
0 => "warn,adsb=info", // no -D
1 => "warn,adsb=debug", // -D
_ => "trace,adsb=trace", // -DD or more
};
env_logger::init_from_env(env_logger::Env::default().filter_or("RUST_LOG", default_filter));
let vid = 0x0BDA;
let pid = 0x2832;
let device_info = DEVICE_RTL2832U;
// Handle connection
if args.connect {
log::info!("Connecting to device");
let mut device = RtlSdrDevice::open(vid, pid)?;
device.read(TransferType::Bulk)
log::info!("Connecting to {:?}", device_info);
let mut device = match RtlSdrDevice::open(device_info.vid, device_info.pid) {
Ok(d) => d,
Err(err) => {
log::error!("Unable to open RTL SDR device: {:?}", err);
return;
}
};
log::debug!("Connected to {:?}", device_info.to_string());
let running = Arc::new(AtomicBool::new(true));
if let Err(err) = ctrlc::set_handler({
let running = running.clone();
move || running.store(false, Ordering::SeqCst)
}) {
log::error!("Error setting Ctrl-C handler: {}", err);
running.store(false, Ordering::SeqCst);
};
if let Err(err) = device.process(running) {
log::error!("Failed to read from device: {}", err);
if let Err(err) = device.close() {
log::error!("Failed to close device: {}", err);
};
};
}
// Display dongle info
else if args.info {
RtlSdrDevice::info(vid, pid);
Ok(())
RtlSdrDevice::info(device_info.vid, device_info.pid);
}
// Handle decode mode
else if let Some(mut hex_string) = args.decode {
if let Some(stripped) = hex_string.strip_prefix("0x") {
hex_string = stripped.to_string();
}
let buf = hex_to_bytes(&hex_string)?;
let frame = ADSBFrame::decode(&buf)?;
log::info!("{}", frame);
Ok(())
let buffer = match hex_to_bytes(&hex_string) {
Ok(buffer) => buffer,
Err(err) => {
eprintln!("Unable to convert hex to bytes: {:?}", err);
return;
}
};
if let Ok(frame) = ADSBFrame::decode(&buffer) {
println!("{:?}", frame);
};
} else {
log::warn!("No connection specified");
Ok(())
eprintln!("No connection specified");
}
}