当前位置:   article > 正文

学习笔记(1)lora_pkt_fwd.c (LoRaWAN Gateway核心代码)

lora_pkt_fwd

目录

1、前言

2、网关(转载)

3、基础知识

3.1 pthread线程库介绍(转载)

3.2 socket 简介

3.2.1 什么是网络、计算机网络的构成是什么?

3.2.2 什么是网络编程?

3.2.3 7层网络模型--OSI

3.2.4 网络模型---对应关系

​3.2.5 Socket与TCP、UDP 

3.2.6 Socket的传输原理

3.3 socket编程(转载)

3.3.1 结构体

3.3.2 socket() 函数

3.4 互斥锁

3.5 信号量

4、线程

3.1 main(主线程)

3.2 thread_up(上行线程)

3.3 thread_down(下行线程)

3.4 thread_jit 

3.5 thread_gps

3.6 thread_valid

3.7 thread_timersync


1、前言

如下图所示,lora_pkt_fwd.c是一个运行在 Lora Gateway Host上的程序,将Concentrator(集中器)接收到的无线数据包通过 IP/UDP 转发到服务器链接,并发射由服务器发送的无线数据包。 它还可以发出用于协调所有节点的全网 GPS 同步信标信号网络。

  1. ((( Y )))
  2. |
  3. |
  4. +- -|- - - - - - - - - - - - -+ xxxxxxxxxxxx +--------+
  5. |+--+-----------+ +------+| xx x x xxx | |
  6. || | | || xx Internet xx | |
  7. || Concentrator |<----+ Host |<------xx or xx-------->| |
  8. || | SPI | || xx Intranet xx | Server |
  9. |+--------------+ +------+| xxxx x xxxx | |
  10. | ^ ^ | xxxxxxxx | |
  11. | | PPS +-----+ NMEA | | | |
  12. | +------| GPS |-------+ | +--------+
  13. | +-----+ |
  14. | |
  15. | Gateway |
  16. +- - - - - - - - - - - - - - -+

Concentrator:radio RX/TX board, based on Semtech multichannel modems 
(SX130x), transceivers (SX135x) and/or low-power stand-alone modems (SX127x). 

(无线电 RX/TX 板,基于 Semtech 多通道调制解调器(SX130x)、收发器 (SX135x) 和/或低功耗独立调制解调器 (SX127x))

Host:embedded computer on which the packet forwarder is run. Drives the 
concentrator through a SPI link.

(运行数据包转发器的嵌入式计算机。 驱动集中器通过 SPI 链接)

GPS: GNSS (GPS, Galileo, GLONASS, etc) receiver with a "1 Pulse Per Second"
output and a serial link to the host to send NMEA frames containing time and
geographical coordinates data. Optional.

(具有“每秒 1 个脉冲”的 GNSS(GPS、Galileo GLONASS 等)接收器输出和到主机的串行链路以发送包含时间和地理坐标数据。 可选的)

Gateway: a device composed of at least one radio concentrator, a host, some 
network connection to the internet or a private network (Ethernet, 3G, Wifi, 
microwave link), and optionally a GPS receiver for synchronization. 

(由至少一个无线电集中器、一个主机、一些到互联网或专用网络的网络连接(以太网、3G、Wifi、微波链接),以及可选的 GPS 接收器用于同步。)

Server: an abstract computer that will process the RF packets received and 
forwarded by the gateway, and issue RF packets in response that the gateway 
will have to emit.

(一个抽象的计算机,它将处理接收到的 RF 数据包和由网关转发,并发出 RF 数据包以响应网关
将不得不发射。)


2、网关(转载)

网关的学习_ycxzz的博客-CSDN博客一 网关的定义       网关(Gateway)又称网间连接器、协议转换器。网关在网络层以上实现网络互连,是最复杂的网络互连设备,仅用于两个高层协议不同的网络互连。网关既可以用于广域网互连,也可以用于局域网互连。 网关是一种充当转换重任的计算机系统或设备。使用在不同的通信协议、数据格式或语言,甚至体系结构完全不同的两种系统之间,网关是一个翻译器。与网桥只是简单地传达信息不同,网关对收到的https://blog.csdn.net/ycxzz/article/details/79191827?spm=1001.2101.3001.6650.13&utm_medium=distribute.pc_relevant.none-task-blog-2~default~CTRLIST~default-13.no_search_link&depth_1-utm_source=distribute.pc_relevant.none-task-blog-2~default~CTRLIST~default-13.no_search_link


3、基础知识

3.1 pthread线程库介绍(转载)

linux多线程pthread系列函数详解 - 开心happy - 博客园linux多线程pthread系列函数详解 (一)为什么要引入线程 线程技术早在60年代就被提出,但是在80年代才真正使用到操作系统中。传统UNIX也支持多线程概念,但在一个进程中只允许有一个线程,这https://www.cnblogs.com/minihaohao/p/5188906.html

3.2 socket 简介


以下来自CSDN博主「折腾java」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:什么是网络编程(一)_折腾java的博客-CSDN博客_网络编程

3.2.1 什么是网络、计算机网络的构成是什么?

        在计算领域中,网络是传输信息、接受、共享的虚拟的平台。通过它可以把各个点、面、体的信息联系到一起,从而实现这些资源的共享。网络是人类发展史上最重要的发明,提高了人类和科技的一个发展。

3.2.2 什么是网络编程?

网络编程从大的方面就是说对信息的发送接收。

通过操作相应API调度计算机资源硬件,并且利用管道(网线)进行数据交互的过程。

更为具体的涉及:网络模型、套接字、数据包。

3.2.3 7层网络模型--OSI

基础层:物理层(physical)、数据链路层(Datalink)、网络层(network).。

传输层(Transport):TCP-UDP协议层、Socket。

高级层::会话层(Session)、表示层(Presentation)、应用层(Application)

3.2.4 网络模型---对应关系

3.2.5 Socket与TCP、UDP 

 Socket:简单来说是ip地址与端口的结合协议(RFC 793).

              一种地址与端口的结合描述协议。

             TCP/IP协议的相关API的总称;是网络API的集合实现.

              涵盖了Stream socket /Datagram Socket 

 socket 的组成与作用:

              在网络传输中用于唯一标识两个端点的链接。

              端点:包括(ip+port)

              4个要素:客户端的地址、客户端的端口、服务器的地址、服务器端口。

3.2.6 Socket的传输原理

Socket之TCP:

         tcp是面向连接的通讯协议。

         通过三次握手建立连接,通讯完成时要拆除连接。

         由于TCP是面向连接的,所以只能用于端到端的通信。

Socket之UDP:

        UDP是面向无连接进行通讯的。

        UDP数据包括目的端口号和源端口号信息。

        由于通讯时是不需要连接,所以可以是实现广播发送,并不局限于端到端。

3.3 socket编程(转载)

socket编程入门:1天玩转socket通信技术(非常详细)socket编程也叫套接字编程,这套socket入门教程将为你讲解socket通信的相关技术。本教程通俗易懂、深入浅出,你将很容易掌握socket套接字编程技巧。http://c.biancheng.net/socket/

3.3.1 结构体

struct sockaddr

   unsigned short sa_family;

     char sa_data[14];  

}; 

1、sa_family 为调用socket()时的domain 参数, 即AF_xxxx 值。AF为地址族(Address Family),也就是 IP 地址类型,常用的有 AF_INET 和 AF_INET6。INET是“Inetnet”的简写。AF_INET 表示 IPv4 地址,例如 127.0.0.1;AF_INET6 表示 IPv6 地址,例如 1030::C9B4:FF12:48AA:1A2B。

2、sa_data 为14字节的协议地址,包含该socket的IP地址和端口号。

注:此数据结构用做bind、connect、recvfrom、sendto等函数的参数,指明地址信息,sockaddr 结构会因使用不同的socket domain 而有不同结构定义。

3.3.2 socket() 函数

1)creates  an  endpoint  for  communication and returns a descriptor

int socket (int domain, int type, int protocol);

domain 参数指定一个通信域; 这个选择将用于通信的协议族。这些系列在 <sys/socket.h> 中定义。 目前可理解的格式包括:

  1. Name Purpose Man page
  2. AF_UNIX, AF_LOCAL Local communication unix(7)
  3. AF_INET IPv4 Internet protocols ip(7)
  4. AF_INET6 IPv6 Internet protocols ipv6(7)
  5. AF_IPX IPX - Novell protocols
  6. AF_NETLINK Kernel user interface device netlink(7)
  7. AF_X25 ITU-T X.25 / ISO-8208 protocol x25(7)
  8. AF_AX25 Amateur radio AX.25 protocol
  9. AF_ATMPVC Access to raw ATM PVCs
  10. AF_APPLETALK AppleTalk ddp(7)
  11. AF_PACKET Low level packet interface packet(7)
  12. AF_ALG Interface to kernel crypto API

type参数为套接字指定类型,它指定了通信语义。 当前定义的类型有:

  1. SOCK_STREAM 提供基于顺序的、可靠的、双向的、连接的字节流。
  2. SOCK_DGRAM 支持数据报(无连接、不可靠固定最大长度的消息)。
  3. SOCK_SEQPACKET 基于数据报提供有序、可靠的、双向连接的数据传输路径固定最大长度。
  4. SOCK_RAW 提供原始网络协议访问。
  5. SOCK_RDM 提供不保证排序的可靠数据报层。

protocol 指定要与套接字一起使用的特定协议。 通常,在给定的protocol 族中,只有一个协议支持特定的套接字类型,在这种情况下,protocol 可以指定为 0。但是,可能存在许多协议,在这种情况下,必须指定一个特定的protocol 。

2) bind a name to a socket  (为套接字分配名称)

int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);

bind() 将 addr 指定的地址分配给文件描述符 sockfd 引用的套接字。addrlen 指定 addr 指向的地址结构的大小(以字节为单位)此操作称为“为套接字分配名称”。

3)initiate a connection on a socket (在套接字上启动连接)

int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen);

connect() 调用后系统将文件描述符 sockfd 引用的套接字连接到 addr 指定的地址。addrlen 参数指定 addr 的大小。addr中地址的格式由socket sockfd的地址空间决定。

4)listen for connections on a socket (侦听套接字上的连接)

int listen(int sockfd, int backlog);
listen() 将 sockfd 引用的套接字标记为被动套接字,用于接受即将传入的套接字,使用accept() 的连接请求。

sockfd 参数是引用套接字的文件描述符:SOCK_STREAM 或 SOCK_SEQPACKET 类型。

backlog 参数定义了 sockfd 的挂起连接队列可以增长到的最大长度。 如果连接请求在队列已满时到达,客户端可能会收到一个带有 ECONNREFUSED 指示的错误,或者,如果底层协议支持重传,则该请求可能会被忽略,以便稍后重新尝试连接成功。

5)accept a connection on a socket (接受套接字上的连接)

int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);

accept() 与基于连接的套接字类型(SOCK_STREAM、SOCK_SEQPACKET)一起使用。它从侦听套接字 sockfd 的挂起连接队列中提取第一个连接请求,创建一个新的连接套接字,并返回一个引用该套接字的新文件描述符。新创建的套接字未处于侦听状态。原始套接字 sockfd 不受此调用的影响。

参数 sockfd 是一个使用 socket() 创建的套接字,使用 bind() 绑定到本地地址,并在 listen() 之后监听连接。

参数 addr 是指向 sockaddr 结构的指针。这个结构用通信层已知的对等套接字的地址填充。返回 addr 的地址的确切格式,由套接字的地址系列决定。当addr为NULL时,什么都不填;在这种情况下,addrlen 未使用,也应为 NULL。

addrlen 参数是一个 value-result 参数:调用者必须初始化它以包含 addr 指向的结构的大小(以字节为单位);返回时,它将包含对等地址的实际大小。

6)get and set options on sockets (获取和设置套接字上的选项)

int getsockopt(int sockfd, int level, int optname, void *optval, socklen_t *optlen);
int setsockopt(int sockfd, int level, int optname, const void *optval, socklen_t optlen);
参数sockfd:是一个使用 socket() 创建的套接字返回的文件描述符
参数level:协议层次
                    SOL_SOCKET 套接字层次
                    IPPROTO_IP ip层次
                    IPPROTO_TCP TCP层次
参数optname:选项的名称(套接字层次)
                   SO_BROADCAST 是否允许发送广播信息
                   SO_REUSEADDR 是否允许重复使用本地地址
                   SO_SNDBUF 获取发送缓冲区长度
                   SO_RCVBUF 获取接收缓冲区长度                                                                                     SO_RCVTIMEO 获取接收超时时间
                   SO_SNDTIMEO 获取发送超时时间                                                                             参数optval:指针,指向获取到的选项的缓冲区
 参数optlen:指针,指向optval缓冲区的长度
 返回值:成功:0  失败:-1

3.4 互斥锁

3.5 信号量


4、线程

在文件lora_pkt_fwd.c中,man()里面创建了6个线程来实现如下功能:

  1. 周期性地从concentrator中取出报文封装无线数据包(PUSH_DATA )并通过UDP传输至互联网上的 LoRa 网关(上行)。
  2. 接受server下行应用报文,
  3. 接收GPS位置和时间

3.1 main(主线程)

1、加载配置文件:全局配置还是局部配置还是调试配置

  1. if (access(debug_cfg_path, R_OK) == 0) { /* if there is a debug conf, parse only the debug conf如果有调试配置,只解析调试配置 */
  2. MSG("INFO: found debug configuration file %s, parsing it\n", debug_cfg_path);
  3. MSG("INFO: other configuration files will be ignored\n");
  4. x = parse_SX1301_configuration(debug_cfg_path);
  5. if (x != 0) {
  6. exit(EXIT_FAILURE);
  7. }
  8. x = parse_gateway_configuration(debug_cfg_path);
  9. if (x != 0) {
  10. exit(EXIT_FAILURE);
  11. }
  12. } else if (access(global_cfg_path, R_OK) == 0) { /* if there is a global conf, parse it and then try to parse local conf */
  13. MSG("INFO: found global configuration file %s, parsing it\n", global_cfg_path);
  14. x = parse_SX1301_configuration(global_cfg_path);
  15. if (x != 0) {
  16. exit(EXIT_FAILURE);
  17. }
  18. x = parse_gateway_configuration(global_cfg_path);
  19. if (x != 0) {
  20. exit(EXIT_FAILURE);
  21. }
  22. if (access(local_cfg_path, R_OK) == 0) {
  23. MSG("INFO: found local configuration file %s, parsing it\n", local_cfg_path);
  24. MSG("INFO: redefined parameters will overwrite global parameters\n");
  25. parse_SX1301_configuration(local_cfg_path);
  26. parse_gateway_configuration(local_cfg_path);
  27. }
  28. } else if (access(local_cfg_path, R_OK) == 0) { /* if there is only a local conf, parse it and that's all */
  29. MSG("INFO: found local configuration file %s, parsing it\n", local_cfg_path);
  30. x = parse_SX1301_configuration(local_cfg_path);
  31. if (x != 0) {
  32. exit(EXIT_FAILURE);
  33. }
  34. x = parse_gateway_configuration(local_cfg_path);
  35. if (x != 0) {
  36. exit(EXIT_FAILURE);
  37. }
  38. } else {
  39. MSG("ERROR: [main] failed to find any configuration file named %s, %s OR %s\n", global_cfg_path, local_cfg_path, debug_cfg_path);
  40. exit(EXIT_FAILURE);
  41. }

2、启动GPS

  1. if (gps_tty_path[0] != '\0') { /* do not try to open GPS device if no path set */
  2. i = lgw_gps_enable(gps_tty_path, "ubx7", 0, &gps_tty_fd); /* HAL only supports u-blox 7 for now */
  3. if (i != LGW_GPS_SUCCESS) {
  4. printf("WARNING: [main] impossible to open %s for GPS sync (check permissions)\n", gps_tty_path);
  5. gps_enabled = false;
  6. gps_ref_valid = false;
  7. } else {
  8. printf("INFO: [main] TTY port %s open for GPS synchronization\n", gps_tty_path);
  9. gps_enabled = true;
  10. gps_ref_valid = false;
  11. }
  12. }

3、地址转换,将主机物理地址转换成网络字节顺序地址

  1. net_mac_h = htonl((uint32_t)(0xFFFFFFFF & (lgwm>>32)));
  2. net_mac_l = htonl((uint32_t)(0xFFFFFFFF & lgwm ));

4、创建网络套接字socket进行通信

  1. i = getaddrinfo(serv_addr, serv_port_up, &hints, &result);
  2. ...
  3. for (q=result; q!=NULL; q=q->ai_next) {
  4. sock_up = socket(q->ai_family, q->ai_socktype,q->ai_protocol);
  5. if (sock_up == -1) continue; /* try next field */
  6. else break; /* success, get out of loop */
  7. }
  8. if (q == NULL) {
  9. MSG("ERROR: [up] failed to open socket to any of server %s addresses (port %s)\n", serv_addr, serv_port_up);
  10. i = 1;
  11. for (q=result; q!=NULL; q=q->ai_next) {
  12. getnameinfo(q->ai_addr, q->ai_addrlen, host_name, sizeof host_name, port_name, sizeof port_name, NI_NUMERICHOST);
  13. MSG("INFO: [up] result %i host:%s service:%s\n", i, host_name, port_name);
  14. ++i;
  15. }
  16. exit(EXIT_FAILURE);
  17. }
  18. i = connect(sock_up, q->ai_addr, q->ai_addrlen);
  19. if (i != 0) {
  20. MSG("ERROR: [up] connect returned %s\n", strerror(errno));
  21. exit(EXIT_FAILURE);
  22. }
  23. freeaddrinfo(result);

5、启动集中器

i = lgw_start();

6、创建4个线程来管理上下行:thrid_up、thrid_down、thrid_jit、thrid_timersync

  1. i = pthread_create( &thrid_up, NULL, (void * (*)(void *))thread_up, NULL);
  2. i = pthread_create( &thrid_down, NULL, (void * (*)(void *))thread_down, NULL);
  3. i = pthread_create( &thrid_jit, NULL, (void * (*)(void *))thread_jit, NULL);
  4. i = pthread_create( &thrid_timersync, NULL, (void * (*)(void *))thread_timersync, NULL);

7、创建2个线程来管理GPS:thrid_gps、thrid_valid

  1. i = pthread_create( &thrid_gps, NULL, (void * (*)(void *))thread_gps, NULL);
  2. i = pthread_create( &thrid_valid, NULL, (void * (*)(void *))thread_valid, NULL);

8、信号量。

  1. sigaction(SIGQUIT, &sigact, NULL); /* Ctrl-\ */
  2. sigaction(SIGINT, &sigact, NULL); /* Ctrl-C */
  3. sigaction(SIGTERM, &sigact, NULL); /* default "kill" command */

9、while循环。只要不是退出进程的信号,一直死循环的以默认的时间间隔收集和显示统计信息并发送给server

  1. while (!exit_sig && !quit_sig) {
  2. wait_ms(1000 * stat_interval);/* wait for next reporting interval */

3.2 thread_up(上行线程)

3.3 thread_down(下行线程)

3.4 thread_jit 

3.5 thread_gps

3.6 thread_valid

3.7 thread_timersync

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/我家小花儿/article/detail/516034
推荐阅读
相关标签
  

闽ICP备14008679号