OpenIPC开源FPV之Adaptive-Link天空端代码解析
1. 源由
在《OpenIPC开源FPV之Adaptive-Link工程解析》中,已经有了整个工程的大体概念,接下来再对代码进行逐步分析。
首先,对天空端的代码进行分析:ALink42n.c
2. 框架代码
ALink42n.c
相对来说,代码量最少,也是最为基本的一份代码。
目前,尚不太清楚具体n/p/q之间的差异,逻辑上看应该是关于切换配置profile
的条件计算方式不太一样,对于稳定性、可靠性方面应该有所差异。
注:感兴趣的朋友,可以跟下帖子,不过随着代码的深入了解,以及性能测试数据,也能慢慢明晰之间的差异。
2.1 消息机制
- 加载配置 - “/etc/alink.conf”
- 加载Profile - “/etc/txprofiles.conf”
- majestic:80
- wfb-cli:8000
- Terminal:bash
- 绑定默认IP - 10.5.0.10:9999
- 接受两种UDP报文:special报文和普通报文
main
├──> load_config(CONFIG_FILE); // "/etc/alink.conf"
├──> load_profiles(PROFILE_FILE); // "/etc/txprofiles.conf"
├──> bind DEFAULT_IP(10.5.0.10) DEFAULT_PORT(9999)
├──>loop recvfrom
│ ├──> <special:> special_command_message(message);
│ └──> process_message(message);
└──> close(sockfd);
2.2 超时机制
当长时间未收到报文,则进行最大功率设置。
count_messages (void *arg)
│
├──> while (1) // Infinite loop
│ ├──> usleep(fallback_ms * 1000); // Sleep for 'fallback_ms' milliseconds
│ │
│ ├──> pthread_mutex_lock(&count_mutex); // Lock the count_mutex to safely access shared data
│ │ ├── local_count = message_count; // Store current message count into local_count
│ │ ├── message_count = 0; // Reset the message count to 0
│ │ └── pthread_mutex_unlock(&count_mutex); // Unlock the count_mutex after accessing shared data
│ │
│ ├──> pthread_mutex_lock(&pause_mutex); // Lock the pause_mutex to safely check and change 'paused'
│ │ ├──> if (local_count == 0 && !paused) { // If no messages received and not paused:
│ │ │ ├──> printf("No messages received in %dms, sending 999\n", fallback_ms);
│ │ │ └──> start_selection(999, 1000); // Call start_selection with parameters 999 and 1000
│ │ ├──> else { // If messages are received or paused:
│ │ │ ├──> if (verbose_mode) { // If verbose mode is enabled:
│ │ │ │ └──> printf("Messages per %dms: %d\n", fallback_ms, local_count);
│ │ │ └──> } // End of verbose_mode check
│ │ └──> } // End of else block
│ └──> pthread_mutex_unlock(&pause_mutex); // Unlock the pause_mutex after checking 'paused' status
│
└──> return NULL; // Return NULL from the function (indicates thread termination)
3. 报文处理
+--------------+---------------+-------------+
| | special? (8B) | Msg content |
| Msg len (4B) |---------------+-------------|
| | RF Signal Estimated Values |
+--------------+---------------+-------------+
3.1 special报文
- 报文格式:
+--------------+---------------+-------------+
| Msg len (4B) | special? (8B) | Msg content |
+--------------+---------------+-------------+
- 代码流程:
处理pause_adaptive
/resume_adaptive
/request_keyframe
命令
special_command_message
├──> "pause_adaptive"
│ └──> paused = true
├──> "resume_adaptive"
│ └──> paused = false
├──> "drop_gop"
│ └──> // 已经注释掉,代码暂时保留
├──> "request_keyframe"
│ └──> < > request_keyframe_interval_ms> `idrCommand`
└──> "Unknown"
3.2 普通报文
- 报文格式:
+--------------+------------------+-----------------+----------------+-----------+------+-------+-------+---------------+
| Msg len (4B) | transmitted_time | link_value_rssi | link_value_snr | recovered | lost | rssi1 | rssi2 | rssi3 | rssi4 |
+--------------+------------------+-----------------+----------------+-----------+------+-------+-------+---------------+
- 代码流程:
解析地面端报文反馈的RF信号参数,比如:RSSI/SNR等
process_message
├──> [index/token parse]
│ ├──> <0> transmitted_time = atoi(token);
│ ├──> <1> link_value_rssi = atoi(token);
│ ├──> <2> link_value_snr = atoi(token);
│ ├──> <3> recovered = atoi(token);
│ ├──> <4> lost = atoi(token);
│ ├──> <5> rssi1 = atoi(token);
│ ├──> <6> rssi2 = atoi(token);
│ ├──> <7> rssi3 = atoi(token);
│ ├──> <8> rssi4 = atoi(token);
│ └──> <.> Ignore extra tokens
├──> <!time_synced> settimeofday(&tv, NULL)
└──> <!paused> start_selection(link_value_rssi, link_value_snr);
4. 工作流程
4.1 Profile
竞选
当 2.1
paused 为 false
时,满足触发条件则进行 start_selection
:
start_selection
├──> <selection_busy> return
├──> <rssi_score == 999> value_chooses_profile(999); // Default settings
│ └──> return
├──> int combined_value = floor(rssi_score * w_rssi + snr_score * w_snr);
├──> constrain(1000, 2000, combined_value)
├──> float percent_change = fabs((float)(value - baseline_value) / baseline_value) * 100;
└──> <percent_change >= hysteresis_percent>
└──> <time_diff_ms >= min_between_changes_ms> value_chooses_profile(value); // apply new settings
注:这里采用了 rssi
和 snr
权重方式。
4.2 Profile
研判
当 Profile
竞选成功后,在实际应用时,需要检查触发条件,比如:如果当前为需要切换的 Profile
则无需触发。
value_chooses_profile
├──> Profile* selectedProfile = get_profile(input_value);
├──> [Find the index of the selected profile]
├──> <previousProfile == currentProfile> return // no changes
├──> <previousProfile == 0 && timeElapsed <= hold_fallback_mode_s> return // first profile in fallback time
├──> <(currentProfile - previousProfile == 1) && timeElapsed <= hold_modes_down_s> // just one step difference in hold time
└──> apply_profile(selectedProfile)
无缝的触发场景判断,能够确保信号的稳定传输和平滑切换:
4.2.1 回退策略
满足下面条件,无需触发回退策略;反之,则进入最大发射效率模式。
- 前一次
Profile
已经指向index=0
- 最近一次检测时间没有超过
hold_fallback_mode_s
设置
if (previousProfile == 0 && timeElapsed <= hold_fallback_mode_s) {
if (verbose_mode) {
puts("Holding fallback...");
}
return false;
}
原因:回退策略是确保无接收端信号时,保持最大发射效率(Do the Best)。
4.2.2 保持策略
满足下面条件,触发保持策略;反之,则进入执行当前 Profile
切换。
- 前一次
Profile
比当前竞选Profile
发射效率更大 - 最近一次检测时间没有超过
hold_modes_down_s
设置
if (previousProfile < currentProfile && timeElapsed <= hold_modes_down_s) {
if (verbose_mode) {
puts("Holding mode down...");
}
return false;
}
原因:发射效率降低时,保持并不影响信号传输质量(信号传输并非功率敏感应用)。
4.3 Profile
应用
这里需要注意几个细节:
- 功率增加/减小其命令执行顺序不一致
- 综合信号质量来选择不同的GI/MCS/FecK/FecN/Bitrate/Gop/Power/ROIqp
apply_profile
├──> Local Variables Initialization
│ └──> Command Templates and Time Calculation
├──> Load Profile Variables into Local Variables
│ └── Copy values from `profile` into local variables
├──> Profile Comparison (currentProfile vs previousProfile)
│ ├──> If currentProfile > previousProfile:
│ │ ├──> Execute Power Command if changed // "iw dev wlan0 set txpower fixed %d"
│ │ ├──> Execute GOP Command if changed // "curl -s 'http://localhost/api/v1/set?video0.gopSize=%f'"
│ │ ├──> Execute MCS Command if changed // "wfb_tx_cmd 8000 set_radio -B 20 -G %s -S 1 -L 1 -M %d"
│ │ ├──> Execute FEC Command if changed // "wfb_tx_cmd 8000 set_fec -k %d -n %d"
│ │ ├──> Execute Bitrate Command if changed // "curl -s 'http://localhost/api/v1/set?video0.bitrate=%d'"
│ │ ├──> Execute ROI Command if changed // "curl -s 'http://localhost/api/v1/set?fpv.roiQp=%s'"
│ │ └──> Execute IDR Command if enabled // "curl localhost/request/idr"
│ └──> Else (if currentProfile <= previousProfile):
│ └──> Execute commands in different order
└──> Display Stats (msposdCommand)
└──> Execute `msposdCommand` // "echo '%ld s %d M:%d %s F:%d/%d P:%d G:%.1f&L30&F28 CPU:&C &Tc %s' >/tmp/MSPOSD.msg"
rangeMin | rangeMax | setGI | setMCS | setFecK | setFecN | setBitrate | setGop | wfbPower | ROIqp |
---|---|---|---|---|---|---|---|---|---|
999 | 999 | long | 0 | 12 | 15 | 3332 | 1.0 | 61 | 0,0,0,0 |
1000 | 1150 | long | 0 | 12 | 15 | 3333 | 1.0 | 60 | 0,0,0,0 |
1151 | 1300 | long | 1 | 12 | 15 | 6667 | 1.0 | 59 | 12,12,12,12 |
1301 | 1700 | long | 2 | 12 | 15 | 10000 | 1.0 | 58 | 12,8,8,12 |
1701 | 1850 | long | 3 | 12 | 15 | 12500 | 1.0 | 56 | 8,0,0,8 |
1851 | 2001 | short | 3 | 12 | 15 | 14000 | 1.0 | 56 | 4,0,0,4 |
rangeMin: Starting value of the range.
rangeMax: Ending value of the range.
setGI: 是无线通信系统中的 保护间隔(GI,Guard Interval)。短GI(400 ns)和长GI(800 ns)是两种常见的保护间隔设置,用于管理OFDM(正交频分复用)符号之间的时间间隔。选择短GI或长GI会影响性能和抗干扰能力。它通常在无线通信协议的 物理层(PHY) 中进行设置,比如Wi-Fi(802.11标准)。
setMCS: 定义了用于数据传输的调制和编码方案。MCS决定了数据是如何编码的(调制类型),以及为错误纠正添加了多少冗余数据(编码率)。在Wi-Fi(802.11n/ac/ax)中,MCS值通常从0到9(或更高,取决于Wi-Fi版本)。MCS索引是802.11协议标准的一部分,并且可以根据链路质量和信号强度进行调整。
setFecK: 指的是前向错误纠正(FEC)方案,特别是表示在应用错误纠正之前的数据位数(K值)。FEC用于通过添加冗余数据来提高无线通信的可靠性,从而使接收方能够纠正噪声或干扰引起的错误。K值通常是Reed-Solomon编码或卷积编码中的一个参数。
setFecN: 表示应用FEC后的总位数(包括数据位和校验位)。 K/N的比率给出了编码率,这决定了为错误纠正添加的冗余程度。较低的FEC值(例如1/2)表示更多的冗余和错误纠正能力,而较高的值(如3/4或5/6)则提供更高的吞吐量,但错误纠正能力较弱。
setBitrate: 表示通过无线链路传输数据的速度,通常以Mbps(兆比特每秒)为单位。该值受到调制方案、编码率和信号强度的影响。在Wi-Fi网络中,通常会根据这些因素动态调整比特率,以优化吞吐量,同时保持稳定的连接。
setGop: GOP设置与视频编码相关,尤其是在像H.264或H.265这样的压缩方案中。定义了关键帧(I帧)之间的间隔。短GOP意味着更频繁的关键帧(更高的视频质量,较低的压缩),而长GOP意味着较少的关键帧(更高的压缩,较低的质量)。在无线通信中,这个设置对于视频流的传输有很大影响。
wfbPower: 指的是无线前端(WFB)硬件的发射功率。发射功率是无线通信中的一个关键参数,影响无线信号的范围和质量。在Wi-Fi设备中,功率通常可以根据法规限制、设备能力和网络状况进行调整。wfbPower值可能用于配置设备中射频(RF)部分的放大器。
ROIqp: ROI QP values as a comma-separated string (e.g.,
0,0,0,0
).
5. 总结
Profile
是一个经验值(测试值),依赖于具体场景应用。- 配置参数(如:
hold_fallback_mode_s
/hold_modes_down_s
) 也是一个经验参数,依赖于具体应用场景。 - RSSI SNR 权重 RF信号质量计算模型,也是一个经验方法,可以调整更优的算法。
基于上述逻辑,对于这些内容的优化,就能更好的将FPV视频无缝的应用于实际环境 - 取决于大量的测试和优化。
注:这里感觉缺少心跳报文丢失的处理,以应对极端情况。
6. 参考资料
【1】OpenIPC开源FPV之Adaptive-Link工程解析
7. 补充资料
RSSI(接收信号强度指示)和SNR(信噪比)是衡量信号质量的常用指标。
加权 RSSI 和 SNR 以综合评估信号质量的做法,基于以下几个理论依据:
- RSSI 反映信号强度,而 SNR 反映信号与噪声的比率,两者结合能够更全面地评估信号质量。
- 加权方式可以根据应用场景和环境的变化,动态调整各个参数的影响,优化信号质量的评估。
- 加权系数的调整通常是基于实际的应用需求和实验数据优化的。
通过加权结合这两个指标,可以更准确地反映无线通信中的实际信号质量,进而为系统做出更合理的决策(如选择最佳基站、调整发射功率、优化资源分配等)。
7.1 RSSI 和 SNR 的物理含义
RSSI:表示接收到的信号强度,是衡量信号功率强度的一个指标。通常情况下,RSSI 越高,表示信号接收的质量越好。然而,RSSI 只反映了信号的强度,并不直接考虑噪声的影响。
SNR:表示信号与噪声的比值,是衡量信号质量的一个重要指标。SNR 越高,意味着信号在噪声背景下越清晰,通信质量越高。高的 SNR 值通常意味着信号更容易被准确解码,而低的 SNR 值则容易导致误码或通信失败。
7.2 信号质量加权的理论依据
信号强度与噪声的相对重要性:
单独依赖 RSSI 来衡量信号质量可能会产生误导。因为高强度的信号也可能伴随着较强的噪声,而高噪声水平会影响信号的清晰度。因此,SNR 提供了一个更全面的衡量标准,考虑了信号的强度与噪声的关系。加权方式结合了这两个指标,能够更准确地反映实际信号质量。加权的数学模型:
一种常见的做法是将 RSSI 和 SNR 作为输入参数,通过某种加权函数或线性组合来得到一个综合的信号质量指标。可以根据具体场景的需求调整权重值。例如:
Q = w 1 ⋅ RSSI + w 2 ⋅ SNR Q = w_1 \cdot \text{RSSI} + w_2 \cdot \text{SNR} Q=w1⋅RSSI+w2⋅SNR
其中,$ w_1 $ 和 $ w_2 $ 是 RSSI 和 SNR 的权重系数,表示它们在信号质量计算中的相对重要性。- 选择合适的权重系数:
权重的设置需要根据具体的应用需求和实验数据来优化。在一些应用中,SNR 可能更为关键,因为它直接影响到数据传输的错误率,而在其他场景中,RSSI 可能更重要,因为信号强度直接决定了通信的覆盖范围。
- 选择合适的权重系数:
7.3 实际应用中的加权方法
无线通信系统:在无线通信中,RSSI 和 SNR 都是评估信号质量的重要指标。将两者加权后,系统可以更好地判断信号的稳定性和传输质量。例如,在 Wi-Fi 或移动通信中,基站或接入点会同时考虑这两个参数,以确保数据传输的可靠性。
动态信号质量评估:无线环境通常是动态变化的,信号强度和噪声水平可能随时间和位置变化。通过加权方式,可以更灵活地反映当前信号的质量,特别是在复杂的多径传播和干扰环境中。
7.4 加权方法的优化
信道的特性:在不同的无线信道中,RSSI 和 SNR 对信号质量的影响可能不同。例如,在高干扰环境下,SNR 的作用更为突出,因此可以为 SNR 分配更大的权重。而在信号强度较好的环境中,RSSI 可能会更重要。
基于经验的调整:通过实际测试和仿真,可以根据不同的环境条件和通信需求,调整 RSSI 和 SNR 的权重。例如,在一个需要长距离传输的场景中,可能会更侧重于 RSSI;而在一个要求高数据速率和低错误率的场景中,可能会更关注 SNR。
7.5 综合考虑信号质量的模型
在一些高级的信号质量评估模型中,除了直接的 RSSI 和 SNR 之外,可能还会考虑其他因素,比如:
- 路径损耗:信号在传播过程中的衰减。
- 干扰:来自其他无线设备或环境的噪声。
- 调制方式:不同的调制方式对信号质量的敏感度不同。
这些因素也可能在加权过程中作为附加的输入,进一步提升信号质量评估的准确性。
7.6 8812EU WiFi模块
功率设置两个方法: WIP: Add support for RTL8812EU-based Wi-Fi adapters for FPV firmware #1344
driver_txpower_override
in/etc/wfb.conf
. The range is0~63
iw dev <wlan0> set txpower fixed <mBm>
. The range is0~3150
, and can be set dynamically when transmitting.