Strip Chart Recorder C/C++ Source Code Example
Strip Chart Recorder C/C++ Source Code Example of Using DSPower®-HwLib or
DirectDSP™ software
Below is a C/C++ source code example of using off-the-shelf DSP/analog hardware to perform a basic
strip chart recorder function. Note the use of the DSShowHardwareSelector call to bring up a
list of supported hardware.
#include <windows.h>
#include <stdlib.h>
// DSPower®-HwLib or DirectDSP™ software package include files
#include "enmgr.h" // Engine Manager
#include "hwmgr.h" // Hardware Manager
#include "hwlib.h" // Hardware Library
// some global variables
...note: many variable and type declarations omitted for brevity...
HANDLE FAR hEngine = NULL; // declare at least one variable FAR to limit this to
a single-instance program
HANDLE hBoard = NULL;
BOOL fBoardInitialized = FALSE;
char szBoard[30];
UINT uMemArch;
WORD wBoardClass;
DWORD dwMemSize,dwBufferBaseAddr;
HWND hwndStrip = NULL;
float FsActual = 8000.0;
DWORD dwFsMode;
short int NumChan = 1;
short int ChanList[MAXCHAN] = { 0, 0, 0, 0, 0, 0, 0, 0 };
DWORD dwBuflen = 4096; // default buffer size is small to accommodate
EVM/eval type boards
DWORD dwNumAcquired = 0L;
DWORD dwNumRequested = 40000L;
BOOL fRunning = FALSE;
char pathstrWvfrm[144] = "test";
int PASCAL WinMain (HANDLE hInstance, HANDLE hPrevInst, LPSTR lpszCmdParam, int
nCmdShow) {
// show hardware selector dialog (Hardware Manager); returns board
designator string
// note: if hardware choice is fixed, then skip this call and use correct
board designator string
// in DSAssignBoard call below
if (DSShowHardwareSelectorDlg(NULL, szBoard) == IDCANCEL) goto Cleanup;
// comment this call if status messages from HwLib should not be visible
DSShowEngMgrStatusWindow(); // turn on debug status window in Engine Manager
// create window for signal display, to hold toolbars, and to receive BUFRDY
//and other messages from engine
if (!hPrevInst) {
...note: window class registration and CreateWindow code omitted
for brevity...
hwndStrip = CreateWindow(...
ShowWindow(hwndStrip, nCmdShow);
UpdateWindow(hwndStrip);
}
// open engine
hEngine = DSEngineOpen(DS_EO_HSM, NULL); // first try to open
Hypersignal-Macro or
// Hypersignal-Macro EX as engine
if (!hEngine) {
hEngine = DSEngineOpen(DS_EO_HSA, NULL); // if that doesn't work, try
// Hypersignal-Acoustic
if (!hEngine) {
itoa(DSGetHWLibErrorStatus(), tmpstr, 10);
lstrcat(tmpstr, " is error code; DSEngineOpen failed");
MessageBox(NULL, tmpstr, szApp, MB_OK);
goto Cleanup;
}
}
// assign a board handle: engine handle, board designator, bus type, IO
base addr, Mem base addr
hBoard = DSAssignBoard(hEngine, szBoard, NULL, NULL, NULL);
// initialize the board; make sure it's installed, reset all processors
fBoardInitialized = DSInitBoard(hBoard);
if (!fBoardInitialized) {
itoa(DSGetEngineErrorStatus(hEngine), tmpstr, 10);
lstrcat(tmpstr, " is error code; DSInitBoard failed");
MessageBox(NULL, tmpstr, szApp, MB_OK);
goto Cleanup;
}
// interrogate engine for board type values
wBoardClass = DSGetBoardClass(hBoard);
// get memory architecture
uMemArch = DSGetMemArch(hBoard);
if (uMemArch == NULL) {
MessageBox(NULL,"DSGetMemArch failed", szApp, MB_OK);
goto Cleanup;
}
// load executable DSP code file (usually a COFF file produced by DSP
manufacturer's linker)
if (!DSLoadFileProcessor(hBoard, NULL, 1)) { // load default file for
the board type (processor 0 only)
MessageBox(NULL, "DSLoadFileBoard: problem loading file", szApp, MB_OK);
goto Cleanup;
}
// get the memory size, (note that this currently has to be done after LoadFile)
dwMemSize = DSGetMemSize(hBoard, 0x01); // processor 0 only
// get address of input time domain data
dwBufferBaseAddr = DSGetDSPProperty(hBoard, DSP_TIMDATAADDR);
// reset DSP board (should already be in reset state; processor 0 only)
DSResetProcessor(hBoard, 0x01);
// register window to receive BUFRDY messages
DSRegisterEngineMsgWnd(hEngine, DS_REMW_SETDSPDATARDYMSG, hwndStrip);
// register callback window for buffer ready messages
// time to give up and become a Windows animal
while (GetMessage(&msg, NULL, 0, 0)) {
TranslateMessage (&msg);
DispatchMessage (&msg);
}
hwndStrip = NULL;
Cleanup:
if (hwndStrip != NULL) DestroyWindow(hwndStrip);
if (fBoardInitialized) DSDisableBoard(hBoard); // disable board
(all processors)
if (hBoard != NULL) DSFreeBoard(hBoard);
if (hEngine != NULL) DSEngineClose(hEngine);
return msg.wParam;
}
long WINAPI _export WndProc(HWND hwnd, WORD wMsg, WORD wParam, LONG lParam) {
...note: most variable declarations omitted for brevity...
static HGLOBAL hBuf = NULL;
static short int nCurBuf = 0;
static BOOL FirstBufferRcvd = FALSE;
switch (wMsg) {
...note: other message-processing code omitted for brevity...
case WM_CREATE:
// allocate some memory to hold board buffers
hBuf = GlobalAlloc(GMEM_ZEROINIT | GMEM_MOVEABLE,
dwBuflen*sizeof(short));
break;
case WM_COMMAND:
switch (wParam) {
case DS_ATC_RUNBOARD:
if (fRunning) break; // already running
// send down some important variables (needed if a default
Hypersignal DSP file is being used)
DSPutDSPProperty(hBoard, DSP_BOARDCLASS, wBoardClass & 0x0ff);
// main type in low byte
DSPutDSPProperty(hBoard, DSP_BOARDSUBCLASS, wBoardClass >> 8);
// subtype in high byte
DSPutDSPProperty(hBoard, DSP_OPMODE, 0);
// acquisition is mode 0
if (DSGetBoardInfo(hBoard, DS_GBI_DSPWORDLENGTH) == 24)
nShift = 7; // don't ask; weird efficiency thing required
by DSP5600x boards
else nShift = 0;
DSPutDSPProperty(hBoard, DSP_SCALEIN, 256L << nShift);
DSPutDSPProperty(hBoard, DSP_FILTTYPE1, 0);
// disable trace 1 real-time filter
DSPutDSPProperty(hBoard, DSP_FILTTYPE2, 0);
// disable trace 2 real-time filter
DSPutDSPProperty(hBoard, DSP_TRIGLEVEL, 0);
// free-run triggering
DSPutDSPProperty(hBoard, DSP_TRIGCHANLIST, 0);
DSPutDSPProperty(hBoard, DSP_BUFLEN, dwBuflen);
// buffer size
DSPutDSPProperty(hBoard, DSP_HOSTBUFNUM, 0);
// starting buffer numbers
DSPutDSPProperty(hBoard, DSP_BUFNUM, 0);
DSPutDSPProperty(hBoard, DSP_NUMCHAN, NumChan);
// number of channels
dw = 0;
for (j=0; j= 32) {
fAmp1 = 0.0;
fAmp2 = 0.0;
DSIEEEToDSP(hBoard, DS_DTI_IEEESIZE32, &fAmp1, &dw1, 1);
DSIEEEToDSP(hBoard, DS_DTI_IEEESIZE32, &fAmp2, &dw2, 1);
}
else {
dw1 = 0;
dw2 = 0;
}
DSPutDSPProperty(hBoard, DSP_MAXVAL1, dw1);
// init max. amplitude values maintained by acquisition driver
DSPutDSPProperty(hBoard, DSP_MAXVAL2, dw2);
// determine sampling rate ctrl. reg. value, and actual rate
(closest rate possible to desired);
// CalcSampFreq returns ctrl. reg. value directly, uses ptr to
return actual sampling frequency (in Hz)
dwFsMode = DSCalcSampFreq(hBoard, FsActual, NumChan,
(short far*)&ChanList, &FsActual);
DSPutDSPProperty(hBoard, DSP_FSMODE, dwFsMode);
// sampling rate control register (mode value)
DSPutDSPProperty(hBoard, DSP_FSVALUE, *((long*)&FsActual));
// actual sampling rate (in Hz)
// reset counters
dwNumAcquired = 0;
// reset waveform file
p = _fstrchr(pathstrWvfrm, '\\');
if (p == NULL) { // if no user-specified path info...
DSGetEngineWaveformPath(hEngine, pathstr);
lstrcat(pathstr, "\\");
lstrcat(pathstr, pathstrWvfrm);
}
else lstrcpy(pathstr, pathstrWvfrm);
p = _fstrchr(pathstr, '.');
if (p == NULL) lstrcat(pathstr, ".tim");
hFile = DSOpenFile(pathstr, EXISTS);
if (hFile != NULL) {
lstrcpy(tmpstr, "Waveform file ");
lstrcat(tmpstr, pathstr);
lstrcat(tmpstr, " already exists; overwrite?");
j = MessageBox(hwnd, tmpstr, szApp, MB_ICONQUESTION |
MB_YESNO);
if (j == IDNO) {
hFile = NULL;
break;
}
}
hFile = DSOpenFile(pathstr, CREATE);
if (hFile == NULL) MessageBox(hwnd, "Waveform file could
not be created", szApp, MB_ICONEXCLAMATION);
// initialize waveform file header
DSInitWvfrmHeader(hFile, HYPLONG); // Hypersignal format;
long header
DSSetWvfrmHeader(hFile, DATAPREC, FIXED | 16);
DSSetWvfrmHeader(hFile, NUMCHAN, NumChan);
DSSetWvfrmHeader(hFile, SAMPFREQ, (long)FsActual);
DSSetWvfrmHeader(hFile, EXPONENT, 0);
DSSetWvfrmHeader(hFile, FRMLEN, dwBuflen);
DSWriteWvfrmHeader(hFile); // leave file pos at end of header,
start of data
// run board (processor 0 only)
DSRunProcessor(hBoard, 0x01);
// initialize wait for buffer
DSWaitForBuffer(hBoard, 0, NULL, DS_WFB_POLLED);
// update status
fRunning = TRUE;
SendMessage(hwnd, WM_UPDATESTATUS, 0, 0L);
break;
case DS_ATC_STOPBOARD:
if (fRunning) { // not stopped yet?
DSResetProcessor(hBoard, 0x01); // data acquired?
stop board
fRunning = FALSE;
}
SendMessage(hwnd, WM_UPDATESTATUS, 0, 0L); // update status bar
if (hFile != NULL) { // update waveform header with maximum
amplitude and length in samples
// record max amplitude
dw1 = DSGetDSPProperty(hBoard, DSP_MAXVAL1);
dw2 = DSGetDSPProperty(hBoard, DSP_MAXVAL2);
if (DSGetBoardInfo(hBoard, DS_GBI_DSPWORDLENGTH) >= 32)
{ // 32-bit floating-point board?
DSDSPToIEEE(hBoard, DS_DTI_IEEESIZE32, &dw1, &fAmp1, 1);
DSDSPToIEEE(hBoard, DS_DTI_IEEESIZE32, &dw2, &fAmp2, 1);
fAmp = max(fabs(fAmp1), fabs(fAmp2));
}
else fAmp = max(labs((long)dw1), labs((long)dw2));
// 16-bit or 24-bit fixed-point board
fAmp = max(min(fAmp, 32767.0), 0.0);
DSUpdateHeader(hFile, WVFRMLEN, dwNumAcquired*NumChan);
DSUpdateHeader(hFile, MAXAMP, (long)&fAmp); // default;
update at end of run
DSCloseFile(hFile);
hFile = NULL;
}
break;
}
break;
case WM_DSPENGINE_BUFRDY: // message sent by DSP engine when data
buffer is ready
// stop board ASAP if acquired data meets request (especially helpful
in cases where very high sampling rate is being used with one
large buffer)
dwNumAcquired += dwBuflen/NumChan;
if (dwNumAcquired >= dwNumRequested) {
DSResetProcessor(hBoard, 0x01); // stop board
fRunning = FALSE;
SendMessage(hwnd, WM_UPDATESTATUS, 0, 0L); // update status
}
pBuf = (short far*)GlobalLock(hBuf);
// transfer below illustrates two common type of DSP memory architectures
if (uMemArch == DS_GMA_VECTOR) { // vector data memory
if (nCurBuf == 0)
uStatus = DSGetMem(hBoard, DS_GM_VECTOR_DATA_X, dwBufferBaseAddr,
DS_GM_SIZE16, pBuf, dwBuflen);
else
uStatus = DSGetMem(hBoard, DS_GM_VECTOR_DATA_Y, dwBufferBaseAddr,
DS_GM_SIZE16, pBuf, dwBuflen);
}
else { // linear data/prog memory, or modified harvard arch. with
linear data memory
uStatus = DSGetMem(hBoard, DS_GM_LINEAR_DATA_RT,
dwBufferBaseAddr+nCurBuf*dwBuflen, DS_GM_SIZE16, pBuf, dwBuflen);
}
GlobalUnlock(hBuf);
if (!uStatus) {
DSResetProcessor(hBoard, 0x01); // error; stop board
MessageBox(hwnd, "DSGetMem: problem with point transfer",
szApp, MB_OK);
goto Done;
}
// allocate temp area for disk write (to tolerate some overflow
of messages)
hBufWr = GlobalAlloc(GMEM_ZEROINIT | GMEM_MOVEABLE,
dwBuflen*sizeof(short));
if (hBufWr != NULL) {
pBufWr = (short far*)GlobalLock(hBufWr);
pBuf = (short far*)GlobalLock(hBuf);
hmemcpy(pBufWr, pBuf, dwBuflen*sizeof(short));
GlobalUnlock(hBuf);
GlobalUnlock(hBufWr);
}
FirstBufferRcvd = TRUE; // at least one buffer received
if (dwNumAcquired < dwNumRequested) {
// wait for next buffer as long as window is not minimized
nCurBuf ^= 1; // switch buffers
DSPutDSPProperty(hBoard, DSP_HOSTBUFNUM, nCurBuf);
// write the new buffer #
if (!IsIconic(hwnd)) DSWaitForBuffer(hBoard, nCurBuf,
NULL, DS_WFB_POLLED);
}
if (uStatus) {
// write new data to waveform file
if (hBufWr != NULL && hFile != NULL) {
pBufWr = (short far*)GlobalLock(hBufWr);
nVerifyState = getverify(); // ensure that write-verify
is off (more speed, Scotty...)
setverify(0);
DSWriteWvfrmData(hFile, pBufWr, dwBuflen/NumChan, FIXED|16);
setverify(nVerifyState); // restore write-verify state
GlobalUnlock(hBufWr);
GlobalFree(hBufWr);
}
// update the screen with new data
UpdateNeeded = TRUE;
InvalidateRect(hwnd, NULL, FALSE);
}
if (dwNumAcquired >= dwNumRequested) {
Done: SendMessage(hwnd, WM_COMMAND, DS_ATC_STOPBOARD, NULL);
}
SendMessage(hwnd, WM_UPDATESTATUS, 0, 0L);
break;
default:
return DefWindowProc(hwnd, wMsg, wParam, lParam);
}
return 0L;
}
|