embedded_fps/fps.rs
1use embedded_time::{duration::Seconds, Clock, Instant};
2use heapless::Deque;
3
4
5
6/// Measures Frames Per Second (FPS).
7///
8/// `MAX_FPS` - Defines the maximum FPS that you expect to measure.
9#[derive(Debug, Clone)]
10pub struct FPS<const MAX_FPS: usize, C: Clock> {
11 /// The last registered frames.
12 last_second_frames: Deque<Instant<C>, MAX_FPS>,
13 /// The embedded [`Clock`] that will be used to track the passed second.
14 clock: C,
15}
16
17impl<const MAX_FPS: usize, C: Clock> FPS<MAX_FPS, C> {
18 /// Creates a new Frames Per Second counter.
19 pub fn new(clock: C) -> FPS<MAX_FPS, C> {
20 FPS {
21 last_second_frames: Deque::<_, MAX_FPS>::new(),
22 clock,
23 }
24 }
25
26 /// Adds another frame tick and returns the current Frames Pre Second.
27 ///
28 /// # Panics
29 ///
30 /// When [`Clock::try_now()`] returns an error or if the `MAX_FPS` is reached.
31 pub fn tick(&mut self) -> usize {
32 self.try_tick().unwrap()
33 }
34
35 /// Adds another frame tick and returns the current Frames Pre Second.
36 ///
37 /// This method will not panic if the `MAX_FPS` is reached,
38 /// instead it will just return the `MAX_FPS` value (capping it in a nutshell).
39 ///
40 /// # Panics
41 ///
42 /// If [`Clock::try_now()`] returns an error.
43 pub fn tick_max(&mut self) -> usize {
44 self.try_tick_max().unwrap()
45 }
46
47 /// Adds another frame tick and returns the current Frames Pre Second.
48 ///
49 /// This method will not return an error if the `MAX_FPS` is reached,
50 /// instead it will just return the `MAX_FPS` value (capping it in a nutshell).
51 pub fn try_tick_max(&mut self) -> Result<usize, Error> {
52 match self.try_tick() {
53 Ok(fps) => Ok(fps),
54 Err(Error::MaxFPS(_)) => Ok(MAX_FPS),
55 Err(err) => Err(err),
56 }
57 }
58
59 /// Adds another frame tick and returns the current Frames Pre Second.
60 ///
61 /// # Panics
62 ///
63 /// When [`Clock::try_now()`] returns an error or if the `MAX_FPS` is reached.
64 pub fn try_tick(&mut self) -> Result<usize, Error> {
65 let now = self.clock.try_now().map_err(Error::Clock)?;
66 let a_second_ago = now - Seconds(1);
67
68 while self
69 .last_second_frames
70 .front()
71 .copied()
72 .map_or(false, |tick| tick < a_second_ago)
73 {
74 self.last_second_frames.pop_front();
75 }
76
77 self.last_second_frames
78 .push_back(now)
79 .map_err(|_cap_err| Error::MaxFPS(MAX_FPS))?;
80
81 // return the frames per second
82 Ok(self.last_second_frames.len())
83 }
84}
85
86impl<const MAX_FPS: usize, C> Default for FPS<MAX_FPS, C>
87where
88 C: Clock + Default,
89{
90 fn default() -> Self {
91 Self::new(C::default())
92 }
93}
94
95/// The errors that [`FPS`] can return.
96///
97/// Keep in mind that [`Error::MaxFPS`] will trigger panic on [`FPS::tick`]
98/// or be returned as an error on [`FPS::try_tick`].
99#[derive(Debug)]
100pub enum Error {
101 /// The clock returned an error when calling [`Clock::try_now`].
102 Clock(embedded_time::clock::Error),
103 /// The maximum reading of Frames per second was reached.
104 /// The internal deque reached it's capacity.
105 ///
106 /// Increase the `MAX_FPS` to avoid this problem.
107 MaxFPS(usize),
108}