dw3000_ng/hl/receiving.rs
1#![allow(unused_imports)]
2
3use core::convert::TryInto;
4
5use byte::BytesExt as _;
6use fixed::traits::LossyInto;
7#[cfg(feature = "rssi")]
8use num_traits::Float;
9
10#[cfg(feature = "defmt")]
11use defmt::Format;
12
13use super::{AutoDoubleBufferReceiving, ReceiveTime, Receiving};
14use crate::{
15 configs::{BitRate, PulseRepetitionFrequency, SfdSequence},
16 maybe_async_attr, spi_type,
17 time::Instant,
18 Config, Error, FastCommand, Ready, DW3000,
19};
20
21use smoltcp::wire::Ieee802154Frame;
22
23/// An incoming message
24#[derive(Debug)]
25#[cfg_attr(feature = "defmt", derive(Format))]
26pub struct Message<'l> {
27 /// The time the message was received
28 ///
29 /// This time is based on the local system time, as defined in the SYS_TIME
30 /// register.
31 pub rx_time: Instant,
32
33 /// quality of the message received
34 pub rx_quality: RxQuality,
35
36 /// The MAC frame
37 pub frame: Ieee802154Frame<&'l [u8]>,
38}
39
40/// A struct representing the quality of the received message.
41#[cfg_attr(feature = "defmt", derive(Format))]
42#[derive(Debug, serde::Serialize, serde::Deserialize)]
43pub struct RxQuality {
44 /// The confidence that there was Line Of Sight between the sender and the
45 /// receiver.
46 ///
47 /// - 0 means it's very unlikely there was LOS.
48 /// - 1 means it's very likely there was LOS.
49 ///
50 /// The number doesn't give a guarantee, but an indication.
51 /// It is based on the
52 /// APS006_Part-3-DW3000-Diagnostics-for-NLOS-Channels-v1.1 document.
53 pub los_confidence_level: f32,
54 /// The radio signal strength indicator in dBm.
55 ///
56 /// The value is an estimation that is quite accurate up to -85 dBm.
57 /// Above -85 dBm, the estimation underestimates the actual value.
58 pub rssi: f32,
59}
60
61impl<SPI, RECEIVING> DW3000<SPI, RECEIVING>
62where
63 SPI: spi_type::spi::SpiDevice<u8>,
64 RECEIVING: Receiving,
65{
66 /// Returns the RX state of the DW3000
67 #[maybe_async_attr]
68 pub async fn rx_state(&mut self) -> Result<u8, Error<SPI>> {
69 Ok(self.ll.sys_state().read().await?.rx_state())
70 }
71
72 #[maybe_async_attr]
73 pub(super) async fn start_receiving(
74 &mut self,
75 recv_time: ReceiveTime,
76 config: Config,
77 ) -> Result<(), Error<SPI>> {
78 if config.frame_filtering {
79 self.ll
80 .sys_cfg()
81 .modify(
82 |_, w| w.ffen(0b1), // enable frame filtering
83 )
84 .await?;
85 self.ll
86 .ff_cfg()
87 .modify(
88 |_, w| {
89 w.ffab(0b1) // receive beacon frames
90 .ffad(0b1) // receive data frames
91 .ffaa(0b1) // receive acknowledgement frames
92 .ffam(0b1)
93 }, // receive MAC command frames
94 )
95 .await?;
96 } else {
97 self.ll.sys_cfg().modify(|_, w| w.ffen(0b0)).await?; // disable frame filtering
98 }
99
100 match recv_time {
101 ReceiveTime::Delayed(time) => {
102 // Panic if the time is not rounded to top 31 bits
103 //
104 // NOTE: DW3000's DX_TIME register is 32 bits wide, but only the top 31 bits are used.
105 // The last bit is ignored per the user manual!!!
106 if time.value() % (1 << 9) != 0 {
107 panic!("Time must be rounded to top 31 bits!");
108 }
109
110 // Put the time into the delay register
111 // By setting this register, the chip knows to delay before transmitting
112 self.ll
113 .dx_time()
114 .modify(|_, w| // 32-bits value of the most significant bits
115 w.value( (time.value() >> 8) as u32 ))
116 .await?;
117 self.fast_cmd(FastCommand::CMD_DRX).await?;
118 }
119 ReceiveTime::Now => self.fast_cmd(FastCommand::CMD_RX).await?,
120 }
121
122 Ok(())
123 }
124
125 /// Wait for receive operation to finish
126 ///
127 /// This method returns an `nb::Result` to indicate whether the transmission
128 /// has finished, or whether it is still ongoing. You can use this to busily
129 /// wait for the transmission to finish, for example using `nb`'s `block!`
130 /// macro, or you can use it in tandem with [`DW3000::enable_rx_interrupts`]
131 /// and the DW3000 IRQ output to wait in a more energy-efficient manner.
132 ///
133 /// Handling the DW3000's IRQ output line is out of the scope of this
134 /// driver, but please note that if you're using the DWM1001 module or
135 /// DWM1001-Dev board, that the `dwm1001` crate has explicit support for
136 /// this.
137 #[maybe_async_attr]
138 pub async fn r_wait<'b>(
139 &mut self,
140 buffer: &'b mut [u8],
141 ) -> nb::Result<Message<'b>, Error<SPI>> {
142 // ATTENTION:
143 // If you're changing anything about which SYS_STATUS flags are being
144 // checked in this method, also make sure to update `enable_interrupts`.
145 let sys_status = self
146 .ll()
147 .sys_status()
148 .read()
149 .await
150 .map_err(|error| nb::Error::Other(Error::Spi(error)))?;
151
152 // Is a frame ready?
153 if sys_status.rxfcg() == 0b0 {
154 // No frame ready. Check for errors.
155 if sys_status.rxfce() == 0b1 {
156 return Err(nb::Error::Other(Error::Fcs));
157 }
158 if sys_status.rxphe() == 0b1 {
159 return Err(nb::Error::Other(Error::Phy));
160 }
161 if sys_status.rxfsl() == 0b1 {
162 return Err(nb::Error::Other(Error::ReedSolomon));
163 }
164 if sys_status.rxsto() == 0b1 {
165 return Err(nb::Error::Other(Error::SfdTimeout));
166 }
167 if sys_status.arfe() == 0b1 {
168 return Err(nb::Error::Other(Error::FrameFilteringRejection));
169 }
170 if sys_status.rxfto() == 0b1 {
171 return Err(nb::Error::Other(Error::FrameWaitTimeout));
172 }
173 if sys_status.rxovrr() == 0b1 {
174 return Err(nb::Error::Other(Error::Overrun));
175 }
176 if sys_status.rxpto() == 0b1 {
177 return Err(nb::Error::Other(Error::PreambleDetectionTimeout));
178 }
179
180 // Some error flags that sound like valid errors aren't checked here,
181 // because experience has shown that they seem to occur spuriously
182 // without preventing a good frame from being received. Those are:
183 // - LDEERR: Leading Edge Detection Processing Error
184 // - RXPREJ: Receiver Preamble Rejection
185
186 // No errors detected. That must mean the frame is just not ready yet.
187 return Err(nb::Error::WouldBlock);
188 }
189
190 // Frame is ready. Continue.
191
192 // Wait until LDE processing is done. Before this is finished, the RX
193 // time stamp is not available.
194 let rx_time = self
195 .ll()
196 .rx_time()
197 .read()
198 .await
199 .map_err(|error| nb::Error::Other(Error::Spi(error)))?
200 .rx_stamp();
201
202 // `rx_time` comes directly from the register, which should always
203 // contain a 40-bit timestamp. Unless the hardware or its documentation
204 // are buggy, the following should never panic.
205 let rx_time = Instant::new(rx_time).unwrap();
206
207 let rssi = self.get_first_path_signal_power().await?;
208 let rx_quality = RxQuality {
209 los_confidence_level: 1.0, // TODO
210 rssi,
211 };
212
213 // Reset status bits. This is not strictly necessary, but it helps, if
214 // you have to inspect SYS_STATUS manually during debugging.
215 // NOTE: The `SYS_STATUS` register is write-to-clear
216 self.ll()
217 .sys_status()
218 .write(|w| {
219 w.rxprd(0b1) // Receiver Preamble Detected
220 .rxsfdd(0b1) // Receiver SFD Detected
221 .ciadone(0b1) // LDE Processing Done
222 .rxphd(0b1) // Receiver PHY Header Detected
223 .rxphe(0b1) // Receiver PHY Header Error
224 .rxfr(0b1) // Receiver Data Frame Ready
225 .rxfcg(0b1) // Receiver FCS Good
226 .rxfce(0b1) // Receiver FCS Error
227 .rxfsl(0b1) // Receiver Reed Solomon Frame Sync Loss
228 .rxfto(0b1) // Receiver Frame Wait Timeout
229 .ciaerr(0b1) // Leading Edge Detection Processing Error
230 .rxovrr(0b1) // Receiver Overrun
231 .rxpto(0b1) // Preamble Detection Timeout
232 .rxsto(0b1) // Receiver SFD Timeout
233 .rxprej(0b1) // Receiver Preamble Rejection
234 })
235 .await
236 .map_err(|error| nb::Error::Other(Error::Spi(error)))?;
237
238 // Read received frame
239 let rx_finfo = self
240 .ll()
241 .rx_finfo()
242 .read()
243 .await
244 .map_err(|error| nb::Error::Other(Error::Spi(error)))?;
245 let rx_buffer = self
246 .ll()
247 .rx_buffer_0()
248 .read()
249 .await
250 .map_err(|error| nb::Error::Other(Error::Spi(error)))?;
251
252 let len = rx_finfo.rxflen() as usize;
253
254 if buffer.len() < len {
255 return Err(nb::Error::Other(Error::BufferTooSmall {
256 required_len: len,
257 }));
258 }
259
260 buffer[..len].copy_from_slice(&rx_buffer.data()[..len]);
261
262 let buffer = &buffer[..len];
263
264 self.state.mark_finished();
265
266 let frame = Ieee802154Frame::new_checked(buffer).map_err(|_| {
267 nb::Error::Other(Error::Frame(byte::Error::BadInput {
268 err: "Cannot decode 802.15.4 frame",
269 }))
270 })?;
271
272 Ok(Message {
273 rx_time,
274 rx_quality,
275 frame,
276 })
277 }
278
279 /// Wait for receive operation to finish
280 ///
281 /// This method returns an `nb::Result` to indicate whether the transmission
282 /// has finished, or whether it is still ongoing. You can use this to busily
283 /// wait for the transmission to finish, for example using `nb`'s `block!`
284 /// macro, or you can use it in tandem with [`DW3000::enable_rx_interrupts`]
285 /// and the DW3000 IRQ output to wait in a more energy-efficient manner.
286 ///
287 /// Handling the DW3000's IRQ output line is out of the scope of this
288 /// driver, but please note that if you're using the DWM1001 module or
289 /// DWM1001-Dev board, that the `dwm1001` crate has explicit support for
290 /// this.
291 #[maybe_async_attr]
292 pub async fn r_wait_buf(
293 &mut self,
294 buffer: &mut [u8],
295 ) -> nb::Result<(usize, Instant, RxQuality), Error<SPI>> {
296 // ATTENTION:
297 // If you're changing anything about which SYS_STATUS flags are being
298 // checked in this method, also make sure to update `enable_interrupts`.
299 let sys_status = self
300 .ll()
301 .sys_status()
302 .read()
303 .await
304 .map_err(|error| nb::Error::Other(Error::Spi(error)))?;
305
306 // Is a frame ready?
307 if sys_status.rxfcg() == 0b0 {
308 // No frame ready. Check for errors.
309 if sys_status.rxfce() == 0b1 {
310 return Err(nb::Error::Other(Error::Fcs));
311 }
312 if sys_status.rxphe() == 0b1 {
313 return Err(nb::Error::Other(Error::Phy));
314 }
315 if sys_status.rxfsl() == 0b1 {
316 return Err(nb::Error::Other(Error::ReedSolomon));
317 }
318 if sys_status.rxsto() == 0b1 {
319 return Err(nb::Error::Other(Error::SfdTimeout));
320 }
321 if sys_status.arfe() == 0b1 {
322 return Err(nb::Error::Other(Error::FrameFilteringRejection));
323 }
324 if sys_status.rxfto() == 0b1 {
325 return Err(nb::Error::Other(Error::FrameWaitTimeout));
326 }
327 if sys_status.rxovrr() == 0b1 {
328 return Err(nb::Error::Other(Error::Overrun));
329 }
330 if sys_status.rxpto() == 0b1 {
331 return Err(nb::Error::Other(Error::PreambleDetectionTimeout));
332 }
333
334 // Some error flags that sound like valid errors aren't checked here,
335 // because experience has shown that they seem to occur spuriously
336 // without preventing a good frame from being received. Those are:
337 // - LDEERR: Leading Edge Detection Processing Error
338 // - RXPREJ: Receiver Preamble Rejection
339
340 // No errors detected. That must mean the frame is just not ready yet.
341 return Err(nb::Error::WouldBlock);
342 }
343
344 // Frame is ready. Continue.
345
346 // Wait until LDE processing is done. Before this is finished, the RX
347 // time stamp is not available.
348 let rx_time = self
349 .ll()
350 .rx_time()
351 .read()
352 .await
353 .map_err(|error| nb::Error::Other(Error::Spi(error)))?
354 .rx_stamp();
355
356 let rssi = self.get_first_path_signal_power().await?;
357 let rx_quality = RxQuality {
358 los_confidence_level: 1.0, // TODO
359 rssi,
360 };
361
362 // `rx_time` comes directly from the register, which should always
363 // contain a 40-bit timestamp. Unless the hardware or its documentation
364 // are buggy, the following should never panic.
365 let rx_time = Instant::new(rx_time).unwrap();
366
367 // Reset status bits. This is not strictly necessary, but it helps, if
368 // you have to inspect SYS_STATUS manually during debugging.
369 self.ll()
370 .sys_status()
371 .write(|w| {
372 w.rxprd(0b1) // Receiver Preamble Detected
373 .rxsfdd(0b1) // Receiver SFD Detected
374 .ciadone(0b1) // LDE Processing Done
375 .rxphd(0b1) // Receiver PHY Header Detected
376 .rxphe(0b1) // Receiver PHY Header Error
377 .rxfr(0b1) // Receiver Data Frame Ready
378 .rxfcg(0b1) // Receiver FCS Good
379 .rxfce(0b1) // Receiver FCS Error
380 .rxfsl(0b1) // Receiver Reed Solomon Frame Sync Loss
381 .rxfto(0b1) // Receiver Frame Wait Timeout
382 .ciaerr(0b1) // Leading Edge Detection Processing Error
383 .rxovrr(0b1) // Receiver Overrun
384 .rxpto(0b1) // Preamble Detection Timeout
385 .rxsto(0b1) // Receiver SFD Timeout
386 .rxprej(0b1) // Receiver Preamble Rejection
387 })
388 .await
389 .map_err(|error| nb::Error::Other(Error::Spi(error)))?;
390
391 // Read received frame
392 let rx_finfo = self
393 .ll()
394 .rx_finfo()
395 .read()
396 .await
397 .map_err(|error| nb::Error::Other(Error::Spi(error)))?;
398 let rx_buffer = self
399 .ll()
400 .rx_buffer_0()
401 .read()
402 .await
403 .map_err(|error| nb::Error::Other(Error::Spi(error)))?;
404
405 let len = rx_finfo.rxflen() as usize;
406
407 if buffer.len() < len {
408 return Err(nb::Error::Other(Error::BufferTooSmall {
409 required_len: len,
410 }));
411 }
412
413 buffer[..len].copy_from_slice(&rx_buffer.data()[..len]);
414
415 self.state.mark_finished();
416
417 Ok((len, rx_time, rx_quality))
418 }
419
420 /// DW3000 User Manual 4.7.1
421 /// returns dBm
422 #[cfg(feature = "rssi")]
423 #[maybe_async_attr]
424 async fn get_first_path_signal_power(&mut self) -> Result<f32, Error<SPI>> {
425 let prf = self.state.get_rx_config().pulse_repetition_frequency;
426 let ll = self.ll();
427
428 #[derive(Copy, Clone)]
429 enum Method {
430 Ipatov,
431 Sts,
432 }
433
434 // prefer ipatov over sts
435 let method: Method = if ll.sys_cfg().read().await?.cia_ipatov() != 0 {
436 Method::Ipatov
437 } else if ll.sys_cfg().read().await?.cia_sts() != 0 {
438 Method::Sts
439 } else {
440 Err(Error::InvalidConfiguration)?
441 };
442
443 let a: f32 = match (prf, method) {
444 (PulseRepetitionFrequency::Mhz16, _) => 113.8,
445 (PulseRepetitionFrequency::Mhz64, Method::Ipatov) => 121.7,
446 (PulseRepetitionFrequency::Mhz64, Method::Sts) => 120.7,
447 };
448
449 let f1;
450 let f2;
451 let f3;
452 let n;
453
454 match method {
455 Method::Ipatov => {
456 f1 = ll.ip_diag_2().read().await?.ip_fp1m();
457 f2 = ll.ip_diag_3().read().await?.ip_fp2m();
458 f3 = ll.ip_diag_4().read().await?.ip_fp3m();
459 n = ll.ip_diag_12().read().await?.ip_nacc();
460 }
461 Method::Sts => {
462 f1 = ll.sts_diag_2().read().await?.cp0_fp1m();
463 f2 = ll.sts_diag_3().read().await?.cp0_fp2m();
464 f3 = ll.sts_diag_4().read().await?.cp0_fp3m();
465 n = ll.sts_diag_12().read().await?.cp0_nacc();
466 }
467 }
468
469 let d6: u32 = if ll.dgc_cfg().read().await?.rx_tune_en() != 0 {
470 let d: u32 = ll.dgc_dbg().read().await?.dgc_decision().into();
471 6u32 * d
472 } else {
473 0u32
474 };
475
476 Ok(10.0
477 * (((f1 * f1 + f2 * f2 + f3 * f3) as f32) / ((u32::from(n) * u32::from(n)) as f32))
478 .log10()
479 + (d6 as f32)
480 - a)
481 }
482
483 #[cfg(not(feature = "rssi"))]
484 #[maybe_async_attr]
485 async fn get_first_path_signal_power(&mut self) -> Result<f32, Error<SPI>> {
486 Ok(0.0)
487 }
488
489 #[allow(clippy::type_complexity)]
490 /// Finishes receiving and returns to the `Ready` state
491 ///
492 /// If the receive operation has finished, as indicated by `wait`, this is a
493 /// no-op. If the receive operation is still ongoing, it will be aborted.
494 #[maybe_async_attr]
495 pub async fn finish_receiving(mut self) -> Result<DW3000<SPI, Ready>, (Self, Error<SPI>)> {
496 // TO DO : if we are not in state 3 (IDLE), we need to have a reset of the module (with a new initialisation)
497 // BECAUSE : using force_idle (fast command 0) is not puting the pll back to stable !!!
498
499 if !self.state.is_finished() {
500 match self.force_idle().await {
501 Ok(()) => (),
502 Err(error) => return Err((self, error)),
503 }
504 }
505
506 Ok(DW3000 {
507 ll: self.ll,
508 seq: self.seq,
509 state: Ready,
510 })
511 }
512}