EZ-WifiBroadcast 在 OpenWrt 上的移植与修改 另一种数字图传思路
本帖最后由 libc0607 于 2019-7-13 16:20 编辑(最近半年除了交过几次板子作业之外主要在搞这个东西……断断续续……
EZ-WifiBroadcast 就是那个树莓派图传 在这价位来说效果还算不错 虽然早就知道有这么个东西了但是没时间搞……
主要是以自己的思路从头开始重新组合了这个项目 改掉了一些个人觉得不太舒服的部分 并且又把成本降了一些 (到大约1/3?没具体算过)
OpenWrt 固件源码在 https://github.com/libc0607/lede/tree/air注意分支 别的分支无关……
基于 Lean 的固件 修改部分在 mac80211 及 ath9k 关于解锁功率/频率 等 主要参考的还是原始项目的 kernel/ 下
OpenWrt 的软件包部分在 https://github.com/libc0607/yjsnpi-broadcast-openwrt
暂时包含 本体 ezwifibroadcast / LuCI 界面 luci-app-wbc / libiniparser 一个库
源码本体在 https://github.com/libc0607/YJSNPI-Broadcast 直接 Fork 自原项目
包含 ezwifibroadcast 软件包的本体源码 树莓派上运行的源码 一些库 以及一些配置文件
(起这个名字因为 想选一种动物->野兽->最厉害的野兽->它们的先辈 233)
上面的源码仅处于勉强能用的状态,暂时还没能也没敢上飞机……欢迎 Star
图片是挺长时间之前拍的了 主要作为了解 等上了飞机再更新图……
(图中的屏幕转接板在 https://lceda.cn/libc0607/raspi-lcd-usbdev)
原项目的结构大概是 树莓派作为世界的中心 完成视频编/解码及无线的监听/注入 几乎所有的逻辑写在 .profile 内
我的版本试图将传输部分与视频部分的耦合程度降低一些 树莓派仅编解码及 OSD 传输部分转移到 OpenWrt 上 并且可以通过网页配置这些东西
主要的进程都守护一下(procd 及 monit)
两部分中间通过网络传输 可以是通过 USB 接口的g_ether模式 也可以是普通的以太网接口
无线路由推荐用那种内置网卡是 ath9k 驱动且带 usb 的 内存越大越好 像 ar934x qca9531 之类的就都不错
一个原因是 ath9k 的网卡可以开 5MHz 频宽的模式 牺牲清晰度的情况下 接收灵敏度可以再提高 3~5 个 dBm
另外是这些网卡都可以在 2.3G 及 2.5G 等频率工作
另吐槽一下,基于树莓派搞 hdmi 视频输入总是绕不开东芝那个至少需要四层板的 bga 芯片……也不知道有没有国产可以代用……所以就不搞了……
会带来的好处比如 地面端不需要买派 2 以上 不用买贵得莫名其妙的二手网卡和显示屏。。。之类的
现在楼主用的是派 0 配 DPI 的 LCD 屏 屏本体不到 30
发射端使用 19.9 的乐视路由带功放 接收可以选择自带双极化定向天线和功放但只要 27 不包邮的百米生活……
然后借助 OpenWrt 可以非常非常方便地实现 4G 图传 插一块 Air720 开个国内阿里云跑 frp 也就一百多(
缺点大概是在飞机上绑一个路由器是不是搞错了什么(?)(其实有考虑过做专用的板……但总是有问题……
两张图大概解释一下结构区别
其他部分的改进大概需要讲很长时间的原项目结构(1.6RC6 版的),所以不会一下子就讲完……慢慢码好了 可能后续会更新一点原项目结构讲解
楼主之后因为上学的原因时间可能会更少,希望可以抛砖引玉让数字图传便宜一点点233
补充内容 (2019-7-16 11:31):
楼下正在努力更新 原项目&我的修改版 的 结构讲解&代码笔记
希望有人看到感兴趣了能一起搞搞((( 最近也在看ez的代码 准备用rust重构下 可惜c学的一塌糊涂 跟着楼主的思路应该没错 楼主QQ号多少?加个QQ互相探讨一下,最近也在开发数字图传 非常感兴趣,希望交流 libc0607 发表于 2019-8-1 14:20
咕了两个星期,更新一点
关于 packetinjection 与 radiotap header
C都不行看不懂了,糊了糊了 大佬,啥时候回来更新呀? 楼主方便➕个qq吗付费求教个问题363004911 libc0607 发表于 2020-8-27 11:44
。。搜不到这个群号。。
群主,你的 的东西调试的怎么样了 openoyp 发表于 2020-8-27 09:43
一直加不到QQ啊,加群吧942062027。可以一起探讨。
。。搜不到这个群号。。 开源值得学习! PfOpKB 发表于 2020-7-1 22:11
楼主能请教几个问题吗?
直接问 除了有点臭都挺好的
挺好的{:1_1:}{:1_1:}{:1_1:} 原版项目在国内也没见到什么公开的中文分析,但这事儿总得有人干。。楼主希望可以带动更多的人在此基础上改进。下文分析会基于 EZ-WifiBroadcast 的 1.6RC6 版。
0. 硬件
最常见的问题 “为什么是树莓派,而不是xx……?我看xx(水果)派便宜好多呢”
在 2019年7月 能搞定h265的派4出现之前:这个派是最容易搞定h.264视频的硬件编解码的:raspivid 输出视频流,hello_video.bin 解码,基本不需要修改。
至于深受只看价格的人的喜爱的sunxi(?)SoC 啥的,看看能不能非常方便地搞定硬件视频编解码先。。如果是自己做板的话,加上512M内存后成本还不一定比派0低。
如果不做实时编码的话不仅要占用掉很大的带宽(从而对信号质量要求高-距离短),搞不好比模拟还糊。
我的修改版也无法避免地使用了树莓派0 233 因为又嫌麻烦又嫌贵的话就找不到什么替代品了。。以后可能会挖坑搞搞海思比如3516啥的
网卡原项目主要推荐的是 AR9271,这个SoC连firmware都开源了(https://github.com/qca/open-ath9k-htc-firmware)。
原项目由于这个SoC默认的包注入速率不会按照radiotap头部来的问题也有修改这个固件(见 https://bitbucket.org/befi/wifibroadcast/src/62d3d35a2374d2b02d0c77a5b6988f2e96fd5913/patches/AR9271/firmware/)。
Ralink的卡主要是RT5572/RT3572,螃蟹的RTL8812*也在支持列表里了。
用这些芯片且带功放的卡在国内平台貌似都比较难淘到合适的,都被搞破解的炒到高价(?)。。不过前几天看到有一批大功率的8812不知道咋样(?)
另一个思路就是买低功率的卡外接功放。接线上无非就是电源线够粗,数据线拧一块儿,供电要足之类的话。。
使用 qca 的路由器芯片就可以避开无线芯片固件问题,实际注入时会按照radiotap中填写的值发送。
1. 软件
这个坑开得有点大不知道能解释成什么样……我尽力吧
https://github.com/rodizio1/EZ-WifiBroadcast
在Github的1.6RC6分支下可以看到各个程序,它们看似独立但却以十分复杂的方式耦合在一起(狗头保命)。
这个分支是镜像里的主要文件,不包括各种运行环境(下面会提到镜像构建和运行环境)。。
/boot 目录下就是sd卡能在windows下看到的那个分区,里面主要是各种配置文件和内核。
/root 是root的主目录,主程序啥的都在这里。
/boot下与官方镜像相比主要新增/变动的东西:
./osdfonts/ 给OSD用的字体,显示用的各种符号都在这
./apconfig.txt 通过Wi-Fi共享视频的设置
./cmdline.txt 改动关于默认console,root分区不用uuid指定,tf卡只读,关掉启动输出,加快启动,以及dwc驱动的一些设置 等等等等
./config.txt 各种超频以提升性能,加快启动,以及一些接口设定
./cmavnode.conf 给cmavnode用的配置文件,这个程序本体是个用c艹写的mavlink转发器
./joyconfig.txt 摇杆设定,也是个c的头文件,不是重点
./osdconfig.txt osd设定,本质是一个c的头文件,改完之后每次启动都会跟osd的源码一起重新编译
./wifibroadcast-1.txt 大部分与图传这件事本身有关的设置,是个shell脚本,会在.profile里被source进去
./kernel.img 派01的内核 一堆魔改
./kernel7.img 派23的内核 一堆魔改 // 内核魔改看develop分支/kernel下的patch和两个config
/root下:
./FlirScripts 及 ./flir8p1-gpl 关于 FlirOne,貌似是热成像?我也没玩过,就先略过;
./cmavnode 与 mavlink 有关,见 https://github.com/MonashUAS/cmavnode
./gnuplot 画统计图用的
./mavlink-router 与 mavlink 有关,见 https://github.com/intel/mavlink-router
./wifibroadcast 大部分主程序的源码/可执行文件,下面会详细提到
./wifibroadcast_misc 杂七杂八的功能,比如拨码开关选择配置文件、步进电机控制等
./wifibroadcast_osd OSD的源码,每次启动都会将osd程序重新编译到/tmp目录下(因为/root只读了)
./wifibroadcast_rc 与RC相关,这部分我也没咋研究。。主要是那个rctx
./wifibroadcast_status wbc_status,在屏幕上显示一行字的那个功能;比如插上U盘后的那个可以移除的提示就是它干的
./各种profile实际上被当作启动脚本
原版的组装和使用方法实在是懒得翻译,推荐去原项目wiki查看。https://github.com/rodizio1/EZ-WifiBroadcast/wiki 啥都有。
这里主要解释他们不是写给人看的部分
在原版项目中,/root/.profile 是启动并root登陆后实际运行的文件,这个项目的绝大多数主要功能都写在这里。在每个root登陆的终端(其实主要是tty1~tty12)都会运行一遍这个脚本,里面为每个tty分配了不同的任务,同时运行。
关于.profile启动的大致几个关键点流程解释:
查找 ”# Start of script“ 找到文件的实际开头(1.6RC6 镜像中的1820行,下同);
首先判断自己是第几个tty(Ln1827)及是否接了摄像头(Ln1833)——接了会被认为是天上的Pi (里面称之为Air Pi),没接就是 Ground Pi;
如果是RX,下一步根据屏幕分辨率设置终端字体(Ln1848);
Ln1867开始加载配置文件,配置文件加载过程如下:启动时会运行 /root/wifibroadcast_misc/wbcconfig.sh,里面调用 /root/wifibroadcast_misc/gpio-config.py 根据GPIO状态判断该加载/boot下的哪个wifibroadcast-*.txt,然后使用dos2unix转换对应的文件到/tmp/settings.sh 并source进来
Ln1885开始把设定的DATARATE转换成视频、遥测、上行的实际发射速率
Ln1923根据设定的fps选择实际显示程序,他们有几个不同修改的hello_video.bin,程序的功能就是从标准输入硬解码视频显示
Ln1980开始进入主switch-case,不同的tty执行不同的任务。
本帖最后由 libc0607 于 2019-7-16 10:48 编辑
进入主Case之后每个tty会负责不同的任务,故下文将按照每个不同的任务简单描述一下。。其实是自己看代码时的笔记为方便这里也传一份profile文件
首先是基础功能,视频传输,1981行
Air Pi 上执行 tx_function
首先使用sharedmem_init_tx初始化共享内存,共享内存的结构见/root/wifibroadcast/lib.h,主要是一些如包计数、网卡数之类的信息,会被tx_rawsock等程序使用;
下面初始化并检查网卡(561~),设定帧类型(598~)、传输速率;扫描周围的wifi来决定是否开启cts(自动模式,618~)
检查是否过热或供电不足(648~及699~),这里如果有问题会强制设定为1000kbit的视频码率且不会运行tx_measure;结果记录到/tmp/undervolt下,这个主要是给osd程序读取;视频码率记录到/tmp/bitrate_kbit 及 /tmp/bitrate_measured_kbit 中,如名字所示
(734~) 检查USB是否有问题,防止网卡挂掉而没发现
765 启动rssitx,rssitx这个程序主要功能是将共享内存中的各网卡信息、视频速率、cts、低压状态等各种信息打包注入发送
769 启动raspivid,并管道定向到tx_rawsock;raspivid见https://www.raspberrypi.org/documentation/usage/camera/raspicam/raspivid.md ,输出压缩好的视频流到stdout;tx_rawsock从stdin读取数据并注入发送
如果一切正常,程序将会在这里一直运行;如异常退出,774~为错误处理,检查网卡状态并调用collect_errorlog收集错误信息,错误信息会存到/boot下
Ground Pi 上执行rx_function(795~)
首先初始化共享内存 sharedmem_init_rx
798~ 为 cmavnode 与 ser2net 创建虚拟串口并配置
809~ 催促用户拔掉U盘 835~初始化网卡
840~ 这里提到了四个命名管道(fifo),这个是在镜像构建阶段已经创建好的,用来将视频流一分四
845~ 关于视频保存的问题,如果保存到tf卡上就试图挂载mmcblk0p3到/video_tmp并把视频的保存位置设置到这里
879~ 使用airodump扫描并记录周围的wifi,结果截图保存
934~ 检查是否过热或供电不足
然后就是获取视频流并显示/转发/ ……等操作(管道要先建立读,不然就broken pipe。。。)
958 创建 从videofifo1中读取视频并送到显示程序(hello_video.bin.*)的管道 这里的hello_video.bin是有修改过的,具体见 https://github.com/RespawnDespair/wifibroadcast-hello_video :
一个是手动指定了目标帧数,让解码程序收到完整的一帧之后可以尽快解码显示;另外,原版的第一个参数是文件名,会从文件中读取数据播放;修改版变成了直接从stdin读取视频流。(我的版本也有修改过,只不过参数变成了一个配置文件,读其中的配置然后监听udp端口获取数据……见https://github.com/libc0607/YJSNPI-Broadcast/blob/op/root/wifibroadcast_osd/video.240-udp.c
959 创建 从videofifo3中读取视频并保存到文件 的管道
videofifo2的读取还不在这里,是关于通过网络推到第二屏的,暂时不提
961~ 如果是relay中继模式的话 videofifo4用于将视频再通过tx_rawsock发送出去
969 非常长非常长的一行 很想吐槽…… rx是无线监听接收数据并输出到stdout的程序,然后他们在这个rx后面用了五个*tee将视频分为了四份存入四个videofifo里………………
971~ 下面就是错误处理了,基本同上
懒人一键版:
视频发射端经过各种初始化后,启动raspivid读视频流并传给tx_rawsock发送
接收端同样初始化,然后启动rx接收空中的无线数据并送给hello_video.bin解码显示
rssitx与rssirx用于传输各网卡信息、视频速率、cts、低压状态等
这是啥
本帖最后由 libc0607 于 2019-7-16 11:51 编辑
然后另一个重点是数据回传,Telemetry,这部分功能与osd密不可分,所以放在一块儿做笔记
profile的第1992行开始, tty2:地面运行 osdrx_function,天上运行 osdtx_function
osdrx_function在999行
先提一下两个版本的区别
osd接收端启动的过程有些麻烦,原项目配置osd的方式为修改一个/boot目录下的头文件(https://github.com/rodizio1/EZ-WifiBroadcast/blob/1.6RC6/boot/osdconfig.txt)并在每次启动时重新编译到/tmp下。
(见 https://github.com/rodizio1/EZ-WifiBroadcast/wiki/Software-~-Advanced-~-Telemetry-&-OSD )
我的版本将这部分改为文件预先编译好,通过参数载入配置文件的方式来指定osd位置(https://github.com/libc0607/YJSNPI-Broadcast/blob/op/boot/osdconfig.ini)
另一个改动是数据的流向,原项目为 rx_rc_telemetry_buf 或串口输入 -> /root/telemetryfifo* -> /tmp/osd
由于我的版本中需要和OpenWrt协同工作,故改为监听udp端口获取数传数据
1002 先将/boot/osdconfig.txt 转为unix格式
1004~ 重新编译osd程序
1018~ 等待hello_video运行后再启动osd
1028~ 选择是从空中接收数传数据还是从串口直接输入,然后据此设定TELEMETRY_RX_CMD这个变量
空中接收则使用rx_rc_telemetry_buf;如果是串口输入就先用stty设置串口属性,并把该变量设为 cat 串口
数传部分的数据流向与视频基本相同,涉及到将数据分到6个fifo中,这部分他有写一些注释就不翻译了:
# telemetryfifo1: local display, osd
# telemetryfifo2: secondary display, hotspot/usb-tethering
# telemetryfifo3: recording
# telemetryfifo4: wbc relay
# telemetryfifo5: mavproxy downlink
# telemetryfifo6: serial downlink
fifo1: 实际由/tmp/osd作为读端,见https://github.com/rodizio1/EZ-WifiBroadcast/blob/1.6RC6/root/wifibroadcast_osd/main.c#L97
fifo2:给第二屏用,在tether_check_function与hotspot_check_function中使用socat为读端通过udp发送出去
fifo3:作为日志记录,在1052行,读取并保存到 /wbc_tmp/telemetrydowntmp.raw
fifo4:中继模式下使用,1057行通过cat读取并再次使用tx_telemetry发送
fifo5:mavproxy downlink,在tether_check_function与hotspot_check_function中又将数据定向到了/dev/pts/0(在rx_function中建立的虚拟终端,804~)
fifo6:1042行读取,数据发到TELEMETRY_OUTPUT_SERIALPORT_GROUND这个串口输出,是在/boot/wifibroadcast-*.txt中的设置
然后比上面视频一分四更骚的操作来了,1065 1067 和 1070,用了一大堆ftee把回传数据一分六存到fifo中…………………………
(不知道说啥好反正ftee也是抄来的(https://github.com/racic/ftee)为啥不改改源码直接一分六((
1072~ 错误处理,收集信息,退出
osdtx_function 运行在天上 这个超短 也没什么复杂逻辑
主要是1087~等待网卡配置完毕
1103 从串口读取,数据发给tx_telemetry发送,下面都是错误处理,没了
懒人一键版:
天上发射端 从串口读数据 送到tx_telemetry 发送
地上接收端 rx_rc_telemetry_buf 接收后一分六 用于osd 第二屏 记录 中继 等等用途
原版的osd是每次启动用/boot/osdconfig.txt新鲜编译的
用树莓派的话功耗有点大,再说树莓派自带的WiFi功率太小了,估计二三十米就断了。 宜兴模友 发表于 2019-7-16 12:28
用树莓派的话功耗有点大,再说树莓派自带的WiFi功率太小了,估计二三十米就断了。 ...
我自己用的派0的发热不算大,我在测试的时候地面端cpu占用也只有30%上下,没加散热片也不会很热。原版用的派23就明显热很多了。
自带的wifi确实鸡肋,所以原版和我的都没用它
咕了两个星期,更新一点
关于 packetinjection 与 radiotap header
由于这玩意儿实在搜不到什么连鸽子都能看懂的中文版的解释所以按照自己的理解随便写点 可能有误请指出
当把支持 monitor mode 下称监听模式 的网卡设置成监听模式后,它就可以监听在你设定的频率上能被这网卡认出来的包
有很多网卡支持这个模式 如原项目的ar9271/rtl8812之类也有一些网卡不支持如 ath6k ath10k那些
这些不支持常见原因是因为没有支持这个功能的firmware
firmware就是字面意思 可以当作是初始化网卡时主机传给网卡内部cpu运行的程序
有的firmware开源了 如ar9271那个 上面有提到 更多的没有开源
firmware的有些行为会无法预测 如ar9271在注入的时候不管你填什么速率 g还是n 都会按1mbps发送 因为顺序写死在firmware里了
还有rtl8192啥的忘了一个 发data包全部默认mcs7 发rts包全部默认1mbps = =...还改不了
所以给图传选个能用的网卡比较难= =
监听模式下同时也可以按照你的要求绕过协议栈发送一些自定义的包 叫做 packet injection 注入
对于这个项目而言 好处是绕过了协议栈的确认重传等等一大堆复杂的问题 底层可以由自己控制 可以降低延迟 等等
在用户态的应用程序告诉驱动该怎么发送这个包的时候 除了包的本体 还需要带上一些额外的信息 常见的如 速率 频率guard interval 等等
这些信息在注入时需要你自己确定好 按照格式写在头部传递给内核 然后驱动会读取这些内容并按照你的设定发送
同样在收包的时候 驱动会按照相同的格式填好信息并返回给用户态的应用程序
这套描述附加信息并写在头部的方式叫 radiotap
下面会简单描述一下这东西在ez-wifibroadcast项目里怎么玩的
先以我用wireshark随便抓到的一个包(接收)为例解释一下它的包头结构
0x00, // Header revision
0x00, // padding (for (uint16)length )
0x24, 0x00, // Length, (le)uint16
0x2f, 0x40, 0x00, 0xa0, // Present flags word (bit31=1, has next present flag)
0x20, 0x08, 0x00, 0x00, // Second Present flags word
0x00, 0x00, 0x00, 0x00, // padding (timestamp uint64)
0xf1, 0x18, 0xc2, 0x3f, 0x00, 0x00, 0x00, 0x00,
// Timestamp, uint64, 1069684977
0x10, // flags (See enum ieee80211_radiotap_flags)
0x30, // Data rate (500kHz unit)
0x85, 0x09, // 0x0985, 2437, Channel frequency MHz
0xc0, 0x00, // Channel flags (See enum ieee80211_radiotap_channel_flags)
0xcb, // Signal, int8, -53, dBm
0x00, // Antenna 0
0x00, 0x00, // RX Flags (See enum ieee80211_radiotap_rx_flags)
// Second Present flags word
0xcb, // Signal, int8, -53, dBm
0x00, 0x00, // Antenna 1
结构都写了注释 应该很好看懂
present flag 用每一bit指示后面是否有出现相应的数据项
可以存在多个 present flag 若某一个present flag 转大端后的最高位为1 则指示紧接着的四个字节是下一个present flag
这段数据中的第一个 present flag如下 (仅写出了为1的位)
1010 0000 0000 0000 0100 0000 0010 1111
.... .... .... .... .... .... .... ...1 = TSFT: Present
.... .... .... .... .... .... .... ..1. = Flags: Present
.... .... .... .... .... .... .... .1.. = Rate: Present
.... .... .... .... .... .... .... 1... = Channel: Present
.... .... .... .... .... .... ..1. .... = dBm Antenna Signal: Present
.... .... .... .... .1.. .... .... .... = RX flags: Present
..1. .... .... .... .... .... .... .... = Radiotap NS next: True
1... .... .... .... .... .... .... .... = Ext: Present
最高位指示有下一个present flag,如下:
0000 0000 0000 0000 0000 1000 0010 0000
.... .... .... .... .... .... ..1. .... = dBm Antenna Signal: Present
.... .... .... .... .... 1... .... .... = Antenna: Present
关于padding在radiotap的规范里有提到 由于每项数据的大小都是不同的 所以在确认是否需要padding 的时候 看下一个数据的大小 把下一个数据对齐到该大小的整数倍 与前一项中间的空位就都填0
比如第一个数据uint8在0x0,第二个放uint32的话就要在前面补3个0x00,这样这个uint32就是对齐的
每个数据占用的大小见Linux内核源码的 include/net/ieee80211_radiotap.h数据排列顺序按照 present flags 字段中先转为大端序后 从低位到高位的顺序
数据本体为小端序
所以按照上面两个present flag 的解析,后面的数据顺序应该如下:
TSFT +Flags +Rate +Channel +dBm Antenna Signal +RX flags +Radiotap NS next +dBm Antenna Signal(2) +Antenna(2) (并按需padding)
然后对着注释就能看明白了。。
至于发送的时候,摘取原项目tx_measure中一点 见 https://github.com/rodizio1/EZ-WifiBroadcast/blob/1.6RC6/root/wifibroadcast/tx_measure.c#L160
packet_header_init() 这个函数负责填写发送缓冲区的radiotap头部与ieee80211头部
具体做的事是 把 u8aRadiotapHeader 以及 u8aIeeeHeader_rts, u8aIeeeHeader_data_short, u8aIeeeHeader_data中(看你的配置)的一个复制到缓冲区的开头
并按照配置填写rate与port
摘录如下
static u8 u8aRadiotapHeader[] = { 0x00, 0x00, // <-- radiotap version 0x0c, 0x00, // <- radiotap header length 0x04, 0x80, 0x00, 0x00, // <-- radiotap present flags 0x00, // datarate (will be overwritten later in packet_header_init) 0x00, 0x00, 0x00};
结构完全可以用上面提到的解释 就不写了
值得注意的是这个在使用ar9271(ath9k_htc)网卡时填写rate也没用,因为他们是靠搞了一堆不同的firmware切换来达成发送速率切换的
另外 上面提到的rate字段仅对802.11abg起作用
如果想要通过radiotap指定发送802.11n或是802.11ac的包 甚至开启ldpc抠2个dbm出来的话
需要网卡驱动支持按照radiotap发包(atk9k就没毛病。。)ldpc需要网卡硬件支持
并在present flag中 设定 mcs 或 vht 的位 并在对应位置增加数据
具体可以见我写的 https://github.com/libc0607/YJSNPI-Broadcast/blob/op/root/wifibroadcast/tx_test.c 是一个可以拿来发测试包的小程序 写了注释
参考资料
https://github.com/torvalds/linux/blob/master/include/net/ieee80211_radiotap.h
https://warmcat.com/git/packetspammer/tree/packetspammer.c
https://github.com/torvalds/linux/blob/master/Documentation/networking/radiotap-headers.txt
https://github.com/torvalds/linux/tree/master/Documentation/networking/mac80211-injection.txt
本帖最后由 libc0607 于 2019-8-2 12:31 编辑
共享内存笔记
wifibroadcast各进程通过共享内存的方式传递一些信息
共享内存结构体在 lib.h 中定义 https://github.com/rodizio1/EZ-WifiBroadcast/blob/1.6RC6/root/wifibroadcast/lib.h 变量名起得非常容易理解 就不解释了
命令行参数中的 port 被原项目定义为 ieee 802.11 header 中的一个 byte 并以此区分过滤不同作用的包
具体位置可以在项目中搜索 u8aIeeeHeader_data, u8aIeeeHeader_rts, u8aIeeeHeader_data_short 定义,内含注释
虽然看上去可以通过命令行自由指定(还像这样用括号注明了0-255),但由于与共享内存的名字相关,所以又不能随便指定
port的分配:
Air Pi <--> Ground Pi
-------------------------------------
0:tx_rawsock --> rx (Video)
1:tx_telemetry --> rx_rc_telemetry_buf (telemetry)
3:rx_rc_telemetry<--tx_telemetry (Uplink)
4: mspdownlink 没用过 不讨论
在所有的其他程序运行前 都要先用sharedmem_init_*x 初始化
air pi 包含
/wifibroadcast_tx_status_0
类型:wifibroadcast_tx_status_t
写端:tx_rawsock
读端:rssitx
内容:视频发射统计等
/wifibroadcast_rx_status_0
类型:wifibroadcast_rx_status_t
写端:rx (中继模式下)
读端:tx_rawsock, tx_telemetry
内容:视频接收统计
/wifibroadcast_rx_status_3
类型:wifibroadcast_rx_status_t
写端:rx_rc_telemetry
读端:rssitx
内容:uplink接收统计
/wifibroadcast_rx_status_rc
类型:wifibroadcast_rx_status_t_rc
写端:rx_rc_telemetry
读端:rssitx
内容:rc接收统计
ground pi 包含
/wifibroadcast_rx_status_0
类型:wifibroadcast_rx_status_t
写端:rx
读端:osd tx_telemetry check_alive rssi_forward
内容:视频接收统计
/wifibroadcast_rx_status_1
类型:wifibroadcast_rx_status_t
写端:rx_rc_telemetry_buf
读端:osd rssi_forward
内容:telemetry接收统计
/wifibroadcast_rx_status_rc
类型:wifibroadcast_rx_status_t_rc
写端:rssirx
读端:osd rssi_forward
内容:Air Pi 上的rc接收统计
/wifibroadcast_rx_status_uplink
类型:wifibroadcast_rx_status_t
写端:rssirx
读端:osd
内容:Air Pi 上的uplink接收统计
(写到这里发现我的rssi_forward*没转发这个。。)
/wifibroadcast_rx_status_sysair
类型:wifibroadcast_rx_status_t_sysair
写端:rssirx
读端:osd rssi_forward
内容:Air Pi 的系统状态
另外除了上面列出的,还有tx_measure.c 但只是用来测试,不是重点
rx_status.c 是一个读取共享内存的小工具
看到这就很清楚了,rssitx/rssirx这一对主要是用于转发 air pi 中的共享内存到 ground pi 的
rssitx 实际发送的内容可以在 https://github.com/rodizio1/EZ-WifiBroadcast/blob/1.6RC6/root/wifibroadcast/rssitx.c#L74 看到
摘要如下:
int8_t signal;
uint32_t lostpackets;
int8_t signal_rc;
uint32_t lostpackets_rc;
uint8_t cpuload;
uint8_t temp;
uint32_t injected_block_cnt;
uint32_t skipped_fec_cnt;
uint32_t injection_fail_cnt;
long long injection_time_block;
uint16_t bitrate_kbit;
uint16_t bitrate_measured_kbit;
uint8_t cts;
uint8_t undervolt;
本帖最后由 libc0607 于 2019-8-4 23:04 编辑
osd笔记
原项目代码在 https://github.com/rodizio1/EZ-WifiBroadcast/tree/1.6RC6/root/wifibroadcast_osd
osd的原项目wiki在 https://github.com/rodizio1/EZ-WifiBroadcast/wiki/Software-~-Advanced-~-Telemetry-&-OSD
这个项目的osd是获取到原始telemetry数据后在本地叠加的
并且因为telemetry数据量小 甚至可以做到在视频刚刚没信号的时候osd还能工作
主要用到(也是编译中主要的坑)是一个叫openvg的画图用的库
原项目里用的是一个fork出来的版本 见https://github.com/SamuelBrucksch/openvg
工作流程
首先 启动的时候 按照上文所说 会在profile中先编译再启动它
编译的时候引用osdconfig.h 这个文件是在电脑上设置时的 boot盘中的osdconfig.txt
从main.c开始看 为简洁将不会提到使用timestamp进行fps等性能统计的相关代码
main.c中,首先初始化了共享内存和render
然后打开 /root/telemetryfifo1 和 /tmp/undervolt 两个文件
上文分析profile时提到过 /tmp/undervolt 是地面端的低供电检测
/root/telemetryfifo1是一个命名管道
rx_rc_telemetry_buf接收到的telemetry消息会送到这个管道一份 osd即为读端
L140进入主循环
使用select的方式循环读取telemetry的命名管道至buf中 超时时间设置为50ms
当成功读到数据后 L158~L166 根据在osdconfig.txt中设置的telemetry类型
送到不同的处理函数 将信息处理好放在td这个telemetry_data_t结构体中
主要如下 (先略过 下文再说)
frsky_parse_buffer(&fs, &td, buf, n);
do_render = ltm_read(&td, buf, n);
do_render = mavlink_read(&td, buf, n);
smartport_read(&td, buf, n);
往下 L176 render()执行渲染
触发渲染的条件是 有需要尽快更新的数据(do_render==1,这个变量如上文,是由处理telemetry数据的函数返回的)
或是3个50ms内没有更新(也就是osd最差情况下的刷新率是150ms/帧)
同时 每隔约1000ms会触发一次地面派的温度与cpu占用更新(L188~L202)
主循环到此为止
上面的四个将telemetry数据处理到telemetry_data_t结构体中的函数分别位于frsky.c ltm.c mavlink.c smartport.c
这四个.c里面基本都是关于协议实现的东西 只要解析正确即可 无需改动
telemetry_data_t 的定义与初始化见telemetry.h与telemetry.c 也无需大改
吃瓜群众最关心的osd界面渲染在render.c中
render_init() 从/boot/osdfonts/ 中按照用户设定加载字体
顶层函数为 void render(telemetry_data_t *td, uint8_t cpuload_gnd, uint8_t temp_gnd, uint8_t undervolt, int osdfps)
该函数的主要功能即为根据各种用户设定决定该用什么颜色显示什么信息在哪
(一片#ifdef的海洋)
(我的版本改成ini的if判断了(
render_init调用的下一层函数为 draw_* 参数主要包含要显示的信息 显示位置及比例
draw_*层会调用TextEnd TextWidth 等实际完成绘制的函数 Fill Stroke 设置文字颜色与边缘颜色 等等
另外 关于osd实际从共享内存中读取的内容 我整理了一下列了出来
/wifibroadcast_rx_status_0
rx_status->adapter.current_signal_dbm
rx_status->adapter.received_packet_cnt
rx_status->adapter.signal_good
rx_status->adapter.type
rx_status->adapter.wrong_crc_cnt
rx_status->damaged_block_cnt
rx_status->kbitrate
rx_status->lost_packet_cnt
rx_status->lost_per_block_cnt
rx_status->received_block_cnt
rx_status->received_packet_cnt
rx_status->tx_restart_cnt
rx_status->wifi_adapter_cnt
/wifibroadcast_rx_status_rc
rx_status_rc->adapter.current_signal_dbm
rx_status_rc->lost_packet_cnt
/wifibroadcast_rx_status_sysair
rx_status_sysair->bitrate_kbit
rx_status_sysair->bitrate_measured_kbit
rx_status_sysair->cpuload
rx_status_sysair->cts
rx_status_sysair->injection_fail_cnt
rx_status_sysair->injection_time_block
rx_status_sysair->skipped_fec_cn
rx_status_sysair->temp
rx_status_sysair->undervolt
/wifibroadcast_rx_status_uplink
rx_status_uplink->adapter.current_signal_dbm
rx_status_uplink->lost_packet_cnt
总结一下
如果想汉化一下加入中文提示?主要改动draw_*层函数,画中文上去即可
如果添加一个新的功能显示?首先解决数据来源,然后新建draw_*,在render()中调用即可
如果加入osd调参?坑太大了我不做。。。(狗头
关于集成度高一点的天空端
核心用了windora bit 3.1 可以外接pcie网卡 我图里挂了个ar9287因为便宜不心疼
屏幕板是dpi模式 二手裸屏20多能收到 因为一开始就定位为“不需要抱着个显示器出去飞”这种所以也没追求特别高清 就只800x480
天空端板子在 https://lceda.cn/libc0607/pi0-widora-video
屏幕板在 https://lceda.cn/libc0607/raspi-lcd-usbdev
关于最近在搞的802.11n
原项目的包注入使用的是legacy速率,比如1 2 5.5 11 这些802.11b ; 6 12 18 24 36 48 这些802.11g
如上文所说 如果使用MCS速率发送,需要做一些改动
除了速率天花板比较高之外就是ldpc和stbc加成了
改了好几天的代码最后发现是我自己把长度写错了。。。。
最后结果如下,换了个ar9382网卡。
原项目的通过内核模块参数设置发射功率的方式貌似他只写了ar9002的,常见的ar9271/ar9287之类的都算它;
9382这个卡算是ar9003,于是从别处抄来另一个patch解锁一下功率,见 https://github.com/libc0607/lede/commit/18343fa657d441d1b6242e3201231729d41612fd
注意 不是功率越大越好,ofdm这玩意儿默认都会自动回退 不然等着evm上天
仅建议在低速率下开大功率
用ar93xx/94xx/95xx的带功放那种全高卡 灵敏度就可以正面肛炒上天的各种8812au了 并且一张也就几十还带双频((
下图里是802.11n模式下的测试结果(没开fec,MCS15,最大包长,rts),这大概是mt7688+ar9382的天花板顶部了。。(是cpu和内存瓶颈了
5Mhz频宽下跑了20.1Mbps
20Mhz频宽下跑出了83576705 ~=79.7Mbps的发射速率
不过实际使用肯定不行。。
下图是实用一点的设置,mcs1, 8/4/2048,大概分别有8M和2.3M的可用带宽,传800x480的h264视频非常够了
补充内容 (2020-2-25 18:04):
呃 发射功率设定这个后面有改动
见 https://github.com/libc0607/lede/commit/7c30b49094acdb4c90e3eea52c7b7f965e5930e0 另外,它的LuCI界面功能越来越多导致越做越丑了(
关于发射功率的更新
改了改上面提到的patch,把ath9k下几种网卡的发射功率设定全部统一移到了debugfs下
上一个patch会导致某些情况下锁定在30dBm 这个功率下我手里几乎所有网卡都不能正常通信了 并且可以煎鸡蛋
这个就干脆绕过这些层 直接喂到驱动里
https://github.com/libc0607/lede/commit/2fcc0a79a65730b54e6c6d37957ef7b5c2a23078
https://github.com/libc0607/yjsnpi-broadcast-openwrt/commit/57d32929c63057bca6fb55c38503f3f8c3ba6d43
现在假如将phy1网卡设置为24dBm 则只需要
echo 48 >/sys/kernel/debug/ieee80211/phy1/ath9k/txpower_man
然后重启一下无线即可
或者在LuCI里设置 但我还没测试
补充内容 (2020-2-25 18:06):
这层的内容有变化
见 https://github.com/libc0607/lede/commit/7c30b49094acdb4c90e3eea52c7b7f965e5930e0 本帖最后由 libc0607 于 2019-8-18 22:14 编辑
关于正在填坑的RC进度与思路
RC挖下的第一个坑是用这一套系统进行sbus中继(类似于高频头用法) 因为sbus足够简单便宜啥都支持 和本项目初衷类似……
关于sbus网上资料非常非常多,重点一个是帧格式,另一个是物理层的反相与反人类的100khz波特率 随便贴一个觉得挺详细的:https://github.com/uzh-rpg/rpg_quadrotor_control/wiki/SBUS-Protocol 和 http://forum.autoquad.org/download/file.php?id=2319
路由器的芯片自身通常只有很少的串口,并且其中一个要作为串口控制台使用,像7688这种比较多的也只有额外的两个可用
所以基本上只能考虑通过usb hub 外接 usb 转 uart 模块或芯片来实现sbus中继
但是首先需要让usb转uart芯片支持 100k 比特率……这是第一个大坑 楼主在掉进去的时候一无所知
首先需要用户层软件支持:楼主找到了 https://github.com/cbrake/linux-serial-test/ 这样一个项目,并抄袭其中的代码杜撰出了一个tx_rc_sbus程序
它通过设置分频系数使得串口模块可以正确地以100k速度发送接收
然后需要Linux内核驱动支持:这里只讨论主线驱动
常见的usb转串口模块如ch340*/ch330*/ch341* (在内核里都使用kmod-ch341这个驱动)驱动并不支持设置分频 (*不过在windows下貌似可以
cp210x也不支持 唯独ftdi的芯片支持……但是这玩意儿太贵了
另外mt76x8的内置串口由于算是 16550-compatible,所以看上去可以——实际上……?再测试一下好了……
但是不管用什么 外面都是要接反相器的……。。可以用三极管+电阻 也可以直接74非门 比如我之前的富斯二代接收用的SN74LVC1G04DCKR
于是就有了楼主还没写完没测试的tx_rc_sbus和rx_rc_sbus,在项目的root/wifibroadcast目录下
发包部分与上面几层楼描述的一模一样,整个程序就是一个循环,读取串口-无线发射-.........
(吐槽一下 写了这么多楼 除了我之外的回复都没几个……
补充内容 (2019-8-22 16:49):
先前的测试代码有点问题,ch340可以找到方法自定义波特率
不能修改只能在这补充了 代码后续会推到 Github
太好了不用改硬件了((((
补充内容 (2019-9-20 17:40):
关于CH340*的正确食用方式 继续往下看( 大部分人也是不明觉厉吧,也不知道怎么帮忙{:1_2:}只想知道什么时候会放出BETA版。