1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418
//! Implementation of double-sided two-way ranging //! //! This ranging technique is described in the DW1000 user manual, section 12.3. //! This module uses three messages for a range measurement, as described in //! section 12.3.2. //! //! This module defines the messages required, and provides code for sending and //! decoding them. It is left to the user to tie all that together, by sending //! out the messages at the right time. //! //! There can be some variation in the use of this module, depending on the use //! case. Here is one example of how this module can be used: //! 1. Nodes are divided into anchors and tags. Tags are those nodes whose //! position interests us. Anchors are placed in known locations to enable //! range measurements. //! 2. Anchors regularly send out pings ([`Ping`]). //! 3. Tags listen for these pings, and reply with a ranging request //! ([`Request`]) for each ping they receive. //! 4. When an anchor receives a ranging request, it replies with a ranging //! response ([`Response`]). //! 5. Once the tag receives the ranging response, it has all the information it //! needs to compute the distance. //! //! Please refer to the [examples] in the DWM1001 Board Support Crate for an //! implementation of this scheme. //! //! In this scheme, anchors initiate the exchange, which results in the tag //! having the distance information. Possible variations include the tag //! initiating the request and the anchor calculating the distance, or a //! peer-to-peer scheme without dedicated tags and anchors. //! //! Please note that using the code in this module without further processing of //! the result will yield imprecise measurements. To improve the precision of //! those measurements, a range bias needs to be applied. Please refer to the //! user manual, and [this DWM1001 issue] for more information. //! //! [`Ping`]: struct.Ping.html //! [`Request`]: struct.Request.html //! [`Response`]: struct.Response.html //! [examples]: https://github.com/braun-robotics/rust-dwm1001/tree/master/examples //! [this DWM1001 issue]: https://github.com/braun-robotics/rust-dwm1001/issues/55 use core::mem::size_of; use embedded_hal::{ blocking::spi, digital::OutputPin, }; use serde::{ Deserialize, Serialize, }; use ssmarshal; use crate::{ hl, mac, time::{ Duration, Instant, }, DW1000, Error, Ready, TxFuture, }; /// The transmission delay /// /// This defines the transmission delay as 10 ms. This should be enough to /// finish the rest of the preparation and send the message, even if we're /// running with unoptimized code. const TX_DELAY: u32 = 10_000_000; /// Implemented by all ranging messages pub trait Message: Sized + for<'de> Deserialize<'de> + Serialize { /// A prelude that identifies the message const PRELUDE: Prelude; /// The length of the message's prelude /// /// This is a bit of a hack that we need until `slice::<impl [T]>::len` is /// stable as a const fn. const PRELUDE_LEN: usize; /// The length of the whole message, including prelude and data const LEN: usize = Self::PRELUDE_LEN + size_of::<Self>(); /// Decodes a received message of this type /// /// The user is responsible for receiving a message using /// [`DW1000::receive`]. Once a message has been received, this method can /// be used to check what type of message this is. /// /// Returns `Ok(None)`, if the message is not of the right type. Otherwise, /// returns `Ok(Some(RxMessage<Self>)), if the message is of the right type, /// and no error occured. fn decode<SPI>(message: &hl::Message) -> Result<Option<RxMessage<Self>>, Error<SPI>> where SPI: spi::Transfer<u8> + spi::Write<u8> { if !message.frame.payload.starts_with(Self::PRELUDE.0) { // Not a message of this type return Ok(None); } if message.frame.payload.len() != Self::LEN { // Invalid message return Err(Error::BufferTooSmall { required_len: Self::LEN, }); } // The message passes muster. Let's decode it. let (payload, _) = ssmarshal::deserialize::<Self>( &message.frame.payload[Self::PRELUDE.0.len().. ])?; Ok(Some(RxMessage { rx_time: message.rx_time, source: message.frame.header.source, payload, })) } } /// An incoming ranging message /// /// Contains the received payload, as well as some metadata that's required to /// create a reply to the message. pub struct RxMessage<T: Message> { /// The time the message was received pub rx_time: Instant, /// The source of the message pub source: mac::Address, /// The message data pub payload: T, } /// An outgoing ranging message /// /// Contains the payload to be sent, as well as some metadata. pub struct TxMessage<T: Message> { /// The recipient of the message /// /// This is an IEEE 802.15.4 MAC address. This could be a broadcast address, /// for messages that are sent to all other nodes in range. pub recipient: mac::Address, /// The time this message is going to be sent /// /// When creating this struct, this is going to be an instant in the near /// future. When sending the message, the sending is delayed to make sure it /// it sent at exactly this instant. pub tx_time: Instant, /// The actual message payload pub payload: T, } impl<T> TxMessage<T> where T: Message { /// Send this message via the DW1000 /// /// Serializes the message payload and uses [`DW1000::send`] internally to /// send it. Returns a [`TxFuture`] to represent the current state of the /// send operation, if no error occurs. pub fn send<'r, SPI, CS>(&self, dw1000: &'r mut DW1000<SPI, CS, Ready>) -> Result<TxFuture<'r, SPI, CS>, Error<SPI>> where SPI: spi::Transfer<u8> + spi::Write<u8>, CS: OutputPin, { // Create a buffer that fits the biggest message currently implemented. // This is a really ugly hack. The size of the buffer should just be // `T::LEN`. Unfortunately that's not possible. See: // https://github.com/rust-lang/rust/issues/42863 const LEN: usize = 48; assert!(T::LEN <= LEN); let mut buf = [0; LEN]; buf[..T::PRELUDE.0.len()].copy_from_slice(T::PRELUDE.0); ssmarshal::serialize( &mut buf[T::PRELUDE.0.len()..], &self.payload, )?; let future = dw1000.send( &buf[..T::LEN], self.recipient, Some(self.tx_time), )?; Ok(future) } } /// Sent before a message's data to identify the message #[derive(Debug, Deserialize, Serialize)] #[repr(C)] pub struct Prelude(pub &'static [u8]); /// Ranging ping message /// /// This message is typically sent to initiate a range measurement transaction. /// See [module documentation] for more info. /// /// [module documentation]: index.html #[derive(Debug, Deserialize, Serialize)] #[repr(C)] pub struct Ping { /// When the ping was sent, in local sender time pub ping_tx_time: Instant, } impl Ping { /// Creates a new ping message /// /// Only creates the message, but doesn't yet send it. Sets the transmission /// time to 10 milliseconds in the future. Make sure to send the message /// within that time frame, or the distance measurement will be negatively /// affected. pub fn new<SPI, CS>(dw1000: &mut DW1000<SPI, CS, Ready>) -> Result<TxMessage<Self>, Error<SPI>> where SPI: spi::Transfer<u8> + spi::Write<u8>, CS: OutputPin, { let tx_time = dw1000.sys_time()? + Duration::from_nanos(TX_DELAY); let ping_tx_time = tx_time + dw1000.get_tx_antenna_delay()?; let payload = Ping { ping_tx_time, }; Ok(TxMessage { recipient: mac::Address::broadcast(&mac::AddressMode::Short), tx_time, payload, }) } } impl Message for Ping { const PRELUDE: Prelude = Prelude(b"RANGING PING"); const PRELUDE_LEN: usize = 12; } /// Ranging request message /// /// This message is typically sent in response to a ranging ping, to request a /// ranging response. See [module documentation] for more info. /// /// [module documentation]: index.html #[derive(Debug, Deserialize, Serialize)] #[repr(C)] pub struct Request { /// When the original ping was sent, in local time on the anchor pub ping_tx_time: Instant, /// The time between the ping being received and the reply being sent pub ping_reply_time: Duration, /// When the ranging request was sent, in local sender time pub request_tx_time: Instant, } impl Request { /// Creates a new ranging request message /// /// Only creates the message, but doesn't yet send it. Sets the transmission /// time to 10 milliseconds in the future. Make sure to send the message /// within that time frame, or the distance measurement will be negatively /// affected. pub fn new<SPI, CS>( dw1000: &mut DW1000<SPI, CS, Ready>, ping: RxMessage<Ping>, ) -> Result<TxMessage<Self>, Error<SPI>> where SPI: spi::Transfer<u8> + spi::Write<u8>, CS: OutputPin, { let tx_time = dw1000.sys_time()? + Duration::from_nanos(TX_DELAY); let request_tx_time = tx_time + dw1000.get_tx_antenna_delay()?; let ping_reply_time = request_tx_time.duration_since(ping.rx_time); let payload = Request { ping_tx_time: ping.payload.ping_tx_time, ping_reply_time, request_tx_time, }; Ok(TxMessage { recipient: ping.source, tx_time, payload, }) } } impl Message for Request { const PRELUDE: Prelude = Prelude(b"RANGING REQUEST"); const PRELUDE_LEN: usize = 15; } /// Ranging response message /// /// This message is typically sent in response to a ranging request, to wrap up /// the range measurement transaction.. See [module documentation] for more /// info. /// /// [module documentation]: index.html #[derive(Debug, Deserialize, Serialize)] #[repr(C)] pub struct Response { /// The time between the ping being received and the reply being sent pub ping_reply_time: Duration, /// The time between the ping being sent and the reply being received pub ping_round_trip_time: Duration, /// The time the ranging request was sent, in local sender time pub request_tx_time: Instant, /// The time between the request being received and a reply being sent pub request_reply_time: Duration, } impl Response { /// Creates a new ranging response message /// /// Only creates the message, but doesn't yet send it. Sets the transmission /// time to 10 milliseconds in the future. Make sure to send the message /// within that time frame, or the distance measurement will be negatively /// affected. pub fn new<SPI, CS>( dw1000: &mut DW1000<SPI, CS, Ready>, request: RxMessage<Request>, ) -> Result<TxMessage<Self>, Error<SPI>> where SPI: spi::Transfer<u8> + spi::Write<u8>, CS: OutputPin, { let tx_time = dw1000.sys_time()? + Duration::from_nanos(TX_DELAY); let response_tx_time = tx_time + dw1000.get_tx_antenna_delay()?; let ping_round_trip_time = request.rx_time.duration_since(request.payload.ping_tx_time); let request_reply_time = response_tx_time.duration_since(request.rx_time); let payload = Response { ping_reply_time: request.payload.ping_reply_time, ping_round_trip_time, request_tx_time: request.payload.request_tx_time, request_reply_time, }; Ok(TxMessage { recipient: request.source, tx_time, payload, }) } } impl Message for Response { const PRELUDE: Prelude = Prelude(b"RANGING RESPONSE"); const PRELUDE_LEN: usize = 16; } /// Computes the distance to another node from a ranging response /// /// Returns `None`, if the computed time of flight is so large the distance /// calculation would overflow. pub fn compute_distance_mm(response: &RxMessage<Response>) -> Option<u64> { let request_round_trip_time = response.rx_time.duration_since(response.payload.request_tx_time); // Compute time of flight according to the formula given in the DW1000 user // manual, section 12.3.2. let rtt_product = response.payload.ping_round_trip_time.value() * request_round_trip_time.value(); let reply_time_product = response.payload.ping_reply_time.value() * response.payload.request_reply_time.value(); let complete_sum = response.payload.ping_round_trip_time.value() + request_round_trip_time.value() + response.payload.ping_reply_time.value() + response.payload.request_reply_time.value(); let time_of_flight = (rtt_product - reply_time_product) / complete_sum; // Nominally, all time units are based on a 64 Ghz clock, meaning each time // unit is 1/64 ns. const SPEED_OF_LIGHT: u64 = 299_792_458; // m/s or nm/ns let distance_nm_times_64 = SPEED_OF_LIGHT.checked_mul(time_of_flight)?; let distance_mm = distance_nm_times_64 / 64 / 1_000_000; Some(distance_mm) }