本项目旨在模拟一个真实的频谱分析仪,它能够为通信信号处理应用提供服务,例如FM接收器。通过使用傅里叶域分析,该应用能够实时监控信号频谱。为了实现这一目标,应用程序将通过进程间通信(IPC)方法与本项目进行通信。需要创建一个预定义结构的共享缓冲区,然后指定一个采样率(Fs)、刷新率(Rf),最后以实时方式向发送信号的时间域样本。频谱分析仪将使用这些信息来计算信号频谱并显示它,而且是实时的。
本频谱分析仪的主要特点是其高速和低CPU负载。为了实现这一目标,利用了Direct3D的强大功能。因此,大部分渲染工作由GPU完成。必须拥有一块性能良好的显卡才能充分利用这一优势。此外,还使用了Intel® Math Kernel Library来执行傅里叶变换和其他繁重的数学运算。如果拥有Intel®处理器,那么已经准备好了!有关更多详细信息,请参见备注。
应用程序的其他重要特性包括:
在继续之前,请下载并运行演示应用程序。然后,将对频谱分析仪的工作原理有一个基本的了解。演示包包括两个项目:
后者项目生成具有1 KHz采样率的QPSK信道的时间域样本。然后,它将执行前者项目,即频谱分析仪。
信号生成项目不断地将样本数据存储在共享缓冲区中,而频谱分析仪则读取共享缓冲区以计算并显示信号的频谱。
在下面的解释中,将遇到几个用括号包围的数字。这些标记指的是源代码中的位置。例如,{1}意味着可以在下载的源代码中搜索/*{1}*/,并且可以在那里找到相关代码。
如前所述,应用程序将通过共享缓冲区与进行通信。要定义此缓冲区并使用以下函数,必须将频谱分析仪项目的两个头文件包含到项目中:
下面,可以看到在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。有什么想法吗?