From 649e903c0aa52f7e9e90cbc4853479561888467b Mon Sep 17 00:00:00 2001 From: lkoester Date: Tue, 16 Apr 2024 15:44:40 +0200 Subject: [PATCH] first spring of camera impl done, testing up next --- Cargo.lock | 13 ++ Cargo.toml | 2 + src/handlers/camera.rs | 302 +++++++++++++++++++++++++++++++++++++++-- 3 files changed, 305 insertions(+), 12 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 29fa20c..dace2a2 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -542,6 +542,8 @@ dependencies = [ "num_enum", "satrs", "satrs-mib", + "serde", + "serde_json", "serialport", "strum", "thiserror", @@ -742,6 +744,17 @@ dependencies = [ "syn 2.0.58", ] +[[package]] +name = "serde_json" +version = "1.0.116" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3e17db7126d17feb94eb3fad46bf1a96b034e8aacbc2e775fe81505f8b0b2813" +dependencies = [ + "itoa", + "ryu", + "serde", +] + [[package]] name = "serialport" version = "4.3.0" diff --git a/Cargo.toml b/Cargo.toml index 927a056..b79613c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -15,6 +15,8 @@ thiserror = "1" derive-new = "0.6" num_enum = "0.7" serialport = "4" +serde = "1" +serde_json = "1" [dependencies.satrs] version = "0.2.0-rc.0" diff --git a/src/handlers/camera.rs b/src/handlers/camera.rs index f07c090..6e1b63d 100644 --- a/src/handlers/camera.rs +++ b/src/handlers/camera.rs @@ -1,18 +1,303 @@ /// Device handler implementation for the IMS-100 Imager used on the OPS-SAT mission. /// +/// from the [OPSSAT Experimenter Wiki](https://opssat1.esoc.esa.int/projects/experimenter-information/wiki/Camera_Introduction): +/// OPS-SAT has a BST IMS-100 Imager onboard for image acquisition. These RGGB images are 2048x1944px in size. +/// +/// There are two ways of taking pictures, with the NMF or by using the camera API directly. +/// +/// As the NMF method is already explained in the NMF documentation we will focus on triggering the camera API. +/// +/// The camera is located on the -Z face of OPS-SAT +/// +/// Mapping between camera and satellite frames: +/// cam body +/// +x -z +/// +y -x +/// +z +y +/// +/// If you look onto Flatsat as in your picture coordinate system for camera it is +/// +/// Z Z pointing inside Flatsat +/// x---> X +/// | +/// | +/// v Y +/// /// see also https://opssat1.esoc.esa.int/dmsf/files/6/view -use crate::interface::serial::SerialInterface; use crate::requests::CompositeRequest; use derive_new::new; +use log::debug; use ops_sat_rs::TimeStampHelper; -use satrs::action::ActionRequest; +use satrs::action::{ActionRequest, ActionRequestVariant}; use satrs::hk::HkRequest; use satrs::pus::EcssTmSenderCore; use satrs::request::{GenericMessage, MessageMetadata, UniqueApidTargetId}; +use serde::{Deserialize, Serialize}; +use std::io::Error; +use std::process::Command; use std::sync::mpsc; -use std::sync::mpsc::TryRecvError; -use chrono::Duration; +#[derive(Debug)] +pub enum CameraActionIds { + DefaultSingle = 1, + BalancedSingle = 2, + DefaultSingleFlatSat = 3, + BalancedSingleFlatSat = 4, + CustomParameters = 5, +} + +impl TryFrom for CameraActionIds { + type Error = (); + + fn try_from(value: u32) -> Result { + match value { + value if value == CameraActionIds::DefaultSingle as u32 => { + Ok(CameraActionIds::DefaultSingle) + } + value if value == CameraActionIds::BalancedSingle as u32 => { + Ok(CameraActionIds::BalancedSingle) + } + value if value == CameraActionIds::DefaultSingleFlatSat as u32 => { + Ok(CameraActionIds::DefaultSingleFlatSat) + } + value if value == CameraActionIds::BalancedSingleFlatSat as u32 => { + Ok(CameraActionIds::BalancedSingleFlatSat) + } + value if value == CameraActionIds::CustomParameters as u32 => { + Ok(CameraActionIds::CustomParameters) + } + _ => Err(()), + } + } +} + +// TODO what happens if limits are exceded +#[allow(non_snake_case)] +#[derive(Serialize, Deserialize, new)] +pub struct CameraPictureParameters { + pub R: u8, + pub G: u8, + pub B: u8, + pub N: u8, // number of images, max: 26 + pub P: bool, // .png flag, true converts raw extracted image from camera to a png + pub E: u32, // exposure time in ms, max: 1580, default: 2, FlatSat: 200 + pub W: u32, // wait time between pictures in ms, max: 40000 +} + +#[allow(dead_code)] +impl CameraPictureParameters { + pub fn default_single() -> Self { + Self { + R: 8, + G: 8, + B: 8, + N: 1, + P: true, + E: 2, + W: 1000, + } + } + + pub fn balanced_single() -> Self { + Self { + R: 13, + G: 7, + B: 8, + N: 1, + P: true, + E: 2, + W: 1000, + } + } + + pub fn default_single_flatsat() -> Self { + Self { + R: 8, + G: 8, + B: 8, + N: 1, + P: true, + E: 200, + W: 1000, + } + } + + pub fn balanced_single_flatsat() -> Self { + Self { + R: 13, + G: 7, + B: 8, + N: 1, + P: true, + E: 200, + W: 1000, + } + } +} + +#[allow(dead_code)] +#[derive(new)] +pub struct IMS100BatchHandler { + id: UniqueApidTargetId, + // mode_interface: MpscModeLeafInterface, + composite_request_receiver: mpsc::Receiver>, + // hk_reply_sender: mpsc::Sender>, + tm_sender: TmSender, + stamp_helper: TimeStampHelper, +} + +#[allow(non_snake_case)] +#[allow(dead_code)] +impl + IMS100BatchHandler +{ + pub fn periodic_operation(&mut self) { + self.stamp_helper.update_from_now(); + // Handle requests. + self.handle_composite_requests(); + // self.handle_mode_requests(); + } + + pub fn handle_composite_requests(&mut self) { + loop { + match self.composite_request_receiver.try_recv() { + Ok(ref msg) => match &msg.message { + CompositeRequest::Hk(hk_request) => { + self.handle_hk_request(&msg.requestor_info, hk_request); + } + CompositeRequest::Action(action_request) => { + self.handle_action_request(&msg.requestor_info, action_request); + } + }, + Err(_) => {} + } + } + } + + pub fn handle_hk_request(&mut self, _requestor_info: &MessageMetadata, _hk_request: &HkRequest) { + // TODO add hk to opssat + } + + pub fn handle_action_request( + &mut self, + _requestor_info: &MessageMetadata, + action_request: &ActionRequest, + ) -> std::io::Result<()> { + let param = match CameraActionIds::try_from(action_request.action_id).unwrap() { + CameraActionIds::DefaultSingle => CameraPictureParameters::default_single(), + CameraActionIds::BalancedSingle => CameraPictureParameters::balanced_single(), + CameraActionIds::DefaultSingleFlatSat => { + CameraPictureParameters::default_single_flatsat() + } + CameraActionIds::BalancedSingleFlatSat => { + CameraPictureParameters::balanced_single_flatsat() + } + CameraActionIds::CustomParameters => match &action_request.variant { + ActionRequestVariant::NoData => return Err(Error::other("No Data sent!")), + ActionRequestVariant::StoreData(_) => { + // let param = serde_json::from_slice() + // TODO implement non dynamic version + return Err(Error::other( + "Static parameter transfer not implemented yet!", + )); + } + ActionRequestVariant::VecData(data) => { + let param: serde_json::Result= + serde_json::from_slice(data.as_slice()); + match param { + Ok(param) => { + param + } + Err(_) => { + return Err(Error::other("Unable to deserialize parameters")); + } + } + } + _ => return Err(Error::other("Invalid Action Request Variant!")), + }, + }; + self.take_picture(param) + } + + pub fn take_picture(&mut self, param: CameraPictureParameters) -> std::io::Result<()> { + let mut cmd = Command::new("ims100_testapp"); + cmd.arg("-R") + .arg(¶m.R.to_string()) + .arg("-G") + .arg(¶m.G.to_string()) + .arg("-B") + .arg(¶m.B.to_string()) + .arg("-c") + .arg("/dev/cam_tty") + .arg("-m") + .arg("/dev/cam_sd") + .arg("-v") + .arg("0") + .arg("-n") + .arg(¶m.N.to_string()); + if param.P { + cmd.arg("-p"); + } + cmd.arg("-e") + .arg(¶m.E.to_string()) + .arg("-w") + .arg(¶m.W.to_string()); + + let output = cmd.output()?; + + debug!("{}", String::from_utf8_lossy(&output.stdout)); + + Ok(()) + } + + pub fn take_picture_from_str( + &mut self, + R: &str, + G: &str, + B: &str, + N: &str, + P: &str, + E: &str, + W: &str, + ) -> std::io::Result<()> { + let mut cmd = Command::new("ims100_testapp"); + cmd.arg("-R") + .arg(R) + .arg("-G") + .arg(G) + .arg("-B") + .arg(B) + .arg("-c") + .arg("/dev/cam_tty") + .arg("-m") + .arg("/dev/cam_sd") + .arg("-v") + .arg("0") + .arg("-n") + .arg(N) + .arg(P) + .arg("-e") + .arg(E) + .arg("-w") + .arg(W); + + let output = cmd.output()?; + + debug!("{}", String::from_utf8_lossy(&output.stdout)); + + Ok(()) + } +} + +#[cfg(test)] +mod tests { + #[test] + fn test_crc() { + // TODO + } +} + +/* const SERIAL_PORT_ATTRIBUTE: &str = "opssat.camera.port"; const SERIAL_PORT_DEFAULT: &str = "/dev/ttyACM0"; const BLOCK_DEVICE_ATTRIBUTE: &str = "opssat.camera.blockdev"; @@ -104,11 +389,4 @@ impl pub fn take_picture(&mut self) {} } - -#[cfg(test)] -mod tests { - #[test] - fn test_crc() { - // TODO - } -} + */