实时频谱分析仪模拟项目

本项目旨在模拟一个真实的频谱分析仪,它能够为通信信号处理应用提供服务,例如FM接收器。通过使用傅里叶域分析,该应用能够实时监控信号频谱。为了实现这一目标,应用程序将通过进程间通信(IPC)方法与本项目进行通信。需要创建一个预定义结构的共享缓冲区,然后指定一个采样率(Fs)、刷新率(Rf),最后以实时方式向发送信号的时间域样本。频谱分析仪将使用这些信息来计算信号频谱并显示它,而且是实时的。

本频谱分析仪的主要特点是其高速和低CPU负载。为了实现这一目标,利用了Direct3D的强大功能。因此,大部分渲染工作由GPU完成。必须拥有一块性能良好的显卡才能充分利用这一优势。此外,还使用了Intel® Math Kernel Library来执行傅里叶变换和其他繁重的数学运算。如果拥有Intel®处理器,那么已经准备好了!有关更多详细信息,请参见备注。

应用程序的其他重要特性包括:

  • 全屏模式(Alt + Enter)
  • 放大和缩小
  • 内置垂直标尺
  • 视图移动(左键点击),在放大后有效
  • 峰值保持

在继续之前,请下载并运行演示应用程序。然后,将对频谱分析仪的工作原理有一个基本的了解。演示包包括两个项目:

  • 频谱分析仪
  • 信号生成

后者项目生成具有1 KHz采样率的QPSK信道的时间域样本。然后,它将执行前者项目,即频谱分析仪。

信号生成项目不断地将样本数据存储在共享缓冲区中,而频谱分析仪则读取共享缓冲区以计算并显示信号的频谱。

使用代码

在下面的解释中,将遇到几个用括号包围的数字。这些标记指的是源代码中的位置。例如,{1}意味着可以在下载的源代码中搜索/*{1}*/,并且可以在那里找到相关代码。

如前所述,应用程序将通过共享缓冲区与进行通信。要定义此缓冲区并使用以下函数,必须将频谱分析仪项目的两个头文件包含到项目中:

  • Shared Buffer.h
  • IPC.h

下面,可以看到在Shared Buffer.h中定义的共享缓冲区的结构:

#define BULK_SIZE 1000 // 缓冲区批量大小 #define FFT_LENGTH 512 // FFT长度 struct SHARED_BUFFER { // 时间域样本(复数) Complex8 Sample[2 * BULK_SIZE]; float Fs; // 采样率 [Hz] float Rf; // 刷新率 [Hz] int nAverage; // 平均帧数 HWND hWnd; // 示波器窗口 };

在IPC.h中创建了此结构的全局实例:

SHARED_BUFFER *pSharedBuffer = NULL;

必须通过调用CreateMapFile()来创建共享缓冲区。然后,初始化采样率pSharedBuffer->Fs、刷新率pSharedBuffer->Rf以及包含在平均中的帧数pSharedBuffer->nAverage。注意,pSharedBuffer->nAverage不应超过20。

if (!CreateMapFile()) goto err; // 致命错误 pSharedBuffer->nAverage = 5; pSharedBuffer->Fs = BULK_SIZE; pSharedBuffer->Rf = 25;

然后,必须执行频谱分析仪。请小心地指定可执行文件的正确路径。

STARTUPINFO si; PROCESS_INFORMATION pi; ZeroMemory(&si, sizeof(si)); ZeroMemory(π, sizeof(pi)); CreateProcess("Spectrum Analyzer.exe", NULL, NULL, NULL, TRUE, NULL, NULL, NULL, &si, π);

之后,需要不断地以采样率同步填充pSharedBuffer->Sample。pSharedBuffer->Sample是一个循环缓冲区;即,每当到达它的末尾时,将不得不继续从它的开始处写入。在演示应用程序中,TimeProc负责在共享缓冲区中写入样本数据。它由多媒体定时器每秒调用一次,以写入1000个新数据(等于采样率)。

// 一个用QPSK样本填充的全局变量 Complex8 Signal[SIGNAL_LENGTH]; void CALLBACK TimeProc(UINT uID, UINT uMsg, DWORD dwUser, DWORD dw1, DWORD dw2) { static int j = 0; static int k = 0; int i = min(BULK_SIZE, 2 * BULK_SIZE - k); // 用样本填充pSharedBuffer->Sample CopyMemory(pSharedBuffer->Sample + k, Signal + j, i * sizeof(Complex8)); // 回绕到pSharedBuffer->Sample的开始 // 并存储剩余的样本 CopyMemory(pSharedBuffer->Sample, Signal + j + i, (BULK_SIZE - i) * sizeof(Complex8)); j = (j + BULK_SIZE) % SIGNAL_LENGTH; k = (k + BULK_SIZE) % (2 * BULK_SIZE); }

当完成与频谱分析仪的工作时,可以通过向其窗口发送WM_QUIT消息,然后调用CloseMapFile()来销毁共享缓冲区,从而轻松地关闭它。

PostMessage(pSharedBuffer->hWnd, WM_QUIT, 0, 0); CloseMapFile();

备注

如果想要自定义或编译代码,必须安装并集成Direct3D 9.0 SDK和Intel® Math Kernel Library到Visual Studio中。另外,拥有可执行的频谱分析仪及其库(包含在演示应用程序中)就足够了。

在使用平均功能时遇到了问题。当增加pSharedBuffer->nAverage时,频谱曲线并没有变得平滑。认为平均不仅仅是将n个连续帧相加然后除以n。有什么想法吗?

沪ICP备2024098111号-1
上海秋旦网络科技中心:上海市奉贤区金大公路8218号1幢 联系电话:17898875485