OpenIPC開源FPV之Adaptive-Link天空端代碼解析
- 1. 源由
- 2. 框架代碼
- 2.1 消息機制
- 2.2 超時機制
- 3. 報文處理
- 3.1 special報文
- 3.2 普通報文
- 4. 工作流程
- 4.1 `Profile` 競選
- 4.2 `Profile` 研判
- 4.2.1 回退策略
- 4.2.2 保持策略
- 4.3 `Profile` 應用
- 5. 總結
- 6. 參考資料
- 7. 補充資料
- 7.1 RSSI 和 SNR 的物理含義
- 7.2 信號質量加權的理論依據
- 7.3 實際應用中的加權方法
- 7.4 加權方法的優化
- 7.5 綜合考慮信號質量的模型
- 7.6 8812EU WiFi模塊
1. 源由
在《OpenIPC開源FPV之Adaptive-Link工程解析》中,已經有了整個工程的大體概念,接下來再對代碼進行逐步分析。
首先,對天空端的代碼進行分析:ALink42n.c
2. 框架代碼
ALink42n.c
相對來說,代碼量最少,也是最為基本的一份代碼。
目前,尚不太清楚具體n/p/q之間的差異,邏輯上看應該是關于切換配置profile
的條件計算方式不太一樣,對于穩定性、可靠性方面應該有所差異。
- The relationship between .c and binary files #7
注:感興趣的朋友,可以跟下帖子,不過隨著代碼的深入了解,以及性能測試數據,也能慢慢明晰之間的差異。
2.1 消息機制
- 加載配置 - “/etc/alink.conf”
- 加載Profile - “/etc/txprofiles.conf”
- majestic:80
- wfb-cli:8000
- Terminal:bash
- 綁定默認IP - 10.5.0.10:9999
n/p/q只有q寫的是10.5.0.10,其他是10.5.0.2,應該有筆誤。
- 接受兩種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)
無縫的觸發場景判斷,能夠確保信號的穩定傳輸和平滑切換:
- what’s the difference between hold_fallback_mode_s and hold_modes_down_s? #9
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模塊
- M8812EU2 2T2R 802.11a/n/ac WiFi Module
- Using BL-M8812EU2 (or other RTL8812EU-based) Wi-Fi Module
功率設置兩個方法: 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.