Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ROS#209: Demo application #86

Merged
merged 28 commits into from
Dec 5, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
bc34fb8
Scripts: typo fix
SteelPh0enix Nov 28, 2023
f31512e
Demo: Implemented CCSDS packet structure
SteelPh0enix Nov 28, 2023
ee208f6
Demo: Added CCSDS primary header parsing
SteelPh0enix Nov 28, 2023
1f1a9db
Scripts: Added more logs to demo_send_frames.py
SteelPh0enix Nov 28, 2023
6e06d70
Demo: Cleanup + replaced naive packet handling with CCSDS parsing
SteelPh0enix Nov 28, 2023
7fdcb4c
Demo: Added delays to test script and changed baudrate
SteelPh0enix Nov 28, 2023
62b04bb
Demo: More cleanups + better error handling, reused lsm6dso types
SteelPh0enix Nov 28, 2023
fd80b1b
Demo: Added SPI configuration
SteelPh0enix Nov 28, 2023
3067c71
Demo: Added LSM6DSO support
SteelPh0enix Nov 28, 2023
e87c01d
Scripts: Changed demo payload to valid one
SteelPh0enix Nov 29, 2023
535bd4d
Demo: Implemented LSM6DSO configuration via tasklets
SteelPh0enix Nov 29, 2023
c6e8a74
Demo: Command -> Telecommand
SteelPh0enix Nov 29, 2023
cee002e
Demo: Implemented telemetry structure and added telemetry task
SteelPh0enix Nov 29, 2023
a2afa63
Demo: Implemented telemetry responses
SteelPh0enix Nov 29, 2023
341c174
Demo: Implemented IMU data transmission
SteelPh0enix Nov 29, 2023
c40c6bc
Python: Added matplotlib (and pyside6 for backend) to dependencies
SteelPh0enix Nov 30, 2023
f8d20f1
Demo: Test mode reset on app start
SteelPh0enix Nov 30, 2023
7fd5ca2
Demo: Implemented real-time data plotting
SteelPh0enix Nov 30, 2023
590d725
Demo: Added data averaging
SteelPh0enix Nov 30, 2023
50028d9
Demo: Fixed sample reading and added proper handling of empty measure…
SteelPh0enix Dec 1, 2023
97636f0
Demo: Removed unnecessary logs
SteelPh0enix Dec 1, 2023
43039f5
Demo: Stops measurements on exit
SteelPh0enix Dec 3, 2023
ab951e5
Demo: Added buttons to switch scales, UI cleanup
SteelPh0enix Dec 3, 2023
e4dcb32
Aerugo: Exported execution stats and tasklet ID
SteelPh0enix Dec 4, 2023
25d8a14
Demo: Imlemented execution monitoring telemetry
SteelPh0enix Dec 4, 2023
2dce024
Demo: Fixed Y axis labels
SteelPh0enix Dec 4, 2023
c9cf50e
Demo: Implemented execution monitoring telemetry in GUI app
SteelPh0enix Dec 4, 2023
3252c54
Demo: PR fixes
SteelPh0enix Dec 5, 2023
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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