基于 DirectX 的强大频谱分析仪






4.86/5 (19投票s)
快速、美观、真实且易于连接到您的应用程序
- 下载源代码 - 634 KB
- 下载演示部分 1 - 3.71 MB
- 下载演示部分 2 - 3.71 MB
- 下载演示部分 3 - 3.71 MB
- 下载演示部分 4 - 3.71 MB
- 下载演示部分 5 - 232 KB
引言
本项目模拟了一个真实的频谱分析仪。任何涉及通信信号的应用程序,例如 FM 接收器,都可以使用此应用程序来监视傅里叶域中的信号频谱。事实上,您的应用程序将使用进程间通信 (IPC) 方法与我的项目进行通信。您需要创建一个具有预定义结构的共享缓冲区。然后,您指定采样率 (Fs)、刷新率 (Rf),最后,您将实时信号的时域样本发送给我。频谱分析仪将使用这些信息来计算信号频谱并实时显示它。
我的频谱分析仪的主要特点是速度快且 CPU 负载低。为了实现这一目标,我利用了 Direct3D 的强大功能。因此,大部分渲染工作都由 GPU 完成。您必须拥有一个好的显卡才能受益。此外,我使用了 Intel® 数学核心库 来执行傅里叶变换和其他繁重的数学运算。如果您有 Intel® 处理器,那就没问题了!更多详情请参阅说明。
该应用程序的其他重要功能是
- 全屏模式 (Alt + Enter)
- 放大和缩小
- 原地垂直标尺
- 视图移动(鼠标左键),缩放后可用
- 峰值保持
背景
在继续之前,请下载并运行演示应用程序。这样,您将对频谱分析仪的工作原理有一个基本的了解。演示包包含两个项目:《频谱分析仪》和《信号生成》。后者项目生成具有 1 KHz 采样率的 QPSK 频道的时域样本。然后,它将执行前者项目,即频谱分析仪。《信号生成》会不断地将样本数据存储在共享缓冲区中,《频谱分析仪》则读取共享缓冲区来计算和显示信号的频谱。
我使用了一种有趣的算法来处理缩放、移动和其他类似操作。正如您可能从 Direct3D 中所知,您必须指定三个矩阵来描述场景的几何形状
- 世界变换:定义对象在世界中的位置
- 视图变换:告诉相机在世界中的位置和方向
- 投影变换:定义相机的视口(例如,其水平和垂直视角)
为了处理放大和缩小操作,我适当地改变了投影矩阵。例如,当您水平放大时,我减小了相机的水平视口角度。为了处理移动,我适当地改变了视图矩阵。例如,当您将场景向右移动时,我改变了视口矩阵,就像相机向左移动一样。
Using the Code
在接下来的解释中,您会遇到几个带有括号的数字。这些标记指的是源代码中的某个位置。例如,{1} 表示您可以在下载的源代码中搜索 /*{1}*/
,并在那里找到相关的代码。
如前所述,您的应用程序将通过共享缓冲区与我通信。要获取此缓冲区的定义并使用以下函数,您必须将《频谱分析仪》项目的两个头文件包含到您的项目中:Shared Buffer.h 和 IPC.h {7}。下面是 Shared Buffer.h 中定义的共享缓冲区的结构
#define BULK_SIZE 1000 // Buffer bulk size
#define FFT_LENGTH 512 // FFT length
struct SHARED_BUFFER
{
// Time-domain samples (float complex)
Complex8 Sample[2 * BULK_SIZE];
float Fs; // Sample rate [Hz]
float Rf; // Refresh rate [Hz]
int nAverage; // Number of frames to include in averaging
HWND hWnd; // Oscilloscope Window
};
此结构的一个全局实例在 IPC.h 中创建。
SHARED_BUFFER *pSharedBuffer = NULL;
您的应用程序必须通过调用 CreateMapFile()
{1} 来创建共享缓冲区。然后,您初始化采样率 pSharedBuffer->Fs
、刷新率 pSharedBuffer->Rf
和用于平均的帧数 pSharedBuffer->nAverage
{2}。请注意,pSharedBuffer->nAverage
不应超过 20。
if(!CreateMapFile())
goto err; // Fatal error
pSharedBuffer->nAverage = 5;
pSharedBuffer->Fs = BULK_SIZE;
pSharedBuffer->Rf = 25;
然后,您必须执行频谱分析仪。请务必指定可执行文件的正确路径 {6}。
STARTUPINFO si;
PROCESS_INFORMATION pi;
ZeroMemory(&si, sizeof(si));
ZeroMemory(&pi, sizeof(pi));
CreateProcess("Spectrum Analyzer.exe", NULL, NULL, NULL, TRUE,
NULL, NULL, NULL, &si, &pi);
之后,您需要同步采样率持续将采样数据填充到 pSharedBuffer->Sample
中 {3},{4}。pSharedBuffer->Sample
是一个循环缓冲区;也就是说,当您到达其末尾时,必须从其开头继续写入。在演示应用程序中,TimeProc
负责将样本数据写入共享缓冲区。它由多媒体计时器每秒调用一次,写入 1000 个新数据(等于采样率){3},{4}。
// A global variable filled with QPSK samples
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;
i = min(BULK_SIZE, 2 * BULK_SIZE - k);
// fill pSharedBuffer->Sample with samples
CopyMemory(pSharedBuffer->Sample + k, Signal + j, i * sizeof(Complex8));
// Wrap to the beginning of pSharedBuffer->Sample
// and store remaining samples
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()
{5} 来销毁共享缓冲区来轻松关闭它。
PostMessage(pSharedBuffer->hWnd, WM_QUIT, 0, 0);
CloseMapFile();
备注
- 如果您想自定义或编译代码,您必须安装 Direct3D 9.0 SDK 和 Intel® 数学核心库,并将其与 Visual Studio 集成。否则,拥有可执行的频谱分析仪及其库(包含在我的演示应用程序中)就足够了。
- 我在平均方面遇到了问题。它不起作用。当我增加
pSharedBuffer->nAverage
时,频谱曲线并没有变得平滑。我认为平均不仅仅是n个连续帧的总和除以n。您有什么想法吗? - 我无意与 Rohde-Schwarz® 这样的真实频谱分析仪竞争!但是它们有许多有趣的功能也可以添加到此项目中。其中可以列举视频带宽、频率变化和屏幕分辨率。如果您对实现这些功能有任何想法,请告知我。您可以修改代码并通知我,以便我们能够合作开发这个免费项目。我期待您的回复。
历史
- 2009 年 11 月 8 日:发布第一个版本。等待反馈。