first spring of camera impl done, testing up next

This commit is contained in:
lkoester 2024-04-16 15:44:40 +02:00
parent efe686becf
commit 649e903c0a
3 changed files with 305 additions and 12 deletions

13
Cargo.lock generated
View File

@ -542,6 +542,8 @@ dependencies = [
"num_enum", "num_enum",
"satrs", "satrs",
"satrs-mib", "satrs-mib",
"serde",
"serde_json",
"serialport", "serialport",
"strum", "strum",
"thiserror", "thiserror",
@ -742,6 +744,17 @@ dependencies = [
"syn 2.0.58", "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]] [[package]]
name = "serialport" name = "serialport"
version = "4.3.0" version = "4.3.0"

View File

@ -15,6 +15,8 @@ thiserror = "1"
derive-new = "0.6" derive-new = "0.6"
num_enum = "0.7" num_enum = "0.7"
serialport = "4" serialport = "4"
serde = "1"
serde_json = "1"
[dependencies.satrs] [dependencies.satrs]
version = "0.2.0-rc.0" version = "0.2.0-rc.0"

View File

@ -1,18 +1,303 @@
/// Device handler implementation for the IMS-100 Imager used on the OPS-SAT mission. /// 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 /// see also https://opssat1.esoc.esa.int/dmsf/files/6/view
use crate::interface::serial::SerialInterface;
use crate::requests::CompositeRequest; use crate::requests::CompositeRequest;
use derive_new::new; use derive_new::new;
use log::debug;
use ops_sat_rs::TimeStampHelper; use ops_sat_rs::TimeStampHelper;
use satrs::action::ActionRequest; use satrs::action::{ActionRequest, ActionRequestVariant};
use satrs::hk::HkRequest; use satrs::hk::HkRequest;
use satrs::pus::EcssTmSenderCore; use satrs::pus::EcssTmSenderCore;
use satrs::request::{GenericMessage, MessageMetadata, UniqueApidTargetId}; 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;
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<u32> for CameraActionIds {
type Error = ();
fn try_from(value: u32) -> Result<Self, Self::Error> {
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<TmSender: EcssTmSenderCore> {
id: UniqueApidTargetId,
// mode_interface: MpscModeLeafInterface,
composite_request_receiver: mpsc::Receiver<GenericMessage<CompositeRequest>>,
// hk_reply_sender: mpsc::Sender<GenericMessage<HkReply>>,
tm_sender: TmSender,
stamp_helper: TimeStampHelper,
}
#[allow(non_snake_case)]
#[allow(dead_code)]
impl<TmSender: EcssTmSenderCore>
IMS100BatchHandler<TmSender>
{
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<CameraPictureParameters>=
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(&param.R.to_string())
.arg("-G")
.arg(&param.G.to_string())
.arg("-B")
.arg(&param.B.to_string())
.arg("-c")
.arg("/dev/cam_tty")
.arg("-m")
.arg("/dev/cam_sd")
.arg("-v")
.arg("0")
.arg("-n")
.arg(&param.N.to_string());
if param.P {
cmd.arg("-p");
}
cmd.arg("-e")
.arg(&param.E.to_string())
.arg("-w")
.arg(&param.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_ATTRIBUTE: &str = "opssat.camera.port";
const SERIAL_PORT_DEFAULT: &str = "/dev/ttyACM0"; const SERIAL_PORT_DEFAULT: &str = "/dev/ttyACM0";
const BLOCK_DEVICE_ATTRIBUTE: &str = "opssat.camera.blockdev"; const BLOCK_DEVICE_ATTRIBUTE: &str = "opssat.camera.blockdev";
@ -104,11 +389,4 @@ impl<ComInterface: SerialInterface, TmSender: EcssTmSenderCore>
pub fn take_picture(&mut self) {} pub fn take_picture(&mut self) {}
} }
*/
#[cfg(test)]
mod tests {
#[test]
fn test_crc() {
// TODO
}
}