diff options
-rw-r--r-- | lora_pkt_fwd/src/lora_pkt_fwd.c | 551 |
1 files changed, 540 insertions, 11 deletions
diff --git a/lora_pkt_fwd/src/lora_pkt_fwd.c b/lora_pkt_fwd/src/lora_pkt_fwd.c index ca19e28..6665964 100644 --- a/lora_pkt_fwd/src/lora_pkt_fwd.c +++ b/lora_pkt_fwd/src/lora_pkt_fwd.c @@ -45,6 +45,8 @@ Maintainer: Michael Coracin #include <arpa/inet.h> /* IP address conversion stuff */ #include <netdb.h> /* gai_strerror */ +#include <spawn.h> +#include <sys/wait.h> #include <pthread.h> #include "trace.h" @@ -56,6 +58,8 @@ Maintainer: Michael Coracin #include "loragw_gps.h" #include "loragw_aux.h" #include "loragw_reg.h" +#include "loragw_radio.h" +#include "loragw_fpga.h" /* -------------------------------------------------------------------------- */ /* --- PRIVATE MACROS ------------------------------------------------------- */ @@ -93,6 +97,7 @@ Maintainer: Michael Coracin #define PKT_PULL_RESP 3 #define PKT_PULL_ACK 4 #define PKT_TX_ACK 5 +#define PKT_SPEC_SCAN 6 #define NB_PKT_MAX 8 /* max number of packets per fetch/send cycle */ @@ -101,12 +106,11 @@ Maintainer: Michael Coracin #define MIN_FSK_PREAMB 3 /* minimum FSK preamble length for this application */ #define STD_FSK_PREAMB 5 -#define STATUS_SIZE 200 +#define STATUS_SIZE 50000 #define TX_BUFF_SIZE ((540 * NB_PKT_MAX) + 30 + STATUS_SIZE) #define UNIX_GPS_EPOCH_OFFSET 315964800 /* Number of seconds ellapsed between 01.Jan.1970 00:00:00 and 06.Jan.1980 00:00:00 */ - #define DEFAULT_BEACON_FREQ_HZ 869525000 #define DEFAULT_BEACON_FREQ_NB 1 #define DEFAULT_BEACON_FREQ_STEP 0 @@ -115,6 +119,29 @@ Maintainer: Michael Coracin #define DEFAULT_BEACON_POWER 14 #define DEFAULT_BEACON_INFODESC 0 +#define DEFAULT_START 867000000 /* start frequency, Hz */ +#define DEFAULT_STOP 870000000 /* stop frequency, Hz */ +#define DEFAULT_STEP 1000000 /* frequency step, Hz */ +#define DEFAULT_SAMPLES 65535 /* number of RSSI reads */ +#define DEFAULT_CHAN_BW LGW_SX127X_RXBW_62K5_HZ /* channel bandwidth */ +#define DEFAULT_SX127X_RSSI_OFFSET -120 + +#define RSSI_RANGE 256 + +#define MAX_FREQ 1000000000 +#define MIN_FREQ 800000000 +#define MIN_step 5000 + +#define FPGA_FEATURE_SPECTRAL_SCAN 1 +#define FPGA_FEATURE_LBT 2 + +/* When FPGA supports LBT, there are few more constraints on above constants */ +#define LBT_DEFAULT_SAMPLES 129*129 /* number of RSSI reads, hard-coded in FPGA*/ +#define LBT_MIN_STEP 100000 + +#define HISTOGRAM_CLEAN_TIMEOUT 10000 +#define HISTOGRAM_READY_TIMEOUT 1000 + /* -------------------------------------------------------------------------- */ /* --- PRIVATE VARIABLES (GLOBAL) ------------------------------------------- */ @@ -209,6 +236,25 @@ static struct coord_s meas_gps_err; /* GPS position of the gateway */ static pthread_mutex_t mx_stat_rep = PTHREAD_MUTEX_INITIALIZER; /* control access to the status report */ static bool report_ready = false; /* true when there is a new report to send to the server */ static char status_report[STATUS_SIZE]; /* status report as a JSON object */ +static char serialized_string[STATUS_SIZE]; + +/* spectral scan parameters */ +static pthread_mutex_t mx_scan_config = PTHREAD_MUTEX_INITIALIZER; /* control access to the spectral scan config */ +static pthread_mutex_t mx_scan_report = PTHREAD_MUTEX_INITIALIZER; /* control access to the spectral scan config */ +static bool scan_ready = false; /* true when there is a new report to send to the server */ + +struct scan_config_s +{ + uint32_t init; + uint32_t start; + uint32_t stop; + uint32_t step; + uint16_t samples; + int8_t rssi_floor; + int8_t offset; + uint32_t bandwidth; + bool read; +} scan_config, scan_config_new, scan_config_update; /* beacon parameters */ static uint32_t beacon_period = 0; /* set beaconing period, must be a sub-multiple of 86400, the nb of sec in a day */ @@ -258,10 +304,139 @@ void thread_gps(void); void thread_valid(void); void thread_jit(void); void thread_timersync(void); +void thread_spectralscan(void); /* -------------------------------------------------------------------------- */ /* --- PRIVATE FUNCTIONS DEFINITION ----------------------------------------- */ +static enum lgw_sx127x_rxbw_e map_bandwidth(uint32_t bandwidth) { + switch (bandwidth) { + case 25: + return LGW_SX127X_RXBW_12K5_HZ; + case 50: + return LGW_SX127X_RXBW_25K_HZ; + case 100: + return LGW_SX127X_RXBW_50K_HZ; + case 125: + return LGW_SX127X_RXBW_62K5_HZ; + case 200: + return LGW_SX127X_RXBW_100K_HZ; + case 250: + return LGW_SX127X_RXBW_125K_HZ; + case 500: + return LGW_SX127X_RXBW_250K_HZ; + default: + printf("ERROR: Failed to parse spectral scan bandwidth.\n"); + return EXIT_FAILURE; + } +} + +static int reset_fpga(bool lbt_support, uint16_t samples) { + int x; + /* Reconnect to FPGA with sw reset and configure */ + x = lgw_disconnect(); + if(x != 0) { + printf("ERROR: Failed to disconnect from FPGA\n"); + return EXIT_FAILURE; + } + x = lgw_connect(false, LGW_DEFAULT_NOTCH_FREQ); /* FPGA reset/configure */ + if(x != 0) { + printf("ERROR: Failed to connect to FPGA\n"); + return EXIT_FAILURE; + } + if (lbt_support == false) { + x = lgw_fpga_reg_w(LGW_FPGA_HISTO_NB_READ, samples-1); + if( x != LGW_REG_SUCCESS ) + { + printf( "ERROR: Failed to configure FPGA\n" ); + return EXIT_FAILURE; + } + } + return EXIT_SUCCESS; +} + +static int setup_spectral_scan(bool *lbt_support, uint64_t *freq_reg, uint32_t *freq_nb) { + + int x; /* return code for functions */ + int32_t reg_val; + + /* Check if FPGA supports Spectral Scan */ + lgw_fpga_reg_r(LGW_FPGA_FEATURE, ®_val); + if (TAKE_N_BITS_FROM((uint8_t)reg_val, FPGA_FEATURE_SPECTRAL_SCAN, 1) != true) { + printf("ERROR: Spectral Scan is not supported (0x%x)\n", (uint8_t)reg_val); + return EXIT_FAILURE; + } + + /* Check if FPGA supports LBT, in order to apply proper constraints on spectral scan parameters */ + lgw_fpga_reg_r(LGW_FPGA_FEATURE, ®_val); + + if (TAKE_N_BITS_FROM((uint8_t)reg_val, FPGA_FEATURE_LBT, 1) == true) { + printf("WARNING: The FPGA supports LBT, so running spectral scan with specific constraints\n"); + printf(" => Check the parameters summary below\n"); + /* Get start frequency from FPGA */ + lgw_fpga_reg_r(LGW_FPGA_LBT_INITIAL_FREQ, ®_val); + switch (reg_val) { + case 0: + scan_config.init = 915000000; + break; + case 1: + scan_config.init = 863000000; + break; + default: + printf("ERROR: init frequency %d is not supported\n", reg_val); + return EXIT_FAILURE; + } + + /* Check parameters based on LBT constraints */ + if (scan_config.start < scan_config.init) { + printf("ERROR: start frequency %d is not supported, should be >=%d\n", scan_config.start, scan_config.init); + return EXIT_FAILURE; + } + if (scan_config.stop > (scan_config.init + 255*LBT_MIN_STEP)) { + printf("ERROR: stop frequency %d is not supported, should be <%d\n", scan_config.stop, scan_config.init + 255*LBT_MIN_STEP); + return EXIT_FAILURE; + } + if (scan_config.step < LBT_MIN_STEP) { + printf("ERROR: step frequency %d is not supported, should be >=%d\n", scan_config.step, LBT_MIN_STEP); + return EXIT_FAILURE; + } else { + /* Ensure the given step is a multiple of LBT_MIN_STEP */ + scan_config.step = (scan_config.step / LBT_MIN_STEP) * LBT_MIN_STEP; + } + + /* Overload hard-coded spectral scan parameters */ + scan_config.samples = LBT_DEFAULT_SAMPLES; + + /* Spectral scan sequence is slightly different depending if LBT is there or not */ + *lbt_support = true; + } else { + /* Reconnect to FPGA with sw reset and configure */ + x = lgw_disconnect(); + if(x != 0) { + printf("ERROR: Failed to disconnect from FPGA\n"); + return EXIT_FAILURE; + } + x = lgw_connect(false, LGW_DEFAULT_NOTCH_FREQ); /* FPGA reset/configure */ + if(x != 0) { + printf("ERROR: Failed to connect to FPGA\n"); + return EXIT_FAILURE; + } + /* Some spectral scan options are only available when there is no LBT support */ + x = lgw_fpga_reg_w(LGW_FPGA_HISTO_NB_READ, scan_config.samples-1); + if( x != LGW_REG_SUCCESS ) + { + printf( "ERROR: Failed to configure FPGA\n" ); + return EXIT_FAILURE; + } + + /* Initialize frequency */ + *freq_reg = ((uint64_t)scan_config.start << 19) / (uint64_t)32000000; + lgw_fpga_reg_w(LGW_FPGA_HISTO_SCAN_FREQ, (int32_t)freq_reg); + } + *freq_nb = (uint32_t)((scan_config.stop - scan_config.start) / scan_config.step) + 1; + return 0; +} + static void sig_handler(int sigio) { if (sigio == SIGQUIT) { quit_sig = true; @@ -992,7 +1167,6 @@ int main(void) char *global_cfg_path= "global_conf.json"; /* contain global (typ. network-wide) configuration */ char *local_cfg_path = "local_conf.json"; /* contain node specific configuration, overwrite global parameters for parameters that are defined in both */ char *debug_cfg_path = "debug_conf.json"; /* if present, all other configuration files are ignored */ - /* threads */ pthread_t thrid_up; pthread_t thrid_down; @@ -1000,6 +1174,7 @@ int main(void) pthread_t thrid_valid; pthread_t thrid_jit; pthread_t thrid_timersync; + pthread_t thrid_spectralscan; /* network socket creation */ struct addrinfo hints; @@ -1249,6 +1424,13 @@ int main(void) } } + i = pthread_create( &thrid_spectralscan, NULL, (void * (*)(void *))thread_spectralscan, NULL); + if (i != 0) { + MSG("ERROR: [main] impossible to create spectral scan thread\n"); + exit(EXIT_FAILURE); + } + + /* configure signal handling */ sigemptyset(&sigact.sa_mask); sigact.sa_flags = 0; @@ -1435,6 +1617,8 @@ int main(void) } } + pthread_join(thrid_spectralscan, NULL); /* don't wait for spec scan thread */ + /* if an exit signal was received, try to quit properly */ if (exit_sig) { /* shut down network sockets */ @@ -1852,6 +2036,20 @@ void thread_up(void) { exit(EXIT_FAILURE); } } + if (scan_ready == true) { + pthread_mutex_lock(&mx_scan_report); + j = snprintf((char *)(buff_up + buff_index), TX_BUFF_SIZE-buff_index, ",\"spectral_scan\":%s", serialized_string); + scan_ready = false; + pthread_mutex_unlock(&mx_scan_report); + if (j > 0) { + buff_index += j; + } else { + MSG("ERROR: [up] snprintf failed line %u\n", (__LINE__ - 5)); + exit(EXIT_FAILURE); + } + } else { + pthread_mutex_unlock(&mx_scan_report); + } /* end of JSON datagram payload */ buff_up[buff_index] = '}'; @@ -1921,6 +2119,7 @@ void thread_down(void) { /* JSON parsing variables */ JSON_Value *root_val = NULL; JSON_Object *txpk_obj = NULL; + JSON_Object *specscan_obj = NULL; JSON_Value *val = NULL; /* needed to detect the absence of some fields */ const char *str; /* pointer to sub-strings in the JSON data */ short x0, x1; @@ -2212,7 +2411,7 @@ void thread_down(void) { } /* if the datagram does not respect protocol, just ignore it */ - if ((msg_len < 4) || (buff_down[0] != PROTOCOL_VERSION) || ((buff_down[3] != PKT_PULL_RESP) && (buff_down[3] != PKT_PULL_ACK))) { + if ((msg_len < 4) || (buff_down[0] != PROTOCOL_VERSION) || ((buff_down[3] != PKT_PULL_RESP) && (buff_down[3] != PKT_PULL_ACK) && (buff_down[3] != PKT_SPEC_SCAN))) { MSG("WARNING: [down] ignoring invalid packet len=%d, protocol_version=%d, id=%d\n", msg_len, buff_down[0], buff_down[3]); continue; @@ -2236,11 +2435,12 @@ void thread_down(void) { } continue; } - - /* the datagram is a PULL_RESP */ - buff_down[msg_len] = 0; /* add string terminator, just to be safe */ - MSG("INFO: [down] PULL_RESP received - token[%d:%d] :)\n", buff_down[1], buff_down[2]); /* very verbose */ - printf("\nJSON down: %s\n", (char *)(buff_down + 4)); /* DEBUG: display JSON payload */ + if (buff_down[3] == PKT_PULL_RESP || buff_down[3] == PKT_SPEC_SCAN) { + /* the datagram is a PULL_RESP */ + buff_down[msg_len] = 0; /* add string terminator, just to be safe */ + MSG("INFO: [down] PULL_RESP received - token[%d:%d] :)\n", buff_down[1], buff_down[2]); /* very verbose */ + printf("\nJSON down: %s\n", (char *)(buff_down + 4)); /* DEBUG: display JSON payload */ + } /* initialize TX struct and try to parse JSON */ memset(&txpkt, 0, sizeof txpkt); @@ -2252,12 +2452,68 @@ void thread_down(void) { /* look for JSON sub-object 'txpk' */ txpk_obj = json_object_get_object(json_value_get_object(root_val), "txpk"); - if (txpk_obj == NULL) { - MSG("WARNING: [down] no \"txpk\" object in JSON, TX aborted\n"); + specscan_obj = json_object_get_object(json_value_get_object(root_val), "scan"); + if (txpk_obj == NULL && specscan_obj == NULL) { + MSG("WARNING: [down] no \"txpk\" or \"scan\"object in JSON, TX aborted\n"); json_value_free(root_val); continue; } + if (specscan_obj != NULL) { + bool valid_config = true; + val = json_object_get_value(specscan_obj, "start"); /* fetch value (if possible) */ + if (json_value_get_type(val) == JSONNumber) { + scan_config_update.start = (uint32_t)json_value_get_number(val); + } else { + MSG("INFO: [down] start is not a number, ignoring scan config\n"); + valid_config = false; + } + val = json_object_get_value(specscan_obj, "stop"); /* fetch value (if possible) */ + if (json_value_get_type(val) == JSONNumber) { + scan_config_update.stop = (uint32_t)json_value_get_number(val); + } else { + MSG("INFO: [down] stop is not a number, ignoring scan config\n"); + valid_config = false; + } + val = json_object_get_value(specscan_obj, "step"); /* fetch value (if possible) */ + if (json_value_get_type(val) == JSONNumber) { + scan_config_update.step = (uint32_t)json_value_get_number(val); + } else { + MSG("INFO: [down] step is not a number, ignoring scan config\n"); + valid_config = false; + } + val = json_object_get_value(specscan_obj, "samples"); /* fetch value (if possible) */ + if (json_value_get_type(val) == JSONNumber) { + scan_config_update.samples = (uint32_t)json_value_get_number(val); + } else { + MSG("INFO: [down] step is not a number, ignoring scan config\n"); + valid_config = false; + } + val = json_object_get_value(specscan_obj, "offset"); /* fetch value (if possible) */ + if (json_value_get_type(val) == JSONNumber) { + scan_config_update.offset = (uint32_t)json_value_get_number(val); + } else { + MSG("INFO: [down] step is not a number, ignoring scan config\n"); + valid_config = false; + } + val = json_object_get_value(specscan_obj, "bandwidth"); /* fetch value (if possible) */ + if (json_value_get_type(val) == JSONNumber) { + scan_config_update.bandwidth = (uint32_t)json_value_get_number(val); + } else { + MSG("INFO: [down] bandwidth is not a number, ignoring scan config\n"); + valid_config = false; + } + scan_config_update.read = false; + pthread_mutex_lock(&mx_scan_config); + if (valid_config) { + scan_config_new = scan_config_update; + } + pthread_mutex_unlock(&mx_scan_config); + + } + if (txpk_obj == NULL) { + continue; + } /* Parse "immediate" tag, or target timestamp, or UTC time to be converted by GPS (mandatory) */ i = json_object_get_boolean(txpk_obj,"imme"); /* can be 1 if true, 0 if false, or -1 if not a JSON boolean */ if (i == 1) { @@ -2893,4 +3149,277 @@ void thread_valid(void) { MSG("\nINFO: End of validation thread\n"); } +/* -------------------------------------------------------------------------- */ +/* --- THREAD 6: Spectral Scan --- */ + +void thread_spectralscan(void) { + struct timeval tv; + /* Application parameters */ + uint16_t i, j, k; /* loop and temporary variables */ + int x; /* return code for functions */ + int32_t reg_val; + + /* Application parameters */ + + /* Local var */ + bool lbt_support = false; + uint16_t histogram_clean_retry = 0; + uint16_t histogram_ready_retry = 0; + uint16_t histogram_clean_counter = 0; + uint16_t histogram_ready_counter = 0; + uint16_t retry_limit = 1500; + int freq_idx; + uint32_t freq_nb; + int old_min = -1; + uint64_t freq_reg; + uint32_t freq; + uint8_t read_burst[RSSI_RANGE*2]; + uint16_t rssi_histo; + uint16_t rssi_cumu; + uint32_t reset_counter_a = 0; + uint32_t reset_counter_b = 0; + scan_config.init = DEFAULT_START; + scan_config.start = DEFAULT_START; + scan_config.stop = DEFAULT_STOP; + scan_config.step = DEFAULT_STEP; + scan_config.samples = DEFAULT_SAMPLES; + scan_config.offset = DEFAULT_SX127X_RSSI_OFFSET; + scan_config.bandwidth = 0; + scan_config.read = true; + pthread_mutex_lock(&mx_scan_config); + scan_config_new = scan_config; + pthread_mutex_unlock(&mx_scan_config); + + while (!exit_sig && !quit_sig) { + gettimeofday(&tv, NULL); + // Form the seconds of the day + long hms = tv.tv_sec % 86400; + // int hour = hms / 3600; + int min = (hms % 3600) / 60; + // int sec = (hms % 3600) % 60; // or hms % SEC_PER_MIN + pthread_mutex_lock(&mx_scan_config); + if (scan_config_new.read == false) { + scan_config = scan_config_new; + scan_config_new.read = true; + } + pthread_mutex_unlock(&mx_scan_config); + if (scan_config.read == false) { + /* Main loop */ + setup_spectral_scan(&lbt_support, &freq_reg, &freq_nb); + JSON_Value *root_value = json_value_init_object(); + JSON_Object *root_object = json_value_get_object(root_value); + JSON_Array *interests_arr = NULL; + json_object_set_number(root_object, "bandwidth", scan_config.bandwidth); + char eui[17]; + sprintf(&eui[0], "%016lX", lgwm); + json_object_set_string(root_object, "eui", eui); + json_object_set_value(root_object, "results", json_value_init_array()); + interests_arr = json_object_get_array(root_object, "results"); + json_object_set_number(root_object, "start", scan_config.start); + json_object_set_number(root_object, "stop", scan_config.stop); + json_object_set_number(root_object, "step", scan_config.step); + json_object_set_number(root_object, "floor", -160); + json_object_set_number(root_object, "offset", scan_config.offset); + json_object_set_number(root_object, "samples", scan_config.samples); + + struct timespec t1; + time_t t; + t = time(NULL); + clock_gettime(CLOCK_MONOTONIC, &t1); + char scan_timestamp[24]; + strftime(scan_timestamp, sizeof scan_timestamp, "%F %T %Z", gmtime(&t)); + json_object_set_string(root_object, "time", scan_timestamp); + for(j = 0; j < freq_nb; j++) { + /* Current frequency */ + if (exit_sig || quit_sig) exit(EXIT_FAILURE); + freq = scan_config.start + j * scan_config.step; + struct timespec t2; + clock_gettime(CLOCK_MONOTONIC, &t2); + + // double scan_time = difftimespec(t2, t1); + int scan_time = (int)(1000 * difftimespec(t2, t1)); + if (lbt_support == false) { + /* Set SX127x */ + x = lgw_setup_sx127x(freq, MOD_FSK, map_bandwidth(scan_config.bandwidth), 0); + if( x != 0 ) { + MSG( "ERROR: SX127x setup failed\n" ); + exit(EXIT_FAILURE); + } + + /* Start FPGA state machine for spectral scal */ + lgw_fpga_reg_w(LGW_FPGA_CTRL_FEATURE_START, 1); + } else { + /* Do Nothing */ + /* LBT setup has already done the necessary */ + } + + /* Clean histogram */ + lgw_fpga_reg_w(LGW_FPGA_CTRL_CLEAR_HISTO_MEM, 1); + uint8_t reg_stat_old = 255; + uint8_t reg_stat_new; + /* Wait for histogram clean to start */ + do { + if (histogram_clean_counter > HISTOGRAM_CLEAN_TIMEOUT) { + reg_stat_new = reg_val; + reg_stat_old = reg_stat_new; + if (histogram_clean_retry >= retry_limit) { + printf("\nHistogram clean retry limit reached."); + exit(EXIT_FAILURE); + } else { + reset_counter_a++; + printf(" - ERROR: Histogram clean timed out! Resetting FPGA and trying again\n"); + lgw_stop(); + lgw_start(); + x = reset_fpga(lbt_support, scan_config.samples); + setup_spectral_scan(&lbt_support, &freq_reg, &freq_nb); + if( x != 0 ) { + printf( "ERROR: SX127x reset failed\n" ); + } + lgw_fpga_reg_w(LGW_FPGA_CTRL_CLEAR_HISTO_MEM, 1); + if (lbt_support == false) { + x = lgw_setup_sx127x(freq, MOD_FSK, map_bandwidth(scan_config.bandwidth), 0); + if( x != 0 ) { + printf( "ERROR: SX127x setup failed\n" ); + return EXIT_FAILURE; + } + lgw_fpga_reg_w(LGW_FPGA_CTRL_FEATURE_START, 1); + } + histogram_clean_counter = 0; + histogram_clean_retry++; + } + } + wait_ms(10); + lgw_fpga_reg_r(LGW_FPGA_STATUS, ®_val); + histogram_clean_counter++; + } + while((TAKE_N_BITS_FROM((uint8_t)reg_val, 0, 5)) != 1); /* Clear has started */ + + /* Set scan frequency during clear process */ + if (lbt_support == false) { + /* We can directly set the scan frequency */ + freq_reg = ((uint64_t)freq << 19) / (uint64_t)32000000; + lgw_fpga_reg_w(LGW_FPGA_HISTO_SCAN_FREQ, (int32_t)freq_reg); + } else { + /* The possible scan frequencies are hard-coded in FPGA, we give an offset from init */ + freq_idx = (freq - scan_config.init) / LBT_MIN_STEP; + lgw_fpga_reg_w(LGW_FPGA_SCAN_FREQ_OFFSET, freq_idx); + } + + /* Release FPGA state machine */ + lgw_fpga_reg_w(LGW_FPGA_CTRL_CLEAR_HISTO_MEM, 0); + + /* Wait for histogram ready */ + do { + if (histogram_ready_counter > HISTOGRAM_READY_TIMEOUT) { + if (histogram_ready_retry >= retry_limit) { + MSG("\nHistogram timeout retry limit reached"); + lgw_stop(); + lgw_start(); + x = reset_fpga(lbt_support, scan_config.samples); + setup_spectral_scan(&lbt_support, &freq_reg, &freq_nb); + } else { + reset_counter_b++; + MSG("ERROR: Histogram ready timed out! Resetting FPGA and trying again\n"); + x = reset_fpga(lbt_support, scan_config.samples); + setup_spectral_scan(&lbt_support, &freq_reg, &freq_nb); + if( x != 0 ) { + MSG( "ERROR: SX127x reset failed\n" ); + exit(EXIT_FAILURE); + } + lgw_fpga_reg_w(LGW_FPGA_CTRL_CLEAR_HISTO_MEM, 1); + if (lbt_support == false) { + x = lgw_setup_sx127x(freq, MOD_FSK, map_bandwidth(scan_config.bandwidth), 0); + if( x != 0 ) { + MSG( "ERROR: SX127x setup failed\n" ); + setup_spectral_scan(&lbt_support, &freq_reg, &freq_nb); + } + lgw_fpga_reg_w(LGW_FPGA_CTRL_FEATURE_START, 1); + } + histogram_ready_counter = 0; + histogram_ready_retry++; + if (lbt_support == false) { + freq_reg = ((uint64_t)freq << 19) / (uint64_t)32000000; + lgw_fpga_reg_w(LGW_FPGA_HISTO_SCAN_FREQ, (int32_t)freq_reg); + } else { + freq_idx = (freq - scan_config.init) / LBT_MIN_STEP; + lgw_fpga_reg_w(LGW_FPGA_SCAN_FREQ_OFFSET, freq_idx); + } + lgw_fpga_reg_w(LGW_FPGA_CTRL_CLEAR_HISTO_MEM, 0); + } + } + wait_ms(1000); + lgw_fpga_reg_r(LGW_FPGA_STATUS, ®_val); + histogram_ready_counter++; + } + while((TAKE_N_BITS_FROM((uint8_t)reg_val, 5, 1)) != 1); + + if (lbt_support == false) { + /* Stop FPGA state machine for spectral scan */ + lgw_fpga_reg_w(LGW_FPGA_CTRL_FEATURE_START, 0); + } else { + /* Do Nothing */ + /* LBT is running */ + } + + /* Read histogram */ + lgw_fpga_reg_w(LGW_FPGA_CTRL_ACCESS_HISTO_MEM, 1); /* HOST gets access to FPGA RAM */ + lgw_fpga_reg_w(LGW_FPGA_HISTO_RAM_ADDR, 0); + lgw_fpga_reg_rb(LGW_FPGA_HISTO_RAM_DATA, read_burst, RSSI_RANGE*2); + lgw_fpga_reg_w(LGW_FPGA_CTRL_ACCESS_HISTO_MEM, 0); /* FPGA gets access to RAM back */ + + rssi_cumu = 0; + k = 0; + char param_name[32]; + char *seria_string = NULL; + JSON_Value *scan_value = json_value_init_object(); + JSON_Object *scan_object = json_value_get_object(scan_value); + char scan_string[1024] = ""; + snprintf(scan_string, sizeof scan_string, "%d, %d", freq, scan_time); + + float rssi_thresh[] = {0.1,0.3,0.5,0.8,1}; + for (i = 0; i < RSSI_RANGE; i++) { + rssi_histo = (uint16_t)read_burst[2*i] | ((uint16_t)read_burst[2*i+1] << 8); + rssi_cumu += rssi_histo; + if (rssi_cumu > scan_config.samples) { + MSG("WARNING: number of RSSI points higher than expected (%u,%u)", rssi_cumu, scan_config.samples); + rssi_cumu = scan_config.samples; + } + if (rssi_cumu > rssi_thresh[k]*scan_config.samples) { + snprintf(param_name, sizeof param_name, "rssi_%d", (uint16_t)(rssi_thresh[k]*100)); + json_object_set_number(scan_object, param_name, -i/2.0); + k++; + } + if (rssi_histo > 0) { + char valid_hiso[1024] = "\""; + snprintf(valid_hiso, sizeof valid_hiso, ",%.1f,%d", (-i/2.0), rssi_histo); + strcat(scan_string, valid_hiso); + } + } + json_array_append_string(interests_arr, scan_string); + json_free_serialized_string(seria_string); + json_value_free(scan_value); + } + char array_string[5000] = "[\""; + for (i = 0; i < json_array_get_count(interests_arr); i++) { + const char * v = json_array_get_string(interests_arr, i); + strcat(array_string, v); + if (i < json_array_get_count(interests_arr) - 1 ) { + strcat(array_string,"\",\""); + } + } + strcat(array_string, "\"]"); + json_object_set_value(root_object, "results", json_parse_string(array_string)); + old_min = min; + + pthread_mutex_lock(&mx_scan_report); + scan_ready = true; + strcpy(serialized_string, json_serialize_to_string(root_value)); + pthread_mutex_unlock(&mx_scan_report); + json_value_free(root_value); + scan_config.read = true; + } + } + MSG("\nINFO: End of spectral scan thread\n"); +} + /* --- EOF ------------------------------------------------------------------ */ |