LoRaWAN网关源码分析(V1.0.2)

一,前言

在做LoraWAN网关的时候,想单纯的跑通很简单,但是你要明白网关里面是如何进行操作来实现这么一个过度的角色,那还要把源码仔细的分析下,但里面的源码是由美国Semtech(先科电子)提供的,注释都是英文,这几天把网关源码整理了一下,分享给大家来理解LoRaWAN中上行数据和下行数据是如何传输的。

二,上行

2.1,上行的过程

节点包括由应用层最开始产生的数据,然后再到数据链路层的MAC(媒体访问控制层),最后再由物理层产生的I/Q(同向数字正交信号)再由sx1278射频芯片变成无线电模拟信号发出,有效的消息是payload

集中器收到无线电模拟信号后,会通过8个IF信道解调成数字信号,然后纠错,再将数据存储在Lora集中器FIFO和数据缓冲区中,集中器这边一段时间会通过函数lgw_receive通过SPI从寄存器里面拿出数据(这个数据包括payload和元数据,元数据是这个负载的出处,比如说来自哪一路RF,哪一路IF等)存放到结构体struct lgw_pkt_rx_s,网关程序中的上行线程thread_up会在开始调用函数lgw_receive并创建一个指针来接受结构体数组rxpkt(每一个数组就是一个包)来解析里面的数据并依次封装到buff_up里面,发送PUSH_DATA包给服务器,发送完成后服务器会下发一个PUSH_ACK的包来确认已经收到了消息。

2.2 上行协议及源码分析

在这里插入图片描述
源码分析,主要是硬件抽象层的lgw_recive函数和网关程序中的thread_up子线程:

int lgw_receive(uint8_t max_pkt, struct lgw_pkt_rx_s *pkt_data) {
    int nb_pkt_fetch;                     /* 循环变量和返回值*/
    struct lgw_pkt_rx_s *p;               /* 指向结构数组中当前结构的指针 */
    uint8_t buff[255+RX_METADATA_NB];     /* 缓冲区来存储SPI读取突发的结果*/
    unsigned sz;                          /*有效载荷的大小,用于处理元数据。 */
    int ifmod;                            /* 接收数据包的if_Chain/MODEM类型 */
    int stat_fifo;                        /* FIFO中指示的数据包状态。 */
    uint32_t raw_timestamp;               /*触发内部“RX完成”时的时间戳 */
    uint32_t delay_x, delay_y, delay_z;   /* 时间戳偏移计算的临时变量 */
    uint32_t timestamp_correction;        /* 处理延迟帐户的更正 */
    uint32_t sf, cr, bw_pow, crc_en, ppm; /* used to calculate timestamp correction */

    /* 检查集中器是否正在运行。*/
    if (lgw_is_started == false) {
        DEBUG_MSG("ERROR: CONCENTRATOR IS NOT RUNNING, START IT BEFORE RECEIVING\n");
        return LGW_HAL_ERROR;
    }

    /* 检查输入变量,即参数合法性*/
    if ((max_pkt <= 0) || (max_pkt > LGW_PKT_FIFO_SIZE)) {
        DEBUG_PRINTF("ERROR: %d = INVALID MAX NUMBER OF PACKETS TO FETCH\n", max_pkt);
        return LGW_HAL_ERROR;
    }
    CHECK_NULL(pkt_data);

    /* 初始化 buffer */
    memset (buff, 0, sizeof buff);

    /*  最多循环max_pkt次,max_pkt为FIFO中数据的个数,最多为8个 */
    for (nb_pkt_fetch = 0; nb_pkt_fetch < max_pkt; ++nb_pkt_fetch) {

        /* p 指向结构体数组中的第nb_pkt_fetch个结构体 */
        p = &pkt_data[nb_pkt_fetch];

        /* 通过spi获取所有RX的FIFO数据,通过读寄存器的方式 */
        lgw_reg_rb(LGW_RX_PACKET_DATA_FIFO_NUM_STORED, buff, 5);
        /* 0:  RX数据缓冲区中可用的数据包数 */
        /* 1,2: RX数据缓冲区中当前数据包的启动地址 */
        /* 3:   当前包的CRC状况 */
        /* 4:   当前包的有效信息段大小 */

        /* RX缓冲区中有多少个数据包?为0则结束循环 */
        if (buff[0] == 0) {
            break; /* 不再需要获取数据包,退出for循环 */
        }

        /* 包的健全性检查 */
        if (buff[0] > LGW_PKT_FIFO_SIZE) {
            DEBUG_PRINTF("WARNING: %u = INVALID NUMBER OF PACKETS TO FETCH, ABORTING\n", buff[0]);
            break;
        }

        /*打印BUF中的值*/
        DEBUG_PRINTF("FIFO content: %x %x %x %x %x\n", buff[0], buff[1], buff[2], buff[3], buff[4]);

        p->size = buff[4];   //将payload大小赋值给结构体中的size字段
        sz = p->size;        //同时也赋值给sz
        stat_fifo = buff[3]; /* crc的状态,将在以后使用,需要在覆盖buff之前保存它 */

        /* 通过SPI获取有效数据payload和metadata,通过读寄存器的方式,前sz个字节是载荷,后面是关于这个数据一些出处*/
        lgw_reg_rb(LGW_RX_DATA_BUF_DATA, buff, sz+RX_METADATA_NB);

        /* 将 payload 复制到结构体的payload字段 */
        memcpy((void *)p->payload, (void *)buff, sz);

        /* 关于数据来源的一些参数,比如哪个信道,哪种模式等 */

        p->if_chain = buff[sz+0];  //数据来自IF0-IF7的哪一个
        if (p->if_chain >= LGW_IF_CHAIN_NB) {
            DEBUG_PRINTF("WARNING: %u NOT A VALID IF_CHAIN NUMBER, ABORTING\n", p->if_chain);
            break;
        }
        ifmod = ifmod_config[p->if_chain];          //得到这个IF的工作模式
        DEBUG_PRINTF("[%d %d]\n", p->if_chain, ifmod);

        p->rf_chain = (uint8_t)if_rf_chain[p->if_chain];       //得到来自哪一路radio的信号
        p->freq_hz = (uint32_t)((int32_t)rf_rx_freq[p->rf_chain] + if_freq[p->if_chain]); //得到IF的中心频率
        p->rssi = (float)buff[sz+5] + rf_rssi_offset[p->rf_chain]; //计算信号的强度

        if ((ifmod == IF_LORA_MULTI) || (ifmod == IF_LORA_STD)) {  //如果是8个在普通模式和标准模式的一种
            DEBUG_MSG("Note: LoRa packet\n");
            switch(stat_fifo & 0x07) {                             //FIFO中指示的数据包状态
                case 5:
                    p->status = STAT_CRC_OK;//0101
                    crc_en = 1;
                    break;
                case 7:
                    p->status = STAT_CRC_BAD;//0111
                    crc_en = 1;
                    break;
                case 1:
                    p->status = STAT_NO_CRC;//0001
                    crc_en = 0;
                    break;
                default:
                    p->status = STAT_UNDEFINED;
                    crc_en = 0;
            }
            p->modulation = MOD_LORA;    //工作在LORA模式
            p->snr = ((float)((int8_t)buff[sz+2]))/4; //得出信噪比
            p->snr_min = ((float)((int8_t)buff[sz+3]))/4;//得到最小分组信噪比
            p->snr_max = ((float)((int8_t)buff[sz+4]))/4;//得到最大信噪比
            
            if (ifmod == IF_LORA_MULTI) {    //如果是普通8信道
                p->bandwidth = BW_125KHZ; /* 固定带宽125Khz */
            } else {
                p->bandwidth = lora_rx_bw; /* 配置文件中获取 */
            }
            sf = (buff[sz+1] >> 4) & 0x0F; //得到是SF扩频因子
            switch (sf) {
                case 7: p->datarate = DR_LORA_SF7; break;
                case 8: p->datarate = DR_LORA_SF8; break;
                case 9: p->datarate = DR_LORA_SF9; break;
                case 10: p->datarate = DR_LORA_SF10; break;
                case 11: p->datarate = DR_LORA_SF11; break;
                case 12: p->datarate = DR_LORA_SF12; break;
                default: p->datarate = DR_UNDEFINED;
            }
            cr = (buff[sz+1] >> 1) & 0x07;    //得到cs编码速率
            switch (cr) {
                case 1: p->coderate = CR_LORA_4_5; break;
                case 2: p->coderate = CR_LORA_4_6; break;
                case 3: p->coderate = CR_LORA_4_7; break;
                case 4: p->coderate = CR_LORA_4_8; break;
                default: p->coderate = CR_UNDEFINED;
            }

            /* 确定“PPM脉冲时间调制”是否开启,是否需要时间戳校正 */
            if (SET_PPM_ON(p->bandwidth,p->datarate)) {
                ppm = 1;
            } else {
                ppm = 0;
            }

            /* 时间戳校正码,基延迟 */
            if (ifmod == IF_LORA_STD) { /* 如果在独立的Lora调制解调器上接收到数据包,他的带宽可以有三种,配置文件给出 */
                switch (lora_rx_bw) {
                    case BW_125KHZ:
                        delay_x = 64;
                        bw_pow = 1;
                        break;
                    case BW_250KHZ:
                        delay_x = 32;
                        bw_pow = 2;
                        break;
                    case BW_500KHZ:
                        delay_x = 16;
                        bw_pow = 4;
                        break;
                    default:
                        DEBUG_PRINTF("ERROR: UNEXPECTED VALUE %d IN SWITCH STATEMENT\n", p->bandwidth);
                        delay_x = 0;
                        bw_pow = 0;
                }
            } else { /*在其中一个传感器信道上接收数据包为125 khz */
                delay_x = 114;
                bw_pow = 1;
            }

            /* 时间戳校正码,可变延迟 */
            if ((sf >= 6) && (sf <= 12) && (bw_pow > 0)) {
                if ((2*(sz + 2*crc_en) - (sf-7)) <= 0) { /* 有效载荷完全符合前8个符号。 */
                    delay_y = ( ((1<<(sf-1)) * (sf+1)) + (3 * (1<<(sf-4))) ) / bw_pow;
                    delay_z = 32 * (2*(sz+2*crc_en) + 5) / bw_pow;
                } else {
                    delay_y = ( ((1<<(sf-1)) * (sf+1)) + ((4 - ppm) * (1<<(sf-4))) ) / bw_pow;
                    delay_z = (16 + 4*cr) * (((2*(sz+2*crc_en)-sf+6) % (sf - 2*ppm)) + 1) / bw_pow;
                }
                timestamp_correction = delay_x + delay_y + delay_z;
            } else {
                timestamp_correction = 0;
                DEBUG_MSG("WARNING: invalid packet, no timestamp correction\n");
            }

            /* RSSI(接收信号强度) 校正 */
            if (ifmod == IF_LORA_MULTI) {
                p->rssi -= RSSI_MULTI_BIAS;
            }

        } else if (ifmod == IF_FSK_STD) {         //如果是FSK信道
            DEBUG_MSG("Note: FSK packet\n");
            switch(stat_fifo & 0x07) {
                case 5:
                    p->status = STAT_CRC_OK;
                    break;
                case 7:
                    p->status = STAT_CRC_BAD;
                    break;
                case 1:
                    p->status = STAT_NO_CRC;
                    break;
                default:
                    p->status = STAT_UNDEFINED;
                    break;
            }
            p->modulation = MOD_FSK;
            p->snr = -128.0;
            p->snr_min = -128.0;
            p->snr_max = -128.0;
            p->bandwidth = fsk_rx_bw;
            p->datarate = fsk_rx_dr;
            p->coderate = CR_UNDEFINED;
            timestamp_correction = ((uint32_t)680000 / fsk_rx_dr) - 20;

            /* RSSI 校正 */
            p->rssi = RSSI_FSK_POLY_0 + RSSI_FSK_POLY_1 * p->rssi + RSSI_FSK_POLY_2 * pow(p->rssi, 2);
        } else {
            DEBUG_MSG("ERROR: UNEXPECTED PACKET ORIGIN\n");
            p->status = STAT_UNDEFINED;
            p->modulation = MOD_UNDEFINED;
            p->rssi = -128.0;
            p->snr = -128.0;
            p->snr_min = -128.0;
            p->snr_max = -128.0;
            p->bandwidth = BW_UNDEFINED;
            p->datarate = DR_UNDEFINED;
            p->coderate = CR_UNDEFINED;
            timestamp_correction = 0;
        }

        raw_timestamp = (uint32_t)buff[sz+6] + ((uint32_t)buff[sz+7] << 8) + ((uint32_t)buff[sz+8] << 16) + ((uint32_t)buff[sz+9] << 24);
        p->count_us = raw_timestamp - timestamp_correction;
        p->crc = (uint16_t)buff[sz+10] + ((uint16_t)buff[sz+11] << 8);

        /* 完成此次 */
        lgw_reg_w(LGW_RX_PACKET_DATA_FIFO_NUM_STORED, 0);
    }

    return nb_pkt_fetch;
}


               /*用于网关程序管理和服务器通信的线程*/
void thread_up(void)
{
    int i, j;              /* 循环变量 */
    unsigned pkt_in_dgram; /* 注意当前数据报中的Lora数据包 */

    /* 为数据包的获取和处理分配内存 */
    struct lgw_pkt_rx_s rxpkt[NB_PKT_MAX]; /* 数组包含入站数据包和元数据 */
    struct lgw_pkt_rx_s *p;                /* 指向RX包的指针 */
    int    nb_pkt;

    /* GPS时间基准的局部拷贝 */
    bool ref_ok = false;   /* 确定是否必须使用GPS时间基准 */
    struct tref local_ref; /* 用于UTC<->时间戳转换的时间基准*/

    /* data buffers */
    uint8_t buff_up[TX_BUFF_SIZE]; /* 组成上行分组的缓冲buff */
    int buff_index;
    uint8_t buff_ack[32]; /* 确认接收的buffer  */

    /* protocol 变量 */
    uint8_t token_h; /* 交流知识匹配的随机标记 */
    uint8_t token_l; /* 交流知识匹配的随机标记 */

    /* ping 测试变量 */
    struct timespec send_time;
    struct timespec recv_time;

    /* GPS同步变量 */
    struct timespec pkt_utc_time;
    struct tm *x; /* 分解UTC时间*/
    struct timespec pkt_gps_time;
    uint64_t pkt_gps_time_ms;

    /* 报道管理变量 */
    bool send_report = false;

    /* mote 信息变量 */
    uint32_t mote_addr = 0;
    uint16_t mote_fcnt = 0;

    /*eLoRa */
    mqtt_pack_t ep;
    struct mosquitto *mosq = NULL;
    int rv = -1;

    time_t timep;
    struct tm *tm;

    /* 设置上游socket的RX超时 */
    i = setsockopt(sock_up, SOL_SOCKET, SO_RCVTIMEO, (void *)&push_timeout_half, sizeof push_timeout_half);
    if (i != 0)
    {
        MSG("ERROR: [up] setsockopt returned %s\n", strerror(errno));
        exit(EXIT_FAILURE);
    }

    /*与mqtt代理建立连接*/
    rv = mqtt_broker_connect(mb, &mosq);
    if (rv < 0)
    {
        MSG("ERROR: Connect mqtt broker is failed!\n");
    }
    else
    {
        MSG("INFO: Connect mqtt broker is successfully!\n");
    }

    /* 用固定字段预填充数据缓冲区 */
    buff_up[0] = PROTOCOL_VERSION;         //protocol版本
    buff_up[3] = PKT_PUSH_DATA;            //通常为0
    *(uint32_t *)(buff_up + 4) = net_mac_h; //网关ID高位
    *(uint32_t *)(buff_up + 8) = net_mac_l;  //网关ID低位

    while (!exit_sig && !quit_sig)          //若没有退出信号
    {

        /* 通过SPI获取包 */
        pthread_mutex_lock(&mx_concent);
        nb_pkt = lgw_receive(NB_PKT_MAX, rxpkt);//调用函数将数据存放在rxpkt,一个结构体数组
        pthread_mutex_unlock(&mx_concent);  //控制与集中器连接的锁
        if (nb_pkt == LGW_HAL_ERROR)  
        {
            MSG("ERROR: [up] failed packet fetch, exiting\n");
            exit(EXIT_FAILURE);
        }

        /* 检查是否已经准备好发送 */
        send_report = report_ready; /* 复制变量,这样它就不会改变中间函数 */
        /* 不用锁,我们只是读不进行操作 */

        /*如果没有数据包,也没有状态报告,请稍候。 */
        if ((nb_pkt == 0) && (send_report == false))
        {
            wait_ms(FETCH_SLEEP_MS);
            continue;
        }

        /* 获取一份全球定位系统时间基准(避免每包一个互斥) */
        if ((nb_pkt > 0) && (gps_enabled == true))
        {
            pthread_mutex_lock(&mx_timeref);
            ref_ok = gps_ref_valid;
            local_ref = time_reference_gps;
            pthread_mutex_unlock(&mx_timeref);
        }
        else
        {
            ref_ok = false;
        }

        /* 使用标头开始合成数据报 */
        token_h = (uint8_t)rand(); /* 随机 token */
        token_l = (uint8_t)rand(); /* 随机 token */
        buff_up[1] = token_h;
        buff_up[2] = token_l;
        buff_index = 12; /* 12个字节的头 */

        /* 启动JSON结构 */
        memcpy((void *)(buff_up + buff_index), (void *)"{\"rxpk\":[", 9);
        buff_index += 9;

        /* 序列化Lora数据包元数据和有效负载 */
        pkt_in_dgram = 0;

        MSG("=======================================\n");
        MSG("INFO: Receive %d sLoRa packets!\n", nb_pkt);   //打印收到数据包的个数
        MSG("=======================================\n");

        for (i = 0; i < nb_pkt; ++i)     //for循环解析每一个数据包
        {

            p = &rxpkt[i];       //p指向这个结构体的地址
            MSG("======================================\n");
            MSG("radio=%d, if=%d Received sLoRa data\n", p->rf_chain, p->if_chain);//哪个RF,多少频偏

            /*收到的包若为TLV包*/
            if (p->payload[0] == mb.header && judge_network(mb.network, mb.network_size, (int)(p->payload[2] << 8) | (p->payload[3])))
            {
              
               /*将要发送的上行数据(sp)中的有用信息提取出来存放到ep中,ep是mqtt发布的消息*/
                parse_sLoRa(&ep, &(p->payload[2]));

                ep.rssi = p->rssi;  //信号强度

                //获取网关时间
                time(&timep);
                tm = localtime(&timep);

                snprintf(ep.time, sizeof(ep.time), "%d-%d-%d %d:%d:%d",
                         (1900 + tm->tm_year), (1 + tm->tm_mon), tm->tm_mday,
                         tm->tm_hour, tm->tm_min, tm->tm_sec);

                mb.json_p = mqtt_pack_json(&ep);   /*将ep中的数据解析成cjson格式发布*/

                printf("Print Received json data:\n%s\n", mb.json_p);  //打印要发布的消息

                rv = mosquitto_publish(mosq, NULL, "sLoRa", strlen(mb.json_p) + 1, mb.json_p, 0, 0); //发布
                if (rv != MOSQ_ERR_SUCCESS)
                {
                    MSG("ERROR: Mqtt publish is failed!\n");
                }
            }

            MSG("\n");
            MSG("=======================================\n");

            /* 从当前数据包(addr,fcnt)获取mote信息 */
            /* FHDR - DevAddr */
            mote_addr = p->payload[1];
            mote_addr |= p->payload[2] << 8;
            mote_addr |= p->payload[3] << 16;
            mote_addr |= p->payload[4] << 24;
            /* FHDR - FCnt */
            mote_fcnt = p->payload[6];
            mote_fcnt |= p->payload[7] << 8;

            /*过滤基础包*/
            pthread_mutex_lock(&mx_meas_up);
            meas_nb_rx_rcv += 1;
            switch (p->status)   //判断CRC状态
            {
            case STAT_CRC_OK:
                meas_nb_rx_ok += 1;
                printf("\nINFO: Received pkt from mote: %08X (fcnt=%u)\n", mote_addr, mote_fcnt);
                if (!fwd_valid_pkt)
                {
                    pthread_mutex_unlock(&mx_meas_up);
                    continue; /* skip that packet */
                }
                break;
            case STAT_CRC_BAD:
                meas_nb_rx_bad += 1;
                if (!fwd_error_pkt)
                {
                    pthread_mutex_unlock(&mx_meas_up);
                    continue; /* skip that packet */
                }
                break;
            case STAT_NO_CRC:
                meas_nb_rx_nocrc += 1;
                if (!fwd_nocrc_pkt)
                {
                    pthread_mutex_unlock(&mx_meas_up);
                    continue; /* skip that packet */
                }
                break;
            default:
                MSG("WARNING: [up] received packet with unknown status %u (size %u, modulation %u, BW %u, DR %u, RSSI %.1f)\n", p->status, p->size, p->modulation, p->bandwidth, p->datarate, p->rssi);
                pthread_mutex_unlock(&mx_meas_up);
                continue; /* 跳过这个包 */
                          // exit(EXIT_FAILURE);
            }
            meas_up_pkt_fwd += 1;  //转发到服务器的无线电分组数加1
            meas_up_payload_byte += p->size;  //发送给上游的无线电有效载荷字节之和
            pthread_mutex_unlock(&mx_meas_up);

            /* 启动数据包,必要时添加包间分隔符。 */
            if (pkt_in_dgram == 0)
            {
                buff_up[buff_index] = '{';
                ++buff_index;
            }
            else
            {
                buff_up[buff_index] = ',';
                buff_up[buff_index + 1] = '{';
                buff_index += 2;
            }

            /* RAW 时间戳,8-17个有用的字符 */
            j = snprintf((char *)(+buff_index), TX_BUFF_SIZE - buff_index, "\"tmst\":%u", p->count_us);
            if (j > 0)
            {
                buff_index += j;
            }
            else
            {
                MSG("ERROR: [up] snprintf failed line %u\n", (__LINE__ - 4));
                exit(EXIT_FAILURE);
            }

            /* 包发送时间 (基于GPS), 37 useful chars */
            if (ref_ok == true)
            {
                /* 将包的时间戳改成UTC绝对时间 */
                j = lgw_cnt2utc(local_ref, p->count_us, &pkt_utc_time);
                if (j == LGW_GPS_SUCCESS)
                {
                    /* 将UNIX时间戳拆分为其日历组件 */
                    x = gmtime(&(pkt_utc_time.tv_sec));

                    j = snprintf((char *)(buff_up + buff_index), TX_BUFF_SIZE - buff_index, ",\"time\":\"%04i-%02i-%02iT%02i:%02i:%02i.%06liZ\"", (x->tm_year) + 1900, (x->tm_mon) + 1, x->tm_mday, x->tm_hour, x->tm_min, x->tm_sec, (pkt_utc_time.tv_nsec) / 1000); /* ISO 8601 format */
                    if (j > 0)
                    {
                        buff_index += j;
                    }
                    else
                    {
                        MSG("ERROR: [up] snprintf failed line %u\n", (__LINE__ - 4));
                        exit(EXIT_FAILURE);
                    }
                }
                /* 将数据包时间戳转换为GPS绝对时间 */
                j = lgw_cnt2gps(local_ref, p->count_us, &pkt_gps_time);
                if (j == LGW_GPS_SUCCESS)
                {
                    pkt_gps_time_ms = pkt_gps_time.tv_sec * 1E3 + pkt_gps_time.tv_nsec / 1E6;
                    j = snprintf((char *)(buff_up + buff_index), TX_BUFF_SIZE - buff_index, ",\"tmms\":%llu",
                                 pkt_gps_time_ms); /* 全球定位系统时间(毫秒)开始于06.Jan.1980 */
                    if (j > 0)
                    {
                        buff_index += j;
                    }
                    else
                    {
                        MSG("ERROR: [up] snprintf failed line %u\n", (__LINE__ - 4));
                        exit(EXIT_FAILURE);
                    }
                }
            }

            /* 分组集中器信道RF和RX频率,34-36个有用的字符. */
            j = snprintf((char *)(buff_up + buff_index), TX_BUFF_SIZE - buff_index, ",\"chan\":%1u,\"rfch\":%1u,\"freq\":%.6lf", p->if_chain, p->rf_chain, ((double)p->freq_hz / 1e6));
            if (j > 0)
            {
                buff_index += j;
            }
            else
            {
                MSG("ERROR: [up] snprintf failed line %u\n", (__LINE__ - 4));
                exit(EXIT_FAILURE);
            }

            /* 包的状态, 9-10 useful chars */
            switch (p->status)
            {
            case STAT_CRC_OK:
                memcpy((void *)(buff_up + buff_index), (void *)",\"stat\":1", 9);
                buff_index += 9;
                break;
            case STAT_CRC_BAD:
                memcpy((void *)(buff_up + buff_index), (void *)",\"stat\":-1", 10);
                buff_index += 10;
                break;
            case STAT_NO_CRC:
                memcpy((void *)(buff_up + buff_index), (void *)",\"stat\":0", 9);
                buff_index += 9;
                break;
            default:
                MSG("ERROR: [up] received packet with unknown status\n");
                memcpy((void *)(buff_up + buff_index), (void *)",\"stat\":?", 9);
                buff_index += 9;
                exit(EXIT_FAILURE);
            }

            /* 包的模式, 13-14 useful chars */
            if (p->modulation == MOD_LORA)
            {
                memcpy((void *)(buff_up + buff_index), (void *)",\"modu\":\"LORA\"", 14);
                buff_index += 14;

                /* SF和BW, 16-19 useful chars */
                switch (p->datarate)
                {
                case DR_LORA_SF7:
                    memcpy((void *)(buff_up + buff_index), (void *)",\"datr\":\"SF7", 12);
                    buff_index += 12;
                    break;
                case DR_LORA_SF8:
                    memcpy((void *)(buff_up + buff_index), (void *)",\"datr\":\"SF8", 12);
                    buff_index += 12;
                    break;
                case DR_LORA_SF9:
                    memcpy((void *)(buff_up + buff_index), (void *)",\"datr\":\"SF9", 12);
                    buff_index += 12;
                    break;
                case DR_LORA_SF10:
                    memcpy((void *)(buff_up + buff_index), (void *)",\"datr\":\"SF10", 13);
                    buff_index += 13;
                    break;
                case DR_LORA_SF11:
                    memcpy((void *)(buff_up + buff_index), (void *)",\"datr\":\"SF11", 13);
                    buff_index += 13;
                    break;
                case DR_LORA_SF12:
                    memcpy((void *)(buff_up + buff_index), (void *)",\"datr\":\"SF12", 13);
                    buff_index += 13;
                    break;
                default:
                    MSG("ERROR: [up] lora packet with unknown datarate\n");
                    memcpy((void *)(buff_up + buff_index), (void *)",\"datr\":\"SF?", 12);
                    buff_index += 12;
                    exit(EXIT_FAILURE);
                }
                switch (p->bandwidth)
                {
                case BW_125KHZ:
                    memcpy((void *)(buff_up + buff_index), (void *)"BW125\"", 6);
                    buff_index += 6;
                    break;
                case BW_250KHZ:
                    memcpy((void *)(buff_up + buff_index), (void *)"BW250\"", 6);
                    buff_index += 6;
                    break;
                case BW_500KHZ:
                    memcpy((void *)(buff_up + buff_index), (void *)"BW500\"", 6);
                    buff_index += 6;
                    break;
                default:
                    MSG("ERROR: [up] lora packet with unknown bandwidth\n");
                    memcpy((void *)(buff_up + buff_index), (void *)"BW?\"", 4);
                    buff_index += 4;
                    exit(EXIT_FAILURE);
                }

                /* Packet ECC 编码速率, 11-13 useful chars */
                switch (p->coderate)
                {
                case CR_LORA_4_5:
                    memcpy((void *)(buff_up + buff_index), (void *)",\"codr\":\"4/5\"", 13);
                    buff_index += 13;
                    break;
                case CR_LORA_4_6:
                    memcpy((void *)(buff_up + buff_index), (void *)",\"codr\":\"4/6\"", 13);
                    buff_index += 13;
                    break;
                case CR_LORA_4_7:
                    memcpy((void *)(buff_up + buff_index), (void *)",\"codr\":\"4/7\"", 13);
                    buff_index += 13;
                    break;
                case CR_LORA_4_8:
                    memcpy((void *)(buff_up + buff_index), (void *)",\"codr\":\"4/8\"", 13);
                    buff_index += 13;
                    break;
                case 0: /* treat the CR0 case (mostly false sync) */
                    memcpy((void *)(buff_up + buff_index), (void *)",\"codr\":\"OFF\"", 13);
                    buff_index += 13;
                    break;
                default:
                    MSG("ERROR: [up] lora packet with unknown coderate\n");
                    memcpy((void *)(buff_up + buff_index), (void *)",\"codr\":\"?\"", 11);
                    buff_index += 11;
                    exit(EXIT_FAILURE);
                }

                /* Lora 信噪比, 11-13 useful chars */
                j = snprintf((char *)(buff_up + buff_index), TX_BUFF_SIZE - buff_index, ",\"lsnr\":%.1f", p->snr);
                if (j > 0)
                {
                    buff_index += j;
                }
                else
                {
                    MSG("ERROR: [up] snprintf failed line %u\n", (__LINE__ - 4));
                    exit(EXIT_FAILURE);
                }
            }
            else if (p->modulation == MOD_FSK)
            {
                memcpy((void *)(buff_up + buff_index), (void *)",\"modu\":\"FSK\"", 13);
                buff_index += 13;

                /* FSK datarate, 11-14 useful chars */
                j = snprintf((char *)(buff_up + buff_index), TX_BUFF_SIZE - buff_index, ",\"datr\":%u", p->datarate);
                if (j > 0)
                {
                    buff_index += j;
                }
                else
                {
                    MSG("ERROR: [up] snprintf failed line %u\n", (__LINE__ - 4));
                    exit(EXIT_FAILURE);
                }
            }
            else
            {
                MSG("ERROR: [up] received packet with unknown modulation\n");
                exit(EXIT_FAILURE);
            }

            /* 包的强度,载荷的大小 18-23 useful chars */
            j = snprintf((char *)(buff_up + buff_index), TX_BUFF_SIZE - buff_index, ",\"rssi\":%.0f,\"size\":%u", p->rssi, p->size);
            if (j > 0)
            {
                buff_index += j;
            }
            else
            {
                MSG("ERROR: [up] snprintf failed line %u\n", (__LINE__ - 4));
                exit(EXIT_FAILURE);
            }

            /* 包的base64位编码载荷, 14-350 useful chars */
            memcpy((void *)(buff_up + buff_index), (void *)",\"data\":\"", 9);
            buff_index += 9;
            j = bin_to_b64(p->payload, p->size, (char *)(buff_up + buff_index), 341); /* 255 bytes = 340 chars in b64 + null char */
            if (j >= 0)
            {
                buff_index += j;
            }
            else
            {
                MSG("ERROR: [up] bin_to_b64 failed line %u\n", (__LINE__ - 5));
                exit(EXIT_FAILURE);
            }
            buff_up[buff_index] = '"';
            ++buff_index;

            /* 封包 */
            buff_up[buff_index] = '}';
            ++buff_index;
            ++pkt_in_dgram;
        }

        /* 如果所有数据包都已过滤掉,则重新启动提取序列而不发送空的JSON*/
        if (pkt_in_dgram == 0)
        {
            if (send_report == true)
            {
                /* 需要清除有效载荷的开始 */
                buff_index -= 8; /* removes "rxpk":[ */
            }
            else
            {
                /* 所有数据包都已过滤掉并没有报告,重新启动循环。 */
                continue;
            }
        }
        else
        {
            /* 将包结尾 */
            buff_up[buff_index] = ']';
            ++buff_index;
            /* 如果需要添加分隔符 */
            if (send_report == true)
            {
                buff_up[buff_index] = ',';
                ++buff_index;
            }
        }

        /* 如果有新的状态报告,则添加状态报告 */
        if (send_report == true)
        {
            pthread_mutex_lock(&mx_stat_rep);
            report_ready = false;
            j = snprintf((char *)(buff_up + buff_index), TX_BUFF_SIZE - buff_index, "%s", status_report);
            pthread_mutex_unlock(&mx_stat_rep);
            if (j > 0)
            {
                buff_index += j;
            }
            else
            {
                MSG("ERROR: [up] snprintf failed line %u\n", (__LINE__ - 5));
                exit(EXIT_FAILURE);
            }
        }

        /* 结束JSON数据报负载 */
        buff_up[buff_index] = '}';
        ++buff_index;
        buff_up[buff_index] = 0; /* 为安全起见,添加字符串终止符 */

        printf("\nJSON up: %s\n", (char *)(buff_up + 12)); /* 打印发送的jison格式里面的数据 */

        /* 将数据报发送到服务器*/
        send(sock_up, (void *)buff_up, buff_index, 0);
        clock_gettime(CLOCK_MONOTONIC, &send_time);
        pthread_mutex_lock(&mx_meas_up);
        meas_up_dgram_sent += 1;
        meas_up_network_byte += buff_index;

        /* 等待服务器下发的PUSH_ACK(2次),捕捉额外的数据包) */
        for (i = 0; i < 2; ++i)
        {
            j = recv(sock_up, (void *)buff_ack, sizeof buff_ack, 0);
            clock_gettime(CLOCK_MONOTONIC, &recv_time);
            if (j == -1)
            {
                if (errno == EAGAIN)
                { /* timeout */
                    continue;
                }
                else
                { /* server connection error */
                    break;
                }
            }
            else if ((j < 4) || (buff_ack[0] != PROTOCOL_VERSION) || (buff_ack[3] != PKT_PUSH_ACK))
            {
                // MSG("WARNING: [up] ignored invalid non-ACL packet\n");
                continue;
            }
            else if ((buff_ack[1] != token_h) || (buff_ack[2] != token_l))
            {
                // MSG("WARNING: [up] ignored out-of sync ACK packet\n");
                continue;
            }
            else
            {
                MSG("INFO: [up] PUSH_ACK received in %i ms\n", (int)(1000 * difftimespec(recv_time, send_time)));
                meas_up_ack_rcv += 1;
                break;
            }
        }
        pthread_mutex_unlock(&mx_meas_up);
    }

    mosquitto_destroy(mosq);
    mosquitto_lib_cleanup();

    MSG("\nINFO: End of upstream thread\n");
}

三,下行

3.1,下行的过程

用到了两个线程来控制下行,thread_up是和服务器进行通信,thread_jit是控制对队列JIT的管理,集中器空闲时上将数据传递给集中器,里面调用了lgw_send函数。

节点收到服务器的PULL_RESP packet以后,在线程thread_down会将buff_down里面的数据解析出来并放到txpkt结构体中(注意这里不是结构体数组,因为下行链路只有一个),收到后会向服务器发一个TX_ACK包确认收到。消息实体里面的playload会通过thread_down里调用函数b64_to_bin转化成二进制,然后将其放到JIT队列中,JIT队列存在的意义是防止集线器和主机消息的冲突,所以这里面设置了一个锁mx_connect来控制对集中器的访问,集中器空闲后调用lgw_send函数(里面对txpkt进行了编码)通过SPI将服务器下发的元数据(包括用哪个信道发送,发送频率等)写到寄存器中,然后集中器从FIFO和数据缓存区拿playload和元数据并调制发送到节点。

3.2,源码分析

主要是网关程序中两个管理下行的线程和硬件抽象层的lgw_send函数:

void thread_down(void)
{
    int i; /* 循环变量 */

    /* 发送包的配置和元数据 */
    struct lgw_pkt_tx_s txpkt;
    bool sent_immediate = false; /* 选项可以立即发送数据包 */

    /*局部计时变量 */
    struct timespec send_time; /* 请求的时间 */
    struct timespec recv_time; /* 收到socket回话的返回时间*/

    /* data buffers */
    uint8_t buff_down[1000]; /* 接收下游数据包的缓冲区 */
    uint8_t buff_req[12];    /* 缓冲区来编写PULL请求 */
    int msg_len;

    /* protocol变量 */
    uint8_t token_h;      /* 随机值 */
    uint8_t token_l;      /* 随机值 */
    bool req_ack = false; /* 跟踪PULL数据是否有ACK回应 */

    /*JSON解析变量*/
    JSON_Value *root_val = NULL;
    JSON_Object *txpk_obj = NULL;
    JSON_Value *val = NULL; /* 需要检测某些字段的缺失 */
    const char *str;        /* 指向JSON数据中子字符串的指针 */
    short x0, x1;
    uint64_t x2;
    double x3, x4;

    /* 要在GPS时间戳上发送的变量 */
    struct tref local_ref;  /* 用于GPS<->时间戳转换的时间基准 */
    struct timespec gps_tx; /* 需要转换为时间戳的GPS时间 */

    /*信标变量*/
    struct lgw_pkt_tx_s beacon_pkt;
    uint8_t beacon_chan;
    uint8_t beacon_loop;
    size_t beacon_RFU1_size = 0;
    size_t beacon_RFU2_size = 0;
    uint8_t beacon_pyld_idx = 0;
    time_t diff_beacon_time;
    struct timespec next_beacon_gps_time; /* 全球定位系统下一个信标包的时间*/
    struct timespec last_beacon_gps_time; /*上一次排队信标分组的GPS时间 */
    int retry;

    /*信标数据字段,字节0是最小的字节。*/
    int32_t field_latitude;  /* 从参考纬度导出的3个字节*/
    int32_t field_longitude; /* 从参考经度导出的3个字节*/
    uint16_t field_crc1, field_crc2;

    /* 自动退出变量 */
    uint32_t autoquit_cnt = 0; /* 计算自最近的PULL_ACK以来发送的PULL_DATA数 */

    /* 及时下行 */
    struct timeval current_unix_time;
    struct timeval current_concentrator_time;
    enum jit_error_e jit_result = JIT_ERROR_OK;
    enum jit_pkt_type_e downlink_type;

    /* 设置下游套接字socket RX 超时时间 */
    i = setsockopt(sock_down, SOL_SOCKET, SO_RCVTIMEO, (void *)&pull_timeout, sizeof pull_timeout);
    if (i != 0)
    {
        MSG("ERROR: [down] setsockopt returned %s\n", strerror(errno));
        exit(EXIT_FAILURE);
    }

    /* 用固定字段预填充PULL_DATA缓冲区 */
    buff_req[0] = PROTOCOL_VERSION;
    buff_req[3] = PKT_PULL_DATA;
    *(uint32_t *)(buff_req + 4) = net_mac_h;
    *(uint32_t *)(buff_req + 8) = net_mac_l;

    /* 信标变量初始化 */
    last_beacon_gps_time.tv_sec = 0;
    last_beacon_gps_time.tv_nsec = 0;

    /* 信标包参数*/
    beacon_pkt.tx_mode = ON_GPS; /* 发送PPS脉冲 */
    beacon_pkt.rf_chain = 0;     /* 天线A */
    beacon_pkt.rf_power = beacon_power;
    beacon_pkt.modulation = MOD_LORA;
    switch (beacon_bw_hz)
    {
    case 125000:
        beacon_pkt.bandwidth = BW_125KHZ;
        break;
    case 500000:
        beacon_pkt.bandwidth = BW_500KHZ;
        break;
    default:
        /* 不应该发生 */
        MSG("ERROR: unsupported bandwidth for beacon\n");
        exit(EXIT_FAILURE);
    }
    switch (beacon_datarate)
    {
    case 8:
        beacon_pkt.datarate = DR_LORA_SF8;
        beacon_RFU1_size = 1;
        beacon_RFU2_size = 3;
        break;
    case 9:
        beacon_pkt.datarate = DR_LORA_SF9;
        beacon_RFU1_size = 2;
        beacon_RFU2_size = 0;
        break;
    case 10:
        beacon_pkt.datarate = DR_LORA_SF10;
        beacon_RFU1_size = 3;
        beacon_RFU2_size = 1;
        break;
    case 12:
        beacon_pkt.datarate = DR_LORA_SF12;
        beacon_RFU1_size = 5;
        beacon_RFU2_size = 3;
        break;
    default:
        /* should not happen */
        MSG("ERROR: unsupported datarate for beacon\n");
        exit(EXIT_FAILURE);
    }
    beacon_pkt.size = beacon_RFU1_size + 4 + 2 + 7 + beacon_RFU2_size + 2;
    beacon_pkt.coderate = CR_LORA_4_5;
    beacon_pkt.invert_pol = false;
    beacon_pkt.preamble = 10;
    beacon_pkt.no_crc = true;
    beacon_pkt.no_header = true;

    /*网络公共部分信标字段(小终端) */
    for (i = 0; i < (int)beacon_RFU1_size; i++)
    {
        beacon_pkt.payload[beacon_pyld_idx++] = 0x0;
    }

    /* 网络公共部分信标字段(小终端) */
    beacon_pyld_idx += 4; /* 时间(变量),稍后填充 */
    beacon_pyld_idx += 2; /* crc1(变量),稍后填充 */

    /* 计算必须公开报告的纬度和经度*/
    field_latitude = (int32_t)((reference_coord.lat / 90.0) * (double)(1 << 23));
    if (field_latitude > (int32_t)0x007FFFFF)
    {
        field_latitude = (int32_t)0x007FFFFF; /* +90 N is represented as 89.99999 N */
    }
    else if (field_latitude < (int32_t)0xFF800000)
    {
        field_latitude = (int32_t)0xFF800000;
    }
    field_longitude = (int32_t)((reference_coord.lon / 180.0) * (double)(1 << 23));
    if (field_longitude > (int32_t)0x007FFFFF)
    {
        field_longitude = (int32_t)0x007FFFFF; /* +180 E is represented as 179.99999 E */
    }
    else if (field_longitude < (int32_t)0xFF800000)
    {
        field_longitude = (int32_t)0xFF800000;
    }

    /*网关专用信标字段*/
    beacon_pkt.payload[beacon_pyld_idx++] = beacon_infodesc;
    beacon_pkt.payload[beacon_pyld_idx++] = 0xFF & field_latitude;
    beacon_pkt.payload[beacon_pyld_idx++] = 0xFF & (field_latitude >> 8);
    beacon_pkt.payload[beacon_pyld_idx++] = 0xFF & (field_latitude >> 16);
    beacon_pkt.payload[beacon_pyld_idx++] = 0xFF & field_longitude;
    beacon_pkt.payload[beacon_pyld_idx++] = 0xFF & (field_longitude >> 8);
    beacon_pkt.payload[beacon_pyld_idx++] = 0xFF & (field_longitude >> 16);

    /* RFU */
    for (i = 0; i < (int)beacon_RFU2_size; i++)
    {
        beacon_pkt.payload[beacon_pyld_idx++] = 0x0;
    }

    /*信标网关的CRC具体部分字*/
    field_crc2 = crc16((beacon_pkt.payload + 6 + beacon_RFU1_size), 7 + beacon_RFU2_size);
    beacon_pkt.payload[beacon_pyld_idx++] = 0xFF & field_crc2;
    beacon_pkt.payload[beacon_pyld_idx++] = 0xFF & (field_crc2 >> 8);

    /* JIT 队列初始化 */
    jit_queue_init(&jit_queue);

    while (!exit_sig && !quit_sig)
    {

        /* 超过阈值时自动退出 */
        if ((autoquit_threshold > 0) && (autoquit_cnt >= autoquit_threshold))
        {
            exit_sig = true;
            MSG("INFO: [down] the last %u PULL_DATA were not ACKed, exiting application\n", autoquit_threshold);
            break;
        }

        /* 为请求生成随机令牌 */
        token_h = (uint8_t)rand(); /* random token */
        token_l = (uint8_t)rand(); /* random token */
        buff_req[1] = token_h;
        buff_req[2] = token_l;

        /* 向服务器发送PULL_DATA包,确保网络路由保持开放状态,确保数据包能双向流动 */
        send(sock_down, (void *)buff_req, sizeof buff_req, 0);
        clock_gettime(CLOCK_MONOTONIC, &send_time);
        pthread_mutex_lock(&mx_meas_dw);
        meas_dw_pull_sent += 1;
        pthread_mutex_unlock(&mx_meas_dw);
        req_ack = false;
        autoquit_cnt++;

        /* 侦听数据包并处理它们,直到必须发送新的PULL_DATA为止 */
        recv_time = send_time;
        while ((int)difftimespec(recv_time, send_time) < keepalive_time)
        {

            /* try to receive a datagram */
            msg_len = recv(sock_down, (void *)buff_down, (sizeof buff_down) - 1, 0);
            clock_gettime(CLOCK_MONOTONIC, &recv_time);

            /* 预分配JIT队列中的信标槽,以检查下行链路冲突 */
            beacon_loop = JIT_NUM_BEACON_IN_QUEUE - jit_queue.num_beacon;
            retry = 0;
            while (beacon_loop && (beacon_period != 0))
            {
                pthread_mutex_lock(&mx_timeref);
                /*在JIT队列中插入信标之前,等待GPS就绪*/
                if ((gps_ref_valid == true) && (xtal_correct_ok == true))
                {

                    /* 为下一个信标计算GPS时间   */
                    /*   LoRaWAN: T = k*beacon_period + TBeaconDelay */
                    /*            with TBeaconDelay = [1.5ms +/- 1µs]*/
                    if (last_beacon_gps_time.tv_sec == 0)
                    {
                        /* 如果没有信标排队,从当前的gps时间获取下一个时隙*/
                        diff_beacon_time = time_reference_gps.gps.tv_sec % ((time_t)beacon_period);
                        next_beacon_gps_time.tv_sec = time_reference_gps.gps.tv_sec +
                                                      ((time_t)beacon_period - diff_beacon_time);
                    }
                    else
                    {
                        /*如果已经有了信标,就把它当作参考 */
                        next_beacon_gps_time.tv_sec = last_beacon_gps_time.tv_sec + beacon_period;
                    }
                    /* 现在,我们可以在引用中添加一个信标周期,以获得下一个信标GPS时间*/
                    next_beacon_gps_time.tv_sec += (retry * beacon_period);
                    next_beacon_gps_time.tv_nsec = 0;

#if DEBUG_BEACON
                    {
                        time_t time_unix;

                        time_unix = time_reference_gps.gps.tv_sec + UNIX_GPS_EPOCH_OFFSET;
                        MSG_DEBUG(DEBUG_BEACON, "GPS-now : %s", ctime(&time_unix));
                        time_unix = last_beacon_gps_time.tv_sec + UNIX_GPS_EPOCH_OFFSET;
                        MSG_DEBUG(DEBUG_BEACON, "GPS-last: %s", ctime(&time_unix));
                        time_unix = next_beacon_gps_time.tv_sec + UNIX_GPS_EPOCH_OFFSET;
                        MSG_DEBUG(DEBUG_BEACON, "GPS-next: %s", ctime(&time_unix));
                    }
#endif

                    /* 将GPS时间转换为集中器时间,并为JIT触发器设置数据包计数器 */
                    lgw_gps2cnt(time_reference_gps, next_beacon_gps_time, &(beacon_pkt.count_us));
                    pthread_mutex_unlock(&mx_timeref);

                    /* 将频率校正应用于信标tx频率 */
                    if (beacon_freq_nb > 1)
                    {
                        beacon_chan = (next_beacon_gps_time.tv_sec / beacon_period) % beacon_freq_nb; /* floor rounding */
                    }
                    else
                    {
                        beacon_chan = 0;
                    }
                    /* 计算信标频率*/
                    beacon_pkt.freq_hz = beacon_freq_hz + (beacon_chan * beacon_freq_step);

                    /* 信标负载时间*/
                    beacon_pyld_idx = beacon_RFU1_size;
                    beacon_pkt.payload[beacon_pyld_idx++] = 0xFF & next_beacon_gps_time.tv_sec;
                    beacon_pkt.payload[beacon_pyld_idx++] = 0xFF & (next_beacon_gps_time.tv_sec >> 8);
                    beacon_pkt.payload[beacon_pyld_idx++] = 0xFF & (next_beacon_gps_time.tv_sec >> 16);
                    beacon_pkt.payload[beacon_pyld_idx++] = 0xFF & (next_beacon_gps_time.tv_sec >> 24);

                    /*计算CRC*/
                    field_crc1 = crc16(beacon_pkt.payload, 4 + beacon_RFU1_size); /* CRC为网络公共部分 */
                    beacon_pkt.payload[beacon_pyld_idx++] = 0xFF & field_crc1;
                    beacon_pkt.payload[beacon_pyld_idx++] = 0xFF & (field_crc1 >> 8);

                    /* 在JIT队列中插入信标PACKET */
                    gettimeofday(&current_unix_time, NULL);
                    get_concentrator_time(&current_concentrator_time, current_unix_time);
                    jit_result = jit_enqueue(&jit_queue, &current_concentrator_time, &beacon_pkt, JIT_PKT_TYPE_BEACON);
                    if (jit_result == JIT_ERROR_OK)
                    {
                        /* 更新状态 */
                        pthread_mutex_lock(&mx_meas_dw);
                        meas_nb_beacon_queued += 1;
                        pthread_mutex_unlock(&mx_meas_dw);

                        /* 队列中还有一个信标 */
                        beacon_loop--;
                        retry = 0;
                        last_beacon_gps_time.tv_sec = next_beacon_gps_time.tv_sec; /* keep this beacon time as reference for next one to be programmed */

                        /* 显示信标有效载荷 */
                        MSG("INFO: Beacon queued (count_us=%u, freq_hz=%u, size=%u):\n", beacon_pkt.count_us, beacon_pkt.freq_hz, beacon_pkt.size);
                        printf("   => ");
                        for (i = 0; i < beacon_pkt.size; ++i)
                        {
                            MSG("%02X ", beacon_pkt.payload[i]);
                        }
                        MSG("\n");
                    }
                    else
                    {
                        MSG_DEBUG(DEBUG_BEACON, "--> beacon queuing failed with %d\n", jit_result);
                        /* 更新状态 */
                        pthread_mutex_lock(&mx_meas_dw);
                        if (jit_result != JIT_ERROR_COLLISION_BEACON)
                        {
                            meas_nb_beacon_rejected += 1;
                        }
                        pthread_mutex_unlock(&mx_meas_dw);
                        /* 如果前面的登记队列失败,我们将在一段时间后重试,直到它成功为止。*/
                        /* 如果GPS已经被解锁了一段时间,那么可能会有很多重试,从上一个信标到一个新的有效的*/
                        retry++;
                        MSG_DEBUG(DEBUG_BEACON, "--> beacon queuing retry=%d\n", retry);
                    }
                }
                else
                {
                    pthread_mutex_unlock(&mx_timeref);
                    break;
                }
            }

            /* 如果没有收到网络消息,请回到侦听sock_down套接字 */
            if (msg_len == -1)
            {
                // MSG("WARNING: [down] recv returned %s\n", strerror(errno)); /* too verbose */
                continue;
            }

            /*如果数据报不遵守协议,只需忽略它。 */
            if ((msg_len < 4) || (buff_down[0] != PROTOCOL_VERSION) || ((buff_down[3] != PKT_PULL_RESP) && (buff_down[3] != PKT_PULL_ACK)))
            {
                MSG("WARNING: [down] ignoring invalid packet len=%d, protocol_version=%d, id=%d\n",
                    msg_len, buff_down[0], buff_down[3]);
                continue;
            }

            /* 如果数据报是ACK,请检查 token */
            if (buff_down[3] == PKT_PULL_ACK)
            {
                if ((buff_down[1] == token_h) && (buff_down[2] == token_l))
                {
                    if (req_ack)
                    {
                        MSG("INFO: [down] duplicate ACK received :)\n");
                    }
                    else
                    { /* 如果这个包没有准备好被确认 */
                        req_ack = true;
                        autoquit_cnt = 0;
                        pthread_mutex_lock(&mx_meas_dw);
                        meas_dw_ack_rcv += 1;
                        pthread_mutex_unlock(&mx_meas_dw);
                        MSG("INFO: [down] PULL_ACK received in %i ms\n", (int)(1000 * difftimespec(recv_time, send_time)));
                    }
                }
                else
                { /* 不同步 token */
                    MSG("INFO: [down] received out-of-sync ACK\n");
                }
                continue;
            }

            /* 数据报是PUR_RESP */
            buff_down[msg_len] = 0;                                                                  /* add string terminator, just to be safe */
            MSG("INFO: [down] PULL_RESP received  - token[%d:%d] :)\n", buff_down[1], buff_down[2]); /* 非常冗长 */
            printf("\nJSON down: %s\n", (char *)(buff_down + 4));                                    /* DEBUG: display JSON payload */

            /* 初始化TX结构并尝试解析JSON */
            memset(&txpkt, 0, sizeof txpkt);
            root_val = json_parse_string_with_comments((const char *)(buff_down + 4)); /* JSON偏移量 */
            if (root_val == NULL)
            {
                MSG("WARNING: [down] invalid JSON, TX aborted\n");
                continue;
            }

            /* 查找JSON子对象‘txpk‘ */
            txpk_obj = json_object_get_object(json_value_get_object(root_val), "txpk");
            if (txpk_obj == NULL)
            {
                MSG("WARNING: [down] no \"txpk\" object in JSON, TX aborted\n");
                json_value_free(root_val);
                continue;
            }

            /* 解析“立即”标记,或目标时间戳,或由GPS转换的UTC时间(强制) */
            i = json_object_get_boolean(txpk_obj, "imme"); /* can be 1 if true, 0 if false, or -1 if not a JSON boolean */
            if (i == 1)
            {
                /*TX程序:立即发送*/
                sent_immediate = true;
                downlink_type = JIT_PKT_TYPE_DOWNLINK_CLASS_C;
                MSG("INFO: [down] a packet will be sent in \"immediate\" mode\n");
            }
            else
            {
                sent_immediate = false;
                val = json_object_get_value(txpk_obj, "tmst");
                if (val != NULL)
                {
                    /* TX过程:发送时间戳值 */
                    txpkt.count_us = (uint32_t)json_value_get_number(val);

                    /*给出了集中器时间戳,我们认为它是A类下行链路 */
                    downlink_type = JIT_PKT_TYPE_DOWNLINK_CLASS_A;
                }
                else
                {
                    /* TX程序:发送GPS时间(转换为时间戳值) */
                    val = json_object_get_value(txpk_obj, "tmms");
                    if (val == NULL)
                    {
                        MSG("WARNING: [down] no mandatory \"txpk.tmst\" or \"txpk.tmms\" objects in JSON, TX aborted\n");
                        json_value_free(root_val);
                        continue;
                    }
                    if (gps_enabled == true)
                    {
                        pthread_mutex_lock(&mx_timeref);
                        if (gps_ref_valid == true)
                        {
                            local_ref = time_reference_gps;
                            pthread_mutex_unlock(&mx_timeref);
                        }
                        else
                        {
                            pthread_mutex_unlock(&mx_timeref);
                            MSG("WARNING: [down] no valid GPS time reference yet, impossible to send packet on specific GPS time, TX aborted\n");
                            json_value_free(root_val);

                            /* 发送tx_ack报文给服务器 */
                            send_tx_ack(buff_down[1], buff_down[2], JIT_ERROR_GPS_UNLOCKED);
                            continue;
                        }
                    }
                    else
                    {
                        MSG("WARNING: [down] GPS disabled, impossible to send packet on specific GPS time, TX aborted\n");
                        json_value_free(root_val);

                        /* 发送tx_ack报文给服务器 */
                        send_tx_ack(buff_down[1], buff_down[2], JIT_ERROR_GPS_UNLOCKED);
                        continue;
                    }

                    /* 从JSON获取GPS时间 */
                    x2 = (uint64_t)json_value_get_number(val);

                    /* 将GPS时间从毫秒转换为时间 */
                    x3 = modf((double)x2 / 1E3, &x4);
                    gps_tx.tv_sec = (time_t)x4;        /* get seconds from integer part */
                    gps_tx.tv_nsec = (long)(x3 * 1E9); /* get nanoseconds from fractional part */

                    /* 将GPS时间转换为时间戳 */
                    i = lgw_gps2cnt(local_ref, gps_tx, &(txpkt.count_us));
                    if (i != LGW_GPS_SUCCESS)
                    {
                        MSG("WARNING: [down] could not convert GPS time to timestamp, TX aborted\n");
                        json_value_free(root_val);
                        continue;
                    }
                    else
                    {
                        MSG("INFO: [down] a packet will be sent on timestamp value %u (calculated from GPS time)\n", txpkt.count_us);
                    }

                    /* 给出了GPS时间戳,我们认为它是B类下行链路 */
                    downlink_type = JIT_PKT_TYPE_DOWNLINK_CLASS_B;
                }
            }

            /*解析“No CRC”标志(可选字段) */
            val = json_object_get_value(txpk_obj, "ncrc");
            if (val != NULL)
            {
                txpkt.no_crc = (bool)json_value_get_boolean(val);
            }

            /*解析目标频率(强制) */
            val = json_object_get_value(txpk_obj, "freq");
            if (val == NULL)
            {
                MSG("WARNING: [down] no mandatory \"txpk.freq\" object in JSON, TX aborted\n");
                json_value_free(root_val);
                continue;
            }
            txpkt.freq_hz = (uint32_t)((double)(1.0e6) * json_value_get_number(val));

            /* 解析用于TX的RF链(强制) */
            val = json_object_get_value(txpk_obj, "rfch");
            if (val == NULL)
            {
                MSG("WARNING: [down] no mandatory \"txpk.rfch\" object in JSON, TX aborted\n");
                json_value_free(root_val);
                continue;
            }
            txpkt.rf_chain = (uint8_t)json_value_get_number(val);

            /* 解析TX功率(可选字段)*/
            val = json_object_get_value(txpk_obj, "powe");
            if (val != NULL)
            {
                txpkt.rf_power = (int8_t)json_value_get_number(val) - antenna_gain;
            }

            /* 解析调制(强制)*/
            str = json_object_get_string(txpk_obj, "modu");
            if (str == NULL)
            {
                MSG("WARNING: [down] no mandatory \"txpk.modu\" object in JSON, TX aborted\n");
                json_value_free(root_val);
                continue;
            }
            if (strcmp(str, "LORA") == 0)
            {
                /* Lora 模式 */
                txpkt.modulation = MOD_LORA;

                /*解析Lora扩展因子和调制带宽(强制)*/
                str = json_object_get_string(txpk_obj, "datr");
                if (str == NULL)
                {
                    MSG("WARNING: [down] no mandatory \"txpk.datr\" object in JSON, TX aborted\n");
                    json_value_free(root_val);
                    continue;
                }
                i = sscanf(str, "SF%2hdBW%3hd", &x0, &x1);
                if (i != 2)
                {
                    MSG("WARNING: [down] format error in \"txpk.datr\", TX aborted\n");
                    json_value_free(root_val);
                    continue;
                }
                switch (x0)
                {
                case 7:
                    txpkt.datarate = DR_LORA_SF7;
                    break;
                case 8:
                    txpkt.datarate = DR_LORA_SF8;
                    break;
                case 9:
                    txpkt.datarate = DR_LORA_SF9;
                    break;
                case 10:
                    txpkt.datarate = DR_LORA_SF10;
                    break;
                case 11:
                    txpkt.datarate = DR_LORA_SF11;
                    break;
                case 12:
                    txpkt.datarate = DR_LORA_SF12;
                    break;
                default:
                    MSG("WARNING: [down] format error in \"txpk.datr\", invalid SF, TX aborted\n");
                    json_value_free(root_val);
                    continue;
                }
                switch (x1)
                {
                case 125:
                    txpkt.bandwidth = BW_125KHZ;
                    break;
                case 250:
                    txpkt.bandwidth = BW_250KHZ;
                    break;
                case 500:
                    txpkt.bandwidth = BW_500KHZ;
                    break;
                default:
                    MSG("WARNING: [down] format error in \"txpk.datr\", invalid BW, TX aborted\n");
                    json_value_free(root_val);
                    continue;
                }

                /*解析ECC编码率(可选字段) */
                str = json_object_get_string(txpk_obj, "codr");
                if (str == NULL)
                {
                    MSG("WARNING: [down] no mandatory \"txpk.codr\" object in json, TX aborted\n");
                    json_value_free(root_val);
                    continue;
                }
                if (strcmp(str, "4/5") == 0)
                    txpkt.coderate = CR_LORA_4_5;
                else if (strcmp(str, "4/6") == 0)
                    txpkt.coderate = CR_LORA_4_6;
                else if (strcmp(str, "2/3") == 0)
                    txpkt.coderate = CR_LORA_4_6;
                else if (strcmp(str, "4/7") == 0)
                    txpkt.coderate = CR_LORA_4_7;
                else if (strcmp(str, "4/8") == 0)
                    txpkt.coderate = CR_LORA_4_8;
                else if (strcmp(str, "1/2") == 0)
                    txpkt.coderate = CR_LORA_4_8;
                else
                {
                    MSG("WARNING: [down] format error in \"txpk.codr\", TX aborted\n");
                    json_value_free(root_val);
                    continue;
                }

                /* 解析信号极性开关(可选字段) */
                val = json_object_get_value(txpk_obj, "ipol");
                if (val != NULL)
                {
                    txpkt.invert_pol = (bool)json_value_get_boolean(val);
                }

                /*解析Lora前导长度(可选字段,强制执行最佳最小值)*/
                val = json_object_get_value(txpk_obj, "prea");
                if (val != NULL)
                {
                    i = (int)json_value_get_number(val);
                    if (i >= MIN_LORA_PREAMB)
                    {
                        txpkt.preamble = (uint16_t)i;
                    }
                    else
                    {
                        txpkt.preamble = (uint16_t)MIN_LORA_PREAMB;
                    }
                }
                else
                {
                    txpkt.preamble = (uint16_t)STD_LORA_PREAMB;
                }
            }
            else if (strcmp(str, "FSK") == 0)
            {
                /* FSK 模式 */
                txpkt.modulation = MOD_FSK;

                /*解析FSK比特率(强制) */
                val = json_object_get_value(txpk_obj, "datr");
                if (val == NULL)
                {
                    MSG("WARNING: [down] no mandatory \"txpk.datr\" object in JSON, TX aborted\n");
                    json_value_free(root_val);
                    continue;
                }
                txpkt.datarate = (uint32_t)(json_value_get_number(val));

                /* 解析频率偏差(强制) */
                val = json_object_get_value(txpk_obj, "fdev");
                if (val == NULL)
                {
                    MSG("WARNING: [down] no mandatory \"txpk.fdev\" object in JSON, TX aborted\n");
                    json_value_free(root_val);
                    continue;
                }
                txpkt.f_dev = (uint8_t)(json_value_get_number(val) / 1000.0); /* JSON value in Hz, txpkt.f_dev in kHz */

                /* 解析FSK前导长度(可选字段,执行最佳最小值) */
                val = json_object_get_value(txpk_obj, "prea");
                if (val != NULL)
                {
                    i = (int)json_value_get_number(val);
                    if (i >= MIN_FSK_PREAMB)
                    {
                        txpkt.preamble = (uint16_t)i;
                    }
                    else
                    {
                        txpkt.preamble = (uint16_t)MIN_FSK_PREAMB;
                    }
                }
                else
                {
                    txpkt.preamble = (uint16_t)STD_FSK_PREAMB;
                }
            }
            else
            {
                MSG("WARNING: [down] invalid modulation in \"txpk.modu\", TX aborted\n");
                json_value_free(root_val);
                continue;
            }

            /* 解析负载长度(强制) */
            val = json_object_get_value(txpk_obj, "size");
            if (val == NULL)
            {
                MSG("WARNING: [down] no mandatory \"txpk.size\" object in JSON, TX aborted\n");
                json_value_free(root_val);
                continue;
            }
            txpkt.size = (uint16_t)json_value_get_number(val);

            /* 分析负载数据(强制)*/
            str = json_object_get_string(txpk_obj, "data");
            if (str == NULL)
            {
                MSG("WARNING: [down] no mandatory \"txpk.data\" object in JSON, TX aborted\n");
                json_value_free(root_val);
                continue;
            }
            i = b64_to_bin(str, strlen(str), txpkt.payload, sizeof txpkt.payload);
            if (i != txpkt.size)
            {
                MSG("WARNING: [down] mismatch between .size and .data size once converter to binary\n");
            }

            /* 从内存中释放JSON解析树 */
            json_value_free(root_val);

            /* 选择TX模式 */
            if (sent_immediate)
            {
                txpkt.tx_mode = IMMEDIATE;
            }
            else
            {
                txpkt.tx_mode = TIMESTAMPED;
            }

            /* 记录测量数据 */
            pthread_mutex_lock(&mx_meas_dw);
            meas_dw_dgram_rcv += 1;          /* 只计数没有JSON错误的数据报*/
            meas_dw_network_byte += msg_len; /* meas_dw_network_byte */
            meas_dw_payload_byte += txpkt.size;
            pthread_mutex_unlock(&mx_meas_dw);

            /* 在连接到队列数据包之前检查tx参数 */
            jit_result = JIT_ERROR_OK;
            if ((txpkt.freq_hz < tx_freq_min[txpkt.rf_chain]) || (txpkt.freq_hz > tx_freq_max[txpkt.rf_chain]))
            {
                jit_result = JIT_ERROR_TX_FREQ;
                MSG("ERROR: Packet REJECTED, unsupported frequency - %u (min:%u,max:%u)\n", txpkt.freq_hz, tx_freq_min[txpkt.rf_chain], tx_freq_max[txpkt.rf_chain]);
            }
            if (jit_result == JIT_ERROR_OK)
            {
                for (i = 0; i < txlut.size; i++)
                {
                    if (txlut.lut[i].rf_power == txpkt.rf_power)
                    {
                        /* 这个射频功率是支持的,我们可以继续 */
                        break;
                    }
                }
                if (i == txlut.size)
                {
                    /* 这个射频功率是不支持的 */
                    jit_result = JIT_ERROR_TX_POWER;
                    MSG("ERROR: Packet REJECTED, unsupported RF power for TX - %d\n", txpkt.rf_power);
                }
            }

            /* 插入要发送到JIT队列中的数据包 */
            if (jit_result == JIT_ERROR_OK)
            {
                gettimeofday(&current_unix_time, NULL);
                get_concentrator_time(&current_concentrator_time, current_unix_time);
                jit_result = jit_enqueue(&jit_queue, &current_concentrator_time, &txpkt, downlink_type);
                if (jit_result != JIT_ERROR_OK)
                {
                    printf("ERROR: Packet REJECTED (jit error=%d)\n", jit_result);
                }
                pthread_mutex_lock(&mx_meas_dw);
                meas_nb_tx_requested += 1;
                pthread_mutex_unlock(&mx_meas_dw);
            }

            /* 发送确认包到服务器 */
            send_tx_ack(buff_down[1], buff_down[2], jit_result);
        }
    }
    MSG("\nINFO: End of downstream thread\n");
}




void thread_jit(void)
{
    int result = LGW_HAL_SUCCESS;
    struct lgw_pkt_tx_s pkt;
    int pkt_index = -1;
    struct timeval current_unix_time;
    struct timeval current_concentrator_time;
    enum jit_error_e jit_result;
    enum jit_pkt_type_e pkt_type;
    uint8_t tx_status;

    while (!exit_sig && !quit_sig)
    {
        wait_ms(10);

        /* 将数据和元数据传输到集中器,并调度tx。*/
        gettimeofday(&current_unix_time, NULL);
        get_concentrator_time(&current_concentrator_time, current_unix_time);
        jit_result = jit_peek(&jit_queue, &current_concentrator_time, &pkt_index);
        if (jit_result == JIT_ERROR_OK)
        {
            if (pkt_index > -1)
            {
                jit_result = jit_dequeue(&jit_queue, pkt_index, &pkt, &pkt_type);
                if (jit_result == JIT_ERROR_OK)
                {
                    /* 更新信标状态 */
                    if (pkt_type == JIT_PKT_TYPE_BEACON)
                    {
                        /* 用xtal误差补偿信标频率 */
                        pthread_mutex_lock(&mx_xcorr);
                        pkt.freq_hz = (uint32_t)(xtal_correct * (double)pkt.freq_hz);
                        MSG_DEBUG(DEBUG_BEACON, "beacon_pkt.freq_hz=%u (xtal_correct=%.15lf)\n", pkt.freq_hz, xtal_correct);
                        pthread_mutex_unlock(&mx_xcorr);

                        /* 更新统计 */
                        pthread_mutex_lock(&mx_meas_dw);
                        meas_nb_beacon_sent += 1;
                        pthread_mutex_unlock(&mx_meas_dw);
                        MSG("INFO: Beacon dequeued (count_us=%u)\n", pkt.count_us);
                    }

                    /* 检查集中器是否可以发送新的packet*/
                    pthread_mutex_lock(&mx_concent); /* 可能得等取完 */
                    result = lgw_status(TX_STATUS, &tx_status);
                    pthread_mutex_unlock(&mx_concent); /* 释放集中器 */
                    if (result == LGW_HAL_ERROR)
                    {
                        MSG("WARNING: [jit] lgw_status failed\n");
                    }
                    else
                    {
                        if (tx_status == TX_EMITTING)
                        {
                            MSG("ERROR: concentrator is currently emitting\n");
                            print_tx_status(tx_status);
                            continue;
                        }
                        else if (tx_status == TX_SCHEDULED)
                        {
                            MSG("WARNING: a downlink was already scheduled, overwritting it...\n");
                            print_tx_status(tx_status);
                        }
                        else
                        {
                            /* Nothing to do */
                        }
                    }

                    /* 将数据包发送到集中器 */
                    pthread_mutex_lock(&mx_concent); /*可能得等取完 */
                    result = lgw_send(pkt);
                    pthread_mutex_unlock(&mx_concent); /*释放集中器 */
                    if (result == LGW_HAL_ERROR)
                    {
                        pthread_mutex_lock(&mx_meas_dw);
                        meas_nb_tx_fail += 1;
                        pthread_mutex_unlock(&mx_meas_dw);
                        MSG("WARNING: [jit] lgw_send failed\n");
                        continue;
                    }
                    else
                    {
                        pthread_mutex_lock(&mx_meas_dw);
                        meas_nb_tx_ok += 1;
                        pthread_mutex_unlock(&mx_meas_dw);
                        MSG_DEBUG(DEBUG_PKT_FWD, "lgw_send done: count_us=%u\n", pkt.count_us);
                    }
                }
                else
                {
                    MSG("ERROR: jit_dequeue failed with %d\n", jit_result);
                }
            }
        }
        else if (jit_result == JIT_ERROR_EMPTY)
        {
            /* Do nothing, it can happen */
        }
        else
        {
            MSG("ERROR: jit_peek failed with %d\n", jit_result);
        }
    }
}




int lgw_send(struct lgw_pkt_tx_s pkt_data) {
    int i, x;
    uint8_t buff[256+TX_METADATA_NB]; /* 缓冲区,以便在SPI写入突发之前准备数据包发送+元数据 */
    uint32_t part_int = 0; /* PLL寄存器值计算的整数部分 */
    uint32_t part_frac = 0; /* 锁相环寄存器值计算的分数部分*/
    uint16_t fsk_dr_div; /* 配置目标数据的除法器 */
    int transfer_size = 0; /* 将数据从主机传输到TX数据库 */
    int payload_offset = 0; /* 启动数据库中的PayLoad内容 */
    uint8_t pow_index = 0; /* 设置固件tx电源的4位值*/
    uint8_t target_mix_gain = 0; /* 用于选择适当的I/Q偏移校正 */
    uint32_t count_trig = 0; /* TX启动延迟校正触发模式下的时间戳值 */
    bool tx_allowed = false;
    uint16_t tx_start_delay;
    bool tx_notch_enable = false;

    /* 检查集中器是否正在运行。 */
    if (lgw_is_started == false) {
        DEBUG_MSG("ERROR: CONCENTRATOR IS NOT RUNNING, START IT BEFORE SENDING\n");
        return LGW_HAL_ERROR;
    }

    /* 检查输入范围(分段故障预防) */
    if (pkt_data.rf_chain >= LGW_RF_CHAIN_NB) {
        DEBUG_MSG("ERROR: INVALID RF_CHAIN TO SEND PACKETS\n");
        return LGW_HAL_ERROR;
    }

    /* 检查输入变量*/
    if (rf_tx_enable[pkt_data.rf_chain] == false) {
        DEBUG_MSG("ERROR: SELECTED RF_CHAIN IS DISABLED FOR TX ON SELECTED BOARD\n");
        return LGW_HAL_ERROR;
    }
    if (rf_enable[pkt_data.rf_chain] == false) {
        DEBUG_MSG("ERROR: SELECTED RF_CHAIN IS DISABLED\n");
        return LGW_HAL_ERROR;
    }
    if (!IS_TX_MODE(pkt_data.tx_mode)) {
        DEBUG_MSG("ERROR: TX_MODE NOT SUPPORTED\n");
        return LGW_HAL_ERROR;
    }
    if (pkt_data.modulation == MOD_LORA) {
        if (!IS_LORA_BW(pkt_data.bandwidth)) {
            DEBUG_MSG("ERROR: BANDWIDTH NOT SUPPORTED BY LORA TX\n");
            return LGW_HAL_ERROR;
        }
        if (!IS_LORA_STD_DR(pkt_data.datarate)) {
            DEBUG_MSG("ERROR: DATARATE NOT SUPPORTED BY LORA TX\n");
            return LGW_HAL_ERROR;
        }
        if (!IS_LORA_CR(pkt_data.coderate)) {
            DEBUG_MSG("ERROR: CODERATE NOT SUPPORTED BY LORA TX\n");
            return LGW_HAL_ERROR;
        }
        if (pkt_data.size > 255) {
            DEBUG_MSG("ERROR: PAYLOAD LENGTH TOO BIG FOR LORA TX\n");
            return LGW_HAL_ERROR;
        }
    } else if (pkt_data.modulation == MOD_FSK) {
        if((pkt_data.f_dev < 1) || (pkt_data.f_dev > 200)) {
            DEBUG_MSG("ERROR: TX FREQUENCY DEVIATION OUT OF ACCEPTABLE RANGE\n");
            return LGW_HAL_ERROR;
        }
        if(!IS_FSK_DR(pkt_data.datarate)) {
            DEBUG_MSG("ERROR: DATARATE NOT SUPPORTED BY FSK IF CHAIN\n");
            return LGW_HAL_ERROR;
        }
        if (pkt_data.size > 255) {
            DEBUG_MSG("ERROR: PAYLOAD LENGTH TOO BIG FOR FSK TX\n");
            return LGW_HAL_ERROR;
        }
    } else {
        DEBUG_MSG("ERROR: INVALID TX MODULATION\n");
        return LGW_HAL_ERROR;
    }

    /* 为Lora 125 kHz启用陷波滤波器 */
    if ((pkt_data.modulation == MOD_LORA) && (pkt_data.bandwidth == BW_125KHZ)) {
        tx_notch_enable = true;
    }

    /* 获取适用于此tx的tx启动延迟 */
    tx_start_delay = lgw_get_tx_start_delay(tx_notch_enable, pkt_data.bandwidth);

    /* TX功率的解释 */
    for (pow_index = txgain_lut.size-1; pow_index > 0; pow_index--) {
        if (txgain_lut.lut[pow_index].rf_power <= pkt_data.rf_power) {
            break;
        }
    }

    /* 加载TX不平衡校正 */
    target_mix_gain = txgain_lut.lut[pow_index].mix_gain;
    if (pkt_data.rf_chain == 0) { /* use radio A calibration table */
        lgw_reg_w(LGW_TX_OFFSET_I, cal_offset_a_i[target_mix_gain - 8]);
        lgw_reg_w(LGW_TX_OFFSET_Q, cal_offset_a_q[target_mix_gain - 8]);
    } else { /* use radio B calibration table */
        lgw_reg_w(LGW_TX_OFFSET_I, cal_offset_b_i[target_mix_gain - 8]);
        lgw_reg_w(LGW_TX_OFFSET_Q, cal_offset_b_q[target_mix_gain - 8]);
    }

    /*从LUT设置数字增益 */
    lgw_reg_w(LGW_TX_GAIN, txgain_lut.lut[pow_index].dig_gain);

    /* 固定元数据、有用的有效载荷和MISC元数据组合*/
    transfer_size = TX_METADATA_NB + pkt_data.size; /*  */
    payload_offset = TX_METADATA_NB; /* start the payload just after the metadata */

    /* 元数据0到2,TX PLL频率*/
    switch (rf_radio_type[0]) { /* 我们假设板上只有一种无线电类型。 */
        case LGW_RADIO_TYPE_SX1255:
            part_int = pkt_data.freq_hz / (SX125x_32MHz_FRAC << 7); /* 整数部分,给出MSB */
            part_frac = ((pkt_data.freq_hz % (SX125x_32MHz_FRAC << 7)) << 9) / SX125x_32MHz_FRAC; /* 小数部分,给出中间部分和LSB */
            break;
        case LGW_RADIO_TYPE_SX1257:
            part_int = pkt_data.freq_hz / (SX125x_32MHz_FRAC << 8); /* 整数部分,给出MSB。 */
            part_frac = ((pkt_data.freq_hz % (SX125x_32MHz_FRAC << 8)) << 8) / SX125x_32MHz_FRAC; /* 小数部分,给出中间部分和LSB */
            break;
        default:
            DEBUG_PRINTF("ERROR: UNEXPECTED VALUE %d FOR RADIO TYPE\n", rf_radio_type[0]);
            break;
    }

    buff[0] = 0xFF & part_int; /*最重要的byte */
    buff[1] = 0xFF & (part_frac >> 8); /* 中间的byte */
    buff[2] = 0xFF & part_frac; /* 最不重要的 Byte */

    /* 元数据3至6,时间戳触发值 */
    /* 必须触发tx状态机 (T0 - lgw_i_tx_start_delay_us) 在t0处开始发出packet */
    if (pkt_data.tx_mode == TIMESTAMPED)
    {
        count_trig = pkt_data.count_us - (uint32_t)tx_start_delay;
        buff[3] = 0xFF & (count_trig >> 24);
        buff[4] = 0xFF & (count_trig >> 16);
        buff[5] = 0xFF & (count_trig >> 8);
        buff[6] = 0xFF &  count_trig;
    }

    /* 参数依赖于调制器  */
    if (pkt_data.modulation == MOD_LORA) {
        /* 元数据7、调制类型、无线电链选择和TX功率 */
        buff[7] = (0x20 & (pkt_data.rf_chain << 5)) | (0x0F & pow_index); /* bit 4 is 0 -> LoRa modulation */

        buff[8] = 0; /*元数据8,未使用 */

        /* 元数据9、CRC、Lora CR和SF*/
        switch (pkt_data.datarate) {
            case DR_LORA_SF7: buff[9] = 7; break;
            case DR_LORA_SF8: buff[9] = 8; break;
            case DR_LORA_SF9: buff[9] = 9; break;
            case DR_LORA_SF10: buff[9] = 10; break;
            case DR_LORA_SF11: buff[9] = 11; break;
            case DR_LORA_SF12: buff[9] = 12; break;
            default: DEBUG_PRINTF("ERROR: UNEXPECTED VALUE %d IN SWITCH STATEMENT\n", pkt_data.datarate);
        }
        switch (pkt_data.coderate) {
            case CR_LORA_4_5: buff[9] |= 1 << 4; break;
            case CR_LORA_4_6: buff[9] |= 2 << 4; break;
            case CR_LORA_4_7: buff[9] |= 3 << 4; break;
            case CR_LORA_4_8: buff[9] |= 4 << 4; break;
            default: DEBUG_PRINTF("ERROR: UNEXPECTED VALUE %d IN SWITCH STATEMENT\n", pkt_data.coderate);
        }
        if (pkt_data.no_crc == false) {
            buff[9] |= 0x80; /* 设置“crc启用”位 */
        } else {
            DEBUG_MSG("Info: packet will be sent without CRC\n");
        }

        /*元数据10,负载大小 */
        buff[10] = pkt_data.size;

        /* 元数据11,隐式标头,调制带宽,PPM偏移量和极性*/
        switch (pkt_data.bandwidth) {
            case BW_125KHZ: buff[11] = 0; break;
            case BW_250KHZ: buff[11] = 1; break;
            case BW_500KHZ: buff[11] = 2; break;
            default: DEBUG_PRINTF("ERROR: UNEXPECTED VALUE %d IN SWITCH STATEMENT\n", pkt_data.bandwidth);
        }
        if (pkt_data.no_header == true) {
            buff[11] |= 0x04; /*设置‘隐式头’位为1 */
        }
        if (SET_PPM_ON(pkt_data.bandwidth,pkt_data.datarate)) {
            buff[11] |= 0x08; /* 将‘PPM偏移’位设置为1 */
        }
        if (pkt_data.invert_pol == true) {
            buff[11] |= 0x10; /* 将‘TX极性’位设置为1 */
        }

        /* 元数据12和13,Lora序言大小*/
        if (pkt_data.preamble == 0) { /* 如果不是显式的,请使用rec修正的Lora前导大小 */
            pkt_data.preamble = STD_LORA_PREAMBLE;
        } else if (pkt_data.preamble < MIN_LORA_PREAMBLE) { /* 强制执行最小前导大小 */
            pkt_data.preamble = MIN_LORA_PREAMBLE;
            DEBUG_MSG("Note: preamble length adjusted to respect minimum LoRa preamble size\n");
        }
        buff[12] = 0xFF & (pkt_data.preamble >> 8);
        buff[13] = 0xFF & pkt_data.preamble;

        /*元数据14和15,未使用 */
        buff[14] = 0;
        buff[15] = 0;

        /* 在AGC固件中现在使用射频MSB来实现SX 1257/55中的大/窄滤波。 */
        buff[0] &= 0x3F; /* 未设置2 MSBS的频率码*/
        if (pkt_data.bandwidth == BW_500KHZ) {
            buff[0] |= 0x80; /* 设置MSB位放大500 kHz BN模拟滤波器 */
        }

        /* 设置msb-1位,以便在需要时启用数字滤波器 */
        if (tx_notch_enable == true) {
            DEBUG_MSG("INFO: Enabling TX notch filter\n");
            buff[0] |= 0x40;
        }
    } else if (pkt_data.modulation == MOD_FSK) {
        /* 元数据7、调制类型、无线电链选择和TX功率 */
        buff[7] = (0x20 & (pkt_data.rf_chain << 5)) | 0x10 | (0x0F & pow_index); /* 位4是1->FSK调制 */

        buff[8] = 0; /* 元数据8,未使用*/

        /*元数据9,频率偏差*/
        buff[9] = pkt_data.f_dev;

        /* 元数据10,有效载荷大小 */
        buff[10] = pkt_data.size;
        /* Todo:如何处理255个字节的packet ?!? */

        /* 元数据11,数据包模式,crc,编码 */
        buff[11] = 0x01 | (pkt_data.no_crc?0:0x02) | (0x02 << 2); /* 则始终处于可变长度数据包模式, whitening, and CCITT CRC if CRC is not disabled  */

        /* 元数据12&13,FSK前导大小*/
        if (pkt_data.preamble == 0) { /* 如果不是显式的,请使用LoraMAC前导大小 */
            pkt_data.preamble = STD_FSK_PREAMBLE;
        } else if (pkt_data.preamble < MIN_FSK_PREAMBLE) { /* 强制执行最小前导大小 */
            pkt_data.preamble = MIN_FSK_PREAMBLE;
            DEBUG_MSG("Note: preamble length adjusted to respect minimum FSK preamble size\n");
        }
        buff[12] = 0xFF & (pkt_data.preamble >> 8);
        buff[13] = 0xFF & pkt_data.preamble;

        /* 元数据14和15,FSK波特率 */
        fsk_dr_div = (uint16_t)((uint32_t)LGW_XTAL_FREQU / pkt_data.datarate); /*好的,在500到250 kbps之间*/
        buff[14] = 0xFF & (fsk_dr_div >> 8);
        buff[15] = 0xFF & fsk_dr_div;

        /* 在可变模式的数据包中插入有效载荷大小 */
        buff[16] = pkt_data.size;
        ++transfer_size; /* 再传输一个字节到TX调制解调器*/
        ++payload_offset; /* 再用一个字节偏移量启动有效负载 */

        /*目前,AGC固件中使用射频NSB来实现SX 1257/55中的大/窄滤波。 */
        buff[0] &= 0x7F; /*FSK总是使用窄频带(Msb To O)。 */

    } else {
        DEBUG_MSG("ERROR: INVALID TX MODULATION..\n");
        return LGW_HAL_ERROR;
    }

    /* 基于TX陷波滤波器的TX启动延迟配置 */
    lgw_reg_w(LGW_TX_START_DELAY, tx_start_delay);

    /* 将有效负载从用户结构复制到包含元数据的缓冲区*/
    memcpy((void *)(buff + payload_offset), (void *)(pkt_data.payload), pkt_data.size);

    /* 重置tx命令标志 */
    lgw_abort_tx();

    /* 将元数据+负载放在TX数据缓冲区中 */
    lgw_reg_w(LGW_TX_DATA_BUF_ADDR, 0);
    lgw_reg_wb(LGW_TX_DATA_BUF_DATA, buff, transfer_size);
    DEBUG_ARRAY(i, transfer_size, buff);

    x = lbt_is_channel_free(&pkt_data, tx_start_delay, &tx_allowed);
    if (x != LGW_LBT_SUCCESS) {
        DEBUG_MSG("ERROR: Failed to check channel availability for TX\n");
        return LGW_HAL_ERROR;
    }
    if (tx_allowed == true) {
        switch(pkt_data.tx_mode) {
            case IMMEDIATE:
                lgw_reg_w(LGW_TX_TRIG_IMMEDIATE, 1);
                break;

            case TIMESTAMPED:
                lgw_reg_w(LGW_TX_TRIG_DELAYED, 1);
                break;

            case ON_GPS:
                lgw_reg_w(LGW_TX_TRIG_GPS, 1);
                break;

            default:
                DEBUG_PRINTF("ERROR: UNEXPECTED VALUE %d IN SWITCH STATEMENT\n", pkt_data.tx_mode);
                return LGW_HAL_ERROR;
        }
    } else {
        DEBUG_MSG("ERROR: Cannot send packet, channel is busy (LBT)\n");
        return LGW_LBT_ISSUE;
    }

    return LGW_HAL_SUCCESS;
}
  • 3
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
Lorawan是一种低功耗广域网络(LPWAN),旨在为物联网设备提供长距离通信和低功耗的连接。Lorawan规范v1.0.2是最新版本的规范,定义了Lorawan网络的通信协议和架构。 Lorawan网络采用了星型拓扑结构,包括终端节点(end device)、网关(gateway)和网络服务器(network server)。终端节点是物联网设备,通过低功耗方式发送数据给网关网关负责接收来自终端节点的数据,并将其转发到网络服务器。网络服务器则处理数据,并将其传递给应用服务器。 Lorawan规范定义了终端节点和网关之间的通信协议。终端节点使用调制方式将数据转换为无线信号,并通过指定的频率频带发送出去。网关接收到信号后,使用多通道接收技术将多个终端节点的信号进行解调和处理。网关将解码后的数据转发给网络服务器。 Lorawan规范还定义了网络服务器和应用服务器之间的通信协议。网络服务器接收到来自网关的数据后,对数据进行分析和处理,并将其转发给应用服务器。应用服务器可以根据需要对数据进行进一步的处理和存储,实现各种物联网应用。 Lorawan规范还考虑了安全性和隐私问题。规范中定义了加密和认证机制,保护数据在传输过程中的安全性。每个终端节点都有唯一的标识符,并且数据传输是端到端加密的。这种安全性保证了物联网设备和数据的安全性。 总之,Lorawan规范v1.0.2Lorawan网络的通信协议和架构的定义,提供了一种低功耗广域网络解决方案,用于物联网设备的长距离通信和连接,并且考虑了安全性和隐私问题。这使得Lorawan成为物联网领域中的重要技术之一。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值