feat: add display UI with splash screen, dashboard, and RSSI sparkline
Implement full SSD1306 OLED display support: - Splash screen on boot (1.5s) with project name, version, board - Dashboard showing radio state, config, counters, RSSI/SNR - RSSI history sparkline (64-sample ring buffer, 2px-wide bars) - DisplayOn/DisplayOff host commands routed via dedicated channel - Pure render functions against generic DrawTarget (no hardware coupling) - Display on/off control bypasses radio task entirely Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
17624 files changed
+4564928
-38
No files matched your search
+130
@@ -0,0 +1,130 @@
|
||||
//! Types for dealing with rational numbers.
|
||||
use core::ops::{Add, Div, Mul};
|
||||
|
||||
use num_traits::{CheckedAdd, CheckedDiv, CheckedMul};
|
||||
|
||||
/// Represents the ratio between two numbers.
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||
pub struct Ratio<T> {
|
||||
/// Numerator.
|
||||
numer: T,
|
||||
/// Denominator.
|
||||
denom: T,
|
||||
}
|
||||
|
||||
impl<T> Ratio<T> {
|
||||
/// Creates a new `Ratio`.
|
||||
#[inline(always)]
|
||||
pub const fn new_raw(numer: T, denom: T) -> Ratio<T> {
|
||||
Ratio { numer, denom }
|
||||
}
|
||||
|
||||
/// Gets an immutable reference to the numerator.
|
||||
#[inline(always)]
|
||||
pub const fn numer(&self) -> &T {
|
||||
&self.numer
|
||||
}
|
||||
|
||||
/// Gets an immutable reference to the denominator.
|
||||
#[inline(always)]
|
||||
pub const fn denom(&self) -> &T {
|
||||
&self.denom
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: CheckedDiv> Ratio<T> {
|
||||
/// Converts to an integer, rounding towards zero.
|
||||
#[inline(always)]
|
||||
pub fn to_integer(&self) -> T {
|
||||
unwrap!(self.numer().checked_div(self.denom()))
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: CheckedMul> Div<T> for Ratio<T> {
|
||||
type Output = Self;
|
||||
|
||||
#[inline(always)]
|
||||
fn div(mut self, rhs: T) -> Self::Output {
|
||||
self.denom = unwrap!(self.denom().checked_mul(&rhs));
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: CheckedMul> Mul<T> for Ratio<T> {
|
||||
type Output = Self;
|
||||
|
||||
#[inline(always)]
|
||||
fn mul(mut self, rhs: T) -> Self::Output {
|
||||
self.numer = unwrap!(self.numer().checked_mul(&rhs));
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: CheckedMul + CheckedAdd> Add<T> for Ratio<T> {
|
||||
type Output = Self;
|
||||
|
||||
#[inline(always)]
|
||||
fn add(mut self, rhs: T) -> Self::Output {
|
||||
self.numer = unwrap!(unwrap!(self.denom().checked_mul(&rhs)).checked_add(self.numer()));
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! impl_from_for_float {
|
||||
($from:ident) => {
|
||||
impl From<Ratio<$from>> for f32 {
|
||||
#[inline(always)]
|
||||
fn from(r: Ratio<$from>) -> Self {
|
||||
(r.numer as f32) / (r.denom as f32)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Ratio<$from>> for f64 {
|
||||
#[inline(always)]
|
||||
fn from(r: Ratio<$from>) -> Self {
|
||||
(r.numer as f64) / (r.denom as f64)
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
impl_from_for_float!(u8);
|
||||
impl_from_for_float!(u16);
|
||||
impl_from_for_float!(u32);
|
||||
impl_from_for_float!(u64);
|
||||
impl_from_for_float!(u128);
|
||||
impl_from_for_float!(i8);
|
||||
impl_from_for_float!(i16);
|
||||
impl_from_for_float!(i32);
|
||||
impl_from_for_float!(i64);
|
||||
impl_from_for_float!(i128);
|
||||
|
||||
impl<T: core::fmt::Display> core::fmt::Display for Ratio<T> {
|
||||
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
|
||||
core::write!(f, "{} / {}", self.numer(), self.denom())
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::Ratio;
|
||||
|
||||
#[test]
|
||||
fn basics() {
|
||||
let mut r = Ratio::new_raw(1, 2) + 2;
|
||||
assert_eq!(*r.numer(), 5);
|
||||
assert_eq!(*r.denom(), 2);
|
||||
assert_eq!(r.to_integer(), 2);
|
||||
|
||||
r = r * 2;
|
||||
assert_eq!(*r.numer(), 10);
|
||||
assert_eq!(*r.denom(), 2);
|
||||
assert_eq!(r.to_integer(), 5);
|
||||
|
||||
r = r / 2;
|
||||
assert_eq!(*r.numer(), 10);
|
||||
assert_eq!(*r.denom(), 4);
|
||||
assert_eq!(r.to_integer(), 2);
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user