LoRaWAN网关源码分析(配置篇)

目录

一、前言

二、主函数逻辑

三、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_publicclksrc 参数,并设置到 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_confgateway_confSX1301_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_0radio_1: 配置两个无线电模块。

  • enable : 设置为 true 表示启用该无线电模块。

  • type : 无线电模块的类型,这里是 SX1257

  • freq: 无线电模块的频率。

  • rssi_offset: RSSI 偏移量。

  • tx_enable : 是否启用传输(true 表示启用)。

  • tx_notch_freq: 传输的凹陷频率。

  • tx_freq_mintx_freq_max : 传输频率的最小值和最大值(仅在 radio_0 中配置)。

      "chan_multiSF_0": {
          /* Lora MAC 频道,125kHz,所有SF,868.1 MHz */
          "enable": true,
          "radio": 1,
          "if": -400000
      },
      ...
    

chan_multiSF_0chan_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_0tx_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_upserv_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

相关推荐
郁大锤4 天前
linux alsa-lib snd_pcm_open函数源码分析(四)
linux·音频·pcm·源码分析·驱动·alsa
liangsheng_g5 天前
基于Kafka2.1解读Consumer原理
kafka·consumer·源码分析
PersistJiao8 天前
Spark RDD中常用聚合算子源码层面的对比分析
spark·源码分析·rdd·聚合算子
努力的小雨16 天前
微服务架构——不可或缺的注册中心
分布式·源码分析
血染河山24 天前
Paimon lookup store 实现
源码分析·data-lake
liangsheng_g1 个月前
基于Kafka2.1解读Producer原理
java·kafka·源码分析
努力的小雨1 个月前
深入解析Spring AI框架:在Java应用中实现智能化交互的关键
源码分析
努力的小雨1 个月前
深入探索Spring AI:源码分析流式回答
源码分析
努力的小雨1 个月前
深度解析Spring AI:请求与响应机制的核心逻辑
源码分析·ai智能