目录
三、parse_SX1301_configuration函数
四、parse_gateway_configuration函数
一、前言
对主函数的功能不了解的同学,可以看我上一篇文章。本篇文章我主要整理了如何通过实现读取配置,以及配置文件的内容。
二、主函数逻辑
主函数关于读取配置文件的代码如下:
/* 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
。