manchester_code/
lib.rs

1//! # Manchester Decoder
2//!
3//! Features:
4//!
5//! * Decode monotonically sampled data stream that is Manchester modulated
6//!   like it is used in Philips RC5
7//!
8//!   <https://techdocs.altium.com/display/FPGA/Philips+RC5+Infrared+Transmission+Protocol>
9//!
10//! * High/low IN-Activitity configuration
11//! * Automatic start and end of datagram detection
12//! * Sampling needs to be 3 times the length of half a bit. (i.e. only a
13//!   single periodic timer is needed), for a infrared receiver
14//!   889 µs halfbit period => the periodic timer should run all 297 µs.
15//!
16//! # Manchester Modulation
17//!
18//! <https://en.wikipedia.org/wiki/Manchester_code>
19//!
20//!
21//! * A datagram starts after a pause period longer than the time of two bits
22//! * A datagram is finished if their is no edge anymore longer than a bit
23//!   period and all subsequent samples are at inactive level
24//! * all other situations are treated as errors and are rejected
25//! * Bit order of a datagram:
26//!   * The first bit received is the most significant bit (MSB) and
27//!     the last bit
28//!
29//!
30//! ## Receiving Algorithm Details
31//!
32//! A Periodic sampling is used.
33//!
34//! * Three samples per half bit period, will do. It gives a one third (of half period)
35//!   tolerance. And allows for one third (of half period) where the signal is
36//!   expected to be stable.
37//!
38//!   Thus, the Philips half bit time can vary 889 µs +/- 296 µs = [595; 1175] µs
39//!
40//! * For every bit there is an edge at the transition from first half bit to
41//!   second half bit. This is period is used to synchronize bit value measurement
42//!
43//! * The first bit value must be pre-known, because it determines where the
44//!   synchronization edges are to be expected:
45//!
46//!   |
47//! # Example
48//!
49//! The lib runs in no_std environments
50//!
51//! ```ignore
52//! #![deny(warnings)]
53//! #![deny(unsafe_code)]
54//! #![no_main]
55//! #![no_std]
56//!
57//! use nucleo_stm32g071rb as board; //  it also includes mem, defmt
58//!
59//! use board::hal::prelude::*;
60//! use board::hal::stm32;
61//! use board::hal::nb::block;
62//!
63//! use manchester_code::{ActivityLevel, SyncOnTurningEdge, BitOrder, Decoder};
64//!
65//! #[cortex_m_rt::entry]
66//! fn main() -> ! {
67//!     let dp = stm32::Peripherals::take().expect("cannot take peripherals");
68//!     let mut rcc = dp.RCC.constrain();
69//!
70//!     let gpioa = dp.GPIOA.split(&mut rcc);
71//!     let infrared = gpioa.pa8.into_pull_up_input();
72//!
73//!     let mut timer = dp.TIM17.timer(&mut rcc);
74//!     timer.start(297.us());
75//!         let mut receiver = Decoder::new(
76//!             ActivityLevel::High,
77//!             SyncOnTurningEdge::First,
78//!             BitOrder::LittleEndian);
79//!     loop {
80//!         match receiver.next(infrared.is_high().unwrap()) {
81//!             None => (),
82//!             Some(t) => if t.length() > 2 {
83//!                 defmt::println!("Datagram: {:?}",  t ); },
84//!         };
85//!         block!(timer.wait()).unwrap();
86//!     }
87//! }
88//! ```
89
90#![no_std]
91// #![deny(warnings)]
92#![deny(unsafe_code)]
93
94use defmt::Format;
95
96use core::iter::Iterator;
97use core::ops::Index;
98
99use embedded_hal::Pwm;
100
101/// BitOrder or endian describes the ordering of bits during transmission
102///
103/// Big endian:  MSB is transmitted first; LSB is transmitted last
104/// Little endian: LSB is transmitted first; MSB is transmitted last
105#[derive(Copy, Clone, Debug)]
106pub enum BitOrder {
107    BigEndian,
108    LittleEndian,
109}
110
111/// Representation of a datagram
112///
113/// The total length is limited to 128 bits
114/// The bits of a telegram are internally enumerated from 0 to 127.
115/// A default datagram is expected to be empty (i.e. containing zero bits)
116#[derive(Default, Copy, Clone, Debug)]
117pub struct Datagram {
118    length_in_bit: u8,
119    buffer: u128,
120}
121
122#[derive(Debug)]
123struct Error;
124
125impl Datagram {
126    /// Add a bit to a datagram
127    ///
128    /// The new bit is placed at index zero.
129    /// The index of all previously added bits gets increased by one.
130    ///
131    /// # Arguments
132    ///
133    /// * `bit` - The bit value to record at index 0
134    /// * `bit_order`- The bit order either BigEndian or LittleEndian determines
135    ///                if the bit is added at the LSB or MSB position
136    ///
137    /// # Returns
138    ///
139    /// * Error - if the datagram is already filled up to its capacity.
140    /// * () - if the bit was successfully added
141    fn add_bit(&mut self, bit: bool, order: BitOrder) -> Result<(), Error> {
142        if self.length_in_bit == 127 {
143            Err(Error)
144        } else {
145            match order {
146                BitOrder::BigEndian => {
147                    self.buffer <<= 1;
148                    if bit {
149                        self.buffer += 1;
150                    };
151                }
152                BitOrder::LittleEndian => {
153                    if bit {
154                        self.buffer += 1 << self.length_in_bit;
155                    }
156                }
157            }
158            self.length_in_bit += 1;
159            Ok(())
160        }
161    }
162
163    pub fn len(&self) -> u8 {
164        self.length_in_bit
165    }
166
167    pub fn is_empty(&self) -> bool {
168        0 == self.length_in_bit
169    }
170
171    /// Extract a data slice from the datagram
172    ///
173    /// # Args
174    ///
175    /// * `min` - start index (included)
176    /// * `max` - max index (not included)
177    ///
178    /// # Returns
179    ///
180    /// the extracted value
181    ///
182    /// # Panics
183    ///  if 0 <= min < max <= len() is violated
184    ///
185    /// # Example
186    /// ```rust
187    ///
188    /// use manchester_code::Datagram;
189    ///
190    /// let datagram = Datagram::new("0-111_10101_00001111");
191    /// assert_eq!(0b1111, datagram.extract_data(0, 4));
192    /// assert_eq!(0b1111, datagram.extract_data(0, 8));
193    /// assert_eq!(0b1111, datagram.extract_data(datagram.len()-5, datagram.len()));
194    /// ```
195    pub fn extract_data(&self, min: u8, max: u8) -> u128 {
196        if max > self.length_in_bit {
197            panic!("Max index to big");
198        }
199        if min >= max {
200            panic!("Min index to greater than max index");
201        }
202
203        let mut value = 0_u128;
204        for index in min..max {
205            let mask: u128 = 1 << (max + min - index - 1);
206            let bit = if (mask & self.buffer) == 0 { &0 } else { &1 };
207            value <<= 1;
208            value += bit;
209        }
210        value
211    }
212
213    /// Create a new datagram from "binary" string
214    ///
215    /// # Arguments
216    ///
217    /// * `bit_repr` - Bit representation as string of zeros and ones.
218    ///                Arbitrary delimiter signs (for readability) are ignored
219    /// # Example
220    ///
221    /// ```rust
222    /// use manchester_code::Datagram;
223    ///
224    /// let datagram = Datagram::new("0-111_10101_00001111");
225    /// ```
226    pub fn new(bit_repr: &str) -> Self {
227        let mut datagram = Datagram::default();
228        for bit in bit_repr.bytes() {
229            match bit {
230                b'0' => datagram.add_bit(false, BitOrder::BigEndian).unwrap(),
231                b'1' => datagram.add_bit(true, BitOrder::BigEndian).unwrap(),
232                _ => (),
233            }
234        }
235        datagram
236    }
237
238    fn into_big_endian_iter(self) -> DatagramBigEndianIterator {
239        DatagramBigEndianIterator {
240            datagram: self,
241            index: self.len(),
242        }
243    }
244
245    fn into_little_endian_iter(self) -> DatagramLittleEndianIterator {
246        DatagramLittleEndianIterator {
247            datagram: self,
248            index: 0,
249        }
250    }
251}
252
253impl Index<u8> for Datagram {
254    type Output = u128;
255
256    /// Access the n-th element via index
257    ///
258    /// # Panics
259    ///
260    /// * if the index is out of range
261    ///
262    /// # Example
263    ///
264    /// ```rust
265    /// use manchester_code::Datagram;
266    ///
267    /// let datagram = Datagram::new("0-111_10101_00001111");
268    /// assert_eq!(1, datagram[0]);
269    /// assert_eq!(0, datagram[5]);
270    /// ```
271    fn index(&self, index: u8) -> &Self::Output {
272        if index >= self.length_in_bit {
273            panic!("Wrong Index")
274        }
275        let mask: u128 = 1 << index;
276        if mask & self.buffer == 0 {
277            &0
278        } else {
279            &1
280        }
281    }
282}
283
284impl PartialEq for Datagram {
285    fn eq(&self, other: &Self) -> bool {
286        self.buffer == other.buffer && self.length_in_bit == other.length_in_bit
287    }
288}
289
290impl Eq for Datagram {}
291
292impl Format for Datagram {
293    fn format(&self, f: defmt::Formatter) {
294        for index in 0..self.length_in_bit {
295            if 0 == index % 4 {
296                defmt::write!(f, "-");
297            }
298            if 1 == self[self.length_in_bit - 1 - index] {
299                defmt::write!(f, "1");
300            } else {
301                defmt::write!(f, "0");
302            }
303        }
304    }
305}
306
307#[derive(Debug)]
308pub struct DatagramBigEndianIterator {
309    datagram: Datagram,
310    index: u8,
311}
312
313impl Iterator for DatagramBigEndianIterator {
314    type Item = bool;
315    fn next(&mut self) -> Option<Self::Item> {
316        if 0 < self.index {
317            self.index -= 1;
318            Some(1 == self.datagram[self.index])
319        } else {
320            None
321        }
322    }
323}
324
325#[derive(Debug)]
326pub struct DatagramLittleEndianIterator {
327    datagram: Datagram,
328    index: u8,
329}
330
331impl Iterator for DatagramLittleEndianIterator {
332    type Item = bool;
333    fn next(&mut self) -> Option<Self::Item> {
334        if self.datagram.len() > self.index {
335            self.index += 1;
336            Some(1 == self.datagram[self.index - 1])
337        } else {
338            None
339        }
340    }
341}
342
343/// Encodes a datagram to Manchester code
344///
345/// The encoder turns into an iterator.
346/// the encoding happens by calling the iterator.
347///
348/// # Example
349///
350/// ```rust
351/// use manchester_code::{
352///     Datagram,
353///     DatagramBigEndianIterator,
354///     Encoder};
355///
356/// let mut encoder = Encoder::<DatagramBigEndianIterator>::new(Datagram::new("01"));
357/// assert_eq!(Some(true), encoder.next());
358/// assert_eq!(Some(false), encoder.next());
359/// assert_eq!(Some(false), encoder.next());
360/// assert_eq!(Some(true), encoder.next());
361/// assert_eq!(None, encoder.next());
362/// ```
363
364#[derive(Debug)]
365pub struct Encoder<I> {
366    datagram_iter: I,
367    first_half_bit: bool,
368    last_value: Option<bool>,
369}
370
371impl Encoder<DatagramBigEndianIterator> {
372    /// Create a new Encoder ready to encode the datagram passed along
373    ///
374    /// # Arguments
375    ///
376    /// * `datagram` - the datagram to be encoded
377    pub fn new(d: Datagram) -> Self {
378        let mut datagram_iter = d.into_big_endian_iter();
379        let last_value = datagram_iter.next();
380        Encoder::<DatagramBigEndianIterator> {
381            datagram_iter,
382            first_half_bit: true,
383            last_value,
384        }
385    }
386}
387
388impl Encoder<DatagramLittleEndianIterator> {
389    /// Create a new Encoder ready to encode the datagram passed along
390    ///
391    /// # Arguments
392    ///
393    /// * `datagram` - the datagram to be encoded
394    pub fn new(d: Datagram) -> Self {
395        let mut datagram_iter = d.into_little_endian_iter();
396        let last_value = datagram_iter.next();
397        Encoder::<DatagramLittleEndianIterator> {
398            datagram_iter,
399            first_half_bit: true,
400            last_value,
401        }
402    }
403}
404
405impl<I: Iterator<Item = bool>> Iterator for Encoder<I> {
406    type Item = bool;
407    fn next(&mut self) -> Option<Self::Item> {
408        match self.last_value {
409            Some(bit) => {
410                if self.first_half_bit {
411                    self.first_half_bit = false;
412                    Some(!bit)
413                } else {
414                    self.first_half_bit = true;
415                    self.last_value = self.datagram_iter.next();
416                    Some(bit)
417                }
418            }
419            None => None,
420        }
421    }
422}
423
424/// Activity level of the Pin where the infrared receiver is attached to.
425/// It is the opposite level the pin takes if no datagram is transmitted.
426#[derive(PartialEq)]
427pub enum ActivityLevel {
428    High,
429    Low,
430}
431
432/// A priori knowledge about the first expected bit of a telegram
433///
434/// It is needed for correct decoding if the datagram length is unknown
435pub enum SyncOnTurningEdge {
436    First,
437    Second,
438}
439
440/// Decode a Manchester encoded stream of periodically taken samples into
441/// a datagram.
442pub struct Decoder {
443    // Config data
444    activity_level: ActivityLevel,
445    sync_on_turning_edge: SyncOnTurningEdge,
446    bit_order: BitOrder,
447    // Collected output data
448    datagram: Datagram,
449    // Internal processing control data
450    previous_sample: bool,
451    edge_distance: u8,
452    recording_distance: u8,
453    receiving_started: bool,
454    record_marker_reached: bool,
455}
456
457const SAMPLES_PER_HALF_BIT_PERIOD: u8 = 3;
458const TOLERANCE: u8 = 1;
459
460//   ___---___------   e - first edge
461//   xxx012345678901   x - exit criteria no bits are send anymore
462//     f----tttt--xxx  t - tolerance range an edge is expected
463
464const LOWER_BARRIER: u8 = 2 * SAMPLES_PER_HALF_BIT_PERIOD - TOLERANCE;
465const UPPER_BARRIER: u8 = 2 * SAMPLES_PER_HALF_BIT_PERIOD + TOLERANCE;
466const NO_EDGE_EXIT_LIMIT: u8 = 3 * SAMPLES_PER_HALF_BIT_PERIOD;
467
468impl Decoder {
469    /// Create an instance of a new manchester encoder
470    ///
471    /// # Arguments
472    ///
473    /// * `activity_level` - Low and High indicate what the activity level is
474    ///                      the negation of the activity level is the inactivity
475    ///                      level where no datagram is transmitted.
476    /// * `sync_on_turning_edge` - Indication if on the First or the Second
477    ///                            edge bits are aligned.
478    /// * `bit_order` - Either BigEndian (MSP is received first) or
479    ///                 LittleEndian (LSB is received first)
480    /// In combination of activity_level and sync_on_turning_edge it is determined
481    /// what if the first bit is either zero or one
482    ///
483    /// | Activity level | Sync on turning edge | Resulting first bit datagram |
484    /// |----------------|----------------------|------------------------------|
485    /// | High           | First                | Zero                         |
486    /// | High           | Second               | One                          |
487    /// | Low            | First                | One                          |
488    /// | Low            | Second               | Zero                         |
489    ///
490    pub const fn new(
491        activity_level: ActivityLevel,
492        sync_on_turning_edge: SyncOnTurningEdge,
493        bit_order: BitOrder,
494    ) -> Self {
495        let previous_sample = match activity_level {
496            ActivityLevel::High => false,
497            ActivityLevel::Low => true,
498        };
499        Decoder {
500            datagram: Datagram {
501                buffer: 0,
502                length_in_bit: 0,
503            },
504            previous_sample,
505            edge_distance: NO_EDGE_EXIT_LIMIT,
506            recording_distance: NO_EDGE_EXIT_LIMIT,
507            receiving_started: false,
508            activity_level,
509            sync_on_turning_edge,
510            record_marker_reached: false,
511            bit_order,
512        }
513    }
514
515    /// Sample a manchester modulated signal periodically and extract datagrams
516    ///
517    /// To cover some jitter the sampling rate is three times the half bit frequency
518    /// i.e. an Infrared manchester decoded bit lasts  2x  889 us),
519    /// Thus sampling period should be 296 us.
520    ///
521    /// Note: three times the bit frequency is good enough to consider the Nyquist
522    /// criterion and some potential jitter in sending frequency.
523    ///
524    /// # Arguments
525    ///
526    ///  * `sample` - the level of the pin true equals high, false equals low
527    ///
528    /// # Returns
529    ///
530    ///  Option of an infrared datagram
531    ///
532    ///  * None - if no complete datagram is received
533    ///  * Some(datagram) - a completely received datagram
534    ///
535    pub fn next(&mut self, sample: bool) -> Option<Datagram> {
536        // To understand the algorithm record marker are introduced.
537        //
538        // Record marker are the sample taken directly after the edge
539        // in the middle of the transmission of a bit
540        // Record marker are aligned to SyncOnTurningEdge.
541        //
542        // As of the manchester protocol definition, there must be always
543        // an edge in the middle of transmission of the bit.
544        //
545        // Example: Start with "1" (high inactivity)
546        //
547        //        |bit_1|bit_0|bit_0     - The bits
548        // -------...------...---...     - The signal
549        //           ^     ^     ^       - The record marker
550        //
551        // Example: Start with "0" (high inactivity)
552        //
553        //        |bit_0|bit_1|bit_1     - The bits
554        // ----------......---...---     - The signal
555        //           ^     ^     ^       - The record marker
556        //
557        // At each record marker the bit value is determined and recorded
558        let mut return_value: Option<Datagram> = None;
559
560        if sample != self.previous_sample {
561            if !self.receiving_started {
562                // cover the start of the telegram
563                match self.sync_on_turning_edge {
564                    SyncOnTurningEdge::First => {
565                        // first edge is the record marker
566                        self.record_marker_reached = true;
567                        self.receiving_started = true;
568                    }
569                    SyncOnTurningEdge::Second => {
570                        // by protocol design it is guaranteed that there is a second edge
571                        // within half-bit time aka within SAMPLES_PER_HALF_BIT_PERIOD
572                        if self.edge_distance <= SAMPLES_PER_HALF_BIT_PERIOD + TOLERANCE {
573                            // first edge at the record marker
574                            self.record_marker_reached = true;
575                            self.receiving_started = true;
576                        } else {
577                            // very first edge -> do nothing on purpose
578                        }
579                    }
580                }
581            }
582            if self.recording_distance >= LOWER_BARRIER && self.recording_distance <= UPPER_BARRIER
583            {
584                self.record_marker_reached = true;
585            }
586            if self.record_marker_reached {
587                // In the middle of a bit transmission the value is derived from the new sample
588                self.datagram
589                    .add_bit(!sample, self.bit_order) // the sample is NOT mixed with activity_level
590                    .unwrap();
591                // reset internal data for the next record_marker
592                self.recording_distance = 1;
593                self.record_marker_reached = false;
594            }
595            self.previous_sample = sample;
596            self.edge_distance = 1;
597        } else {
598            self.edge_distance += 1;
599            self.recording_distance += 1;
600        }
601
602        if self.edge_distance > NO_EDGE_EXIT_LIMIT {
603            // end of datagram condition no edge anymore
604            if !self.datagram.is_empty() && (sample ^ (self.activity_level == ActivityLevel::High))
605            {
606                return_value = Some(self.datagram);
607                self.receiving_started = false;
608            }
609            self.datagram = Datagram::default();
610            self.edge_distance -= 1; // prevent number overflow
611        }
612        if self.recording_distance > NO_EDGE_EXIT_LIMIT {
613            self.recording_distance -= 1; // prevent number overflow
614        }
615        return_value
616    }
617}
618
619/// Control sending of datagrams, manage infrared radiation pollution
620///
621/// The InfraredEmitter behaves socially by enforcing a pause time between
622/// subsequent
623/// Required resources:
624///
625/// * A configured PWM - typically at a frequency of 36..38 kHz (RC5 protocol)
626/// * A facility that periodically runs half bit sending, e.g. a timer ISR
627///   typically at a period of 889 µs (half bit time, RC5 protocol)
628///
629/// # Example
630///
631/// ```ignore
632/// #[cortex_m_rt::entry]
633///
634/// fn main() -> ! {
635///     let dp = stm32::Peripherals::take().expect("cannot take peripherals");
636///
637///    // Set up the system clock
638///     //// let mut flash = dp.FLASH.constrain();
639///     let mut rcc = dp.RCC.constrain();
640///
641///     // Setup PWM we use Arduino PIN D5 -> is PB4 / TIM3_CH1 on stm32g071
642///     let gpiob = dp.GPIOB.split(&mut rcc);
643///
644///     // let clocks = rcc.cfgr.sysclk(16.MHz()).freeze(&mut flash.acr);
645///
646///     let pwm_pin = gpiob.pb4;
647///     let pwm = dp.TIM3.pwm(36_u32.khz(), &mut rcc);
648///     let mut pwm_send_ir = pwm.bind_pin(pwm_pin);
649///
650///     pwm_send_ir.set_duty(pwm_send_ir.get_max_duty() / 4); // 25% duty cyle
651///
652///     // Set up the interrupt timer
653///     let mut timer = dp.TIM2.timer(&mut rcc);
654///     timer.start(889.us());
655///
656///     const PAUSE_HALF_BITS_BETWEEN_DATAGRAMS: u8 = 3;
657///     let mut infrared_emitter = InfraredEmitter::new(PAUSE_HALF_BITS_BETWEEN_DATAGRAMS, pwm_send_ir, ());
658///
659///     defmt::println!("Init done");
660///
661///     let datagram = Datagram::new("0101_0011_0111_0001");
662///     defmt::println!("Send new datagram {}", datagram);
663///     infrared_emitter.send_if_possible(datagram, 25);
664///
665///     loop {
666///         infrared_emitter.send_half_bit();
667///         block!(timer.wait()).unwrap();
668///     }
669/// }
670/// ```
671#[derive(Debug)]
672pub struct InfraredEmitter<P, C, I> {
673    encoder: Option<Encoder<I>>,
674    max_pause_cycles: u8,
675    current_pause_cycles: u8,
676    pwm: P,
677    channel: C,
678}
679
680impl<P, C, D, I> InfraredEmitter<P, C, I>
681where
682    P: Pwm + Pwm<Channel = C> + Pwm<Duty = D>,
683    C: Copy,
684    D: core::ops::Mul<Output = D> + core::ops::Div<Output = D>,
685    I: Iterator<Item = bool>,
686{
687    /// Create a new infrared Emitter
688    ///
689    /// # Arguments
690    ///
691    /// * `pause_cycles` - configures the time between subsequent datagram
692    ///                    emissions. The total duration is half-bit-time (889 µs)
693    ///                    times number of pause bit cycles. In the pause time
694    ///                    no infrared radiation is emitted and other
695    ///                    participants can occupy the radiation space.
696    /// * `pwm` - the PWM to be used for ir pulse emission
697    /// * `channel` - the channel to be used by the PWM
698    pub fn new(pause_cycles: u8, pwm: P, channel: C) -> Self {
699        InfraredEmitter {
700            encoder: None,
701            max_pause_cycles: pause_cycles,
702            current_pause_cycles: 0,
703            pwm,
704            channel,
705        }
706    }
707
708    /// Progress on sending a datagram by emitting a half bit
709    ///
710    /// This function needs to be called every half-bit period, i.e. each 889 µs.
711    /// The periodically required call is most likely delegated to a timer ISR.
712    ///
713    /// half-bit emitting happens by enabling/disabling a a properly configured
714    /// PWM.
715    pub fn send_half_bit(&mut self) {
716        match &mut self.encoder {
717            Some(encoder) => match encoder.next() {
718                Some(half_bit) => {
719                    if half_bit {
720                        self.pwm.enable(self.channel);
721                    } else {
722                        self.pwm.disable(self.channel);
723                    }
724                }
725                None => {
726                    self.pwm.disable(self.channel);
727                    self.encoder = None;
728                    self.current_pause_cycles = 0;
729                }
730            },
731            None => {
732                // the pwm is already disabled -> manage pause period
733                self.current_pause_cycles += 1;
734            }
735        }
736    }
737}
738
739impl<P, C, D> InfraredEmitter<P, C, DatagramBigEndianIterator>
740where
741    P: Pwm + Pwm<Channel = C> + Pwm<Duty = D>,
742    C: Copy,
743    D: core::ops::Mul<Output = D> + core::ops::Div<Output = D>,
744{
745    /// Immediately start sending a datagram if possible
746    ///
747    /// Sending is possible iff there is no sending procedure in progress.
748    /// A call to this function is not blocking
749    ///
750    /// # Arguments
751    ///
752    /// * `datagram` - The datagram to be send
753    /// * `sending_power` - The duty cycle of the pwm in percent
754    ///                     should be less than or equal 25 (percent)
755    ///                     Is reduced to 25 if a higher value is given.
756    ///                     Lower sending power is appropriate for pairing datagrams.
757    ///
758    /// # Returns
759    ///
760    /// * *true* - if sending was initiated
761    /// * *false* - if sending was not possible to initiate
762    pub fn send_if_possible(&mut self, datagram: Datagram, sending_power: D) -> bool {
763        if self.current_pause_cycles < self.max_pause_cycles {
764            false
765        } else {
766            // let mut sending_power: D = if sending_power > 25 { 25 } else { sending_power };
767            // let duty = self.pwm.get_max_duty() * sending_power / 100;
768            // self.pwm.set_duty(self.channel, duty);
769            self.pwm.set_duty(self.channel, sending_power);
770            self.encoder = Some(Encoder::<DatagramBigEndianIterator>::new(datagram));
771            true
772        }
773    }
774}
775
776impl<P, C, D> InfraredEmitter<P, C, DatagramLittleEndianIterator>
777where
778    P: Pwm + Pwm<Channel = C> + Pwm<Duty = D>,
779    C: Copy,
780    D: core::ops::Mul<Output = D> + core::ops::Div<Output = D>,
781{
782    /// Immediately start sending a datagram if possible
783    ///
784    /// Sending is possible iff there is no sending procedure in progress.
785    /// A call to this function is not blocking
786    ///
787    /// # Arguments
788    ///
789    /// * `datagram` - The datagram to be send
790    /// * `sending_power` - The duty cycle of the pwm in percent
791    ///                     should be less than or equal 25 (percent)
792    ///                     Is reduced to 25 if a higher value is given.
793    ///                     Lower sending power is appropriate for pairing datagrams.
794    ///
795    /// # Returns
796    ///
797    /// * *true* - if sending was initiated
798    /// * *false* - if sending was not possible to initiate
799    pub fn send_if_possible(&mut self, datagram: Datagram, sending_power: D) -> bool {
800        if self.current_pause_cycles < self.max_pause_cycles {
801            false
802        } else {
803            // let mut sending_power: D = if sending_power > 25 { 25 } else { sending_power };
804            // let duty = self.pwm.get_max_duty() * sending_power / 100;
805            // self.pwm.set_duty(self.channel, duty);
806            self.pwm.set_duty(self.channel, sending_power);
807            self.encoder = Some(Encoder::<DatagramLittleEndianIterator>::new(datagram));
808            true
809        }
810    }
811}
812#[cfg(test)]
813mod tests;