"m17core",
]
+[[package]]
+name = "m17rt-rxpacket"
+version = "0.1.0"
+dependencies = [
+ "cpal",
+ "env_logger",
+ "log",
+ "m17app",
+ "m17core",
+]
+
+[[package]]
+name = "m17rt-txpacket"
+version = "0.1.0"
+dependencies = [
+ "env_logger",
+ "log",
+ "m17app",
+ "m17core",
+]
+
[[package]]
name = "mach2"
version = "0.4.2"
[workspace]
resolver = "2"
members = [
- "m17app", "m17codec2", "m17core", "tools/m17rt-demod", "tools/m17rt-mod",
+ "m17app", "m17codec2", "m17core", "tools/m17rt-demod", "tools/m17rt-mod", "tools/m17rt-txpacket", "tools/m17rt-rxpacket"
]
-use crate::app::TxHandle;
-use m17core::protocol::{LsfFrame, PacketType};
+use crate::{app::TxHandle, link_setup::LinkSetup};
+use m17core::protocol::PacketType;
use std::sync::Arc;
pub trait PacketAdapter: Send + Sync + 'static {
- fn adapter_registered(&self, id: usize, handle: TxHandle);
- fn adapter_removed(&self);
- fn tnc_started(&self);
- fn tnc_closed(&self);
- fn packet_received(&self, lsf: LsfFrame, packet_type: PacketType, content: Arc<[u8]>);
+ fn adapter_registered(&self, _id: usize, _handle: TxHandle) {}
+ fn adapter_removed(&self) {}
+ fn tnc_started(&self) {}
+ fn tnc_closed(&self) {}
+ fn packet_received(
+ &self,
+ _link_setup: LinkSetup,
+ _packet_type: PacketType,
+ _content: Arc<[u8]>,
+ ) {
+ }
}
pub trait StreamAdapter: Send + Sync + 'static {
- fn adapter_registered(&self, id: usize, handle: TxHandle);
- fn adapter_removed(&self);
- fn tnc_started(&self);
- fn tnc_closed(&self);
- fn stream_began(&self, lsf: LsfFrame);
- fn stream_data(&self, frame_number: u16, is_final: bool, data: Arc<[u8; 16]>);
+ fn adapter_registered(&self, _id: usize, _handle: TxHandle) {}
+ fn adapter_removed(&self) {}
+ fn tnc_started(&self) {}
+ fn tnc_closed(&self) {}
+ fn stream_began(&self, _link_setup: LinkSetup) {}
+ fn stream_data(&self, _frame_number: u16, _is_final: bool, _data: Arc<[u8; 16]>) {}
// TODO
// fn stream_lost(&self);
}
impl TxHandle {
- pub fn transmit_packet(
- &self,
- link_setup: &LinkSetup,
- packet_type: &PacketType,
- payload: &[u8],
- ) {
+ pub fn transmit_packet(&self, link_setup: &LinkSetup, packet_type: PacketType, payload: &[u8]) {
let (pack_type, pack_type_len) = packet_type.as_proto();
if pack_type_len + payload.len() > 823 {
// TODO: error for invalid transmission type
adapters.read().unwrap().packet.values().cloned().collect();
for s in subs {
s.packet_received(
- lsf.clone(),
+ LinkSetup::new_raw(lsf.clone()),
packet_type.clone(),
packet_payload.clone(),
);
let subs: Vec<_> =
adapters.read().unwrap().stream.values().cloned().collect();
for s in subs {
- s.stream_began(lsf.clone());
+ s.stream_began(LinkSetup::new_raw(lsf.clone()));
}
} else if n == 26 {
if !stream_running {
Self { raw: frame }
}
+ pub fn source(&self) -> M17Address {
+ M17Address(self.raw.source())
+ }
+
+ pub fn destination(&self) -> M17Address {
+ M17Address(self.raw.destination())
+ }
+
/// Set up an unencrypted voice stream with channel access number 0 and the given source and destination.
pub fn new_voice(source: &M17Address, destination: &M17Address) -> Self {
Self {
fn tnc_closed(&self) {}
- fn stream_began(&self, _lsf: LsfFrame) {
+ fn stream_began(&self, _link_setup: LinkSetup) {
// for now we will assume:
// - unencrypted
// - data type is Voice (Codec2 3200), not Voice+Data
Mode::Stream => {
let kiss = KissFrame::new_stream_setup(&lsf.0).unwrap();
self.kiss_to_host(kiss);
- self.state = State::RxStream(RxStreamState { lsf, index: 0 });
+ self.state = State::RxStream(RxStreamState {
+ _lsf: lsf,
+ index: 0,
+ });
}
}
}
let start = 25 * rx.count;
let end = start + payload_len;
rx.packet[start..(start + payload_len)]
- .copy_from_slice(&packet.payload);
+ .copy_from_slice(&packet.payload[0..payload_len]);
// TODO: compatible packets should be sent on port 0 too
let kiss =
KissFrame::new_full_packet(&rx.lsf.0, &rx.packet[0..end])
// TODO: avoid discarding the first data payload here
// need a queue depth of 2 for outgoing kiss
self.state = State::RxStream(RxStreamState {
- lsf,
+ _lsf: lsf,
index: stream.frame_number + 1,
});
}
}
pending.lsf = Some(lsf);
let app_data_len = len - 30;
- pending.app_data[0..app_data_len].copy_from_slice(&payload[30..]);
+ pending.app_data[0..app_data_len].copy_from_slice(&payload[30..len]);
pending.app_data_len = app_data_len;
self.packet_queue[self.packet_next] = pending;
self.packet_next = (self.packet_next + 1) % 4;
struct RxStreamState {
/// Track identifying information for this transmission so we can tell if it changes.
- lsf: LsfFrame,
+ _lsf: LsfFrame,
/// Expected next frame number. Allowed to skip values on RX, but not go backwards.
index: u16,
)
};
let mut payload = [0u8; 25];
- payload.copy_from_slice(
+ payload[0..data_len].copy_from_slice(
&self.app_data[self.app_data_transmitted..(self.app_data_transmitted + data_len)],
);
self.app_data_transmitted += data_len;
--- /dev/null
+[package]
+name = "m17rt-rxpacket"
+version = "0.1.0"
+edition = "2021"
+license = "MIT"
+authors = ["Thomas Karpiniec <tom.karpiniec@outlook.com"]
+publish = false
+
+[dependencies]
+m17core = { path = "../../m17core" }
+m17app = { path = "../../m17app" }
+
+cpal = "0.15.3"
+env_logger = "0.11.6"
+log = "0.4.22"
--- /dev/null
+use m17app::adapter::PacketAdapter;
+use m17app::app::M17App;
+use m17app::link_setup::LinkSetup;
+use m17app::soundmodem::{InputRrcFile, NullOutputSink, NullPtt, Soundmodem};
+use m17core::protocol::PacketType;
+use std::path::PathBuf;
+use std::sync::Arc;
+
+fn main() {
+ let path = PathBuf::from("../../../Data/mypacket.rrc");
+ let soundmodem = Soundmodem::new(
+ InputRrcFile::new(path),
+ NullOutputSink::new(),
+ NullPtt::new(),
+ );
+ let app = M17App::new(soundmodem);
+ app.add_packet_adapter(PacketPrinter);
+ app.start();
+
+ loop {
+ std::thread::park();
+ }
+}
+
+struct PacketPrinter;
+impl PacketAdapter for PacketPrinter {
+ fn packet_received(&self, link_setup: LinkSetup, packet_type: PacketType, content: Arc<[u8]>) {
+ println!(
+ "from {} to {} type {:?} len {}",
+ link_setup.source(),
+ link_setup.destination(),
+ packet_type,
+ content.len()
+ );
+ println!("{}", String::from_utf8_lossy(&content));
+ }
+}
--- /dev/null
+[package]
+name = "m17rt-txpacket"
+version = "0.1.0"
+edition = "2021"
+license = "MIT"
+authors = ["Thomas Karpiniec <tom.karpiniec@outlook.com"]
+publish = false
+
+[dependencies]
+m17core = { path = "../../m17core" }
+m17app = { path = "../../m17app" }
+
+env_logger = "0.11.6"
+log = "0.4.22"
--- /dev/null
+use m17app::app::M17App;
+use m17app::link_setup::{LinkSetup, M17Address};
+use m17app::soundmodem::{
+ InputRrcFile, InputSoundcard, NullInputSource, NullOutputSink, NullPtt, OutputRrcFile,
+ OutputSoundcard, Soundmodem,
+};
+use m17core::protocol::PacketType;
+use std::path::PathBuf;
+
+pub fn mod_test() {
+ let out_path = PathBuf::from("../../../Data/mypacket.rrc");
+ let output = OutputRrcFile::new(out_path);
+ //let output = OutputSoundcard::new();
+ let soundmodem = Soundmodem::new(NullInputSource::new(), output, NullPtt::new());
+ let app = M17App::new(soundmodem);
+ app.start();
+ std::thread::sleep(std::time::Duration::from_secs(1));
+ println!("Transmitting packet...");
+
+ let source = M17Address::from_callsign("VK7XT").unwrap();
+ let destination = M17Address::new_broadcast();
+ let link_setup = LinkSetup::new_packet(&source, &destination);
+ let payload = b"Hello, world!";
+ app.tx()
+ .transmit_packet(&link_setup, PacketType::Raw, payload);
+
+ std::thread::sleep(std::time::Duration::from_secs(5));
+}
+
+fn main() {
+ env_logger::init();
+ mod_test();
+}