OpenIPC开源FPV之Adaptive-Link地面站代码解析

news2024/12/23 18:27:23

OpenIPC开源FPV之Adaptive-Link地面站代码解析

  • 1. 源由
  • 2. 框架代码
  • 3. 软件配置
    • 3.1 默认配置
    • 3.2 加载配置
    • 3.3 更新配置
  • 4. 通信例程
    • 4.1 TCP报文解析
    • 4.2 UDP报文发送
  • 5. 特殊指令
    • 5.1 request_keyframe
    • 5.2 drop_gop
    • 5.3 resume_adaptive
    • 5.4 pause_adaptive
  • 6. 总结
  • 7. 参考资料

1. 源由

在《OpenIPC开源FPV之Adaptive-Link工程解析》中,已经有了整个工程的大体概念,接下来再对代码进行逐步分析。

在之前对天空端代码进行了研读,接下来对地面端的代码进行分析:alink_gs.py

2. 框架代码

  • 配置:“/etc/adaptive_link.conf”
  • wfb-cli:127.0.0.1:8003
  • UDP: 10.5.0.10:9999
__main__
 ├──> load_config(args.config)  // --config "/etc/adaptive_link.conf"
 └──> connect_and_receive_msgpack()

注:connect_and_receive_msgpack 建立TCP和UDP通信管道。

3. 软件配置

3.1 默认配置

VERSION = "0.1.1"

# Global variables
results = []
link_health_score_rssi = 1000  # Default score before calculation for RSSI
link_health_score_snr = 1000  # Default score before calculation for SNR
recovered_packets = 0  # To store the number of recovered packets (FEC)
lost_packets = 0  # To store the number of lost packets
best_antennas_rssi = [-105, -105, -105, -105]  # Default values for best antennas RSSI if less than 4
best_antennas_snr = [-105, -105, -105, -105]  # Default values for best antennas SNR if less than 4
config = None  # Configuration file object
udp_socket = None  # To store the UDP socket globally
udp_ip = None  # To store the UDP IP globally
udp_port = None  # To store the UDP port globally
previous_link_health_score_rssi = None
previous_link_health_score_snr = None



# Default config values
DEFAULT_CONFIG = {
    'Settings': {
        'version': VERSION,  # Current version of the script
        'message_interval': '100',  # Time between sending messages (milliseconds)
        'use_best_rssi': 'True',  # Option to pick best available RSSI for link health score
        'min_rssi': '-85',  # Min RSSI range for link health
        'max_rssi': '-50',  # Max RSSI range for link health
        'min_snr': '12',  # Min SNR range for link health
        'max_snr': '28',  # Max SNR range for link health
        'host': '127.0.0.1',  # Host address to connect to
        'port': '8003',  # Port to connect to
        'udp_ip': '10.5.0.10',  # UDP IP to send link health data
        'udp_port': '9999',  # UDP port to send link health data
        'retry_interval': '1',  # Time in seconds to wait before retrying TCP connection on failure
    },
    'Descriptions': {
        'version': 'Version of the script',
        'message_interval': 'Time between sending UDP messages in milliseconds',
        'use_best_rssi': 'Use True to pick the best available RSSI for health score, or False for average RSSI',
        'min_rssi': 'Minimum RSSI value used for link health calculation',
        'max_rssi': 'Maximum RSSI value used for link health calculation',
        'min_snr': 'Minimum SNR value used for link health calculation',
        'max_snr': 'Maximum SNR value used for link health calculation',
        'host': 'Host address to connect to for the TCP connection',
        'port': 'Port to connect to for the TCP connection',
        'udp_ip': 'UDP IP to send the link health data',
        'udp_port': 'UDP port to send the link health data',
        'retry_interval': 'Time in seconds to wait before retrying TCP connection on failure'
    }
}

3.2 加载配置

配置通过"/etc/adaptive_link.conf"文件传入,若没有该文件,系统默认生成一份。

load_config(config_file='config.ini')
 ├──> Check if config file exists
 │     ├──> File does not exist
 │     │     ├──> Print "Config file not found, creating..."
 │     │     ├──> Load default values into config (DEFAULT_CONFIG)
 │     │     └──> Write default config to file
 │     └──> File exists
 │           └──> Read existing config file
 ├──> Check version mismatch
 │     ├──> Config version != VERSION
 │     │     ├──> Print "Updating version in config file..."
 │     │     ├──> Update config['Settings']['version'] to VERSION
 │     │     └──> Call update_version_history(config_file)
 │     └──> Config version == VERSION (do nothing)
 ├──> Ensure all default fields are present
 │     ├──> Iterate through DEFAULT_CONFIG sections and keys
 │     │     ├──> Section missing → Add section to config
 │     │     ├──> Key missing in section → Add key to config
 │     │     └──> Mark config as updated
 ├──> Save updated config (if needed)
 │     └──> Write updated config to file
 └──> Return (config is now fully loaded and updated)

3.3 更新配置

若无该配置文件,通过该命令直接生成一个默认配置文件。

注:配置文件升级更新,暂时无该功能。

def update_version_history(config_file):
    """
    Updates the version history in the configuration file.
    """
    if 'Version History' not in config:
        config['Version History'] = {}

    # Add the current version with a timestamp
    timestamp = time.strftime('%Y-%m-%d %H:%M:%S', time.gmtime())
    config['Version History'][VERSION] = f"Version {VERSION} updated on {timestamp}"

    # Write the updated config to file
    with open(config_file, 'w') as f:
        config.write(f)

4. 通信例程

  • UDP: 10.5.0.10:9999
  • TCP: 127.0.0.1:8003
connect_and_receive_msgpack()
 ├──> Initialize UDP socket and thread  // 10.5.0.10:9999
 │     ├──> Create global UDP socket (udp_socket)
 │     └──> Start generate_package() in a separate thread
 ├──> Infinite loop to handle TCP connection and data // 127.0.0.1:8003
 │     ├──> Try to connect to the server
 │     │     ├──> Create TCP socket (client_socket)
 │     │     ├──> Connect to (host, port)
 │     │     └──> Read data from server in a loop
 │     │           ├──> Read 4-byte length prefix
 │     │           ├──> Extract message length (msg_length)
 │     │           ├──> Read actual message data of length msg_length
 │     │           │     ├──> Append chunks of data until full message is received
 │     │           │     └──> Handle incomplete data or connection issues
 │     │           └──> Unpack and process MessagePack data
 │     │                 ├──> Append unpacked data to results array
 │     │                 ├──> Handle "video rx" message
 │     │                 │     └──> Update link health metrics (RSSI, SNR, etc.)
 │     │                 └──> Handle unpack errors
 │     └──> Handle connection issues
 │           └──> Print error and retry connection after retry_interval seconds
 └──> Return (loop runs indefinitely)

4.1 TCP报文解析

通过wfb-cli的TCP 8003端口获取本地RF信息数据。

calculate_link_health(video_rx)
├──> Global Variables Declaration
├──> Configuration Settings (Extract from `config`)
│   ├──> message_interval
│   ├──> use_best_rssi
│   ├──> min_rssi, max_rssi
│   └──> min_snr, max_snr
├──> Packets Data Retrieval (from `video_rx`)
│   ├──> fec_rec → recovered_packets
│   ├──> lost → lost_packets
│   └──> Special Messages Logic
│       ├──> request_keyframe()
│       └──> (Optional) drop_gop()
├──> Antenna Statistics Processing
│   ├──> Extract RSSI/SNR per antenna
│   └──> Fallback Logic (Default Values)
├──> Best Antennas Selection
│   ├──> Sort `rssi_values` and `snr_values`
│   └──> Pad with `-105` if fewer than 4 antennas
├──> Link Health Score Calculation
│   ├──> RSSI Health Score
│   │   ├──> rssi_to_use (Best vs Average)
│   │   └──> Interpolation Logic
│   ├──> SNR Health Score
│   │   ├──> avg_best_snr (Best vs Average)
│   │   └──> Interpolation Logic
│   └──> (Optional) Hysteresis Logic
├──> Rounding and Logging
└──> Return Scores (RSSI, SNR)

4.2 UDP报文发送

  • 定期将本地最新全局变量组包
def generate_package():
    """
    Generate, at interval, string with all the variables
    """
    message_interval = int(config['Settings']['message_interval']) / 1000  # Convert to seconds
    
    while True:
        timestamp = int(time.time())  # Get epoch time in seconds since 1970
        # Include best antennas in the message
        message = f"{timestamp}:{link_health_score_rssi}:{link_health_score_snr}:{recovered_packets}:{lost_packets}:{best_antennas_rssi[0]}:{best_antennas_rssi[1]}:{best_antennas_rssi[2]}:{best_antennas_rssi[3]}"
                    
        # Pass the message to send_udp function
        send_udp(message)
                
        time.sleep(message_interval)  # Send at the specified interval
  • 通过UDP tunnel管道发送到天空端
def send_udp(message):
    """
    Adds message length to the start and sends message on provided port
    """
    if verbose_mode:
        print("send_udp function has started")  # Debug statement to confirm function start
            
    # Prepend the size of the message as a 4-byte unsigned integer
    message_bytes = message.encode('utf-8')
    message_size = struct.pack('!I', len(message_bytes))  # Use network byte order (big-endian)

    # Full message with size prefix
    full_message = message_size + message_bytes
    
    if verbose_mode:
        print("Preparing UDP message to be sent")  # Debug statement
    
    # Send the message
    try:
        udp_socket.sendto(full_message, (udp_ip, udp_port))
        if verbose_mode:
            print(f"UDP Message Sent: {message} (size: {len(message_bytes)} bytes)")
    except Exception as e:
        if verbose_mode:
            print(f"Error sending UDP data: {e}")

5. 特殊指令

5.1 request_keyframe

def request_keyframe():
    """
    Send a special message to request a keyframe
    """
    special_message = "special:request_keyframe"
    num_attempts = 0  # Number of times to send the message

    for attempt in range(num_attempts):
        send_udp(special_message)  # Call send_udp to send the special message
        if verbose_mode:
            print(f"Sent special message: {special_message}, attempt {attempt + 1}/{num_attempts}")
        time.sleep(0.1)  # Wait before the next attempt

5.2 drop_gop

def drop_gop():
    """
    Send a special message to drop the gop
    """
    special_message = "special:drop_gop"
    num_attempts = 0  # Number of times to send the message

    for attempt in range(num_attempts):
        send_udp(special_message)  # Call send_udp to send the special message
        if verbose_mode:
            print(f"Sent special message: {special_message}, attempt {attempt + 1}/{num_attempts}")
        time.sleep(0.1)  # Wait before the next attempt

5.3 resume_adaptive

TBD.

5.4 pause_adaptive

TBD.

6. 总结

地面端程序主要采用 python 进行编写,所以可以跨平台兼容。对于软件的部署来说非常便捷。

从代码实现角度,存在较多异常场景以及开放性问题值得讨论和完善。

  • How to config/monitor 8812AU(gs) 8812EU(airunit) working status? #5
  • The relationship between .c and binary files #7
  • what’s the difference between hold_fallback_mode_s and hold_modes_down_s? #9
  • WIP: Add support for RTL8812EU-based Wi-Fi adapters for FPV firmware #1344
  • How to interpret each row of configuration data in txprofiles.conf? #10

7. 参考资料

【1】OpenIPC开源FPV之Adaptive-Link工程解析
【2】OpenIPC开源FPV之Adaptive-Link天空端代码解析

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2264370.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

基于 iAP2 协议 的指令协议,用于对安防设备的 MCU 进行操作

协议设计目标 1. 安全性:通过 iAP2 协议与 MCU 设备进行安全通信。 2. 通用性:支持对安防设备的常见功能进行操作,如状态查询、设备控制、参数配置等。 3. 高效性:数据结构简洁清晰,易于解析和扩展。 4. 扩展性&#x…

Type-C单口便携屏LDR6021

随着科技的飞速发展,便携式电子产品在我们的日常生活中扮演着越来越重要的角色。在这一背景下,Type-C单口便携显示器作为一种新兴的显示设备,凭借其独特的优势迅速崭露头角,成为市场的新宠。本文将深入探讨Type-C单口便携显示器的…

Ubuntu 20.04 卸载和安装 MySQL8.0

卸载 首先,检查一下系统安装的软件包有哪些,使用dpkg -l | grep mysql命令: 为了将MySQL卸载干净,这些文件都需要被删除。 在Ubuntu20.04系统下,卸载干净MySQL8.0以确保下一次安装不会出错,可以按照以下…

NOTEBOOK_11 汽车电子设备分享(工作经验)

汽车电子设备分享 摘要 本文主要列出汽车电子应用的一些实验设备和生产设备,部分会给予一定推荐。目录 摘要一、通用工具:二、测量与测试仪器2.1测量仪器2.2无线通讯测量仪器2.3元器件测试仪2.4安规测试仪2.5电源供应器2.6电磁兼容测试设备2.7可靠性环境…

黑马Java面试教程_P8_并发编程

系列博客目录 文章目录 系列博客目录前言1.线程的基础知识1.1 线程和进程的区别?难2频3面试文稿 1.2 并行和并发有什么区别? 难1频1面试文稿 1.3 创建线程的四种方式 难2频4面试文稿 1.4 runnable 和 callable 有什么区别 难2频3面试文稿 1.5 线程的 run…

【活动邀请·深圳】深圳COC社区 深圳 AWS UG 2024 re:Invent re:Cap

re:Invent 是全球云计算领域的顶级盛会,每年都会吸引来自世界各地的技术领袖、创新者和实践者汇聚一堂,分享最新的技术成果和创新实践,深圳 UG 作为亚马逊云科技技术社区的重要组成部分,将借助 re:Invent 的东风,举办此…

一起学Git【第二节:创建版本库】

创建库 这个库相当于一个目录,目录中的文件都被Git管理,会记录每个文件的修改删除和添加工作,便于之后随时跟踪历史记录还原到之前的某一版本。如何创建库呢?有两种方式,本地创建库和云端克隆一个库。 1.本地创建库 …

本地部署webrtc应用怎么把http协议改成https协议?

环境: WSL2 Ubuntu22.04 webrtc视频聊天应用 问题描述: 本地部署webrtc应用怎么把http协议改成https协议? http协议在安卓手机浏览器上用不了麦克风本,来地应用webrtc 本来是http协议,在安卓手机上浏览器不支持使…

web实操8-cookie

会话技术 会话: 一次会话中包含多次请求和响应。 客户端浏览器访问服务器的资源,只要客户端或者服务器端不关闭,这始终在一次会话范围内,这一次会话范围内可以包含多次请求并且收到多次相应。 一次会话:浏览器第一…

vue2 - Day03 - (生命周期、组件、组件通信)

文章目录 一、生命周期1. 创建阶段2. 挂载阶段3. 更新阶段4. 销毁阶段5. 错误捕获总结 二、组件2.1 注册1. 全局注册 - 公共的组件。2. 局部注册总结 2.2 三大重要的组成部分1. 模板 (Template)主要功能:说明: 2. 脚本 (Script)主要功能:说明…

java日常工作开发高并发问题

前言 本篇文章将是以工作中经常遇到的问题,和面试中经常遇到的java问题进行描写。内容包括微服架构,java并发编程以及相应的中间件的高级知识。本文所有的问题都在描述多线程编程的高级知识。 一. 面试题 1.Sychronized和ReentrantLock有哪些不同点? …

【Python】【数据分析】深入探索 Python 数据可视化:Matplotlib 绘图库完整教程

目录 引言一、什么是 Matplotlib?1.1 Matplotlib 的安装1.2 Matplotlib 的基本功能 二、Matplotlib 的基础绘图2.1 绘制折线图2.2 绘制柱状图2.3 绘制散点图2.4 绘制饼图 三、高级功能与定制3.1 设置图表样式3.2 使用子图3.3 保存图表 四、Matplotlib 流程图4.1 Mer…

【代码随想录|动态规划背包问题】

一、背包问题分类 01背包:n种物品,每种物品只有一个 完全背包:n种物品,每种物品有无限个 多重背包:n种物品,每种物品的个数各不相同 二、01背包问题三道题 卡码网46题.携带研究材料(二维背包…

第1章 命题逻辑

2024年12月22日 一稿 1.1 现代逻辑学的基本研究方法 1.2 命题及其表示法 1.2.1 命题的概念 定义1.1 命题是一个可以判断真假的陈述句。 1.2.2 联结词 非 与 或 蕴含 等价 1.3 命题公式与语句形式化 1.3.1 命题公式的定义 1.3.2 公式的层次 1.3.3 语句形式化 1…

Unity-Editor扩展GUI基本实现一个可拖拉放的格子列表

短短几百行代码,好吧,又是“参考”了国外的月亮 操作,还真地挺自然的。。。。。。国外的实现有点小牛 拖拉,增加+ 一个Element 鼠标左键长按,可以出提示 鼠标右键,清除Element, 有点小bug,不是很自然地完全清除, using System.Collections; using System.Collecti…

解决vscode ssh远程连接服务器一直卡在下载 vscode server问题

目录 方法1:使用科学上网 方法2:手动下载 方法3 在使用vscode使用ssh远程连接服务器时,一直卡在下载"vscode 服务器"阶段,但MobaXterm可以正常连接服务器,大概率是网络问题,解决方法如下: 方…

重拾设计模式--外观模式

文章目录 外观模式(Facade Pattern)概述定义 外观模式UML图作用 外观模式的结构C 代码示例1C代码示例2总结 外观模式(Facade Pattern)概述 定义 外观模式是一种结构型设计模式,它为子系统中的一组接口提供了一个统一…

jvm栈帧结构

JVM(Java虚拟机)中的虚拟机栈是线程私有的,用于支持Java虚拟机进行方法调用和方法执行。而栈帧(Stack Frame)则是虚拟机栈的基本元素,每一个方法从调用开始至执行结束的整个过程,都对应着一个栈帧在虚拟机栈中从入栈到出栈的过程。栈帧的内部结构主要包括以下几个部分:…

2009 ~ 2019 年 408【计算机网络】大题解析

2009 年 路由算法(9’) 讲解视频推荐:【BOK408真题讲解-2009年(催更就退网版)】 某网络拓扑如下图所示,路由器 R1 通过接口 E1 、E2 分别连接局域网 1 、局域网 2 ,通过接口 L0 连接路由器 R2 &…

Flamingo论文介绍:把视觉特征向语言模型看齐

今天介绍一篇经典的多模态论文,来自NeurIPS 2022的《Flamingo: a Visual Language Model for Few-Shot Learning》 ,论文地址:https://arxiv.org/pdf/2103.00020 文章目录 一、Motivate二、Method三、模块细节:Perceiver Resampl…