Introduction
This project simulates a real spectrum analyzer. Any application that deals with communication signals, such as an FM receiver, can use this application to monitor signal spectrum in Fourier-domain. In fact, your application will communicate with my project using the Inter-Process Communication (IPC) method. You create a shared buffer with a predefined structure. Then, you specify a sample rate (Fs), a refresh rate (Rf), and finally, you send me time-domain samples of the signal in a real-time manner. The spectrum analyzer will use these information to compute signal spectrum and display it, yet in a real-time manner.
The principal feature of my spectrum analyzer is its high speed and low load on CPU. To achieve this goal, I took advantage of Direct3D power. So, most of the rendering workload is carried out by the GPU. You must have a good graphics card to use the benefits. In addition, I used the Intel® Math Kernel Library to perform Fourier transformation and other heavy mathematical operations. If you have an Intel® processor, you are set! See remarks for more details.
Other important features of the application are:
- Full screen mode (Alt + Enter)
- Zoom-in and zoom-out
- In-place vertical ruler
- Movement of the view (left mouse button), which works after zooming-in
- Peak hold
Background
Before proceeding, please download and run the demo application. Then, you will have a basic understanding of how the spectrum analyzer works. The demo package consists of two projects: Spectrum Analyzer and Signal Generation. The latter project generates the time-domain samples of a QPSK channel having a sample rate of 1 KHz. Then, it will execute the former project, which is the spectrum analyzer. Signal Generation continually stores sample data in a shared buffer, and Spectrum Analyzer reads the shared buffer to calculate and display the spectrum of the signal.
I used an interesting algorithm to handle zoom-in, zoom-out, movement, and other similar operations. As you may know from Direct3D, you have to specify three matrices to describe the geometry of the scene:
- World Transform: which defines the position of objects within the world
- View Transform: which tells the position and orientation of the camera within the world
- Projection Transform: which defines the camera viewport (e.g., its horizontal and vertical view angles)
To handle zoom-in and zoom-out operations, I change the projection matrix appropriately. For example, when you zoom-in horizontally, I decrease the horizontal viewport angle of the camera. To handle movements, I change the view matrix appropriately. For example, when you move the scene to the right, I change the viewport matrix as if the camera moves to the left.
Using the Code
In the following explanation, you will encounter several numbers surrounded by brackets. These marks refer to a location within the source code. For example, {1} means that you can search for
within the downloaded source, and the related code can be found there.
As I mentioned earlier, your application will communicate with me using a shared buffer. To have the definition of this buffer and to use the following functions, you must include two header files of the Spectrum Analyzer project to your project: Shared Buffer.h and IPC.h {7}. Below, you see the structure of the shared buffer, defined in Shared Buffer.h:
#define BULK_SIZE 1000 #define FFT_LENGTH 512
struct SHARED_BUFFER
{
Complex8 Sample[2 * BULK_SIZE];
float Fs; float Rf; int nAverage;
HWND hWnd; };
A global instance of this structure is created within IPC.h:
SHARED_BUFFER *pSharedBuffer = NULL;
The shared buffer must be created by your application by calling CreateMapFile()
{1}. Then, you initialize the sample rate pSharedBuffer->Fs
, the refresh rate pSharedBuffer->Rf
, and the number of frames to include in averaging pSharedBuffer->nAverage
{2}. Note that pSharedBuffer->nAverage
should not exceed 20.
if(!CreateMapFile())
goto err; pSharedBuffer->nAverage = 5;
pSharedBuffer->Fs = BULK_SIZE;
pSharedBuffer->Rf = 25;
Then, you have to execute the spectrum analyzer. Be careful to state the correct path to the executable file {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);
After that, you continually fill pSharedBuffer->Sample
with sample data in sync with the sample rate {3},{4}. pSharedBuffer->Sample
is a circular buffer; i.e., whenever you reach the end of it, you will have to continue to write from its beginning. In the demo application, TimeProc
is responsible to write sample data in the shared buffer. It is called by a multimedia timer every second to write 1000 new data (which equals the sample rate) {3},{4}.
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);
CopyMemory(pSharedBuffer->Sample + k, Signal + j, i * sizeof(Complex8));
CopyMemory(pSharedBuffer->Sample, Signal + j + i,
(BULK_SIZE - i) * sizeof(Complex8));
j = (j + BULK_SIZE) % SIGNAL_LENGTH;
k = (k + BULK_SIZE) % (2 * BULK_SIZE);
}
When you are finished working with the spectrum analyzer, you can close it easily by sending a WM_QUIT
message to its window and then destroying the shared buffer by calling CloseMapFile()
{5}.
PostMessage(pSharedBuffer->hWnd, WM_QUIT, 0, 0);
CloseMapFile();
Remarks
- In case you want to customize or compile the code, you must have Direct3D 9.0 SDK and Intel® Math Kernel Library installed and integrated with Visual Studio. Elsewhere, having the executable spectrum analyzer and its libraries (included in my demo application) is enough.
- I have a problem with averaging. It doesn't work. When I increase
pSharedBuffer->nAverage
, the spectrum curve does not become smooth. I think averaging is not just adding n consecutive frames divided by n. Got any ideas?
- I'm not going to compete with a real spectrum analyzer like a Rohde-Schwarz® one! But they have many interesting features that can also be added to this project. Among them, video bandwidth, frequency change, and screen resolution can be enumerated. If you have any ideas about implementing these features, please notify me about it. You can change the code and inform me to collaborate on this free project. I'm looking forward to hearing from you.
History
- November 8, 2009: First version released. Waiting for feedback.