Skip to content

Commit

Permalink
Merge pull request #86 from SteelPh0enix/demo-app
Browse files Browse the repository at this point in the history
ROS#209: Demo application
  • Loading branch information
SteelPh0enix authored Dec 5, 2023
2 parents 1ec4eaf + 3252c54 commit 3d7c986
Show file tree
Hide file tree
Showing 21 changed files with 1,946 additions and 265 deletions.
4 changes: 3 additions & 1 deletion calldwell/pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,12 @@ license = "MIT"
readme = "README.md"

[tool.poetry.dependencies]
python = "^3.9"
python = ">=3.9,<=3.12"
pygdbmi = "^0.11.0.0"
paramiko = "^3.3.1"
option = "^2.1.0"
matplotlib = "^3.8.2"
pyside6 = "^6.6.0"

[tool.poetry.group.dev.dependencies]
black = "^23.10.1"
Expand Down
2 changes: 1 addition & 1 deletion demos/samv71-spi-accelerometer/.cargo/config.toml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
target = "thumbv7em-none-eabihf"

[env]
AERUGO_TASKLET_COUNT = { value = "7" }
AERUGO_TASKLET_COUNT = { value = "8" }
AERUGO_EVENT_COUNT = { value = "3" }

[target.thumbv7em-none-eabihf]
Expand Down
9 changes: 8 additions & 1 deletion demos/samv71-spi-accelerometer/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
name = "samv71-spi-accelerometer"
authors = [
"Filip Demski <glamhoth@protonmail.com>",
"Wojciech Olech <wojciech_olech@hotmail.com>"
"Wojciech Olech <wojciech_olech@hotmail.com>",
]
edition = "2021"
version = "1.0.0"
Expand All @@ -17,6 +17,13 @@ aerugo = { version = "0.1.0", path = "../..", features = [
cortex-m = { version = "0.7.7", features = ["critical-section-single-core"] }
cortex-m-rt = { version = "0.7.3", features = ["device"] }
panic-rtt-target = { version = "0.1.2", features = ["cortex-m"] }
bitfield-enum = { path = "../../utils/bitfield-enum" }
paste = "1.0.14"
derive_more = { version = "1.0.0-beta.6", features = [
"try_from",
"try_into",
], default-features = false }
lsm6dso = { path = "../../utils/lsm6dso" }

[features]
rt = ["aerugo/rt"]
Expand Down
87 changes: 87 additions & 0 deletions demos/samv71-spi-accelerometer/src/bounded_int.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
#![allow(dead_code)]
//! Module with implementation of Bounded integer types.
use paste::paste;

/// Macro creating a generic bounded value type, which allows for storing a value of specific type
/// in specified range.
///
/// You'd usually want to create a type alias that uses this structure.
#[macro_export]
macro_rules! generic_bounded_value {
($underlying_type:ty) => {
paste! {
#[doc = "Generic bounded value that uses `" $underlying_type "` as underlying type. "]
#[doc = "Provided range is inclusive."]
#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)]
pub struct [<Bounded$underlying_type:upper>]<const LOW: $underlying_type, const HIGH: $underlying_type>($underlying_type);

impl<const LOW: $underlying_type, const HIGH: $underlying_type> [<Bounded$underlying_type:upper>]<{ LOW }, { HIGH }> {
#[doc = "Lowest value that an instance of this struct can have."]
pub const LOW: $underlying_type = LOW;
#[doc = "Highest value that an instance of this struct can have."]
pub const HIGH: $underlying_type = HIGH;

#[doc = "Creates a new bounded value. Returns `None` if the value is out of range."]
pub const fn new(value: $underlying_type) -> Option<Self> {
if value >= Self::LOW && value <= Self::HIGH {
Some(Self(value))
} else {
None
}
}

#[doc = "Creates a new bounded value. If `value` is out of range, it will be saturated "]
#[doc = "to `LOW` or `HIGH`, depending whether it's too small or large."]
pub const fn new_saturated(value: $underlying_type) -> Self {
Self(match value {
value if value < Self::LOW => Self::LOW,
value if value > Self::HIGH => Self::HIGH,
value => value
})
}

#[doc = "Returns the stored value. A `const` alternative to `Deref` trait."]
pub const fn get(&self) -> $underlying_type {
self.0
}
}

impl<const LOW: $underlying_type, const HIGH: $underlying_type> core::ops::Deref
for [<Bounded$underlying_type:upper>]<{ LOW }, { HIGH }> {
type Target = $underlying_type;

fn deref(&self) -> &Self::Target {
&self.0
}
}

impl<const LOW: $underlying_type, const HIGH: $underlying_type> core::convert::TryFrom<$underlying_type>
for [<Bounded$underlying_type:upper>]<{ LOW }, { HIGH }> {
type Error = $underlying_type;

fn try_from(value: $underlying_type) -> Result<Self, Self::Error> {
Self::new(value).ok_or(value)
}
}

impl<const LOW: $underlying_type, const HIGH: $underlying_type> core::convert::From<[<Bounded$underlying_type:upper>]<{ LOW }, { HIGH }>>
for $underlying_type {
fn from(value: [<Bounded$underlying_type:upper>]<{ LOW }, { HIGH }>) -> Self {
value.get()
}
}
}
};
}

generic_bounded_value!(u8);
generic_bounded_value!(u16);
generic_bounded_value!(u32);
generic_bounded_value!(u64);
generic_bounded_value!(usize);

generic_bounded_value!(i8);
generic_bounded_value!(i16);
generic_bounded_value!(i32);
generic_bounded_value!(i64);
generic_bounded_value!(isize);
171 changes: 171 additions & 0 deletions demos/samv71-spi-accelerometer/src/ccsds.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,171 @@
//! Module with items related to CCSDS packets. Contains definition of CCSDS primary header, and
//! functions for conversion from/to byte buffer.
//!
//! This implementation assumes that bit 0 of the header is the MSB of the first received byte.
//!
//! Header's structure is implemented as defined in CCSDS 133.0-B-2 Recommended Standard, section 4,
//! which is available here: https://public.ccsds.org/Pubs/133x0b2e1.pdf

use bitfield_enum::{
BitFieldFromByte, BitFieldToByte, BitFieldTryFromByte, MultiByteBitField,
MultiByteBitFieldFromBytes, MultiByteBitFieldToBytes,
};

use crate::bitfield_enum::bitfield_enum;
use crate::bounded_int::BoundedU16;

/// CCSDS packet's primary header structure.
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct CCSDSPrimaryHeader {
/// Packet version number
pub version_number: PacketVersionNumber,
/// Packet identification
pub identity: Identity,
/// Packet sequence control
pub sequence_control: SequenceControl,
/// Packet data length
pub data_length: u16,
}

/// A segment of CCSDS packet's primary header that identifies the packet.
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct Identity {
/// Packet type
pub packet_type: PacketType,
/// Secondary header flag
pub secondary_header_presence: SecondaryHeaderPresence,
/// Application process identified
pub apid: ApplicationProcessIdentifier,
}

/// A segment of CCSDS packet's primary header that provides information about packet's ID
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct SequenceControl {
/// Sequence flags
pub flags: SequenceFlags,
/// Packet sequence count or packet name
pub name: PacketName,
}

bitfield_enum!(PacketVersionNumber [mask=0xE0, offset=5] {
CCSDSVersion1 = 0b000,
});

bitfield_enum!(PacketType [mask=0x10, offset=4] {
Telemetry = 0,
Telecommand = 1,
});

bitfield_enum!(SecondaryHeaderPresence [mask=0x08, offset=3] {
NotPresent = 0,
Present = 1,
});

pub type ApplicationProcessIdentifier = BoundedU16<0, 0x7FF>;
/// Constant representing the APID of an idle CCSDS packet.
#[allow(dead_code)]
pub const IDLE_PACKET_APID: ApplicationProcessIdentifier =
ApplicationProcessIdentifier::new_saturated(ApplicationProcessIdentifier::HIGH);

impl MultiByteBitField for ApplicationProcessIdentifier {
const MASKS: [u8; 2] = [0x07, 0xFF];
const OFFSETS: [usize; 2] = [0, 0];
}

impl MultiByteBitFieldToBytes for ApplicationProcessIdentifier {
fn to_bytes(self) -> [u8; 2] {
[(self.get() >> 8) as u8, (self.get() & 0xFF) as u8]
}
}

impl MultiByteBitFieldFromBytes for ApplicationProcessIdentifier {
fn from_bytes(bytes: &[u8]) -> Self {
let msb = (bytes[0] & Self::MASKS[0]) >> Self::OFFSETS[0];
let lsb = (bytes[1] & Self::MASKS[1]) >> Self::OFFSETS[1];
let value = u16::from_be_bytes([msb, lsb]);
// This should never fail, as every value of the field is valid.
ApplicationProcessIdentifier::new(value).unwrap()
}
}

bitfield_enum!(SequenceFlags [mask=0xC0, offset=6] {
UserDataContinuation = 0b00,
UserDataFirstSegment = 0b01,
UserDataLastSegment = 0b10,
UnsegmentedUserData = 0b11,
});

pub type PacketName = BoundedU16<0, 0x3FFF>;

impl MultiByteBitField for PacketName {
const MASKS: [u8; 2] = [0x3F, 0xFF];
const OFFSETS: [usize; 2] = [0, 0];
}

impl MultiByteBitFieldToBytes for PacketName {
fn to_bytes(self) -> [u8; 2] {
[(self.get() >> 8) as u8, (self.get() & 0xFF) as u8]
}
}

impl MultiByteBitFieldFromBytes for PacketName {
fn from_bytes(bytes: &[u8]) -> Self {
let msb = (bytes[0] & Self::MASKS[0]) >> Self::OFFSETS[0];
let lsb = (bytes[1] & Self::MASKS[1]) >> Self::OFFSETS[1];
let value = u16::from_be_bytes([msb, lsb]);
// This should never fail, as every value of the field is valid.
PacketName::new(value).unwrap()
}
}

pub const CCSDS_PRIMARY_HEADER_LENGTH: usize = 6;
pub type CCSDSPrimaryHeaderBuffer = [u8; CCSDS_PRIMARY_HEADER_LENGTH];

/// Every error contains the raw value of the field it failed to recognize.
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum PacketParsingError {
InvalidVersionNumber(u8),
}

impl TryFrom<&CCSDSPrimaryHeaderBuffer> for CCSDSPrimaryHeader {
type Error = PacketParsingError;

fn try_from(buffer: &CCSDSPrimaryHeaderBuffer) -> Result<Self, Self::Error> {
let version_number = PacketVersionNumber::try_from_byte(buffer[0])
.map_err(|err| PacketParsingError::InvalidVersionNumber(err.input))?;

Ok(Self {
version_number,
identity: Identity {
packet_type: PacketType::from_byte(buffer[0]),
secondary_header_presence: SecondaryHeaderPresence::from_byte(buffer[0]),
apid: ApplicationProcessIdentifier::from_bytes(&buffer[0..=1]),
},
sequence_control: SequenceControl {
flags: SequenceFlags::from_byte(buffer[2]),
name: PacketName::from_bytes(&buffer[2..=3]),
},
data_length: u16::from_be_bytes([buffer[4], buffer[5]]),
})
}
}

impl From<CCSDSPrimaryHeader> for CCSDSPrimaryHeaderBuffer {
fn from(header: CCSDSPrimaryHeader) -> Self {
let apid = header.identity.apid.to_bytes();
let name = header.sequence_control.name.to_bytes();
let data_length = header.data_length.to_be_bytes();

[
header.version_number.to_byte()
| header.identity.packet_type.to_byte()
| header.identity.secondary_header_presence.to_byte()
| apid[0],
apid[1],
header.sequence_control.flags.to_byte() | name[0],
name[1],
data_length[0],
data_length[1],
]
}
}
59 changes: 0 additions & 59 deletions demos/samv71-spi-accelerometer/src/command.rs

This file was deleted.

Loading

0 comments on commit 3d7c986

Please sign in to comment.