Skip to content
File

Blob: firmware/crates/radio-core/src/metrics.rs

rust51 lines
1//! Windowed scheduler accounting and measured hardware telemetry; no platform calls.
2use serde::Serialize;
3 
4#[derive(Debug, Clone, Copy, Serialize)]
5#[serde(rename_all = "camelCase")]
6pub struct Memory {
7 pub free: u32,
8 pub minimum_free: u32,
9 pub largest_block: u32,
10}
11 
12#[derive(Debug, Clone, Copy, Serialize)]
13#[serde(rename_all = "camelCase")]
14pub struct Hardware {
15 pub sampled_at_ms: u64,
16 pub cpu_busy_bps: [Option<u16>; 2],
17 pub chip_temperature_mc: Option<i32>,
18 pub internal: Memory,
19 pub psram: Memory,
20 pub sample_cost_us: u32,
21 pub sampler_stack_free: u32,
22}
23 
24/// Cumulative idle times are committed on task switches, so invalid deltas are
25/// unavailable samples rather than a fabricated zero or saturated percentage.
26#[derive(Debug, Default)]
27pub struct CpuWindow {
28 previous: Option<(u64, [u64; 2])>,
29}
30 
31impl CpuWindow {
32 pub fn sample(&mut self, now_us: u64, idle_us: [u64; 2]) -> [Option<u16>; 2] {
33 let previous = self.previous.replace((now_us, idle_us));
34 let Some((before_us, before_idle)) = previous else {
35 return [None; 2];
36 };
37 let Some(elapsed) = now_us
38 .checked_sub(before_us)
39 .filter(|n| (100_000..=5_000_000).contains(n))
40 else {
41 return [None; 2];
42 };
43 std::array::from_fn(|core| {
44 let idle = idle_us[core]
45 .checked_sub(before_idle[core])
46 .filter(|&n| n <= elapsed)?;
47 Some(((elapsed - idle) * 10_000 / elapsed) as u16)
48 })
49 }
50}