summaryrefslogtreecommitdiff
path: root/lora_pkt_fwd/src/lora_pkt_fwd.c
diff options
context:
space:
mode:
Diffstat (limited to 'lora_pkt_fwd/src/lora_pkt_fwd.c')
-rw-r--r--lora_pkt_fwd/src/lora_pkt_fwd.c398
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, &current_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) */