|
|
|
@@ -1,9 +1,12 @@
|
|
|
|
|
|
|
|
#![feature(extern_types)]
|
|
|
|
|
|
|
|
|
|
|
|
mod constants;
|
|
|
|
mod constants;
|
|
|
|
mod device;
|
|
|
|
mod device;
|
|
|
|
mod error;
|
|
|
|
mod error;
|
|
|
|
mod frame;
|
|
|
|
mod frame;
|
|
|
|
mod hex;
|
|
|
|
mod hex;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
use std::ffi::{c_char, c_int, c_void, CStr};
|
|
|
|
use std::sync::Arc;
|
|
|
|
use std::sync::Arc;
|
|
|
|
use std::sync::atomic::{AtomicBool, Ordering};
|
|
|
|
use std::sync::atomic::{AtomicBool, Ordering};
|
|
|
|
use crate::device::RtlSdrDevice;
|
|
|
|
use crate::device::RtlSdrDevice;
|
|
|
|
@@ -12,87 +15,217 @@ use crate::constants::DEVICE_RTL2832U;
|
|
|
|
use crate::frame::ADSBFrame;
|
|
|
|
use crate::frame::ADSBFrame;
|
|
|
|
use crate::hex::hex_to_bytes;
|
|
|
|
use crate::hex::hex_to_bytes;
|
|
|
|
|
|
|
|
|
|
|
|
#[derive(Parser, Debug)]
|
|
|
|
#[link(name = "rtlsdr")]
|
|
|
|
#[command(author, version, about = "An ADS-B Receiver")]
|
|
|
|
unsafe extern "C" {
|
|
|
|
struct ReceiverArgs {
|
|
|
|
fn rtlsdr_get_device_count() -> u32;
|
|
|
|
/// Hex-string to decode
|
|
|
|
fn rtlsdr_get_device_name(index: u32) -> *const c_char;
|
|
|
|
#[arg(short = 'd', long)]
|
|
|
|
fn rtlsdr_get_device_usb_strings(
|
|
|
|
decode: Option<String>,
|
|
|
|
index: u32,
|
|
|
|
|
|
|
|
manufact: *mut c_char,
|
|
|
|
|
|
|
|
product: *mut c_char,
|
|
|
|
|
|
|
|
serial: *mut c_char,
|
|
|
|
|
|
|
|
) -> c_int;
|
|
|
|
|
|
|
|
|
|
|
|
/// Connect to the USB device
|
|
|
|
type rtlsdr_dev_t;
|
|
|
|
#[arg(short = 'c', long, action)]
|
|
|
|
|
|
|
|
connect: bool,
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/// Display ADS-B/Mode-S receiver info
|
|
|
|
fn rtlsdr_open(dev: *mut *mut rtlsdr_dev_t, index: u32) -> c_int;
|
|
|
|
#[arg(short = 'i', long, action)]
|
|
|
|
fn rtlsdr_close(dev: *mut rtlsdr_dev_t) -> c_int;
|
|
|
|
info: bool,
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/// Enable debug logging
|
|
|
|
fn rtlsdr_set_sample_rate(dev: *mut rtlsdr_dev_t, rate: u32) -> c_int;
|
|
|
|
#[arg(short = 'D', long, action = clap::ArgAction::Count)]
|
|
|
|
fn rtlsdr_set_center_freq(dev: *mut rtlsdr_dev_t, freq: u32) -> c_int;
|
|
|
|
debug: u8,
|
|
|
|
fn rtlsdr_set_agc_mode(dev: *mut rtlsdr_dev_t, on: c_int) -> c_int;
|
|
|
|
|
|
|
|
fn rtlsdr_set_tuner_gain_mode(dev: *mut rtlsdr_dev_t, manual: c_int) -> c_int;
|
|
|
|
|
|
|
|
fn rtlsdr_reset_buffer(dev: *mut rtlsdr_dev_t) -> c_int;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
fn rtlsdr_read_sync(
|
|
|
|
|
|
|
|
dev: *mut rtlsdr_dev_t,
|
|
|
|
|
|
|
|
buf: *mut c_void,
|
|
|
|
|
|
|
|
len: c_int,
|
|
|
|
|
|
|
|
n_read: *mut c_int,
|
|
|
|
|
|
|
|
) -> c_int;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
struct Device {
|
|
|
|
|
|
|
|
ptr: *mut rtlsdr_dev_t,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
fn main() {
|
|
|
|
impl Device {
|
|
|
|
let args = ReceiverArgs::parse();
|
|
|
|
fn open(index: u32) -> Result<Self, String> {
|
|
|
|
|
|
|
|
let mut dev: *mut rtlsdr_dev_t = std::ptr::null_mut();
|
|
|
|
|
|
|
|
let rc = unsafe { rtlsdr_open(&mut dev as *mut _, index) };
|
|
|
|
|
|
|
|
if rc != 0 {
|
|
|
|
|
|
|
|
return Err(format!("Failed to open device: {}", rc));
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
Ok(Self { ptr: dev })
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
let default_filter = match args.debug {
|
|
|
|
fn set_sample_rate(&mut self, rate: u32) -> Result<(), String> {
|
|
|
|
0 => "warn,adsb=info", // no -D
|
|
|
|
let rc = unsafe { rtlsdr_set_sample_rate(self.ptr, rate) };
|
|
|
|
1 => "warn,adsb=debug", // -D
|
|
|
|
if rc != 0 {
|
|
|
|
_ => "trace,adsb=trace", // -DD or more
|
|
|
|
return Err(format!("Failed to set sample rate: {}", rc));
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
Ok(())
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
fn set_center_freq(&mut self, freq: u32) -> Result<(), String> {
|
|
|
|
|
|
|
|
let rc = unsafe { rtlsdr_set_center_freq(self.ptr, freq) };
|
|
|
|
|
|
|
|
if rc != 0 {
|
|
|
|
|
|
|
|
return Err(format!("Failed to set center freq: {}", rc));
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
Ok(())
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
fn set_agc(&mut self, on: bool) -> Result<(), String> {
|
|
|
|
|
|
|
|
let rc = unsafe { rtlsdr_set_agc_mode(self.ptr, if on { 1 } else { 0 }) };
|
|
|
|
|
|
|
|
if rc != 0 {
|
|
|
|
|
|
|
|
return Err(format!("Failed to set AGC: {}", rc));
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
Ok(())
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// manual = false -> auto-gain, manual = true -> set a gain
|
|
|
|
|
|
|
|
fn set_tuner_gain_mode(&mut self, manual: bool) -> Result<(), String> {
|
|
|
|
|
|
|
|
let rc = unsafe { rtlsdr_set_tuner_gain_mode(self.ptr, if manual { 1 } else { 0 }) };
|
|
|
|
|
|
|
|
if rc != 0 {
|
|
|
|
|
|
|
|
return Err(format!("Failed to set tuner gain mode: {}", rc));
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
Ok(())
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
fn set_buffer(&mut self) -> Result<(), String> {
|
|
|
|
|
|
|
|
let rc = unsafe { rtlsdr_reset_buffer(self.ptr) };
|
|
|
|
|
|
|
|
if rc != 0 {
|
|
|
|
|
|
|
|
return Err(format!("Failed to reset buffer: {}", rc));
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
Ok(())
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Returns the number of bytes placed in buf (I/Q interleaved, u8
|
|
|
|
|
|
|
|
fn read_sync(&mut self, buf: &mut [u8]) -> Result<usize, String> {
|
|
|
|
|
|
|
|
let mut n_read: c_int = 0;
|
|
|
|
|
|
|
|
let rc = unsafe {
|
|
|
|
|
|
|
|
rtlsdr_read_sync(
|
|
|
|
|
|
|
|
self.ptr,
|
|
|
|
|
|
|
|
buf.as_mut_ptr() as *mut c_void,
|
|
|
|
|
|
|
|
buf.len() as c_int,
|
|
|
|
|
|
|
|
&mut n_read as *mut _,
|
|
|
|
|
|
|
|
)
|
|
|
|
};
|
|
|
|
};
|
|
|
|
|
|
|
|
if rc != 0 {
|
|
|
|
|
|
|
|
return Err(format!("Failed to read sync: {}", rc));
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
Ok(n_read as usize)
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
env_logger::init_from_env(env_logger::Env::default().filter_or("RUST_LOG", default_filter));
|
|
|
|
impl Drop for Device {
|
|
|
|
|
|
|
|
fn drop(&mut self) {
|
|
|
|
|
|
|
|
if !self.ptr.is_null() {
|
|
|
|
|
|
|
|
unsafe { rtlsdr_close(self.ptr) };
|
|
|
|
|
|
|
|
self.ptr = std::ptr::null_mut();
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
let device_info = DEVICE_RTL2832U;
|
|
|
|
fn cstr_from_ptr(ptr: *const c_char) -> String {
|
|
|
|
|
|
|
|
if ptr.is_null() {
|
|
|
|
|
|
|
|
return String::new();
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
unsafe { CStr::from_ptr(ptr).to_string_lossy().into_owned() }
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Handle connection
|
|
|
|
fn main() -> Result<(), Box<dyn std::error::Error>> {
|
|
|
|
if args.connect {
|
|
|
|
unsafe {
|
|
|
|
log::info!("Connecting to {:?}", device_info);
|
|
|
|
let count = rtlsdr_get_device_count();
|
|
|
|
let mut device = match RtlSdrDevice::open(device_info.vid, device_info.pid) {
|
|
|
|
log::debug!("Found {} devices", count);
|
|
|
|
Ok(d) => d,
|
|
|
|
for i in 0..count {
|
|
|
|
Err(err) => {
|
|
|
|
let name = cstr_from_ptr(rtlsdr_get_device_name(i));
|
|
|
|
log::error!("Unable to open RTL SDR device: {:?}", err);
|
|
|
|
log::debug!("Device {}: {}", i, name);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
log::debug!("Connected to {:?}", device_info.to_string());
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
let running = Arc::new(AtomicBool::new(true));
|
|
|
|
// #[derive(Parser, Debug)]
|
|
|
|
if let Err(err) = ctrlc::set_handler({
|
|
|
|
// #[command(author, version, about = "An ADS-B Receiver")]
|
|
|
|
let running = running.clone();
|
|
|
|
// struct ReceiverArgs {
|
|
|
|
move || running.store(false, Ordering::SeqCst)
|
|
|
|
// /// Hex-string to decode
|
|
|
|
}) {
|
|
|
|
// #[arg(short = 'd', long)]
|
|
|
|
log::error!("Error setting Ctrl-C handler: {}", err);
|
|
|
|
// decode: Option<String>,
|
|
|
|
running.store(false, Ordering::SeqCst);
|
|
|
|
//
|
|
|
|
};
|
|
|
|
// /// Connect to the USB device
|
|
|
|
|
|
|
|
// #[arg(short = 'c', long, action)]
|
|
|
|
if let Err(err) = device.process(running) {
|
|
|
|
// connect: bool,
|
|
|
|
log::error!("Failed to read from device: {}", err);
|
|
|
|
//
|
|
|
|
if let Err(err) = device.close() {
|
|
|
|
// /// Display ADS-B/Mode-S receiver info
|
|
|
|
log::error!("Failed to close device: {}", err);
|
|
|
|
// #[arg(short = 'i', long, action)]
|
|
|
|
};
|
|
|
|
// info: bool,
|
|
|
|
};
|
|
|
|
//
|
|
|
|
}
|
|
|
|
// /// Enable debug logging
|
|
|
|
// Display dongle info
|
|
|
|
// #[arg(short = 'D', long, action = clap::ArgAction::Count)]
|
|
|
|
else if args.info {
|
|
|
|
// debug: u8,
|
|
|
|
RtlSdrDevice::info(device_info.vid, device_info.pid);
|
|
|
|
// }
|
|
|
|
}
|
|
|
|
//
|
|
|
|
// Handle decode mode
|
|
|
|
// fn main() {
|
|
|
|
else if let Some(mut hex_string) = args.decode {
|
|
|
|
// let args = ReceiverArgs::parse();
|
|
|
|
if let Some(stripped) = hex_string.strip_prefix("0x") {
|
|
|
|
//
|
|
|
|
hex_string = stripped.to_string();
|
|
|
|
// let default_filter = match args.debug {
|
|
|
|
}
|
|
|
|
// 0 => "warn,adsb=info", // no -D
|
|
|
|
let buffer = match hex_to_bytes(&hex_string) {
|
|
|
|
// 1 => "warn,adsb=debug", // -D
|
|
|
|
Ok(buffer) => buffer,
|
|
|
|
// _ => "trace,adsb=trace", // -DD or more
|
|
|
|
Err(err) => {
|
|
|
|
// };
|
|
|
|
eprintln!("Unable to convert hex to bytes: {:?}", err);
|
|
|
|
//
|
|
|
|
return;
|
|
|
|
// env_logger::init_from_env(env_logger::Env::default().filter_or("RUST_LOG", default_filter));
|
|
|
|
}
|
|
|
|
//
|
|
|
|
};
|
|
|
|
// let device_info = DEVICE_RTL2832U;
|
|
|
|
if let Ok(frame) = ADSBFrame::decode(&buffer) {
|
|
|
|
//
|
|
|
|
println!("{:?}", frame);
|
|
|
|
// // Handle connection
|
|
|
|
};
|
|
|
|
// if args.connect {
|
|
|
|
} else {
|
|
|
|
// log::info!("Connecting to {:?}", device_info);
|
|
|
|
eprintln!("No connection specified");
|
|
|
|
// 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(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 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 {
|
|
|
|
|
|
|
|
// eprintln!("No connection specified");
|
|
|
|
|
|
|
|
// }
|
|
|
|
|
|
|
|
// }
|
|
|
|
|