python基于千兆以太网的FPGA频谱分析,上位机部分

本文详细介绍了如何使用Python的scapy库进行以太网数据抓包,实现实时频谱分析。项目通过监听网口数据,利用sniff函数抓取UDP包,然后进行FFT变换以显示频谱。在设计中,使用了线程来处理实时数据和图形更新,同时利用matplotlib在Pyqt5界面中嵌入动态图。文章还提到了遇到的问题,如数据包处理和线程同步,并分享了解决方案。

一、背景

        该项目原课题为基于千兆以太网的FPGA的频谱仪显示,上位机的难点显然不在于FFT的频谱分析,如何实时获取数据,与FPGA进行对接成为主要的难点。

程序语言:python

环境:Anaconda envs:python3.7

平台:Pycharm; Qt designer

参考平台:Wireshark

二、设计原理

        首先设计信号监听函数,若有数据输入,则接口正确;若无数据输入则继续监听直到传入数据。接收到数据后根据控件可打开线程,首先是线程一,实时监听数据并将数据存入数组,并附有一个时间轴数组与之对应。主线程为作图函数,首先对接受的数据进行FFT变换,之后对信号与频谱作图并实时刷新。线程二为控制绘图线程,点击控件后会改变绘制条件来结束绘制。逻辑图如下:

三、具体设计

1.基于sniff函数实时抓包以太网网口的数据

        首先要安装python抓包的核心库:scapy,之后核心是实时监听网口抓包数据,用到的是sniff函数,它可以用于接收和发包,便于上位机自检。抓包函数只需下面两行:

while 1:
    sniff(filter="udp", prn=pack_callback, iface=IFACES.dev_from_index(11), count=1)
#其中11为以太网的网口,每次接收一个包,prn后的是自己定义的回调函数

        其中回调函数可定义,因为基于千兆以太网的FPGA传来的是UDP包,设置过滤为udp,我们需要从抓到的udp包中获得它的数据部分,于是我们可定义以下回调函数:

    def pack_callback(packet):
        p = packet

        # 如果UDP包中含有数据层,则把Raw曾的数据提取出来
        if 'Raw' in p:
            data = p.getlayer(Raw)
            for each in data.load:
                tim = np.append(tim, ttemp / (bagsize *sample_bag))
                # datax为数据数组
                datax = np.append(datax, int(each))
                # ttemp为所有数据的个数
                ttemp += 1  # 数据个数
                # bagsize为一组显示单元,控制数组长度,避免数组无限长导致的开销加大
                if ttemp > bagsize:
                    self.tim = np.delete(self.tim, [0])
                    self.datax = np.delete(self.datax, [0])

        tim为时间数组,用于与datax的数据对应,需要将时间归一化。其次需要及时删掉之前的数据防止数组无限大。其中bagsize为一个包中数据部分大小,此处为512,sample_bag为一秒抓包个数,此处为10。这两个参数需要与发包方对接修改。

        sniff同时可以自发包自抓包来进行自检,发送一个八个0和八个8循环的方波,长度为512,0.1秒发一个包,注意网口要和抓包那边的网口一致并且存在。发包函数如下定义:


L=[0,0,0,0,0,0,0,0,8,8,8,8,8,8,8,8,0,0,0,0,0,0,0,0,8,8,8,8,8,8,8,8,0,0,0,0,0,0,0,0,8,8,8,8,8,8,8,8,0,0,0,0,0,0,0,0,8,8,8,8,8,8,8,8,0,0,0,0,0,0,0,0,8,8,8,8,8,8,8,8,0,0,0,0,0,0,0,0,8,8,8,8,8,8,8,8,0,0,0,0,0,0,0,0,8,8,8,8,8,8,8,8,0,0,0,0,0,0,0,0,8,8,8,8,8,8,8,8,0,0,0,0,0,0,0,0,8,8,8,8,8,8,8,8,0,0,0,0,0,0,0,0,8,8,8,8,8,8,8,8,0,0,0,0,0,0,0,0,8,8,8,8,8,8,8,8,0,0,0,0,0,0,0,0,8,8,8,8,8,8,8,8,0,0,0,0,0,0,0,0,8,8,8,8,8,8,8,8,0,0,0,0,0,0,0,0,8,8,8,8,8,8,8,8,0,0,0,0,0,0,0,0,8,8,8,8,8,8,8,8,0,0,0,0,0,0,0,0,8,8,8,8,8,8,8,8,0,0,0,0,0,0,0,0,8,8,8,8,8,8,8,8,0,0,0,0,0,0,0,0,8,8,8,8,8,8,8,8,0,0,0,0,0,0,0,0,8,8,8,8,8,8,8,8,0,0,0,0,0,0,0,0,8,8,8,8,8,8,8,8,0,0,0,0,0,0,0,0,8,8,8,8,8,8,8,8,0,0,0,0,0,0,0,0,8,8,8,8,8,8,8,8,0,0,0,0,0,0,0,0,8,8,8,8,8,8,8,8,0,0,0,0,0,0,0,0,8,8,8,8,8,8,8,8,0,0,0,0,0,0,0,0,8,8,8,8,8,8,8,8,0,0,0,0,0,0,0,0,8,8,8,8,8,8,8,8,0,0,0,0,0,0,0,0,8,8,8,8,8,8,8,8,0,0,0,0,0,0,0,0,8,8,8,8,8,8,8,8,0,0,0,0,0,0,0,0,8,8,8,8,8,8,8,8,0,0,0,0,0,0,0,0,8,8,8,8,8,8,8,8,0,0,0,0,0,0,0,0,8,8,8,8,8,8,8,8,0,0,0,0,0,0,0,0,8,8,8,8,8,8,8,8,0,0,0,0,0,0,0,0,8,8,8,8,8,8,8,8,0,0,0,0,0,0,0,0,8,8,8,8,8,8,8,8,0,0,0,0,0,0,0,0,8,8,8,8,8,8,8,8,0,0,0,0,0,0,0,0,8,8,8,8,8,8,8,8,0,0,0,0,0,0,0,0,8,8,8,8,8,8,8,8,0,0,0,0,0,0,0,0,8,8,8,8,8,8,8,8,0,0,0,0,0,0,0,0,8,8,8,8,8,8,8,8,0,0,0,0,0,0,0,0,8,8,8,8,8,8,8,8,0,0,0,0,0,0,0,0,8,8,8,8,8,8,8,8,0,0,0,0,0,0,0,0,8,8,8,8,8,8,8,8,0,0,0,0,0,0,0,0,8,8,8,8,8,8,8,8,0,0,0,0,0,0,0,0,8,8,8,8,8,8,8,8,0,0,0,0,0,0,0,0,8,8,8,8,8,8,8,8,0,0,0,0,0,0,0,0,8,8,8,8,8,8,8,8,0,0,0,0,0,0,0,0,8,8,8,8,8,8,8,8,0,0,0,0,0,0,0,0,8,8,8,8,8,8,8,8,0,0,0,0,0,0,0,0,8,8,8,8,8,8,8,8,0,0,0,0,0,0,0,0,8,8,8,8,8,8,8,8,0,0,0,0,0,0,0,0,8,8,8,8,8,8,8,8,0,0,0,0,0,0,0,0,8,8,8,8,8,8,8,8,0,0,0,0,0,0,0,0,8,8,8,8,8,8,8,8,0,0,0,0,0,0,0,0,8,8,8,8,8,8,8,8,0,0,0,0,0,0,0,0,8,8,8,8,8,8,8,8,0,0,0,0,0,0,0,0,8,8,8,8,8,8,8,8,0,0,0,0,0,0,0,0,8,8,8,8,8,8,8,8,0,0,0,0,0,0,0,0,8,8,8,8,8,8,8,8,0,0,0,0,0,0,0,0,8,8,8,8,8,8,8,8,0,0,0,0,0,0,0,0,8,8,8,8,8,8,8,8,0,0,0,0,0,0,0,0,8,8,8,8,8,8,8,8,0,0,0,0,0,0,0,0,8,8,8,8,8,8,8,8,0,0,0,0,0,0,0,0,8,8,8,8,8,8,8,8,0,0,0,0,0,0,0,0,8,8,8,8,8,8,8,8]
L3=L[0:512]
line=str(L3)
de=b''.join([bytes([int(i)]) for i in line[1:-1].split(',')])
# # 发送端IP地址不是本机ip地址   目的端IP地址不详      传输层的TCP并未指明数据包类型:syn fin ack 窗口大小 数据包如果分片,要指明序号
pkt = Ether(dst="22:33:44:55:66:77:88")/IP(src='1.1.1.200', dst='2.2.2.200')/UDP(sport=100, dport=200)/de
# # 间隔0.1秒发送一次   总共发送1000次   发送网卡口:11口
sendp(pkt, inter=0.1, count=1000, iface=IFACES.dev_from_index(11))

        当时将网口设置为本地连接时没有任何问题,但是改为以太网的网口会出现错误,具体报错也不记得了,但是解决方法是将npcap退回至1.3.1,希望可以有所帮助。 

关于sniff函数的具体用法我参照了这篇文章:Scapy 嗅探Python仿真实验(GUI界面)--2.2使用sniff监听网口抓包_谁月的博客-优快云博客

2.FFT作图

        在得到实时抓包的数据后,直接调用scipy的fft函数,得到的是一个复数,其中包括了它的幅度、频率等信息,不过需要手动处理(python fft的文章太多了,就不展开了)。主要问题如下:因为需要一边实时抓包一边作图,就需要设置线程。将抓包函数可定义为线程与控件结合,一开始我把matplotlib的作图函数放在线程下导致报错,原因在于matplotlib的相关函数必须放在主线程下。因为要与pyqt5的界面交互显示,用到了下面这些东西:

from matplotlib.pylab import mpl
from matplotlib.backends.qt_compat import QtWidgets
from matplotlib.backends.backend_qtagg import (FigureCanvas, NavigationToolbar2QT as NavigationToolbar)
from matplotlib.figure import Figure

        之后便是动态图与UI嵌入设计,从matplotlib中学到具体方法,同时也需要设计回调函数与刷新速率,刷新速率应该与抓包速率相同。回调函数只需要表示你想要画的东西,因为里面的参数在实时修改,所以会进行图像刷新。详见这篇:Embedding in Qt — Matplotlib 3.7.1 documentation

         其中刷新速率如下定义:

timer1 = dynamic_canvas.new_timer(1000/sample_bag))

        这方面就不多展开了,只需要将上面那篇文章显示与于CentralWidget改为listview便可以进行交互的设计了。ui设计如下:

四、结语 

        这是在本科第一次做的联合项目,文章介绍了上位机的核心思路,当时拿到课题真的是一无所知,最后还是一步一步做出来了,我把我当时遇到的瓶颈的解决思路向大家分享出来了,剩下的都是简单的码农该干的事了(bushi。希望这篇文章对大家有所帮助。

⚫ 选题三:基于 FPGA 的多功能协议调试器 设计要求: 使用高云 FPGA 设计一个多功能数字信号调试器(推荐使用 ACG525 或 ACG720),能够实现对各种数字信号的分析和调试功能 基础要求: 1、 串行协议转换:基于 CDC 串口驱动的 USB 转 SPI、I2C、UART、多路 PWM 输出 2、 数字信号测量:能够测量周期性数字信号的频率、占空比、高低电平时间 3、 模拟信号采集:能够驱动高速 ADC(并口或 LVDS 接口,采样率不低于 10Msps)实现高速模拟信号的采集,存储和传输, 4、 模拟信号发生器:能够驱动高速 DAC(并口或 LVDS 接口,采样率不低于 10Msps)实现高速模拟信号输出,支持含正弦波、方波、三角波、锯齿波等不低于 5 种波形输出 拓展要求(任选一个或多个): 1、 通信接口优化:传输接口可使用 USB HS、以太网等通信速率更高接口 2、 能够实现多通道循环自定义序列输出 3、 数字信号分析:能够设计用户分析软件或界面,对采集的数字信号进行协议 解析 4、 模拟信号分析:能够设计用户分析软件或界面,对采集的模拟信号进行时域 波形显示,频谱显示 5、 模拟信号发生器:能够支持手绘或自定义波形输出 6、 系统优化:多功能可同时使用 7、 增加其他功能,如串行协议转换增加 CAN 接口,模拟信号分析功能增加触发 采样功能,模拟和数字信号采集传输模式支持 buffer 模式和流模式
08-10
高速处理芯片分块设计方案(驱动+采集+信号处理) 一、驱动模块(DA信号发生器) 核心功能:生成高精度、高稳定性的模拟激励信号,为后续采集模块提供输入源。 1. 核心芯片选型 • 推荐芯片:ADI AD9789(16位)、TI DAC38J84(14位,支持多通道) ◦ 选型理由:均为高速数模转换器(DA),支持高采样率,满足“高速处理”场景对信号带宽的需求;内置校准功能,降低外围电路复杂度,保证输出信号精度。 2. 关键参数 • 分辨率(位数):14-16位 ◦ 16位DA(如AD9789):输出电压量化误差更小,适合对信号精度要求高的场景(如毫伏级小信号激励); ◦ 14位DA(如DAC38J84):性价比更高,若系统对精度要求适中(如伏级信号),可优先选择。 • 采样率:≥1GSPS(千兆采样/秒) ◦ 决定输出信号的最高带宽(根据奈奎斯特准则,1GSPS采样率可生成最高500MHz的模拟信号),满足高速信号驱动需求。 • 输出电压范围:±1V~±5V(可通过外围运放调理至目标范围,如OPA847高速运放)。 3. 需完成的工作 1. 确定DA输出信号类型(正弦波、方波、自定义波形),通过FPGA向DA发送波形数据(需匹配DA的数据接口,如LVDS、CMOS); 2. 设计DA的电源电路(需单独模拟电源、数字电源,降低串扰); 3. 增加信号调理电路(用高速运放缓冲、放大,确保输出信号无失真,带宽匹配DA采样率)。 二、采集模块(AD转换+电阻电压采集) 核心功能:采集电阻两端的模拟电压信号,将其转换为数字信号,传输至FPGA进行处理。 1. 核心芯片选型 (1)AD转换芯片 • 推荐芯片:ADI AD9249(14位)、TI ADS8900B(16位) ◦ 选型理由:高速、高精度模数转换器(AD),支持同步采样,适配“电阻电压采集”的直流/低频信号场景,且抗干扰能力强。 (2)信号调理辅助芯片 • 仪表放大器:TI INA826(低失调电压、高共模抑制比) ◦ 作用:电阻两端电压可能为小信号(如mV级),需先通过仪表放大器放大(放大倍数1~1000倍可调),再输入AD,避免AD量化误差占比过高。 • 采样保持电路:ADI AD8436(高速采样保持) ◦ 作用:若电阻电压存在波动,通过采样保持电路锁定瞬时电压,确保AD转换时信号稳定。 2. 关键参数 • AD分辨率(位数):14-16位 ◦ 16位AD(如ADS8900B):电压量化精度更高(假设输入范围0~5V,16位AD的量化步长≈76μV),适合小信号采集; ◦ 14位AD(如AD9249):量化步长≈390μV,若电阻电压信号较大(如V级),精度可满足需求,成本更低。 • AD采样率:≥100MSPS(百兆采样/秒) ◦ 匹配电阻电压信号的变化速率(若为直流或低频信号,10MSPS即可;若为动态信号,需提升至100MSPS以上)。 • 精度指标: ◦ 失调误差:≤±0.5LSB(确保零输入时AD输出无明显偏差); ◦ 积分非线性(INL):≤±1LSB(保证全量程内转换精度稳定)。 3. 需完成的工作 1. 设计电阻电压采集电路:将电阻两端引出,通过差分方式接入仪表放大器(避免共模干扰,如电源噪声); 2. 确定AD的输入范围(如0~5V、±2.5V),通过仪表放大器将电阻电压调理至AD输入范围内; 3. 设计AD与FPGA的接口电路(如SPI、LVDS,优先选LVDS,传输速率更高,抗干扰); 4. 增加AD的校准电路(通过FPGA控制,定期校准AD的失调误差和增益误差,提升长期稳定性)。 三、信号处理模块(FPGA) 核心功能:接收DA的控制指令、AD的数字信号,完成信号的逻辑处理(无需关注具体算法,仅需保障硬件适配)。 1. FPGA芯片选型 • 推荐芯片:Xilinx Kintex-7(XC7K325T)、Altera Cyclone V(5CGXFC7D7F31C6) ◦ 选型理由:中高端FPGA,具备足够的逻辑资源(查找表、触发器)和高速接口资源,支持DA/AD的高速数据传输,且主频满足需求。 2. 关键参数 • 主频:≥200MHz(核心逻辑时钟) ◦ 确保FPGA能实时处理DA的波形数据发送(如1GSPS DA需FPGA提供高速数据通道)和AD的数字信号接收(100MSPS AD需FPGA每10ns处理1个数据); ◦ 高速接口时钟(如LVDS):≥500MHz(匹配DA/AD的高速数据接口速率)。 • 接口资源: ◦ 高速差分接口(LVDS):至少8对(用于DA/AD的数据传输); ◦ 时钟管理模块(PLL):至少2个(为DA、AD、FPGA核心逻辑提供稳定时钟,降低时钟抖动)。 3. 需完成的工作 1. 设计FPGA的电源电路(核心电压1.0V~1.2V,IO电压3.3V/2.5V,需多组电源,独立供电); 2. 开发FPGA的接口逻辑: ◦ DA控制逻辑:生成DA的采样时钟、复位信号,按DA时序发送波形数据; ◦ AD接收逻辑:接收AD的转换数据,进行数据缓存(如FIFO),避免数据丢失; 3. 设计FPGA的时钟树:通过PLL生成DA(如1GSPS采样时钟)、AD(如100MSPS采样时钟)、核心逻辑(200MHz)所需时钟,确保时钟相位稳定; 4. 预留外部接口(如PCIe、以太网):用于将处理后的数据上传至上位机(若需后续分析)。
09-05
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值