Receive CIR Data issue

Hello! I’d like to ask about the sequence of reading CIR data for calculating why I cannot accurately capture the values at the First Path and Peak Path. My approach involves calculating the amplitude directly from the CIR values on the board. Afterward, I plot the graph using Python by reading the data from the board’s Serial Monitor. The graph looks like this

I chose to collect 3968 + 1 values (reserving one for the dummy byte) at PRF 16 MHz.

Here is the relevant part of the ESP32 code:

#include <SPI.h>
#include <DW1000Ranging.h>
#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
#include <DW1000.h>
#include <DW1000Time.h>

// Device configuration
#define ANCHOR_ADD “11:11:11:11:11:11:11:11” // Anchor Address
#define TAG_ADD “12:12:12:12:12:12:12:12” // Tag Address

// Pin definitions
#define SPI_SCK 18
#define SPI_MISO 19
#define SPI_MOSI 23

#define UWB_RST 27
#define UWB_IRQ 34
#define UWB_SS 21

#define I2C_SDA 4
#define I2C_SCL 5

#define ACC_MEM_ADDRESS 0x25
#define ACCUMULATOR_SIZE (3968 + 1) // 3968 samples + 1 dummy byte
#define SPI_MAX_SPEED 115200

// Display initialization
Adafruit_SSD1306 display(128, 64, &Wire, -1);
SPISettings spiSettings(SPI_MAX_SPEED, MSBFIRST, SPI_MODE0);

// Global buffer for CIR data
uint8_t cirBuffer[ACCUMULATOR_SIZE];

void setup() {
Serial.begin(115200);
Wire.begin(I2C_SDA, I2C_SCL);
delay(1000);

// Initialize display
if (!display.begin(SSD1306_SWITCHCAPVCC, 0x3C)) {
    Serial.println(F("SSD1306 allocation failed"));
    while (true);
}
display.clearDisplay();
displayLogo();

// Initialize SPI and UWB
SPI.begin(SPI_SCK, SPI_MISO, SPI_MOSI);
DW1000Ranging.initCommunication(UWB_RST, UWB_SS, UWB_IRQ);

DW1000Ranging.attachNewRange(newRange);
DW1000Ranging.attachBlinkDevice(newBlink);
DW1000Ranging.attachInactiveDevice(inactiveDevice);

DW1000Ranging.startAsAnchor(ANCHOR_ADD, DW1000.MODE_SHORTDATA_FAST_LOWPOWER, false);

}

void loop() {
DW1000Ranging.loop();
}

// Callback for a new range event
void newRange() {
// Send start marker
for (int i = 0; i < 10; i++) {
Serial.write(0xFF);
}

// Calculate and send ToF
double tof_ns = calculateToF();
Serial.print("ToF (ns): ");
Serial.println(tof_ns, 4);

// Read CIR data
enableCIRClock();
readCIRData();
disableCIRClock();

// Send end marker
for (int i = 0; i < 10; i++) {
    Serial.write(0xFF);
}

delay(1000); // Optional delay

}

// Calculate time of flight (ToF)
double calculateToF() {
DW1000Time txTime, rxTime;
DW1000.getTransmitTimestamp(txTime);
DW1000.getReceiveTimestamp(rxTime);

uint64_t tof_units = rxTime.getTimestamp() - txTime.getTimestamp();
double tof_ns = (tof_units * (1.0 / 499.2e6) * 1e9) / 2; // Convert to nanoseconds
return tof_ns;

}

// Enable CIR clock
void enableCIRClock() {
uint8_t reg[2];
DW1000.readBytes(PMSC, PMSC_CTRL0_SUB, reg, 2);
reg[0] = 0x48 | (reg[0] & 0xb3);
reg[1] = 0x80 | reg[1];
DW1000.writeBytes(PMSC, PMSC_CTRL0_SUB, reg, 2);
}

// Disable CIR clock
void disableCIRClock() {
uint8_t reg[2];
DW1000.readBytes(PMSC, PMSC_CTRL0_SUB, reg, 2);
reg[0] &= 0xb3;
reg[1] &= 0x7f;
DW1000.writeBytes(PMSC, PMSC_CTRL0_SUB, reg, 2);
}

// Read CIR data and calculate amplitude
void readCIRData() {
const int sampleSize = 4; // 1 sample = 4 bytes (real + imag)
const uint16_t num_samples = (ACCUMULATOR_SIZE - 1) / sampleSize;

// Read raw CIR data
DW1000.readBytes(ACC_MEM_ADDRESS, 0, cirBuffer, ACCUMULATOR_SIZE);

for (uint16_t i = 0; i < num_samples; i++) {
    uint16_t offset = (i * sampleSize) + 1;

    // Convert to signed values
    int16_t real = (cirBuffer[offset + 1] << 8) | cirBuffer[offset];
    int16_t imag = (cirBuffer[offset + 3] << 8) | cirBuffer[offset + 2];

    // Compute amplitude using SQRT approximation
    int16_t abs_real = abs(real);
    int16_t abs_imag = abs(imag);
    float amplitude = max(abs_real, abs_imag) + 0.25 * min(abs_real, abs_imag);

    // Print amplitude for debugging
    Serial.print("Amplitude[");
    Serial.print(i);
    Serial.print("]: ");
    Serial.println(amplitude, 6);
}

}

// Display logo on OLED
void displayLogo() {
display.clearDisplay();
display.setTextSize(2);
display.setTextColor(SSD1306_WHITE);
display.setCursor(0, 0);
display.println(F(“Makerfabs”));
display.println(F(“UWB Anchor”));
display.setTextSize(1);
display.setCursor(0, 40);
display.println(ANCHOR_ADD);
display.display();
}

// Blink detection callback
void newBlink(DW1000Device *device) {
Serial.print("Blink from Tag detected! Device added → ");
Serial.print("Short Address: ");
Serial.println(device->getShortAddress(), HEX);

if (String(device->getShortAddress(), HEX) == TAG_ADD) {
    Serial.println("Tag connected successfully!");
}

}

// Inactive device callback
void inactiveDevice(DW1000Device *device) {
Serial.print("Delete inactive device: ");
Serial.println(device->getShortAddress(), HEX);
}

I would appreciate your advice on this. Thank you!

Your values are all very small. Not sure if it will help but make sure it’s promoting the data types correctly in void readCIRData()

    int16_t real = (((int16_t)cirBuffer[offset + 1] )<< 8) | cirBuffer[offset];
    int16_t imag = (((int16_t)cirBuffer[offset + 3]) << 8) | cirBuffer[offset + 2];

and then you are already spending ages outputting things over serial so the hit of calculating amplitude correctly probably won’t make much difference

    float amplitude = sqrt((int32_t)real *real  + (int32_t)imag*imag);

Also please use preformated text mode when posting code, it makes it so much more readable.

I would like to know how to adjust and improve the process so that the plot can correctly identify the First Path and Peak Path. Which areas should I focus on for improvement, and should I apply additional filters? The values seem incorrect, even though the positions appear to be accurate.

Unless it’s changed from what you first posted your plot is not correct, the first path peak should be obvious by looking at the plot. If it isn’t then the first task is to figure out why the plot isn’t showing the correct data.
Your peak amplitude is under 60, it should be in the thousands.

From the code you provided, the amplitude values now exceed 1000.