diff options
Diffstat (limited to 'lora_pkt_fwd/src')
-rw-r--r-- | lora_pkt_fwd/src/lora_pkt_fwd.c | 398 |
1 files changed, 291 insertions, 107 deletions
diff --git a/lora_pkt_fwd/src/lora_pkt_fwd.c b/lora_pkt_fwd/src/lora_pkt_fwd.c index 474cf17..8e20457 100644 --- a/lora_pkt_fwd/src/lora_pkt_fwd.c +++ b/lora_pkt_fwd/src/lora_pkt_fwd.c @@ -294,12 +294,12 @@ static struct jit_queue_s jit_queue; /* Gateway specificities */ static int8_t antenna_gain = 0; -#define TEMP_ADJ_MAX 4 -#define DEFAULT_TEMP_COMP_FILE "/var/run/lora/current_temp" -static int16_t temp_comp_table[TEMP_ADJ_MAX][2] = { 0 }; -static char temp_comp_file[64] = {0}; -static int16_t temp_comp_value = 0; -static int16_t temp_comp_adj = 0; +#define DEFAULT_TEMP_COMP_TYPE "SENSOR" +#define DEFAULT_TEMP_COMP_FILE "/sys/class/hwmon/hwmon0/temp1_input" +static char temp_comp_type[16] = {0}; +static uint8_t temp_comp_file_type = 0; +static char temp_comp_file[128] = {0}; +static int temp_comp_value = 20; static bool temp_comp_enabled = false; /* TX capabilities */ @@ -334,29 +334,181 @@ void thread_spectralscan(void); /* -------------------------------------------------------------------------- */ /* --- PRIVATE FUNCTIONS DEFINITION ----------------------------------------- */ +#define TEMP_LUT_SIZE_MAX 13 + +/** +@struct lgw_tx_alt_gain_s +@brief Structure containing all gains of Tx chain +*/ +struct lgw_tx_alt_gain_s { + float dig_gain[4];/*!> RF power output measured at each dig setting 0-3 */ + uint8_t pa_gain; /*!> 2 bits, control of the external PA (SX1301 I/O) */ + uint8_t dac_gain; /*!> 2 bits, control of the radio DAC */ + uint8_t mix_gain; /*!> 4 bits, control of the radio mixer */ + int8_t rf_power; /*!> measured TX power at the board connector, in dBm */ +}; + +/** +@struct lgw_tx_alt_gain_lut_s +@brief Structure defining the Tx gain LUT +*/ +struct lgw_tx_alt_gain_lut_s { + struct lgw_tx_alt_gain_s lut[TX_GAIN_LUT_SIZE_MAX]; /*!> Array of Tx gain struct */ + int8_t temp; + uint8_t size; /*!> Number of LUT indexes */ +}; + +/** +@struct lgw_tx_alt_gain_lut_s +@brief Structure defining the Tx gain LUT +*/ +struct lgw_tx_temp_lut_s { + struct lgw_tx_alt_gain_lut_s lut[TEMP_LUT_SIZE_MAX]; /*!> Array of Tx gain struct */ + uint8_t size; /*!> Number of LUT indexes */ +}; + +struct lgw_tx_temp_lut_s tx_temp_lut; + +void lookup_power_settings(float tx_pwr, int8_t* rf_power, int8_t* dig_gain) { + float min_diff = 99; + + for (int i = 0; i < TEMP_LUT_SIZE_MAX; i++) { + if (i == TEMP_LUT_SIZE_MAX-1 || (tx_temp_lut.lut[i].temp <= temp_comp_value && tx_temp_lut.lut[i+1].temp > temp_comp_value)) { + for (int j = 0; j < TX_GAIN_LUT_SIZE_MAX; j++) { + for (int h = 0; h < 4; h++) { + if (tx_pwr >= tx_temp_lut.lut[i].lut[j].dig_gain[h] && (tx_pwr - tx_temp_lut.lut[i].lut[j].dig_gain[h]) < min_diff) { + min_diff = (tx_pwr - tx_temp_lut.lut[i].lut[j].dig_gain[h]); + *rf_power = j; + *dig_gain = h; + } + } + } + break; + } + } + + if (min_diff == 99) { + // minimum output if no match was found + *rf_power = 0; + *dig_gain = 3; + } +} + +void load_temp_lookup() { + + int i; + char param_name[32]; /* used to generate variable parameter names */ + const char *str; /* used to store string value from JSON object */ + const char conf_obj_name[] = "SX1301_conf"; + JSON_Value *root_val = NULL; + JSON_Object *conf_obj = NULL; + JSON_Object *conf_lut_obj = NULL; + JSON_Value *val = NULL; + JSON_Array *conf_array = NULL; + char *temp_lut_path= "temp_lut.json"; /* contain temperature lut configuration */ + if (access(temp_lut_path, R_OK) != 0) { /* if there is a global conf, parse it and then try to parse local conf */ + return; + } + + MSG("INFO: found temp_lut configuration file %s, parsing it\n", temp_lut_path); + + memset(&tx_temp_lut, 0, sizeof tx_temp_lut); /* initialize configuration structure */ + + /* try to parse JSON */ + root_val = json_parse_file_with_comments(temp_lut_path); + if (root_val == NULL) { + MSG("ERROR: %s is not a valid JSON file\n", temp_lut_path); + exit(EXIT_FAILURE); + } + + temp_comp_enabled = true; + + int index = 0; + for (int temp = -60; temp <= 100; temp++) { + snprintf(param_name, sizeof param_name, "LUT%i", temp); /* compose parameter path inside JSON structure */ + /* point to the gateway configuration object */ + conf_obj = json_object_get_object(json_value_get_object(root_val), param_name); + if (conf_obj == NULL) { + continue; + } + + tx_temp_lut.lut[index].temp = temp; + + for (i = 0; i < TX_GAIN_LUT_SIZE_MAX; i++) { + snprintf(param_name, sizeof param_name, "tx_lut_%i", i); /* compose parameter path inside JSON structure */ + conf_lut_obj = json_object_get_object(conf_obj, param_name); /* fetch value (if possible) */ + if (conf_lut_obj == NULL) { + MSG("INFO: no configuration for tx gain lut %i\n", i); + continue; + } + tx_temp_lut.lut[index].size++; /* update TX LUT size based on JSON object found in configuration file */ + /* there is an object to configure that TX gain index, let's parse it */ + snprintf(param_name, sizeof param_name, "tx_lut_%i.pa_gain", i); + val = json_object_dotget_value(conf_obj, param_name); + if (json_value_get_type(val) == JSONNumber) { + tx_temp_lut.lut[index].lut[i].pa_gain = (uint8_t)json_value_get_number(val); + } else { + MSG("WARNING: Data type for %s[%d] seems wrong, please check\n", param_name, i); + tx_temp_lut.lut[index].lut[i].pa_gain = 0; + } + snprintf(param_name, sizeof param_name, "tx_lut_%i.dac_gain", i); + val = json_object_dotget_value(conf_obj, param_name); + if (json_value_get_type(val) == JSONNumber) { + tx_temp_lut.lut[index].lut[i].dac_gain = (uint8_t)json_value_get_number(val); + } else { + tx_temp_lut.lut[index].lut[i].dac_gain = 3; /* This is the only dac_gain supported for now */ + } + snprintf(param_name, sizeof param_name, "tx_lut_%i.mix_gain", i); + val = json_object_dotget_value(conf_obj, param_name); + if (json_value_get_type(val) == JSONNumber) { + tx_temp_lut.lut[index].lut[i].mix_gain = (uint8_t)json_value_get_number(val); + } else { + MSG("WARNING: Data type for %s[%d] seems wrong, please check\n", param_name, i); + tx_temp_lut.lut[index].lut[i].mix_gain = 0; + } + snprintf(param_name, sizeof param_name, "tx_lut_%i.rf_power", i); + val = json_object_dotget_value(conf_obj, param_name); + if (json_value_get_type(val) == JSONNumber) { + tx_temp_lut.lut[index].lut[i].rf_power = (int8_t)json_value_get_number(val); + } else { + MSG("WARNING: Data type for %s[%d] seems wrong, please check\n", param_name, i); + tx_temp_lut.lut[index].lut[i].rf_power = 0; + } + + conf_array = json_object_get_array(conf_lut_obj, "dig_gain"); + if (conf_array != NULL) { + for (int j = 0; j < 4; j++) { + /* Get lut channel configuration object from array */ + tx_temp_lut.lut[index].lut[i].dig_gain[j] = (float)json_array_get_number(conf_array, j); + } + } + } + + index++; + } +} + + static void update_temp_comp_value() { if (!temp_comp_enabled) { return; } /* try to open file to read */ FILE *filePointer; + if (filePointer = fopen(temp_comp_file, "r")) { - int bufferLength = 4; + int bufferLength = 10; char buffer[bufferLength]; fgets(buffer, bufferLength, filePointer); - temp_comp_value = atoi(buffer); - int16_t adj_val = 0; + temp_comp_value = atoi(buffer); - for (int i = 0; i < TEMP_ADJ_MAX; i++) { - if (temp_comp_table[i][0] <= temp_comp_value) { - adj_val = temp_comp_table[i][1]; - } + if (temp_comp_file_type == 0) { + // SENSOR provides a mC reading + temp_comp_value = ((temp_comp_value % 1000) >= 500 ? 1 : 0) + (temp_comp_value / 1000); } - temp_comp_adj = adj_val; - fclose(filePointer); } @@ -517,6 +669,8 @@ static int parse_SX1301_configuration(const char * conf_file) { struct lgw_conf_rxif_s ifconf; uint32_t sf, bw, fdev; + load_temp_lookup(); + /* try to parse JSON */ root_val = json_parse_file_with_comments(conf_file); if (root_val == NULL) { @@ -654,13 +808,12 @@ static int parse_SX1301_configuration(const char * conf_file) { conf_temp_comp_obj = json_object_get_object(conf_obj, "temperature_comp"); /* fetch value (if possible) */ if (conf_temp_comp_obj == NULL) { - MSG("INFO: no configuration for Temperature Compensation\n"); + MSG("INFO: Default Temperature Compensation\n"); + strncpy(temp_comp_file, DEFAULT_TEMP_COMP_FILE, sizeof(temp_comp_file)-1); } else { val = json_object_get_value(conf_temp_comp_obj, "enable"); /* fetch value (if possible) */ if (json_value_get_type(val) == JSONBoolean) { temp_comp_enabled = (bool)json_value_get_boolean(val); - } else { - temp_comp_enabled = false; } if (temp_comp_enabled) { @@ -675,99 +828,100 @@ static int parse_SX1301_configuration(const char * conf_file) { strncpy(temp_comp_file, DEFAULT_TEMP_COMP_FILE, sizeof(temp_comp_file)-1); } - /* load temp adj table */ - int adj_count = 0; - conf_array = json_object_get_array(conf_temp_comp_obj, "values"); - if (conf_array != NULL) { - adj_count = json_array_get_count( conf_array ); - - JSON_Array *temp_values = NULL; - for (i = 0; i < (int)adj_count; i++) { - /* Sanity check */ - if (i >= TEMP_ADJ_MAX) - { - MSG("ERROR: Temp adj %d not supported, skip it\n", i ); - break; - } - - /* Get LBT channel configuration object from array */ - temp_values = json_array_get_array(conf_array, i); - size_t cnt = json_array_get_count(temp_values); - - if (cnt == 2) { - temp_comp_table[i][0] = (int16_t)json_array_get_number(temp_values, 0); - temp_comp_table[i][1] = (int16_t)json_array_get_number(temp_values, 1); - } - } + /* Current temperature type (optional) */ + str = json_object_get_string(conf_temp_comp_obj, "current_temp_type"); + if (str != NULL) { + strncpy(temp_comp_type, str, sizeof(temp_comp_type)-1); + MSG("INFO: Current temperature file is configured to \"%s\"\n", temp_comp_type); + } else { + strncpy(temp_comp_type, DEFAULT_TEMP_COMP_TYPE, sizeof(temp_comp_type)-1); } - update_temp_comp_value(); + if (strncmp(temp_comp_type, "FILE", 4) == 0) { + temp_comp_file_type = 1; + } } else { MSG("INFO: Temperature Compensation disabled\n"); } } - /* set configuration for tx gains */ - memset(&txlut, 0, sizeof txlut); /* initialize configuration structure */ - for (i = 0; i < TX_GAIN_LUT_SIZE_MAX; i++) { - snprintf(param_name, sizeof param_name, "tx_lut_%i", i); /* compose parameter path inside JSON structure */ - val = json_object_get_value(conf_obj, param_name); /* fetch value (if possible) */ - if (json_value_get_type(val) != JSONObject) { - MSG("INFO: no configuration for tx gain lut %i\n", i); - continue; - } - txlut.size++; /* update TX LUT size based on JSON object found in configuration file */ - /* there is an object to configure that TX gain index, let's parse it */ - snprintf(param_name, sizeof param_name, "tx_lut_%i.pa_gain", i); - val = json_object_dotget_value(conf_obj, param_name); - if (json_value_get_type(val) == JSONNumber) { - txlut.lut[i].pa_gain = (uint8_t)json_value_get_number(val); - } else { - MSG("WARNING: Data type for %s[%d] seems wrong, please check\n", param_name, i); - txlut.lut[i].pa_gain = 0; - } - snprintf(param_name, sizeof param_name, "tx_lut_%i.dac_gain", i); - val = json_object_dotget_value(conf_obj, param_name); - if (json_value_get_type(val) == JSONNumber) { - txlut.lut[i].dac_gain = (uint8_t)json_value_get_number(val); - } else { - txlut.lut[i].dac_gain = 3; /* This is the only dac_gain supported for now */ - } - snprintf(param_name, sizeof param_name, "tx_lut_%i.dig_gain", i); - val = json_object_dotget_value(conf_obj, param_name); - if (json_value_get_type(val) == JSONNumber) { - txlut.lut[i].dig_gain = (uint8_t)json_value_get_number(val); - } else { - MSG("WARNING: Data type for %s[%d] seems wrong, please check\n", param_name, i); + if (temp_comp_enabled) { + update_temp_comp_value(); + + MSG("INFO: Loading temperature compensated LUT values\n"); + + for (i = 0; i < TX_GAIN_LUT_SIZE_MAX; i++) { + txlut.lut[i].rf_power = tx_temp_lut.lut[0].lut[i].rf_power; + txlut.lut[i].pa_gain = tx_temp_lut.lut[0].lut[i].pa_gain; + txlut.lut[i].mix_gain = tx_temp_lut.lut[0].lut[i].mix_gain; txlut.lut[i].dig_gain = 0; + txlut.lut[i].dac_gain = 3; + MSG("LUT %d RF: %d PA: %d MIX: %d DIG: %d DAC: %d\n", i, txlut.lut[i].rf_power, txlut.lut[i].pa_gain, txlut.lut[i].mix_gain, txlut.lut[i].dig_gain, txlut.lut[i].dac_gain); } - snprintf(param_name, sizeof param_name, "tx_lut_%i.mix_gain", i); - val = json_object_dotget_value(conf_obj, param_name); - if (json_value_get_type(val) == JSONNumber) { - txlut.lut[i].mix_gain = (uint8_t)json_value_get_number(val); - } else { - MSG("WARNING: Data type for %s[%d] seems wrong, please check\n", param_name, i); - txlut.lut[i].mix_gain = 0; + } else { + + /* set configuration for tx gains */ + memset(&txlut, 0, sizeof txlut); /* initialize configuration structure */ + for (i = 0; i < TX_GAIN_LUT_SIZE_MAX; i++) { + snprintf(param_name, sizeof param_name, "tx_lut_%i", i); /* compose parameter path inside JSON structure */ + val = json_object_get_value(conf_obj, param_name); /* fetch value (if possible) */ + if (json_value_get_type(val) != JSONObject) { + MSG("INFO: no configuration for tx gain lut %i\n", i); + continue; + } + txlut.size++; /* update TX LUT size based on JSON object found in configuration file */ + /* there is an object to configure that TX gain index, let's parse it */ + snprintf(param_name, sizeof param_name, "tx_lut_%i.pa_gain", i); + val = json_object_dotget_value(conf_obj, param_name); + if (json_value_get_type(val) == JSONNumber) { + txlut.lut[i].pa_gain = (uint8_t)json_value_get_number(val); + } else { + MSG("WARNING: Data type for %s[%d] seems wrong, please check\n", param_name, i); + txlut.lut[i].pa_gain = 0; + } + snprintf(param_name, sizeof param_name, "tx_lut_%i.dac_gain", i); + val = json_object_dotget_value(conf_obj, param_name); + if (json_value_get_type(val) == JSONNumber) { + txlut.lut[i].dac_gain = (uint8_t)json_value_get_number(val); + } else { + txlut.lut[i].dac_gain = 3; /* This is the only dac_gain supported for now */ + } + snprintf(param_name, sizeof param_name, "tx_lut_%i.dig_gain", i); + val = json_object_dotget_value(conf_obj, param_name); + if (json_value_get_type(val) == JSONNumber) { + txlut.lut[i].dig_gain = (uint8_t)json_value_get_number(val); + } else { + MSG("WARNING: Data type for %s[%d] seems wrong, please check\n", param_name, i); + txlut.lut[i].dig_gain = 0; + } + snprintf(param_name, sizeof param_name, "tx_lut_%i.mix_gain", i); + val = json_object_dotget_value(conf_obj, param_name); + if (json_value_get_type(val) == JSONNumber) { + txlut.lut[i].mix_gain = (uint8_t)json_value_get_number(val); + } else { + MSG("WARNING: Data type for %s[%d] seems wrong, please check\n", param_name, i); + txlut.lut[i].mix_gain = 0; + } + snprintf(param_name, sizeof param_name, "tx_lut_%i.rf_power", i); + val = json_object_dotget_value(conf_obj, param_name); + if (json_value_get_type(val) == JSONNumber) { + txlut.lut[i].rf_power = (int8_t)json_value_get_number(val); + } else { + MSG("WARNING: Data type for %s[%d] seems wrong, please check\n", param_name, i); + txlut.lut[i].rf_power = 0; + } } - snprintf(param_name, sizeof param_name, "tx_lut_%i.rf_power", i); - val = json_object_dotget_value(conf_obj, param_name); - if (json_value_get_type(val) == JSONNumber) { - txlut.lut[i].rf_power = (int8_t)json_value_get_number(val); + /* all parameters parsed, submitting configuration to the HAL */ + if (txlut.size > 0) { + MSG("INFO: Configuring TX LUT with %u indexes\n", txlut.size); + if (lgw_txgain_setconf(&txlut) != LGW_HAL_SUCCESS) { + MSG("ERROR: Failed to configure concentrator TX Gain LUT\n"); + return -1; + } } else { - MSG("WARNING: Data type for %s[%d] seems wrong, please check\n", param_name, i); - txlut.lut[i].rf_power = 0; + MSG("WARNING: No TX gain LUT defined\n"); } } - /* all parameters parsed, submitting configuration to the HAL */ - if (txlut.size > 0) { - MSG("INFO: Configuring TX LUT with %u indexes\n", txlut.size); - if (lgw_txgain_setconf(&txlut) != LGW_HAL_SUCCESS) { - MSG("ERROR: Failed to configure concentrator TX Gain LUT\n"); - return -1; - } - } else { - MSG("WARNING: No TX gain LUT defined\n"); - } /* set configuration for RF chains */ for (i = 0; i < LGW_RF_CHAIN_NB; ++i) { @@ -1852,7 +2006,6 @@ int main(int argc, char** argv) if (temp_comp_enabled) { update_temp_comp_value(); printf("# Temperature: %i C\n", temp_comp_value); - printf("# Temp TxPow Adj: %i dB\n", temp_comp_adj); } jit_print_queue (&jit_queue, false, DEBUG_LOG); @@ -2504,14 +2657,25 @@ void thread_down(void) { beacon_pkt.tx_mode = ON_GPS; /* send on PPS pulse */ beacon_pkt.rf_chain = 0; /* antenna A */ beacon_pkt.rf_power = beacon_power - antenna_gain; + beacon_pkt.dig_gain = -1; if (max_tx_power != -99) { - if (txpkt.rf_power > max_tx_power - antenna_gain) { + if (beacon_power > max_tx_power) { MSG("INFO: [beacon] tx power reduced tx power: % dBm attn gain: %d dBi\n", max_tx_power, antenna_gain); - beacon_pkt.rf_power = max_tx_power - antenna_gain; + beacon_power = max_tx_power - antenna_gain; } } + if (temp_comp_enabled) { + float pwr = beacon_power; + /* look for power index and DIG gain for current temperature */ + int8_t lut_pwr = 0; + int8_t lut_dig = 0; + lookup_power_settings(pwr, &lut_pwr, &lut_dig); + beacon_pkt.rf_power = lut_pwr; + beacon_pkt.dig_gain = lut_dig; + } + beacon_pkt.modulation = MOD_LORA; switch (beacon_bw_hz) { case 125000: @@ -2716,6 +2880,16 @@ void thread_down(void) { break; } + if (temp_comp_enabled) { + float pwr = beacon_power; + /* look for power index and DIG gain for current temperature */ + int8_t lut_pwr = 0; + int8_t lut_dig = 0; + lookup_power_settings(pwr, &lut_pwr, &lut_dig); + beacon_pkt.rf_power = lut_pwr; + beacon_pkt.dig_gain = lut_dig; + } + jit_result = jit_enqueue(&jit_queue, ¤t_concentrator_time, &beacon_pkt, JIT_PKT_TYPE_BEACON); if (jit_result == JIT_ERROR_OK) { /* update stats */ @@ -2964,23 +3138,33 @@ void thread_down(void) { continue; } txpkt.rf_chain = (uint8_t)json_value_get_number(val); + txpkt.dig_gain = -1; + + float pwr = 0; /* parse TX power (optional field) */ val = json_object_get_value(txpk_obj,"powe"); if (val != NULL) { - txpkt.rf_power = (int8_t)json_value_get_number(val) - antenna_gain; - + pwr = json_value_get_number(val) - antenna_gain; if (max_tx_power != -99) { - if (txpkt.rf_power > max_tx_power - antenna_gain) { + if (pwr > max_tx_power - antenna_gain) { MSG("INFO: [down] tx power reduced tx power: % dBm attn gain: %d dBi\n", max_tx_power, antenna_gain); - txpkt.rf_power = max_tx_power - antenna_gain; + pwr = max_tx_power - antenna_gain; } } } if (temp_comp_enabled) { - txpkt.rf_power += (int8_t)temp_comp_adj; - MSG("INFO: Tx power temp adjusted to %d\n", (int)txpkt.rf_power); + /* look for power index and DIG gain for current temperature */ + int8_t lut_pwr = 0; + int8_t lut_dig = 0; + lookup_power_settings(pwr, &lut_pwr, &lut_dig); + txpkt.rf_power = lut_pwr; + txpkt.dig_gain = lut_dig; + /* look for power index and DIG gain for current temperature */ + MSG("INFO: Tx power %f temp adjusted to IDX: %d DIG: %d\n", pwr, (int)txpkt.rf_power, (int)txpkt.dig_gain); + } else { + txpkt.rf_power = pwr; } /* Parse modulation (mandatory) */ |