Receiving packets from wrong channels

I’m running the following experiment to test that I am correctly assigning and switching channels:

-2 devices.

-1 device transmits 1 packet periodically, each time on a different channel. Specifically, the device will transmit on channel 1 then 4, 5, 7, 3, and 2 before starting back over with 1. This is done continuously

  • The other device is continuously listening on ONLY channel 1.

It is my expectation that out of the repeated period (i.e running through all channels once), only one packet will be received by the receiver (on channel 1). However, the receiver is actually receiving three packets over the interval before starting over from channels 3,2,1; in that order (no packets received from 4, 5, or 7). This is despite the fact that the receiver is ONLY listening to channel 1. The code I am using to switch channels is at the end of this post (most of it was copied from the dwt_configure function with substituted variables). The code is called directly before starting to either transmit or receive (e.g. directly before calling dwt_rxenable(DWT_START_RX_IMMEDIATE))

Things I’ve tried:

  1. removed the tx power changes that the function makes. No effect.
  2. tried different devices. No effect.
  3. increased the amount of time between subsequent packet transmissions. No effect
  4. stopped changing the channels and repeated the experiment using only channel 1for both devices, only channel 2 for both devices, etc. This worked as expected. The receiver received all packets that it was supposed to (in this case, all).

I’m at a loss as to why I’m receiving on channels I’m not listening to. If anyone has an idea of what’s going on or could point me the direction of a possible solution I would much appreciate it.
My best guess at this point is that either (1) I’m misunderstanding what channels mean on this device (e.g. channels are not completely isolated from each other), (2) There is a configuration wrong somewhere in the code below, or (3) it takes a very long time to switch channels and I’m not giving the device enough time to switch before transmitting.

Configuration used (note that dwt_setsmarttxpower(0) is ran after initialization and configuration)
static dwt_config_t defaultConfig_dwConfig = {
1, // starting channel. Won’t be used, just filler
DWT_PRF_64M,
DWT_PLEN_1024,
DWT_PAC32,
9, // for tx preamble code
9, // for rx preamble code
0,
DWT_BR_110K,
DWT_PHRMODE_STD,
(4096+64)
};

Code used to change channels

typedef struct ChIndex
{
      // channel to use
      uint8_t channel_uint8;
      // preamble code
      uint8_t preambleCode_uint8;
} t_ChIndex

static t_ChIndex chIndexConfigs_ChIndexArray[] = {{.channel_uint8=1, .preambleCode_uint8=9}, {.channel_uint8=4, .preambleCode_uint8=17}, {.channel_uint8=5, .preambleCode_uint8=9}, {.channel_uint8=7, .preambleCode_uint8=17}, {.channel_uint8=3, .preambleCode_uint8=9}, {.channel_uint8=2, .preambleCode_uint8=9}};

void
App_Config_Change_Channel_Index(uint8_t channelIndex_uint8, uint8_t powerLevel_uint8)
{
      // make sure that the given channel index is valid
      if(channelIndex_uint8 >= APP_CONFIG_NUM_VALID_CHANNELS)
      {
            APP_CONFIG_ERROR(APP_CONFIG_ERROR_INVALID_CHANNEL_INDEX_HOP);
      }

      // make sure that the power level is valid
      if(powerLevel_uint8 >= 62)
      {
            APP_CONFIG_ERROR(APP_CONFIG_ERROR_INVALID_POWER_LEVEL);
      }

      // reconfigure the radio for the new channel
      uint8_t channel_uint8 = chIndexConfigs_ChIndexArray[channelIndex_uint8].channel_uint8;
      uint8_t preambleCode_uint8 = chIndexConfigs_ChIndexArray[channelIndex_uint8].preambleCode_uint8;
      dwt_write32bitoffsetreg(FS_CTRL_ID, FS_PLLCFG_OFFSET, fs_pll_cfg[chan_idx[channel_uint8]]);
      dwt_write8bitoffsetreg(FS_CTRL_ID, FS_PLLTUNE_OFFSET, fs_pll_tune[chan_idx[channel_uint8]]);
      dwt_write8bitoffsetreg(RF_CONF_ID, RF_RXCTRLH_OFFSET, rx_config[((channel_uint8 == 4) || (channel_uint8 == 7)) ? 1 : 0]);
      dwt_write32bitoffsetreg(RF_CONF_ID, RF_TXCTRL_OFFSET, tx_config[chan_idx[channel_uint8]]);
      #if APP_CONFIG_DW_SFD_TYPE
            dwt_write32bitreg(CHAN_CTRL_ID, (CHAN_CTRL_TX_CHAN_MASK & (channel_uint8 << CHAN_CTRL_TX_CHAN_SHIFT)) | 
                  (CHAN_CTRL_RX_CHAN_MASK & (channel_uint8 << CHAN_CTRL_RX_CHAN_SHIFT)) | 
                  (CHAN_CTRL_RXFPRF_MASK & (APP_CONFIG_DW_DEFAULT_PULSE_FREQ << CHAN_CTRL_RXFPRF_SHIFT)) |
                  ((CHAN_CTRL_TNSSFD|CHAN_CTRL_RNSSFD) & (3 << CHAN_CTRL_TNSSFD_SHIFT)) | 
                  (CHAN_CTRL_DWSFD & (1 << CHAN_CTRL_DWSFD_SHIFT)) | 
                  (CHAN_CTRL_TX_PCOD_MASK & (preambleCode_uint8 << CHAN_CTRL_TX_PCOD_SHIFT)) | 
                  (CHAN_CTRL_RX_PCOD_MASK & (preambleCode_uint8 << CHAN_CTRL_RX_PCOD_SHIFT))); 
      #else
            dwt_write32bitreg(CHAN_CTRL_ID, (CHAN_CTRL_TX_CHAN_MASK & (channel_uint8 << CHAN_CTRL_TX_CHAN_SHIFT)) | 
                  (CHAN_CTRL_RX_CHAN_MASK & (channel_uint8 << CHAN_CTRL_RX_CHAN_SHIFT)) | 
                  (CHAN_CTRL_RXFPRF_MASK & (APP_CONFIG_DW_DEFAULT_PULSE_FREQ << CHAN_CTRL_RXFPRF_SHIFT)) |
                  ((CHAN_CTRL_TNSSFD|CHAN_CTRL_RNSSFD) & (0 << CHAN_CTRL_TNSSFD_SHIFT)) | 
                  (CHAN_CTRL_DWSFD & (0 << CHAN_CTRL_DWSFD_SHIFT)) | 
                  (CHAN_CTRL_TX_PCOD_MASK & (preambleCode_uint8 << CHAN_CTRL_TX_PCOD_SHIFT)) | 
                  (CHAN_CTRL_RX_PCOD_MASK & (preambleCode_uint8 << CHAN_CTRL_RX_PCOD_SHIFT))); 
      #endif

      // configure the tx power levels 
      //dwt_write8bitoffsetreg(TX_CAL_ID, TC_PGDELAY_OFFSET, pgDelays_uint8Array[chan_idx[channel_uint8]]);
      //dwt_write32bitreg(TX_POWER_ID, txPowerLevels_uint32Array[powerLevel_uint8]);

      printf("%d\n", channel_uint8);
}

This app note will greatly expand your understanding of these effects:

https://www.decawave.com/wp-content/uploads/2018/10/APH010_DW1000-Inter-Channel-Interference_v1.1.pdf

[For some reason, this app note isn’t currently listed on the Decawave web site under the app notes. That does make it hard for someone to find it presently.]

Fundamentally, there is the potential to cross channels and preamble codes if the signal is strong enough.

The only sure isolation is to use a different PRF, but there are only two choices, 16 MHz and 64 MHz, so you can’t isolate more than 2 signals that way.

If you need multiple channels in operation, consider use of different preamble codes for each one. The frequency isolation plus the preamble code isolation together makes the problem mostly solvable if you can assure a separation distance greater than some minimum, basically about 10 meters or so. This is detailed in the charts in the app note.

A possible plan might be:

Ch 1, PRF 64, PC 9
Ch 2, PRF 16, PC 1
Ch 3, PRF 64, PC10

Since ch 2 interferes with ch 1 and ch 3 more easily, split it on another PRF, and then give everybody different preamble codes (PC). Ch 2 will not interfere with ch 1 and ch 3 due to PRF difference. Ch 1 to ch 3 isolation with different PC is pretty good, Table 7 shows you have to be under 2 meters to get cross reception.

I didn’t review your code, but suspect it doesn’t have a major flaw based on what you reported. The DW1000 changes channel essentially instantly, a few microseconds, much faster than any packet time would be.

Mike Ciholas, President, Ciholas, Inc
3700 Bell Road, Newburgh, IN 47630 USA
mikec@ciholas.com
+1 812 962 9408

Thank you for the document. The problem was indeed the channels crossing.