The Rust implementation of COP-1 can be found
here.
COP-1 is split into FOP-1, which is running on ground and FARM-1, which is running on the satellite. In other open source implementations usually only the “needed” part is implemented. So for groundstations FOP-1 and for satellites FARM-1. In an effort to use COP-1 biderectional and during inter-satellite links both parts are implemented here.
FOP-1 and FARM-1 are implemented “as defined” in the CCSDS. It was a focus during implementation to keep the structure of the implementation as closely connected to the standard as possible to make the implementaion better understandable. This led to some design choices, which I currently regard as unfortunate.
Usage #
FOP-1 #
As stated above, FOP-1 is the groundstation or transmitting part of COP-1. Additionally, it is the “complex” part of the algorithm. FOP-1 owns multiple buffers, handles retransmission and manages the link. Each virtual channel shall have its own FOP-1 object running.
Mission specific definitions must be set during object specification. Depending on the Frame to be used, the according predefined types can be used: UslpFop1 and TcFop1, instead of the Fop1 object, since the frame generic is predefined here already.
pub type UslpFop1<Connector: Fop1Connector, const MAX_FRAME_SIZE: usize, const TFDZ_SIZE: usize = 0, const INSERT_ZONE_SIZE: usize = 0> = Fop1<Connector, UslpFrameConstructor<TFDZ_SIZE, INSERT_ZONE_SIZE>, MAX_FRAME_SIZE, TFDZ_SIZE, INSERT_ZONE_SIZE>;
pub type TcFop1<Connector: Fop1Connector, const MAX_FRAME_SIZE: usize> = Fop1<Connector, TCFrameConstructor, MAX_FRAME_SIZE>;
pub struct Fop1<Connector: Fop1Connector, TCFrameConstructor: CCSDSFrameTCConstructor, const MAX_FRAME_SIZE: usize, const TFDZ_SIZE: usize = 0, const INSERT_ZONE_SIZE: usize = 0>
- Uslp Frames: If the USLP Frame shall be used for a mission the UslpFop1 may be used. The UslpFop1 requires a couple of generic constants: MAX_FRAME_SIZE, TFDZ_SIZE and INSERT_ZONE_SIZE (even if the frame size is variable the maximimum buffer space must be provided). Due to the flexibility of the USLP frame the MAX_FRAME_SIZE can not be calculated from the TFDZ and INSERT_ZONE sizes or vice versa.
- TC Frames: Accordingly to the UslpFop1 type, for the TcFop1 Object only needs the MAX_FRAME_SIZE to be provided.
- Custom Frames: If another frame shall be used with this FOP-1 implementation, the following traits have to be implemented for said frame it: CCSDSFrame and CCSDSFrameTC. Additionally, a constructor object without a lifetime (see Open Tasks below) must be implemented which implements the CCSDSFrameTCConstructor trait.
the Fop1 implementation requires an user implemented Fop1Connector. This object is called at the lower levels of the FOP-1 implementation when the frame shall be send or an error occured. The object shall implement the Fop1Connector trait.
FOP1connector trait #
The FOP1Connector trait looks like this:
/// Trait to be used to implement FOP-1 actions, which are used by the FOP-1 algorithm to interact with the system.
pub trait Fop1Connector {
fn transmit_request_for_frame<'raw, PackageType: CCSDSFrame<'raw>>(&mut self, frame: &PackageType, req_id: u64) -> Result<TransmitResult, FOP1Error>;
fn notify(&self, notification: FOP1Notifications, req_id: u64) -> Result<(), FOP1Error>;
fn alert(&self, alert: FOP1Alerts)-> Result<(), FOP1Error>;
fn abort(&self, gvcid: u8) -> Result<(), FOP1Error>;
}
The four functions come from the CCSDS. An example for the implementation of the transmit_request_for_frame function could be the usage of a serial interface and the transmission of the raw data to that interface. In
RACCOON OS implementation this function uses the zenoh middleware to forward the data to an according key. The data is picked up by the fec app and further handled there.
The notify, alert and abort functions shall provide feedback to higher layers of the code. This could be simply print statements (or using the log crate) or also messages to zenoh keys, depending on the FDIR mechanism. In RACCOON OS the FDIR mechanism is not yet fully defined.
If a transmission perforemd one state of the following enum shall be returned. If the transmission was performed in the FOP1Connector, e.g. via a serial interface, Failure or Success shall be returned, depending on the result. This will lead to the call of the receive_response_from_lower_layer function, described below. If for example a middleware is used between the sending algorithm and the FOP1 thread the receive_response_from_lower_layer function must be called manually depending on the result.
#[derive(PartialEq, Clone)]
pub enum TransmitResult{
Failure,
Unknown,
Success
}
Functions #
After initialization the FOP-1 algorithms has ?? functions to interact:
directive handling #
pub fn handle_fop1_directive(&mut self, control_input: FOP1Directives) -> Result<(), Error>
With this function the FOP-1 Algorithm can be directly controlled using the following directives:
pub enum FOP1Directives{
InitAdWithoutCLCWCheck(u64),
InitAdWithCLCLCheck(u64),
InitAdWithUnlock(u64),
InitAdWithSetVr((u64, u8)),
TerminateAdService(u64),
ResumeAdService(u64),
SetVs((u64, u8)),
SetFopSlidingWindow((u64, u8)), //k
SetT1Initial((u64, u64)),
SetTransmissionLimit((u64, u8)),
SetTimeoutType((u64, u8)), //tt
}
The four Init functions as well as the Terminate function will send a control frame via the transmit path provided in the FOP1connector to the FARM-1 part. The other functions only adapt setting on the FOP-1 end. The functions called depending on the given directive are also public and can be called directly.
Processing a CLCW field #
pub fn process_clcw_from_valid_cop1(&mut self, clcw: CLCW) -> Result<bool, Error>
This function must be called when a clcw field is received. The informations inside the CLCW field will provide feedback to the FOP-1 algorithm about the state of the FARM-1 algorithm, which frames have been received and which frames must be transmitted next.
check timer #
pub fn check_timer(&mut self)-> Result<bool, Error>
This crate does not do threading. Threading or tasking must be done externally and so the timer must be repeatedly checked. To do this, the check_timer function must be called. Depending on the timeout provided in FOP-1 initialization the algorithm will perform resending of a frame or will abbort the transmition process.
transfer fdu #
pub fn receive_request_to_transfer_fdu<'raw, PackageType: CCSDSFrameTC<'raw>>(&mut self, req_id: u64, frame: &PackageType)-> Result<(), Error>
To send a frame the receive_request_to_transfer_fdu function shall be called. There is not much else to say here.
lower layer interaction #
pub fn receive_response_from_lower_layer(&mut self, req_id: u64, response: u8) -> Result<(), Error>
Depending on the implementation this function can is called directly. If, for example, a middleware is used between the transmission algorithm and the the FOP1 Connector this functrion must be called manually.
Open Tasks #
- This code was developed after the implementation of the TC and USLP frame, which are both using rust lifetimes to allow efficiant usage of micro controllers. FOP-1 and FARM-1 both are implemented as frame agnostic as possible. Currently, in rust lifetimes, traits and generics do not behave well together ( further infos), which led to the implementation as it is. If rust improves upon the behaviour I would like to improve this here as well.
- The code currently uses a vector as buffer for frames. I would like to switch this in the future to a basic array, as this leed to better embedded support.
FARM-1 #
Farm-1 runs on the receiving end of the link and