diff options
author | Harsh Sharma <92harshsharma@gmail.com> | 2018-06-13 13:24:54 -0500 |
---|---|---|
committer | Harsh Sharma <92harshsharma@gmail.com> | 2018-06-13 13:24:54 -0500 |
commit | 7c383be1542368f2601015d9fc2a417197677677 (patch) | |
tree | bc06453f879cbadf65fd88123c506956403c5684 /libloragw/tst | |
download | lora_gateway_mtac_full-7c383be1542368f2601015d9fc2a417197677677.tar.gz lora_gateway_mtac_full-7c383be1542368f2601015d9fc2a417197677677.tar.bz2 lora_gateway_mtac_full-7c383be1542368f2601015d9fc2a417197677677.zip |
Initial Commit
Diffstat (limited to 'libloragw/tst')
-rw-r--r-- | libloragw/tst/test_loragw_cal.c | 756 | ||||
-rw-r--r-- | libloragw/tst/test_loragw_gps.c | 288 | ||||
-rw-r--r-- | libloragw/tst/test_loragw_hal.c | 416 | ||||
-rw-r--r-- | libloragw/tst/test_loragw_reg.c | 134 | ||||
-rw-r--r-- | libloragw/tst/test_loragw_spi.c | 85 |
5 files changed, 1679 insertions, 0 deletions
diff --git a/libloragw/tst/test_loragw_cal.c b/libloragw/tst/test_loragw_cal.c new file mode 100644 index 0000000..533d189 --- /dev/null +++ b/libloragw/tst/test_loragw_cal.c @@ -0,0 +1,756 @@ +/* + / _____) _ | | +( (____ _____ ____ _| |_ _____ ____| |__ + \____ \| ___ | (_ _) ___ |/ ___) _ \ + _____) ) ____| | | || |_| ____( (___| | | | +(______/|_____)_|_|_| \__)_____)\____)_| |_| + (C)2013 Semtech-Cycleo + +Description: + Minimum test program for the loragw_hal 'library' + +License: Revised BSD License, see LICENSE.TXT file include in the project +Maintainer: Sylvain Miermont +*/ + + +/* -------------------------------------------------------------------------- */ +/* --- DEPENDANCIES --------------------------------------------------------- */ + +/* fix an issue between POSIX and C99 */ +#if __STDC_VERSION__ >= 199901L + #define _XOPEN_SOURCE 600 +#else + #define _XOPEN_SOURCE 500 +#endif + +#include <stdint.h> /* C99 types */ +#include <stdbool.h> /* bool type */ +#include <stdio.h> /* printf */ +#include <string.h> /* memset */ +#include <signal.h> /* sigaction */ +#include <math.h> /* cos */ +#include <unistd.h> /* getopt access */ + +#include "loragw_hal.h" +#include "loragw_reg.h" +#include "loragw_aux.h" +#include "loragw_radio.h" + +/* -------------------------------------------------------------------------- */ +/* --- PRIVATE MACROS ------------------------------------------------------- */ + +#define ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0])) + +/* -------------------------------------------------------------------------- */ +/* --- PRIVATE CONSTANTS ---------------------------------------------------- */ + +#define DEFAULT_TX_NOTCH_FREQ 129E3 +#define DEFAULT_RSSI_OFFSET 0.0 +#define NB_CAL_MAX 100 +#define MCU_AGC 1 +#define MCU_AGC_FW_BYTE 8192 /* size of the firmware IN BYTES (= twice the number of 14b words) */ +#define FW_VERSION_ADDR 0x20 +#define FW_VERSION_CAL 2 +#define RAM_SIZE 4096 +#define FREQ_SIG_NORM 0.078125 + +/* -------------------------------------------------------------------------- */ +/* --- PRIVATE VARIABLES ---------------------------------------------------- */ + +#include "../src/cal_fw.var" /* external definition of the variable */ + +/* -------------------------------------------------------------------------- */ +/* --- PRIVATE TYPES --------------------------------------------------------- */ + +struct cal_res_s { + int8_t amp_a; + int8_t phi_a; + int8_t amp_b; + int8_t phi_b; + int8_t offset_i_a [8]; + int8_t offset_q_a [8]; + int8_t offset_i_b [8]; + int8_t offset_q_b [8]; + uint8_t img_rej_a; + uint8_t img_rej_b; + uint8_t offset_rej_a [8]; + uint8_t offset_rej_b [8]; + uint8_t debug [8]; +}; + +/* -------------------------------------------------------------------------- */ +/* --- PRIVATE FUNCTIONS DECLARATION ---------------------------------------- */ + +int load_firmware(uint8_t target, uint8_t *firmware, uint16_t size); /* defined in loragw_hal.c */ + +uint8_t sx125x_cal(uint8_t cal_cmd, struct cal_res_s *cal_res); + +int read_capture(int16_t *i, int16_t *q, int nb_samp); + +uint8_t get_img_rej(int16_t *sig_i, int16_t *sig_q, int nb_samp, double f_sig_norm); + +void usage (void); + +/* -------------------------------------------------------------------------- */ +/* --- PRIVATE FUNCTIONS DEFINITION ----------------------------------------- */ + +/* describe command line options */ +void usage(void) { + printf("Library version information: %s\n", lgw_version_info()); + printf( "Available options:\n"); + printf( " -h print this help\n"); + printf( " -a <float> Radio A frequency in MHz\n"); + printf( " -b <float> Radio B frequency in MHz\n"); + printf( " -r <int> Radio type (SX1255:1255, SX1257:1257)\n"); + printf( " -n <uint> Number of calibration iterations\n"); + printf( " -k <int> Concentrator clock source (0:radio_A, 1:radio_B(default))\n"); + printf( " -t <int> Radio to run TX calibration on (0:None(default), 1:radio_A, 2:radio_B, 3:both)\n"); +} + +/* -------------------------------------------------------------------------- */ +/* --- MAIN FUNCTION -------------------------------------------------------- */ + +int main(int argc, char **argv) +{ + int i, j, x; + int32_t read_val; + struct lgw_conf_board_s boardconf; + struct lgw_conf_rxrf_s rfconf; + uint8_t fw_version; + uint8_t cal_cmd; + uint8_t cal_status; + struct cal_res_s cal_res [NB_CAL_MAX]; + struct cal_res_s cal_res_max; + struct cal_res_s cal_res_min; + int16_t sig_i [RAM_SIZE]; + int16_t sig_q [RAM_SIZE]; + uint8_t img_rej_a [NB_CAL_MAX]; + uint8_t img_rej_b [NB_CAL_MAX]; + uint8_t img_rej_a_max; + uint8_t img_rej_a_min; + uint8_t img_rej_b_max; + uint8_t img_rej_b_min; + //FILE *file; + + /* command line options */ + int xi = 0; + double xd = 0.0; + uint32_t fa = 0, fb = 0; + enum lgw_radio_type_e radio_type = LGW_RADIO_TYPE_NONE; + uint8_t clocksource = 1; /* Radio B is source by default */ + uint8_t tx_enable = 0; + int nb_cal = 5; + + /* parse command line options */ + while ((i = getopt (argc, argv, "ha:b:r:n:k:t:")) != -1) { + switch (i) { + case 'h': + usage(); + return -1; + break; + case 'a': /* <float> Radio A frequency in MHz */ + sscanf(optarg, "%lf", &xd); + fa = (uint32_t)((xd*1e6) + 0.5); /* .5 Hz offset to get rounding instead of truncating */ + break; + case 'b': /* <float> Radio B frequency in MHz */ + sscanf(optarg, "%lf", &xd); + fb = (uint32_t)((xd*1e6) + 0.5); /* .5 Hz offset to get rounding instead of truncating */ + break; + case 'r': /* <int> Radio type (1255, 1257) */ + sscanf(optarg, "%i", &xi); + switch (xi) { + case 1255: + radio_type = LGW_RADIO_TYPE_SX1255; + break; + case 1257: + radio_type = LGW_RADIO_TYPE_SX1257; + break; + default: + printf("ERROR: invalid radio type\n"); + usage(); + return -1; + } + break; + case 'n': /* <uint> Number of calibration iterations */ + i = sscanf(optarg, "%i", &xi); + if ((i != 1) || (xi > NB_CAL_MAX)) { + printf("ERROR: invalid number of calibration iterations (MAX %d)\n",NB_CAL_MAX); + usage(); + return -1; + } else { + nb_cal = xi; + } + break; + case 'k': /* <int> Concentrator clock source (Radio A or Radio B) */ + sscanf(optarg, "%i", &xi); + clocksource = (uint8_t)xi; + break; + case 't': /* <int> Radio to run TX calibration on */ + sscanf(optarg, "%i", &xi); + tx_enable = (uint8_t)xi; + break; + default: + printf("ERROR: argument parsing\n"); + usage(); + return -1; + } + } + + /* check input parameters */ + if ((fa == 0) || (fb == 0)) { + printf("ERROR: missing frequency input parameter:\n"); + printf(" Radio A RX: %u\n", fa); + printf(" Radio B RX: %u\n", fb); + usage(); + return -1; + } + + if (radio_type == LGW_RADIO_TYPE_NONE) { + printf("ERROR: missing radio type parameter:\n"); + usage(); + return -1; + } + + /* starting the concentrator */ + /* board config */ + memset(&boardconf, 0, sizeof(boardconf)); + + boardconf.lorawan_public = true; + boardconf.clksrc = clocksource; + lgw_board_setconf(boardconf); + + /* RF config */ + memset(&rfconf, 0, sizeof(rfconf)); + + rfconf.enable = true; + rfconf.freq_hz = fa; + rfconf.rssi_offset = DEFAULT_RSSI_OFFSET; + rfconf.type = radio_type; + rfconf.tx_enable = false; /* ignored */ + lgw_rxrf_setconf(0, rfconf); + + rfconf.freq_hz = fb; + rfconf.tx_enable = false; /* ignored */ + lgw_rxrf_setconf(1, rfconf); + + /* Calibration command */ + cal_cmd = 0; + //cal_cmd |= 0x01; /* Bit 0: Calibrate Rx IQ mismatch compensation on radio A */ + //cal_cmd |= 0x02; /* Bit 1: Calibrate Rx IQ mismatch compensation on radio B */ + //cal_cmd |= 0x04; /* Bit 2: Calibrate Tx DC offset on radio A */ + //cal_cmd |= 0x08; /* Bit 3: Calibrate Tx DC offset on radio B */ + cal_cmd |= 0x10; /* Bit 4: 0: calibrate with DAC gain=2, 1: with DAC gain=3 (use 3) */ + + switch (radio_type) { + case LGW_RADIO_TYPE_SX1255: + cal_cmd |= 0x20; /* Bit 5: 0: SX1257, 1: SX1255 */ + break; + case LGW_RADIO_TYPE_SX1257: + cal_cmd |= 0x00; /* Bit 5: 0: SX1257, 1: SX1255 */ + break; + default: + break; + } + + cal_cmd |= 0x00; /* Bit 6-7: Board type 0: ref, 1: FPGA, 3: board X */ + + /* Recap parameters*/ + printf("Library version information: %s\n", lgw_version_info()); + printf("Radio type: %d\n",radio_type); + printf("Radio A frequency: %f MHz\n",fa/1e6); + printf("Radio B frequency: %f MHz\n",fb/1e6); + printf("Number of calibration iterations: %d\n",nb_cal); + printf("Calibration command: brd: %d, chip: %d, dac: %d\n\n", cal_cmd >> 6, 1257-2*((cal_cmd & 0x20) >> 5), 2+((cal_cmd & 0x10) >> 4)); + + x = lgw_connect(false, DEFAULT_TX_NOTCH_FREQ); + if (x == -1) { + printf("ERROR: FAIL TO CONNECT BOARD\n"); + return -1; + } + + /* reset the registers (also shuts the radios down) */ + lgw_soft_reset(); + + /* ungate clocks (gated by default) */ + lgw_reg_w(LGW_GLOBAL_EN, 1); + + /* switch on and reset the radios (also starts the 32 MHz XTAL) */ + lgw_reg_w(LGW_RADIO_A_EN,1); + lgw_reg_w(LGW_RADIO_B_EN,1); + wait_ms(500); /* TODO: optimize */ + lgw_reg_w(LGW_RADIO_RST,1); + wait_ms(5); + lgw_reg_w(LGW_RADIO_RST,0); + + /* setup the radios */ + lgw_setup_sx125x(0, clocksource, true, radio_type, fa); + lgw_setup_sx125x(1, clocksource, false, radio_type, fb); + + /* Set GPIO 4 high for calibration */ + lgw_reg_w(LGW_GPIO_MODE,31); /* Set all GPIOs as output */ + lgw_reg_w(LGW_GPIO_SELECT_OUTPUT,2); /* AGC MCU drives GPIOs */ + + /* Load the calibration firmware */ + load_firmware(MCU_AGC, cal_firmware, MCU_AGC_FW_BYTE); + lgw_reg_w(LGW_MCU_RST_1,0); + lgw_reg_w(LGW_DBG_AGC_MCU_RAM_ADDR, FW_VERSION_ADDR); + lgw_reg_r(LGW_DBG_AGC_MCU_RAM_DATA, &read_val); + fw_version = (uint8_t)read_val; + if (fw_version != FW_VERSION_CAL) { + printf("ERROR: Version of calibration firmware not expected, actual:%d expected:%d\n", fw_version, FW_VERSION_CAL); + return -1; + } + + /* Run Rx A IQ mismatch calibration only */ + for (i=0; i<nb_cal; i++) { + cal_status = sx125x_cal(cal_cmd | 0x01, &cal_res[i]); + x = read_capture(sig_i, sig_q, RAM_SIZE); + /* + file = fopen("toto.txt","w"); + for (j=0; j<RAM_SIZE; j++) { + fprintf(file, "%d %d\n", sig_i[j], sig_q[j]); + } + fclose(file); + */ + img_rej_a[i] = get_img_rej(sig_i, sig_q, RAM_SIZE, FREQ_SIG_NORM); + + printf("Rx A IQ mismatch: Amp: %3d Phi: %3d Rej: %2d dB Status: %3d | Debug: Rej: %2d dB Lna: %1d BB: %2d Dec: %2d\n", cal_res[i].amp_a, cal_res[i].phi_a, cal_res[i].img_rej_a, cal_status, img_rej_a[i], cal_res[i].debug[0], cal_res[i].debug[1], cal_res[i].debug[2]); + } + + /* Run Rx B IQ mismatch calibation only */ + printf("\n"); + for (i=0; i<nb_cal; i++) { + cal_status = sx125x_cal(cal_cmd | 0x02, &cal_res[i]); + x = read_capture(sig_i, sig_q, RAM_SIZE); + img_rej_b[i] = get_img_rej(sig_i, sig_q, RAM_SIZE, FREQ_SIG_NORM); + + printf("Rx B IQ mismatch: Amp: %3d Phi: %3d Rej: %2d dB Status: %3d | Debug: Rej: %2d dB Lna: %1d BB: %2d Dec: %2d\n", cal_res[i].amp_b, cal_res[i].phi_b, cal_res[i].img_rej_b, cal_status, img_rej_b[i], cal_res[i].debug[0], cal_res[i].debug[1], cal_res[i].debug[2]); + } + + /* Run Tx A DC offset calibation only */ + printf("\n"); + if ((tx_enable == 1) || (tx_enable == 3)) { + for (i=0; i<nb_cal; i++) { + cal_status = sx125x_cal(cal_cmd | 0x04, &cal_res[i]); + + printf("Tx A DC offset I :"); + for (j=0; j<8; j++) { + printf(" %3d", cal_res[i].offset_i_a[j]); + } + printf("\n"); + printf("Tx A DC offset Q :"); + for (j=0; j<8; j++) { + printf(" %3d", cal_res[i].offset_q_a[j]); + } + printf("\n"); + printf("Tx A DC rejection:"); + for (j=0; j<8; j++) { + printf(" %3d", cal_res[i].offset_rej_a[j]); + } + printf("\n"); + printf("Tx A DC debug BB :"); + for (j=0; j<8; j++) { + printf(" %3d", (cal_res[i].debug[j] & 0xF0) >> 4); + } + printf("\n"); + printf("Tx A DC debug Dec:"); + for (j=0; j<8; j++) { + printf(" %3d", cal_res[i].debug[j] & 0x0F); + } + printf("\n"); + printf("Tx A DC Status : %3d\n", cal_status); + } + } else { + printf("Tx A calibration bypassed\n"); + } + + /* Run Tx B DC offset calibation only */ + printf("\n"); + if ((tx_enable == 2) || (tx_enable == 3)) { + for (i=0; i<nb_cal; i++) { + cal_status = sx125x_cal(cal_cmd | 0x08, &cal_res[i]); + + printf("Tx B DC offset I :"); + for (j=0; j<8; j++) { + printf(" %3d", cal_res[i].offset_i_b[j]); + } + printf("\n"); + printf("Tx B DC offset Q :"); + for (j=0; j<8; j++) { + printf(" %3d", cal_res[i].offset_q_b[j]); + } + printf("\n"); + printf("Tx B DC rejection:"); + for (j=0; j<8; j++) { + printf(" %3d", cal_res[i].offset_rej_b[j]); + } + printf("\n"); + printf("Tx B DC debug BB :"); + for (j=0; j<8; j++) { + printf(" %3d", (cal_res[i].debug[j] & 0xF0) >> 4); + } + printf("\n"); + printf("Tx B DC debug Dec:"); + for (j=0; j<8; j++) { + printf(" %3d", cal_res[i].debug[j] & 0x0F); + } + printf("\n"); + printf("Tx B DC Status : %3d\n", cal_status); + } + } else { + printf("Tx B calibration bypassed\n"); + } + + /* Compute statistics */ + cal_res_max.amp_a = -128; + cal_res_max.phi_a = -128; + cal_res_max.amp_b = -128; + cal_res_max.phi_b = -128; + cal_res_max.img_rej_a = 0; + cal_res_max.img_rej_b = 0; + for (j=0; j<8; j++) { + cal_res_max.offset_i_a[j] = -128; + cal_res_max.offset_q_a[j] = -128; + cal_res_max.offset_i_b[j] = -128; + cal_res_max.offset_q_b[j] = -128; + cal_res_max.offset_rej_a[j] = 0; + cal_res_max.offset_rej_b[j] = 0; + } + + cal_res_min.amp_a = 127; + cal_res_min.phi_a = 127; + cal_res_min.amp_b = 127; + cal_res_min.phi_b = 127; + cal_res_min.img_rej_a = 255; + cal_res_min.img_rej_b = 255; + for (j=0; j<8; j++) { + cal_res_min.offset_i_a[j] = 127; + cal_res_min.offset_q_a[j] = 127; + cal_res_min.offset_i_b[j] = 127; + cal_res_min.offset_q_b[j] = 127; + cal_res_min.offset_rej_a[j] = 255; + cal_res_min.offset_rej_b[j] = 255; + } + + img_rej_a_max = 0; + img_rej_a_min = 255; + img_rej_b_max = 0; + img_rej_b_min = 255; + + for (i=0; i<nb_cal; i++) { + if (cal_res[i].amp_a > cal_res_max.amp_a) { + cal_res_max.amp_a = cal_res[i].amp_a; + } + if (cal_res[i].phi_a > cal_res_max.phi_a) { + cal_res_max.phi_a = cal_res[i].phi_a; + } + if (cal_res[i].amp_b > cal_res_max.amp_b) { + cal_res_max.amp_b = cal_res[i].amp_b; + } + if (cal_res[i].phi_b > cal_res_max.phi_b) { + cal_res_max.phi_b = cal_res[i].phi_b; + } + if (cal_res[i].phi_b > cal_res_max.phi_b) { + cal_res_max.phi_b = cal_res[i].phi_b; + } + if (cal_res[i].img_rej_a > cal_res_max.img_rej_a) { + cal_res_max.img_rej_a = cal_res[i].img_rej_a; + } + if (cal_res[i].img_rej_b > cal_res_max.img_rej_b) { + cal_res_max.img_rej_b = cal_res[i].img_rej_b; + } + for (j=0; j<8; j++) { + if (cal_res[i].offset_i_a[j] > cal_res_max.offset_i_a[j]) { + cal_res_max.offset_i_a[j] = cal_res[i].offset_i_a[j]; + } + if (cal_res[i].offset_q_a[j] > cal_res_max.offset_q_a[j]) { + cal_res_max.offset_q_a[j] = cal_res[i].offset_q_a[j]; + } + if (cal_res[i].offset_i_b[j] > cal_res_max.offset_i_b[j]) { + cal_res_max.offset_i_b[j] = cal_res[i].offset_i_b[j]; + } + if (cal_res[i].offset_q_b[j] > cal_res_max.offset_q_b[j]) { + cal_res_max.offset_q_b[j] = cal_res[i].offset_q_b[j]; + } + if (cal_res[i].offset_rej_a[j] > cal_res_max.offset_rej_a[j]) { + cal_res_max.offset_rej_a[j] = cal_res[i].offset_rej_a[j]; + } + if (cal_res[i].offset_rej_b[j] > cal_res_max.offset_rej_b[j]) { + cal_res_max.offset_rej_b[j] = cal_res[i].offset_rej_b[j]; + } + } + + if (cal_res[i].amp_a < cal_res_min.amp_a) { + cal_res_min.amp_a = cal_res[i].amp_a; + } + if (cal_res[i].phi_a < cal_res_min.phi_a) { + cal_res_min.phi_a = cal_res[i].phi_a; + } + if (cal_res[i].amp_b < cal_res_min.amp_b) { + cal_res_min.amp_b = cal_res[i].amp_b; + } + if (cal_res[i].phi_b < cal_res_min.phi_b) { + cal_res_min.phi_b = cal_res[i].phi_b; + } + if (cal_res[i].phi_b < cal_res_min.phi_b) { + cal_res_min.phi_b = cal_res[i].phi_b; + } + if (cal_res[i].img_rej_a < cal_res_min.img_rej_a) { + cal_res_min.img_rej_a = cal_res[i].img_rej_a; + } + if (cal_res[i].img_rej_b < cal_res_min.img_rej_b) { + cal_res_min.img_rej_b = cal_res[i].img_rej_b; + } + for (j=0; j<8; j++) { + if (cal_res[i].offset_i_a[j] < cal_res_min.offset_i_a[j]) { + cal_res_min.offset_i_a[j] = cal_res[i].offset_i_a[j]; + } + if (cal_res[i].offset_q_a[j] < cal_res_min.offset_q_a[j]) { + cal_res_min.offset_q_a[j] = cal_res[i].offset_q_a[j]; + } + if (cal_res[i].offset_i_b[j] < cal_res_min.offset_i_b[j]) { + cal_res_min.offset_i_b[j] = cal_res[i].offset_i_b[j]; + } + if (cal_res[i].offset_q_b[j] < cal_res_min.offset_q_b[j]) { + cal_res_min.offset_q_b[j] = cal_res[i].offset_q_b[j]; + } + if (cal_res[i].offset_rej_a[j] < cal_res_min.offset_rej_a[j]) { + cal_res_min.offset_rej_a[j] = cal_res[i].offset_rej_a[j]; + } + if (cal_res[i].offset_rej_b[j] < cal_res_min.offset_rej_b[j]) { + cal_res_min.offset_rej_b[j] = cal_res[i].offset_rej_b[j]; + } + } + + if (img_rej_a[i] > img_rej_a_max) { + img_rej_a_max = img_rej_a[i]; + } + if (img_rej_a[i] < img_rej_a_min) { + img_rej_a_min = img_rej_a[i]; + } + if (img_rej_b[i] > img_rej_b_max) { + img_rej_b_max = img_rej_b[i]; + } + if (img_rej_b[i] < img_rej_b_min) { + img_rej_b_min = img_rej_b[i]; + } + } + + /* Print statistics */ + printf("\n"); + printf("Rx A IQ mismatch calibration statistics on %3d iterations (min, max):\n", nb_cal); + printf("Amp: %3d %3d Phi: %3d %3d Rej: %2d %2d dB (capt.: %2d %2d dB)\n", cal_res_min.amp_a, cal_res_max.amp_a, cal_res_min.phi_a, cal_res_max.phi_a, cal_res_min.img_rej_a, cal_res_max.img_rej_a, img_rej_a_min, img_rej_a_max); + + printf("\n"); + printf("Rx B IQ mismatch calibration statistics on %3d iterations (min, max):\n", nb_cal); + printf("Amp: %3d %3d Phi: %3d %3d Rej: %2d %2d dB (capt.: %2d %2d dB)\n", cal_res_min.amp_b, cal_res_max.amp_b, cal_res_min.phi_b, cal_res_max.phi_b, cal_res_min.img_rej_b, cal_res_max.img_rej_b, img_rej_b_min, img_rej_b_max); + + if ((tx_enable == 1) || (tx_enable == 3)) { + printf("\n"); + printf("Tx A DC offset calibration statistics on %3d iterations (min, max):\n", nb_cal); + for (j=0; j<8; j++) { + printf(" Mix gain %2d: I: %3d %3d Q: %3d %3d Rej: %2d %2d dB\n", 8+j, cal_res_min.offset_i_a[j], cal_res_max.offset_i_a[j], cal_res_min.offset_q_a[j], cal_res_max.offset_q_a[j], cal_res_min.offset_rej_a[j], cal_res_max.offset_rej_a[j]); + } + } + + if ((tx_enable == 2) || (tx_enable == 3)) { + printf("\n"); + printf("Tx B DC offset calibration statistics on %3d iterations (min, max):\n", nb_cal); + for (j=0; j<8; j++) { + printf(" Mix gain %2d: I: %3d %3d Q: %3d %3d Rej: %2d %2d dB\n", 8+j, cal_res_min.offset_i_b[j], cal_res_max.offset_i_b[j], cal_res_min.offset_q_b[j], cal_res_max.offset_q_b[j], cal_res_min.offset_rej_b[j], cal_res_max.offset_rej_b[j]); + } + } + + lgw_stop(); + + printf("\nEnd of radio calibration test\n"); + + return 0; +} + +/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ + +uint8_t sx125x_cal(uint8_t cal_cmd, struct cal_res_s *cal_res) { + + int i; + int32_t read_val; + uint8_t cal_status; + + lgw_reg_w(LGW_FORCE_HOST_RADIO_CTRL,0); /* gives to AGC MCU the control of the radios */ + lgw_reg_w(LGW_RADIO_SELECT,cal_cmd); /* send calibration configuration word */ + lgw_reg_w(LGW_MCU_RST_1,1); + lgw_reg_w(LGW_MCU_RST_1,0); + lgw_reg_w(LGW_PAGE_REG,3); /* Calibration will start on this condition as soon as MCU can talk to concentrator registers */ + lgw_reg_w(LGW_EMERGENCY_FORCE_HOST_CTRL,0); /* Give control of concentrator registers to MCU */ + + wait_ms(2000); /* Wait for end of calibration */ + + lgw_reg_w(LGW_EMERGENCY_FORCE_HOST_CTRL,1); /* Take back control */ + + /* Get calibration status */ + lgw_reg_r(LGW_MCU_AGC_STATUS, &read_val); + cal_status = (uint8_t)read_val; + + /* Check calibration flags + bit 0: could access SX1301 registers + bit 1: could access radio A registers + bit 2: could access radio B registers + bit 3: radio A RX image rejection successful + bit 4: radio B RX image rejection successful + bit 5: radio A TX imbalance correction successful + bit 6: radio B TX imbalance correction successful + bit 7: calibration finished */ + + if ((cal_status & 0x01) == 0) { + printf("WARNING: calibration could not access SX1301 registers\n"); + } + if ((cal_status & 0x02) == 0) { + printf("WARNING: calibration could not access radio A\n"); + } + if ((cal_status & 0x04) == 0) { + printf("WARNING: calibration could not access radio B\n"); + } + if ((cal_cmd & 0x01) && ((cal_status & 0x08) == 0)) { + printf("WARNING: problem in calibration of radio A for image rejection\n"); + } + if ((cal_cmd & 0x02) && ((cal_status & 0x10) == 0)) { + printf("WARNING: problem in calibration of radio B for image rejection\n"); + } + if ((cal_cmd & 0x04) && ((cal_status & 0x20) == 0)) { + printf("WARNING: problem in calibration of radio A for TX imbalance\n"); + } + if ((cal_cmd & 0x08) && ((cal_status & 0x40) == 0)) { + printf("WARNING: problem in calibration of radio B for TX imbalance\n"); + } + if ((cal_status & 0x80) == 0) { + printf("WARNING: Calibration not finished\n"); + } + + /* Get calibration results */ + if (cal_cmd & 0x01) { + lgw_reg_r(LGW_IQ_MISMATCH_A_AMP_COEFF, &read_val); + (*cal_res).amp_a = (int8_t)((read_val > 31) ? read_val - 64 : read_val); + lgw_reg_r(LGW_IQ_MISMATCH_A_PHI_COEFF, &read_val); + (*cal_res).phi_a = (int8_t)((read_val > 31) ? read_val - 64 : read_val); + lgw_reg_w(LGW_DBG_AGC_MCU_RAM_ADDR, 0xD0); + lgw_reg_r(LGW_DBG_AGC_MCU_RAM_DATA, &read_val); + (*cal_res).img_rej_a = (uint8_t)read_val; + lgw_reg_w(LGW_DBG_AGC_MCU_RAM_ADDR, 0xD2); + lgw_reg_r(LGW_DBG_AGC_MCU_RAM_DATA, &read_val); + (*cal_res).debug[0] = (uint8_t)read_val; + lgw_reg_w(LGW_DBG_AGC_MCU_RAM_ADDR, 0xD3); + lgw_reg_r(LGW_DBG_AGC_MCU_RAM_DATA, &read_val); + (*cal_res).debug[1] = (uint8_t)read_val; + lgw_reg_w(LGW_DBG_AGC_MCU_RAM_ADDR, 0xD4); + lgw_reg_r(LGW_DBG_AGC_MCU_RAM_DATA, &read_val); + (*cal_res).debug[2] = (uint8_t)read_val; + } + if (cal_cmd & 0x02) { + lgw_reg_r(LGW_IQ_MISMATCH_B_AMP_COEFF, &read_val); + (*cal_res).amp_b = (int8_t)((read_val > 31) ? read_val - 64 : read_val); + lgw_reg_r(LGW_IQ_MISMATCH_B_PHI_COEFF, &read_val); + (*cal_res).phi_b = (int8_t)((read_val > 31) ? read_val - 64 : read_val); + lgw_reg_w(LGW_DBG_AGC_MCU_RAM_ADDR, 0xD1); + lgw_reg_r(LGW_DBG_AGC_MCU_RAM_DATA, &read_val); + (*cal_res).img_rej_b = (uint8_t)read_val; + lgw_reg_w(LGW_DBG_AGC_MCU_RAM_ADDR, 0xD2); + lgw_reg_r(LGW_DBG_AGC_MCU_RAM_DATA, &read_val); + (*cal_res).debug[0] = (uint8_t)read_val; + lgw_reg_w(LGW_DBG_AGC_MCU_RAM_ADDR, 0xD3); + lgw_reg_r(LGW_DBG_AGC_MCU_RAM_DATA, &read_val); + (*cal_res).debug[1] = (uint8_t)read_val; + lgw_reg_w(LGW_DBG_AGC_MCU_RAM_ADDR, 0xD4); + lgw_reg_r(LGW_DBG_AGC_MCU_RAM_DATA, &read_val); + (*cal_res).debug[2] = (uint8_t)read_val; + } + if (cal_cmd & 0x04) { + for (i=0; i<=7; ++i) { + lgw_reg_w(LGW_DBG_AGC_MCU_RAM_ADDR, 0xA0+i); + lgw_reg_r(LGW_DBG_AGC_MCU_RAM_DATA, &read_val); + (*cal_res).offset_i_a[i] = (int8_t)read_val; + lgw_reg_w(LGW_DBG_AGC_MCU_RAM_ADDR, 0xA8+i); + lgw_reg_r(LGW_DBG_AGC_MCU_RAM_DATA, &read_val); + (*cal_res).offset_q_a[i] = (int8_t)read_val; + lgw_reg_w(LGW_DBG_AGC_MCU_RAM_ADDR, 0xC0+i); + lgw_reg_r(LGW_DBG_AGC_MCU_RAM_DATA, &read_val); + (*cal_res).offset_rej_a[i] = (uint8_t)read_val; + lgw_reg_w(LGW_DBG_AGC_MCU_RAM_ADDR, 0xD2+i); + lgw_reg_r(LGW_DBG_AGC_MCU_RAM_DATA, &read_val); + (*cal_res).debug[i] = (uint8_t)read_val; + } + } + if (cal_cmd & 0x08) { + for (i=0; i<=7; ++i) { + lgw_reg_w(LGW_DBG_AGC_MCU_RAM_ADDR, 0xB0+i); + lgw_reg_r(LGW_DBG_AGC_MCU_RAM_DATA, &read_val); + (*cal_res).offset_i_b[i] = (int8_t)read_val; + lgw_reg_w(LGW_DBG_AGC_MCU_RAM_ADDR, 0xB8+i); + lgw_reg_r(LGW_DBG_AGC_MCU_RAM_DATA, &read_val); + (*cal_res).offset_q_b[i] = (int8_t)read_val; + lgw_reg_w(LGW_DBG_AGC_MCU_RAM_ADDR, 0xC8+i); + lgw_reg_r(LGW_DBG_AGC_MCU_RAM_DATA, &read_val); + (*cal_res).offset_rej_b[i] = (uint8_t)read_val; + lgw_reg_w(LGW_DBG_AGC_MCU_RAM_ADDR, 0xD2+i); + lgw_reg_r(LGW_DBG_AGC_MCU_RAM_DATA, &read_val); + (*cal_res).debug[i] = (uint8_t)read_val; + } + } + + return cal_status; +} + +/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ + +int read_capture(int16_t *sig_i, int16_t *sig_q, int nb_samp) { + + uint8_t read_burst[4]; + uint16_t data_i_c2; + uint16_t data_q_c2; + int i; + + lgw_reg_w(LGW_CAPTURE_RAM_ADDR, 0); + for (i=0 ; i<nb_samp ; i++) { + lgw_reg_rb(LGW_CAPTURE_RAM_DATA, read_burst, 4); + data_i_c2 = ((uint16_t)read_burst[3] << 4) + ((uint16_t)read_burst[2] >> 4); + data_q_c2 = ((uint16_t)read_burst[1] << 4) + ((uint16_t)read_burst[0] >> 4); + sig_i[i] = (int16_t)((data_i_c2 > 2047) ? data_i_c2 - 4096 : data_i_c2); + sig_q[i] = (int16_t)((data_q_c2 > 2047) ? data_q_c2 - 4096 : data_q_c2); + } + + return 0; +} + +/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ + +uint8_t get_img_rej(int16_t *sig_i, int16_t *sig_q, int nb_samp, double f_sig_norm) { + + int i; + double phase; + double corr_sig_i, corr_sig_q, corr_sig_abs; + double corr_img_i, corr_img_q, corr_img_abs; + double img_rej; + + corr_sig_i = 0; + corr_sig_q = 0; + corr_img_i = 0; + corr_img_q = 0; + + for (i=0 ; i<nb_samp ; i++) { + phase = 6.28318530717959*i*f_sig_norm; + corr_sig_i += (double)sig_i[i]*cos( phase) - (double)sig_q[i]*sin( phase); + corr_sig_q += (double)sig_q[i]*cos( phase) + (double)sig_i[i]*sin( phase); + corr_img_i += (double)sig_i[i]*cos(-phase) - (double)sig_q[i]*sin(-phase); + corr_img_q += (double)sig_q[i]*cos(-phase) + (double)sig_i[i]*sin(-phase); + } + + corr_sig_abs = sqrt( corr_sig_i*corr_sig_i + corr_sig_q*corr_sig_q ); + corr_img_abs = sqrt( corr_img_i*corr_img_i + corr_img_q*corr_img_q ); + + img_rej = 20*log10(corr_sig_abs/corr_img_abs); + + return (uint8_t)img_rej; +} + + +/* --- EOF ------------------------------------------------------------------ */ diff --git a/libloragw/tst/test_loragw_gps.c b/libloragw/tst/test_loragw_gps.c new file mode 100644 index 0000000..a4164a3 --- /dev/null +++ b/libloragw/tst/test_loragw_gps.c @@ -0,0 +1,288 @@ +/* + / _____) _ | | +( (____ _____ ____ _| |_ _____ ____| |__ + \____ \| ___ | (_ _) ___ |/ ___) _ \ + _____) ) ____| | | || |_| ____( (___| | | | +(______/|_____)_|_|_| \__)_____)\____)_| |_| + (C)2013 Semtech-Cycleo + +Description: + Minimum test program for the loragw_gps 'library' + +License: Revised BSD License, see LICENSE.TXT file include in the project +Maintainer: Michael Coracin +*/ + + +/* -------------------------------------------------------------------------- */ +/* --- DEPENDANCIES --------------------------------------------------------- */ + +/* fix an issue between POSIX and C99 */ +#if __STDC_VERSION__ >= 199901L + #define _XOPEN_SOURCE 600 +#else + #define _XOPEN_SOURCE 500 +#endif + +#include <stdint.h> /* C99 types */ +#include <stdbool.h> /* bool type */ +#include <stdio.h> /* printf */ +#include <string.h> /* memset */ +#include <signal.h> /* sigaction */ +#include <stdlib.h> /* exit */ +#include <unistd.h> /* read */ + +#include "loragw_hal.h" +#include "loragw_gps.h" +#include "loragw_aux.h" + +/* -------------------------------------------------------------------------- */ +/* --- PRIVATE VARIABLES ---------------------------------------------------- */ + +static int exit_sig = 0; /* 1 -> application terminates cleanly (shut down hardware, close open files, etc) */ +static int quit_sig = 0; /* 1 -> application terminates without shutting down the hardware */ + +struct tref ppm_ref; + +/* -------------------------------------------------------------------------- */ +/* --- PRIVATE FUNCTIONS DECLARATION ---------------------------------------- */ + +static void sig_handler(int sigio); +static void gps_process_sync(void); +static void gps_process_coords(void); + +/* -------------------------------------------------------------------------- */ +/* --- PRIVATE FUNCTIONS DEFINITION ----------------------------------------- */ + +static void sig_handler(int sigio) { + if (sigio == SIGQUIT) { + quit_sig = 1;; + } else if ((sigio == SIGINT) || (sigio == SIGTERM)) { + exit_sig = 1; + } +} + +static void gps_process_sync(void) { + /* variables for PPM pulse GPS synchronization */ + uint32_t ppm_tstamp; + struct timespec ppm_gps; + struct timespec ppm_utc; + + /* variables for timestamp <-> GPS time conversions */ + uint32_t x, z; + struct timespec y; + + /* get GPS time for synchronization */ + int i = lgw_gps_get(&ppm_utc, &ppm_gps, NULL, NULL); + if (i != LGW_GPS_SUCCESS) { + printf(" No valid reference GPS time available, synchronization impossible.\n"); + return; + } + + /* get timestamp for synchronization */ + i = lgw_get_trigcnt(&ppm_tstamp); + if (i != LGW_HAL_SUCCESS) { + printf(" Failed to read timestamp, synchronization impossible.\n"); + return; + } + + /* try to update synchronize time reference with the new GPS & timestamp */ + i = lgw_gps_sync(&ppm_ref, ppm_tstamp, ppm_utc, ppm_gps); + if (i != LGW_GPS_SUCCESS) { + printf(" Synchronization error.\n"); + return; + } + + /* display result */ + printf(" * Synchronization successful *\n"); + printf(" UTC reference time: %lld.%09ld\n", (long long)ppm_ref.utc.tv_sec, ppm_ref.utc.tv_nsec); + printf(" GPS reference time: %lld.%09ld\n", (long long)ppm_ref.gps.tv_sec, ppm_ref.gps.tv_nsec); + printf(" Internal counter reference value: %u\n", ppm_ref.count_us); + printf(" Clock error: %.9f\n", ppm_ref.xtal_err); + + x = ppm_tstamp + 500000; + printf(" * Test of timestamp counter <-> GPS value conversion *\n"); + printf(" Test value: %u\n", x); + lgw_cnt2gps(ppm_ref, x, &y); + printf(" Conversion to GPS: %lld.%09ld\n", (long long)y.tv_sec, y.tv_nsec); + lgw_gps2cnt(ppm_ref, y, &z); + printf(" Converted back: %u ==> %dµs\n", z, (int32_t)(z-x)); + printf(" * Test of timestamp counter <-> UTC value conversion *\n"); + printf(" Test value: %u\n", x); + lgw_cnt2utc(ppm_ref, x, &y); + printf(" Conversion to UTC: %lld.%09ld\n", (long long)y.tv_sec, y.tv_nsec); + lgw_utc2cnt(ppm_ref, y, &z); + printf(" Converted back: %u ==> %dµs\n", z, (int32_t)(z-x)); +} + +static void gps_process_coords(void) { + /* position variable */ + struct coord_s coord; + struct coord_s gpserr; + int i = lgw_gps_get(NULL, NULL, &coord, &gpserr); + + /* update gateway coordinates */ + if (i == LGW_GPS_SUCCESS) { + printf("# GPS coordinates: latitude %.5f, longitude %.5f, altitude %i m\n", coord.lat, coord.lon, coord.alt); + printf("# GPS err: latitude %.5f, longitude %.5f, altitude %i m\n", gpserr.lat, gpserr.lon, gpserr.alt); + } +} + +/* -------------------------------------------------------------------------- */ +/* --- MAIN FUNCTION -------------------------------------------------------- */ + +int main() +{ + struct sigaction sigact; /* SIGQUIT&SIGINT&SIGTERM signal handling */ + + int i; + + /* concentrator variables */ + struct lgw_conf_board_s boardconf; + struct lgw_conf_rxrf_s rfconf; + + /* serial variables */ + char serial_buff[128]; /* buffer to receive GPS data */ + size_t wr_idx = 0; /* pointer to end of chars in buffer */ + int gps_tty_dev; /* file descriptor to the serial port of the GNSS module */ + + /* NMEA/UBX variables */ + enum gps_msg latest_msg; /* keep track of latest NMEA/UBX message parsed */ + + /* configure signal handling */ + sigemptyset(&sigact.sa_mask); + sigact.sa_flags = 0; + sigact.sa_handler = sig_handler; + sigaction(SIGQUIT, &sigact, NULL); + sigaction(SIGINT, &sigact, NULL); + sigaction(SIGTERM, &sigact, NULL); + + /* Intro message and library information */ + printf("Beginning of test for loragw_gps.c\n"); + printf("*** Library version information ***\n%s\n***\n", lgw_version_info()); + + /* Open and configure GPS */ + i = lgw_gps_enable("/dev/ttyAMA0", "ubx7", 0, &gps_tty_dev); + if (i != LGW_GPS_SUCCESS) { + printf("ERROR: IMPOSSIBLE TO ENABLE GPS\n"); + exit(EXIT_FAILURE); + } + + /* start concentrator (default conf for IoT SK) */ + /* board config */ + memset(&boardconf, 0, sizeof(boardconf)); + boardconf.lorawan_public = true; + boardconf.clksrc = 1; + lgw_board_setconf(boardconf); + + /* RF config */ + memset(&rfconf, 0, sizeof(rfconf)); + rfconf.enable = true; + rfconf.freq_hz = 868000000; + rfconf.rssi_offset = 0.0; + rfconf.type = LGW_RADIO_TYPE_SX1257; + rfconf.tx_enable = true; + lgw_rxrf_setconf(0, rfconf); + + lgw_start(); + + /* initialize some variables before loop */ + memset(serial_buff, 0, sizeof serial_buff); + memset(&ppm_ref, 0, sizeof ppm_ref); + + /* loop until user action */ + while ((quit_sig != 1) && (exit_sig != 1)) { + size_t rd_idx = 0; + size_t frame_end_idx = 0; + + /* blocking non-canonical read on serial port */ + ssize_t nb_char = read(gps_tty_dev, serial_buff + wr_idx, LGW_GPS_MIN_MSG_SIZE); + if (nb_char <= 0) { + printf("WARNING: [gps] read() returned value %d\n", nb_char); + continue; + } + wr_idx += (size_t)nb_char; + + /******************************************* + * Scan buffer for UBX/NMEA sync chars and * + * attempt to decode frame if one is found * + *******************************************/ + while (rd_idx < wr_idx) { + size_t frame_size = 0; + + /* Scan buffer for UBX sync char */ + if (serial_buff[rd_idx] == LGW_GPS_UBX_SYNC_CHAR) { + + /*********************** + * Found UBX sync char * + ***********************/ + latest_msg = lgw_parse_ubx(&serial_buff[rd_idx], (wr_idx - rd_idx), &frame_size); + + if (frame_size > 0) { + if (latest_msg == INCOMPLETE) { + /* UBX header found but frame appears to be missing bytes */ + frame_size = 0; + } else if (latest_msg == INVALID) { + /* message header received but message appears to be corrupted */ + printf("WARNING: [gps] could not get a valid message from GPS (no time)\n"); + frame_size = 0; + } else if (latest_msg == UBX_NAV_TIMEGPS) { + printf("\n~~ UBX NAV-TIMEGPS sentence, triggering synchronization attempt ~~\n"); + gps_process_sync(); + } + } + } else if(serial_buff[rd_idx] == LGW_GPS_NMEA_SYNC_CHAR) { + /************************ + * Found NMEA sync char * + ************************/ + /* scan for NMEA end marker (LF = 0x0a) */ + char* nmea_end_ptr = memchr(&serial_buff[rd_idx],(int)0x0a, (wr_idx - rd_idx)); + + if (nmea_end_ptr) { + /* found end marker */ + frame_size = nmea_end_ptr - &serial_buff[rd_idx] + 1; + latest_msg = lgw_parse_nmea(&serial_buff[rd_idx], frame_size); + + if(latest_msg == INVALID || latest_msg == UNKNOWN) { + /* checksum failed */ + frame_size = 0; + } else if (latest_msg == NMEA_RMC) { /* Get location from RMC frames */ + gps_process_coords(); + } + } + } + + if (frame_size > 0) { + /* At this point message is a checksum verified frame + we're processed or ignored. Remove frame from buffer */ + rd_idx += frame_size; + frame_end_idx = rd_idx; + } else { + rd_idx++; + } + } /* ...for(rd_idx = 0... */ + + if (frame_end_idx) { + /* Frames have been processed. Remove bytes to end of last processed frame */ + memcpy(serial_buff,&serial_buff[frame_end_idx],wr_idx - frame_end_idx); + wr_idx -= frame_end_idx; + } /* ...for(rd_idx = 0... */ + + /* Prevent buffer overflow */ + if ((sizeof(serial_buff) - wr_idx) < LGW_GPS_MIN_MSG_SIZE) { + memcpy(serial_buff,&serial_buff[LGW_GPS_MIN_MSG_SIZE],wr_idx - LGW_GPS_MIN_MSG_SIZE); + wr_idx -= LGW_GPS_MIN_MSG_SIZE; + } + } + + /* clean up before leaving */ + if (exit_sig == 1) { + lgw_gps_disable(gps_tty_dev); + lgw_stop(); + } + + printf("\nEnd of test for loragw_gps.c\n"); + exit(EXIT_SUCCESS); +} + +/* --- EOF ------------------------------------------------------------------ */ diff --git a/libloragw/tst/test_loragw_hal.c b/libloragw/tst/test_loragw_hal.c new file mode 100644 index 0000000..e2fee5e --- /dev/null +++ b/libloragw/tst/test_loragw_hal.c @@ -0,0 +1,416 @@ +/* + / _____) _ | | +( (____ _____ ____ _| |_ _____ ____| |__ + \____ \| ___ | (_ _) ___ |/ ___) _ \ + _____) ) ____| | | || |_| ____( (___| | | | +(______/|_____)_|_|_| \__)_____)\____)_| |_| + (C)2013 Semtech-Cycleo + +Description: + Minimum test program for the loragw_hal 'library' + +License: Revised BSD License, see LICENSE.TXT file include in the project +Maintainer: Sylvain Miermont +*/ + + +/* -------------------------------------------------------------------------- */ +/* --- DEPENDANCIES --------------------------------------------------------- */ + +/* fix an issue between POSIX and C99 */ +#if __STDC_VERSION__ >= 199901L + #define _XOPEN_SOURCE 600 +#else + #define _XOPEN_SOURCE 500 +#endif + +#include <stdint.h> /* C99 types */ +#include <stdbool.h> /* bool type */ +#include <stdio.h> /* printf */ +#include <string.h> /* memset */ +#include <signal.h> /* sigaction */ +#include <unistd.h> /* getopt access */ + +#include "loragw_hal.h" +#include "loragw_reg.h" +#include "loragw_aux.h" + +/* -------------------------------------------------------------------------- */ +/* --- PRIVATE MACROS ------------------------------------------------------- */ + +#define ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0])) + +/* -------------------------------------------------------------------------- */ +/* --- PRIVATE CONSTANTS ---------------------------------------------------- */ + +#define DEFAULT_RSSI_OFFSET 0.0 +#define DEFAULT_NOTCH_FREQ 129000U + +/* -------------------------------------------------------------------------- */ +/* --- PRIVATE VARIABLES ---------------------------------------------------- */ + +static int exit_sig = 0; /* 1 -> application terminates cleanly (shut down hardware, close open files, etc) */ +static int quit_sig = 0; /* 1 -> application terminates without shutting down the hardware */ + +/* -------------------------------------------------------------------------- */ +/* --- PRIVATE FUNCTIONS DECLARATION ---------------------------------------- */ + +static void sig_handler(int sigio); + +/* -------------------------------------------------------------------------- */ +/* --- PRIVATE FUNCTIONS DEFINITION ----------------------------------------- */ + +static void sig_handler(int sigio) { + if (sigio == SIGQUIT) { + quit_sig = 1;; + } else if ((sigio == SIGINT) || (sigio == SIGTERM)) { + exit_sig = 1; + } +} + +/* describe command line options */ +void usage(void) { + printf("Library version information: %s\n", lgw_version_info()); + printf( "Available options:\n"); + printf( " -h print this help\n"); + printf( " -a <float> Radio A RX frequency in MHz\n"); + printf( " -b <float> Radio B RX frequency in MHz\n"); + printf( " -t <float> Radio TX frequency in MHz\n"); + printf( " -r <int> Radio type (SX1255:1255, SX1257:1257)\n"); + printf( " -k <int> Concentrator clock source (0: radio_A, 1: radio_B(default))\n"); +} + +/* -------------------------------------------------------------------------- */ +/* --- MAIN FUNCTION -------------------------------------------------------- */ + +int main(int argc, char **argv) +{ + struct sigaction sigact; /* SIGQUIT&SIGINT&SIGTERM signal handling */ + + struct lgw_conf_board_s boardconf; + struct lgw_conf_rxrf_s rfconf; + struct lgw_conf_rxif_s ifconf; + + struct lgw_pkt_rx_s rxpkt[4]; /* array containing up to 4 inbound packets metadata */ + struct lgw_pkt_tx_s txpkt; /* configuration and metadata for an outbound packet */ + struct lgw_pkt_rx_s *p; /* pointer on a RX packet */ + + int i, j; + int nb_pkt; + uint32_t fa = 0, fb = 0, ft = 0; + enum lgw_radio_type_e radio_type = LGW_RADIO_TYPE_NONE; + uint8_t clocksource = 1; /* Radio B is source by default */ + + uint32_t tx_cnt = 0; + unsigned long loop_cnt = 0; + uint8_t status_var = 0; + double xd = 0.0; + int xi = 0; + + /* parse command line options */ + while ((i = getopt (argc, argv, "ha:b:t:r:k:")) != -1) { + switch (i) { + case 'h': + usage(); + return -1; + break; + case 'a': /* <float> Radio A RX frequency in MHz */ + sscanf(optarg, "%lf", &xd); + fa = (uint32_t)((xd*1e6) + 0.5); /* .5 Hz offset to get rounding instead of truncating */ + break; + case 'b': /* <float> Radio B RX frequency in MHz */ + sscanf(optarg, "%lf", &xd); + fb = (uint32_t)((xd*1e6) + 0.5); /* .5 Hz offset to get rounding instead of truncating */ + break; + case 't': /* <float> Radio TX frequency in MHz */ + sscanf(optarg, "%lf", &xd); + ft = (uint32_t)((xd*1e6) + 0.5); /* .5 Hz offset to get rounding instead of truncating */ + break; + case 'r': /* <int> Radio type (1255, 1257) */ + sscanf(optarg, "%i", &xi); + switch (xi) { + case 1255: + radio_type = LGW_RADIO_TYPE_SX1255; + break; + case 1257: + radio_type = LGW_RADIO_TYPE_SX1257; + break; + default: + printf("ERROR: invalid radio type\n"); + usage(); + return -1; + } + break; + case 'k': /* <int> Concentrator clock source (Radio A or Radio B) */ + sscanf(optarg, "%i", &xi); + clocksource = (uint8_t)xi; + break; + default: + printf("ERROR: argument parsing\n"); + usage(); + return -1; + } + } + + /* check input parameters */ + if ((fa == 0) || (fb == 0) || (ft == 0)) { + printf("ERROR: missing frequency input parameter:\n"); + printf(" Radio A RX: %u\n", fa); + printf(" Radio B RX: %u\n", fb); + printf(" Radio TX: %u\n", ft); + usage(); + return -1; + } + + if (radio_type == LGW_RADIO_TYPE_NONE) { + printf("ERROR: missing radio type parameter:\n"); + usage(); + return -1; + } + + /* configure signal handling */ + sigemptyset(&sigact.sa_mask); + sigact.sa_flags = 0; + sigact.sa_handler = sig_handler; + sigaction(SIGQUIT, &sigact, NULL); + sigaction(SIGINT, &sigact, NULL); + sigaction(SIGTERM, &sigact, NULL); + + /* beginning of LoRa concentrator-specific code */ + printf("Beginning of test for loragw_hal.c\n"); + + printf("*** Library version information ***\n%s\n\n", lgw_version_info()); + + /* set configuration for board */ + memset(&boardconf, 0, sizeof(boardconf)); + + boardconf.lorawan_public = true; + boardconf.clksrc = clocksource; + lgw_board_setconf(boardconf); + + /* set configuration for RF chains */ + memset(&rfconf, 0, sizeof(rfconf)); + + rfconf.enable = true; + rfconf.freq_hz = fa; + rfconf.rssi_offset = DEFAULT_RSSI_OFFSET; + rfconf.type = radio_type; + rfconf.tx_enable = true; + rfconf.tx_notch_freq = DEFAULT_NOTCH_FREQ; + lgw_rxrf_setconf(0, rfconf); /* radio A, f0 */ + + rfconf.enable = true; + rfconf.freq_hz = fb; + rfconf.rssi_offset = DEFAULT_RSSI_OFFSET; + rfconf.type = radio_type; + rfconf.tx_enable = false; + lgw_rxrf_setconf(1, rfconf); /* radio B, f1 */ + + /* set configuration for LoRa multi-SF channels (bandwidth cannot be set) */ + memset(&ifconf, 0, sizeof(ifconf)); + + ifconf.enable = true; + ifconf.rf_chain = 1; + ifconf.freq_hz = -400000; + ifconf.datarate = DR_LORA_MULTI; + lgw_rxif_setconf(0, ifconf); /* chain 0: LoRa 125kHz, all SF, on f1 - 0.4 MHz */ + + ifconf.enable = true; + ifconf.rf_chain = 1; + ifconf.freq_hz = -200000; + ifconf.datarate = DR_LORA_MULTI; + lgw_rxif_setconf(1, ifconf); /* chain 1: LoRa 125kHz, all SF, on f1 - 0.2 MHz */ + + ifconf.enable = true; + ifconf.rf_chain = 1; + ifconf.freq_hz = 0; + ifconf.datarate = DR_LORA_MULTI; + lgw_rxif_setconf(2, ifconf); /* chain 2: LoRa 125kHz, all SF, on f1 - 0.0 MHz */ + + ifconf.enable = true; + ifconf.rf_chain = 0; + ifconf.freq_hz = -400000; + ifconf.datarate = DR_LORA_MULTI; + lgw_rxif_setconf(3, ifconf); /* chain 3: LoRa 125kHz, all SF, on f0 - 0.4 MHz */ + + ifconf.enable = true; + ifconf.rf_chain = 0; + ifconf.freq_hz = -200000; + ifconf.datarate = DR_LORA_MULTI; + lgw_rxif_setconf(4, ifconf); /* chain 4: LoRa 125kHz, all SF, on f0 - 0.2 MHz */ + + ifconf.enable = true; + ifconf.rf_chain = 0; + ifconf.freq_hz = 0; + ifconf.datarate = DR_LORA_MULTI; + lgw_rxif_setconf(5, ifconf); /* chain 5: LoRa 125kHz, all SF, on f0 + 0.0 MHz */ + + ifconf.enable = true; + ifconf.rf_chain = 0; + ifconf.freq_hz = 200000; + ifconf.datarate = DR_LORA_MULTI; + lgw_rxif_setconf(6, ifconf); /* chain 6: LoRa 125kHz, all SF, on f0 + 0.2 MHz */ + + ifconf.enable = true; + ifconf.rf_chain = 0; + ifconf.freq_hz = 400000; + ifconf.datarate = DR_LORA_MULTI; + lgw_rxif_setconf(7, ifconf); /* chain 7: LoRa 125kHz, all SF, on f0 + 0.4 MHz */ + + /* set configuration for LoRa 'stand alone' channel */ + memset(&ifconf, 0, sizeof(ifconf)); + ifconf.enable = true; + ifconf.rf_chain = 0; + ifconf.freq_hz = 0; + ifconf.bandwidth = BW_250KHZ; + ifconf.datarate = DR_LORA_SF10; + lgw_rxif_setconf(8, ifconf); /* chain 8: LoRa 250kHz, SF10, on f0 MHz */ + + /* set configuration for FSK channel */ + memset(&ifconf, 0, sizeof(ifconf)); + ifconf.enable = true; + ifconf.rf_chain = 1; + ifconf.freq_hz = 0; + ifconf.bandwidth = BW_250KHZ; + ifconf.datarate = 64000; + lgw_rxif_setconf(9, ifconf); /* chain 9: FSK 64kbps, on f1 MHz */ + + /* set configuration for TX packet */ + memset(&txpkt, 0, sizeof(txpkt)); + txpkt.freq_hz = ft; + txpkt.tx_mode = IMMEDIATE; + txpkt.rf_power = 10; + txpkt.modulation = MOD_LORA; + txpkt.bandwidth = BW_125KHZ; + txpkt.datarate = DR_LORA_SF9; + txpkt.coderate = CR_LORA_4_5; + strcpy((char *)txpkt.payload, "TX.TEST.LORA.GW.????" ); + txpkt.size = 20; + txpkt.preamble = 6; + txpkt.rf_chain = 0; +/* + memset(&txpkt, 0, sizeof(txpkt)); + txpkt.freq_hz = F_TX; + txpkt.tx_mode = IMMEDIATE; + txpkt.rf_power = 10; + txpkt.modulation = MOD_FSK; + txpkt.f_dev = 50; + txpkt.datarate = 64000; + strcpy((char *)txpkt.payload, "TX.TEST.LORA.GW.????" ); + txpkt.size = 20; + txpkt.preamble = 4; + txpkt.rf_chain = 0; +*/ + + /* connect, configure and start the LoRa concentrator */ + i = lgw_start(); + if (i == LGW_HAL_SUCCESS) { + printf("*** Concentrator started ***\n"); + } else { + printf("*** Impossible to start concentrator ***\n"); + return -1; + } + + /* once configured, dump content of registers to a file, for reference */ + // FILE * reg_dump = NULL; + // reg_dump = fopen("reg_dump.log", "w"); + // if (reg_dump != NULL) { + // lgw_reg_check(reg_dump); + // fclose(reg_dump); + // } + + while ((quit_sig != 1) && (exit_sig != 1)) { + loop_cnt++; + + /* fetch N packets */ + nb_pkt = lgw_receive(ARRAY_SIZE(rxpkt), rxpkt); + + if (nb_pkt == 0) { + wait_ms(300); + } else { + /* display received packets */ + for(i=0; i < nb_pkt; ++i) { + p = &rxpkt[i]; + printf("---\nRcv pkt #%d >>", i+1); + if (p->status == STAT_CRC_OK) { + printf(" if_chain:%2d", p->if_chain); + printf(" tstamp:%010u", p->count_us); + printf(" size:%3u", p->size); + switch (p-> modulation) { + case MOD_LORA: printf(" LoRa"); break; + case MOD_FSK: printf(" FSK"); break; + default: printf(" modulation?"); + } + switch (p->datarate) { + case DR_LORA_SF7: printf(" SF7"); break; + case DR_LORA_SF8: printf(" SF8"); break; + case DR_LORA_SF9: printf(" SF9"); break; + case DR_LORA_SF10: printf(" SF10"); break; + case DR_LORA_SF11: printf(" SF11"); break; + case DR_LORA_SF12: printf(" SF12"); break; + default: printf(" datarate?"); + } + switch (p->coderate) { + case CR_LORA_4_5: printf(" CR1(4/5)"); break; + case CR_LORA_4_6: printf(" CR2(2/3)"); break; + case CR_LORA_4_7: printf(" CR3(4/7)"); break; + case CR_LORA_4_8: printf(" CR4(1/2)"); break; + default: printf(" coderate?"); + } + printf("\n"); + printf(" RSSI:%+6.1f SNR:%+5.1f (min:%+5.1f, max:%+5.1f) payload:\n", p->rssi, p->snr, p->snr_min, p->snr_max); + + for (j = 0; j < p->size; ++j) { + printf(" %02X", p->payload[j]); + } + printf(" #\n"); + } else if (p->status == STAT_CRC_BAD) { + printf(" if_chain:%2d", p->if_chain); + printf(" tstamp:%010u", p->count_us); + printf(" size:%3u\n", p->size); + printf(" CRC error, damaged packet\n\n"); + } else if (p->status == STAT_NO_CRC){ + printf(" if_chain:%2d", p->if_chain); + printf(" tstamp:%010u", p->count_us); + printf(" size:%3u\n", p->size); + printf(" no CRC\n\n"); + } else { + printf(" if_chain:%2d", p->if_chain); + printf(" tstamp:%010u", p->count_us); + printf(" size:%3u\n", p->size); + printf(" invalid status ?!?\n\n"); + } + } + } + + /* send a packet every X loop */ + if (loop_cnt%16 == 0) { + /* 32b counter in the payload, big endian */ + txpkt.payload[16] = 0xff & (tx_cnt >> 24); + txpkt.payload[17] = 0xff & (tx_cnt >> 16); + txpkt.payload[18] = 0xff & (tx_cnt >> 8); + txpkt.payload[19] = 0xff & tx_cnt; + i = lgw_send(txpkt); /* non-blocking scheduling of TX packet */ + j = 0; + printf("+++\nSending packet #%d, rf path %d, return %d\nstatus -> ", tx_cnt, txpkt.rf_chain, i); + do { + ++j; + wait_ms(100); + lgw_status(TX_STATUS, &status_var); /* get TX status */ + printf("%d:", status_var); + } while ((status_var != TX_FREE) && (j < 100)); + ++tx_cnt; + printf("\nTX finished\n"); + } + } + + if (exit_sig == 1) { + /* clean up before leaving */ + lgw_stop(); + } + + printf("\nEnd of test for loragw_hal.c\n"); + return 0; +} + +/* --- EOF ------------------------------------------------------------------ */ diff --git a/libloragw/tst/test_loragw_reg.c b/libloragw/tst/test_loragw_reg.c new file mode 100644 index 0000000..37a6f5a --- /dev/null +++ b/libloragw/tst/test_loragw_reg.c @@ -0,0 +1,134 @@ +/* + / _____) _ | | +( (____ _____ ____ _| |_ _____ ____| |__ + \____ \| ___ | (_ _) ___ |/ ___) _ \ + _____) ) ____| | | || |_| ____( (___| | | | +(______/|_____)_|_|_| \__)_____)\____)_| |_| + (C)2013 Semtech-Cycleo + +Description: + Minimum test program for the loragw_spi 'library' + +License: Revised BSD License, see LICENSE.TXT file include in the project +Maintainer: Sylvain Miermont +*/ + + +/* -------------------------------------------------------------------------- */ +/* --- DEPENDANCIES --------------------------------------------------------- */ + +#include <stdint.h> +#include <stdio.h> + +#include "loragw_reg.h" + +/* -------------------------------------------------------------------------- */ +/* --- MAIN FUNCTION -------------------------------------------------------- */ + +#define BURST_TEST_LENGTH 8192 + +int main() +{ + int32_t read_value, test_value; + uint16_t lfsr; + uint8_t burst_buffout[BURST_TEST_LENGTH]; + uint8_t burst_buffin[BURST_TEST_LENGTH]; + int i; + + printf("Beginning of test for loragw_reg.c\n"); + + lgw_connect(false, 129E3); + /* 2 SPI transactions: + -> 0x80 0x00 <- 0x00 0x00 forcing page 0 + -> 0x01 0x00 <- 0x00 0x64 checking version + */ + + /* --- READ TEST --- */ + + lgw_reg_w(LGW_SOFT_RESET, 1); + lgw_reg_check(stdout); + + /* --- READ/WRITE COHERENCY TEST --- */ + + /* 8b unsigned */ + test_value = 197; /* 11000101b */ + lgw_reg_w(LGW_IMPLICIT_PAYLOAD_LENGHT, test_value); + lgw_reg_r(LGW_IMPLICIT_PAYLOAD_LENGHT, &read_value); + printf("IMPLICIT_PAYLOAD_LENGHT = %d (should be %d)\n", read_value, test_value); + + /* 8b signed */ + /* NO SUCH REG AVAILABLE */ + // /* RADIO_SELECT is normally unsigned, modify it manually in loragw_reg.c */ + // test_value = -59; /* 11000101b */ + // lgw_reg_w(LGW_RADIO_SELECT, test_value); + // lgw_reg_r(LGW_RADIO_SELECT, &read_value); + // printf("RADIO_SELECT = %d (should be %d)\n", read_value, test_value); + + /* less than 8b, with offset, unsigned */ + test_value = 11; /* 1011b */ + lgw_reg_w(LGW_FRAME_SYNCH_PEAK2_POS, test_value); + lgw_reg_r(LGW_FRAME_SYNCH_PEAK2_POS, &read_value); + printf("FRAME_SYNCH_PEAK2_POS = %d (should be %d)\n", read_value, test_value); + + /* less than 8b, with offset, signed */ + /* NO SUCH REG AVAILABLE */ + // /* MBWSSF_FRAME_SYNCH_PEAK2_POS is normally unsigned, modify it manually in loragw_reg.c */ + // test_value = -5; /* 1011b */ + // lgw_reg_w(LGW_MBWSSF_FRAME_SYNCH_PEAK2_POS, test_value); + // lgw_reg_r(LGW_MBWSSF_FRAME_SYNCH_PEAK2_POS, &read_value); + // printf("MBWSSF_FRAME_SYNCH_PEAK2_POS = %d (should be %d)\n", read_value, test_value); + + /* 16b unsigned */ + test_value = 49253; /* 11000000 01100101b */ + lgw_reg_w(LGW_PREAMBLE_SYMB1_NB, test_value); + lgw_reg_r(LGW_PREAMBLE_SYMB1_NB, &read_value); + printf("PREAMBLE_SYMB1_NB = %d (should be %d)\n", read_value, test_value); + + /* 16b signed */ + /* NO SUCH REG AVAILABLE */ + // /* CAPTURE_PERIOD is normally unsigned, modify it manually in loragw_reg.c */ + // test_value = -16283; /* 11000000 01100101b */ + // lgw_reg_w(LGW_CAPTURE_PERIOD, test_value); + // lgw_reg_r(LGW_CAPTURE_PERIOD, &read_value); + // printf("CAPTURE_PERIOD = %d (should be %d)\n", read_value, test_value); + + /* between 8b and 16b, unsigned */ + test_value = 3173; /* 1100 01100101b */ + lgw_reg_w(LGW_ADJUST_MODEM_START_OFFSET_SF12_RDX4, test_value); + lgw_reg_r(LGW_ADJUST_MODEM_START_OFFSET_SF12_RDX4, &read_value); + printf("ADJUST_MODEM_START_OFFSET_SF12_RDX4 = %d (should be %d)\n", read_value, test_value); + + /* between 8b and 16b, signed */ + test_value = -1947; /* 11000 01100101b */ + lgw_reg_w(LGW_IF_FREQ_1, test_value); + lgw_reg_r(LGW_IF_FREQ_1, &read_value); + printf("IF_FREQ_1 = %d (should be %d)\n", read_value, test_value); + + /* --- BURST WRITE AND READ TEST --- */ + + /* initialize data for SPI test */ + lfsr = 0xFFFF; + for(i=0; i<BURST_TEST_LENGTH; ++i) { + burst_buffout[i] = (uint8_t)(lfsr ^ (lfsr >> 4)); + /* printf("%05d # 0x%04x 0x%02x\n", i, lfsr, burst_buffout[i]); */ + lfsr = (lfsr & 1) ? ((lfsr >> 1) ^ 0x8679) : (lfsr >> 1); + } + + lgw_reg_wb(LGW_TX_DATA_BUF_DATA, burst_buffout, 256); + lgw_reg_rb(LGW_RX_DATA_BUF_DATA, burst_buffin, 256); + + /* impossible to check in software, + RX_DATA_BUF_DATA is read-only, + TX_DATA_BUF_DATA is write only, + use a logic analyser */ + + /* --- END OF TEST --- */ + + lgw_disconnect(); + /* no SPI transaction */ + + printf("End of test for loragw_reg.c\n"); + return 0; +} + +/* --- EOF ------------------------------------------------------------------ */ diff --git a/libloragw/tst/test_loragw_spi.c b/libloragw/tst/test_loragw_spi.c new file mode 100644 index 0000000..872a075 --- /dev/null +++ b/libloragw/tst/test_loragw_spi.c @@ -0,0 +1,85 @@ +/* + / _____) _ | | +( (____ _____ ____ _| |_ _____ ____| |__ + \____ \| ___ | (_ _) ___ |/ ___) _ \ + _____) ) ____| | | || |_| ____( (___| | | | +(______/|_____)_|_|_| \__)_____)\____)_| |_| + (C)2013 Semtech-Cycleo + +Description: + Minimum test program for the loragw_spi 'library' + Use logic analyser to check the results. + +License: Revised BSD License, see LICENSE.TXT file include in the project +Maintainer: Sylvain Miermont +*/ + + +/* -------------------------------------------------------------------------- */ +/* --- DEPENDANCIES --------------------------------------------------------- */ + +#include <stdint.h> +#include <stdio.h> + +#include "loragw_spi.h" + +/* -------------------------------------------------------------------------- */ +/* --- PRIVATE MACROS ------------------------------------------------------- */ + +#define ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0])) + +/* -------------------------------------------------------------------------- */ +/* --- PRIVATE CONSTANTS ---------------------------------------------------- */ + +#define BURST_TEST_SIZE 2500 /* >> LGW_BURST_CHUNK */ +#define TIMING_REPEAT 1 /* repeat transactions multiple times for timing characterisation */ + +/* -------------------------------------------------------------------------- */ +/* --- MAIN FUNCTION -------------------------------------------------------- */ + +int main() +{ + int i; + void *spi_target = NULL; + uint8_t data = 0; + uint8_t dataout[BURST_TEST_SIZE]; + uint8_t datain[BURST_TEST_SIZE]; + uint8_t spi_mux_mode = LGW_SPI_MUX_MODE0; + + for (i = 0; i < BURST_TEST_SIZE; ++i) { + dataout[i] = 0x30 + (i % 10); /* ASCCI code for 0 -> 9 */ + datain[i] = 0x23; /* garbage data, to be overwritten by received data */ + } + + printf("Beginning of test for loragw_spi.c\n"); + lgw_spi_open(&spi_target); + + /* normal R/W test */ + for (i = 0; i < TIMING_REPEAT; ++i) + lgw_spi_w(spi_target, spi_mux_mode, LGW_SPI_MUX_TARGET_SX1301, 0xAA, 0x96); + for (i = 0; i < TIMING_REPEAT; ++i) + lgw_spi_r(spi_target, spi_mux_mode, LGW_SPI_MUX_TARGET_SX1301, 0x55, &data); + + /* burst R/W test, small bursts << LGW_BURST_CHUNK */ + for (i = 0; i < TIMING_REPEAT; ++i) + lgw_spi_wb(spi_target, spi_mux_mode, LGW_SPI_MUX_TARGET_SX1301, 0x55, dataout, 16); + for (i = 0; i < TIMING_REPEAT; ++i) + lgw_spi_rb(spi_target, spi_mux_mode, LGW_SPI_MUX_TARGET_SX1301, 0x55, datain, 16); + + /* burst R/W test, large bursts >> LGW_BURST_CHUNK */ + for (i = 0; i < TIMING_REPEAT; ++i) + lgw_spi_wb(spi_target, spi_mux_mode, LGW_SPI_MUX_TARGET_SX1301, 0x5A, dataout, ARRAY_SIZE(dataout)); + for (i = 0; i < TIMING_REPEAT; ++i) + lgw_spi_rb(spi_target, spi_mux_mode, LGW_SPI_MUX_TARGET_SX1301, 0x5A, datain, ARRAY_SIZE(datain)); + + /* last read (blocking), just to be sure no to quit before the FTDI buffer is flushed */ + lgw_spi_r(spi_target, spi_mux_mode, LGW_SPI_MUX_TARGET_SX1301, 0x55, &data); + printf("data received (simple read): %d\n",data); + + lgw_spi_close(spi_target); + printf("End of test for loragw_spi.c\n"); + + return 0; +} + +/* --- EOF ------------------------------------------------------------------ */ |