◯由浅入深Linux下pthread线程库介绍.pdf

◯lora_pkt_fwd.c

前1000+主体

1160 MAIN FUNCTION

1173-1178 5个线程

1180-1185 network socket creation

1187-1211 可以去改,都是全局变量

1223-1230 timestamp:时间

1232-1251 用getopt这个函数去取hc命令行,进行解析,不同的后缀有不同的处理方法。(什么意思?编译怎么编译的来着?makefile决定我们怎么)

后面开始就是线程,协议的细节,利用不同的线程实现,看一下线程的功能。

THREAD1

2187 THREAD2

2926 THREAD3

3015 THREAD4

THREAD5

Packet forwarder 是运行在 LoRa 网关上的一个程序,用来将集中器收到的 RF 数据包 通过 IP/UDP 链路转发给服务器,以及将 服务器发出的数据包通过 RF 发出来。它也可以发射 GPS 同步信标帧,用于协调网络内的所有节点。

Packet forwarder 顾名思义,就是一个网关与Server间的包转发器。

c++的基础数据类型如下:

名称

字节长度

取值范围

常用定义

Boolean

1

False,true

bool

char

1

-128~127

char

Signed char

1

-128~127

int8

unsigned char

1

0~255

uint8

Short(signed short)

2

-215~215-1

int16

Unsigned short

2

0~216-1

uint16

Int(signed int)

4

-231~231-1

int32

Unsigned int

4

0~232-1

uint32

Long(signed long)

4

-231~231-1

int64

Long long

8

-263~263-1

int64

Unsigned long

4

0~232-1

uint64

float

4

-3.4*10-38~3.4*1038

float

double

8

-1.7*10308~1.7*10308

double

61-67:私有宏

63:定义了一个名叫ARRAY_SIZE(a)的宏,代表(sizeof(a) / sizeof((a)[0])),其中sizeof是求字节数的,sizeof数组名是数组占用的字节数,sizeof(a[0])是第一个元素的字节数,总数除以一个的大小,就是个数。

64:定义了一个名叫STRINGIFY(x)的宏,代表#x,(用于调试程序使用的,#运算符把跟在后面的参数转化成一个字符串)具体用法及如何转换,不是很明白

65:定义了一个名叫STR(x)的宏,代表宏STRINGIFY(x)

68-118:私有常数

#ifndef 版本字符串/#define版本字符串 "不明确的"/#endif 防止该头文件被重复引用

74:定义了一个宏DEFAULT_SERVER(默认服务器),代表127.0.0.1(主机名也受支持)

75:定义了一个宏DEFAULT_PORT_UP(默认端口向上),代表1780

76:定义了一个宏DEFAULT_PORT_DW(默认端口关闭),代表1782

77:定义了一个宏DEFAULT_KEEPALIVE(默认值保持不变),代表5(下游保活分组默认时间间隔)不是很懂

78:定义了一个宏DEFAULT_STAT,代表30(统计数据的默认时间间隔)

79:定义了一个宏PUSH_TIMEOUT_MS(推送超时毫秒),代表100

80:定义了一个宏PULL_TIMEOUT_MS(拉超时毫秒),代表200

81:定义了一个宏GPS_REF_MAX_AGE(全球定位系统参考最大年龄),代表30(在认为最新全球定位系统同步不可用之前,全球定位系统丢失的最大允许延迟时间(秒))

82:定义了一个宏FETCH_SLEEP_MS(获取睡眠时间,以毫秒),代表10(当提取没有返回分组时,ms的nb等待)

83:定义了一个宏BEACON_POLL_MS(信标轮询时间,以毫秒),代表50(信标发射状态轮询之间的时间(毫秒))

85:定义了一个宏PROTOCOL_VERSION(协议版本号),代表2(v1.3)

87:定义了一个宏XERR_INIT_AVG(初始平均值),代表128(测量值的nb XTAL校正值作为初始值进行平均)

88:定义了一个宏XERR_FILT_COEF (不清楚),代表256(低通XTAL误差跟踪系数)

90:定义了一个宏PKT_PUSH_DATA (数据包推送数据),代表0

91:定义了一个宏PKT_PUSH_ACK (数据包推送确认),代表1

92:定义了一个宏PKT_PULL_DATA (分组拉数据),代表2

93:定义了一个宏PKT_PULL_RESP (不清楚),代表3

94:定义了一个宏PKT_PULL_ACK (分组拉确认),代表4

95:定义了一个宏PKT_TX_ACK (数据包发送确认),代表5

97:定义了一个宏NB_PKT_MAX ,代表8(每个提取/发送周期的最大数据包数)

99:定义了一个宏MIN_LORA_PREAMB (最小LORA前置放大器),代表6(本应用的最小Lora前导码长度)

100:定义了一个宏STD_LORA_PREAMB (标准LORA序言),代表8

101:定义了一个宏MIN_FSK_PREAMB (最小FSK前置放大器),代表3(该应用的最小FSK前导长度)

102:定义了一个宏STD_FSK_PREAMB (标准FSK前置放大器),代表5

104:定义了一个宏STATUS_SIZE (状态大小),代表200

105:定义了一个宏TX_BUFF_SIZE (发送缓冲大小),代表((540 * NB_PKT_MAX) + 30 + STATUS_SIZE)((540×每个提取/发送周期的最大数据包数)+30+状态大小)

107:定义了一个宏UNIX_GPS_EPOCH_OFFSET (UNIX和LINUX时间的弥补),代表315964800(Unix时间起始于:1970年1月01日,GPS时间起始于:1980年1月06日。如果不考虑GPS跳秒的影响,两个时间系统的差值为315964800。从1970年1月1日00:00:00到1980年1月6日00:00:00经过的秒数)

110:定义了一个宏DEFAULT_BEACON_FREQ_HZ(默认信标频率,赫兹),代表869525000

111:定义了一个宏DEFAULT_BEACON_FREQ_NB(默认信标频率),代表1

112:定义了一个宏DEFAULT_BEACON_FREQ_STEP(默认信标频率步骤),代表0

113:定义了一个宏DEFAULT_BEACON_DATARATE(默认信标数据速率),代表9

114:定义了一个宏DEFAULT_BEACON_BW_HZ(默认信标带宽赫兹),代表125000

115:定义了一个宏DEFAULT_BEACON_POWER(默认信标功率),代表14

116:定义了一个宏DEFAULT_BEACON_INFODESC(默认信标信息描述),代表0

130-135:网络配置变量

131:定义了一个typedef unsigned long类型的静态变量lgwm = 0(Lora网关媒体访问控制地址)

132:定义了一个静态的char类型数组serv_addr[64]= STR(DEFAULT_SERVER),(数组用来装服务器地址(主机名或IPv4/IPv6),STR(x)定义在line 65, DEFAULT_SERVER定义在line 74)

133:定义了一个静态的char类型数组serv_port_up[8]= STR(DEFAULT_PORT_UP)(上游流量的服务器端口, STR(x)定义在line 65, DEFAULT_PORT_UP定义在line 75)

134:定义了一个静态的char类型数组serv_port_down[8]= STR(DEFAULT_PORT_DW)(下游流量的服务器端口,STR(x)定义在line 65, DEFAULT_PORT_DW定义在line 76)

135:定义了一个int类型的静态变量keepalive_time=DEFAULT_KEEPALIVE(每X秒发送一次PULL_DATA请求,负值=禁用,DEFAULT_KEEPALIVE定义在line 77)

140-142:网关<->媒体访问控制协议变量

141:定义了一个typedef unsigned int类型的静态变量net_mac_h(最高有效半字节,网络顺序)

142:定义了一个typedef unsigned int类型的静态变量net_mac_l(最低有效半字节,网络顺序)

144-146:网络套接字

145:定义了一个int类型的静态变量sock_up(上游流量的套接字)

146:定义了一个int类型的静态变量sock_down(下游流量的套接字)

158-161: 全球定位系统配置和同步

159: 定义了一个静态的char类型数组gps_tty_path[64]= "\0",(TTY端口全球定位系统的路径接通)

160:定义了一个静态的int类型变量gps_tty_fd=-1(全球定位系统TTY端口的文件描述符)

161:定义了一个静态的boolean类型变量gps_enabled = false(判断那个网关上有全球定位系统吗?)

163-166:全球定位系统时间基准

164: 定义了一个静态的pthread_mutex_t 结构类型的变量mx_timeref,用宏PTHREAD_MUTEX_INITIALIZER来静态初始化互斥锁 (控制对全球定位系统时间基准的访问)不确定说法是否正确

165:定义了一个静态的boolean类型的变量gps_ref_valid(用于判断GPS参考是否可接受(即,不太老))

166:定义了一个静态的tref结构体类型变量time_reference_gps作为时间参考全球定位系统(用于GPS <->时间标记转换的时间参考)

168-169:参考坐标,用于广播(信标)

169:定义了一个静态的coord_s结构体类型变量reference_coord

171-172:启用伪造网关的 GPS 坐标

172:定义了一个静态的boolean类型变量gps_fake_enable(用于启用该功能)

174-210:建立统计数据的测量

175:定义一个静态的pthread_mutex_t 结构类型的变量mx_meas_up,用宏PTHREAD_MUTEX_INITIALIZER来静态初始化互斥锁(控制对上游测量的访问)

注:关于pthread_mutex_t是什么,以及它的使用

pthread_mutex_t是互斥锁,关于互斥锁,找到了这样一个函数原型:

int pthread_mutex_init(pthread_mutex_t *restrict mutex,const pthread_mutexattr_t*restrict attr)

pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;

该函数用于C函数的多线程编程中,互斥锁的初始化。

互斥锁pthread_mutex_t的使用:

1. 互斥锁创建(两种方式)

静态方式动态方式两种。

静态方式:定义一个宏PTHREAD_MUTEX_INITIALIZER来静态初始化互斥锁,方法如下:

pthread_mutex_t mutex=PTHREAD_MUTEX_INITIALIZER;

在LinuxThreads实现中,pthread_mutex_t是一个结构,而PTHREAD_MUTEX_INITIALIZER则是一个结构常量。

动态方式:

采用pthread_mutex_init()函数来初始化互斥锁,API定义如下:

int pthread_mutex_init(pthread_mutex_t *mutex, const pthread_mutexattr_t *mutexattr)

其中mutexattr用于指定互斥锁属性(见下文),如果为NULL则使用默认的互斥锁属性,默认属性为快速互斥锁。

pthread_mutexattr_init() 函数成功完成之后会返回零,其他任何返回值都表示出现了错误。

函数成功执行后,互斥锁被初始化为未锁住态。

注销一个互斥锁

调用pthread_mutex_destroy (),API定义如下:

int pthread_mutex_destroy(pthread_mutex_t *mutex)

销毁一个互斥锁即意味着释放它所占用的资源,且要求锁当前处于开放状态。由于在Linux中,互斥锁并不占用任何资源,因此LinuxThreads中的 pthread_mutex_destroy()除了检查锁状态以外(锁定状态则返回EBUSY)没有其他动作。

2. 互斥锁属性

互斥锁的属性在创建锁的时候指定,在LinuxThreads实现中仅有一个锁类型属性,不同的锁类型在试图对一个已经被锁定的互斥锁加锁时表现不同。

有四个值可供选择:

* PTHREAD_MUTEX_TIMED_NP:这是缺省值,也就是普通锁。当一个线程加锁以后,其余请求锁的线程将形成一个等待队列,并在解锁后按优先级获得锁。这种锁策略保证了资源分配的公平性。

* PTHREAD_MUTEX_RECURSIVE_NP嵌套锁,允许同一个线程对同一个锁成功获得多次,并通过多次unlock解锁。如果是不同线程请求,则在加锁线程解锁时重新竞争。

* PTHREAD_MUTEX_ERRORCHECK_NP检错锁,如果同一个线程请求同一个锁,则返回EDEADLK,否则与PTHREAD_MUTEX_TIMED_NP类型动作相同。这样就保证当不允许多次加锁时不会出现最简单情况下的死锁。

* PTHREAD_MUTEX_ADAPTIVE_NP适应锁,动作最简单的锁类型,仅等待解锁后重新竞争。

3. 锁操作

①主要包括:加锁 pthread_mutex_lock()、解锁pthread_mutex_unlock()和测试加锁 pthread_mutex_trylock()三个

②不论哪种类型的锁,都不可能被两个不同的线程同时得到,而必须等待解锁。在同一进程中的线程,如果加锁后没有解锁,则任何其他线程都无法再获得锁。

不同锁对应不同的解锁者:

普通锁适应锁:解锁者可以是同进程内任何线程;

检错锁:必须由加锁者解锁才有效,否则返回EPERM;

嵌套锁:文档和实现要求必须由加锁者解锁,但实验结果表明并没有这种限制,这个不同目前还没有得到解释。

Eg:

int pthread_mutex_lock(pthread_mutex_t *mutex)

int pthread_mutex_unlock(pthread_mutex_t *mutex)

int pthread_mutex_trylock(pthread_mutex_t *mutex)

pthread_mutex_trylock()语义与pthread_mutex_lock()类似,不同的是在锁已经被占据时返回EBUSY而不是挂起等待。

176:定义了一个typedef unsigned int类型的静态变量meas_nb_rx_rcv并赋值0(统计收到的数据包)

177:定义了一个typedef unsigned int类型的静态变量meas_nb_rx_ok并赋值0(对进行crc校验成功过后的数据包计数)

178:定义了一个typedef unsigned int类型的静态变量meas_nb_rx_bad并赋值0(对进行crc校验失败过后的数据包计数)

179:定义了一个typedef unsigned int类型的静态变量meas_nb_rx_nocrc并赋值0(对没有收到进行crc校验码的数据包计数)

180:定义了一个typedef unsigned int类型的静态变量meas_up_pkt_fwd并赋值0(转发到服务器的无线电数据包的数量)

181:定义了一个typedef unsigned int类型的静态变量meas_up_network_byte并赋值0(为上行通道发送的 UDP 字节总和)

182:定义了一个typedef unsigned int类型的静态变量meas_up_payload_byte并赋值0(为上行通道发送的无线电有效载荷字节的总和)

183:定义了一个typedef unsigned int类型的静态变量meas_up_dgram_sent并赋值0(为上行通道发送的数据报数)

184:定义了一个typedef unsigned int类型的静态变量meas_up_ack_rcv并赋值0(为上行通道确认的数据报数)

186:定义一个静态的pthread_mutex_t 结构类型的变量mx_meas_dw,用宏PTHREAD_MUTEX_INITIALIZER来静态初始化互斥锁(控制对下游测量的访问)

187:定义了一个typedef unsigned int类型的静态变量meas_dw_pull_sent并赋值0(为下行通道发送的 PULL 请求计数)

188:定义了一个typedef unsigned int类型的静态变量meas_dw_ack_rcv并赋值0(为下行通道确认的 PULL 请求计数)

189:定义了一个typedef unsigned int类型的静态变量meas_dw_dgram_rcv并赋值0(为下行通道接收的 PULL 响应数据包计数)

190:定义了一个typedef unsigned int类型的静态变量meas_dw_network_byte并赋值0(为上游流量发送的 UDP 字节总和)

191:定义了一个typedef unsigned int类型的静态变量meas_dw_payload_byte并赋值0(为上行流量发送的无线电有效载荷字节的总和)

192:定义了一个typedef unsigned int类型的静态变量meas_nb_tx_ok并赋值0(计数成功发出的数据包)

193:定义了一个typedef unsigned int类型的静态变量meas_nb_tx_fail并赋值0(计数数据包因其他原因而发送失败)

194:定义了一个typedef unsigned int类型的静态变量meas_nb_tx_requested并赋值0(计算来自服务器的发送请求(下行链路))

195:定义了一个typedef unsigned int类型的静态变量meas_nb_tx_rejected_collision_packet并赋值0(由于与另一个已编程的数据包发生冲突,计数数据包被拒绝发送请求)

196:定义了一个typedef unsigned int类型的静态变量meas_nb_tx_rejected_collision_beacon并赋值0(计数数据包是发送请求由于与已编程的信标发生冲突而被拒绝)

197:定义了一个typedef unsigned int类型的静态变量meas_nb_tx_rejected_too_late并赋值0(计数数据包发送请求被拒绝,因为编程太晚了)

198:定义了一个typedef unsigned int类型的静态变量meas_nb_tx_rejected_too_early并赋值0(count数据包发送请求被拒绝,因为时间标记提前太多)

199:定义了一个typedef unsigned int类型的静态变量meas_nb_beacon_queued并赋值0(计数插入实时队列的信标)

200:定义了一个typedef unsigned int类型的静态变量meas_nb_beacon_sent并赋值0(计数信标实际发送到集中器)

201:定义了一个typedef unsigned int类型的静态变量meas_nb_beacon_rejected并赋值0(计数信标拒绝排队)

203:定义一个静态的pthread_mutex_t 结构类型的变量mx_meas_gps,用宏PTHREAD_MUTEX_INITIALIZER来静态初始化互斥锁(控制对 GPS 统计数据的访问)

204:定义一个静态的Boolean类型变量gps_coord_valid(判断:我们能得到有效的 GPS 坐标吗?)

205:定义一个coord_s结构体类型的变量meas_gps_coord(网关的 GPS 位置)

206:定义一个coord_s结构体类型的变量meas_gps_err(网关的 GPS 位置)

208:定义一个静态的pthread_mutex_t 结构类型的变量mx_stat_rep,用宏PTHREAD_MUTEX_INITIALIZER来静态初始化互斥锁(控制对状态报告的访问)

209:定义一个静态的Boolean类型变量report_ready,并赋值为false(当有新报告发送到服务器时为真)

210:定义了一个char类型的数组status_report[STATUS_SIZE](STATUS_SIZE定义在line 104,为200)(状态报告作为 JSON 对象)

974-1440 MAIN FUNCTION主函数

978:定义了一个sigaction结构体类型的sigact(用于退出信号&输入信号&信号术语的信号处理)

979、980:定义了int类型的i和x(分别作为返回值的循环变量和临时变量)

983-985:在配置文件相关(配置的三个文件介绍)

983:定义了一个char类型一级指针global_cfg_path(全局配置路径)=“global_conf.json”( 包含全局(网络范围)配置)

984:定义了一个char类型的一级指针local_cfg_path(本地配置路径)= "local_conf.json"(包含特定于节点的配置,覆盖两者中定义的参数的全局参数)

985:定义了一个char类型的一级指针debug_cfg_path(调试配置路径)="debug_conf.json"

注:配置的三个文件介绍

local_conf.json、global_conf.json、debug_conf.json是什么?能用来干什么的?

local_conf.json文件:是针对单个网关的配置信息,比如 MAC 地址,或者特定频点。一般配置“SX1301_conf”、“gateway_conf” 中的部分字段。

global_conf.json文件:是某一类网关的通用配置,一般产品批量是定型下来

debug_conf.json文件:是调试采用的配置,如果有它就忽略其他配置文件

他们三个文件的解析和覆盖优先级如下:

debug_conf.json

local_conf.json

global_conf.json

987-993:声明线程(其中pthread_t是线程标识符,用于声明线程ID)

988:声明上行线程thrid_up

989:声明下行线程thrid_down

990:声明定位线程thrid_gps

991:声明验证线程thrid_valid

992:声明实时线程thrid_jit

993:声明同步定时器线程thrid_timersync(不确定)

995-1000:创建网络套接字

996:定义了一个addrinfo结构体类型的hints,用于提示

997:定义了一个addrinfo结构体类型的一级指针result,指针用于存储获取的地址信息结果

998:定义了一个addrinfo结构体类型的一级指针q,用于移动到存储获取的地址信息结果数据的指针

999:定义了一个char类型的数组host_name[64],装主机名

1000:定义了一个char类型的数组port_name[64],装端口名

1002-1026:定义变量来获取测量值的本地副本(uint32_t:typedef unsigned int)

1003:定义了一个typedef unsigned int类型的变量cp_nb_rx_rcv

1004:定义了一个typedef unsigned int类型的变量cp_nb_rx_ok

1005:定义了一个typedef unsigned int类型的变量cp_nb_rx_bad

1006:定义了一个typedef unsigned int类型的变量cp_nb_rx_nocrc

1007:定义了一个typedef unsigned int类型的变量cp_up_pkt_fwd

1008:定义了一个typedef unsigned int类型的变量cp_up_network_byte

1009:定义了一个typedef unsigned int类型的变量cp_up_payload_byte

1010:定义了一个typedef unsigned int类型的变量cp_up_dgram_sent

1011:定义了一个typedef unsigned int类型的变量cp_up_ack_rcv

1012:定义了一个typedef unsigned int类型的变量cp_dw_pull_sent

1013:定义了一个typedef unsigned int类型的变量cp_dw_ack_rcv

1014:定义了一个typedef unsigned int类型的变量cp_dw_dgram_rcv

1015:定义了一个typedef unsigned int类型的变量cp_dw_network_byte

1016:定义了一个typedef unsigned int类型的变量cp_dw_payload_byte

1017:定义了一个typedef unsigned int类型的变量cp_nb_tx_ok

1018:定义了一个typedef unsigned int类型的变量cp_nb_tx_fail

1019:定义了一个typedef unsigned int类型的变量cp_nb_tx_requested

1020:定义了一个typedef unsigned int类型的变量cp_nb_tx_rejected_collision_packet

1021:定义了一个typedef unsigned int类型的变量cp_nb_tx_rejected_collision_beacon

1022:定义了一个typedef unsigned int类型的变量cp_nb_tx_rejected_too_late

1023:定义了一个typedef unsigned int类型的变量cp_nb_tx_rejected_too_early

1024:定义了一个typedef unsigned int类型的变量cp_nb_beacon_queued

1025:定义了一个typedef unsigned int类型的变量cp_nb_beacon_sent

1026:定义了一个typedef unsigned int类型的变量cp_nb_beacon_rejected

1028-1030:全球定位系统坐标变量

1029:定义了一个boolean类型的变量coord_ok=false(coord坐标,bool布尔类型)

1030:定义了一个coord_s结构体类型变量cp_gps_coord={0.0,0.0,0}

1032-1033:SX1301数据变量

1033:定义了一个typedef unsigned int类型的变量trig_tstamp

1035-1042:统计变量

1036:定义了一个time_t类型的变量t

1037:定义了一个数组stat_timestamp[24],数组中每一位的类型都是char。用于统计时间标记。

1038:定义了一个float类型的变量rx_ok_ratio(接受正常比率)

1039:定义了一个float类型的变量rx_bad_ratio(接收坏比率)

1040:定义了一个float类型的变量rx_nocrc_ratio(接收没有CRC比率)

1041:定义了一个float类型的变量up_ack_ratio(上行应答比率)

1042:定义了一个float类型的变量dw_ack_ratio(下行应答比率)

1044-1047:显示版本信息

1045:显示Lora网关信标分组转发器版本号

1046:显示Lora集中器HAL库版本信息

1048-1055:显示主机字符顺序

1049-1050:如果字节顺序是小端序(LITTLE-ENDIAN(小字节序、低字节序),即低位字节排放在内存的低地址端,高位字节排放在内存的高地址端。)就显示信息:小端主机

1051-1052:不满足上一个条件,则看是否字节顺序是大端序(高位字节排放在内存的低地址端,低位字节排放在内存的高地址端。)就显示信息:大端主机

1053-1054:除此之外,显示信息:未知的主机端序

1057-1098:加载配置文件(x作为返回值的临时变量)

1058-1069:如果有调试配置文件,只分析调试配置文件。

1059:显示信息:找到调试配置文件%s,正在分析它(debug_cfg_path调试配置路径,指针指向debug_conf.json)

1060:显示信息:其他配置文件将被忽略

1061:解析SX1301配置(指向debug_cfg_path文件),并将返回值存入临时变量x

1062-1064:如果x不等于0,就表示异常退出

1065:解析网关配置(指向debug_cfg_path文件),并将返回值存入临时变量x

1066-1068:如果x不等于0,就表示异常退出

1069-1076:不满足上一个条件,如果有全局配置,解析它,然后尝试解析本地配置

1070:显示信息:找到全局配置文件%s,正在分析它(global_cfg_path全局配置路径,指针指向global_conf.json)

1071:解析SX1301配置(指向global_conf.json文件),并将返回值存入临时变量x

1072-1074:如果x不等于0,就表示异常退出

1075:解析网关配置(指向global_conf.json文件),并将返回值存入临时变量x

1076-1078:如果x不等于0,就表示异常退出

1079:如果有找到本地配置文件,分析它,重新定义的参数将覆盖全局参数

1080:显示信息:找到本地配置文件%s,正在分析它(local_cfg_path本地配置路径,指针指向 local_conf.json)

1081:显示信息:重新定义的参数将覆盖全局参数

1082:解析SX1301配置(指向local_conf.json文件)

1083:解析网关配置(指向local_conf.json文件)

1085-1094:如果只有一个本地配置,解析它,如此而已

1086:显示信息:找到本地配置文件%s,正在分析它(local_cfg_path本地配置路径,指针指向 local_conf.json)

1087:解析SX1301配置(指向local_conf.json文件)

1088-1090:如果x不等于0,就表示异常退出

1091:解析网关配置(指向local_conf.json文件)

1092-1094:如果x不等于0,就表示异常退出

1095:以上情况都不满足

1096:显示信息:错误:[主]找不到任何名为%s、%s或%s的配置文件(global_cfg_path, local_cfg_path, debug_cfg_path三个指针无法寻址到相应的文件)

1097:异常退出

1100-1112:启动全球定位系统,让它锁定

1101:如果gps_tty_path数组的第0位不是结束位(看gps_tty_path数组的第0位是否为结束位,就知道TTY端口全球定位系统的路径是否接通,如果没有设置路径,不要尝试打开GPS设备,数组定义在line 159)

1102:就将lora网关的GPS使能打开,使用i作为lgw_gps_enable()返回值的循环变量(HAL目前只支持核心定位技术平台u-blox 7)

1103-1106:如果i不等于LGW_GPS_SUCCESS

1104:就输出警告:[主]无法打开%s(gps_tty_path数组存储的路径)进行全球定位系统同步(检查权限)

1105:变量gps_enabled = false(变量定义在line 161)

1106:变量gps_ref_valid = false(变量定义在line 165)

1107-1112:i除此之外

1108:就输出信息:[主] TTY端口%s打开,用于全球定位系统同步(gps_tty_path数组指向存储的路径)

1109:变量gps_enabled = true(变量定义在line 161,表示网关上有全球定位系统)

1110:变量gps_ref_valid = false(变量定义在line 165,表示GPS参考无效)

1114-1115: 获取时区信息

1115:调用函数tzset()获取时区信息

1117-1118:去做配置变量的健全性检查

1120-1122:处理一些配置变量(具体见htonl()函数学习)

1121:给net_mac_h赋值=htonl((uint32_t)(0xFFFFFFFF & (lgwm>>32)))(将Lora网关媒体访问控制地址右移32位和0xFFFFFFFF相与,再转换成typedef unsigned int类型,使用htonl()函数把本机字节顺序转化为网络字节顺序。)

1122:给net_mac_l赋值= htonl((uint32_t)(0xFFFFFFFF &  lgwm  ))(将Lora网关媒体访问控制地址和0xFFFFFFFF相与,再转换成typedef unsigned int类型,使用htonl()函数把本机字节顺序转化为网络字节顺序。)

注:htonl()函数学习

htonl(host to network ,l代表返回类型是unsigned long)就是把本机字节顺序转化为网络字节顺序。

所谓网络字节顺序(大尾顺序)就是指一个数在内存中存储的时候“高对低,低对高”(即一个数的高位字节存放于低地址单元,低位字节存放在高地址单元中)。

但是计算机的内存存储数据时既有可能是大尾顺序,也可能是小尾顺序。

当我们需要网络字节顺序的时,本机字节顺序是不确定的,所以就通过htonl()函数来将它们都转化成大尾顺序。

举个例子:

int a = 0x403214;

int b = htonl(a);

在VC++6.0调试这段代码,发现

&a的值为:0x0012ff44

其中0x0012ff44、0x0012ff45、0x0012ff46、0x0012ff47这四个单元的值依次为:14、32、40、00,即0x403214这个数的高位部分存放在高位地址中,低位部分存放在低位地址中,即小尾顺序。

&b的值为:0x0012ff40

其中0x0012ff40、0x0012ff41、0x0012ff42、0x0012ff43这四个单元的值依次为:00、40、32、14,即把原数0x403214的高位部分存放在低位地址中,低位部分存放在高位地址中。

由此可见,如果一个数以小尾顺序存储,经htonl函数调用后这个数的高地位字节会完全颠倒过来成为一个新的数。这个新的数在机器内部其实还是以小尾顺序存储的,但是相对于原来的数而言相当于是变成大尾顺序的了。

long型的0x40写完整为:0x 00 00 00 40,共四个字节,调用htonl后四个字节颠倒顺序,为0x 40 00 00 00。

同理,0x40 00 00 00调用htonl后变为0x 00 00 00 40,即0x40

1124-1127: 准备打开网络套接字的提示

1125:调用memset()函数

1126:给hints结构体的ai_family(指定调用者期待返回的套接口地址结构的类型)赋值=AF_INET(WA:将IPv4强制为AF _ UNDENCE会导致本地主机上的连接失败)

注:ai_family

ai_family的值包括三种:AF_INET、AF_INET6、AF_UNSPEC

①如果指定AF_INET:函数就不能返回任何IPV6相关的地址信息;

②如果指定了AF_INET6:就不能返回任何IPV4地址信息;

③AF_UNSPEC:意味着函数返回的是适用于指定主机名和服务名且适合任何协议族的地址。

如果某个主机既有AAAA记录(IPV6)地址,同时又有A记录(IPV4)地址,那么AAAA记录将作为sockaddr_int6结构返回,而A记录则作为sockaddr_in结构返回。

1127:给hints结构体的成员变量ai_socktype赋值=SOCK_DGRAM(套接字)

1129-1134:寻找上游端口的服务器地址

1130:把调用函数getaddrinfo()的返回值赋给变量i(&result获取地址信息结果)

1131:如果i不等于0

1132:显示信息:错误:[向上]获取地址%s(端口%s)上的地址信息返回%s\n "(三个%s分别对应serv_addr,serv_addr,gai_strerror(i),前两个定义在line 132、line 133)

1133:异常退出

1136-1140:尝试为上游流量打开套接字

1137:(for循环)q的初值为result,当q不为空时,进行下列循环,每循环一次q就被赋值q的ai_next

1138:将调用函数socket()的返回值赋值给套接字sock_up (sock_up的定义在line 145)

1139:如果套接字sock_up等于-1,就跳出本次循环,进入下一次循环(尝试下一个字段)

1140:如果套接字sock_up不等于-1,就结束for循环(成功,脱离循环)

1142:假如q等于空的时候(包含下面的for循环)

1143:显示信息:错误:[up]无法打开到任何服务器%s地址(端口%s)的套接字(两个%s分别对应serv_addr,serv_port_up)

1144:i被赋值为1

1145:(for循环)q的初值为result,当q不为空时,进行下列循环,每循环一次q就被赋值q的ai_next

     1146:调用函数getnameinfo()

1147:显示信息:信息:[向上]结果%i主机:%s服务:%s(分别对应i, host_name, port_name)

1148:i=i+1

1150:异常退出

1153-1159:连接,以便我们可以只与服务器发送/接收数据包

1154:调用函数connect(sock_up, q->ai_addr, q->ai_addrlen),并将返回值赋值给1(sock_up定义在line 145)

1155:如果i不等于0

1156:就显示信息:错误:[上行]连接返回%s(%s对应strerror(errno))

注:strerror(errno):根据errno的值返回出errno所对应的描述信息

1157:异常退出

1158:如果i=0,就调用函数freeaddrinfo(result)( result定义在line 997)

1161-1166: 寻找带有下游端口的服务器地址

1162:调用函数getaddrinfo(),并将返回值赋值给i。(hints定义在line 996,result定义在line 997)

1163:如果i不等于0

1164:就显示信息:错误:[下行]获取地址%s(端口%s)的地址信息返回了%s(%s分别对应serv_addr,serv_port_up,gai_strerror(i))

1165:异常退出

1168-1183:尝试为下游流量打开套接字

(过程和1136-1140:尝试为上游流量打开套接字一样)

1169:(for循环)初值q= result,当q不为空时,进行下列循环,每循环一次q就被赋值q的ai_next

1170:将调用函数socket()的返回值赋值给套接字sock_down (sock_down的定义在line 146)

1171:如果套接字sock_down等于-1,就跳出本次循环,进入下一次循环(尝试下一个字段)

1172:如果套接字sock_down不等于-1,就结束for循环(成功,脱离循环)

1174:假如q等于空的时候(包含下面的for循环)

1175:显示信息:错误:[down]无法打开到任何服务器%s地址(端口%s)的套接字(两个%s分别对应serv_addr,serv_port_up)

1176:i被赋值为1

1177:(for循环)q的初值为result,当q不为空时,进行下列循环,每循环一次q就被赋值q的ai_next

       1178:调用函数getnameinfo()

1179:显示信息:信息:[向下]结果%i主机:%s服务:%s(分别对应i, host_name, port_name)

1180:i=i+1

1182:异常退出

1185-1191:连接,以便我们可以只与服务器发送/接收数据包

(和1153-1159:连接,以便我们可以只与服务器发送/接收数据包的过程一样)

1186:调用函数connect(sock_down, q->ai_addr, q->ai_addrlen),并将返回值赋值给1(sock_down定义在line 146)

1187:如果i不等于0

1188:就显示信息:错误:[下行]连接返回%s(%s对应strerror(errno))

注:strerror(errno):根据errno的值返回出errno所对应的描述信息

1189:异常退出

1190:如果i=0,就调用函数freeaddrinfo(result)( result定义在line 997)

1193-1200:启动集中器

1194:调用函数lgw_start(),并将返回值赋值给临时变量i

1195-1196:如果i等于LGW_HAL_SUCCESS,就输出信息:信息:[主]集中器启动,现在可以接收数据包

1197-1200:如果不是,就输出信息:错误:[主]无法启动集中器,异常退出

1202-1222:产生线程来管理上游和下游

1203:调用函数pthread_create(&thrid_up, NULL, (void * (*)(void *))thread_up, NULL),并将返回值赋值给临时变量i(thrid_up定义在line 988)

1204-1207:如果i不等于0,就输出信息:错误:[main] 无法创建上游线程,异常退出

1208:调用函数pthread_create( &thrid_down, NULL, (void * (*)(void *))thread_down, NULL),并将返回值赋值给临时变量i(thrid_down定义在line 989)

1209-1212:如果i不等于0,就输出信息:错误:[main] 无法创建下游线程,异常退出

1213:调用函数pthread_create( &thrid_jit, NULL, (void * (*)(void *))thread_jit, NULL),并将返回值赋值给临时变量i(thrid_jit定义在line 992)

1214-1217:如果i不等于0,就输出信息:错误:[main] 无法创建实时线程,异常退出

1218:调用函数pthread_create( &thrid_timersync, NULL, (void * (*)(void *))thread_timersync, NULL),并将返回值赋值给临时变量i(thrid_timersync定义在line 993)

1219-1222:如果i不等于0,就输出信息:错误:[main] 无法创建定时器同步线程,异常退出

1224-1236:生成线程来管理 GPS

1225:如果gps_enabled等于true

1226:就调用函数pthread_create( &thrid_gps, NULL, (void * (*)(void *))thread_gps, NULL) ,并将返回值赋值给临时变量i(thrid_gps定义在line 990)

1227-1230:如果i不等于0,就输出信息:错误:[main]无法创建GPS线程,异常退出

1231:调用函数pthread_create( &thrid_valid, NULL, (void * (*)(void *))thread_valid, NULL) ,并将返回值赋值给临时变量i(thrid_valid定义在line 991)

1232-1235:如果i不等于0,就输出信息:错误:[main] 无法创建验证线程,异常退出

1238-1244:配置信号处理

1239:调用函数sigemptyset(&sigact.sa_mask)

1240:给结构体sigact的成员变量sa_flags赋值0(结构体sigact的定义在line 978)

1241:给结构体sigact的成员变量sa_handler赋值sig_handler(sig_handler的定义在其他文件)

1242:调用函数sigaction(SIGQUIT, &sigact, NULL)(Ctrl-\)

1243:调用函数sigaction(SIGQUIT, &sigact, NULL)(Ctrl-C)

1244:调用函数sigaction(SIGTERM, &sigact, NULL)(默认的“kill”命令)

1246-1406:主循环任务:统计收集

1247-1405:当非exit_sig且非quit_sig时(定义在其他文件)

1248-1249:等待下一个报告间隔

1249:调用函数wait_ms(1000 * stat_interval)

1251-1253:获取时间标记去统计

1252:调用函数time(NULL),并将返回值赋值给t(t定义在line 1036)

1253:调用函数strftime(stat_timestamp, sizeof stat_timestamp, "%F %T %Z", gmtime(&t))(stat_timestamp定义在line 1037)

1255-1289:访问上游统计信息,复制并重置它们

1256:调用函数pthread_mutex_lock(&mx_meas_up)(mx_meas_up定义在line 175)

1257:变量cp_nb_rx_rcv被赋值meas_nb_rx_rcv(变量cp_nb_rx_rcv定义在line 1003,meas_nb_rx_rcv定义在line 176)

1258:变量cp_nb_rx_ok被赋值meas_nb_rx_ok(cp_nb_rx_ok定义在line1004,meas_nb_rx_ok定义在line 177)

1259:变量cp_nb_rx_bad被赋值meas_nb_rx_bad(cp_nb_rx_bad定义在line 1005,meas_nb_rx_bad定义在line 178)

1260:变量cp_nb_rx_nocrc被赋值meas_nb_rx_nocrc(cp_nb_rx_nocrc定义在line 1006,meas_nb_rx_nocrc定义在line 179)

1261:变量cp_up_pkt_fwd被赋值meas_up_pkt_fwd(cp_up_pkt_fwd定义在line 1007,meas_up_pkt_fwd定义在line 180)

1262:变量cp_up_network_byte被赋值meas_up_network_byte(cp_up_network_byte定义在line 1008,meas_up_network_byte定义在line 181)

1263:变量cp_up_payload_byte被赋值meas_up_payload_byte(cp_up_payload_byte定义在line 1009,meas_up_payload_byte定义在line 182)

1264:变量cp_up_dgram_sent被赋值meas_up_dgram_sent(cp_up_dgram_sent定义在line 1010,meas_up_dgram_sent定义在line 183)

1265:变量cp_up_ack_rcv被赋值meas_up_ack_rcv(meas_nb_rx_rcv定义在line 1011,meas_up_ack_rcv定义在line 184)

1266:变量meas_nb_rx_rcv被赋值0(meas_nb_rx_rcv定义在line 176)

1267:变量meas_nb_rx_ok被赋值0(meas_nb_rx_ok定义在line 177)

1268:变量meas_nb_rx_bad被赋值0(meas_nb_rx_bad定义在line 178)

1269:变量meas_nb_rx_nocrc被赋值0(meas_nb_rx_nocrc定义在line 179)

1270:变量meas_up_pkt_fwd被赋值0(meas_up_pkt_fwd定义在line 180)

1271:变量meas_up_network_byte被赋值0(meas_up_network_byte定义在line 181)

1272:变量meas_up_payload_byte被赋值0(meas_up_payload_byte定义在line 182)

1273:变量meas_up_dgram_sent被赋值0(meas_up_dgram_sent定义在line 183)

1274:变量meas_up_ack_rcv被赋值0(meas_up_ack_rcv定义在line 184)

1275:给mx_meas_up互斥锁解锁(mx_meas_up定义在line 175)

1276-1279:如果cp_nb_rx_rcv大于0

1277:将cp_nb_rx_ok转化为float类型除以同转化为float类型的cp_nb_rx_rcv,结果赋值给rx_ok_ratio

1278:将cp_nb_rx_bad转化为float类型除以同转化为float类型的cp_nb_rx_rcv,结果赋值给rx_bad_ratio

1279:将cp_nb_rx_nocrc转化为float类型除以同转化为float类型的cp_nb_rx_rcv,结果赋值给rx_nocrc_ratio

1280-1284:如果cp_nb_rx_rcv小于等于0

1281:给rx_ok_ratio赋值0.0

1282:给rx_bad_ratio赋值0.0

1283:给rx_nocrc_ratio赋值0.0

1285-1286:如果cp_up_dgram_sent大于0

1286:将cp_up_ack_rcv转化为float类型除以同转化为float类型的cp_up_dgram_sent,结果赋值给up_ack_ratio

1287-1289:如果cp_up_dgram_sent小于等于0

1288:up_ack_ratio赋值0.0

1291-1328:访问下游统计信息,复制并重置它们

1292:给mx_meas_dw互斥锁解锁(mx_meas_dw定义在line 186)

1293:给变量cp_dw_pull_sent赋值meas_dw_pull_sent(cp_dw_pull_sent定义在line 1012,meas_dw_pull_sent定义在line 187)

1294:给变量cp_dw_ack_rcv赋值meas_dw_ack_rcv(cp_dw_ack_rcv定义在line 1013,meas_dw_ack_rcv定义在line 188)

1295:给变量cp_dw_dgram_rcv赋值meas_dw_dgram_rcv(cp_dw_dgram_rcv定义在line 1014,meas_dw_dgram_rcv定义在line 189)

1296:给变量cp_dw_network_byte赋值meas_dw_network_byte(cp_dw_network_byte定义在line 1015,meas_dw_network_byte定义在line 190)

1297:给变量cp_dw_payload_byte赋值meas_dw_payload_byte(cp_dw_payload_byte定义在line 1016,meas_dw_payload_byte定义在line 191)

1298:给变量cp_nb_tx_ok赋值meas_nb_tx_ok(cp_nb_tx_ok定义在line 1017,meas_nb_tx_ok定义在line 192)

1299:给变量cp_nb_tx_fail赋值meas_nb_tx_fail(cp_nb_tx_fail定义在line 1018,meas_nb_tx_fail定义在line 193)

1300: 变量cp_nb_tx_requested赋值+=meas_nb_tx_requested(cp_nb_tx_requested定义在line 1019,meas_nb_tx_requested定义在line 194)

1301:给变量cp_nb_tx_rejected_collision_packet赋值+=meas_nb_tx_rejected_collision_packet(cp_nb_tx_rejected_collision_packet定义在line 1020,meas_nb_tx_rejected_collision_packet定义在line 195)

1302:给变量cp_nb_tx_rejected_collision_beacon赋值+=meas_nb_tx_rejected_collision_beacon(cp_nb_tx_rejected_collision_beacon定义在line 1021,meas_nb_tx_rejected_collision_beacon定义在line 196)

1303:给变量cp_nb_tx_rejected_too_late赋值+=meas_nb_tx_rejected_too_late(cp_nb_tx_rejected_too_late定义在line 1022,meas_nb_tx_rejected_too_late定义在line 197)

1304:给变量cp_nb_tx_rejected_too_early赋值+=meas_nb_tx_rejected_too_early(cp_nb_tx_rejected_too_early定义在line 1023,meas_nb_tx_rejected_too_early定义在line 198)

1305:给变量cp_nb_beacon_queued赋值+=meas_nb_beacon_queued(cp_nb_beacon_queued定义在line 1024,meas_nb_beacon_queued定义在line 199)

1306:给变量cp_nb_beacon_sent赋值+=meas_nb_beacon_sent(cp_nb_beacon_sent定义在line 1025,meas_nb_beacon_sent定义在line 200)

1307:给变量cp_nb_beacon_rejected赋值+=meas_nb_beacon_rejected(cp_nb_beacon_rejected定义在line 1026,meas_nb_beacon_rejected定义在line 201)

1308:给变量meas_dw_pull_sent赋值(定义在line 187)

1309:给变量meas_dw_ack_rcv赋值(定义在line 188)

1310:给变量meas_dw_dgram_rcv赋值(定义在line 189)

1311:给变量meas_dw_network_byte赋值(定义在line 190)

1312:给变量meas_dw_payload_byte赋值(定义在line 191)

1313:给变量meas_nb_tx_ok赋值(定义在line 192)

1314:给变量meas_nb_tx_fail赋值(定义在line 193)

1315:给变量meas_nb_tx_requested赋值(定义在line 194)

1316:给变量meas_nb_tx_rejected_collision_packet赋值(定义在line 195)

1317:给变量meas_nb_tx_rejected_collision_beacon赋值(定义在line 196)

1318:给变量meas_nb_tx_rejected_too_late赋值(定义在line 197)

1319:给变量meas_nb_tx_rejected_too_early赋值(定义在line 198)

1320:给变量meas_nb_beacon_queued赋值(定义在line 199)

1321:给变量meas_nb_beacon_sent赋值(定义在line 200)

1322:给变量meas_nb_beacon_rejected赋值(定义在line 201)

1323:给mx_meas_dw互斥锁解锁(mx_meas_dw定义在line 186

1324-1325:如果cp_dw_pull_sent大于0

1325:将cp_dw_ack_rcv转化为float类型除以同转化为float类型的cp_dw_pull_sent,结果赋值给dw_ack_ratio

1326-1328:如果cp_dw_pull_sent小于等于0

1327:dw_ack_ratio赋值0.0

1330-1336:访问 GPS 统计数据,复制它们

1331:如果gps_enabled为true

1332:给mx_meas_gps互斥锁锁上(mx_meas_gps定义在line 203)

1333:给变量coord_ok赋值gps_coord_valid

1334:给变量cp_gps_coord赋值meas_gps_coord

1335:给mx_meas_gps互斥锁解锁(mx_meas_gps定义在line 203)

1338-1341:如果功能启用,则用参考坐标覆盖

1339:如果gps_fake_enable为true

1340:给变量cp_gps_coord赋值reference_coord

1343-1365:显示报告

1344:打印:“##### %s #####”( %s对应stat_timestamp,定义在line 1037)

1345:打印:“### [UPSTREAM] ###”

1346:打印:“#集中器收到的 RF 数据包:%u”( %u对应的cp_nb_rx_rcv,定义在line 1003)

1347:打印:“# CRC_OK: %.2f%%, CRC_FAIL: %.2f%%, NO_CRC: %.2f%%”(三个分别对应100.0 × rx_ok_ratio、100.0 ×rx_bad_ratio、100.0 ×rx_nocrc_ratio)

1348:打印:“# 转发的 RF 数据包:%u(%u 字节)”(分别对应的是cp_up_pkt_fwd、cp_up_payload_byte)

1349:打印:“# PUSH_DATA 数据包发送:%u(%u 字节)”(分别对应的是cp_up_dgram_sent、cp_up_network_byte)

1350:打印:“# PUSH_DATA 确认:%.2f%%”(对应的是100.0 * up_ack_ratio)

1351:打印:“### [下游] ###”

1352:打印:“# PULL_DATA 已发送:%u(%.2f%% 已确认)”(分别对应的是cp_dw_pull_sent、100.0 * dw_ack_ratio)

1353:打印:“# PULL_RESP(onse) 收到的数据包:%u(%u 字节)”(分别对应的是cp_dw_dgram_rcv、cp_dw_network_byte)

1354:打印:“# 发送到集中器的 RF 数据包:%u(%u 字节)”(分别对应的是cp_nb_tx_ok与cp_nb_tx_fail的和、cp_dw_payload_byte)

1355:打印:“# 发送错误:%u”(对应的是cp_nb_tx_fail)

1356-1361:如果cp_nb_tx_requested不等于0

1357:打印:“# 发送被拒绝(碰撞包):%.2f%% (req:%u, rej:%u)”(分别对应的是100.0 乘cp_nb_tx_rejected_collision_packet再除以cp_nb_tx_requested、cp_nb_tx_requested、cp_nb_tx_rejected_collision_packet)

lora_pkt_fwd.c代码解读相关推荐

  1. 200行代码解读TDEngine背后的定时器

    作者 | beyondma来源 | CSDN博客 导读:最近几周,本文作者几篇有关陶建辉老师最新的创业项目-TdEngine代码解读文章出人意料地引起了巨大的反响,原以为C语言已经是昨日黄花,不过从读 ...

  2. 装逼一步到位!GauGAN代码解读来了

    ↑↑↑关注后"星标"Datawhale 每日干货 & 每月组队学习,不错过 Datawhale干货 作者:游璐颖,福州大学,Datawhale成员 AI神笔马良 如何装逼一 ...

  3. Unet论文解读代码解读

    论文地址:http://www.arxiv.org/pdf/1505.04597.pdf 论文解读 网络 架构: a.U-net建立在FCN的网络架构上,作者修改并扩大了这个网络框架,使其能够使用很少 ...

  4. Lossless Codec---APE代码解读系列(二)

    APE file 一些概念 APE代码解读系列(一) APE代码解读系列(三) 1. 先要了解APE compression level APE主要有5level, 分别是: CompressionL ...

  5. RT-Thread 学习笔记(五)—— RTGUI代码解读

    ---恢复内容开始--- RT-Thread 版本:2.1.0 RTGUI相关代码解读,仅为自己学习记录,若有错误之处,请告知maoxudong0813@163.com,不胜感激! GUI流程: ma ...

  6. vins 解读_代码解读 | VINS 视觉前端

    AI 人工智能 代码解读 | VINS 视觉前端 本文作者是计算机视觉life公众号成员蔡量力,由于格式问题部分内容显示可能有问题,更好的阅读体验,请查看原文链接:代码解读 | VINS 视觉前端 v ...

  7. BERT:代码解读、实体关系抽取实战

    目录 前言 一.BERT的主要亮点 1. 双向Transformers 2.句子级别的应用 3.能够解决的任务 二.BERT代码解读 1. 数据预处理 1.1 InputExample类 1.2 In ...

  8. shfflenetv2代码解读

    shufflenetv2代码解读 目录 shufflenetv2代码解读 概述 shufflenetv2网络结构图 shufflenetv2架构参数 shufflenetv2代码细节分析 概述 shu ...

  9. GoogLeNet代码解读

    GoogLeNet代码解读 目录 GoogLeNet代码解读 概述 GooLeNet网络结构图 1)从输入到第一层inception 2)从第2层inception到第4层inception 3)从第 ...

  10. Inception代码解读

    Inception代码解读 目录 Inception代码解读 概述 Inception网络结构图 inception网络结构框架 inception代码细节分析 概述 inception相比起最开始兴 ...

最新文章

  1. 互联网协议 — IPv4 — 分片与重组
  2. opencv python全屏显示、置窗口大小和位置
  3. 《51单片机应用开发范例大全(第3版)》——第1章 单片机C语言开发基础
  4. 下拉多选择框 实现方式_物体检测之旅(三)|设计选择,经验教训和物体检测的趋势...
  5. C#刷剑指Offer | 从上到下打印二叉树
  6. jQuery跨域调用Web API
  7. FPGA开平方的实现
  8. Codeforces 319C DP 斜率优化
  9. pytorch按照索引取batch中的数
  10. cocos2d-x 中创建 CCSprite 精灵动画
  11. [深入浅出WP8.1(Runtime)]文本框(TextBox)
  12. JSJ——主数据类型和引用
  13. Gensim进阶教程
  14. idm chrome扩展被阻止解决办法
  15. steam授权_Epic喜+1:塔洛斯的法则|Steam上周销量榜出炉|格力折叠屏手机专利授权...
  16. 面经个人向(算法岗)
  17. R语言系列:rgl包安装出错的解决办法
  18. 一文贯穿“如何制定研究生规划、找实习、秋招”
  19. Pytorch练习--绘制Loss曲线
  20. 使用shareSDK调用QQ好友分享遇到的问题

热门文章

  1. 实战:第十五章:摸爬滚打这些年的心路历程
  2. linux wrf软件安装,wrf安装
  3. 孙中原:为什么墨家会产生科学思想?
  4. 打游戏买什么手机好?rog3性能强 网速稳!
  5. 异或为什么满足结合律,布尔代数与布尔环简介
  6. 后退N帧协议中发送窗口的尺寸大小
  7. Centos7上搭建迅雷远程下载服务器
  8. Maven(六)Maven传递性和依赖性
  9. 2021-2027中国光电红外传感器市场现状及未来发展趋势
  10. iOS之HomeKit