/* / _____) _ | | ( (____ _____ ____ _| |_ _____ ____| |__ \____ \| ___ | (_ _) ___ |/ ___) _ \ _____) ) ____| | | || |_| ____( (___| | | | (______/|_____)_|_|_| \__)_____)\____)_| |_| (C)2019 Semtech Description: SX1302 RX buffer Hardware Abstraction Layer License: Revised BSD License, see LICENSE.TXT file include in the project */ /* -------------------------------------------------------------------------- */ /* --- DEPENDANCIES --------------------------------------------------------- */ #include /* C99 types */ #include /* printf fprintf */ #include /* memset */ #include /* assert */ #include "loragw_aux.h" #include "loragw_reg.h" #include "loragw_sx1302_rx.h" #include "loragw_sx1302_timestamp.h" /* -------------------------------------------------------------------------- */ /* --- PRIVATE MACROS ------------------------------------------------------- */ #define ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0])) #if DEBUG_SX1302 == 1 #define DEBUG_MSG(str) fprintf(stdout, str) #define DEBUG_PRINTF(fmt, args...) fprintf(stdout, fmt, args) #define CHECK_NULL(a) if(a==NULL){fprintf(stderr,"%s:%d: ERROR: NULL POINTER AS ARGUMENT\n", __FUNCTION__, __LINE__);return LGW_REG_ERROR;} #else #define DEBUG_MSG(str) #define DEBUG_PRINTF(fmt, args...) #define CHECK_NULL(a) if(a==NULL){return LGW_REG_ERROR;} #endif #define SX1302_PKT_PAYLOAD_LENGTH(buffer, start_index) TAKE_N_BITS_FROM(buffer[start_index + 2], 0, 8) #define SX1302_PKT_CHANNEL(buffer, start_index) TAKE_N_BITS_FROM(buffer[start_index + 3], 0, 8) #define SX1302_PKT_CRC_EN(buffer, start_index) TAKE_N_BITS_FROM(buffer[start_index + 4], 0, 1) #define SX1302_PKT_CODING_RATE(buffer, start_index) TAKE_N_BITS_FROM(buffer[start_index + 4], 1, 3) #define SX1302_PKT_DATARATE(buffer, start_index) TAKE_N_BITS_FROM(buffer[start_index + 4], 4, 4) #define SX1302_PKT_MODEM_ID(buffer, start_index) TAKE_N_BITS_FROM(buffer[start_index + 5], 0, 8) #define SX1302_PKT_FREQ_OFFSET_7_0(buffer, start_index) TAKE_N_BITS_FROM(buffer[start_index + 6], 0, 8) #define SX1302_PKT_FREQ_OFFSET_15_8(buffer, start_index) TAKE_N_BITS_FROM(buffer[start_index + 7], 0, 8) #define SX1302_PKT_FREQ_OFFSET_19_16(buffer, start_index) TAKE_N_BITS_FROM(buffer[start_index + 8], 0, 4) #define SX1302_PKT_CRC_ERROR(buffer, start_index) TAKE_N_BITS_FROM(buffer[start_index + 9], 0, 1) #define SX1302_PKT_SYNC_ERROR(buffer, start_index) TAKE_N_BITS_FROM(buffer[start_index + 9], 2, 1) #define SX1302_PKT_HEADER_ERROR(buffer, start_index) TAKE_N_BITS_FROM(buffer[start_index + 9], 3, 1) #define SX1302_PKT_TIMING_SET(buffer, start_index) TAKE_N_BITS_FROM(buffer[start_index + 9], 4, 1) #define SX1302_PKT_SNR_AVG(buffer, start_index) TAKE_N_BITS_FROM(buffer[start_index + 10], 0, 8) #define SX1302_PKT_RSSI_CHAN(buffer, start_index) TAKE_N_BITS_FROM(buffer[start_index + 11], 0, 8) #define SX1302_PKT_RSSI_SIG(buffer, start_index) TAKE_N_BITS_FROM(buffer[start_index + 12], 0, 8) #define SX1302_PKT_RSSI_CHAN_MAX_NEG_DELTA(buffer, start_index) TAKE_N_BITS_FROM(buffer[start_index + 13], 0, 4) #define SX1302_PKT_RSSI_CHAN_MAX_POS_DELTA(buffer, start_index) TAKE_N_BITS_FROM(buffer[start_index + 13], 4, 4) #define SX1302_PKT_RSSI_SIG_MAX_NEG_DELTA(buffer, start_index) TAKE_N_BITS_FROM(buffer[start_index + 14], 0, 4) #define SX1302_PKT_RSSI_SIG_MAX_POS_DELTA(buffer, start_index) TAKE_N_BITS_FROM(buffer[start_index + 14], 4, 4) #define SX1302_PKT_TIMESTAMP_7_0(buffer, start_index) TAKE_N_BITS_FROM(buffer[start_index + 15], 0, 8) #define SX1302_PKT_TIMESTAMP_15_8(buffer, start_index) TAKE_N_BITS_FROM(buffer[start_index + 16], 0, 8) #define SX1302_PKT_TIMESTAMP_23_16(buffer, start_index) TAKE_N_BITS_FROM(buffer[start_index + 17], 0, 8) #define SX1302_PKT_TIMESTAMP_31_24(buffer, start_index) TAKE_N_BITS_FROM(buffer[start_index + 18], 0, 8) #define SX1302_PKT_CRC_PAYLOAD_7_0(buffer, start_index) TAKE_N_BITS_FROM(buffer[start_index + 19], 0, 8) #define SX1302_PKT_CRC_PAYLOAD_15_8(buffer, start_index) TAKE_N_BITS_FROM(buffer[start_index + 20], 0, 8) #define SX1302_PKT_NUM_TS_METRICS(buffer, start_index) TAKE_N_BITS_FROM(buffer[start_index + 21], 0, 8) /* -------------------------------------------------------------------------- */ /* --- PRIVATE TYPES -------------------------------------------------------- */ /* -------------------------------------------------------------------------- */ /* --- PRIVATE CONSTANTS ---------------------------------------------------- */ /* RX buffer packet structure */ #define SX1302_PKT_SYNCWORD_BYTE_0 0xA5 #define SX1302_PKT_SYNCWORD_BYTE_1 0xC0 #define SX1302_PKT_HEAD_METADATA 9 #define SX1302_PKT_TAIL_METADATA 14 /* modem IDs */ #define SX1302_LORA_MODEM_ID_MAX 15 #define SX1302_LORA_STD_MODEM_ID 16 #define SX1302_FSK_MODEM_ID 17 /* -------------------------------------------------------------------------- */ /* --- PRIVATE VARIABLES ---------------------------------------------------- */ /* -------------------------------------------------------------------------- */ /* --- PRIVATE FUNCTIONS DECLARATION ---------------------------------------- */ /* -------------------------------------------------------------------------- */ /* --- PRIVATE FUNCTIONS DEFINITION ----------------------------------------- */ /* -------------------------------------------------------------------------- */ /* --- PUBLIC FUNCTIONS DEFINITION ------------------------------------------ */ int rx_buffer_new(rx_buffer_t * self) { /* Check input params */ CHECK_NULL(self); /* Initialize members */ memset(self->buffer, 0, sizeof self->buffer); self->buffer_size = 0; self->buffer_index = 0; self->buffer_pkt_nb = 0; return LGW_REG_SUCCESS; } /* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ int rx_buffer_del(rx_buffer_t * self) { /* Check input params */ CHECK_NULL(self); /* Reset index & size */ self->buffer_size = 0; self->buffer_index = 0; self->buffer_pkt_nb = 0; return LGW_REG_SUCCESS; } /* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ int rx_buffer_fetch(rx_buffer_t * self) { int i, res; uint8_t buff[2]; uint8_t payload_len; uint16_t next_pkt_idx; int idx; uint16_t nb_bytes_1, nb_bytes_2; /* Check input params */ CHECK_NULL(self); /* Check if there is data in the FIFO */ lgw_reg_rb(SX1302_REG_RX_TOP_RX_BUFFER_NB_BYTES_MSB_RX_BUFFER_NB_BYTES, buff, sizeof buff); nb_bytes_1 = (buff[0] << 8) | (buff[1] << 0); /* Workaround for multi-byte read issue: read again and ensure new read is not lower than the previous one */ lgw_reg_rb(SX1302_REG_RX_TOP_RX_BUFFER_NB_BYTES_MSB_RX_BUFFER_NB_BYTES, buff, sizeof buff); nb_bytes_2 = (buff[0] << 8) | (buff[1] << 0); self->buffer_size = (nb_bytes_2 > nb_bytes_1) ? nb_bytes_2 : nb_bytes_1; /* Fetch bytes from fifo if any */ if (self->buffer_size > 0) { DEBUG_MSG ("-----------------\n"); DEBUG_PRINTF("%s: nb_bytes to be fetched: %u (%u %u)\n", __FUNCTION__, self->buffer_size, buff[1], buff[0]); memset(self->buffer, 0, sizeof self->buffer); res = lgw_mem_rb(0x4000, self->buffer, self->buffer_size, true); if (res != LGW_REG_SUCCESS) { printf("ERROR: Failed to read RX buffer, SPI error\n"); return LGW_REG_ERROR; } /* print debug info */ DEBUG_MSG("RX_BUFFER: "); for (i = 0; i < self->buffer_size; i++) { DEBUG_PRINTF("%02X ", self->buffer[i]); } DEBUG_MSG("\n"); /* Sanity check: is there at least 1 complete packet in the buffer */ if (self->buffer_size < (SX1302_PKT_HEAD_METADATA + SX1302_PKT_TAIL_METADATA)) { printf("WARNING: not enough data to have a complete packet, discard rx_buffer\n"); return rx_buffer_del(self); } /* Sanity check: is there a syncword at 0 ? If not, move to the first syncword found */ idx = 0; while (idx <= (self->buffer_size - 2)) { if ((self->buffer[idx] == SX1302_PKT_SYNCWORD_BYTE_0) && (self->buffer[idx + 1] == SX1302_PKT_SYNCWORD_BYTE_1)) { DEBUG_PRINTF("INFO: syncword found at idx %d\n", idx); break; } else { printf("INFO: syncword not found at idx %d\n", idx); idx += 1; } } if (idx > self->buffer_size - 2) { printf("WARNING: no syncword found, discard rx_buffer\n"); return rx_buffer_del(self); } if (idx != 0) { printf("INFO: re-sync rx_buffer at idx %d\n", idx); memmove((void *)(self->buffer), (void *)(self->buffer + idx), self->buffer_size - idx); self->buffer_size -= idx; } /* Rewind and parse buffer to get the number of packet fetched */ idx = 0; while (idx < self->buffer_size) { if ((self->buffer[idx] != SX1302_PKT_SYNCWORD_BYTE_0) || (self->buffer[idx + 1] != SX1302_PKT_SYNCWORD_BYTE_1)) { printf("WARNING: syncword not found at idx %d, discard the rx_buffer\n", idx); return rx_buffer_del(self); } /* One packet found in the buffer */ self->buffer_pkt_nb += 1; /* Compute the number of bytes for this packet */ payload_len = SX1302_PKT_PAYLOAD_LENGTH(self->buffer, idx); next_pkt_idx = SX1302_PKT_HEAD_METADATA + payload_len + SX1302_PKT_TAIL_METADATA + (2 * SX1302_PKT_NUM_TS_METRICS(self->buffer, idx + payload_len)); /* Move to next packet */ idx += (int)next_pkt_idx; } } /* Initialize the current buffer index to iterate on */ self->buffer_index = 0; return LGW_REG_SUCCESS; } /* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ int rx_buffer_pop(rx_buffer_t * self, rx_packet_t * pkt) { int i; uint8_t checksum_rcv, checksum_calc = 0; uint16_t checksum_idx; uint16_t pkt_num_bytes; /* Check input params */ CHECK_NULL(self); CHECK_NULL(pkt); /* Is there any data to be parsed ? */ if (self->buffer_index >= self->buffer_size) { DEBUG_MSG("INFO: No more data to be parsed\n"); return LGW_REG_ERROR; } /* Get pkt sync words */ if ((self->buffer[self->buffer_index] != SX1302_PKT_SYNCWORD_BYTE_0) || (self->buffer[self->buffer_index + 1] != SX1302_PKT_SYNCWORD_BYTE_1)) { return LGW_REG_ERROR; } DEBUG_PRINTF("INFO: pkt syncword found at index %u\n", self->buffer_index); /* Get payload length */ pkt->rxbytenb_modem = SX1302_PKT_PAYLOAD_LENGTH(self->buffer, self->buffer_index); /* Get fine timestamp metrics */ pkt->num_ts_metrics_stored = SX1302_PKT_NUM_TS_METRICS(self->buffer, self->buffer_index + pkt->rxbytenb_modem); /* Calculate the total number of bytes in the packet */ pkt_num_bytes = SX1302_PKT_HEAD_METADATA + pkt->rxbytenb_modem + SX1302_PKT_TAIL_METADATA + (2 * pkt->num_ts_metrics_stored); /* Check if we have a complete packet in the rx buffer fetched */ if((self->buffer_index + pkt_num_bytes) > self->buffer_size) { printf("WARNING: aborting truncated message (size=%u)\n", self->buffer_size); return LGW_REG_WARNING; } /* Get the checksum as received in the RX buffer */ checksum_idx = pkt_num_bytes - 1; checksum_rcv = self->buffer[self->buffer_index + pkt_num_bytes - 1]; /* Calculate the checksum from the actual payload bytes received */ for (i = 0; i < (int)checksum_idx; i++) { checksum_calc += self->buffer[self->buffer_index + i]; } /* Check if the checksum is correct */ if (checksum_rcv != checksum_calc) { printf("WARNING: checksum failed (got:0x%02X calc:0x%02X)\n", checksum_rcv, checksum_calc); return LGW_REG_WARNING; } else { DEBUG_PRINTF("Packet checksum OK (0x%02X)\n", checksum_rcv); } /* Parse packet metadata */ pkt->modem_id = SX1302_PKT_MODEM_ID(self->buffer, self->buffer_index); pkt->rx_channel_in = SX1302_PKT_CHANNEL(self->buffer, self->buffer_index); pkt->crc_en = SX1302_PKT_CRC_EN(self->buffer, self->buffer_index); pkt->payload_crc_error = SX1302_PKT_CRC_ERROR(self->buffer, self->buffer_index + pkt->rxbytenb_modem); pkt->sync_error = SX1302_PKT_SYNC_ERROR(self->buffer, self->buffer_index + pkt->rxbytenb_modem); pkt->header_error = SX1302_PKT_HEADER_ERROR(self->buffer, self->buffer_index + pkt->rxbytenb_modem); pkt->timing_set = SX1302_PKT_TIMING_SET(self->buffer, self->buffer_index + pkt->rxbytenb_modem); pkt->coding_rate = SX1302_PKT_CODING_RATE(self->buffer, self->buffer_index); pkt->rx_rate_sf = SX1302_PKT_DATARATE(self->buffer, self->buffer_index); pkt->rssi_chan_avg = SX1302_PKT_RSSI_CHAN(self->buffer, self->buffer_index + pkt->rxbytenb_modem); pkt->rssi_signal_avg = SX1302_PKT_RSSI_SIG(self->buffer, self->buffer_index + pkt->rxbytenb_modem); pkt->rx_crc16_value = (uint16_t)((SX1302_PKT_CRC_PAYLOAD_7_0(self->buffer, self->buffer_index + pkt->rxbytenb_modem) << 0) & 0x00FF); pkt->rx_crc16_value |= (uint16_t)((SX1302_PKT_CRC_PAYLOAD_15_8(self->buffer, self->buffer_index + pkt->rxbytenb_modem) << 8) & 0xFF00); pkt->snr_average = (int8_t)SX1302_PKT_SNR_AVG(self->buffer, self->buffer_index + pkt->rxbytenb_modem); pkt->frequency_offset_error = (int32_t)((SX1302_PKT_FREQ_OFFSET_19_16(self->buffer, self->buffer_index) << 16) | (SX1302_PKT_FREQ_OFFSET_15_8(self->buffer, self->buffer_index) << 8) | (SX1302_PKT_FREQ_OFFSET_7_0(self->buffer, self->buffer_index) << 0)); if (pkt->frequency_offset_error >= (1<<19)) { /* Handle signed value on 20bits */ pkt->frequency_offset_error = (pkt->frequency_offset_error - (1<<20)); } /* Packet timestamp (32MHz ) */ pkt->timestamp_cnt = (uint32_t)((SX1302_PKT_TIMESTAMP_7_0(self->buffer, self->buffer_index + pkt->rxbytenb_modem) << 0) & 0x000000FF); pkt->timestamp_cnt |= (uint32_t)((SX1302_PKT_TIMESTAMP_15_8(self->buffer, self->buffer_index + pkt->rxbytenb_modem) << 8) & 0x0000FF00); pkt->timestamp_cnt |= (uint32_t)((SX1302_PKT_TIMESTAMP_23_16(self->buffer, self->buffer_index + pkt->rxbytenb_modem) << 16) & 0x00FF0000); pkt->timestamp_cnt |= (uint32_t)((SX1302_PKT_TIMESTAMP_31_24(self->buffer, self->buffer_index + pkt->rxbytenb_modem) << 24) & 0xFF000000); /* TS metrics: it is expected the nb_symbols parameter is set to 0 here */ for (i = 0; i < (pkt->num_ts_metrics_stored * 2); i++) { pkt->timestamp_avg[i] = (int8_t)SX1302_PKT_NUM_TS_METRICS(self->buffer, self->buffer_index + pkt->rxbytenb_modem + 1 + i); pkt->timestamp_stddev[i] = 0; /* no stddev when nb_symbols == 0 */ } DEBUG_MSG ("-----------------\n"); DEBUG_PRINTF(" modem: %u\n", pkt->modem_id); DEBUG_PRINTF(" chan: %u\n", pkt->rx_channel_in); DEBUG_PRINTF(" size: %u\n", pkt->rxbytenb_modem); DEBUG_PRINTF(" crc_en: %u\n", pkt->crc_en); DEBUG_PRINTF(" crc_err: %u\n", pkt->payload_crc_error); DEBUG_PRINTF(" sync_err: %u\n", pkt->sync_error); DEBUG_PRINTF(" hdr_err: %u\n", pkt->header_error); DEBUG_PRINTF(" timing_set: %u\n", pkt->timing_set); DEBUG_PRINTF(" codr: %u\n", pkt->coding_rate); DEBUG_PRINTF(" datr: %u\n", pkt->rx_rate_sf); DEBUG_PRINTF(" num_ts: %u\n", pkt->num_ts_metrics_stored); if (pkt->num_ts_metrics_stored > 0) { DEBUG_MSG(" ts_avg: "); for (i = 0; i < (pkt->num_ts_metrics_stored * 2); i++) { DEBUG_PRINTF("%d ", pkt->timestamp_avg[i]); } DEBUG_MSG("\n"); DEBUG_MSG(" ts_stdev: NONE (nb_symbols=0)\n"); } DEBUG_MSG ("-----------------\n"); /* Sanity checks: check the range of few metadata */ if (pkt->modem_id > SX1302_FSK_MODEM_ID) { printf("ERROR: modem_id is out of range - %u\n", pkt->modem_id); return LGW_REG_ERROR; } else { if (pkt->modem_id <= SX1302_LORA_STD_MODEM_ID) { /* LoRa modems */ if (pkt->rx_channel_in > 9) { printf("ERROR: channel is out of range - %u\n", pkt->rx_channel_in); return LGW_REG_ERROR; } if ((pkt->rx_rate_sf < 5) || (pkt->rx_rate_sf > 12)) { printf("ERROR: SF is out of range - %u\n", pkt->rx_rate_sf); return LGW_REG_ERROR; } } else { /* FSK modem */ /* TODO: not checked */ } } /* Parse & copy payload in packet struct */ memcpy((void *)pkt->payload, (void *)(&(self->buffer[self->buffer_index + SX1302_PKT_HEAD_METADATA])), pkt->rxbytenb_modem); /* Move buffer index toward next message */ self->buffer_index += (SX1302_PKT_HEAD_METADATA + pkt->rxbytenb_modem + SX1302_PKT_TAIL_METADATA + (2 * pkt->num_ts_metrics_stored)); /* Update the number of packets currently stored in the rx_buffer */ self->buffer_pkt_nb -= 1; return LGW_REG_SUCCESS; } /* -------------------------------------------------------------------------- */ /* --- DEBUG FUNCTIONS DEFINITION ------------------------------------------- */ uint16_t rx_buffer_read_ptr_addr(void) { int32_t val; uint16_t addr; lgw_reg_r(SX1302_REG_RX_TOP_RX_BUFFER_LAST_ADDR_READ_MSB_LAST_ADDR_READ, &val); /* mandatory to read MSB first */ addr = (uint16_t)(val << 8); lgw_reg_r(SX1302_REG_RX_TOP_RX_BUFFER_LAST_ADDR_READ_LSB_LAST_ADDR_READ, &val); addr |= (uint16_t)val; return addr; } /* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ uint16_t rx_buffer_write_ptr_addr(void) { int32_t val; uint16_t addr; lgw_reg_r(SX1302_REG_RX_TOP_RX_BUFFER_LAST_ADDR_WRITE_MSB_LAST_ADDR_WRITE, &val); /* mandatory to read MSB first */ addr = (uint16_t)(val << 8); lgw_reg_r(SX1302_REG_RX_TOP_RX_BUFFER_LAST_ADDR_WRITE_LSB_LAST_ADDR_WRITE, &val); addr |= (uint16_t)val; return addr; } /* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ void rx_buffer_dump(FILE * file, uint16_t start_addr, uint16_t end_addr) { int i; uint8_t rx_buffer_debug[4096]; printf("Dumping %u bytes, from 0x%X to 0x%X\n", end_addr - start_addr + 1, start_addr, end_addr); memset(rx_buffer_debug, 0, sizeof rx_buffer_debug); lgw_reg_w(SX1302_REG_RX_TOP_RX_BUFFER_DIRECT_RAM_IF, 1); lgw_mem_rb(0x4000 + start_addr, rx_buffer_debug, end_addr - start_addr + 1, false); lgw_reg_w(SX1302_REG_RX_TOP_RX_BUFFER_DIRECT_RAM_IF, 0); for (i = 0; i < (end_addr - start_addr + 1); i++) { if (file == NULL) { printf("%02X ", rx_buffer_debug[i]); } else { fprintf(file, "%02X ", rx_buffer_debug[i]); } } if (file == NULL) { printf("\n"); } else { fprintf(file, "\n"); } /* Switching to direct-access memory could lead to corruption, so to be done only for debugging */ assert(0); } /* --- EOF ------------------------------------------------------------------ */