RadioLib icon indicating copy to clipboard operation
RadioLib copied to clipboard

SX1280 ranging not working

Open miked531 opened this issue 3 years ago • 6 comments

I am attempting to use the SX128x_Ranging example and it always reports 0 meters. Transmit/receive examples work successfully.

Comparing the SX1280 DS (section 14.5) to the code and I see the calibration (step 9) missing. There may be a few steps missing with trying to read the result in step 12. I have no idea if any of the steps are important. (I am new to Arduino and SX1280). Using DS_SX1280-1_V3.2 as my reference PDF.

Sketch that is causing the module fail

/*
   RadioLib SX128x Ranging Example

   This example performs ranging exchange between two
   SX1280 LoRa radio modules. Ranging allows to measure
   distance between the modules using time-of-flight
   measurement.

   Only SX1280 and SX1282 support ranging!

   For default module settings, see the wiki page
   https://github.com/jgromes/RadioLib/wiki/Default-configuration#sx128x---lora-modem

   For full API reference, see the GitHub Pages
   https://jgromes.github.io/RadioLib/
*/

// include the library
#include <RadioLib.h>

// SX1280 has the following connections:
// NSS pin:   10
// DIO1 pin:  2
// NRST pin:  3
// BUSY pin:  9
// my wiring matches these pins vvv
SX1280 radio = new Module(10, 7, 9, 8);

// or using RadioShield
// https://github.com/jgromes/RadioShield
//SX1280 radio = RadioShield.ModuleA;

void setup() {
  Serial.begin(9600);

  // initialize SX1280 with default settings
  Serial.print(F("[SX1280] Initializing ... "));
  int state = radio.begin();
  if (state == ERR_NONE) {
    Serial.println(F("success!"));
  } else {
    Serial.print(F("failed, code "));
    Serial.println(state);
    while (true);
  }
}

#define IS_MASTER true
void loop() {
  Serial.print(IS_MASTER ? "[master]" : "[slave]");
  Serial.print(F(" [SX1280] Ranging ... "));

  // start ranging exchange
  // range as master:             true
  // slave address:               0x12345678
  int state = radio.range(IS_MASTER, 0x12345678);

  if (state == ERR_NONE) {
    // ranging finished successfully
    Serial.println(F("success!"));
    Serial.print(F("[SX1280] Distance:\t\t\t"));
    Serial.print(radio.getRangingResult());
    Serial.println(F(" meters"));

  } else if (state == ERR_RANGING_TIMEOUT) {
    // timed out waiting for ranging packet
    Serial.println(F("timed out!"));

  } else {
    // some other error occurred
    Serial.print(F("failed, code "));
    Serial.println(state);

  }

  // wait for a second before ranging again
  delay(1000);
}

Hardware setup Two Arduino Nano 33 IOT boards with NiceRF SX1280-TCXO modules.

Debug mode output this repeats...

23:06:45.948 -> [master] [SX1280] Ranging ... CMD	3	
23:06:45.948 -> DATR	0	47	0	2	
23:06:45.948 -> 
23:06:45.948 -> CMD	8B	
23:06:45.948 -> DATW	90	43	18	43	3	43	
23:06:45.948 -> 
23:06:45.948 -> CMD	8C	
23:06:45.948 -> DATW	16	43	0	43	FF	43	20	43	40	43	0	43	0	43	
23:06:45.948 -> 
23:06:45.948 -> CMD	19	9	31	
23:06:45.948 -> DATR	0	47	0	C0	
23:06:45.948 -> 
23:06:45.948 -> CMD	18	9	31	
23:06:45.948 -> DATW	C0	47	
23:06:45.948 -> 
23:06:45.948 -> CMD	18	9	12	
23:06:45.948 -> DATW	12	47	34	47	56	47	78	47	
23:06:45.948 -> 
23:06:45.948 -> CMD	8D	
23:06:45.948 -> DATW	6	43	0	43	2	43	0	43	0	43	0	43	0	43	0	43	
23:06:45.948 -> 
23:06:45.948 -> CMD	A3	
23:06:45.948 -> DATW	1	43	
23:06:45.948 -> 
23:06:45.948 -> CMD	83	
23:06:45.948 -> DATW	0	43	0	43	0	43	
23:06:45.948 -> 
23:06:46.014 -> CMD	97	
23:06:46.014 -> DATW	FF	43	FF	43	
23:06:46.014 -> 
23:06:46.014 -> CMD	80	
23:06:46.014 -> DATW	0	43	
23:06:46.014 -> 
23:06:46.014 -> success!
23:06:46.014 -> [SX1280] Distance:			CMD	19	9	61	
23:06:46.014 -> DATR	0	47	0	0	0	0	0	0	
23:06:46.014 -> 
23:06:46.014 -> 0.00 meters

Additional info (please complete):

  • MCU: Arduino Nano 33 IOT
  • Wireless module type SX1280-TCXO
  • Arduino IDE version 1.8.13
  • Library version 4.4.0

miked531 avatar Apr 26 '21 03:04 miked531

I haven't used SX1280 ranging since it was originally implemented, so it's very possible it's been broken at some point, or even that it never worked correctly.

The missing calibration as well as the missing readout steps were added in the last commit, could you check if that fixed the issue?

jgromes avatar Apr 26 '21 16:04 jgromes

Thanks for the quick update. The commit is almost working after a few small tweaks. I can see the values in the ranging result register and they change as I move the devices around, but the conversion to meters is not accurate, though the formula appears correct.

My copy of the code is a mess with debug garbage in it now. When I get it more presentable I can submit a PR or just share the changes.

How did you come up with the values for calibration?

Sent from my iPhone

On Apr 26, 2021, at 12:29 PM, Jan Gromeš @.***> wrote:

 I haven't used SX1280 ranging since it was originally implemented, so it's very possible it's been broken at some point, or even that it never worked correctly.

The missing calibration as well as the missing readout steps were added in the last commit, could you check if that fixed the issue?

— You are receiving this because you authored the thread. Reply to this email directly, view it on GitHub, or unsubscribe.

miked531 avatar Apr 27 '21 04:04 miked531

The values are from SX1280 ranging guide - to get more accurate results you might have to do your own calibration, using the process described in that document.

jgromes avatar Apr 27 '21 13:04 jgromes

I tried doing a PR with the changes, but screwed up something on my system so I'm falling back to the rather crude "sending you the diffs" method.

These changes are still returning incorrect ranging, and I need to investigate further but wanted to get this off my mind. I don't think the 3 separate readRegister calls make any difference over a single read of 3 bytes, but that's what I found in Semtech code someplace. The more significant changes are the (1<<1) for enabling the clock and make sure to respect endianness when building the raw value.

diff --git a/src/modules/SX128x/SX1280.cpp b/src/modules/SX128x/SX1280.cpp
index 66b728c..4e6429c 100644
--- a/src/modules/SX128x/SX1280.cpp
+++ b/src/modules/SX128x/SX1280.cpp
@@ -105,7 +105,7 @@ int16_t SX1280::startRanging(bool master, uint32_t addr) {
       return(ERR_INVALID_BANDWIDTH);
   }
   uint8_t calBuff[] = { (uint8_t)((val >> 8) & 0xFF), (uint8_t)(val & 0xFF) };
-  state = writeRegister(SX128X_REG_RANGING_CALIBRATION_MSB, calBuff, 4);
+  state = writeRegister(SX128X_REG_RANGING_CALIBRATION_MSB, calBuff, 2);
   RADIOLIB_ASSERT(state);
 
   // set role and start ranging
@@ -138,7 +138,7 @@ float SX1280::getRangingResult() {
   state = readRegister(SX128X_REG_RANGING_LORA_CLOCK_ENABLE, data, 1);
   RADIOLIB_ASSERT(state);
 
-  data[0] |= 1;
+  data[0] |= (1 << 1);
   state = writeRegister(SX128X_REG_RANGING_LORA_CLOCK_ENABLE, data, 1);
   RADIOLIB_ASSERT(state);
 
@@ -147,12 +147,16 @@ float SX1280::getRangingResult() {
   RADIOLIB_ASSERT(state);
 
   data[0] &= 0xCF;
-  data[0] |= 1 << 4;
+  data[0] |= (1 << 4);
   state = writeRegister(SX128X_REG_RANGING_TYPE, data, 1);
   RADIOLIB_ASSERT(state);
 
   // read the register values
-  state = readRegister(SX128X_REG_RANGING_RESULT_MSB, data + 1, 3);
+  state = readRegister(SX128X_REG_RANGING_RESULT_MSB, data + 0, 1);
+  RADIOLIB_ASSERT(state);
+  state = readRegister(SX128X_REG_RANGING_RESULT_MID, data + 1, 1);
+  RADIOLIB_ASSERT(state);
+  state = readRegister(SX128X_REG_RANGING_RESULT_LSB, data + 2, 1);
   RADIOLIB_ASSERT(state);
 
   // set mode to standby RC
@@ -160,9 +164,8 @@ float SX1280::getRangingResult() {
   RADIOLIB_ASSERT(state);
 
   // calculate the real result
-  uint32_t raw = 0;
-  memcpy(&raw, data, sizeof(uint32_t));
-  return((float)raw * (150.0/(4.096 * _bwKhz)));
+  uint32_t raw = ((uint32_t)data[0] << 16) | ((uint32_t)data[1] << 8) | data[2];
+  return((float)raw * 150.0 / (4.096 * _bwKhz));
 }
 
 #endif

miked531 avatar May 05 '21 01:05 miked531

Thanks, I'll put that into commits and push it. What sort of values do you get out of ranging now?

jgromes avatar May 05 '21 08:05 jgromes

I have been happy that I can just get the values to change as I move the devices around. I still need to look into the calibration some more. I did find an additional issue: _sf should not be used as an index into calTable (line 96+). Try:

uint16_t sfindex = (_sf >> 4) - 5;
...
val = calTable[0][sfindex]
...

...I really need to get my dev environment sane again so I can just submit the PR...

miked531 avatar May 07 '21 03:05 miked531

@miked531 not sure if this is still relevant, but I went through the documentation available for SX128x ranging (mainly the datasheet, AN1200.29 and https://os.mbed.com/teams/Semtech/code/SX1280Lib/). It seems everything in RadioLib should be correct, the only issue is the calibration.

Since it seems the process is quite involved, I added the option for users to set their own calibration table.

jgromes avatar Jan 01 '23 17:01 jgromes