Formatting

This commit is contained in:
2025-04-22 22:23:34 -04:00
parent 95e4b8abf3
commit fdb53f0b7f
9 changed files with 170 additions and 168 deletions

View File

@@ -12,7 +12,7 @@ help: ## This info
@cat Makefile | grep -E '^[a-zA-Z\/_-]+:.*?## .*$$' | awk 'BEGIN {FS = ":.*?## "}; {printf "\033[36m%-30s\033[0m %s\n", $$1, $$2}' @cat Makefile | grep -E '^[a-zA-Z\/_-]+:.*?## .*$$' | awk 'BEGIN {FS = ":.*?## "}; {printf "\033[36m%-30s\033[0m %s\n", $$1, $$2}'
@echo @echo
format: format-api format-ui ## Format code format: format-api format-ui format-adsb ## Format code
psql: ## Connect to the PSQL DB psql: ## Connect to the PSQL DB
@docker exec -it aviation-postgres psql -U ${POSTGRES_USER} -P pager=off @docker exec -it aviation-postgres psql -U ${POSTGRES_USER} -P pager=off
@@ -34,6 +34,9 @@ run-api: ## Run the API project
# ADS-B Commands # # ADS-B Commands #
################## ##################
format-adsb: ## Format code
@cd adsb && cargo fmt
build-adsb: ## Build the ADS-B project build-adsb: ## Build the ADS-B project
@cd adsb && cargo build @cd adsb && cargo build

View File

@@ -1,3 +1,4 @@
use crate::hex_to_bytes;
use std::fmt::Display; use std::fmt::Display;
use std::io::{Error, ErrorKind, Result}; use std::io::{Error, ErrorKind, Result};
@@ -24,7 +25,8 @@ impl ADSBFrame {
if frame.len() != 14 { if frame.len() != 14 {
return Err(Error::new( return Err(Error::new(
ErrorKind::InvalidInput, ErrorKind::InvalidInput,
format!("expected 14 bytes, received {}", frame.len()))); format!("expected 14 bytes, received {}", frame.len()),
));
} }
let mut raw_frame = "".to_string(); let mut raw_frame = "".to_string();
@@ -37,7 +39,10 @@ impl ADSBFrame {
if downlink_format != 17 { if downlink_format != 17 {
return Err(Error::new( return Err(Error::new(
ErrorKind::Unsupported, ErrorKind::Unsupported,
format!("downlink format {} is not currently supported", downlink_format) format!(
"downlink format {} is not currently supported",
downlink_format
),
)); ));
} }
@@ -55,17 +60,23 @@ impl ADSBFrame {
capability, capability,
icao, icao,
message, message,
parity parity,
}) })
} }
pub fn encode(&self) -> Result<Vec<u8>> {
Ok(hex_to_bytes(&self.raw_frame)?)
}
fn decode_icao(data: &[u8]) -> Result<String> { fn decode_icao(data: &[u8]) -> Result<String> {
if data.len() != 3 { if data.len() != 3 {
return Err(Error::new( return Err(Error::new(
ErrorKind::InvalidInput, ErrorKind::InvalidInput,
format!("ICAO must be 3 bytes, received {}", data.len()))); format!("ICAO must be 3 bytes, received {}", data.len()),
));
} }
let s = data.iter() let s = data
.iter()
.map(|b| format!("{:02X}", b)) .map(|b| format!("{:02X}", b))
.collect::<String>(); .collect::<String>();
Ok(s) Ok(s)
@@ -75,29 +86,26 @@ impl ADSBFrame {
if data.len() != 3 { if data.len() != 3 {
return Err(Error::new( return Err(Error::new(
ErrorKind::InvalidInput, ErrorKind::InvalidInput,
format!("parity must be 3 bytes, received {}", data.len()))); format!("parity must be 3 bytes, received {}", data.len()),
));
} }
let p = ((data[0] as u32) << 16) let p = ((data[0] as u32) << 16) | ((data[1] as u32) << 8) | (data[2] as u32);
| ((data[1] as u32) << 8)
| (data[2] as u32);
Ok(p) Ok(p)
} }
} }
impl Display for ADSBFrame { impl Display for ADSBFrame {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "Frame: {}\ write!(
f,
"Frame: {}\
\nDF: {}\ \nDF: {}\
\nCA: {:?}\ \nCA: {:?}\
\nICAO: {}\ \nICAO: {}\
\nME: {:?}\ \nME: {:?}\
\nPI: {}", \nPI: {}",
self.raw_frame, self.raw_frame, self.downlink_format, &self.capability, self.icao, &self.message, self.parity
self.downlink_format, )
&self.capability,
self.icao,
&self.message,
self.parity)
} }
} }
@@ -176,7 +184,9 @@ impl ADSBMessage {
// First 5 bits is the type code // First 5 bits is the type code
let type_code = data[0] >> 3; let type_code = data[0] >> 3;
let message = match type_code { let message = match type_code {
1..=4 => ADSBMessage::AircraftIdentification(AircraftIdentification::decode(type_code, data)?), 1..=4 => {
ADSBMessage::AircraftIdentification(AircraftIdentification::decode(type_code, data)?)
}
5..=8 => ADSBMessage::SurfacePosition(SurfacePosition::decode(data)?), 5..=8 => ADSBMessage::SurfacePosition(SurfacePosition::decode(data)?),
9..=18 => ADSBMessage::AirbornePositionBaro(AirbornePositionBaro::decode(data)?), 9..=18 => ADSBMessage::AirbornePositionBaro(AirbornePositionBaro::decode(data)?),
19 => ADSBMessage::AirborneVelocities(AirborneVelocities::decode(data)?), 19 => ADSBMessage::AirborneVelocities(AirborneVelocities::decode(data)?),
@@ -185,10 +195,12 @@ impl ADSBMessage {
28 => ADSBMessage::AircraftStatus(AircraftStatus::decode(data)?), 28 => ADSBMessage::AircraftStatus(AircraftStatus::decode(data)?),
29 => ADSBMessage::TargetState(TargetState::decode(data)?), 29 => ADSBMessage::TargetState(TargetState::decode(data)?),
31 => ADSBMessage::AircraftOperationStatus(AircraftOperationStatus::decode(data)?), 31 => ADSBMessage::AircraftOperationStatus(AircraftOperationStatus::decode(data)?),
_ => return Err(Error::new( _ => {
return Err(Error::new(
ErrorKind::InvalidData, ErrorKind::InvalidData,
format!("unsupported ADSB type_code {}", type_code), format!("unsupported ADSB type_code {}", type_code),
)) ))
}
}; };
Ok(message) Ok(message)
@@ -289,78 +301,64 @@ impl WakeVortexCategory {
} }
#[derive(Debug, Clone, PartialEq, Eq)] #[derive(Debug, Clone, PartialEq, Eq)]
pub struct SurfacePosition { pub struct SurfacePosition {}
}
impl SurfacePosition { impl SurfacePosition {
pub fn decode(data: &[u8]) -> Result<Self> { pub fn decode(_data: &[u8]) -> Result<Self> {
Ok(Self {}) Ok(Self {})
} }
} }
#[derive(Debug, Clone, PartialEq, Eq)] #[derive(Debug, Clone, PartialEq, Eq)]
pub struct AirbornePositionBaro { pub struct AirbornePositionBaro {}
}
impl AirbornePositionBaro { impl AirbornePositionBaro {
pub fn decode(data: &[u8]) -> Result<Self> { pub fn decode(_data: &[u8]) -> Result<Self> {
Ok(Self {}) Ok(Self {})
} }
} }
#[derive(Debug, Clone, PartialEq, Eq)] #[derive(Debug, Clone, PartialEq, Eq)]
pub struct AirborneVelocities { pub struct AirborneVelocities {}
}
impl AirborneVelocities { impl AirborneVelocities {
pub fn decode(data: &[u8]) -> Result<Self> { pub fn decode(_data: &[u8]) -> Result<Self> {
Ok(Self {}) Ok(Self {})
} }
} }
#[derive(Debug, Clone, PartialEq, Eq)] #[derive(Debug, Clone, PartialEq, Eq)]
pub struct AirbornePositionGNSS { pub struct AirbornePositionGNSS {}
}
impl AirbornePositionGNSS { impl AirbornePositionGNSS {
pub fn decode(data: &[u8]) -> Result<Self> { pub fn decode(_data: &[u8]) -> Result<Self> {
Ok(Self {}) Ok(Self {})
} }
} }
#[derive(Debug, Clone, PartialEq, Eq)] #[derive(Debug, Clone, PartialEq, Eq)]
pub struct AircraftStatus { pub struct AircraftStatus {}
}
impl AircraftStatus { impl AircraftStatus {
pub fn decode(data: &[u8]) -> Result<Self> { pub fn decode(_data: &[u8]) -> Result<Self> {
Ok(Self {}) Ok(Self {})
} }
} }
#[derive(Debug, Clone, PartialEq, Eq)] #[derive(Debug, Clone, PartialEq, Eq)]
pub struct TargetState { pub struct TargetState {}
}
impl TargetState { impl TargetState {
pub fn decode(data: &[u8]) -> Result<Self> { pub fn decode(_data: &[u8]) -> Result<Self> {
Ok(Self {}) Ok(Self {})
} }
} }
#[derive(Debug, Clone, PartialEq, Eq)] #[derive(Debug, Clone, PartialEq, Eq)]
pub struct AircraftOperationStatus { pub struct AircraftOperationStatus {}
}
impl AircraftOperationStatus { impl AircraftOperationStatus {
pub fn decode(data: &[u8]) -> Result<Self> { pub fn decode(_data: &[u8]) -> Result<Self> {
Ok(Self {}) Ok(Self {})
} }
} }
@@ -372,8 +370,7 @@ mod tests {
#[test] #[test]
fn test_decode_df_17_aircraft_information() { fn test_decode_df_17_aircraft_information() {
let input = [ let input = [
0x8D, 0x48, 0x40, 0xD6, 0x20, 0x2C, 0xC3, 0x71, 0x8D, 0x48, 0x40, 0xD6, 0x20, 0x2C, 0xC3, 0x71, 0xC3, 0x1C, 0x32, 0xCE, 0x05, 0x76,
0xC3, 0x1C, 0x32, 0xCE, 0x05, 0x76,
]; ];
let frame = ADSBFrame::decode(&input).unwrap(); let frame = ADSBFrame::decode(&input).unwrap();
assert_eq!(frame.downlink_format, 17); assert_eq!(frame.downlink_format, 17);
@@ -391,8 +388,7 @@ mod tests {
assert_eq!(frame.parity, 13501814); assert_eq!(frame.parity, 13501814);
let input = [ let input = [
0x8D, 0x48, 0x40, 0xD6, 0x20, 0x2C, 0xC3, 0x71, 0x8D, 0x48, 0x40, 0xD6, 0x20, 0x2C, 0xC3, 0x71, 0xC3, 0x2C, 0xE0, 0x57, 0x60, 0x98,
0xC3, 0x2C, 0xE0, 0x57, 0x60, 0x98
]; ];
let frame = ADSBFrame::decode(&input).unwrap(); let frame = ADSBFrame::decode(&input).unwrap();
assert_eq!(frame.downlink_format, 17); assert_eq!(frame.downlink_format, 17);
@@ -410,8 +406,7 @@ mod tests {
assert_eq!(frame.parity, 5726360); assert_eq!(frame.parity, 5726360);
let input = [ let input = [
0x8D, 0x7C, 0x71, 0x81, 0x21, 0x5D, 0x01, 0xA0, 0x8D, 0x7C, 0x71, 0x81, 0x21, 0x5D, 0x01, 0xA0, 0x82, 0x08, 0x20, 0x4D, 0x8B, 0xF1,
0x82, 0x08, 0x20, 0x4D, 0x8B, 0xF1
]; ];
let frame = ADSBFrame::decode(&input).unwrap(); let frame = ADSBFrame::decode(&input).unwrap();
assert_eq!(frame.downlink_format, 17); assert_eq!(frame.downlink_format, 17);
@@ -429,8 +424,7 @@ mod tests {
assert_eq!(frame.parity, 5082097); assert_eq!(frame.parity, 5082097);
let input = [ let input = [
0x8D, 0x7C, 0x77, 0x45, 0x22, 0x61, 0x51, 0xA0, 0x8D, 0x7C, 0x77, 0x45, 0x22, 0x61, 0x51, 0xA0, 0x82, 0x08, 0x20, 0x5C, 0xE9, 0xC2,
0x82, 0x08, 0x20, 0x5C, 0xE9, 0xC2
]; ];
let frame = ADSBFrame::decode(&input).unwrap(); let frame = ADSBFrame::decode(&input).unwrap();
assert_eq!(frame.downlink_format, 17); assert_eq!(frame.downlink_format, 17);
@@ -448,8 +442,7 @@ mod tests {
assert_eq!(frame.parity, 6089154); assert_eq!(frame.parity, 6089154);
let input = [ let input = [
0x8D, 0x7C, 0x80, 0xAD, 0x23, 0x58, 0xF6, 0xB1, 0x8D, 0x7C, 0x80, 0xAD, 0x23, 0x58, 0xF6, 0xB1, 0xE3, 0x5C, 0x60, 0xFF, 0x19, 0x25,
0xE3, 0x5C, 0x60, 0xFF, 0x19, 0x25
]; ];
let frame = ADSBFrame::decode(&input).unwrap(); let frame = ADSBFrame::decode(&input).unwrap();
assert_eq!(frame.downlink_format, 17); assert_eq!(frame.downlink_format, 17);
@@ -467,8 +460,7 @@ mod tests {
assert_eq!(frame.parity, 16718117); assert_eq!(frame.parity, 16718117);
let input = [ let input = [
0x8D, 0x7C, 0x14, 0x65, 0x25, 0x44, 0x60, 0x74, 0x8D, 0x7C, 0x14, 0x65, 0x25, 0x44, 0x60, 0x74, 0xDF, 0x58, 0x20, 0x73, 0x8E, 0x90,
0xDF, 0x58, 0x20, 0x73, 0x8E, 0x90
]; ];
let frame = ADSBFrame::decode(&input).unwrap(); let frame = ADSBFrame::decode(&input).unwrap();
assert_eq!(frame.downlink_format, 17); assert_eq!(frame.downlink_format, 17);
@@ -489,8 +481,7 @@ mod tests {
#[test] #[test]
fn test_decode_df_17_operation_status() { fn test_decode_df_17_operation_status() {
let input = [ let input = [
0x8D, 0x89, 0x65, 0xD2, 0xF8, 0x21, 0x00, 0x02, 0x8D, 0x89, 0x65, 0xD2, 0xF8, 0x21, 0x00, 0x02, 0x00, 0x49, 0xB8, 0x94, 0xA4, 0x5F,
0x00, 0x49, 0xB8, 0x94, 0xA4, 0x5F,
]; ];
let frame = ADSBFrame::decode(&input).unwrap(); let frame = ADSBFrame::decode(&input).unwrap();
dbg!(frame); dbg!(frame);

View File

@@ -1,4 +1,6 @@
use std::io::Result; use std::io::{Error, ErrorKind, Result};
pub mod adsb;
pub trait RtlDevice { pub trait RtlDevice {
/// Send a control message to the device /// Send a control message to the device
@@ -26,10 +28,7 @@ pub fn run<S: RtlDevice>(device: &mut S) -> Result<()> {
device.control_send(0x04, &[1])?; device.control_send(0x04, &[1])?;
// Precompute the preamble pattern in “halfbit” units (16 samples) // Precompute the preamble pattern in “halfbit” units (16 samples)
let preamble_halfbit_pattern = [ let preamble_halfbit_pattern = [1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0];
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 // Create a big buffer to hold raw I/Q bytes
let mut iq_buffer = [0u8; 16_384]; let mut iq_buffer = [0u8; 16_384];
@@ -50,11 +49,7 @@ pub fn run<S: RtlDevice>(device: &mut S) -> Result<()> {
for pair in raw.chunks_exact(2) { for pair in raw.chunks_exact(2) {
let i_sample = pair[0] as u16; let i_sample = pair[0] as u16;
// Threshold at 200 // Threshold at 200
halfbit_samples.push(if i_sample > 200 { halfbit_samples.push(if i_sample > 200 { 1 } else { 0 });
1
} else {
0
});
} }
// Scan for the 16-sample preamble // Scan for the 16-sample preamble
@@ -68,7 +63,7 @@ pub fn run<S: RtlDevice>(device: &mut S) -> Result<()> {
let data_start = match data_start_index { let data_start = match data_start_index {
Some(i) => i, Some(i) => i,
None => continue // No preamble found in this chunk None => continue, // No preamble found in this chunk
}; };
// Collect 112 ADS-B bits, each manchester-encoded into 2 half-bits // Collect 112 ADS-B bits, each manchester-encoded into 2 half-bits
@@ -117,3 +112,46 @@ pub fn run<S: RtlDevice>(device: &mut S) -> Result<()> {
println!(); println!();
} }
} }
pub fn hex_to_bytes(s: &str) -> Result<Vec<u8>> {
let bytes = s.as_bytes();
if bytes.len() % 2 != 0 {
return Err(Error::new(
ErrorKind::InvalidInput,
format!("hex string must have even length, got {}", bytes.len()),
));
}
let mut out = Vec::with_capacity(bytes.len() / 2);
for chunk in bytes.chunks(2) {
let hi = match hex_val(chunk[0]) {
Some(hi) => hi,
None => {
return Err(Error::new(
ErrorKind::InvalidInput,
format!("invalid hex char '{}'", chunk[0] as char),
))
}
};
let lo = match hex_val(chunk[1]) {
Some(lo) => lo,
None => {
return Err(Error::new(
ErrorKind::InvalidInput,
format!("invalid hex char '{}'", chunk[1] as char),
))
}
};
out.push((hi << 4) | lo);
}
Ok(out)
}
fn hex_val(b: u8) -> Option<u8> {
match b {
b'0'..=b'9' => Some(b - b'0'),
b'a'..=b'f' => Some(b - b'a' + 10),
b'A'..=b'F' => Some(b - b'A' + 10),
_ => None,
}
}

View File

@@ -1,13 +1,12 @@
mod tcp_rtl;
mod rusb_rtl; mod rusb_rtl;
mod adsb; mod tcp_rtl;
use std::io::{Error, ErrorKind, Result};
use clap::Parser;
use adsb_lib::run;
use crate::adsb::ADSBFrame;
use crate::rusb_rtl::RusbRtl; use crate::rusb_rtl::RusbRtl;
use crate::tcp_rtl::TcpRtl; use crate::tcp_rtl::TcpRtl;
use adsb_lib::adsb::ADSBFrame;
use adsb_lib::{hex_to_bytes, run};
use clap::Parser;
use std::io::Result;
#[derive(Parser, Debug)] #[derive(Parser, Debug)]
#[command(author, version, about, long_about = None)] #[command(author, version, about, long_about = None)]
@@ -23,11 +22,11 @@ struct ReceiverArgs {
fn main() -> Result<()> { fn main() -> Result<()> {
let args = ReceiverArgs::parse(); let args = ReceiverArgs::parse();
if let Some(mut hexString) = args.decode { if let Some(mut hex_string) = args.decode {
if let Some(stripped) = hexString.strip_prefix("0x") { if let Some(stripped) = hex_string.strip_prefix("0x") {
hexString = stripped.to_string(); hex_string = stripped.to_string();
} }
let buf = hex_to_bytes(&hexString)?; let buf = hex_to_bytes(&hex_string)?;
let frame = ADSBFrame::decode(&buf)?; let frame = ADSBFrame::decode(&buf)?;
println!("{}", frame); println!("{}", frame);
@@ -44,41 +43,3 @@ fn main() -> Result<()> {
run(&mut device) run(&mut device)
} }
} }
fn hex_to_bytes(s: &str) -> Result<Vec<u8>> {
let bytes = s.as_bytes();
if bytes.len() % 2 != 0 {
return Err(Error::new(
ErrorKind::InvalidInput,
format!("hex string must have even length, got {}", bytes.len())));
}
let mut out = Vec::with_capacity(bytes.len() / 2);
for chunk in bytes.chunks(2) {
let hi = match hex_val(chunk[0]) {
Some(hi) => hi,
None => return Err(Error::new(
ErrorKind::InvalidInput,
format!("invalid hex char '{}'", chunk[0] as char)
))
};
let lo = match hex_val(chunk[1]) {
Some(lo) => lo,
None => return Err(Error::new(
ErrorKind::InvalidInput,
format!("invalid hex char '{}'", chunk[1] as char)
))
};
out.push((hi << 4) | lo);
}
Ok(out)
}
fn hex_val(b: u8) -> Option<u8> {
match b {
b'0'..=b'9' => Some(b - b'0'),
b'a'..=b'f' => Some(b - b'a' + 10),
b'A'..=b'F' => Some(b - b'A' + 10),
_ => None,
}
}

View File

@@ -1,7 +1,7 @@
use adsb_lib::RtlDevice;
use rusb::{request_type, Context, DeviceHandle, Direction, Recipient, RequestType, UsbContext};
use std::io::{Error, ErrorKind, Result}; use std::io::{Error, ErrorKind, Result};
use std::time::Duration; use std::time::Duration;
use rusb::{request_type, Context, DeviceHandle, Direction, Recipient, RequestType, UsbContext};
use adsb_lib::RtlDevice;
// USB identifiers for RTL-SDR // USB identifiers for RTL-SDR
const VENDOR_ID: u16 = 0x0BDA; const VENDOR_ID: u16 = 0x0BDA;
@@ -19,15 +19,17 @@ impl RusbRtl {
/// Open the USB device, claim interface 0, and return a wrapper /// Open the USB device, claim interface 0, and return a wrapper
pub fn open() -> Result<Self> { pub fn open() -> Result<Self> {
// Create a new libusb context // Create a new libusb context
let mut ctx = Context::new().map_err(|err| Error::new(ErrorKind::Other, err))?; let ctx = Context::new().map_err(|err| Error::new(ErrorKind::Other, err))?;
// Find and open the RTL-SDR by VID/PID // Find and open the RTL-SDR by VID/PID
let mut handle = ctx let handle = ctx
.open_device_with_vid_pid(VENDOR_ID, PRODUCT_ID) .open_device_with_vid_pid(VENDOR_ID, PRODUCT_ID)
.ok_or_else(|| Error::new(ErrorKind::NotFound, "Device not found"))?; .ok_or_else(|| Error::new(ErrorKind::NotFound, "Device not found"))?;
// Claim interface 0 // Claim interface 0
handle.claim_interface(0).map_err(|err| Error::new(ErrorKind::Other, err))?; handle
.claim_interface(0)
.map_err(|err| Error::new(ErrorKind::Other, err))?;
Ok(Self { handle }) Ok(Self { handle })
} }
@@ -42,14 +44,16 @@ impl RusbRtl {
let bm_request_type = request_type(Direction::Out, RequestType::Vendor, Recipient::Device); let bm_request_type = request_type(Direction::Out, RequestType::Vendor, Recipient::Device);
// Perform the control transfer. // Perform the control transfer.
self.handle self
.handle
.write_control( .write_control(
bm_request_type, bm_request_type,
request, request,
0, // wValue 0, // wValue
0, // wIndex 0, // wIndex
data, data,
Duration::from_secs(1)) USB_TRANSFER_TIMEOUT,
)
.map(|_bytes_written| ()) .map(|_bytes_written| ())
} }
@@ -66,14 +70,14 @@ impl RusbRtl {
let mut buffer = vec![0u8; response_length]; let mut buffer = vec![0u8; response_length];
// Read the control response into the buffer // Read the control response into the buffer
let n = self.handle let n = self.handle.read_control(
.read_control(
bm_request_type, bm_request_type,
request, request,
0, // wValue 0, // wValue
0, // wIndex 0, // wIndex
&mut buffer, &mut buffer,
Duration::from_secs(1))?; USB_TRANSFER_TIMEOUT,
)?;
// Truncate to the actual length returned by the device // Truncate to the actual length returned by the device
buffer.truncate(n); buffer.truncate(n);
@@ -83,18 +87,21 @@ impl RusbRtl {
impl RtlDevice for RusbRtl { impl RtlDevice for RusbRtl {
fn control_send(&mut self, b_request: u8, data: &[u8]) -> Result<()> { fn control_send(&mut self, b_request: u8, data: &[u8]) -> Result<()> {
self.control_out(b_request, data) self
.control_out(b_request, data)
.map_err(|err| Error::new(ErrorKind::Other, err)) .map_err(|err| Error::new(ErrorKind::Other, err))
} }
fn control_recv(&mut self, b_request: u8, length: usize) -> Result<Vec<u8>> { fn control_recv(&mut self, b_request: u8, length: usize) -> Result<Vec<u8>> {
self.control_in(b_request, length) self
.control_in(b_request, length)
.map_err(|err| Error::new(ErrorKind::Other, err)) .map_err(|err| Error::new(ErrorKind::Other, err))
} }
fn read_bulk(&mut self, buffer: &mut [u8]) -> Result<usize> { fn read_bulk(&mut self, buffer: &mut [u8]) -> Result<usize> {
self.handle self
.read_bulk(DATA_ENDPOINT_ADDRESS, buffer, Duration::from_secs(1)) .handle
.read_bulk(DATA_ENDPOINT_ADDRESS, buffer, USB_TRANSFER_TIMEOUT)
.map_err(|err| Error::new(ErrorKind::Other, err)) .map_err(|err| Error::new(ErrorKind::Other, err))
} }
} }

View File

@@ -1,6 +1,6 @@
use adsb_lib::RtlDevice;
use std::io::{Error, ErrorKind, Read, Result, Write}; use std::io::{Error, ErrorKind, Read, Result, Write};
use std::net::TcpStream; use std::net::TcpStream;
use adsb_lib::RtlDevice;
// Tags for framing requests/responses over the TCP socket // Tags for framing requests/responses over the TCP socket
const TAG_CTRL_OUT: u8 = 0x10; const TAG_CTRL_OUT: u8 = 0x10;
@@ -112,7 +112,9 @@ impl RtlDevice for TcpRtl {
let actual_payload_length = u32::from_le_bytes(length_bytes) as usize; let actual_payload_length = u32::from_le_bytes(length_bytes) as usize;
// Read the payload // Read the payload
self.socket.read_exact(&mut buffer[..actual_payload_length])?; self
.socket
.read_exact(&mut buffer[..actual_payload_length])?;
Ok(actual_payload_length) Ok(actual_payload_length)
} }
} }

View File

@@ -1,8 +1,8 @@
use clap::Parser;
use std::io::{Read, Write}; use std::io::{Read, Write};
use std::net::{TcpListener, TcpStream}; use std::net::{TcpListener, TcpStream};
use std::thread; use std::thread;
use std::time::Duration; use std::time::Duration;
use clap::Parser;
// Framing tags // Framing tags
const TAG_CONTROL_OUT: u8 = 0x10; const TAG_CONTROL_OUT: u8 = 0x10;
@@ -10,8 +10,7 @@ const TAG_CONTROL_IN: u8 = 0x11;
const TAG_BULK: u8 = 0x20; const TAG_BULK: u8 = 0x20;
const ADSB_MESSAGE: [u8; 14] = [ const ADSB_MESSAGE: [u8; 14] = [
0x8D, 0x48, 0x40, 0xD6, 0x20, 0x2C, 0xC3, 0x8D, 0x48, 0x40, 0xD6, 0x20, 0x2C, 0xC3, 0x71, 0xC3, 0x2C, 0xE0, 0x57, 0x60, 0x98,
0x71, 0xC3, 0x2C, 0xE0, 0x57, 0x60, 0x98,
]; ];
#[derive(Parser, Debug)] #[derive(Parser, Debug)]
@@ -84,13 +83,13 @@ fn handle_client_connection(mut connection: TcpStream) {
payload_buffer[0], payload_buffer[0],
payload_buffer[1], payload_buffer[1],
payload_buffer[2], payload_buffer[2],
payload_buffer[3] payload_buffer[3],
]); ]);
println!("SET_FREQ -> {} Hz", current_frequency_hz); println!("SET_FREQ -> {} Hz", current_frequency_hz);
} }
// Acknowledge with a single byte = 0 (OK) // Acknowledge with a single byte = 0 (OK)
connection.write_all(&[0u8]).ok(); connection.write_all(&[0u8]).ok();
}, }
TAG_CONTROL_IN => { TAG_CONTROL_IN => {
dbg!(message_tag); dbg!(message_tag);
// Simulate a CONTROL_IN reply with a fixed pattern // Simulate a CONTROL_IN reply with a fixed pattern
@@ -105,7 +104,7 @@ fn handle_client_connection(mut connection: TcpStream) {
// Payload (0x42 repeated) // Payload (0x42 repeated)
let reply = vec![0x42; payload_length]; let reply = vec![0x42; payload_length];
let _ = connection.write_all(&reply).ok(); let _ = connection.write_all(&reply).ok();
}, }
TAG_BULK => { TAG_BULK => {
dbg!(message_tag); dbg!(message_tag);
// Generate a ADS-B IQ burst // Generate a ADS-B IQ burst
@@ -123,11 +122,11 @@ fn handle_client_connection(mut connection: TcpStream) {
// Throttle a bit to simulate real USB/bulk behavior // Throttle a bit to simulate real USB/bulk behavior
thread::sleep(Duration::from_millis(10)); thread::sleep(Duration::from_millis(10));
}, }
_unknown_tag => { _unknown_tag => {
// On any unrecognized tag, break out // On any unrecognized tag, break out
break break;
}, }
} }
} }
@@ -158,8 +157,7 @@ fn generate_adsb_iq_samples() -> Vec<u8> {
} }
// Concatenate preamble + data // Concatenate preamble + data
let mut full_bitstream = Vec::with_capacity( let mut full_bitstream = Vec::with_capacity(preamble_bits.len() * 2 + manchester_bits.len());
preamble_bits.len() * 2 + manchester_bits.len());
// Preamble: each '1' or '0' is one microsecond = 2 samples // Preamble: each '1' or '0' is one microsecond = 2 samples
for &pb in preamble_bits.iter() { for &pb in preamble_bits.iter() {
@@ -175,11 +173,7 @@ fn generate_adsb_iq_samples() -> Vec<u8> {
// I = 128 + 127*(bit), Q = 128 // I = 128 + 127*(bit), Q = 128
let mut iq = Vec::with_capacity(full_bitstream.len() * 2); let mut iq = Vec::with_capacity(full_bitstream.len() * 2);
for &level in full_bitstream.iter() { for &level in full_bitstream.iter() {
let i_sample = if level == 1 { let i_sample = if level == 1 { 255u8 } else { 128u8 };
255u8
} else {
128u8
};
let q_sample = 128u8; let q_sample = 128u8;
iq.push(i_sample); iq.push(i_sample);
iq.push(q_sample); iq.push(q_sample);

3
adsb/rust-toolchain.toml Normal file
View File

@@ -0,0 +1,3 @@
[toolchain]
channel = "stable"
components = ["rustfmt", "clippy"]

3
adsb/rustfmt.toml Normal file
View File

@@ -0,0 +1,3 @@
indent_style = "Block"
reorder_imports = false
tab_spaces = 2