int main(int argc, char ** argv)
{
struct sigaction sigact; /* SIGQUIT&SIGINT&SIGTERM signal handling */
int i; /* loop variable and temporary variable for return value */
int x;
int l, m;
/* configuration file related */
const char defaut_conf_fname[] = JSON_CONF_DEFAULT;
const char * conf_fname = defaut_conf_fname; /* pointer to a string we won't touch */
/* threads */
pthread_t thrid_up;
pthread_t thrid_down;
pthread_t thrid_gps;
pthread_t thrid_valid;
pthread_t thrid_jit;
/* 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];
/* variables to get local copies of measurements */
uint32_t cp_nb_rx_rcv;
uint32_t cp_nb_rx_ok;
uint32_t cp_nb_rx_bad;
uint32_t cp_nb_rx_nocrc;
uint32_t cp_up_pkt_fwd;
uint32_t cp_up_network_byte;
uint32_t cp_up_payload_byte;
uint32_t cp_up_dgram_sent;
uint32_t cp_up_ack_rcv;
uint32_t cp_dw_pull_sent;
uint32_t cp_dw_ack_rcv;
uint32_t cp_dw_dgram_rcv;
uint32_t cp_dw_network_byte;
uint32_t cp_dw_payload_byte;
uint32_t cp_nb_tx_ok;
uint32_t cp_nb_tx_fail;
uint32_t cp_nb_tx_requested = 0;
uint32_t cp_nb_tx_rejected_collision_packet = 0;
uint32_t cp_nb_tx_rejected_collision_beacon = 0;
uint32_t cp_nb_tx_rejected_too_late = 0;
uint32_t cp_nb_tx_rejected_too_early = 0;
uint32_t cp_nb_beacon_queued = 0;
uint32_t cp_nb_beacon_sent = 0;
uint32_t cp_nb_beacon_rejected = 0;
/* GPS coordinates variables */
bool coord_ok = false;
struct coord_s cp_gps_coord = {0.0, 0.0, 0};
/* SX1302 data variables */
uint32_t trig_tstamp;
uint32_t inst_tstamp;
uint64_t eui;
float temperature;
/* statistics variable */
time_t t;
char stat_timestamp[24];
float rx_ok_ratio;
float rx_bad_ratio;
float rx_nocrc_ratio;
float up_ack_ratio;
float dw_ack_ratio;
/* Parse command line options
判断命令行选项的:可执行命令lora_pkt_fwd,配置文件global_conf.json
参数:
-c 配置文件名 一般用于配置文件不是默认的global_config.json
-h 打印帮助手册
其他 都会显示帮助手册
不加选项和参数默认寻找配置文件global_config.json
*/
while( (i = getopt( argc, argv, "hc:" )) != -1 )
{
switch( i )
{
case 'h':
usage( );
return EXIT_SUCCESS;
break;
case 'c':
conf_fname = optarg;
break;
default:
printf( "ERROR: argument parsing options, use -h option for help\n" );
usage( );
return EXIT_FAILURE;
}
}
/* display version informations
打印版本信息:VERSION_STRING和lgw_version_info定义在libloragw和packet_forwarder目录下的Makefile
也就是lorawan顶层目录下VERSION文件中的内容
VERSION_STRING:= `cat ../VERSION`
lgw_version_info:= `cat ../VERSION`
*/
MSG("*** Packet Forwarder ***\nVersion: " VERSION_STRING "\n");
MSG("*** SX1302 HAL library version info ***\n%s\n***\n", lgw_version_info());
/* display host endianness
判断主机的架构,和顶层目录下ARCH和CROSS_COMPILE的值有关,我们的arm架构32位小端对齐
*/
#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
MSG("INFO: Little endian host\n");
#elif __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
MSG("INFO: Big endian host\n");
#else
MSG("INFO: Host endianness unknown\n");
#endif
/* load configuration files
加载配置文件,我们的是global_config.json
*/
if (access(conf_fname, R_OK) == 0) { /* if there is a global conf, parse it */
MSG("INFO: found configuration file %s, parsing it\n", conf_fname);
x = parse_SX130x_configuration(conf_fname);
/* 加载sx1302的配置,对应global_config.json中的"SX130x_conf"节点
先判断是否是JSON文件(键值对),再将global_config.json文件中的对应的名称赋值
eg :"spidev_path": "/dev/spidev0.0", 默认读写的spi设备就是0.0
*/
if (x != 0) {
exit(EXIT_FAILURE);
}
/*加载网关的相关配置,对应global_config.json中的"gateway_conf"节点,
x = parse_gateway_configuration(conf_fname);
if (x != 0) {
exit(EXIT_FAILURE);
}
/*加载调试打印信息的配置,对应global_config.json中的"debug_conf"节点
libloragw/library.cfg,键值对的值等于1打印对应的调试信息*/
x = parse_debug_configuration(conf_fname);
if (x != 0) {
MSG("INFO: no debug configuration\n");
}
} else {
MSG("ERROR: [main] failed to find any configuration file named %s\n", conf_fname);
exit(EXIT_FAILURE);
}
/* Start GPS a.s.a.p., to allow it to lock
使能GPS模块,当GPS没有没有在global_config.json中定义时就不会打开gps模块的设备了
*/
if (gps_tty_path[0] != '\0') { /* do not try to open GPS device if no path set */
i = lgw_gps_enable(gps_tty_path, "ubx7", 0, &gps_tty_fd); /* HAL only supports u-blox 7 for now */
if (i != LGW_GPS_SUCCESS) {
printf("WARNING: [main] impossible to open %s for GPS sync (check permissions)\n", gps_tty_path);
gps_enabled = false;
gps_ref_valid = false;
} else {
printf("INFO: [main] TTY port %s open for GPS synchronization\n", gps_tty_path);
gps_enabled = true;
gps_ref_valid = false;
}
}
/* get timezone info */
tzset();
/* sanity check on configuration variables */
// TODO
/* process some of the configuration variables */
net_mac_h = htonl((uint32_t)(0xFFFFFFFF & (lgwm>>32)));
net_mac_l = htonl((uint32_t)(0xFFFFFFFF & lgwm ));
/* prepare hints to open network sockets
创建套接字与NS能够上传/下发数据报,通过UDP的方式
*/
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;
/* look for server address w/ upstream port */
i = getaddrinfo(serv_addr, serv_port_up, &hints, &result);
if (i != 0) {
MSG("ERROR: [up] getaddrinfo on address %s (PORT %s) returned %s\n", serv_addr, serv_port_up, gai_strerror(i));
exit(EXIT_FAILURE);
}
/* try to open socket for upstream traffic */
for (q=result; q!=NULL; q=q->ai_next) {
sock_up = socket(q->ai_family, q->ai_socktype,q->ai_protocol);
if (sock_up == -1) continue; /* try next field */
else break; /* success, get out of loop */
}
if (q == NULL) {
MSG("ERROR: [up] failed to open socket to any of server %s addresses (port %s)\n", serv_addr, serv_port_up);
i = 1;
for (q=result; q!=NULL; q=q->ai_next) {
getnameinfo(q->ai_addr, q->ai_addrlen, host_name, sizeof host_name, port_name, sizeof port_name, NI_NUMERICHOST);
MSG("INFO: [up] result %i host:%s service:%s\n", i, host_name, port_name);
++i;
}
exit(EXIT_FAILURE);
}
/* connect so we can send/receive packet with the server only */
i = connect(sock_up, q->ai_addr, q->ai_addrlen);
if (i != 0) {
MSG("ERROR: [up] connect returned %s\n", strerror(errno));
exit(EXIT_FAILURE);
}
freeaddrinfo(result);
/* 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_up, 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 */
}
if (q == NULL) {
MSG("ERROR: [up] failed to open socket to any of server %s addresses (port %s)\n", serv_addr, serv_port_up);
i = 1;
for (q=result; q!=NULL; q=q->ai_next) {
getnameinfo(q->ai_addr, q->ai_addrlen, host_name, sizeof host_name, port_name, sizeof port_name, NI_NUMERICHOST);
MSG("INFO: [up] result %i host:%s service:%s\n", i, host_name, port_name);
++i;
}
exit(EXIT_FAILURE);
}
/* connect so we can send/receive packet with the server only */
i = connect(sock_up, q->ai_addr, q->ai_addrlen);
if (i != 0) {
MSG("ERROR: [up] connect returned %s\n", strerror(errno));
exit(EXIT_FAILURE);
}
freeaddrinfo(result);
/* 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_up, 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 */
}
if (q == NULL) {
MSG("ERROR: [down] failed to open socket to any of server %s addresses (port %s)\n", serv_addr, serv_port_up);
i = 1;
for (q=result; q!=NULL; q=q->ai_next) {
getnameinfo(q->ai_addr, q->ai_addrlen, host_name, sizeof host_name, port_name, sizeof port_name, NI_NUMERICHOST);
MSG("INFO: [down] result %i host:%s service:%s\n", i, host_name, port_name);
++i;
}
exit(EXIT_FAILURE);
}
/* 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);
}
freeaddrinfo(result);
/* Board reset */
if (system("./reset_lgw.sh start") != 0) {
printf("ERROR: failed to reset SX1302, check your reset_lgw.sh script\n");
exit(EXIT_FAILURE);
}
for (l = 0; l < LGW_IF_CHAIN_NB; l++) {
for (m = 0; m < 8; m++) {
nb_pkt_log[l][m] = 0;
}
}
/* starting the concentrator
* 执行启动脚本,通过GPIO复位集中器
*/
i = lgw_start();
if (i == LGW_HAL_SUCCESS) {
MSG("INFO: [main] concentrator started, packet can now be received\n");
} else {
MSG("ERROR: [main] failed to start the concentrator\n");
exit(EXIT_FAILURE);
}
/* get the concentrator EUI
*得到集中器的EUI,EUI是区分设备的标识符
*/
i = lgw_get_eui(&eui);
if (i != LGW_HAL_SUCCESS) {
printf("ERROR: failed to get concentrator EUI\n");
} else {
printf("INFO: concentrator EUI: 0x%016" PRIx64 "\n", eui);
}
/* spawn threads to manage upstream and downstream
*创建多线程:分别创建:
*处理上行数据的线程(thread_up):负责将lora终端数据通过lorawan上传到NS
*处理下行数据的线程(thread_down):将NS的数据包通过lorawan下发给lora终端
*/
i = pthread_create( &thrid_up, NULL, (void * (*)(void *))thread_up, NULL);
if (i != 0) {
MSG("ERROR: [main] impossible to create upstream thread\n");
exit(EXIT_FAILURE);
}
i = pthread_create( &thrid_down, NULL, (void * (*)(void *))thread_down, NULL);
if (i != 0) {
MSG("ERROR: [main] impossible to create downstream thread\n");
exit(EXIT_FAILURE);
}
i = pthread_create( &thrid_jit, NULL, (void * (*)(void *))thread_jit, NULL);
if (i != 0) {
MSG("ERROR: [main] impossible to create JIT thread\n");
exit(EXIT_FAILURE);
}
/* spawn thread to manage GPS
*如果GPS使能,创建处理gps数据的线程
*/
if (gps_enabled == true) {
i = pthread_create( &thrid_gps, NULL, (void * (*)(void *))thread_gps, NULL);
if (i != 0) {
MSG("ERROR: [main] impossible to create GPS thread\n");
exit(EXIT_FAILURE);
}
i = pthread_create( &thrid_valid, NULL, (void * (*)(void *))thread_valid, NULL);
if (i != 0) {
MSG("ERROR: [main] impossible to create validation thread\n");
exit(EXIT_FAILURE);
}
}
/* configure signal handling
*配置处理信号:比如按ctrl +c 时候退出当前进程
*/
sigemptyset(&sigact.sa_mask);
sigact.sa_flags = 0;
sigact.sa_handler = sig_handler;
sigaction(SIGQUIT, &sigact, NULL); /* Ctrl-\ */
sigaction(SIGINT, &sigact, NULL); /* Ctrl-C */
sigaction(SIGTERM, &sigact, NULL); /* default "kill" command */
/* main loop task : statistics collection */
while (!exit_sig && !quit_sig) {
/* wait for next reporting interval */
wait_ms(1000 * stat_interval);
/* get timestamp for statistics */
t = time(NULL);
strftime(stat_timestamp, sizeof stat_timestamp, "%F %T %Z", gmtime(&t));
/* access upstream statistics, copy and reset them
*先获取上行数据,然后将获得的数据复制以后在清0方便下次获取正确的数据
*同时加锁保护这些数据
*/
pthread_mutex_lock(&mx_meas_up);
cp_nb_rx_rcv = meas_nb_rx_rcv;
cp_nb_rx_ok = meas_nb_rx_ok;
cp_nb_rx_bad = meas_nb_rx_bad;
cp_nb_rx_nocrc = meas_nb_rx_nocrc;
cp_up_pkt_fwd = meas_up_pkt_fwd;
cp_up_network_byte = meas_up_network_byte;
cp_up_payload_byte = meas_up_payload_byte;
cp_up_dgram_sent = meas_up_dgram_sent;
cp_up_ack_rcv = meas_up_ack_rcv;
meas_nb_rx_rcv = 0;
meas_nb_rx_ok = 0;
meas_nb_rx_bad = 0;
meas_nb_rx_nocrc = 0;
meas_up_pkt_fwd = 0;
meas_up_network_byte = 0;
meas_up_payload_byte = 0;
meas_up_dgram_sent = 0;
meas_up_ack_rcv = 0;
pthread_mutex_unlock(&mx_meas_up);
/*如果集中器接收到数据包,就会将CRC校验的结果赋值给ok、bad、nocrc
* 最后会在报告中按三种的情况的百分比打印
*/
if (cp_nb_rx_rcv > 0) {
rx_ok_ratio = (float)cp_nb_rx_ok / (float)cp_nb_rx_rcv;
rx_bad_ratio = (float)cp_nb_rx_bad / (float)cp_nb_rx_rcv;
rx_nocrc_ratio = (float)cp_nb_rx_nocrc / (float)cp_nb_rx_rcv;
} else {
rx_ok_ratio = 0.0;
rx_bad_ratio = 0.0;
rx_nocrc_ratio = 0.0;
}
if (cp_up_dgram_sent > 0) {
up_ack_ratio = (float)cp_up_ack_rcv / (float)cp_up_dgram_sent;
} else {
up_ack_ratio = 0.0;
}
/* access GPS statistics, copy them
*如果GPS使能,获取GPS的经纬度
*/
if (gps_enabled == true) {
pthread_mutex_lock(&mx_meas_gps);
coord_ok = gps_coord_valid;
cp_gps_coord = meas_gps_coord;
pthread_mutex_unlock(&mx_meas_gps);
}
/* overwrite with reference coordinates if function is enabled */
if (gps_fake_enable == true) {
cp_gps_coord = reference_coord;
}
/* display a report
*对接收的数据包相关信息进行打印,帮助调试,主要信息有:上行数据、下行数据、SX1302状态、jit、GPS
*/
printf("\n##### %s #####\n", stat_timestamp);
printf("### [UPSTREAM] ###\n");
printf("# RF packets received by concentrator: %u\n", cp_nb_rx_rcv);
printf("# CRC_OK: %.2f%%, CRC_FAIL: %.2f%%, NO_CRC: %.2f%%\n", 100.0 * rx_ok_ratio, 100.0 * rx_bad_ratio, 100.0 * rx_nocrc_ratio);
printf("# RF packets forwarded: %u (%u bytes)\n", cp_up_pkt_fwd, cp_up_payload_byte);
printf("# PUSH_DATA datagrams sent: %u (%u bytes)\n", cp_up_dgram_sent, cp_up_network_byte);
printf("# PUSH_DATA acknowledged: %.2f%%\n", 100.0 * up_ack_ratio);
printf("### [DOWNSTREAM] ###\n");
printf("# PULL_DATA sent: %u (%.2f%% acknowledged)\n", cp_dw_pull_sent, 100.0 * dw_ack_ratio);
printf("# PULL_RESP(onse) datagrams received: %u (%u bytes)\n", cp_dw_dgram_rcv, cp_dw_network_byte);
printf("# RF packets sent to concentrator: %u (%u bytes)\n", (cp_nb_tx_ok+cp_nb_tx_fail), cp_dw_payload_byte);
printf("# TX errors: %u\n", cp_nb_tx_fail);
if (cp_nb_tx_requested != 0 ) {
printf("# TX rejected (collision packet): %.2f%% (req:%u, rej:%u)\n", 100.0 * cp_nb_tx_rejected_collision_packet / cp_nb_tx_requested, cp_nb_tx_requested, cp_nb_tx_rejected_collision_packet);
printf("# TX rejected (collision beacon): %.2f%% (req:%u, rej:%u)\n", 100.0 * cp_nb_tx_rejected_collision_beacon / cp_nb_tx_requested, cp_nb_tx_requested, cp_nb_tx_rejected_collision_beacon);
printf("# TX rejected (too late): %.2f%% (req:%u, rej:%u)\n", 100.0 * cp_nb_tx_rejected_too_late / cp_nb_tx_requested, cp_nb_tx_requested, cp_nb_tx_rejected_too_late);
printf("# TX rejected (too early): %.2f%% (req:%u, rej:%u)\n", 100.0 * cp_nb_tx_rejected_too_early / cp_nb_tx_requested, cp_nb_tx_requested, cp_nb_tx_rejected_too_early);
}
printf("### SX1302 Status ###\n");
pthread_mutex_lock(&mx_concent);
i = lgw_get_instcnt(&inst_tstamp);
i |= lgw_get_trigcnt(&trig_tstamp);
pthread_mutex_unlock(&mx_concent);
if (i != LGW_HAL_SUCCESS) {
printf("# SX1302 counter unknown\n");
} else {
printf("# SX1302 counter (INST): %u\n", inst_tstamp);
printf("# SX1302 counter (PPS): %u\n", trig_tstamp);
}
printf("# BEACON queued: %u\n", cp_nb_beacon_queued);
printf("# BEACON sent so far: %u\n", cp_nb_beacon_sent);
printf("# BEACON rejected: %u\n", cp_nb_beacon_rejected);
printf("### [JIT] ###\n");
/* get timestamp captured on PPM pulse */
jit_print_queue (&jit_queue[0], false, DEBUG_LOG);
printf("#--------\n");
jit_print_queue (&jit_queue[1], false, DEBUG_LOG);
printf("### [GPS] ###\n");
if (gps_enabled == true) {
/* no need for mutex, display is not critical */
if (gps_ref_valid == true) {
printf("# Valid time reference (age: %li sec)\n", (long)difftime(time(NULL), time_reference_gps.systime));
} else {
printf("# Invalid time reference (age: %li sec)\n", (long)difftime(time(NULL), time_reference_gps.systime));
}
if (coord_ok == true) {
printf("# GPS coordinates: latitude %.5f, longitude %.5f, altitude %i m\n", cp_gps_coord.lat, cp_gps_coord.lon, cp_gps_coord.alt);
} else {
printf("# no valid GPS coordinates available yet\n");
}
} else if (gps_fake_enable == true) {
printf("# GPS *FAKE* coordinates: latitude %.5f, longitude %.5f, altitude %i m\n", cp_gps_coord.lat, cp_gps_coord.lon, cp_gps_coord.alt);
} else {
printf("# GPS sync is disabled\n");
}
i = lgw_get_temperature(&temperature);
if (i != LGW_HAL_SUCCESS) {
printf("### Concentrator temperature unknown ###\n");
} else {
printf("### Concentrator temperature: %.0f C ###\n", temperature);
}
printf("##### END #####\n");
/* generate a JSON report (will be sent to server by upstream thread) */
pthread_mutex_lock(&mx_stat_rep);
if (((gps_enabled == true) && (coord_ok == true)) || (gps_fake_enable == true)) {
snprintf(status_report, STATUS_SIZE, "\"stat\":{\"time\":\"%s\",\"lati\":%.5f,\"long\":%.5f,\"alti\":%i,\"rxnb\":%u,\"rxok\":%u,\"rxfw\":%u,\"ackr\":%.1f,\"dwnb\":%u,\"txnb\":%u,\"temp\":%.1f}", stat_timestamp, cp_gps_coord.lat, cp_gps_coord.lon, cp_gps_coord.alt, cp_nb_rx_rcv, cp_nb_rx_ok, cp_up_pkt_fwd, 100.0 * up_ack_ratio, cp_dw_dgram_rcv, cp_nb_tx_ok, temperature);
} else {
snprintf(status_report, STATUS_SIZE, "\"stat\":{\"time\":\"%s\",\"rxnb\":%u,\"rxok\":%u,\"rxfw\":%u,\"ackr\":%.1f,\"dwnb\":%u,\"txnb\":%u,\"temp\":%.1f}", stat_timestamp, cp_nb_rx_rcv, cp_nb_rx_ok, cp_up_pkt_fwd, 100.0 * up_ack_ratio, cp_dw_dgram_rcv, cp_nb_tx_ok, temperature);
}
report_ready = true;
pthread_mutex_unlock(&mx_stat_rep);
}
/* wait for upstream thread to finish (1 fetch cycle max) */
/*上行数据结束线程pthread_join,jit线程、gps线程请求pthread_cancel取消这些线程,
*也就是说数据下行线程、jit线程、GPS线程只要数据传输完毕就请求退出,因为从NS下发数据的
*时间不固定,上行数据固定时间,隔一段时间发一次
*/
pthread_join(thrid_up, NULL);
pthread_cancel(thrid_down); /* don't wait for downstream thread */
pthread_cancel(thrid_jit); /* don't wait for jit thread */
if (gps_enabled == true) {
pthread_cancel(thrid_gps); /* don't wait for GPS thread */
pthread_cancel(thrid_valid); /* don't wait for validation thread */
i = lgw_gps_disable(gps_tty_fd);
if (i == LGW_HAL_SUCCESS) {
MSG("INFO: GPS closed successfully\n");
} else {
MSG("WARNING: failed to close GPS successfully\n");
}
}
/* if an exit signal was received, try to quit properly
*如果信号结束进程,安全退出流程:关闭上行下行网络套接字、脚本执行stop关闭集中器
*/
if (exit_sig) {
/* shut down network sockets */
shutdown(sock_up, SHUT_RDWR);
shutdown(sock_down, SHUT_RDWR);
/* stop the hardware */
i = lgw_stop();
if (i == LGW_HAL_SUCCESS) {
MSG("INFO: concentrator stopped successfully\n");
} else {
MSG("WARNING: failed to stop concentrator successfully\n");
}
}
/* Board reset */
if (system("./reset_lgw.sh stop") != 0) {
printf("ERROR: failed to reset SX1302, check your reset_lgw.sh script\n");
exit(EXIT_FAILURE);
}
MSG("INFO: Exiting packet forwarder program\n");
exit(EXIT_SUCCESS);
}
/* -------------------------------------------------------------------------- */
/* --- THREAD 1: RECEIVING PACKETS AND FORWARDING THEM ---------------------- */
/*上行数据线程*/
void thread_up(void) {
int i, j, k; /* loop variables */
unsigned pkt_in_dgram; /* nb on Lora packet in the current datagram */
char stat_timestamp[24];
time_t t;
/* allocate memory for packet fetching and processing */
struct lgw_pkt_rx_s rxpkt[NB_PKT_MAX]; /* array containing inbound packets + metadata */
struct lgw_pkt_rx_s *p; /* pointer on a RX packet */
int nb_pkt;
/* local copy of GPS time reference */
bool ref_ok = false; /* determine if GPS time reference must be used or not */
struct tref local_ref; /* time reference used for UTC <-> timestamp conversion */
/* data buffers */
uint8_t buff_up[TX_BUFF_SIZE]; /* buffer to compose the upstream packet */
int buff_index;
uint8_t buff_ack[32]; /* buffer to receive acknowledges */
/* protocol variables */
uint8_t token_h; /* random token for acknowledgement matching */
uint8_t token_l; /* random token for acknowledgement matching */
/* ping measurement variables */
struct timespec send_time;
struct timespec recv_time;
/* GPS synchronization variables */
struct timespec pkt_utc_time;
struct tm * x; /* broken-up UTC time */
struct timespec pkt_gps_time;
uint64_t pkt_gps_time_ms;
/* report management variable */
bool send_report = false;
/* mote info variables */
uint32_t mote_addr = 0;
uint16_t mote_fcnt = 0;
/* set upstream socket RX timeout */
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);
}
/* pre-fill the data buffer with fixed fields */
buff_up[0] = PROTOCOL_VERSION;
buff_up[3] = PKT_PUSH_DATA;
*(uint32_t *)(buff_up + 4) = net_mac_h;
*(uint32_t *)(buff_up + 8) = net_mac_l;
while (!exit_sig && !quit_sig) {
/* fetch packets */
pthread_mutex_lock(&mx_concent);
nb_pkt = lgw_receive(NB_PKT_MAX, rxpkt);
pthread_mutex_unlock(&mx_concent);
if (nb_pkt == LGW_HAL_ERROR) {
MSG("ERROR: [up] failed packet fetch, exiting\n");
exit(EXIT_FAILURE);
}
/* check if there are status report to send */
send_report = report_ready; /* copy the variable so it doesn't change mid-function */
/* no mutex, we're only reading */
/* wait a short time if no packets, nor status report */
if ((nb_pkt == 0) && (send_report == false)) {
wait_ms(FETCH_SLEEP_MS);
continue;
}
/* get a copy of GPS time reference (avoid 1 mutex per packet) */
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;
}
/* get timestamp for statistics */
t = time(NULL);
strftime(stat_timestamp, sizeof stat_timestamp, "%F %T %Z", gmtime(&t));
MSG_DEBUG(DEBUG_PKT_FWD, "\nCurrent time: %s \n", stat_timestamp);
/* start composing datagram with the header */
token_h = (uint8_t)rand(); /* random token */
token_l = (uint8_t)rand(); /* random token */
buff_up[1] = token_h;
buff_up[2] = token_l;
buff_index = 12; /* 12-byte header */
/* start of JSON structure */
memcpy((void *)(buff_up + buff_index), (void *)"{\"rxpk\":[", 9);
buff_index += 9;
/* serialize Lora packets metadata and payload */
pkt_in_dgram = 0;
for (i = 0; i < nb_pkt; ++i) {
p = &rxpkt[i];
/* Get mote information from current packet (addr, fcnt) */
/* FHDR - DevAddr */
if (p->size >= 8) {
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;
} else {
mote_addr = 0;
mote_fcnt = 0;
}
/* basic packet filtering */
pthread_mutex_lock(&mx_meas_up);
meas_nb_rx_rcv += 1;
switch(p->status) {
case STAT_CRC_OK:
meas_nb_rx_ok += 1;
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->rssic);
pthread_mutex_unlock(&mx_meas_up);
continue; /* skip that packet */
// exit(EXIT_FAILURE);
}
meas_up_pkt_fwd += 1;
meas_up_payload_byte += p->size;
pthread_mutex_unlock(&mx_meas_up);
printf( "\nINFO: Received pkt from mote: %08X (fcnt=%u)\n", mote_addr, mote_fcnt );
/* Start of packet, add inter-packet separator if necessary */
if (pkt_in_dgram == 0) {
buff_up[buff_index] = '{';
++buff_index;
} else {
buff_up[buff_index] = ',';
buff_up[buff_index+1] = '{';
buff_index += 2;
}
/* JSON rxpk frame format version, 8 useful chars */
j = snprintf((char *)(buff_up + buff_index), TX_BUFF_SIZE-buff_index, "\"jver\":%d", PROTOCOL_JSON_RXPK_FRAME_FORMAT );
if (j > 0) {
buff_index += j;
} else {
MSG("ERROR: [up] snprintf failed line %u\n", (__LINE__ - 4));
exit(EXIT_FAILURE);
}
/* RAW timestamp, 8-17 useful chars */
j = snprintf((char *)(buff_up + 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);
}
/* Packet RX time (GPS based), 37 useful chars */
if (ref_ok == true) {
/* convert packet timestamp to UTC absolute time */
j = lgw_cnt2utc(local_ref, p->count_us, &pkt_utc_time);
if (j == LGW_GPS_SUCCESS) {
/* split the UNIX timestamp to its calendar components */
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);
}
}
/* convert packet timestamp to GPS absolute time */
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\":%" PRIu64 "", pkt_gps_time_ms); /* GPS time in milliseconds since 06.Jan.1980 */
if (j > 0) {
buff_index += j;
} else {
MSG("ERROR: [up] snprintf failed line %u\n", (__LINE__ - 4));
exit(EXIT_FAILURE);
}
}
}
/* Packet concentrator channel, RF chain & RX frequency, 34-36 useful chars */
j = snprintf((char *)(buff_up + buff_index), TX_BUFF_SIZE-buff_index, ",\"chan\":%1u,\"rfch\":%1u,\"freq\":%.6lf,\"mid\":%2u", p->if_chain, p->rf_chain, ((double)p->freq_hz / 1e6), p->modem_id);
if (j > 0) {
buff_index += j;
} else {
MSG("ERROR: [up] snprintf failed line %u\n", (__LINE__ - 4));
exit(EXIT_FAILURE);
}
/* Packet status, 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 0x%02X\n", p->status);
memcpy((void *)(buff_up + buff_index), (void *)",\"stat\":?", 9);
buff_index += 9;
exit(EXIT_FAILURE);
}
/* Packet modulation, 13-14 useful chars */
if (p->modulation == MOD_LORA) {
memcpy((void *)(buff_up + buff_index), (void *)",\"modu\":\"LORA\"", 14);
buff_index += 14;
/* Lora datarate & bandwidth, 16-19 useful chars */
switch (p->datarate) {
case DR_LORA_SF5:
memcpy((void *)(buff_up + buff_index), (void *)",\"datr\":\"SF5", 12);
buff_index += 12;
break;
case DR_LORA_SF6:
memcpy((void *)(buff_up + buff_index), (void *)",\"datr\":\"SF6", 12);
buff_index += 12;
break;
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 0x%02X\n", p->datarate);
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 0x%02X\n", p->bandwidth);
memcpy((void *)(buff_up + buff_index), (void *)"BW?\"", 4);
buff_index += 4;
exit(EXIT_FAILURE);
}
/* Packet ECC coding rate, 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 0x%02X\n", p->coderate);
memcpy((void *)(buff_up + buff_index), (void *)",\"codr\":\"?\"", 11);
buff_index += 11;
exit(EXIT_FAILURE);
}
/* Signal RSSI, payload size */
j = snprintf((char *)(buff_up + buff_index), TX_BUFF_SIZE-buff_index, ",\"rssis\":%.0f", roundf(p->rssis));
if (j > 0) {
buff_index += j;
} else {
MSG("ERROR: [up] snprintf failed line %u\n", (__LINE__ - 4));
exit(EXIT_FAILURE);
}
/* Lora SNR */
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);
}
/* Lora frequency offset */
j = snprintf((char *)(buff_up + buff_index), TX_BUFF_SIZE-buff_index, ",\"foff\":%d", p->freq_offset);
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 0x%02X\n", p->modulation);
exit(EXIT_FAILURE);
}
/* Channel RSSI, payload size, 18-23 useful chars */
j = snprintf((char *)(buff_up + buff_index), TX_BUFF_SIZE-buff_index, ",\"rssi\":%.0f,\"size\":%u", roundf(p->rssic), p->size);
if (j > 0) {
buff_index += j;
} else {
MSG("ERROR: [up] snprintf failed line %u\n", (__LINE__ - 4));
exit(EXIT_FAILURE);
}
/* Packet base64-encoded payload, 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;
/* End of packet serialization */
buff_up[buff_index] = '}';
++buff_index;
++pkt_in_dgram;
if (p->modulation == MOD_LORA) {
/* Log nb of packets per channel, per SF */
nb_pkt_log[p->if_chain][p->datarate - 5] += 1;
nb_pkt_received_lora += 1;
/* Log nb of packets for ref_payload (DEBUG) */
for (k = 0; k < debugconf.nb_ref_payload; k++) {
if ((p->payload[0] == (uint8_t)(debugconf.ref_payload[k].id >> 24)) &&
(p->payload[1] == (uint8_t)(debugconf.ref_payload[k].id >> 16)) &&
(p->payload[2] == (uint8_t)(debugconf.ref_payload[k].id >> 8)) &&
(p->payload[3] == (uint8_t)(debugconf.ref_payload[k].id >> 0))) {
nb_pkt_received_ref[k] += 1;
}
}
} else if (p->modulation == MOD_FSK) {
nb_pkt_log[p->if_chain][0] += 1;
nb_pkt_received_fsk += 1;
}
}
/* DEBUG: print the number of packets received per channel and per SF */
{
int l, m;
MSG_PRINTF(DEBUG_PKT_FWD, "\n");
for (l = 0; l < (LGW_IF_CHAIN_NB - 1); l++) {
MSG_PRINTF(DEBUG_PKT_FWD, "CH%d: ", l);
for (m = 0; m < 8; m++) {
MSG_PRINTF(DEBUG_PKT_FWD, "\t%d", nb_pkt_log[l][m]);
}
MSG_PRINTF(DEBUG_PKT_FWD, "\n");
}
MSG_PRINTF(DEBUG_PKT_FWD, "FSK: \t%d", nb_pkt_log[9][0]);
MSG_PRINTF(DEBUG_PKT_FWD, "\n");
MSG_PRINTF(DEBUG_PKT_FWD, "Total number of LoRa packet received: %u\n", nb_pkt_received_lora);
MSG_PRINTF(DEBUG_PKT_FWD, "Total number of FSK packet received: %u\n", nb_pkt_received_fsk);
for (l = 0; l < debugconf.nb_ref_payload; l++) {
MSG_PRINTF(DEBUG_PKT_FWD, "Total number of LoRa packet received from 0x%08X: %u\n", debugconf.ref_payload[l].id, nb_pkt_received_ref[l]);
}
}
/* restart fetch sequence without sending empty JSON if all packets have been filtered out */
if (pkt_in_dgram == 0) {
if (send_report == true) {
/* need to clean up the beginning of the payload */
buff_index -= 8; /* removes "rxpk":[ */
} else {
/* all packet have been filtered out and no report, restart loop */
continue;
}
} else {
/* end of packet array */
buff_up[buff_index] = ']';
++buff_index;
/* add separator if needed */
if (send_report == true) {
buff_up[buff_index] = ',';
++buff_index;
}
}
/* add status report if a new one is available */
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);
}
}
/* end of JSON datagram payload */
buff_up[buff_index] = '}';
++buff_index;
buff_up[buff_index] = 0; /* add string terminator, for safety */
printf("\nJSON up: %s\n", (char *)(buff_up + 12)); /* DEBUG: display JSON payload */
/* send datagram to server */
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;
/* wait for acknowledge (in 2 times, to catch extra packets) */
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);
}
MSG("\nINFO: End of upstream thread\n");
}
/* -------------------------------------------------------------------------- */
/* --- THREAD 2: POLLING SERVER AND ENQUEUING PACKETS IN JIT QUEUE ---------- */
static int get_tx_gain_lut_index(uint8_t rf_chain, int8_t rf_power, uint8_t * lut_index) {
uint8_t pow_index;
int current_best_index = -1;
uint8_t current_best_match = 0xFF;
int diff;
/* Check input parameters */
if (lut_index == NULL) {
MSG("ERROR: %s - wrong parameter\n", __FUNCTION__);
return -1;
}
/* Search requested power in TX gain LUT */
for (pow_index = 0; pow_index < txlut[rf_chain].size; pow_index++) {
diff = rf_power - txlut[rf_chain].lut[pow_index].rf_power;
if (diff < 0) {
/* The selected power must be lower or equal to requested one */
continue;
} else {
/* Record the index corresponding to the closest rf_power available in LUT */
if ((current_best_index == -1) || (diff < current_best_match)) {
current_best_match = diff;
current_best_index = pow_index;
}
}
}
/* Return corresponding index */
if (current_best_index > -1) {
*lut_index = (uint8_t)current_best_index;
} else {
*lut_index = 0;
MSG("ERROR: %s - failed to find tx gain lut index\n", __FUNCTION__);
return -1;
}
return 0;
}
/*** 下行数据线程 ******* /
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;
/* protocol variables */
uint8_t token_h; /* random token for acknowledgement matching */
uint8_t token_l; /* random token for acknowledgement matching */
bool req_ack = false; /* keep track of whether PULL_DATA was acknowledged or not */
/* JSON parsing variables */
JSON_Value *root_val = NULL;
JSON_Object *txpk_obj = NULL;
JSON_Value *val = NULL; /* needed to detect the absence of some fields */
const char *str; /* pointer to sub-strings in the JSON data */
short x0, x1;
uint64_t x2;
double x3, x4;
/* 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 */
int retry;
/* beacon data fields, byte 0 is Least Significant Byte */
int32_t field_latitude; /* 3 bytes, derived from reference latitude */
int32_t field_longitude; /* 3 bytes, derived from reference longitude */
uint16_t field_crc1, field_crc2;
/* auto-quit variable */
uint32_t autoquit_cnt = 0; /* count the number of PULL_DATA sent since the latest PULL_ACK */
/* Just In Time downlink */
uint32_t current_concentrator_time;
enum jit_error_e jit_result = JIT_ERROR_OK;
enum jit_pkt_type_e downlink_type;
enum jit_error_e warning_result = JIT_ERROR_OK;
int32_t warning_value = 0;
uint8_t tx_lut_idx = 0;
/* 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);
}
/* pre-fill the pull request buffer with fixed fields */
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;
/* 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;
switch (beacon_bw_hz) {
case 125000:
beacon_pkt.bandwidth = BW_125KHZ;
break;
case 500000:
beacon_pkt.bandwidth = BW_500KHZ;
break;
default:
/* should not happen */
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;
/* network common part beacon fields (little endian) */
for (i = 0; i < (int)beacon_RFU1_size; i++) {
beacon_pkt.payload[beacon_pyld_idx++] = 0x0;
}
/* network common part beacon fields (little endian) */
beacon_pyld_idx += 4; /* time (variable), filled later */
beacon_pyld_idx += 2; /* crc1 (variable), filled later */
/* calculate the latitude and longitude that must be publicly reported */
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;
}
/* 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);
Loarwan源码(lora_pkt_fwd.c)阅读分析
最新推荐文章于 2022-07-29 11:23:27 发布