当前位置: 首页 > news >正文

无线定位之 二 SX1302 网关源码 thread_down 线程详解

前言

笔者计划通过无线定位系列文章、系统的描述 TDOA 无线定位和混合定位相关技术知识点,
并以实践来验证此定位系统精度。

笔者从实践出发、本篇直接走读无线定位系统关键节点、网关 SX1302 源码框架,并在源码走读过程
中、着重分析与无线定位相关的PPS时间的来龙去脉、并在后期文章中以实际代码讲解 TDOA 无线定位
实现过程及多网关综合定位内容,敬请期待。

semtech 公司在 2020年06月份推出 LR1110\LR1120 两款GNSS、WIFI和Lora(LR-HFSS)混合
定位芯片、并提供’定位云服务’的接入、国内与腾讯云合作,腾讯云也提供定位云服务接入,这是
笔者对混合无线定位技术背景简单描述、此用意看官自行审度。

第1节 主程序代码走读

主线程基本功能:
<1>. 读取 *.conf.json 文件内容、并解析内容把变量赋值到相关全局变量中;
<2>. 启动各子线程、子线程清单如下所述;
<3>. 固定周期定时检测gps的时间戳、并上报网关的状态信息;
<4>. 等待退出信号量、网络断开信号量和各子线程退出.

子线程清单.

/* threads */
void thread_up(void);               //> 上行线程:负责接收lora模块的数据、并把数据通过网络上传至网络服务器;
void thread_down(void);             //> 下行线程:负责接收服务器的数据,并把数据通过lora无线下方给终端模块;
void thread_jit(void);              //> jit 下行数据处理线程:
void thread_gps(void);              //> gps 线程时间同步线程
void thread_valid(void);            //> 时钟校正线程
void thread_spectral_scan(void);    //> SCAN扫描线程:

主程序源码基本功能就这么多,笔者就不贴出源码对照了,下面进入我们本章主题 thread_down 线程的代码走读。

第2节 thread_down 程序框架描述

此线程是负责接收网络服务器数据内容,并把此内容下方至 Lora 模块。

线程代码基本逻辑如下:
<1>. 配置网络通讯接收超时、进入接收数据循环主体;
<2>. 最外侧循环主体先发送PULL请求、然后在保活时间内容接收数据;并检测是否有退出线程请求;
<3>. 中间循环主体在网络保活周期内、接收到网络服务器下方的数据,解析json数据并发送ACK内容给网络服务器;
<4>. 最内侧循环主体是获取gps时标同步信息、如果同步时间到、在 jit_enqueue() 函数同步时间;

线程大体功能就是这样、在 2.3 部分源码中笔者做了简单注解;
其中 jit_enqueue() 同步时标函数是关注重点。

2.1 通讯协议数据格式

看一下 lgw_pkt_tx_s 结构体内容定义:

/**
@struct lgw_pkt_tx_s
@brief Structure containing the configuration of a packet to send and a pointer to the payload
*/
struct lgw_pkt_tx_s {uint32_t    freq_hz;        /*!> center frequency of TX */uint8_t     tx_mode;        /*!> select on what event/time the TX is triggered, 发送模式 */uint32_t    count_us;       /*!> timestamp or delay in microseconds for TX trigger, 延时发送 */uint8_t     rf_chain;       /*!> through which RF chain will the packet be sent */int8_t      rf_power;       /*!> TX power, in dBm */uint8_t     modulation;     /*!> modulation to use for the packet */int8_t      freq_offset;    /*!> frequency offset from Radio Tx frequency (CW mode) */uint8_t     bandwidth;      /*!> modulation bandwidth (LoRa only),信道带宽 */uint32_t    datarate;       /*!> TX datarate (baudrate for FSK, SF for LoRa),数据速率 */uint8_t     coderate;       /*!> error-correcting code of the packet (LoRa only),码速率 */bool        invert_pol;     /*!> invert signal polarity, for orthogonal downlinks (LoRa only), 信号极性 */uint8_t     f_dev;          /*!> frequency deviation, in kHz (FSK only) */uint16_t    preamble;       /*!> set the preamble length, 0 for default,前导码长度 */bool        no_crc;         /*!> if true, do not send a CRC in the packet */bool        no_header;      /*!> if true, enable implicit header mode (LoRa), fixed length (FSK) */uint16_t    size;           /*!> payload size in bytes */uint8_t     payload[256];   /*!> buffer containing the payload */
};

此结构体内容注释已经描述很清晰了、我们关注的是 发送模式和延时发送部分内容,此部分都与定位精度相关。

2.2 thread_down 线程网络通讯建立

  /* network socket creation */struct addrinfo hints;struct addrinfo *result; /* store result of getaddrinfo */struct addrinfo *q; /* pointer to move into *result data */char host_name[64];char port_name[64];/* prepare hints to open network sockets */memset(&hints, 0, sizeof hints);hints.ai_family = AF_INET; /* WA: Forcing IPv4 as AF_UNSPEC makes connection on localhost to fail */hints.ai_socktype = SOCK_DGRAM;  //> UDP 通讯方式/* look for server address w/ downstream port */i = getaddrinfo(serv_addr, serv_port_down, &hints, &result);if (i != 0) {MSG("ERROR: [down] getaddrinfo on address %s (port %s) returned %s\n", serv_addr, serv_port_down, gai_strerror(i));exit(EXIT_FAILURE);}/* try to open socket for downstream traffic */for (q=result; q!=NULL; q=q->ai_next) {sock_down = socket(q->ai_family, q->ai_socktype,q->ai_protocol);if (sock_down == -1) continue; /* try next field */else break; /* success, get out of loop */}/* connect so we can send/receive packet with the server only */i = connect(sock_down, q->ai_addr, q->ai_addrlen);if (i != 0) {MSG("ERROR: [down] connect returned %s\n", strerror(errno));exit(EXIT_FAILURE);}/* set downstream socket RX timeout */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);}

网络服务器的ip地址和端口号在 *.conf.json 文件中,如下:

 "gateway_conf": {"gateway_ID": "AA555A0000000000",/* change with default server address/ports */"server_address": "localhost","serv_port_up": 1730,"serv_port_down": 1730,/* adjust the following parameters for your network */"keepalive_interval": 10,"stat_interval": 30,"push_timeout_ms": 100,/* forward only valid packets */"forward_crc_valid": true,"forward_crc_error": false,"forward_crc_disabled": false,/* GPS configuration */"gps_tty_path": "/dev/ttyS0",/* GPS reference coordinates */"ref_latitude": 0.0,"ref_longitude": 0.0,"ref_altitude": 0,/* Beaconing parameters */"beacon_period": 0,"beacon_freq_hz": 869525000,"beacon_datarate": 9,"beacon_bw_hz": 125000,"beacon_power": 14,"beacon_infodesc": 0},

此文件中 server_address 和 serv_port_down 在主程序中解码json文件时、就把此内容放入到 serv_addr 中,由此知道
上行和下行线程采用 UDP socket 方式与网络服务器进行通讯;
其中 Beaconing parameters 样例是 869 MHz 欧洲频道、中国ISM频段是470MHz。

2.3 thread_down 代码框架

线程代码有删减、只提取功能性内容。

void thread_down(void) {int i; /* loop variables *//* configuration and metadata for an outbound packet */struct lgw_pkt_tx_s txpkt;bool sent_immediate = false; /* option to sent the packet immediately *//* local timekeeping variables */struct timespec send_time; /* time of the pull request */struct timespec recv_time; /* time of return from recv socket call *//* data buffers */uint8_t buff_down[1000]; /* buffer to receive downstream packets */uint8_t buff_req[12]; /* buffer to compose pull requests */int msg_len;/* variables to send on GPS timestamp */struct tref local_ref; /* time reference used for GPS <-> timestamp conversion */struct timespec gps_tx; /* GPS time that needs to be converted to timestamp *//* beacon variables */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; /* gps time of next beacon packet */struct timespec last_beacon_gps_time; /* gps time of last enqueued beacon packet *//* beacon variables initialization,构建发送数据对象 */last_beacon_gps_time.tv_sec = 0;last_beacon_gps_time.tv_nsec = 0;/* beacon packet parameters */beacon_pkt.tx_mode = ON_GPS; /* send on PPS pulse */beacon_pkt.rf_chain = 0; /* antenna A */beacon_pkt.rf_power = beacon_power;beacon_pkt.modulation = MOD_LORA;beacon_pkt.bandwidth = BW_125KHZ;beacon_pkt.datarate = DR_LORA_SF8;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;/* calculate the latitude and longitude that must be publicly reported */field_latitude = (int32_t)((reference_coord.lat / 90.0) * (double)(1<<23));field_longitude = (int32_t)((reference_coord.lon / 180.0) * (double)(1<<23));/* gateway specific beacon fields */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);while (!exit_sig && !quit_sig) {/* auto-quit if the threshold is crossed */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;}/* send PULL request and record time, 向网络服务器发送 PULL请求、获取任务队列内容 */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++;/* listen to packets and process them until a new PULL request must be sent */recv_time = send_time;while (((int)difftimespec(recv_time, send_time) < keepalive_time) && !exit_sig && !quit_sig) {/* try to receive a datagram, 如果网络服务器对应的网关任务列表中有任务就下方 */msg_len = recv(sock_down, (void *)buff_down, (sizeof buff_down)-1, 0);clock_gettime(CLOCK_MONOTONIC, &recv_time);while (beacon_loop && (beacon_period != 0)) {pthread_mutex_lock(&mx_timeref);/* Wait for GPS to be ready before inserting beacons in JiT queue */if ((gps_ref_valid == true) && (xtal_correct_ok == true)) {/* compute GPS time for next beacon to come      *//*   LoRaWAN: T = k*beacon_period + TBeaconDelay *//*            with TBeaconDelay = [1.5ms +/- 1µs]*/if (last_beacon_gps_time.tv_sec == 0) {/* if no beacon has been queued, get next slot from current GPS time */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 {/* if there is already a beacon, take it as reference */next_beacon_gps_time.tv_sec = last_beacon_gps_time.tv_sec + beacon_period;}/* now we can add a beacon_period to the reference to get next beacon GPS time */next_beacon_gps_time.tv_sec += (retry * beacon_period);next_beacon_gps_time.tv_nsec = 0;/* convert GPS time to concentrator time, and set packet counter for JiT trigger */lgw_gps2cnt(time_reference_gps, next_beacon_gps_time, &(beacon_pkt.count_us));/* load time in beacon payload */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);/* calculate CRC */field_crc1 = crc16(beacon_pkt.payload, 4 + beacon_RFU1_size); /* CRC for the network common part */beacon_pkt.payload[beacon_pyld_idx++] = 0xFF & field_crc1;beacon_pkt.payload[beacon_pyld_idx++] = 0xFF & (field_crc1 >> 8);//> 获取 beacon_pkt 时间、并同步时间jit_result = jit_enqueue(&jit_queue[0], current_concentrator_time, &beacon_pkt, JIT_PKT_TYPE_BEACON);                    }//> 退出 beacon_loop 循环break;}//> 接收数据内容解析,数据格式不符合格式要求就放弃数据内容./* if the datagram does not respect protocol, just ignore it */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数据包、也放弃接收到的数据内容/* if the datagram is an ACK, check 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 { /* if that packet was not already acknowledged */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 { /* out-of-sync token */MSG("INFO: [down] received out-of-sync ACK\n");}continue;}//> 接收到数据、并解析网络服务器发送过来的数据内容、处理json串/* initialize TX struct and try to parse JSON */memset(&txpkt, 0, sizeof txpkt);root_val = json_parse_string_with_comments((const char *)(buff_down + 4)); /* JSON offset */if (root_val == NULL) {MSG("WARNING: [down] invalid JSON, TX aborted\n");continue;}/* look for JSON sub-object '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;}/* Parse "immediate" tag, or target timestamp, or UTC time to be converted by GPS (mandatory) */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 procedure: send immediately, 此处定义与Lora 模块之间通讯类型位 CLASS_C 模式 */sent_immediate = true;downlink_type = JIT_PKT_TYPE_DOWNLINK_CLASS_C;MSG("INFO: [down] a packet will be sent in \"immediate\" mode\n");} else {/* GPS timestamp is given, we consider it is a Class B downlink */downlink_type = JIT_PKT_TYPE_DOWNLINK_CLASS_B;}sent_immediate = false;val = json_object_get_value(txpk_obj,"tmst");/* TX procedure: send on GPS time (converted to timestamp value) */val = json_object_get_value(txpk_obj, "tmms");/* Get GPS time from JSON */x2 = (uint64_t)json_value_get_number(val);/* Convert GPS time from milliseconds to timespec */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 *//* transform GPS time to timestamp */i = lgw_gps2cnt(local_ref, gps_tx, &(txpkt.count_us));downlink_type = JIT_PKT_TYPE_DOWNLINK_CLASS_B;/* Parse "No CRC" flag (optional field) */val = json_object_get_value(txpk_obj,"ncrc");/* 处理json串内容下列几项 */val = json_object_get_value(txpk_obj,"nhdr");val = json_object_get_value(txpk_obj,"freq");val = json_object_get_value(txpk_obj,"rfch");val = json_object_get_value(txpk_obj,"powe");str = json_object_get_string(txpk_obj, "modu");str = json_object_get_string(txpk_obj, "datr");str = json_object_get_string(txpk_obj, "codr");val = json_object_get_value(txpk_obj,"ipol");val = json_object_get_value(txpk_obj,"prea");/* Parse payload length (mandatory) */val = json_object_get_value(txpk_obj,"size");/* Parse payload data (mandatory) */str = json_object_get_string(txpk_obj, "data");         }/* Send acknoledge datagram to server */send_tx_ack(buff_down[1], buff_down[2], jit_result, warning_value);}MSG("\nINFO: End of downstream thread\n");
}

在线程初始化时、设置 Lora 网络通讯类型是 CLASS A 模式,网络服务可以通过 imme 字段配置
Lora 通讯网络模式为 CLASS B 或 C 模式。

2.4 jit_enqueue 函数

源码路径@packet_forwarder/src/jitqueue.c

enum jit_error_e jit_enqueue(struct jit_queue_s *queue, uint32_t time_us, struct lgw_pkt_tx_s *packet, enum jit_pkt_type_e pkt_type) {int i = 0;uint32_t packet_post_delay = 0;uint32_t packet_pre_delay = 0;uint32_t target_pre_delay = 0;enum jit_error_e err_collision;uint32_t asap_count_us;MSG_DEBUG(DEBUG_JIT, "Current concentrator time is %u, pkt_type=%d\n", time_us, pkt_type);if (packet == NULL) {MSG_DEBUG(DEBUG_JIT_ERROR, "ERROR: invalid parameter\n");return JIT_ERROR_INVALID;}if (jit_queue_is_full(queue)) {MSG_DEBUG(DEBUG_JIT_ERROR, "ERROR: cannot enqueue packet, JIT queue is full\n");return JIT_ERROR_FULL;}/* Compute packet pre/post delays depending on packet's type */switch (pkt_type) {case JIT_PKT_TYPE_DOWNLINK_CLASS_A:case JIT_PKT_TYPE_DOWNLINK_CLASS_B:case JIT_PKT_TYPE_DOWNLINK_CLASS_C:packet_pre_delay = TX_START_DELAY + TX_JIT_DELAY;packet_post_delay = lgw_time_on_air(packet) * 1000UL; /* in us */break;case JIT_PKT_TYPE_BEACON:/* As defined in LoRaWAN spec */packet_pre_delay = TX_START_DELAY + BEACON_GUARD + TX_JIT_DELAY;packet_post_delay = BEACON_RESERVED;break;default:break;}pthread_mutex_lock(&mx_jit_queue);/* An immediate downlink becomes a timestamped downlink "ASAP" *//* Set the packet count_us to the first available slot */if (pkt_type == JIT_PKT_TYPE_DOWNLINK_CLASS_C) {/* change tx_mode to timestamped */packet->tx_mode = TIMESTAMPED;/* Search for the ASAP timestamp to be given to the packet */asap_count_us = time_us + 2 * TX_JIT_DELAY; /* margin */if (queue->num_pkt == 0) {/* If the jit queue is empty, we can insert this packet */MSG_DEBUG(DEBUG_JIT, "DEBUG: insert IMMEDIATE downlink, first in JiT queue (count_us=%u)\n", asap_count_us);} else {/* Else we can try to insert it:- ASAP meaning NOW + MARGIN- at the last index of the queue- between 2 downlinks in the queue*//* First, try if the ASAP time collides with an already enqueued downlink */for (i=0; i<queue->num_pkt; i++) {if (jit_collision_test(asap_count_us, packet_pre_delay, packet_post_delay, queue->nodes[i].pkt.count_us, queue->nodes[i].pre_delay, queue->nodes[i].post_delay) == true) {MSG_DEBUG(DEBUG_JIT, "DEBUG: cannot insert IMMEDIATE downlink at count_us=%u, collides with %u (index=%d)\n", asap_count_us, queue->nodes[i].pkt.count_us, i);break;}}if (i == queue->num_pkt) {/* No collision with ASAP time, we can insert it */MSG_DEBUG(DEBUG_JIT, "DEBUG: insert IMMEDIATE downlink ASAP at %u (no collision)\n", asap_count_us);} else {/* Search for the best slot then */for (i=0; i<queue->num_pkt; i++) {asap_count_us = queue->nodes[i].pkt.count_us + queue->nodes[i].post_delay + packet_pre_delay + TX_JIT_DELAY + TX_MARGIN_DELAY;if (i == (queue->num_pkt - 1)) {/* Last packet index, we can insert after this one */MSG_DEBUG(DEBUG_JIT, "DEBUG: insert IMMEDIATE downlink, last in JiT queue (count_us=%u)\n", asap_count_us);} else {/* Check if packet can be inserted between this index and the next one */MSG_DEBUG(DEBUG_JIT, "DEBUG: try to insert IMMEDIATE downlink (count_us=%u) between index %d and index %d?\n", asap_count_us, i, i+1);if (jit_collision_test(asap_count_us, packet_pre_delay, packet_post_delay, queue->nodes[i+1].pkt.count_us, queue->nodes[i+1].pre_delay, queue->nodes[i+1].post_delay) == true) {MSG_DEBUG(DEBUG_JIT, "DEBUG: failed to insert IMMEDIATE downlink (count_us=%u), continue...\n", asap_count_us);continue;} else {MSG_DEBUG(DEBUG_JIT, "DEBUG: insert IMMEDIATE downlink (count_us=%u)\n", asap_count_us);break;}}}}}/* Set packet with ASAP timestamp */packet->count_us = asap_count_us;}/* Check criteria_1: is it already too late to send this packet ?*  The packet should arrive at least at (tmst - TX_START_DELAY) to be programmed into concentrator*  Note: - Also add some margin, to be checked how much is needed, if needed*        - Valid for both Downlinks and Beacon packets**  Warning: unsigned arithmetic (handle roll-over)*      t_packet < t_current + TX_START_DELAY + MARGIN*/if ((packet->count_us - time_us) <= (TX_START_DELAY + TX_MARGIN_DELAY + TX_JIT_DELAY)) {MSG_DEBUG(DEBUG_JIT_ERROR, "ERROR: Packet REJECTED, already too late to send it (current=%u, packet=%u, type=%d)\n", time_us, packet->count_us, pkt_type);pthread_mutex_unlock(&mx_jit_queue);return JIT_ERROR_TOO_LATE;}/* Check criteria_2: Does packet timestamp seem plausible compared to current time*  We do not expect the server to program a downlink too early compared to current time*  Class A: downlink has to be sent in a 1s or 2s time window after RX*  Class B: downlink has to occur in a 128s time window*  Class C: no check needed, departure time has been calculated previously*  So let's define a safe delay above which we can say that the packet is out of bound: TX_MAX_ADVANCE_DELAY*  Note: - Valid for Downlinks only, not for Beacon packets**  Warning: unsigned arithmetic (handle roll-over)t_packet > t_current + TX_MAX_ADVANCE_DELAY*/if ((pkt_type == JIT_PKT_TYPE_DOWNLINK_CLASS_A) || (pkt_type == JIT_PKT_TYPE_DOWNLINK_CLASS_B)) {if ((packet->count_us - time_us) > TX_MAX_ADVANCE_DELAY) {MSG_DEBUG(DEBUG_JIT_ERROR, "ERROR: Packet REJECTED, timestamp seems wrong, too much in advance (current=%u, packet=%u, type=%d)\n", time_us, packet->count_us, pkt_type);pthread_mutex_unlock(&mx_jit_queue);return JIT_ERROR_TOO_EARLY;}}/* Check criteria_3: does this new packet overlap with a packet already enqueued ?*  Note: - need to take into account packet's pre_delay and post_delay of each packet*        - Valid for both Downlinks and beacon packets*        - Beacon guard can be ignored if we try to queue a Class A downlink*/for (i=0; i<queue->num_pkt; i++) {/* We ignore Beacon Guard for Class A/C downlinks */if (((pkt_type == JIT_PKT_TYPE_DOWNLINK_CLASS_A) || (pkt_type == JIT_PKT_TYPE_DOWNLINK_CLASS_C)) && (queue->nodes[i].pkt_type == JIT_PKT_TYPE_BEACON)) {target_pre_delay = TX_START_DELAY;} else {target_pre_delay = queue->nodes[i].pre_delay;}/* Check if there is a collision*  Warning: unsigned arithmetic (handle roll-over)*      t_packet_new - pre_delay_packet_new < t_packet_prev + post_delay_packet_prev (OVERLAP on post delay)*      t_packet_new + post_delay_packet_new > t_packet_prev - pre_delay_packet_prev (OVERLAP on pre delay)*/if (jit_collision_test(packet->count_us, packet_pre_delay, packet_post_delay, queue->nodes[i].pkt.count_us, target_pre_delay, queue->nodes[i].post_delay) == true) {switch (queue->nodes[i].pkt_type) {case JIT_PKT_TYPE_DOWNLINK_CLASS_A:case JIT_PKT_TYPE_DOWNLINK_CLASS_B:case JIT_PKT_TYPE_DOWNLINK_CLASS_C:MSG_DEBUG(DEBUG_JIT_ERROR, "ERROR: Packet (type=%d) REJECTED, collision with packet already programmed at %u (%u)\n", pkt_type, queue->nodes[i].pkt.count_us, packet->count_us);err_collision = JIT_ERROR_COLLISION_PACKET;break;case JIT_PKT_TYPE_BEACON:if (pkt_type != JIT_PKT_TYPE_BEACON) {/* do not overload logs for beacon/beacon collision, as it is expected to happen with beacon pre-scheduling algorith used */MSG_DEBUG(DEBUG_JIT_ERROR, "ERROR: Packet (type=%d) REJECTED, collision with beacon already programmed at %u (%u)\n", pkt_type, queue->nodes[i].pkt.count_us, packet->count_us);}err_collision = JIT_ERROR_COLLISION_BEACON;break;default:MSG("ERROR: Unknown packet type, should not occur, BUG?\n");assert(0);break;}pthread_mutex_unlock(&mx_jit_queue);return err_collision;}}/* Finally enqueue it *//* Insert packet at the end of the queue */memcpy(&(queue->nodes[queue->num_pkt].pkt), packet, sizeof(struct lgw_pkt_tx_s));queue->nodes[queue->num_pkt].pre_delay = packet_pre_delay;queue->nodes[queue->num_pkt].post_delay = packet_post_delay;queue->nodes[queue->num_pkt].pkt_type = pkt_type;if (pkt_type == JIT_PKT_TYPE_BEACON) {queue->num_beacon++;}queue->num_pkt++;/* Sort the queue in ascending order of packet timestamp */jit_sort_queue(queue);/* Done */pthread_mutex_unlock(&mx_jit_queue);jit_print_queue(queue, false, DEBUG_JIT);MSG_DEBUG(DEBUG_JIT, "enqueued packet with count_us=%u (size=%u bytes, toa=%u us, type=%u)\n", packet->count_us, packet->size, packet_post_delay, pkt_type);return JIT_ERROR_OK;
}

此函数代码基本功能:
<1>. 根据网络通讯模式CLASS A B C 计算数据传输时间;
<2>. 把数据包插入到发送队列、并排序发送时间的数据包;
数据的发送是在 thread_jit 线程中处理,此处理解为 thread_down 线程负责数据打包、并入队。

总结

此线程主要功能是推拉式、向网络服务器索要数据内容、并根据数据内容动态刷新网关状态参数;
如果本篇文章对您有所启发或帮助、请给笔者点赞助力、鼓励笔者坚持把此系列内容尽快梳理、分享出来。
谢谢。

相关文章:

  • RAGFlow 初步尝试 (01)
  • Leetcode (力扣)做题记录 hot100(34,215,912,121)
  • MongoDB 操作可能抛出哪些异常? 如何优雅的处理?
  • 全球变暖-bfs
  • matlab计算天线的近场和远场
  • MongoDB使用x.509证书认证
  • Matlab基于PSO-MVMD粒子群算法优化多元变分模态分解
  • 逆向破解:x64dbg
  • Python 处理图像并生成 JSONL 元数据文件 - 灵活text版本
  • 机器学习——集成学习基础
  • AI边缘网关_5G/4G边缘计算网关厂家_计讯物联
  • Clion远程开发git触发“No such device or address”的解决方案
  • 数据库笔记(1)
  • Oracle adg环境下调整redo日志组以及standby日志组大小
  • 音视频学习:使用NDK编译FFmpeg动态库
  • Matlab 基于GUI的汽车巡航模糊pid控制
  • 榜单按行显示
  • Baumer工业相机堡盟工业相机的工业视觉是否可以在室外可以做视觉检测项目
  • Fellou智能体调研
  • c# 如何在集合中转换为子类集合
  • 为惩戒“工贼”,美国编剧工会“痛下杀手”
  • 铁路部门:确保沿线群众安全,焦柳铁路6个区段将陆续安装防护栅栏
  • 上报集团社长李芸:发挥媒体优势,让中非民心在数字时代更深层互联互通
  • 三星“七天机”质保期内屏幕漏液被要求自费维修,商家:系人为损坏
  • 北外滩集团21.6亿元摘上海虹口地块,为《酱园弄》取景地
  • 梵蒂冈选出新教皇,外交部:望新教皇推动中梵关系不断改善