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>
247 lines
8.5 KiB
Rust
247 lines
8.5 KiB
Rust
// Copyright © 2018–2025 Trevor Spiteri
|
||
|
||
// This library is free software: you can redistribute it and/or
|
||
// modify it under the terms of either
|
||
//
|
||
// * the Apache License, Version 2.0 or
|
||
// * the MIT License
|
||
//
|
||
// at your option.
|
||
//
|
||
// You should have recieved copies of the Apache License and the MIT
|
||
// License along with the library. If not, see
|
||
// <https://www.apache.org/licenses/LICENSE-2.0> and
|
||
// <https://opensource.org/licenses/MIT>.
|
||
|
||
use crate::int256;
|
||
use crate::int256::U256;
|
||
use core::num::NonZero;
|
||
|
||
// The square root method used is based on code by Martin Guy @UKC, June 1985.
|
||
// His method of square root by abacus method is from a book on programming
|
||
// abaci by Mr C. Woo.
|
||
// http://medialab.freaknet.org/martin/src/sqrt/
|
||
|
||
macro_rules! impl_hypot {
|
||
($Single:ident, $Double:ident $(, $Half:ident)?) => {
|
||
pub const fn $Single(a: $Single, b: $Single) -> ($Single, bool) {
|
||
$(
|
||
if a <= ($Half::MAX as $Single) && b <= ($Half::MAX as $Single) {
|
||
let val = match $Half(a as $Half, b as $Half) {
|
||
(val, false) => val as $Single,
|
||
(val, true) => (val as $Single) + (1 << $Half::BITS),
|
||
};
|
||
return (val, false);
|
||
}
|
||
)?
|
||
|
||
let aa = (a as $Double) * (a as $Double);
|
||
let bb = (b as $Double) * (b as $Double);
|
||
let (sum, overflow) = aa.overflowing_add(bb);
|
||
|
||
let mut x = sum;
|
||
let mut y;
|
||
let mut bit;
|
||
if overflow {
|
||
// Perform initialization and two iterations assuming word wider than n bits.
|
||
// Initialization:
|
||
// Set y = 0
|
||
// Set bit = 1 << n
|
||
// First iteration:
|
||
// Since x >= y + bit (because the overflow is equivalent to 1 << n on x):
|
||
// x -= y + bit (removing the effect of the overflow on x)
|
||
// y = (y >> 1) + bit (y becomes 1 << n)
|
||
// bit >>= 2 (bit becomes 1 << (n - 2))
|
||
// Second iteration:
|
||
// Now x < y (because y is 1 << n):
|
||
// y >>= 1 (y becomes 1 << (n - 1))
|
||
// bit >>= 2 (bit becomes 1 << (n - 4))
|
||
// In effect, we can set y = 1 << (n - 1), and bit = 1 << (n - 4)
|
||
y = 1 << ($Double::BITS - 1);
|
||
bit = 1 << ($Double::BITS - 4);
|
||
} else {
|
||
let sum_lz = match NonZero::<$Double>::new(sum) {
|
||
None => return (0, false),
|
||
Some(s) => s.leading_zeros(),
|
||
};
|
||
y = 0;
|
||
bit = 1 << ($Double::BITS - 2 - sum_lz / 2 * 2);
|
||
}
|
||
while bit != 0 {
|
||
let y_plus_bit = y + bit;
|
||
y >>= 1;
|
||
if x >= y_plus_bit {
|
||
x -= y_plus_bit;
|
||
y += bit;
|
||
}
|
||
bit >>= 2;
|
||
}
|
||
debug_assert!((y >> $Single::BITS) as $Single == overflow as $Single);
|
||
(y as $Single, overflow)
|
||
}
|
||
};
|
||
}
|
||
|
||
impl_hypot! { u8, u16 }
|
||
impl_hypot! { u16, u32, u8 }
|
||
impl_hypot! { u32, u64, u16 }
|
||
impl_hypot! { u64, u128, u32 }
|
||
|
||
pub const fn u128(a: u128, b: u128) -> (u128, bool) {
|
||
if a <= (u64::MAX as u128) && b <= (u64::MAX as u128) {
|
||
let val = match u64(a as u64, b as u64) {
|
||
(val, false) => val as u128,
|
||
(val, true) => (val as u128) + (1 << 64),
|
||
};
|
||
return (val, false);
|
||
}
|
||
|
||
let aa = int256::wide_mul_u128(a, a);
|
||
let bb = int256::wide_mul_u128(b, b);
|
||
let (sum, overflow) = int256::overflowing_add_u256(aa, bb);
|
||
|
||
let mut x = sum;
|
||
let mut y;
|
||
let mut bit;
|
||
if overflow {
|
||
// Perform initialization and two iterations assuming word wider than n bits.
|
||
// Initialization:
|
||
// Set y = 0
|
||
// Set bit = 1 << n
|
||
// First iteration:
|
||
// Since x >= y + bit (because the overflow is equivalent to 1 << n on x):
|
||
// x -= y + bit (removing the effect of the overflow on x)
|
||
// y = (y >> 1) + bit (y becomes 1 << n)
|
||
// bit >>= 2 (bit becomes 1 << (n - 2))
|
||
// Second iteration:
|
||
// Now x < y (because y is 1 << n):
|
||
// y >>= 1 (y becomes 1 << (n - 1))
|
||
// bit >>= 2 (bit becomes 1 << (n - 4))
|
||
// In effect, we can set y = 1 << (n - 1), and bit = 1 << (n - 4)
|
||
y = U256 {
|
||
lo: 0,
|
||
hi: 1 << 127,
|
||
};
|
||
bit = 1 << 124;
|
||
} else {
|
||
y = U256 { lo: 0, hi: 0 };
|
||
bit = match NonZero::<u128>::new(sum.hi) {
|
||
None => panic!("small operands; should have used crate::hypot::u64"),
|
||
Some(s) => 1 << (126 - s.leading_zeros() / 2 * 2),
|
||
};
|
||
}
|
||
while bit != 0 {
|
||
let y_hi_plus_bit = y.hi + bit;
|
||
y.hi >>= 1;
|
||
if x.hi >= y_hi_plus_bit {
|
||
x.hi -= y_hi_plus_bit;
|
||
y.hi += bit;
|
||
}
|
||
bit >>= 2;
|
||
}
|
||
bit = 1 << 126;
|
||
while bit != 0 {
|
||
let y_plus_bit = U256 {
|
||
lo: y.lo + bit,
|
||
hi: y.hi,
|
||
};
|
||
y.lo = (y.lo >> 1) + ((y.hi & 1) << 127);
|
||
y.hi >>= 1;
|
||
if (x.hi > y_plus_bit.hi) || (x.hi == y_plus_bit.hi && x.lo >= y_plus_bit.lo) {
|
||
x = int256::wrapping_sub_u256(x, y_plus_bit);
|
||
y.lo += bit;
|
||
}
|
||
bit >>= 2
|
||
}
|
||
|
||
debug_assert!(y.hi == overflow as u128);
|
||
(y.lo, overflow)
|
||
}
|
||
|
||
#[cfg(test)]
|
||
mod tests {
|
||
use crate::hypot;
|
||
use crate::types::{U1F7, U1F15, U1F31, U1F63, U1F127};
|
||
|
||
#[test]
|
||
fn check_max() {
|
||
assert_eq!(hypot::u8(u8::MAX, u8::MAX), (104, true));
|
||
assert_eq!(hypot::u16(u16::MAX, u16::MAX), (27_144, true));
|
||
assert_eq!(hypot::u32(u32::MAX, u32::MAX), (1_779_033_702, true));
|
||
assert_eq!(
|
||
hypot::u64(u64::MAX, u64::MAX),
|
||
(7_640_891_576_956_012_807, true)
|
||
);
|
||
assert_eq!(
|
||
hypot::u128(u128::MAX, u128::MAX),
|
||
(140_949_571_415_070_559_626_692_937_523_481_902_396, true)
|
||
);
|
||
}
|
||
|
||
#[test]
|
||
fn check_zero() {
|
||
assert_eq!(hypot::u8(0, 0), (0, false));
|
||
assert_eq!(hypot::u16(0, 0), (0, false));
|
||
assert_eq!(hypot::u32(0, 0), (0, false));
|
||
assert_eq!(hypot::u64(0, 0), (0, false));
|
||
assert_eq!(hypot::u128(0, 0), (0, false));
|
||
}
|
||
|
||
#[test]
|
||
fn check_zero_max() {
|
||
assert_eq!(hypot::u8(u8::MAX, 0), (u8::MAX, false));
|
||
assert_eq!(hypot::u8(0, u8::MAX), (u8::MAX, false));
|
||
assert_eq!(hypot::u16(u16::MAX, 0), (u16::MAX, false));
|
||
assert_eq!(hypot::u16(0, u16::MAX), (u16::MAX, false));
|
||
assert_eq!(hypot::u32(u32::MAX, 0), (u32::MAX, false));
|
||
assert_eq!(hypot::u32(0, u32::MAX), (u32::MAX, false));
|
||
assert_eq!(hypot::u64(u64::MAX, 0), (u64::MAX, false));
|
||
assert_eq!(hypot::u64(0, u64::MAX), (u64::MAX, false));
|
||
assert_eq!(hypot::u128(u128::MAX, 0), (u128::MAX, false));
|
||
assert_eq!(hypot::u128(0, u128::MAX), (u128::MAX, false));
|
||
}
|
||
|
||
#[test]
|
||
fn check_max_plus() {
|
||
// hypot(2^n - 1, x) = 2^n; x = sqrt(2^(n+1) - 1)
|
||
// e.g. for u32, sqrt(2^33 - 1) = 92681.9
|
||
assert_eq!(hypot::u8(u8::MAX, 22), (u8::MAX, false));
|
||
assert_eq!(hypot::u8(u8::MAX, 23), (0, true));
|
||
assert_eq!(hypot::u16(u16::MAX, 362), (u16::MAX, false));
|
||
assert_eq!(hypot::u16(u16::MAX, 363), (0, true));
|
||
assert_eq!(hypot::u32(u32::MAX, 92_681), (u32::MAX, false));
|
||
assert_eq!(hypot::u32(u32::MAX, 92_682), (0, true));
|
||
assert_eq!(hypot::u64(u64::MAX, 6_074_000_999), (u64::MAX, false));
|
||
assert_eq!(hypot::u64(u64::MAX, 6_074_001_000), (0, true));
|
||
assert_eq!(
|
||
hypot::u128(u128::MAX, 26_087_635_650_665_564_424),
|
||
(u128::MAX, false)
|
||
);
|
||
assert_eq!(
|
||
hypot::u128(u128::MAX, 26_087_635_650_665_564_425),
|
||
(0, true)
|
||
);
|
||
}
|
||
|
||
#[test]
|
||
fn check_sqrt_2() {
|
||
assert_eq!(hypot::u8(1 << 7, 1 << 7), (U1F7::SQRT_2.to_bits(), false));
|
||
assert_eq!(
|
||
hypot::u16(1 << 15, 1 << 15),
|
||
(U1F15::SQRT_2.to_bits(), false)
|
||
);
|
||
assert_eq!(
|
||
hypot::u32(1 << 31, 1 << 31),
|
||
(U1F31::SQRT_2.to_bits(), false)
|
||
);
|
||
assert_eq!(
|
||
hypot::u64(1 << 63, 1 << 63),
|
||
(U1F63::SQRT_2.to_bits(), false)
|
||
);
|
||
assert_eq!(
|
||
hypot::u128(1 << 127, 1 << 127),
|
||
(U1F127::SQRT_2.to_bits(), false)
|
||
);
|
||
}
|
||
}
|