1use core::{iter::FusedIterator, num::NonZeroU64, ops};
4
5use crate::{
6 gcd,
7 span::TimeSpan,
8 stamp::TimeStamp,
9 step::ClockStep,
10};
11
12#[cfg(feature = "serde")]
13use serde::ser::SerializeTupleStruct;
14
15#[derive(Clone, Copy)]
17pub struct Frequency {
18 pub count: u64,
20
21 pub cycle: NonZeroU64,
23}
24
25impl Frequency {
26 #[must_use]
33 pub fn new(count: u64, period: TimeSpan) -> Self {
34 assert_ne!(period, TimeSpan::ZERO, "Frequency period cannot be zero");
35
36 let period = period.as_nanos().unsigned_abs();
37 let gcd = gcd(count, period);
38 let count = count / gcd;
39 let period_nanos = period / gcd;
40
41 match NonZeroU64::new(period_nanos) {
42 None => unreachable!(),
43 Some(cycle) => Frequency { count, cycle },
44 }
45 }
46
47 #[inline]
49 #[must_use]
50 pub fn from_hz(value: u64) -> Self {
51 Frequency::new(value, TimeSpan::SECOND)
52 }
53
54 #[inline]
56 #[must_use]
57 pub fn from_khz(value: u64) -> Self {
58 Frequency::new(value, TimeSpan::MILLISECOND)
59 }
60
61 #[inline]
63 #[must_use]
64 pub fn from_mhz(value: u64) -> Self {
65 Frequency::new(value, TimeSpan::MICROSECOND)
66 }
67
68 #[inline]
70 #[must_use]
71 pub fn from_ghz(value: u64) -> Self {
72 Frequency::new(value, TimeSpan::NANOSECOND)
73 }
74
75 #[inline]
78 fn elements(&self, span: TimeSpan) -> Elements {
79 #![allow(clippy::cast_sign_loss)] debug_assert!(!span.is_negative(), "Span must not be negative");
82
83 Elements(span.as_nanos() as u64 * self.count)
84 }
85
86 #[inline]
88 #[must_use]
89 pub fn periods_in_span(&self, span: TimeSpan) -> u64 {
90 self.periods_in_elements(self.elements(span)).0
91 }
92
93 #[inline]
95 fn period_elements(&self) -> Elements {
96 Elements(self.cycle.get())
97 }
98
99 #[inline]
101 fn periods_elements(&self, count: u64) -> Elements {
102 Elements(self.cycle.get() * count)
103 }
104
105 #[inline]
108 fn periods_in_elements(&self, span: Elements) -> (u64, Elements) {
109 let periods = span.0 / self.cycle.get();
110 let remaining = Elements(span.0 % self.cycle.get());
111 (periods, remaining)
112 }
113
114 #[inline]
116 fn until_next(&self, span: Elements) -> Elements {
117 Elements(self.cycle.get() - span.0 % self.cycle)
118 }
119
120 #[inline]
122 fn span_fitting_elements(&self, span: Elements) -> Option<TimeSpan> {
123 #![allow(clippy::cast_possible_wrap)]
124
125 match (span.0, self.count) {
126 (0, 0) => Some(TimeSpan::ZERO),
127 (_, 0) => None,
128 (span, count) => {
129 let nanos = span.div_ceil(count);
130 debug_assert!(i64::try_from(nanos).is_ok(), "Nanos overflow");
131 Some(TimeSpan::new(nanos as i64))
132 }
133 }
134 }
135
136 #[inline]
138 #[must_use]
139 pub fn ticker(&self, start: TimeStamp) -> FrequencyTicker {
140 FrequencyTicker::new(*self, start)
141 }
142}
143
144#[cfg(feature = "serde")]
145impl serde::Serialize for Frequency {
146 fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
147 if serializer.is_human_readable() {
148 serializer.serialize_str(&format!("{}/{} Hz", self.count, self.cycle))
149 } else {
150 let mut serializer = serializer.serialize_tuple_struct("Frequency", 2)?;
151 serializer.serialize_field(&self.count)?;
152 serializer.serialize_field(&self.cycle)?;
153 serializer.end()
154 }
155 }
156}
157
158#[cfg(feature = "serde")]
159impl<'de> serde::Deserialize<'de> for Frequency {
160 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
161 where
162 D: serde::Deserializer<'de>,
163 {
164 if deserializer.is_human_readable() {
165 let s = String::deserialize(deserializer)?;
166
167 match s.split_once("/") {
168 None => {
169 let count = s
170 .strip_suffix("Hz")
171 .ok_or_else(|| serde::de::Error::custom("Wrong frequency format"))?;
172 let count = count.trim();
173 let count = count.parse().map_err(serde::de::Error::custom)?;
174
175 let cycle = const { NonZeroU64::new(1).unwrap() };
176 return Ok(Frequency { count, cycle });
177 }
178
179 Some((count, s)) => {
180 let count = count.trim();
181 let count = count.parse().map_err(serde::de::Error::custom)?;
182 let cycle = s
183 .strip_suffix("Hz")
184 .ok_or_else(|| serde::de::Error::custom("Wrong frequency format"))?;
185 let cycle = cycle.trim();
186 let cycle = cycle.parse().map_err(serde::de::Error::custom)?;
187
188 return Ok(Frequency { count, cycle });
189 }
190 }
191 } else {
192 struct FrequencyVisitor;
193
194 impl<'de> serde::de::Visitor<'de> for FrequencyVisitor {
195 type Value = Frequency;
196
197 fn expecting(&self, formatter: &mut core::fmt::Formatter) -> core::fmt::Result {
198 formatter.write_str("a tuple of 2 elements")
199 }
200
201 fn visit_seq<A>(self, mut seq: A) -> Result<Self::Value, A::Error>
202 where
203 A: serde::de::SeqAccess<'de>,
204 {
205 let count = seq
206 .next_element()?
207 .ok_or_else(|| serde::de::Error::custom("Frequency is empty"))?;
208 let cycle = seq
209 .next_element()?
210 .ok_or_else(|| serde::de::Error::custom("Frequency is empty"))?;
211 Ok(Frequency { count, cycle })
212 }
213 }
214
215 deserializer.deserialize_tuple_struct("Frequency", 2, FrequencyVisitor)
216 }
217 }
218}
219
220#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
221#[repr(transparent)]
222struct Elements(u64);
223
224impl ops::Add for Elements {
225 type Output = Self;
226
227 #[inline]
228 fn add(self, rhs: Self) -> Self::Output {
229 Elements(self.0 + rhs.0)
230 }
231}
232
233impl ops::AddAssign for Elements {
234 #[inline]
235 fn add_assign(&mut self, rhs: Self) {
236 self.0 += rhs.0;
237 }
238}
239
240impl ops::Sub for Elements {
241 type Output = Self;
242
243 #[inline]
244 fn sub(self, rhs: Self) -> Self::Output {
245 Elements(self.0 - rhs.0)
246 }
247}
248
249impl ops::SubAssign for Elements {
250 #[inline]
251 fn sub_assign(&mut self, rhs: Self) {
252 self.0 -= rhs.0;
253 }
254}
255
256impl ops::Rem for Elements {
257 type Output = Self;
258
259 #[inline]
260 fn rem(self, rhs: Self) -> Self {
261 Elements(self.0 % rhs.0)
262 }
263}
264
265impl ops::RemAssign for Elements {
266 #[inline]
267 fn rem_assign(&mut self, rhs: Self) {
268 self.0 %= rhs.0;
269 }
270}
271
272pub struct FrequencyTicker {
276 freq: Frequency,
277
278 until_next: Elements,
280
281 now: TimeStamp,
283}
284
285impl FrequencyTicker {
286 #[inline]
288 #[must_use]
289 pub fn new(freq: Frequency, now: TimeStamp) -> Self {
290 FrequencyTicker::with_delay(freq, 0, now)
291 }
292
293 #[inline]
295 #[must_use]
296 pub fn with_delay(freq: Frequency, periods: u64, now: TimeStamp) -> Self {
297 FrequencyTicker {
298 freq,
299 until_next: freq.periods_elements(1 + periods),
300 now,
301 }
302 }
303
304 #[inline]
306 #[must_use]
307 pub fn next_tick(&self) -> Option<TimeStamp> {
308 Some(self.now + self.freq.span_fitting_elements(self.until_next)?)
309 }
310
311 #[inline]
314 pub fn ticks(&mut self, step: TimeSpan) -> FrequencyTickerIter {
315 let span = self.freq.elements(step);
316
317 let iter = FrequencyTickerIter {
318 span,
319 freq: self.freq,
320 until_next: self.until_next,
321 accumulated: 0,
322 now: self.now,
323 };
324
325 if span >= self.until_next {
326 self.until_next = self.freq.until_next(span - self.until_next);
327 } else {
328 self.until_next -= span;
329 }
330
331 self.now += step;
332
333 iter
334 }
335
336 #[inline]
339 pub fn tick_count(&mut self, step: TimeSpan) -> u64 {
340 self.ticks(step).ticks()
341 }
342
343 #[inline]
345 pub fn with_ticks(&mut self, step: TimeSpan, f: impl FnMut(ClockStep)) {
346 self.ticks(step).for_each(f);
347 }
348
349 #[inline]
351 #[must_use]
352 pub fn frequency(&self) -> Frequency {
353 self.freq
354 }
355
356 #[inline]
361 pub fn set_frequency(&mut self, freq: Frequency, clip_period: bool) {
362 self.freq = freq;
363 if clip_period {
364 let period = freq.period_elements();
365 if self.until_next > period {
366 self.until_next = period;
367 }
368 }
369 }
370}
371
372pub struct FrequencyTickerIter {
374 span: Elements,
375 freq: Frequency,
376 until_next: Elements,
377 accumulated: u64,
378 now: TimeStamp,
379}
380
381impl FrequencyTickerIter {
382 #[inline]
384 #[must_use]
385 pub fn ticks(&self) -> u64 {
386 if self.span < self.until_next {
387 return 0;
388 }
389
390 let span = self.span - self.until_next;
391 1 + self.freq.periods_in_elements(span).0
392 }
393}
394
395impl Iterator for FrequencyTickerIter {
396 type Item = ClockStep;
397
398 #[inline]
399 fn next(&mut self) -> Option<ClockStep> {
400 if self.accumulated > 0 {
401 self.accumulated -= 1;
402 return Some(ClockStep {
403 now: self.now,
404 step: TimeSpan::ZERO,
405 });
406 }
407
408 if self.span < self.until_next {
409 return None;
410 }
411
412 let next = self
417 .freq
418 .span_fitting_elements(self.until_next)
419 .unwrap_or(TimeSpan::ZERO);
420
421 let advance = self.freq.elements(next);
423
424 debug_assert!(
425 advance <= self.span,
426 "Span cannot be greater than total span left in iterator"
427 );
428 debug_assert!(
429 advance >= self.until_next,
430 "Span cannot be less then span until next tick"
431 );
432
433 let (periods, remaining) = self.freq.periods_in_elements(advance - self.until_next);
434
435 self.accumulated = periods;
436
437 self.until_next = self.freq.period_elements() - remaining;
438
439 self.span -= advance;
440 self.now += next;
441
442 Some(ClockStep {
443 now: self.now,
444 step: next,
445 })
446 }
447}
448
449impl FusedIterator for FrequencyTickerIter {}
450
451pub trait FrequencyNumExt {
453 fn hz(self) -> Frequency;
455
456 fn khz(self) -> Frequency;
458
459 fn mhz(self) -> Frequency;
461
462 fn ghz(self) -> Frequency;
464}
465
466impl FrequencyNumExt for u64 {
467 #[inline]
468 fn hz(self) -> Frequency {
469 Frequency::from_hz(self)
470 }
471
472 #[inline]
473 fn khz(self) -> Frequency {
474 Frequency::from_khz(self)
475 }
476
477 #[inline]
478 fn mhz(self) -> Frequency {
479 Frequency::from_mhz(self)
480 }
481
482 #[inline]
483 fn ghz(self) -> Frequency {
484 Frequency::from_ghz(self)
485 }
486}
487
488#[test]
489fn test_freq_ticker() {
490 use crate::span::TimeSpanNumExt;
491
492 let mut ticker = FrequencyTicker::new(Frequency::new(3, 10.nanoseconds()), TimeStamp::start());
493
494 assert_eq!(ticker.tick_count(10.nanoseconds()), 3);
495
496 let ticks = [0, 0, 0, 1, 0, 0, 1, 0, 0, 1];
497
498 for _ in 0..10 {
499 for tick in ticks {
500 assert_eq!(ticker.tick_count(TimeSpan::NANOSECOND), tick);
501 }
502 }
503}
504
505#[test]
506fn test_freq_ticker_delay() {
507 use crate::span::TimeSpanNumExt;
508
509 const DELAY: u64 = 12;
510
511 let mut ticker = FrequencyTicker::with_delay(
512 Frequency::new(3, 10.nanoseconds()),
513 DELAY,
514 TimeStamp::start(),
515 );
516
517 assert_eq!(0, ticker.tick_count(40.nanoseconds()));
518
519 let ticks = [0, 0, 0, 1, 0, 0, 1, 0, 0, 1];
520
521 for _ in 0..10 {
522 for tick in ticks {
523 assert_eq!(ticker.tick_count(TimeSpan::NANOSECOND), tick);
524 }
525 }
526}
527
528#[test]
529fn test_freq_ticker_next_tick() {
530 use crate::span::TimeSpanNumExt;
531
532 let mut ticker = FrequencyTicker::new(Frequency::new(3, 10.nanoseconds()), TimeStamp::start());
533
534 let ticks = [0, 0, 0, 1, 0, 0, 1, 0, 0, 1];
535
536 let mut next_tick = ticker.next_tick().unwrap();
537
538 for _ in 0..100 {
539 for tick in ticks {
540 assert_eq!(ticker.tick_count(TimeSpan::NANOSECOND), tick);
541
542 if tick > 0 {
543 assert_eq!(next_tick, ticker.now);
544 next_tick = ticker.next_tick().unwrap();
545 } else {
546 assert_eq!(next_tick, ticker.next_tick().unwrap());
547 }
548 }
549 }
550}
551
552#[test]
553fn test_hz() {
554 let mut freq = Frequency::from_hz(3).ticker(TimeStamp::start());
555
556 let ticks = freq.ticks(TimeSpan::SECOND).collect::<Vec<_>>();
557 assert_eq!(
558 ticks,
559 vec![
560 ClockStep {
561 now: TimeStamp::start() + TimeSpan::new(333_333_334),
562 step: TimeSpan::new(333_333_334),
563 },
564 ClockStep {
565 now: TimeStamp::start() + TimeSpan::new(666_666_667),
566 step: TimeSpan::new(333_333_333),
567 },
568 ClockStep {
569 now: TimeStamp::start() + TimeSpan::new(1_000_000_000),
570 step: TimeSpan::new(333_333_333),
571 },
572 ]
573 );
574}