From e50e6cc4ec97f653d193ed82d9730e6247b72662 Mon Sep 17 00:00:00 2001
From: Stephen D <webmaster@scd31.com>
Date: Wed, 10 Jul 2024 17:06:52 -0300
Subject: [PATCH] most basic txing

---
 Cargo.lock                                | 120 ++++++++++++++++++++++
 companion-software/Cargo.toml             |   1 +
 companion-software/src/controller.rs      |   4 +-
 companion-software/src/main.rs            |   8 +-
 companion-software/src/radio.rs           |  89 +++++++++++++---
 companion-software/src/storage/message.rs |   8 ++
 6 files changed, 207 insertions(+), 23 deletions(-)

diff --git a/Cargo.lock b/Cargo.lock
index 292cae9..9775354 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -56,6 +56,18 @@ version = "0.14.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "2d7e60934ceec538daadb9d8432424ed043a904d8e0243f3c6446bce549a46ac"
 
+[[package]]
+name = "bitvec"
+version = "1.0.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1bc2832c24239b0141d5674bb9174f9d68a8b5b3f2753311927c172ca46f7e9c"
+dependencies = [
+ "funty",
+ "radium",
+ "tap",
+ "wyz",
+]
+
 [[package]]
 name = "byteorder"
 version = "1.5.0"
@@ -79,6 +91,7 @@ dependencies = [
  "embedded-hal 0.2.7",
  "embedded-text",
  "fugit",
+ "ham-cats",
  "heapless 0.8.0",
  "panic-rtt-target",
  "rf4463",
@@ -182,12 +195,24 @@ version = "1.1.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "7059fff8937831a9ae6f0fe4d658ffabf58f2ca96aa9dec1c889f936f705f216"
 
+[[package]]
+name = "crunchy"
+version = "0.2.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7"
+
 [[package]]
 name = "debug-helper"
 version = "0.3.13"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "f578e8e2c440e7297e008bb5486a3a8a194775224bbc23729b0dbdfaeebf162e"
 
+[[package]]
+name = "doc-comment"
+version = "0.3.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fea41bba32d969b513997752735605054bc0dfa92b4c56bf1189f2e174be7a10"
+
 [[package]]
 name = "either"
 version = "1.13.0"
@@ -278,6 +303,15 @@ dependencies = [
  "object-chain",
 ]
 
+[[package]]
+name = "encoding_rs"
+version = "0.8.34"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b45de904aa0b010bce2ab45264d0631681847fa7b6f2eaa7dab7619943bc4f59"
+dependencies = [
+ "cfg-if",
+]
+
 [[package]]
 name = "float-cmp"
 version = "0.9.0"
@@ -335,6 +369,12 @@ dependencies = [
  "gcd",
 ]
 
+[[package]]
+name = "funty"
+version = "2.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e6d5a32815ae3f33302d95fdcb2ce17862f8c65363dcfd29360480ba1001fc9c"
+
 [[package]]
 name = "futures-core"
 version = "0.3.30"
@@ -365,6 +405,31 @@ version = "2.3.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "1d758ba1b47b00caf47f24925c0074ecb20d6dfcffe7f6d53395c0465674841a"
 
+[[package]]
+name = "half"
+version = "2.4.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6dd08c532ae367adf81c312a4580bc67f1d0fe8bc9c460520283f4c0ff277888"
+dependencies = [
+ "cfg-if",
+ "crunchy",
+]
+
+[[package]]
+name = "ham-cats"
+version = "0.2.1"
+source = "git+https://gitlab.scd31.com/cats/ham-cats#c09720400728eae584ea285913d109b3b36b2f32"
+dependencies = [
+ "arrayvec",
+ "bitvec",
+ "crc",
+ "encoding_rs",
+ "half",
+ "labrador-ldpc",
+ "paste",
+ "snafu",
+]
+
 [[package]]
 name = "hash32"
 version = "0.2.1"
@@ -412,6 +477,12 @@ dependencies = [
  "stable_deref_trait",
 ]
 
+[[package]]
+name = "heck"
+version = "0.4.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8"
+
 [[package]]
 name = "indexmap"
 version = "1.9.3"
@@ -431,6 +502,12 @@ dependencies = [
  "either",
 ]
 
+[[package]]
+name = "labrador-ldpc"
+version = "1.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "52bbada3351e4ae9078bcd095b83eac3a0bc29ed7d63031f062830f8f22485a4"
+
 [[package]]
 name = "lock_api"
 version = "0.4.12"
@@ -584,6 +661,12 @@ dependencies = [
  "proc-macro2",
 ]
 
+[[package]]
+name = "radium"
+version = "0.7.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "dc33ff2d4973d518d823d61aa239014831e521c75da58e3df4840d3f47749d09"
+
 [[package]]
 name = "rand_core"
 version = "0.6.4"
@@ -791,6 +874,28 @@ version = "0.7.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3"
 
+[[package]]
+name = "snafu"
+version = "0.7.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e4de37ad025c587a29e8f3f5605c00f70b98715ef90b9061a815b9e59e9042d6"
+dependencies = [
+ "doc-comment",
+ "snafu-derive",
+]
+
+[[package]]
+name = "snafu-derive"
+version = "0.7.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "990079665f075b699031e9c08fd3ab99be5029b96f3b78dc0709e8f77e4efebf"
+dependencies = [
+ "heck",
+ "proc-macro2",
+ "quote",
+ "syn 1.0.109",
+]
+
 [[package]]
 name = "spin"
 version = "0.9.8"
@@ -828,6 +933,12 @@ dependencies = [
  "unicode-ident",
 ]
 
+[[package]]
+name = "tap"
+version = "1.0.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369"
+
 [[package]]
 name = "ufmt-write"
 version = "0.1.0"
@@ -893,3 +1004,12 @@ checksum = "de437e2a6208b014ab52972a27e59b33fa2920d3e00fe05026167a1c509d19cc"
 dependencies = [
  "vcell",
 ]
+
+[[package]]
+name = "wyz"
+version = "0.5.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "05f360fc0b24296329c78fda852a1e9ae82de9cf7b27dae4b7f62f118f77b9ed"
+dependencies = [
+ "tap",
+]
diff --git a/companion-software/Cargo.toml b/companion-software/Cargo.toml
index f39204f..2ca7b57 100644
--- a/companion-software/Cargo.toml
+++ b/companion-software/Cargo.toml
@@ -23,3 +23,4 @@ crc = "3.2.1"
 rp2040-flash = "0.5.0"
 sectorize = "0.1.0"
 rf4463 = { path = "../rf4463-lib" }
+ham-cats = { git = "https://gitlab.scd31.com/cats/ham-cats" }
diff --git a/companion-software/src/controller.rs b/companion-software/src/controller.rs
index 735cc6e..68ffb48 100644
--- a/companion-software/src/controller.rs
+++ b/companion-software/src/controller.rs
@@ -8,7 +8,6 @@ use embedded_graphics::{
 };
 use heapless::{String, Vec};
 use rp2040_hal::timer::Instant;
-use rtt_target::rprintln;
 
 use crate::{
     app::{monotonics, MySpi, HEIGHT, WIDTH},
@@ -202,7 +201,8 @@ impl Controller {
             self.tx_queue.remove(id);
 
             let message = self.storage.get_message(msg.id).unwrap();
-            rprintln!("send message {}", message.content());
+
+            self.radio.send_message(&message, spi);
         }
 
         self.radio.tick(spi);
diff --git a/companion-software/src/main.rs b/companion-software/src/main.rs
index 2630382..8ee6815 100644
--- a/companion-software/src/main.rs
+++ b/companion-software/src/main.rs
@@ -28,7 +28,7 @@ mod app {
         drivers::st7789::St7789,
         gui::Element,
         keyboard::{Keyboard, Led},
-        radio::RadioController,
+        radio::{RadioController, MAX_PACKET_LEN},
         touchpad::Touchpad,
         voltage::VoltMeter,
     };
@@ -213,8 +213,7 @@ mod app {
         let row_ser = pins.gpio6.into_floating_input();
         let row_ld = pins.gpio7.into_push_pull_output();
 
-        let mut keyboard =
-            Keyboard::new(timer, col_clk, col_ser, col_rclk, row_clk, row_ser, row_ld);
+        let keyboard = Keyboard::new(timer, col_clk, col_ser, col_rclk, row_clk, row_ser, row_ld);
 
         let mut pwm = pwm_slices.pwm4;
         pwm.set_ph_correct();
@@ -247,7 +246,8 @@ mod app {
         // SPI Radio
         let radio_sdn = pins.gpio19.into_push_pull_output();
         let radio_cs = pins.gpio21.into_push_pull_output();
-        let radio = RadioController::new(&mut spi, radio_sdn, radio_cs, timer);
+        let buf = singleton!(: [u8; MAX_PACKET_LEN] = [0; MAX_PACKET_LEN]).unwrap();
+        let radio = RadioController::new(&mut spi, radio_sdn, radio_cs, timer, buf);
 
         // Set up voltage monitoring
         let adc = Adc::new(pac.ADC, &mut pac.RESETS);
diff --git a/companion-software/src/radio.rs b/companion-software/src/radio.rs
index 8ffa94d..94c2204 100644
--- a/companion-software/src/radio.rs
+++ b/companion-software/src/radio.rs
@@ -1,3 +1,8 @@
+use ham_cats::{
+    buffer::Buffer,
+    packet::Packet,
+    whisker::{Destination, Identification, NodeInfoBuilder, Route},
+};
 use rf4463::{config, Rf4463};
 use rp2040_hal::{
     gpio::{
@@ -6,9 +11,8 @@ use rp2040_hal::{
     },
     Timer,
 };
-use rtt_target::rdbg;
 
-use crate::app::MySpi;
+use crate::{app::MySpi, storage::Message};
 
 type SdnPin = Pin<Gpio19, FunctionSio<SioOutput>, PullDown>;
 type CsPin = Pin<Gpio21, FunctionSio<SioOutput>, PullDown>;
@@ -16,35 +20,75 @@ type CsPin = Pin<Gpio21, FunctionSio<SioOutput>, PullDown>;
 type MyRf4463 = Rf4463<SdnPin, CsPin, Timer>;
 
 pub const MAX_PACKET_LEN: usize = 8191;
-pub const CATS_FREQUENCY: u32 = 430_500_000;
+const CATS_FREQUENCY: u32 = 430_500_000;
+const HARDWARE_ID: u16 = 0x7c87;
+const SOFTWARE_ID: u8 = 0;
+const MAX_HOPS: u8 = 3;
 
 pub struct RadioController {
     radio: Option<MyRf4463>,
+    buf: &'static mut [u8; MAX_PACKET_LEN],
 }
 
 impl RadioController {
-    pub fn new(spi: &mut MySpi, sdn: SdnPin, cs: CsPin, delay: Timer) -> Self {
+    pub fn new(
+        spi: &mut MySpi,
+        sdn: SdnPin,
+        cs: CsPin,
+        delay: Timer,
+        buf: &'static mut [u8; MAX_PACKET_LEN],
+    ) -> Self {
         let mut config = config::RADIO_CONFIG_CATS;
 
-        let mut radio = Rf4463::new(spi, sdn, cs, delay, &mut config).ok();
+        let radio = Rf4463::new(spi, sdn, cs, delay, &mut config).ok();
 
-        if let Some(ref mut radio) = radio {
-            radio.set_frequency(spi, CATS_FREQUENCY).unwrap(); // TODO fix unwrap
-            radio.start_tx(spi, &[0x55; 4096]).unwrap();
-        }
+        let mut s = Self { radio, buf };
 
-        Self { radio }
+        Self::radio_handler(&mut s.radio, |rad| rad.set_frequency(spi, CATS_FREQUENCY));
+
+        s
     }
 
     pub fn tick(&mut self, spi: &mut MySpi) {
-        let Some(ref mut radio) = self.radio else {
-            return;
-        };
+        Self::radio_handler(&mut self.radio, |rad| {
+            rad.interrupt(spi, None, Some(self.buf))
+        });
+    }
 
-        if let Err(e) = radio.interrupt(spi, None, Some(&[0xE3; 4096])) {
-            rdbg!(e);
-            self.radio = None;
-        }
+    // TODO hardware/software IDs
+    pub fn send_message(&mut self, message: &Message, spi: &mut MySpi) {
+        // Construct CATS packet
+        let mut buf = [0; MAX_PACKET_LEN];
+        let mut pkt = Packet::new(&mut buf);
+        pkt.add_identification(Identification {
+            callsign: "VE9QLE".try_into().unwrap(),
+            ssid: 29,
+            icon: 0,
+        })
+        .unwrap(); // TODO
+
+        // TODO could do battery voltage here
+        pkt.add_node_info(
+            NodeInfoBuilder::default()
+                .hardware_id(HARDWARE_ID)
+                .software_id(SOFTWARE_ID)
+                .build(),
+        )
+        .unwrap(); // TODO
+
+        pkt.add_destination(
+            Destination::new(false, 0, message.callsign(), message.ssid()).unwrap(),
+        )
+        .unwrap(); // TODO
+
+        pkt.add_route(Route::new(MAX_HOPS)).unwrap(); // TODO
+
+        pkt.add_comment(message.content()).unwrap(); // TODO
+
+        let mut out = Buffer::new_empty(self.buf);
+        pkt.fully_encode(&mut out).unwrap(); // TODO
+
+        Self::radio_handler(&mut self.radio, |rad| rad.start_tx(spi, &out));
     }
 
     pub fn errored(&self) -> bool {
@@ -57,4 +101,15 @@ impl RadioController {
             .map(|r| r.is_busy_txing())
             .unwrap_or(false)
     }
+
+    fn radio_handler<T, E, F: FnMut(&mut MyRf4463) -> Result<T, E>>(
+        radio: &mut Option<MyRf4463>,
+        mut f: F,
+    ) {
+        if let Some(ref mut rad) = radio {
+            if f(rad).is_err() {
+                *radio = None;
+            }
+        }
+    }
 }
diff --git a/companion-software/src/storage/message.rs b/companion-software/src/storage/message.rs
index 022176d..6480787 100644
--- a/companion-software/src/storage/message.rs
+++ b/companion-software/src/storage/message.rs
@@ -137,6 +137,14 @@ impl Message {
         }
     }
 
+    pub fn callsign(&self) -> &str {
+        &self.callsign
+    }
+
+    pub fn ssid(&self) -> u8 {
+        self.ssid
+    }
+
     pub fn content(&self) -> &str {
         &self.message
     }
-- 
GitLab