目录
一、前言
二、主函数逻辑
三、parse_SX1301_configuration函数
四、parse_gateway_configuration函数
五、local_conf.json配置文件
六、global_conf.json配置文件
一、前言
对主函数的功能不了解的同学,可以看我上一篇文章。本篇文章我主要整理了如何通过实现读取配置,以及配置文件的内容。
二、主函数逻辑
主函数关于读取配置文件的代码如下:
/* load configuration files */
if (access(debug_cfg_path, R_OK) == 0)
{ /* if there is a debug conf, parse only the debug conf */MSG("INFO: found debug configuration file %s, parsing it\n", debug_cfg_path);MSG("INFO: other configuration files will be ignored\n");x = parse_SX1301_configuration(debug_cfg_path);if (x != 0) {exit(EXIT_FAILURE);}x = parse_gateway_configuration(debug_cfg_path);if (x != 0){exit(EXIT_FAILURE);}
}
else if (access(global_cfg_path, R_OK) == 0)
{ /* if there is a global conf, parse it and then try to parse local conf */MSG("INFO: found global configuration file %s, parsing it\n", global_cfg_path);x = parse_SX1301_configuration(global_cfg_path);if (x != 0) {exit(EXIT_FAILURE);}x = parse_gateway_configuration(global_cfg_path);if (x != 0) {exit(EXIT_FAILURE);}if (access(local_cfg_path, R_OK) == 0) {MSG("INFO: found local configuration file %s, parsing it\n", local_cfg_path);MSG("INFO: redefined parameters will overwrite global parameters\n");parse_SX1301_configuration(local_cfg_path);parse_gateway_configuration(local_cfg_path);}
}
else if (access(local_cfg_path, R_OK) == 0)
{ /* if there is only a local conf, parse it and that's all */MSG("INFO: found local configuration file %s, parsing it\n", local_cfg_path);x = parse_SX1301_configuration(local_cfg_path);if (x != 0) {exit(EXIT_FAILURE);}x = parse_gateway_configuration(local_cfg_path);if (x != 0) {exit(EXIT_FAILURE);}
}
else
{MSG("ERROR: [main] failed to find any configuration file named %s, %s OR %s\n", global_cfg_path, local_cfg_path, debug_cfg_path);exit(EXIT_FAILURE);
}
这段代码是用来加载配置文件的,具体来说是按照一定的优先级顺序去解析和加载调试配置、全局配置和本地配置。具体的逻辑如下:
-
检查调试配置文件 (
debug_cfg_path
) 是否存在并可读:
(1)如果存在并且可读,程序会打印一条信息消息,然后解析这个调试配置文件,并忽略其他配置文件。
(2)调用 parse_SX1301_configuration(debug_cfg_path)
函数解析 SX1301 配置。
(3)如果解析失败(返回值不为 0),程序会退出。
(3)调用 parse_gateway_configuration(debug_cfg_path)
函数解析网关配置。
(4)如果解析失败(返回值不为 0),程序会退出。
-
如果调试配置文件不存在或不可读,检查全局配置文件 (
global_cfg_path
) 是否存在并可读:
(1)如果全局配置文件存在并且可读,程序会打印一条信息消息,然后解析这个全局配置文件。
(2)调用 parse_SX1301_configuration(global_cfg_path)
函数解析 SX1301 配置。
(3)如果解析失败(返回值不为 0),程序会退出。
(4)调用 parse_gateway_configuration(global_cfg_path)
函数解析网关配置。
(5)如果解析失败(返回值不为 0),程序会退出。
(6)检查本地配置文件 (local_cfg_path
) 是否存在并可读。
(6.1)如果本地配置文件存在并且可读。
(6.1.1)程序会打印一条信息消息,然后解析这个本地配置文件。
(6.1.2) 本地配置文件中的重新定义的参数将覆盖全局配置文件中的参数。
(6.1.3)调用 parse_SX1301_configuration(local_cfg_path)
函数解析 SX1301 配置。
(6.1.4)调用 parse_gateway_configuration(local_cfg_path)
函数解析网关配置。
-
如果全局配置文件不存在或不可读,检查本地配置文件 (
local_cfg_path
) 是否存在并可读:
(1)如果本地配置文件存在并且可读,程序会打印一条信息消息,然后解析这个本地配置文件。
(2)调用 parse_SX1301_configuration(local_cfg_path)
函数解析 SX1301 配置。
(3)如果解析失败(返回值不为 0),程序会退出。
(4)调用 parse_gateway_configuration(local_cfg_path)
函数解析网关配置。
(5)如果解析失败(返回值不为 0),程序会退出。
-
如果没有找到任何一个配置文件:
程序会打印一条错误消息,指出没有找到任何配置文件,并退出。
三、parse_SX1301_configuration函数
这个函数主要是通过解析 JSON 文件,提取相关的配置参数,然后调用相应的库函数将这些配置参数设置到硬件上,使得 SX1301 LoRa 集中器板可以按照配置文件中的参数正常工作。
源码对于该函数的定义如下:
static int parse_SX1301_configuration(const char * conf_file)
{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_lbt_obj = NULL;JSON_Object *conf_lbtchan_obj = NULL;JSON_Value *val = NULL;JSON_Array *conf_array = NULL;struct lgw_conf_board_s boardconf;struct lgw_conf_lbt_s lbtconf;struct lgw_conf_rxrf_s rfconf;struct lgw_conf_rxif_s ifconf;uint32_t sf, bw, fdev;/* try to parse JSON */root_val = json_parse_file_with_comments(conf_file);if (root_val == NULL) {MSG("ERROR: %s is not a valid JSON file\n", conf_file);exit(EXIT_FAILURE);}/* point to the gateway configuration object */conf_obj = json_object_get_object(json_value_get_object(root_val), conf_obj_name);if (conf_obj == NULL) {MSG("INFO: %s does not contain a JSON object named %s\n", conf_file, conf_obj_name);return -1;} else {MSG("INFO: %s does contain a JSON object named %s, parsing SX1301 parameters\n", conf_file, conf_obj_name);}/* set board configuration */memset(&boardconf, 0, sizeof boardconf); /* initialize configuration structure */val = json_object_get_value(conf_obj, "lorawan_public"); /* fetch value (if possible) */if (json_value_get_type(val) == JSONBoolean) {boardconf.lorawan_public = (bool)json_value_get_boolean(val);} else {MSG("WARNING: Data type for lorawan_public seems wrong, please check\n");boardconf.lorawan_public = false;}val = json_object_get_value(conf_obj, "clksrc"); /* fetch value (if possible) */if (json_value_get_type(val) == JSONNumber) {boardconf.clksrc = (uint8_t)json_value_get_number(val);} else {MSG("WARNING: Data type for clksrc seems wrong, please check\n");boardconf.clksrc = 0;}MSG("INFO: lorawan_public %d, clksrc %d\n", boardconf.lorawan_public, boardconf.clksrc);/* all parameters parsed, submitting configuration to the HAL */if (lgw_board_setconf(boardconf) != LGW_HAL_SUCCESS) {MSG("ERROR: Failed to configure board\n");return -1;}/* set LBT configuration */memset(&lbtconf, 0, sizeof lbtconf); /* initialize configuration structure */conf_lbt_obj = json_object_get_object(conf_obj, "lbt_cfg"); /* fetch value (if possible) */if (conf_lbt_obj == NULL) {MSG("INFO: no configuration for LBT\n");} else {val = json_object_get_value(conf_lbt_obj, "enable"); /* fetch value (if possible) */if (json_value_get_type(val) == JSONBoolean) {lbtconf.enable = (bool)json_value_get_boolean(val);} else {MSG("WARNING: Data type for lbt_cfg.enable seems wrong, please check\n");lbtconf.enable = false;}if (lbtconf.enable == true) {val = json_object_get_value(conf_lbt_obj, "rssi_target"); /* fetch value (if possible) */if (json_value_get_type(val) == JSONNumber){lbtconf.rssi_target = (int8_t)json_value_get_number(val);} else {MSG("WARNING: Data type for lbt_cfg.rssi_target seems wrong, please check\n");lbtconf.rssi_target = 0;}val = json_object_get_value(conf_lbt_obj, "sx127x_rssi_offset"); /* fetch value (if possible) */if (json_value_get_type(val) == JSONNumber) {lbtconf.rssi_offset = (int8_t)json_value_get_number(val);} else {MSG("WARNING: Data type for lbt_cfg.sx127x_rssi_offset seems wrong, please check\n");lbtconf.rssi_offset = 0;}/* set LBT channels configuration */conf_array = json_object_get_array(conf_lbt_obj, "chan_cfg");if (conf_array != NULL) {lbtconf.nb_channel = json_array_get_count( conf_array );MSG("INFO: %u LBT channels configured\n", lbtconf.nb_channel);}for (i = 0; i < (int)lbtconf.nb_channel; i++) {/* Sanity check */if (i >= LBT_CHANNEL_FREQ_NB){MSG("ERROR: LBT channel %d not supported, skip it\n", i );break;}/* Get LBT channel configuration object from array */conf_lbtchan_obj = json_array_get_object(conf_array, i);/* Channel frequency */val = json_object_dotget_value(conf_lbtchan_obj, "freq_hz"); /* fetch value (if possible) */if (json_value_get_type(val) == JSONNumber) {lbtconf.channels[i].freq_hz = (uint32_t)json_value_get_number(val);}else {MSG("WARNING: Data type for lbt_cfg.channels[%d].freq_hz seems wrong, please check\n", i);lbtconf.channels[i].freq_hz = 0;}/* Channel scan time */val = json_object_dotget_value(conf_lbtchan_obj, "scan_time_us"); /* fetch value (if possible) */if (json_value_get_type(val) == JSONNumber) {lbtconf.channels[i].scan_time_us = (uint16_t)json_value_get_number(val);}else {MSG("WARNING: Data type for lbt_cfg.channels[%d].scan_time_us seems wrong, please check\n", i);lbtconf.channels[i].scan_time_us = 0;}}/* all parameters parsed, submitting configuration to the HAL */if (lgw_lbt_setconf(lbtconf) != LGW_HAL_SUCCESS) {MSG("ERROR: Failed to configure LBT\n");return -1;}} else {MSG("INFO: LBT is disabled\n");}}/* set antenna gain configuration */val = json_object_get_value(conf_obj, "antenna_gain"); /* fetch value (if possible) */if (val != NULL) {if (json_value_get_type(val) == JSONNumber) {antenna_gain = (int8_t)json_value_get_number(val);}else {MSG("WARNING: Data type for antenna_gain seems wrong, please check\n");antenna_gain = 0;}}MSG("INFO: antenna_gain %d dBi\n", antenna_gain);/* 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;}}/* 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) {memset(&rfconf, 0, sizeof rfconf); /* initialize configuration structure */snprintf(param_name, sizeof param_name, "radio_%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 radio %i\n", i);continue;}/* there is an object to configure that radio, let's parse it */snprintf(param_name, sizeof param_name, "radio_%i.enable", i);val = json_object_dotget_value(conf_obj, param_name);if (json_value_get_type(val) == JSONBoolean) {rfconf.enable = (bool)json_value_get_boolean(val);} else {rfconf.enable = false;}if (rfconf.enable == false) { /* radio disabled, nothing else to parse */MSG("INFO: radio %i disabled\n", i);} else { /* radio enabled, will parse the other parameters */snprintf(param_name, sizeof param_name, "radio_%i.freq", i);rfconf.freq_hz = (uint32_t)json_object_dotget_number(conf_obj, param_name);snprintf(param_name, sizeof param_name, "radio_%i.rssi_offset", i);rfconf.rssi_offset = (float)json_object_dotget_number(conf_obj, param_name);snprintf(param_name, sizeof param_name, "radio_%i.type", i);str = json_object_dotget_string(conf_obj, param_name);if (!strncmp(str, "SX1255", 6)) {rfconf.type = LGW_RADIO_TYPE_SX1255;} else if (!strncmp(str, "SX1257", 6)) {rfconf.type = LGW_RADIO_TYPE_SX1257;} else {MSG("WARNING: invalid radio type: %s (should be SX1255 or SX1257)\n", str);}snprintf(param_name, sizeof param_name, "radio_%i.tx_enable", i);val = json_object_dotget_value(conf_obj, param_name);if (json_value_get_type(val) == JSONBoolean) {rfconf.tx_enable = (bool)json_value_get_boolean(val);if (rfconf.tx_enable == true) {/* tx is enabled on this rf chain, we need its frequency range */snprintf(param_name, sizeof param_name, "radio_%i.tx_freq_min", i);tx_freq_min[i] = (uint32_t)json_object_dotget_number(conf_obj, param_name);snprintf(param_name, sizeof param_name, "radio_%i.tx_freq_max", i);tx_freq_max[i] = (uint32_t)json_object_dotget_number(conf_obj, param_name);if ((tx_freq_min[i] == 0) || (tx_freq_max[i] == 0)) {MSG("WARNING: no frequency range specified for TX rf chain %d\n", i);}/* ... and the notch filter frequency to be set */snprintf(param_name, sizeof param_name, "radio_%i.tx_notch_freq", i);rfconf.tx_notch_freq = (uint32_t)json_object_dotget_number(conf_obj, param_name);}} else {rfconf.tx_enable = false;}MSG("INFO: radio %i enabled (type %s), center frequency %u, RSSI offset %f, tx enabled %d, tx_notch_freq %u\n", i, str, rfconf.freq_hz, rfconf.rssi_offset, rfconf.tx_enable, rfconf.tx_notch_freq);}/* all parameters parsed, submitting configuration to the HAL */if (lgw_rxrf_setconf(i, rfconf) != LGW_HAL_SUCCESS) {MSG("ERROR: invalid configuration for radio %i\n", i);return -1;}}/* set configuration for Lora multi-SF channels (bandwidth cannot be set) */for (i = 0; i < LGW_MULTI_NB; ++i) {memset(&ifconf, 0, sizeof ifconf); /* initialize configuration structure */snprintf(param_name, sizeof param_name, "chan_multiSF_%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 Lora multi-SF channel %i\n", i);continue;}/* there is an object to configure that Lora multi-SF channel, let's parse it */snprintf(param_name, sizeof param_name, "chan_multiSF_%i.enable", i);val = json_object_dotget_value(conf_obj, param_name);if (json_value_get_type(val) == JSONBoolean) {ifconf.enable = (bool)json_value_get_boolean(val);} else {ifconf.enable = false;}if (ifconf.enable == false) { /* Lora multi-SF channel disabled, nothing else to parse */MSG("INFO: Lora multi-SF channel %i disabled\n", i);} else { /* Lora multi-SF channel enabled, will parse the other parameters */snprintf(param_name, sizeof param_name, "chan_multiSF_%i.radio", i);ifconf.rf_chain = (uint32_t)json_object_dotget_number(conf_obj, param_name);snprintf(param_name, sizeof param_name, "chan_multiSF_%i.if", i);ifconf.freq_hz = (int32_t)json_object_dotget_number(conf_obj, param_name);// TODO: handle individual SF enabling and disabling (spread_factor)MSG("INFO: Lora multi-SF channel %i> radio %i, IF %i Hz, 125 kHz bw, SF 7 to 12\n", i, ifconf.rf_chain, ifconf.freq_hz);}/* all parameters parsed, submitting configuration to the HAL */if (lgw_rxif_setconf(i, ifconf) != LGW_HAL_SUCCESS) {MSG("ERROR: invalid configuration for Lora multi-SF channel %i\n", i);return -1;}}/* set configuration for Lora standard channel */memset(&ifconf, 0, sizeof ifconf); /* initialize configuration structure */val = json_object_get_value(conf_obj, "chan_Lora_std"); /* fetch value (if possible) */if (json_value_get_type(val) != JSONObject) {MSG("INFO: no configuration for Lora standard channel\n");} else {val = json_object_dotget_value(conf_obj, "chan_Lora_std.enable");if (json_value_get_type(val) == JSONBoolean) {ifconf.enable = (bool)json_value_get_boolean(val);} else {ifconf.enable = false;}if (ifconf.enable == false) {MSG("INFO: Lora standard channel %i disabled\n", i);} else {ifconf.rf_chain = (uint32_t)json_object_dotget_number(conf_obj, "chan_Lora_std.radio");ifconf.freq_hz = (int32_t)json_object_dotget_number(conf_obj, "chan_Lora_std.if");bw = (uint32_t)json_object_dotget_number(conf_obj, "chan_Lora_std.bandwidth");switch(bw) {case 500000: ifconf.bandwidth = BW_500KHZ; break;case 250000: ifconf.bandwidth = BW_250KHZ; break;case 125000: ifconf.bandwidth = BW_125KHZ; break;default: ifconf.bandwidth = BW_UNDEFINED;}sf = (uint32_t)json_object_dotget_number(conf_obj, "chan_Lora_std.spread_factor");switch(sf) {case 7: ifconf.datarate = DR_LORA_SF7; break;case 8: ifconf.datarate = DR_LORA_SF8; break;case 9: ifconf.datarate = DR_LORA_SF9; break;case 10: ifconf.datarate = DR_LORA_SF10; break;case 11: ifconf.datarate = DR_LORA_SF11; break;case 12: ifconf.datarate = DR_LORA_SF12; break;default: ifconf.datarate = DR_UNDEFINED;}MSG("INFO: Lora std channel> radio %i, IF %i Hz, %u Hz bw, SF %u\n", ifconf.rf_chain, ifconf.freq_hz, bw, sf);}if (lgw_rxif_setconf(8, ifconf) != LGW_HAL_SUCCESS) {MSG("ERROR: invalid configuration for Lora standard channel\n");return -1;}}/* set configuration for FSK channel */memset(&ifconf, 0, sizeof ifconf); /* initialize configuration structure */val = json_object_get_value(conf_obj, "chan_FSK"); /* fetch value (if possible) */if (json_value_get_type(val) != JSONObject) {MSG("INFO: no configuration for FSK channel\n");} else {val = json_object_dotget_value(conf_obj, "chan_FSK.enable");if (json_value_get_type(val) == JSONBoolean) {ifconf.enable = (bool)json_value_get_boolean(val);} else {ifconf.enable = false;}if (ifconf.enable == false) {MSG("INFO: FSK channel %i disabled\n", i);} else {ifconf.rf_chain = (uint32_t)json_object_dotget_number(conf_obj, "chan_FSK.radio");ifconf.freq_hz = (int32_t)json_object_dotget_number(conf_obj, "chan_FSK.if");bw = (uint32_t)json_object_dotget_number(conf_obj, "chan_FSK.bandwidth");fdev = (uint32_t)json_object_dotget_number(conf_obj, "chan_FSK.freq_deviation");ifconf.datarate = (uint32_t)json_object_dotget_number(conf_obj, "chan_FSK.datarate");/* if chan_FSK.bandwidth is set, it has priority over chan_FSK.freq_deviation */if ((bw == 0) && (fdev != 0)) {bw = 2 * fdev + ifconf.datarate;}if (bw == 0) ifconf.bandwidth = BW_UNDEFINED;else if (bw <= 7800) ifconf.bandwidth = BW_7K8HZ;else if (bw <= 15600) ifconf.bandwidth = BW_15K6HZ;else if (bw <= 31200) ifconf.bandwidth = BW_31K2HZ;else if (bw <= 62500) ifconf.bandwidth = BW_62K5HZ;else if (bw <= 125000) ifconf.bandwidth = BW_125KHZ;else if (bw <= 250000) ifconf.bandwidth = BW_250KHZ;else if (bw <= 500000) ifconf.bandwidth = BW_500KHZ;else ifconf.bandwidth = BW_UNDEFINED;MSG("INFO: FSK channel> radio %i, IF %i Hz, %u Hz bw, %u bps datarate\n", ifconf.rf_chain, ifconf.freq_hz, bw, ifconf.datarate);}if (lgw_rxif_setconf(9, ifconf) != LGW_HAL_SUCCESS) {MSG("ERROR: invalid configuration for FSK channel\n");return -1;}}json_value_free(root_val);return 0;
}
1、变量声明
声明了一些变量和结构体,用于存储配置参数。例如 lgw_conf_board_s
用于板配置,lgw_conf_lbt_s
用于LBT配置,lgw_conf_rxrf_s
用于RF链配置,lgw_conf_rxif_s
用于多SF(多速率)和标准Lora信道配置。
2、解析JSON文件
root_val = json_parse_file_with_comments(conf_file);
if (root_val == NULL)
{MSG("ERROR: %s is not a valid JSON file\n", conf_file);exit(EXIT_FAILURE);
}
3、定位到JSON
找到名为 SX1301_conf
的 JSON 对象,它包含了所有配置参数。如果找不到这个对象,会输出错误信息并返回。
4、设置板配置
初始化 boardconf
结构体。从 JSON 中提取 lorawan_public
和 clksrc
参数,并设置到 boardconf
结构体中。调用 lgw_board_setconf
函数提交板配置到硬件。
5、设置 LBT(Listen Before Talk)配置
初始化 lbtconf
结构体。提取并设置 lbt_cfg
参数,包括是否启用 LBT、RSSI 目标值、RSSI 偏移量等。提取并配置 LBT 信道参数,包括频率和扫描时间。调用 lgw_lbt_setconf
提交 LBT 配置。
6、设置天线增益配置
从 JSON 中提取 antenna_gain
参数,并设置天线增益。
7、设置发射增益配置
初始化 txlut
结构体。提取并配置 tx_lut
参数,包括各种增益值和发射功率。调用 lgw_txgain_setconf
提交 TX 增益配置。
8、设置 RF 链配置
循环配置每个 RF 链。从 JSON 中提取 radio
参数,并设置各个 RF 链的频率、RSSI 偏移量、类型(SX1255 或 SX1257)、发射启用和发射频率范围。调用 lgw_rxrf_setconf
提交 RF 链配置。
9、设置多 SF 信道配置
循环配置每个多 SF 信道。从 JSON 中提取 chan_multiSF
参数,并设置各个信道的 RF 链、频率等。调用 lgw_rxif_setconf
提交多 SF 信道配置。
10、设置标准 LoRa 信道配置
初始化 ifconf
结构体。从 JSON 中提取 chan_Lora_std
参数,并设置信道的 RF 链、频率、带宽和扩频因子。调用 lgw_rxif_setconf
提交标准 LoRa 信道配置。
11、设置 FSK 信道配置
初始化 ifconf
结构体。从 JSON 中提取 chan_FSK
参数,并设置信道的 RF 链、频率、带宽、频偏和数据率。调用 lgw_rxif_setconf
提交 FSK 信道配置。
12、释放 JSON 解析的内存
json_value_free(root_val);
13、返回结果
如果配置成功,返回 0;如果有错误发生,则返回 -1。
四、parse_gateway_configuration函数
这个函数的作用是读取并解析用于配置网关的 JSON 配置文件,并根据配置文件中的参数设置网关的配置。
源码中对于该函数的定义如下:
static int parse_gateway_configuration(const char * conf_file) {const char conf_obj_name[] = "gateway_conf";JSON_Value *root_val;JSON_Object *conf_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 */unsigned long long ull = 0;/* try to parse JSON */root_val = json_parse_file_with_comments(conf_file);if (root_val == NULL) {MSG("ERROR: %s is not a valid JSON file\n", conf_file);exit(EXIT_FAILURE);}/* point to the gateway configuration object */conf_obj = json_object_get_object(json_value_get_object(root_val), conf_obj_name);if (conf_obj == NULL) {MSG("INFO: %s does not contain a JSON object named %s\n", conf_file, conf_obj_name);return -1;} else {MSG("INFO: %s does contain a JSON object named %s, parsing gateway parameters\n", conf_file, conf_obj_name);}/* gateway unique identifier (aka MAC address) (optional) */str = json_object_get_string(conf_obj, "gateway_ID");if (str != NULL) {sscanf(str, "%llx", &ull);lgwm = ull;MSG("INFO: gateway MAC address is configured to %016llX\n", ull);}/* server hostname or IP address (optional) */str = json_object_get_string(conf_obj, "server_address");if (str != NULL) {strncpy(serv_addr, str, sizeof serv_addr);MSG("INFO: server hostname or IP address is configured to \"%s\"\n", serv_addr);}/* get up and down ports (optional) */val = json_object_get_value(conf_obj, "serv_port_up");if (val != NULL) {snprintf(serv_port_up, sizeof serv_port_up, "%u", (uint16_t)json_value_get_number(val));MSG("INFO: upstream port is configured to \"%s\"\n", serv_port_up);}val = json_object_get_value(conf_obj, "serv_port_down");if (val != NULL) {snprintf(serv_port_down, sizeof serv_port_down, "%u", (uint16_t)json_value_get_number(val));MSG("INFO: downstream port is configured to \"%s\"\n", serv_port_down);}/* get keep-alive interval (in seconds) for downstream (optional) */val = json_object_get_value(conf_obj, "keepalive_interval");if (val != NULL) {keepalive_time = (int)json_value_get_number(val);MSG("INFO: downstream keep-alive interval is configured to %u seconds\n", keepalive_time);}/* get interval (in seconds) for statistics display (optional) */val = json_object_get_value(conf_obj, "stat_interval");if (val != NULL) {stat_interval = (unsigned)json_value_get_number(val);MSG("INFO: statistics display interval is configured to %u seconds\n", stat_interval);}/* get time-out value (in ms) for upstream datagrams (optional) */val = json_object_get_value(conf_obj, "push_timeout_ms");if (val != NULL) {push_timeout_half.tv_usec = 500 * (long int)json_value_get_number(val);MSG("INFO: upstream PUSH_DATA time-out is configured to %u ms\n", (unsigned)(push_timeout_half.tv_usec / 500));}/* packet filtering parameters */val = json_object_get_value(conf_obj, "forward_crc_valid");if (json_value_get_type(val) == JSONBoolean) {fwd_valid_pkt = (bool)json_value_get_boolean(val);}MSG("INFO: packets received with a valid CRC will%s be forwarded\n", (fwd_valid_pkt ? "" : " NOT"));val = json_object_get_value(conf_obj, "forward_crc_error");if (json_value_get_type(val) == JSONBoolean) {fwd_error_pkt = (bool)json_value_get_boolean(val);}MSG("INFO: packets received with a CRC error will%s be forwarded\n", (fwd_error_pkt ? "" : " NOT"));val = json_object_get_value(conf_obj, "forward_crc_disabled");if (json_value_get_type(val) == JSONBoolean) {fwd_nocrc_pkt = (bool)json_value_get_boolean(val);}MSG("INFO: packets received with no CRC will%s be forwarded\n", (fwd_nocrc_pkt ? "" : " NOT"));/* GPS module TTY path (optional) */str = json_object_get_string(conf_obj, "gps_tty_path");if (str != NULL) {strncpy(gps_tty_path, str, sizeof gps_tty_path);MSG("INFO: GPS serial port path is configured to \"%s\"\n", gps_tty_path);}/* get reference coordinates */val = json_object_get_value(conf_obj, "ref_latitude");if (val != NULL) {reference_coord.lat = (double)json_value_get_number(val);MSG("INFO: Reference latitude is configured to %f deg\n", reference_coord.lat);}val = json_object_get_value(conf_obj, "ref_longitude");if (val != NULL) {reference_coord.lon = (double)json_value_get_number(val);MSG("INFO: Reference longitude is configured to %f deg\n", reference_coord.lon);}val = json_object_get_value(conf_obj, "ref_altitude");if (val != NULL) {reference_coord.alt = (short)json_value_get_number(val);MSG("INFO: Reference altitude is configured to %i meters\n", reference_coord.alt);}/* Gateway GPS coordinates hardcoding (aka. faking) option */val = json_object_get_value(conf_obj, "fake_gps");if (json_value_get_type(val) == JSONBoolean) {gps_fake_enable = (bool)json_value_get_boolean(val);if (gps_fake_enable == true) {MSG("INFO: fake GPS is enabled\n");} else {MSG("INFO: fake GPS is disabled\n");}}/* Beacon signal period (optional) */val = json_object_get_value(conf_obj, "beacon_period");if (val != NULL) {beacon_period = (uint32_t)json_value_get_number(val);if ((beacon_period > 0) && (beacon_period < 6)) {MSG("ERROR: invalid configuration for Beacon period, must be >= 6s\n");return -1;} else {MSG("INFO: Beaconing period is configured to %u seconds\n", beacon_period);}}/* Beacon TX frequency (optional) */val = json_object_get_value(conf_obj, "beacon_freq_hz");if (val != NULL) {beacon_freq_hz = (uint32_t)json_value_get_number(val);MSG("INFO: Beaconing signal will be emitted at %u Hz\n", beacon_freq_hz);}/* Number of beacon channels (optional) */val = json_object_get_value(conf_obj, "beacon_freq_nb");if (val != NULL) {beacon_freq_nb = (uint8_t)json_value_get_number(val);MSG("INFO: Beaconing channel number is set to %u\n", beacon_freq_nb);}/* Frequency step between beacon channels (optional) */val = json_object_get_value(conf_obj, "beacon_freq_step");if (val != NULL) {beacon_freq_step = (uint32_t)json_value_get_number(val);MSG("INFO: Beaconing channel frequency step is set to %uHz\n", beacon_freq_step);}/* Beacon datarate (optional) */val = json_object_get_value(conf_obj, "beacon_datarate");if (val != NULL) {beacon_datarate = (uint8_t)json_value_get_number(val);MSG("INFO: Beaconing datarate is set to SF%d\n", beacon_datarate);}/* Beacon modulation bandwidth (optional) */val = json_object_get_value(conf_obj, "beacon_bw_hz");if (val != NULL) {beacon_bw_hz = (uint32_t)json_value_get_number(val);MSG("INFO: Beaconing modulation bandwidth is set to %dHz\n", beacon_bw_hz);}/* Beacon TX power (optional) */val = json_object_get_value(conf_obj, "beacon_power");if (val != NULL) {beacon_power = (int8_t)json_value_get_number(val);MSG("INFO: Beaconing TX power is set to %ddBm\n", beacon_power);}/* Beacon information descriptor (optional) */val = json_object_get_value(conf_obj, "beacon_infodesc");if (val != NULL) {beacon_infodesc = (uint8_t)json_value_get_number(val);MSG("INFO: Beaconing information descriptor is set to %u\n", beacon_infodesc);}/* Auto-quit threshold (optional) */val = json_object_get_value(conf_obj, "autoquit_threshold");if (val != NULL) {autoquit_threshold = (uint32_t)json_value_get_number(val);MSG("INFO: Auto-quit after %u non-acknowledged PULL_DATA\n", autoquit_threshold);}/* free JSON parsing data structure */json_value_free(root_val);return 0;
}
1、变量声明
(1)conf_obj_name
定义了 JSON 对象的名称 "gateway_conf"。
(2)root_val
是 JSON 文件的根值。
(3)conf_obj
指向解析的 JSON 对象。
(4)val
用于检测某些字段是否存在。
(5)str
用于指向 JSON 数据中的子字符串。
(6)ull
用于存储解析后的网关唯一标识符(MAC 地址)。
2、解析 JSON 文件
root_val = json_parse_file_with_comments(conf_file);
if (root_val == NULL)
{MSG("ERROR: %s is not a valid JSON file\n", conf_file);exit(EXIT_FAILURE);
}
3、定位到 JSON 对象
找到名为 gateway_conf
的 JSON 对象。如果找不到这个对象,会输出错误信息并返回 -1。
4、解析并设置网关唯一标识符(MAC 地址)
str = json_object_get_string(conf_obj, "gateway_ID");
if (str != NULL)
{sscanf(str, "%llx", &ull);lgwm = ull;MSG("INFO: gateway MAC address is configured to %016llX\n", ull);
}
5、解析并设置服务器地址
str = json_object_get_string(conf_obj, "server_address");
if (str != NULL)
{strncpy(serv_addr, str, sizeof serv_addr);MSG("INFO: server hostname or IP address is configured to \"%s\"\n", serv_addr);
}
6、解析并设置上行和下行端口
val = json_object_get_value(conf_obj, "serv_port_up");
if (val != NULL)
{snprintf(serv_port_up, sizeof serv_port_up, "%u", (uint16_t)json_value_get_number(val));MSG("INFO: upstream port is configured to \"%s\"\n", serv_port_up);
}
val = json_object_get_value(conf_obj, "serv_port_down");
if (val != NULL)
{snprintf(serv_port_down, sizeof serv_port_down, "%u", (uint16_t)json_value_get_number(val));MSG("INFO: downstream port is configured to \"%s\"\n", serv_port_down);
}
7、解析并设置下行保持连接的间隔时间
8、解析并设置统计信息显示的间隔时间
9、解析并设置上行数据包的超时时间
10、解析并设置数据包过滤参数
11、解析并设置 GPS 模块的 TTY 路径
12、解析并设置参考坐标
13、解析并设置假 GPS 选项
14、解析并设置信标参数
信标周期、信标频率、信标信道数量、信道频率步长、信标数据速率、信标带宽、信标发射功率。
五、local_conf.json配置文件
这个配置文件比较简单,下载后的源代码如下:
{
/* Put there parameters that are different for each gateway (eg. pointing one gateway to a test server while the others
stay in production) */
/* Settings defined in global_conf will be overwritten by those in local_conf */"gateway_conf": {"gateway_ID": "AA555A0000000101" /* you must pick a unique 64b number for each gateway (represented by an hex
string) */}
}
gateway_conf是这个对象包含特定于网关的配置。gateway_ID是这是网关的唯一标识符。每个网关必须有一个唯一的64位十六进制字符串。
但是要注意,global_conf中定义的设置会被local_conf中的设置覆盖。
六、global_conf.json配置文件
这是一个用于配置LoRa网关的完整配置文件,其中包含了SX1301和网关的详细设置。对于移植该源代码来说,是很重要的。
配置文件分为两个主要部分:SX1301_conf
和 gateway_conf
。SX1301_conf
部分主要是关于 LoRa 网关的 SX1301 基带芯片的配置。gateway_conf
部分则是关于网关本身的一些设置。接下来我进行逐一整理。
SX1301_conf
"SX1301_conf": {"lorawan_public": true,"clksrc": 1, /* radio_1 提供集中器的时钟 */
- lorawan_public: 设置为
true
表示使用公共的 LoRaWAN 网络。如果是私有网络,可以设置为false
。 - clksrc: 设置时钟源为
1
,表示使用radio_1
作为时钟源。
"lbt_cfg": {"enable": false,"rssi_target": -80, /* dBm */"chan_cfg": [ /* 最多8个频道 */{ "freq_hz": 867100000, "scan_time_us": 128 },{ "freq_hz": 867300000, "scan_time_us": 5000 },{ "freq_hz": 867500000, "scan_time_us": 128 },{ "freq_hz": 869525000, "scan_time_us": 128 }],"sx127x_rssi_offset": -4 /* dB */},
lbt_cfg: 侦听前发送(LBT)配置。
- enable: 设置为
false
表示禁用 LBT。 - rssi_target: 目标 RSSI 值,为 -80 dBm。
- chan_cfg: 配置各个频道的频率和扫描时间,最多可配置8个频道。
- sx127x_rssi_offset: 设置 RSSI 的偏移量为 -4 dB。
"antenna_gain": 0, /* 天线增益,以dBi表示 */
antenna_gain: 天线增益,单位是 dBi。这里设置为 0。
"radio_0": {"enable": true,"type": "SX1257","freq": 867500000,"rssi_offset": -166.0,"tx_enable": true,"tx_notch_freq": 129000, /* [126..250] KHz */"tx_freq_min": 863000000,"tx_freq_max": 870000000},"radio_1": {"enable": true,"type": "SX1257","freq": 868500000,"rssi_offset": -166.0,"tx_enable": false},
radio_0 和 radio_1: 配置两个无线电模块。
- enable: 设置为
true
表示启用该无线电模块。 - type: 无线电模块的类型,这里是
SX1257
。 - freq: 无线电模块的频率。
- rssi_offset: RSSI 偏移量。
- tx_enable: 是否启用传输(
true
表示启用)。 - tx_notch_freq: 传输的凹陷频率。
- tx_freq_min 和 tx_freq_max: 传输频率的最小值和最大值(仅在
radio_0
中配置)。
"chan_multiSF_0": {/* Lora MAC 频道,125kHz,所有SF,868.1 MHz */"enable": true,"radio": 1,"if": -400000},...
chan_multiSF_0 到 chan_multiSF_7: 配置多个 LoRa MAC 频道。
- enable: 设置为
true
表示启用该频道。 - radio: 使用的无线电模块(0 或 1)。
- if: 中频偏移。
"chan_Lora_std": {/* Lora MAC 频道,250kHz,SF7,868.3 MHz */"enable": true,"radio": 1,"if": -200000,"bandwidth": 250000,"spread_factor": 7},
chan_Lora_std: 配置标准 LoRa 频道。
- enable: 启用该频道。
- radio: 使用的无线电模块。
- if: 中频偏移。
- bandwidth: 带宽。
- spread_factor: 扩频因子。
"chan_FSK": {/* FSK 50kbps 频道,868.8 MHz */"enable": true,"radio": 1,"if": 300000,"bandwidth": 125000,"datarate": 50000},
chan_FSK: 配置 FSK 频道。
- enable: 启用该频道。
- radio: 使用的无线电模块。
- if: 中频偏移。
- bandwidth: 带宽。
- datarate: 数据速率。
"tx_lut_0": {/* TX 增益表,索引0 */"pa_gain": 0,"mix_gain": 8,"rf_power": -6,"dig_gain": 0},...
tx_lut_0 到 tx_lut_15: 配置传输增益表。
- pa_gain: 功率放大器增益。
- mix_gain: 混合增益。
- rf_power: 射频功率。
- dig_gain: 数字增益。
gateway_conf
"gateway_conf": {"gateway_ID": "AA555A0000000000",/* 更改默认服务器地址/端口,或在local_conf.json中覆盖 */"server_address": "localhost","serv_port_up": 1680,"serv_port_down": 1680,
- gateway_ID: 网关的唯一标识符,这里是
AA555A0000000000
。 - server_address: 服务器地址,这里设置为
localhost
。 - serv_port_up 和 serv_port_down: 上行和下行数据的服务器端口,默认是
1680
。
/* 根据您的网络调整以下参数 */"keepalive_interval": 10,"stat_interval": 30,"push_timeout_ms": 100,
- keepalive_interval: 保持连接的时间间隔,单位是秒,这里设置为
10
秒。 - stat_interval: 统计信息的时间间隔,单位是秒,这里设置为
30
秒。 - push_timeout_ms: 推送超时时间,单位是毫秒,这里设置为
100
毫秒。
/* 仅转发有效的数据包 */"forward_crc_valid": true,"forward_crc_error": false,"forward_crc_disabled": false
}
- forward_crc_valid: 仅转发CRC校验有效的数据包,这里设置为
true
。 - forward_crc_error: 是否转发CRC校验错误的数据包,这里设置为
false
。 - forward_crc_disabled: 是否转发未进行CRC校验的数据包,这里设置为
false
。