Direction finding on NRF52811

Hi!
I developed a locator and beacon pcb boards for AoA direction finding using the NRF52811 chip. The locator has 4 patch antennas and the SKY13575-639LF external antenna switch.
Software configuration for the beacon (using infocenter.nordicsemi.com/.../intro.html) :

1. Radio:

/* Enable power to RADIO */
NRF_RADIO->POWER = 1;

/* Set radio transmit power to 0dBm */
NRF_RADIO->TXPOWER = (RADIO_TXPOWER_TXPOWER_0dBm << RADIO_TXPOWER_TXPOWER_Pos);

/* Set radio mode to 1Mbit/s Bluetooth Low Energy */
NRF_RADIO->MODE = (RADIO_MODE_MODE_Ble_1Mbit << RADIO_MODE_MODE_Pos);

/* Set the requested channel */
NRF_RADIO->FREQUENCY = GetChannelFrequency(channel);

/* This value needs to correspond to the channel being used */
NRF_RADIO->DATAWHITEIV = channel;

/* Configure Access Address according to the BLE standard */
NRF_RADIO->PREFIX0 = 0x8e;
NRF_RADIO->BASE0 = 0x89bed600;

/* Use logical address 0 (prefix0 + base0) = 0x8E89BED6 when transmitting and receiving */
NRF_RADIO->TXADDRESS = 0x00;
NRF_RADIO->RXADDRESSES = 0x01;

/* PCNF-> Packet Configuration. 
* We now need to configure the sizes S0, S1 and length field to match the
* datapacket format of the advertisement packets.
*/
NRF_RADIO->PCNF0 = (
	(((1UL) << RADIO_PCNF0_S0LEN_Pos) & RADIO_PCNF0_S0LEN_Msk) |  /* Length of S0 field in bytes 0-1.    */
	(((0UL) << RADIO_PCNF0_S1LEN_Pos) & RADIO_PCNF0_S1LEN_Msk) |  /* Length of S1 field in bits 0-8.     */
	(((8UL) << RADIO_PCNF0_LFLEN_Pos) & RADIO_PCNF0_LFLEN_Msk)    /* Length of length field in bits 0-8. */
);

/* Packet configuration */
NRF_RADIO->PCNF1 = (
	(((64UL) << RADIO_PCNF1_MAXLEN_Pos) & RADIO_PCNF1_MAXLEN_Msk)   |                      /* Maximum length of payload in bytes [0-255] */
	(((0UL) << RADIO_PCNF1_STATLEN_Pos) & RADIO_PCNF1_STATLEN_Msk)   |                      /* Expand the payload with N bytes in addition to LENGTH [0-255] */
	(((3UL) << RADIO_PCNF1_BALEN_Pos) & RADIO_PCNF1_BALEN_Msk)       |                      /* Base address length in number of bytes. */
	(((RADIO_PCNF1_ENDIAN_Little) << RADIO_PCNF1_ENDIAN_Pos) & RADIO_PCNF1_ENDIAN_Msk) |  /* Endianess of the S0, LENGTH, S1 and PAYLOAD fields. */
	(((1UL) << RADIO_PCNF1_WHITEEN_Pos) & RADIO_PCNF1_WHITEEN_Msk)                         /* Enable packet whitening */
);

/* CRC config */
NRF_RADIO->CRCCNF  = (RADIO_CRCCNF_LEN_Three << RADIO_CRCCNF_LEN_Pos) |
					 (RADIO_CRCCNF_SKIPADDR_Skip << RADIO_CRCCNF_SKIPADDR_Pos); /* Skip Address when computing CRC */
NRF_RADIO->CRCINIT = 0x555555;                                                  /* Initial value of CRC */
NRF_RADIO->CRCPOLY = 0x00065B;                                                  /* CRC polynomial function */

/* Clear events */
NRF_RADIO->EVENTS_DISABLED = 0;
NRF_RADIO->EVENTS_END = 0;
NRF_RADIO->EVENTS_READY = 0;
NRF_RADIO->EVENTS_ADDRESS = 0;	
//-------------------------------------
//-------------------------------------
NRF_RADIO->PACKETPTR = (uint32_t) Packet;

// Set up AoA mode
NRF_RADIO->DFEMODE = RADIO_DFEMODE_DFEOPMODE_AoA;

NRF_RADIO->DFECTRL1 = 3 << RADIO_DFECTRL1_NUMBEROF8US_Pos | 1 << RADIO_DFECTRL1_DFEINEXTENSION_Pos;

2. Sending a packet:
NRF_RADIO->EVENTS_READY = 0U;
// Enable radio and wait for ready.
NRF_RADIO->TASKS_TXEN   = 1; 											
while (NRF_RADIO->EVENTS_READY == 0U){}

NRF_RADIO->TASKS_START = 1U;
// Start transmission.
NRF_RADIO->EVENTS_PHYEND = 0;

NRF_RADIO->EVENTS_END  = 0U;


// Wait for end of the transmission packet.	   										

while (NRF_RADIO->EVENTS_PHYEND == 0U)
{
    while (NRF_RADIO->EVENTS_END == 0U)
    {
		//counter1++;
    }
    counter2++;
} 											

NRF_RADIO->EVENTS_DISABLED = 0U;
// Disable the radio.																	
NRF_RADIO->TASKS_DISABLE   = 1U; 									
while (NRF_RADIO->EVENTS_DISABLED == 0U){} 

3. The resulting structure of the packet:

Software configuration for the locator (using infocenter.nordicsemi.com/.../intro.html):

1. Radio:

/* Enable power to RADIO */
NRF_RADIO->POWER = 1;

/* Set radio transmit power to 0dBm */
NRF_RADIO->TXPOWER = (RADIO_TXPOWER_TXPOWER_0dBm << RADIO_TXPOWER_TXPOWER_Pos);

/* Set radio mode to 1Mbit/s Bluetooth Low Energy */
NRF_RADIO->MODE = (RADIO_MODE_MODE_Ble_1Mbit << RADIO_MODE_MODE_Pos);

/* Set the requested channel */
NRF_RADIO->FREQUENCY = GetChannelFrequency(channel);

/* This value needs to correspond to the channel being used */
NRF_RADIO->DATAWHITEIV = channel;

/* Configure Access Address according to the BLE standard */
NRF_RADIO->PREFIX0 = 0x8e;
NRF_RADIO->BASE0 = 0x89bed600;

/* Use logical address 0 (prefix0 + base0) = 0x8E89BED6 when transmitting and receiving */
NRF_RADIO->TXADDRESS = 0x00;
NRF_RADIO->RXADDRESSES = 0x01;

/* PCNF-> Packet Configuration. 
* We now need to configure the sizes S0, S1 and length field to match the
* datapacket format of the advertisement packets.
*/
NRF_RADIO->PCNF0 = (
	(((1UL) << RADIO_PCNF0_S0LEN_Pos) & RADIO_PCNF0_S0LEN_Msk) |  /* Length of S0 field in bytes 0-1.    */
	(((0UL) << RADIO_PCNF0_S1LEN_Pos) & RADIO_PCNF0_S1LEN_Msk) |  /* Length of S1 field in bits 0-8.     */
	(((8UL) << RADIO_PCNF0_LFLEN_Pos) & RADIO_PCNF0_LFLEN_Msk)    /* Length of length field in bits 0-8. */
);

/* Packet configuration */
NRF_RADIO->PCNF1 = (
	(((64UL) << RADIO_PCNF1_MAXLEN_Pos) & RADIO_PCNF1_MAXLEN_Msk)   |                      /* Maximum length of payload in bytes [0-255] */
	(((0UL) << RADIO_PCNF1_STATLEN_Pos) & RADIO_PCNF1_STATLEN_Msk)   |                      /* Expand the payload with N bytes in addition to LENGTH [0-255] */
	(((3UL) << RADIO_PCNF1_BALEN_Pos) & RADIO_PCNF1_BALEN_Msk)       |                      /* Base address length in number of bytes. */
	(((RADIO_PCNF1_ENDIAN_Little) << RADIO_PCNF1_ENDIAN_Pos) & RADIO_PCNF1_ENDIAN_Msk) |  /* Endianess of the S0, LENGTH, S1 and PAYLOAD fields. */
	(((1UL) << RADIO_PCNF1_WHITEEN_Pos) & RADIO_PCNF1_WHITEEN_Msk)                         /* Enable packet whitening */
);

/* CRC config */
NRF_RADIO->CRCCNF  = (RADIO_CRCCNF_LEN_Three << RADIO_CRCCNF_LEN_Pos) |
					 (RADIO_CRCCNF_SKIPADDR_Skip << RADIO_CRCCNF_SKIPADDR_Pos); /* Skip Address when computing CRC */
NRF_RADIO->CRCINIT = 0x555555;                                                  /* Initial value of CRC */
NRF_RADIO->CRCPOLY = 0x00065B;                                                  /* CRC polynomial function */

/* Clear events */
NRF_RADIO->EVENTS_DISABLED = 0;
NRF_RADIO->EVENTS_END = 0;
NRF_RADIO->EVENTS_READY = 0;
NRF_RADIO->EVENTS_ADDRESS = 0;	
//-------------------------------------
//-------------------------------------
NRF_RADIO->PACKETPTR = (uint32_t) Packet;

// Set up AoA receiver mode
NRF_RADIO->DFEMODE |= RADIO_DFEMODE_DFEOPMODE_AoA;

NRF_RADIO->DFECTRL1 = 3 << RADIO_DFECTRL1_NUMBEROF8US_Pos | 1 << RADIO_DFECTRL1_DFEINEXTENSION_Pos;

NRF_RADIO->CTEINLINECONF |= (RADIO_CTEINLINECONF_CTEINLINECTRLEN_Enabled << RADIO_CTEINLINECONF_CTEINLINECTRLEN_Pos)
						 | (RADIO_CTEINLINECONF_CTEINFOINS1_NotInS1 << RADIO_CTEINLINECONF_CTEINFOINS1_Pos)
						 | (0x07 << RADIO_CTEINLINECONF_S0CONF_Pos)
						 | (0x0F << RADIO_CTEINLINECONF_S0MASK_Pos);

2. Antenna switching:
#define ANTENNA_SWICTH_PIN0 30
#define ANTENNA_SWICTH_PIN1 28

// Configure antenna switching every 2 µs
NRF_RADIO->DFECTRL1 |= RADIO_DFECTRL1_TSWITCHSPACING_2us << RADIO_DFECTRL1_TSWITCHSPACING_Pos;

// Antenna 1 (for data)
NRF_RADIO->SWITCHPATTERN = 0b00;
// Antenna 1 (guard/reference)
NRF_RADIO->SWITCHPATTERN = 0b00;
// Antenna 2 (guard/reference)
NRF_RADIO->SWITCHPATTERN = 0b01;
// Antenna 3 (guard/reference)
NRF_RADIO->SWITCHPATTERN = 0b10;
// Antenna 4 (guard/reference)
NRF_RADIO->SWITCHPATTERN = 0b11;


// Set up the first switching pin
NRF_RADIO->PSEL.DFEGPIO[0] = ANTENNA_SWICTH_PIN0;
// Set up the second switching pin
NRF_RADIO->PSEL.DFEGPIO[0] = ANTENNA_SWICTH_PIN1;

// Set the switch offset to zero
NRF_RADIO->DFECTRL2 |= (0 << RADIO_DFECTRL2_TSWITCHOFFSET_Pos);

// Config antenna swithcing pins as output
nrf_gpio_cfg_output(ANTENNA_SWICTH_PIN0);
nrf_gpio_cfg_output(ANTENNA_SWICTH_PIN1);
nrf_gpio_pin_write(ANTENNA_SWICTH_PIN0, 0);
nrf_gpio_pin_write(ANTENNA_SWICTH_PIN0, 0);

3. IQ samples receiving:
struct IQ_Sample
{
    int16_t I;
    int16_t Q;
};

static IQ_Sample DFE_Packet[64] = {0};


// Set the sample type to IQ format
NRF_RADIO->DFECTRL1 |= (0 << RADIO_DFECTRL1_SAMPLETYPE_Pos);
// Configure the DMA pointer to RAM
NRF_RADIO->DFEPACKET.PTR = (uint32_t) DFE_Packet;
// Configure the size of the reserved buffer
NRF_RADIO->DFEPACKET.MAXCNT = 0x1C;
// Set the sample spacing to 1 µs for the reference period
NRF_RADIO->DFECTRL1 |= (RADIO_DFECTRL1_TSAMPLESPACINGREF_1us << RADIO_DFECTRL1_TSAMPLESPACINGREF_Pos);
// Set sample offset to 1
NRF_RADIO->DFECTRL2 |= (3 << RADIO_DFECTRL2_TSAMPLEOFFSET_Pos);
// Set sample spacing to 2 µs for the sample slots with Constant Tone Extension (CTE) inline mode
NRF_RADIO->CTEINLINECONF |= (RADIO_CTEINLINECONF_CTEINLINERXMODE2US_2us << RADIO_CTEINLINECONF_CTEINLINERXMODE1US_Pos);

4. Waiting for the packet:
NRF_RADIO->EVENTS_READY = 0U;
// Enable radio and wait for ready
NRF_RADIO->TASKS_RXEN = 1U;

while (NRF_RADIO->EVENTS_READY == 0U)
{
    // wait
}
NRF_RADIO->EVENTS_END = 0U;
// Start listening and wait for address received event
NRF_RADIO->TASKS_START = 1U;

// Wait for end of packet or buttons state changed
while (NRF_RADIO->EVENTS_END == 0U)
{
    // wait
}

if (NRF_RADIO->CRCSTATUS == 1U)
{
    result = 0;
}
NRF_RADIO->EVENTS_DISABLED = 0U;
// Disable radio
NRF_RADIO->TASKS_DISABLE = 1U;

while (NRF_RADIO->EVENTS_DISABLED == 0U)
{
    // wait
}
 

5. Handling the packet:
if (NRF_RADIO->EVENTS_CTEPRESENT)
{
    NRF_RADIO->EVENTS_CTEPRESENT = 0;

    for (uint8_t i = 0; i < 4; i++)
    {
        UART_Instance.Printf("I and Q;");
        UART_Instance.Printf("%d;%d;\r\n", DFE_Packet[i].I, DFE_Packet[i].Q);
    }
}

Why the resulting IQ samples look like random values?

Related