Formatting
This commit is contained in:
5
Makefile
5
Makefile
@@ -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}'
|
||||
@echo
|
||||
|
||||
format: format-api format-ui ## Format code
|
||||
format: format-api format-ui format-adsb ## Format code
|
||||
|
||||
psql: ## Connect to the PSQL DB
|
||||
@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 #
|
||||
##################
|
||||
|
||||
format-adsb: ## Format code
|
||||
@cd adsb && cargo fmt
|
||||
|
||||
build-adsb: ## Build the ADS-B project
|
||||
@cd adsb && cargo build
|
||||
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
use crate::hex_to_bytes;
|
||||
use std::fmt::Display;
|
||||
use std::io::{Error, ErrorKind, Result};
|
||||
|
||||
@@ -24,7 +25,8 @@ impl ADSBFrame {
|
||||
if frame.len() != 14 {
|
||||
return Err(Error::new(
|
||||
ErrorKind::InvalidInput,
|
||||
format!("expected 14 bytes, received {}", frame.len())));
|
||||
format!("expected 14 bytes, received {}", frame.len()),
|
||||
));
|
||||
}
|
||||
|
||||
let mut raw_frame = "".to_string();
|
||||
@@ -37,7 +39,10 @@ impl ADSBFrame {
|
||||
if downlink_format != 17 {
|
||||
return Err(Error::new(
|
||||
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,
|
||||
icao,
|
||||
message,
|
||||
parity
|
||||
parity,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn encode(&self) -> Result<Vec<u8>> {
|
||||
Ok(hex_to_bytes(&self.raw_frame)?)
|
||||
}
|
||||
|
||||
fn decode_icao(data: &[u8]) -> Result<String> {
|
||||
if data.len() != 3 {
|
||||
return Err(Error::new(
|
||||
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))
|
||||
.collect::<String>();
|
||||
Ok(s)
|
||||
@@ -75,29 +86,26 @@ impl ADSBFrame {
|
||||
if data.len() != 3 {
|
||||
return Err(Error::new(
|
||||
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)
|
||||
| ((data[1] as u32) << 8)
|
||||
| (data[2] as u32);
|
||||
let p = ((data[0] as u32) << 16) | ((data[1] as u32) << 8) | (data[2] as u32);
|
||||
Ok(p)
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for ADSBFrame {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
write!(f, "Frame: {}\
|
||||
write!(
|
||||
f,
|
||||
"Frame: {}\
|
||||
\nDF: {}\
|
||||
\nCA: {:?}\
|
||||
\nICAO: {}\
|
||||
\nME: {:?}\
|
||||
\nPI: {}",
|
||||
self.raw_frame,
|
||||
self.downlink_format,
|
||||
&self.capability,
|
||||
self.icao,
|
||||
&self.message,
|
||||
self.parity)
|
||||
self.raw_frame, self.downlink_format, &self.capability, self.icao, &self.message, self.parity
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -176,7 +184,9 @@ impl ADSBMessage {
|
||||
// First 5 bits is the type code
|
||||
let type_code = data[0] >> 3;
|
||||
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)?),
|
||||
9..=18 => ADSBMessage::AirbornePositionBaro(AirbornePositionBaro::decode(data)?),
|
||||
19 => ADSBMessage::AirborneVelocities(AirborneVelocities::decode(data)?),
|
||||
@@ -185,10 +195,12 @@ impl ADSBMessage {
|
||||
28 => ADSBMessage::AircraftStatus(AircraftStatus::decode(data)?),
|
||||
29 => ADSBMessage::TargetState(TargetState::decode(data)?),
|
||||
31 => ADSBMessage::AircraftOperationStatus(AircraftOperationStatus::decode(data)?),
|
||||
_ => return Err(Error::new(
|
||||
_ => {
|
||||
return Err(Error::new(
|
||||
ErrorKind::InvalidData,
|
||||
format!("unsupported ADS‑B type_code {}", type_code),
|
||||
))
|
||||
}
|
||||
};
|
||||
|
||||
Ok(message)
|
||||
@@ -289,78 +301,64 @@ impl WakeVortexCategory {
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub struct SurfacePosition {
|
||||
|
||||
}
|
||||
pub struct SurfacePosition {}
|
||||
|
||||
impl SurfacePosition {
|
||||
pub fn decode(data: &[u8]) -> Result<Self> {
|
||||
pub fn decode(_data: &[u8]) -> Result<Self> {
|
||||
Ok(Self {})
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub struct AirbornePositionBaro {
|
||||
|
||||
}
|
||||
pub struct AirbornePositionBaro {}
|
||||
|
||||
impl AirbornePositionBaro {
|
||||
pub fn decode(data: &[u8]) -> Result<Self> {
|
||||
pub fn decode(_data: &[u8]) -> Result<Self> {
|
||||
Ok(Self {})
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub struct AirborneVelocities {
|
||||
|
||||
}
|
||||
pub struct AirborneVelocities {}
|
||||
|
||||
impl AirborneVelocities {
|
||||
pub fn decode(data: &[u8]) -> Result<Self> {
|
||||
pub fn decode(_data: &[u8]) -> Result<Self> {
|
||||
Ok(Self {})
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub struct AirbornePositionGNSS {
|
||||
|
||||
}
|
||||
pub struct AirbornePositionGNSS {}
|
||||
|
||||
impl AirbornePositionGNSS {
|
||||
pub fn decode(data: &[u8]) -> Result<Self> {
|
||||
pub fn decode(_data: &[u8]) -> Result<Self> {
|
||||
Ok(Self {})
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub struct AircraftStatus {
|
||||
|
||||
}
|
||||
pub struct AircraftStatus {}
|
||||
|
||||
impl AircraftStatus {
|
||||
pub fn decode(data: &[u8]) -> Result<Self> {
|
||||
pub fn decode(_data: &[u8]) -> Result<Self> {
|
||||
Ok(Self {})
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub struct TargetState {
|
||||
|
||||
}
|
||||
pub struct TargetState {}
|
||||
|
||||
impl TargetState {
|
||||
pub fn decode(data: &[u8]) -> Result<Self> {
|
||||
pub fn decode(_data: &[u8]) -> Result<Self> {
|
||||
Ok(Self {})
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub struct AircraftOperationStatus {
|
||||
|
||||
}
|
||||
pub struct AircraftOperationStatus {}
|
||||
|
||||
impl AircraftOperationStatus {
|
||||
pub fn decode(data: &[u8]) -> Result<Self> {
|
||||
pub fn decode(_data: &[u8]) -> Result<Self> {
|
||||
Ok(Self {})
|
||||
}
|
||||
}
|
||||
@@ -372,8 +370,7 @@ mod tests {
|
||||
#[test]
|
||||
fn test_decode_df_17_aircraft_information() {
|
||||
let input = [
|
||||
0x8D, 0x48, 0x40, 0xD6, 0x20, 0x2C, 0xC3, 0x71,
|
||||
0xC3, 0x1C, 0x32, 0xCE, 0x05, 0x76,
|
||||
0x8D, 0x48, 0x40, 0xD6, 0x20, 0x2C, 0xC3, 0x71, 0xC3, 0x1C, 0x32, 0xCE, 0x05, 0x76,
|
||||
];
|
||||
let frame = ADSBFrame::decode(&input).unwrap();
|
||||
assert_eq!(frame.downlink_format, 17);
|
||||
@@ -391,8 +388,7 @@ mod tests {
|
||||
assert_eq!(frame.parity, 13501814);
|
||||
|
||||
let input = [
|
||||
0x8D, 0x48, 0x40, 0xD6, 0x20, 0x2C, 0xC3, 0x71,
|
||||
0xC3, 0x2C, 0xE0, 0x57, 0x60, 0x98
|
||||
0x8D, 0x48, 0x40, 0xD6, 0x20, 0x2C, 0xC3, 0x71, 0xC3, 0x2C, 0xE0, 0x57, 0x60, 0x98,
|
||||
];
|
||||
let frame = ADSBFrame::decode(&input).unwrap();
|
||||
assert_eq!(frame.downlink_format, 17);
|
||||
@@ -410,8 +406,7 @@ mod tests {
|
||||
assert_eq!(frame.parity, 5726360);
|
||||
|
||||
let input = [
|
||||
0x8D, 0x7C, 0x71, 0x81, 0x21, 0x5D, 0x01, 0xA0,
|
||||
0x82, 0x08, 0x20, 0x4D, 0x8B, 0xF1
|
||||
0x8D, 0x7C, 0x71, 0x81, 0x21, 0x5D, 0x01, 0xA0, 0x82, 0x08, 0x20, 0x4D, 0x8B, 0xF1,
|
||||
];
|
||||
let frame = ADSBFrame::decode(&input).unwrap();
|
||||
assert_eq!(frame.downlink_format, 17);
|
||||
@@ -429,8 +424,7 @@ mod tests {
|
||||
assert_eq!(frame.parity, 5082097);
|
||||
|
||||
let input = [
|
||||
0x8D, 0x7C, 0x77, 0x45, 0x22, 0x61, 0x51, 0xA0,
|
||||
0x82, 0x08, 0x20, 0x5C, 0xE9, 0xC2
|
||||
0x8D, 0x7C, 0x77, 0x45, 0x22, 0x61, 0x51, 0xA0, 0x82, 0x08, 0x20, 0x5C, 0xE9, 0xC2,
|
||||
];
|
||||
let frame = ADSBFrame::decode(&input).unwrap();
|
||||
assert_eq!(frame.downlink_format, 17);
|
||||
@@ -448,8 +442,7 @@ mod tests {
|
||||
assert_eq!(frame.parity, 6089154);
|
||||
|
||||
let input = [
|
||||
0x8D, 0x7C, 0x80, 0xAD, 0x23, 0x58, 0xF6, 0xB1,
|
||||
0xE3, 0x5C, 0x60, 0xFF, 0x19, 0x25
|
||||
0x8D, 0x7C, 0x80, 0xAD, 0x23, 0x58, 0xF6, 0xB1, 0xE3, 0x5C, 0x60, 0xFF, 0x19, 0x25,
|
||||
];
|
||||
let frame = ADSBFrame::decode(&input).unwrap();
|
||||
assert_eq!(frame.downlink_format, 17);
|
||||
@@ -467,8 +460,7 @@ mod tests {
|
||||
assert_eq!(frame.parity, 16718117);
|
||||
|
||||
let input = [
|
||||
0x8D, 0x7C, 0x14, 0x65, 0x25, 0x44, 0x60, 0x74,
|
||||
0xDF, 0x58, 0x20, 0x73, 0x8E, 0x90
|
||||
0x8D, 0x7C, 0x14, 0x65, 0x25, 0x44, 0x60, 0x74, 0xDF, 0x58, 0x20, 0x73, 0x8E, 0x90,
|
||||
];
|
||||
let frame = ADSBFrame::decode(&input).unwrap();
|
||||
assert_eq!(frame.downlink_format, 17);
|
||||
@@ -489,8 +481,7 @@ mod tests {
|
||||
#[test]
|
||||
fn test_decode_df_17_operation_status() {
|
||||
let input = [
|
||||
0x8D, 0x89, 0x65, 0xD2, 0xF8, 0x21, 0x00, 0x02,
|
||||
0x00, 0x49, 0xB8, 0x94, 0xA4, 0x5F,
|
||||
0x8D, 0x89, 0x65, 0xD2, 0xF8, 0x21, 0x00, 0x02, 0x00, 0x49, 0xB8, 0x94, 0xA4, 0x5F,
|
||||
];
|
||||
let frame = ADSBFrame::decode(&input).unwrap();
|
||||
dbg!(frame);
|
||||
@@ -1,4 +1,6 @@
|
||||
use std::io::Result;
|
||||
use std::io::{Error, ErrorKind, Result};
|
||||
|
||||
pub mod adsb;
|
||||
|
||||
pub trait RtlDevice {
|
||||
/// 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])?;
|
||||
|
||||
// Precompute the preamble pattern in “half‐bit” units (16 samples)
|
||||
let preamble_halfbit_pattern = [
|
||||
1,1, 0,0, 1,1, 0,0,
|
||||
1,1, 0,0, 0,0, 0,0
|
||||
];
|
||||
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];
|
||||
@@ -50,11 +49,7 @@ pub fn run<S: RtlDevice>(device: &mut S) -> Result<()> {
|
||||
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
|
||||
});
|
||||
halfbit_samples.push(if i_sample > 200 { 1 } else { 0 });
|
||||
}
|
||||
|
||||
// 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 {
|
||||
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
|
||||
@@ -117,3 +112,46 @@ pub fn run<S: RtlDevice>(device: &mut S) -> Result<()> {
|
||||
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,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,13 +1,12 @@
|
||||
mod tcp_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::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)]
|
||||
#[command(author, version, about, long_about = None)]
|
||||
@@ -23,11 +22,11 @@ struct ReceiverArgs {
|
||||
fn main() -> Result<()> {
|
||||
let args = ReceiverArgs::parse();
|
||||
|
||||
if let Some(mut hexString) = args.decode {
|
||||
if let Some(stripped) = hexString.strip_prefix("0x") {
|
||||
hexString = stripped.to_string();
|
||||
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(&hexString)?;
|
||||
let buf = hex_to_bytes(&hex_string)?;
|
||||
let frame = ADSBFrame::decode(&buf)?;
|
||||
|
||||
println!("{}", frame);
|
||||
@@ -44,41 +43,3 @@ fn main() -> Result<()> {
|
||||
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,
|
||||
}
|
||||
}
|
||||
@@ -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::time::Duration;
|
||||
use rusb::{request_type, Context, DeviceHandle, Direction, Recipient, RequestType, UsbContext};
|
||||
use adsb_lib::RtlDevice;
|
||||
|
||||
// USB identifiers for RTL-SDR
|
||||
const VENDOR_ID: u16 = 0x0BDA;
|
||||
@@ -19,15 +19,17 @@ impl RusbRtl {
|
||||
/// Open the USB device, claim interface 0, and return a wrapper
|
||||
pub fn open() -> Result<Self> {
|
||||
// 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
|
||||
let mut handle = ctx
|
||||
let handle = ctx
|
||||
.open_device_with_vid_pid(VENDOR_ID, PRODUCT_ID)
|
||||
.ok_or_else(|| Error::new(ErrorKind::NotFound, "Device not found"))?;
|
||||
|
||||
// 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 })
|
||||
}
|
||||
@@ -42,14 +44,16 @@ impl RusbRtl {
|
||||
let bm_request_type = request_type(Direction::Out, RequestType::Vendor, Recipient::Device);
|
||||
|
||||
// Perform the control transfer.
|
||||
self.handle
|
||||
self
|
||||
.handle
|
||||
.write_control(
|
||||
bm_request_type,
|
||||
request,
|
||||
0, // wValue
|
||||
0, // wIndex
|
||||
data,
|
||||
Duration::from_secs(1))
|
||||
USB_TRANSFER_TIMEOUT,
|
||||
)
|
||||
.map(|_bytes_written| ())
|
||||
}
|
||||
|
||||
@@ -66,14 +70,14 @@ impl RusbRtl {
|
||||
let mut buffer = vec![0u8; response_length];
|
||||
|
||||
// Read the control response into the buffer
|
||||
let n = self.handle
|
||||
.read_control(
|
||||
let n = self.handle.read_control(
|
||||
bm_request_type,
|
||||
request,
|
||||
0, // wValue
|
||||
0, // wIndex
|
||||
&mut buffer,
|
||||
Duration::from_secs(1))?;
|
||||
USB_TRANSFER_TIMEOUT,
|
||||
)?;
|
||||
|
||||
// Truncate to the actual length returned by the device
|
||||
buffer.truncate(n);
|
||||
@@ -83,18 +87,21 @@ impl RusbRtl {
|
||||
|
||||
impl RtlDevice for RusbRtl {
|
||||
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))
|
||||
}
|
||||
|
||||
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))
|
||||
}
|
||||
|
||||
fn read_bulk(&mut self, buffer: &mut [u8]) -> Result<usize> {
|
||||
self.handle
|
||||
.read_bulk(DATA_ENDPOINT_ADDRESS, buffer, Duration::from_secs(1))
|
||||
self
|
||||
.handle
|
||||
.read_bulk(DATA_ENDPOINT_ADDRESS, buffer, USB_TRANSFER_TIMEOUT)
|
||||
.map_err(|err| Error::new(ErrorKind::Other, err))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
use adsb_lib::RtlDevice;
|
||||
use std::io::{Error, ErrorKind, Read, Result, Write};
|
||||
use std::net::TcpStream;
|
||||
use adsb_lib::RtlDevice;
|
||||
|
||||
// Tags for framing requests/responses over the TCP socket
|
||||
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;
|
||||
|
||||
// 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)
|
||||
}
|
||||
}
|
||||
@@ -1,8 +1,8 @@
|
||||
use clap::Parser;
|
||||
use std::io::{Read, Write};
|
||||
use std::net::{TcpListener, TcpStream};
|
||||
use std::thread;
|
||||
use std::time::Duration;
|
||||
use clap::Parser;
|
||||
|
||||
// Framing tags
|
||||
const TAG_CONTROL_OUT: u8 = 0x10;
|
||||
@@ -10,8 +10,7 @@ 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,
|
||||
0x8D, 0x48, 0x40, 0xD6, 0x20, 0x2C, 0xC3, 0x71, 0xC3, 0x2C, 0xE0, 0x57, 0x60, 0x98,
|
||||
];
|
||||
|
||||
#[derive(Parser, Debug)]
|
||||
@@ -84,13 +83,13 @@ fn handle_client_connection(mut connection: TcpStream) {
|
||||
payload_buffer[0],
|
||||
payload_buffer[1],
|
||||
payload_buffer[2],
|
||||
payload_buffer[3]
|
||||
payload_buffer[3],
|
||||
]);
|
||||
println!("SET_FREQ -> {} Hz", current_frequency_hz);
|
||||
}
|
||||
// Acknowledge with a single byte = 0 (OK)
|
||||
connection.write_all(&[0u8]).ok();
|
||||
},
|
||||
}
|
||||
TAG_CONTROL_IN => {
|
||||
dbg!(message_tag);
|
||||
// Simulate a CONTROL_IN reply with a fixed pattern
|
||||
@@ -105,7 +104,7 @@ fn handle_client_connection(mut connection: TcpStream) {
|
||||
// Payload (0x42 repeated)
|
||||
let reply = vec![0x42; payload_length];
|
||||
let _ = connection.write_all(&reply).ok();
|
||||
},
|
||||
}
|
||||
TAG_BULK => {
|
||||
dbg!(message_tag);
|
||||
// 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
|
||||
thread::sleep(Duration::from_millis(10));
|
||||
},
|
||||
}
|
||||
_unknown_tag => {
|
||||
// On any unrecognized tag, break out
|
||||
break
|
||||
},
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -158,8 +157,7 @@ fn generate_adsb_iq_samples() -> Vec<u8> {
|
||||
}
|
||||
|
||||
// Concatenate preamble + data
|
||||
let mut full_bitstream = Vec::with_capacity(
|
||||
preamble_bits.len() * 2 + manchester_bits.len());
|
||||
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() {
|
||||
@@ -175,11 +173,7 @@ fn generate_adsb_iq_samples() -> Vec<u8> {
|
||||
// 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 i_sample = if level == 1 { 255u8 } else { 128u8 };
|
||||
let q_sample = 128u8;
|
||||
iq.push(i_sample);
|
||||
iq.push(q_sample);
|
||||
|
||||
3
adsb/rust-toolchain.toml
Normal file
3
adsb/rust-toolchain.toml
Normal file
@@ -0,0 +1,3 @@
|
||||
[toolchain]
|
||||
channel = "stable"
|
||||
components = ["rustfmt", "clippy"]
|
||||
3
adsb/rustfmt.toml
Normal file
3
adsb/rustfmt.toml
Normal file
@@ -0,0 +1,3 @@
|
||||
indent_style = "Block"
|
||||
reorder_imports = false
|
||||
tab_spaces = 2
|
||||
Reference in New Issue
Block a user