From: Thomas Karpiniec Date: Sun, 19 Jan 2025 11:02:49 +0000 (+1100) Subject: Serial PTT support in soundmodem X-Git-Tag: v0.1.0~12 X-Git-Url: https://code.octet-stream.net/m17rt/commitdiff_plain/bc4b6ea4290384cd8c380af2a9014f162fd43683?ds=sidebyside;hp=253c89360003ac72c4f7078c03af973c58a1b129 Serial PTT support in soundmodem --- diff --git a/Cargo.lock b/Cargo.lock index 840aad6..3b49346 100755 --- a/Cargo.lock +++ b/Cargo.lock @@ -213,6 +213,16 @@ dependencies = [ "memchr", ] +[[package]] +name = "core-foundation" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b55271e5c8c478ad3f38ad24ef34923091e0548492a266d19b3c0b4d82574c63" +dependencies = [ + "core-foundation-sys", + "libc", +] + [[package]] name = "core-foundation-sys" version = "0.8.7" @@ -352,6 +362,16 @@ dependencies = [ "hashbrown", ] +[[package]] +name = "io-kit-sys" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "617ee6cf8e3f66f3b4ea67a4058564628cde41901316e19f559e14c7c72c5e7b" +dependencies = [ + "core-foundation-sys", + "mach2", +] + [[package]] name = "is_terminal_polyfill" version = "1.70.1" @@ -437,6 +457,7 @@ dependencies = [ "cpal", "log", "m17core", + "serialport", ] [[package]] @@ -533,6 +554,17 @@ dependencies = [ "jni-sys", ] +[[package]] +name = "nix" +version = "0.26.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "598beaf3cc6fdd9a5dfb1630c2800c7acd31df7aaf0f565796fba2b53ca1af1b" +dependencies = [ + "bitflags 1.3.2", + "cfg-if", + "libc", +] + [[package]] name = "nom" version = "7.1.3" @@ -690,6 +722,30 @@ dependencies = [ "winapi-util", ] +[[package]] +name = "scopeguard" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" + +[[package]] +name = "serialport" +version = "4.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5ecfc4858c2266c7695d8b8460bbd612fa81bd2e250f5f0dd16195e4b4f8b3d8" +dependencies = [ + "bitflags 2.6.0", + "cfg-if", + "core-foundation", + "core-foundation-sys", + "io-kit-sys", + "mach2", + "nix", + "scopeguard", + "unescaper", + "winapi", +] + [[package]] name = "shlex" version = "1.3.0" @@ -744,6 +800,15 @@ dependencies = [ "winnow", ] +[[package]] +name = "unescaper" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c878a167baa8afd137494101a688ef8c67125089ff2249284bd2b5f9bfedb815" +dependencies = [ + "thiserror", +] + [[package]] name = "unicode-ident" version = "1.0.14" @@ -843,6 +908,22 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + [[package]] name = "winapi-util" version = "0.1.9" @@ -852,6 +933,12 @@ dependencies = [ "windows-sys 0.59.0", ] +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" + [[package]] name = "windows" version = "0.54.0" diff --git a/m17app/Cargo.toml b/m17app/Cargo.toml index daca51f..4a083bd 100755 --- a/m17app/Cargo.toml +++ b/m17app/Cargo.toml @@ -13,3 +13,4 @@ repository = "https://code.octet-stream.net/m17rt" cpal = "0.15.3" m17core = { path = "../m17core" } log = "0.4.22" +serialport = {version = "4.7.0", default-features = false } diff --git a/m17app/src/app.rs b/m17app/src/app.rs index 561a2b7..b142663 100644 --- a/m17app/src/app.rs +++ b/m17app/src/app.rs @@ -72,6 +72,8 @@ impl M17App { } pub fn close(&self) { + // TODO: blocking function to indicate TNC has finished closing + // then we could call this in a signal handler to ensure PTT is dropped before quit let _ = self.event_tx.send(TncControlEvent::Close); } } diff --git a/m17app/src/lib.rs b/m17app/src/lib.rs index 7530aa2..6154585 100755 --- a/m17app/src/lib.rs +++ b/m17app/src/lib.rs @@ -1,4 +1,5 @@ pub mod adapter; pub mod app; +pub mod serial; pub mod soundmodem; pub mod tnc; diff --git a/m17app/src/serial.rs b/m17app/src/serial.rs new file mode 100644 index 0000000..d3eec19 --- /dev/null +++ b/m17app/src/serial.rs @@ -0,0 +1,47 @@ +use serialport::SerialPort; + +use crate::soundmodem::Ptt; + +/// The pin on the serial port which is driving PTT +pub enum PttPin { + // Ready To Send (RTS) + Rts, + // Data Terminal ready (DTR) + Dtr, +} + +pub struct SerialPtt { + port: Box, + pin: PttPin, +} + +impl SerialPtt { + pub fn available_ports() -> impl Iterator { + serialport::available_ports() + .unwrap_or_else(|_| vec![]) + .into_iter() + .map(|i| i.port_name) + } + + pub fn new(port_name: &str, pin: PttPin) -> Self { + // TODO: error handling + let port = serialport::new(port_name, 9600).open().unwrap(); + Self { port, pin } + } +} + +impl Ptt for SerialPtt { + fn ptt_on(&mut self) { + let _ = match self.pin { + PttPin::Rts => self.port.write_request_to_send(true), + PttPin::Dtr => self.port.write_data_terminal_ready(true), + }; + } + + fn ptt_off(&mut self) { + let _ = match self.pin { + PttPin::Rts => self.port.write_request_to_send(false), + PttPin::Dtr => self.port.write_data_terminal_ready(false), + }; + } +} diff --git a/m17app/src/soundmodem.rs b/m17app/src/soundmodem.rs index 74c176b..dced157 100644 --- a/m17app/src/soundmodem.rs +++ b/m17app/src/soundmodem.rs @@ -23,7 +23,7 @@ pub struct Soundmodem { } impl Soundmodem { - pub fn new_with_input_and_output(input: I, output: O) -> Self { + pub fn new(input: I, output: O, ptt: P) -> Self { // must create TNC here let (event_tx, event_rx) = sync_channel(128); let (kiss_out_tx, kiss_out_rx) = sync_channel(128); @@ -33,6 +33,7 @@ impl Soundmodem { kiss_out_tx, Box::new(input), Box::new(output), + Box::new(ptt), ); Self { event_tx, @@ -127,6 +128,7 @@ fn spawn_soundmodem_worker( kiss_out_tx: SyncSender>, input: Box, output: Box, + mut ptt_driver: Box, ) { std::thread::spawn(move || { // TODO: should be able to provide a custom Demodulator for a soundmodem @@ -170,7 +172,10 @@ fn spawn_soundmodem_worker( input.start(event_tx.clone()); output.start(event_tx.clone(), out_buffer.clone()); } - SoundmodemEvent::Close => break, + SoundmodemEvent::Close => { + ptt_driver.ptt_off(); + break; + } SoundmodemEvent::DidReadFromOutputBuffer { len, timestamp } => { let (occupied, internal_latency) = { let out_buffer = out_buffer.read().unwrap(); @@ -194,9 +199,9 @@ fn spawn_soundmodem_worker( let new_ptt = tnc.ptt(); if new_ptt != ptt { if new_ptt { - // turn it on + ptt_driver.ptt_on(); } else { - // turn it off + ptt_driver.ptt_off(); } } ptt = new_ptt; @@ -637,3 +642,22 @@ impl OutputSink for OutputSoundcard { let _ = self.end_tx.lock().unwrap().take(); } } + +pub trait Ptt: Send + 'static { + fn ptt_on(&mut self); + fn ptt_off(&mut self); +} + +/// There is no PTT because this TNC will never make transmissions on a real radio. +pub struct NullPtt; + +impl NullPtt { + pub fn new() -> Self { + Self + } +} + +impl Ptt for NullPtt { + fn ptt_on(&mut self) {} + fn ptt_off(&mut self) {} +} diff --git a/m17codec2/src/lib.rs b/m17codec2/src/lib.rs index e33409f..5146bca 100755 --- a/m17codec2/src/lib.rs +++ b/m17codec2/src/lib.rs @@ -142,7 +142,7 @@ fn stream_thread(end: Receiver<()>, state: Arc>, output_card let stream = device .build_output_stream( &config.into(), - move |data: &mut [i16], info: &cpal::OutputCallbackInfo| { + move |data: &mut [i16], _info: &cpal::OutputCallbackInfo| { output_cb(data, &state); }, |e| { diff --git a/tools/m17rt-demod/src/main.rs b/tools/m17rt-demod/src/main.rs index 40ab3db..9c229af 100755 --- a/tools/m17rt-demod/src/main.rs +++ b/tools/m17rt-demod/src/main.rs @@ -1,5 +1,5 @@ use m17app::app::M17App; -use m17app::soundmodem::{InputRrcFile, InputSoundcard, NullOutputSink, Soundmodem}; +use m17app::soundmodem::{InputRrcFile, InputSoundcard, NullOutputSink, NullPtt, Soundmodem}; use m17codec2::Codec2Adapter; use std::path::PathBuf; @@ -9,7 +9,7 @@ pub fn m17app_test() { //let path = PathBuf::from("../../../Data/mymod-noisy.raw"); let source = InputRrcFile::new(path); //let source = InputSoundcard::new(); - let soundmodem = Soundmodem::new_with_input_and_output(source, NullOutputSink::new()); + let soundmodem = Soundmodem::new(source, NullOutputSink::new(), NullPtt::new()); let app = M17App::new(soundmodem); app.add_stream_adapter(Codec2Adapter::new()); app.start(); diff --git a/tools/m17rt-mod/src/main.rs b/tools/m17rt-mod/src/main.rs index 968c22a..7ba6961 100644 --- a/tools/m17rt-mod/src/main.rs +++ b/tools/m17rt-mod/src/main.rs @@ -1,7 +1,7 @@ use m17app::app::M17App; use m17app::soundmodem::{ - InputRrcFile, InputSoundcard, NullInputSource, NullOutputSink, OutputRrcFile, OutputSoundcard, - Soundmodem, + InputRrcFile, InputSoundcard, NullInputSource, NullOutputSink, NullPtt, OutputRrcFile, + OutputSoundcard, Soundmodem, }; use m17codec2::{Codec2Adapter, WavePlayer}; use std::path::PathBuf; @@ -11,7 +11,7 @@ pub fn mod_test() { //let out_path = PathBuf::from("../../../Data/mymod.rrc"); //let output = OutputRrcFile::new(out_path); let output = OutputSoundcard::new(); - let soundmodem = Soundmodem::new_with_input_and_output(NullInputSource::new(), output); + 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));