Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

A Beautiful Oscilloscope Based on DirectX

0.00/5 (No votes)
1 Nov 2009 1  
Fast, real, and easy to connect to your application.

The Oscilloscope, displaying demodulated QPSK data

Introduction

This project is a simple oscilloscope that can be connected to any application. The graphics of the software is based on Direct3D, which makes the scene similar to a physical oscilloscope; and instead of the CPU, much of the workload is performed by the GPU.

If your application deals with digital demodulation, this project is for you. To be specific, if your application demodulates the PSK data, then you can use this software oscilloscope to view the constellation of output data. The oscilloscope can display data of any rate. You just need to implant a little bunch of code to your application, and my executable oscilloscope will communicate with your application through Inter-Process Communication (IPC). If you don't know IPC, don't worry. You just need to copy and paste it to your own code.

Background

Before proceeding, I recommend you to download and run the demo application. This will help you to have a basic understanding of how the oscilloscope looks and how it connects to a data-source (i.e., your application). The demo application contains a data-source 'Signal Generation.exe' which generates the QPSK data and runs the oscilloscope to simultaneously show the constellation.

Using the Code

I'll explain how to work with the oscilloscope in four steps:

  1. Inside your code, create a shared buffer. This will be the memory to store the constellation data and the symbol rate. Your application will use this buffer to communicate with me. Add the following code to your application. Read the comments for more information:
  2. #include "..\Oscilloscope\SharedBuffer.h"
    
    // Specify the path to SharedBuffer.h
    
    // Global Variable
    SHARED_BUFFER *pSharedBuffer = NULL;
    HANDLE hMapFile = NULL;
    // Global Variable
    
    bool CreateMapFile(void)
    // Call this function from inside
    // the entry-point of your code. Must return true.
    {
        TCHAR szMapName[] = L"MyFileMappingObject";
    
        hMapFile = CreateFileMapping(
            INVALID_HANDLE_VALUE,    // use paging file
            NULL,            // default security 
            PAGE_READWRITE,        // read/write access
            0,            // max. object size 
            sizeof(SHARED_BUFFER),    // buffer size  
            szMapName);        // name of mapping object
    
        if (!hMapFile) 
        { 
            MessageBox(NULL, L"Could not open file mapping object.", 
                       L"Error", MB_OK | MB_ICONERROR);
            return false;
        } 
    
        pSharedBuffer = (SHARED_BUFFER *) MapViewOfFile(
            hMapFile,            // handle to mapping object
            FILE_MAP_ALL_ACCESS,    // read/write permission
            0,
            0,
            sizeof(SHARED_BUFFER));
    
        if (!pSharedBuffer) 
        { 
            MessageBox(NULL, L"Could not map view of file.", 
                             L"Error", MB_OK | MB_ICONERROR);
            return false;
        }
    
        return true;
    }
    
    void CloseMapFile()
    // Call this function just before the exit point of your code.
    {
        if(pSharedBuffer)
            UnmapViewOfFile(pSharedBuffer);
        pSharedBuffer = NULL;
    
        if(hMapFile)
            CloseHandle(hMapFile);
        hMapFile = NULL;
    }
  3. Now, it's your responsibility to store the constellation data (the X and Y components of the sample data) in the shared buffer in sync with the symbol rate. For example, having a symbol rate of 2000, you will have to store 1000 samples in the shared buffer every 0.5 seconds. Samples better be within the range [-1, 1]. pSharedBuffer->x and pSharedBuffer->y are circular buffers; i.e., whenever you reach the end of these buffers, you must wrap to the beginning of them. The oscilloscope also reads these buffers in a circular way. Buffer length is defined by BUFF_SIZE. The structure SHARED_BUFFER is declared in SharedBuffer.h.
  4. Run the oscilloscope to view the constellation. Add the following snippet to your code:
  5. STARTUPINFO si;
    PROCESS_INFORMATION pi;
    ZeroMemory(&si, sizeof(si));
    ZeroMemory(&pi, sizeof(pi));
    CreateProcess(L"Oscilloscope.exe", NULL, NULL, NULL, TRUE, NULL, 
                  NULL, NULL, &si, &pi);
  6. My oscilloscope will initialize pSharedBuffer->hWnd to point to itself. So, when you are finished working with the oscilloscope, you can close it easily:
  7. PostMessage(pSharedBuffer->hWnd, WM_QUIT, 0, 0);

Remarks

  1. To use the executable oscilloscope, no extra file or module is required. But if you want to customize the oscilloscope code, or compile and build it, you must have DirectX 9.0 SDK installed and integrated with Visual Studio.
  2. The first version is for demonstration, and is subject to change and improvements. Your ideas, suggestions, and experiences can help me make the application better. I'm looking forward to hearing from you.

History

  • November 1, 2009: First version uploaded. Waiting for feedback.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here