Skip to content
Snippets Groups Projects
Commit 29687ee1 authored by Stephen D's avatar Stephen D
Browse files

telemetry packets (text)

parent cd289fbe
No related branches found
No related tags found
No related merge requests found
......@@ -6,6 +6,8 @@ use base64::{engine::general_purpose, Engine};
use kiss_tnc::Tnc;
use sha3::{Digest, Sha3_256};
use crate::packet::Packet;
#[derive(Debug)]
enum Command {
ReqImageId(u8),
......@@ -40,6 +42,8 @@ pub struct CommandHandler {
burst_command: String,
cutdown_command: String,
img_request: Sender<u8>,
telem_tx: Sender<Packet>,
}
impl CommandHandler {
......@@ -49,6 +53,7 @@ impl CommandHandler {
burst_command: String,
cutdown_command: String,
img_request: Sender<u8>,
telem_tx: Sender<Packet>,
) -> Self {
Self {
callsign,
......@@ -58,6 +63,8 @@ impl CommandHandler {
burst_command,
cutdown_command,
img_request,
telem_tx,
}
}
......@@ -127,8 +134,18 @@ impl CommandHandler {
let cmd = Command::decode(cmd.as_bytes()).context("Invalid command")?;
match cmd {
Command::ReqImageId(id) => self.img_request.send(id)?,
Command::ReqImageId(id) => {
let _ = self
.telem_tx
.send(Packet::new_text_message("HQ image request received"));
self.img_request.send(id)?
}
Command::BurstBalloon => {
let _ = self
.telem_tx
.send(Packet::new_text_message("Burst balloon command received"));
let (_, stderr) = subprocess::Exec::shell(&self.burst_command)
.communicate()?
.read()?;
......@@ -140,6 +157,10 @@ impl CommandHandler {
}
}
Command::CutPayload => {
let _ = self
.telem_tx
.send(Packet::new_text_message("Payload cutdown command received"));
let (_, stderr) = subprocess::Exec::shell(&self.cutdown_command)
.communicate()?
.read()?;
......
use std::{
io::{Read, Write},
sync::mpsc::{self, Receiver, Sender, SyncSender},
thread,
time::Duration,
};
use anyhow::Context;
use serial::{BaudRate::BaudOther, SerialPort};
use crate::{
aprs::CommandHandler, config::Config, img::ImgManager, packet::FecPacket, ssdv::ssdv_encode,
aprs::CommandHandler,
config::Config,
img::ImgManager,
packet::{FecPacket, Packet},
radio::UartRadio,
ssdv::ssdv_encode,
};
const IMAGE_PACKET_QUEUE_LENGTH: usize = 1024;
......@@ -25,12 +28,13 @@ impl Controller {
pub fn run_forever(self) {
let (img_tx, img_rx) = mpsc::sync_channel(IMAGE_PACKET_QUEUE_LENGTH);
let (telem_tx, telem_rx) = mpsc::channel();
let (cmd_tx, cmd_rx) = mpsc::channel();
thread::spawn(|| Self::tx_thread(img_rx));
thread::spawn(|| Self::tx_thread(img_rx, telem_rx));
{
let config = self.config.clone();
thread::spawn(|| Self::aprs_thread(config, cmd_tx));
thread::spawn(|| Self::aprs_thread(config, cmd_tx, telem_tx));
}
let mut manager = ImgManager::new(self.config.paths.clone(), cmd_rx);
......@@ -59,7 +63,7 @@ impl Controller {
// manages incoming APRS packets
// used to request HD images, as well as
// to initiate burst/cutdown
fn aprs_thread(config: Config, tx: Sender<u8>) {
fn aprs_thread(config: Config, tx: Sender<u8>, telem_tx: Sender<Packet>) {
if let Some(ctrl) = config.control {
let mut handler = CommandHandler::new(
config.callsign,
......@@ -67,6 +71,7 @@ impl Controller {
ctrl.burst_command,
ctrl.cutdown_command,
tx,
telem_tx,
);
handler.process_forever();
......@@ -77,23 +82,21 @@ impl Controller {
// TODO currently very hacky, because we're going to rip
// most of this out when we stop using a lobotomized NPR-70
// and move to just accessing the RF4463 directly over SPI
fn tx_thread(rx: Receiver<FecPacket>) {
let mut uart = serial::open("/dev/ttyACM0").unwrap();
uart.reconfigure(&|settings| settings.set_baud_rate(BaudOther(921600)))
.unwrap();
uart.set_timeout(Duration::from_millis(5)).unwrap();
while let Ok(packet) = rx.recv() {
uart.write_all(&packet.0).unwrap();
let mut buf = [0; 1];
if let Ok(1) = uart.read(&mut buf) {
if buf[0] == b'F' {
// uC is full. Need to wait until we receive
// the empty signal
while buf[0] != b'E' {
let _ = uart.read(&mut buf);
}
}
fn tx_thread(image_rx: Receiver<FecPacket>, telem_rx: Receiver<Packet>) {
let mut radio = UartRadio::new("/dev/ttyACM0");
let mut text_msg_id = 0;
loop {
while let Ok(tm) = telem_rx.try_recv() {
let tm = tm.into_raw(&mut text_msg_id).into();
radio.send_packet(&tm);
}
if let Ok(img) = image_rx.try_recv() {
radio.send_packet(&img);
} else {
thread::sleep(Duration::from_millis(5));
}
}
}
......
......@@ -8,6 +8,7 @@ mod hrow;
mod img;
mod ldpc;
mod packet;
mod radio;
mod ssdv;
fn main() -> anyhow::Result<()> {
......
......@@ -2,6 +2,57 @@ use crc::{Crc, CRC_16_IBM_3740};
use crate::ldpc::ldpc_encode;
const TEXT_MESSAGE_LEN: usize = 252;
pub enum Packet {
TextMessage(u8, [u8; TEXT_MESSAGE_LEN]),
}
impl Packet {
// Cuts off messages that are too long
pub fn new_text_message(msg: &str) -> Self {
let mut out = [0x55; TEXT_MESSAGE_LEN];
// this technically would allow part of a character to be sent
// (unicode multi-byte character that gets cut off at the end)
// ideally, we would iterate over characters and add them, so that
// we could abort before adding a partial character
for (i, b) in msg.as_bytes().iter().enumerate().take(TEXT_MESSAGE_LEN) {
out[i] = *b;
}
let len = msg
.as_bytes()
.len()
.min(TEXT_MESSAGE_LEN)
.try_into()
.unwrap();
Self::TextMessage(len, out)
}
}
impl Packet {
// increments text_id if we're sending a text message packet
pub fn into_raw(self, text_id: &mut u16) -> RawPacket {
match self {
Packet::TextMessage(len, txt) => {
let id = *text_id;
*text_id = text_id.wrapping_add(1);
let mut out = [0; 256];
out[0] = 0x00; // packet type
out[1] = len;
out[2..4].clone_from_slice(&id.to_be_bytes());
out[4..].clone_from_slice(&txt);
RawPacket(out)
}
}
}
}
pub struct RawPacket(pub [u8; 256]);
pub struct FecPacket(pub [u8; 256 + 2 + 65]);
......
use serial::{unix::TTYPort, BaudRate::BaudOther, SerialPort};
use std::{
io::{Read, Write},
time::Duration,
};
use crate::packet::FecPacket;
// Used for talking to an RF4463 via a microcontroller
// The microcontroller takes in packet data over UART and sends it to the RF4463
// Note that this is a little hacky. It's only intended to be used
// for debugging, and not for an actual balloon flight
pub struct UartRadio {
uart: TTYPort,
}
impl UartRadio {
pub fn new(uart: &str) -> Self {
let mut uart = serial::open(uart).unwrap();
uart.reconfigure(&|settings| settings.set_baud_rate(BaudOther(921600)))
.unwrap();
uart.set_timeout(Duration::from_millis(5)).unwrap();
Self { uart }
}
pub fn send_packet(&mut self, packet: &FecPacket) {
self.uart.write_all(&packet.0).unwrap();
let mut buf = [0; 1];
if let Ok(1) = self.uart.read(&mut buf) {
if buf[0] == b'F' {
// uC is full. Need to wait until we receive
// the empty signal
while buf[0] != b'E' {
let _ = self.uart.read(&mut buf);
}
}
}
}
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment