//------------------------------------------------------------------------------------------
// FastWavePortClient.cpp : Defines the entry point for the console application.
//
// Prototype C++ client application for "Fast Wave Port' Math Processor
// 
// Anthony Cake, March 2003
//
// Compatibility:
// Microsoft Visual C++ 6.0, 7.1, 8.0
// MinGW 'gcc' based compiler (free download from http://www.mingw.org/)
// Compile with: mingw32-c++ -o fastWavePortClient.exe fastwaveportclient.cpp
//------------------------------------------------------------------------------------------
#include "windows.h"
#include <stdlib.h>
#include <stdio.h>
#include <math.h>
#include "MultiPortClient.h"

//------------------------------------------------------------------------------------------
// FastWavePort header, describes various properties of the waveform passed to the user-processing
// function. Also used to carry the properties of the processed waveform back to the DSO.
//
//------------------------------------------------------------------------------------------
// Pentium read-time-stamp-counter instruction.
//#pragma warning(disable:4035)
#ifndef _WIN64
inline unsigned __int64 read64bitCounter()
{
__asm rdtsc
}
#endif
//------------------------------------------------------------------------------------------
bool checkInt16WformHeader(CWaveDescHeader* descHeader)
{
return (descHeader->descType == _int16WformDescriptor && descHeader->headerSize == 
CWaveDescHeaderSize);
}
//------------------------------------------------------------------------------------------
bool checkPmHeader(CParameterDescHeader* descHeader)
{
return (descHeader->descType == _parameterDescriptor && descHeader->headerSize == 
CParameterDescHeaderSize);
}
//------------------------------------------------------------------------------------------
// The buffer size is 80MB (40,000,000 samples, stored as short integers) plus 0x1000 bytes for the header.
const unsigned long HEADER_SIZE = 0x1000;
const unsigned long MEM_MAP_FILE_SIZE_WAVEFORM = 80000000 + CWaveDescHeaderSize;// = 40MSamples, or 80MBytes 
const unsigned long MEM_MAP_FILE_SIZE_PARAMETER = 1000 * CParameterValueSize + CParameterDescHeaderSize; // some space 
int main(int argc, char* argv[])
{
// names based on 'FastWavePort1' name defined in Processor setup.
char szMapFileName1[] = "FMWavePort1FileIn1";
char szMapFileName2[] = "FMWavePort1FileIn2";
// additional parameter input pins (July 2009)
char szMapFileNameP1[] = "FMWavePort1FileInParam1";
char szMapFileNameP2[] = "FMWavePort1FileInParam2";
char szMapFileName3[] = "FMWavePort1FileOut1"; // first parameter output
char szMapFileName4[] = "FMWavePort1FileOut2"; // second parameter output
char szMutexDataAvailableName[] = "FMWavePort1MutexDataAvailable";
char szMutexProcessingCompleteName[] = "FMWavePort1MutexProcessingComplete";
// Associate shared memory file handle value.
HANDLE m_hMMFile1 = CreateFileMappingA (INVALID_HANDLE_VALUE, NULL, PAGE_READWRITE, 0, 
MEM_MAP_FILE_SIZE_WAVEFORM, szMapFileName1);
if(m_hMMFile1 == 0)
{
printf("Unable to create file1 mapping\n");
return 0;
}
// Map a view of this file for writing.
short *m_lpMMFile1 = (short *)MapViewOfFile (m_hMMFile1, FILE_MAP_ALL_ACCESS, 0, 0, 0); 
if(m_lpMMFile1 == 0)
{
printf("Unable to map view of file1\n");
return 0;
}
// Associate shared memory file handle value.
HANDLE m_hMMFile2 = CreateFileMappingA (INVALID_HANDLE_VALUE, NULL, PAGE_READWRITE, 0, 
MEM_MAP_FILE_SIZE_WAVEFORM, szMapFileName2);
if(m_hMMFile2 == 0)
{
printf("Unable to create file2 mapping\n");
return 0;
}
// Map a view of this file for writing.
short *m_lpMMFile2 = (short *)MapViewOfFile (m_hMMFile2, FILE_MAP_ALL_ACCESS, 0, 0, 0); 
if(m_lpMMFile2 == 0)
{
printf("Unable to map view of file2\n");
return 0;
}
// Associate shared memory file handle value for 1st parameter input.
HANDLE m_hMMFileParam1 = CreateFileMappingA (INVALID_HANDLE_VALUE, NULL, 
PAGE_READWRITE
, 0, MEM_MAP_FILE_SIZE_PARAMETER, szMapFileNameP1);
if(m_hMMFileParam1 == 0)
{
printf("Unable to create param file1 mapping\n");
return 0;
}
// Map a view of this file for writing.
short *m_lpMMFileParam1 = (short *)MapViewOfFile (m_hMMFileParam1, FILE_MAP_ALL_ACCESS
, 0, 0, 0); 
if(m_lpMMFileParam1 == 0)
{
printf("Unable to map view of param file1\n");
return 0;
}
// Associate shared memory file handle value for 2nd parameter input.
HANDLE m_hMMFileParam2 = CreateFileMappingA (INVALID_HANDLE_VALUE, NULL, 
PAGE_READWRITE
, 0, MEM_MAP_FILE_SIZE_PARAMETER, szMapFileNameP2);
if(m_hMMFileParam2 == 0)
{
printf("Unable to create param file2 mapping\n");
return 0;
}
// Map a view of this file for writing.
short *m_lpMMFileParam2 = (short *)MapViewOfFile (m_hMMFileParam2, FILE_MAP_ALL_ACCESS
, 0, 0, 0); 
if(m_lpMMFileParam2 == 0)
{
printf("Unable to map view of param file2\n");
return 0;
}
// Associate shared memory file handle value.
HANDLE m_hMMFile3 = CreateFileMappingA (INVALID_HANDLE_VALUE, NULL, PAGE_READWRITE, 0, 
MEM_MAP_FILE_SIZE_PARAMETER, szMapFileName3);
if(m_hMMFile3 == 0)
{
printf("Unable to create file3 mapping\n");
return 0;
}
// Map a view of this file for writing.
short *m_lpMMFile3 = (short *)MapViewOfFile (m_hMMFile3, FILE_MAP_ALL_ACCESS, 0, 0, 0); 
if(m_lpMMFile3 == 0)
{
printf("Unable to map view of file3\n");
return 0;
}
// Associate shared memory file handle value.
HANDLE m_hMMFile4 = CreateFileMappingA (INVALID_HANDLE_VALUE, NULL, PAGE_READWRITE, 0, 
MEM_MAP_FILE_SIZE_PARAMETER, szMapFileName3);
if(m_hMMFile4 == 0)
{
printf("Unable to create file4 mapping\n");
return 0;
}
// Map a view of this file for writing.
short *m_lpMMFile4 = (short *)MapViewOfFile (m_hMMFile4, FILE_MAP_ALL_ACCESS, 0, 0, 0); 
if(m_lpMMFile3 == 0)
{
printf("Unable to map view of file4\n");
return 0;
}
// create/open events used for synchronization
// if the client app. was run before the scope then these events will be created, if the scope was run first then these events
// will just be opened
HANDLE m_hDataAvailable = CreateEventA(NULL, FALSE, FALSE /* initial state */, 
szMutexDataAvailableName);
HANDLE m_hProcessingComplete = CreateEventA(NULL, FALSE, FALSE /* initial state */, 
szMutexProcessingCompleteName);
if(m_hDataAvailable == 0 || m_hProcessingComplete == 0)
{
printf("Unable to open events\n");
return 0;
}
// install appropriate descriptors for two waveform results
{
CWaveDescHeader* descWaveformHeader[2];
descWaveformHeader[0] = (CWaveDescHeader *) m_lpMMFile1;
descWaveformHeader[1] = (CWaveDescHeader *) m_lpMMFile2;
for(int i = 0; i < 2; ++i)
{
descWaveformHeader[i]->descType = _int16WformDescriptor;
descWaveformHeader[i]->descVersion = 1;
descWaveformHeader[i]->headerSize = CWaveDescHeaderSize;
descWaveformHeader[i]->windowSize = MEM_MAP_FILE_SIZE_WAVEFORM;
}
// two parameter inputs need preparation (if processor is to use them)
CParameterDescHeader* descInputParamHeader[2];
descInputParamHeader[0] = (CParameterDescHeader *) m_lpMMFileParam1;
descInputParamHeader[1] = (CParameterDescHeader *) m_lpMMFileParam2;
for(int i = 0; i < 2; ++i)
{
descInputParamHeader[i]->descType = _parameterDescriptor;
descInputParamHeader[i]->descVersion = 1;
descInputParamHeader[i]->headerSize = CParameterDescHeaderSize;
descInputParamHeader[i]->windowSize = MEM_MAP_FILE_SIZE_PARAMETER;
}
// two parameter outputs for starters ... up to 8 are possible
CParameterDescHeader* descParamHeader[2];
descParamHeader[0] = (CParameterDescHeader *) m_lpMMFile3;
descParamHeader[1] = (CParameterDescHeader *) m_lpMMFile4;
for(int i = 0; i < 2; ++i)
{
descParamHeader[i]->descType = _parameterDescriptor;
descParamHeader[i]->descVersion = 1;
descParamHeader[i]->headerSize = CParameterDescHeaderSize;
descParamHeader[i]->windowSize = MEM_MAP_FILE_SIZE_PARAMETER;
}
}
// main loop ... intereacting with the (FastMultiWavePort) processor in the scope
int loopCount = 0;
while(1)
{
if(loopCount % 64 == 0)
printf("\nWaiting.");
else
printf(".");
++loopCount;
// wait an infinite amount of time for data to be available
DWORD waitSuccess = WaitForSingleObject(m_hDataAvailable, INFINITE);
// look for the input parameter output block which should be ready-made ... but may
// or may not be valid (may have inputs connected or not)
int numInputParameterValues[2] = {0, 0};
CParameterValue* myInputValuePointer[2];
myInputValuePointer[0] = 
(CParameterValue*)&m_lpMMFileParam1[CParameterDescHeaderSize/sizeof(short)];
myInputValuePointer[1] = 
(CParameterValue*)&m_lpMMFileParam2[CParameterDescHeaderSize/sizeof(short)];
CParameterDescHeader* descInputParamHeader[2];
descInputParamHeader[0] = (CParameterDescHeader *) m_lpMMFileParam1;
descInputParamHeader[1] = (CParameterDescHeader *) m_lpMMFileParam2;
// check to see if there are input parameters in the two input pins
bool validInputParameterBlock[2] = {false, false};
for(int i = 0; i < 2; ++i)
{
lecDescType descType = descInputParamHeader[i]->descType;
int descVersion = descInputParamHeader[i]->descVersion;
int windowSize = descInputParamHeader[i]->windowSize;
int headerSize = descInputParamHeader[i]->headerSize;
if(descType == _parameterDescriptor && descVersion == 1 && 
headerSize == CParameterDescHeaderSize)
{
validInputParameterBlock[i] = true;
if(descInputParamHeader[i]->flags == LStatus_Valid)
{
// we're going to get some values there
printf("\nValid input parameter %1d block!\n", i + 1);
int numValues = descInputParamHeader[i]->numValues;
printf("\nThere are %1d values\n", numValues);
}
}
else
{
printf("\nunexpected input descriptor type (%d), expected (%d) for parameter header", descInputParamHeader[i]->descType, _parameterDescriptor);
myInputValuePointer[i] = NULL;
}
}
// look for the parameter output block which should be ready-made ... but invalidated
int numParameterValues[2] = {0, 0};
CParameterValue* myValuePointer[2];
myValuePointer[0] = (CParameterValue*)&m_lpMMFile3[CParameterDescHeaderSize/sizeof(short)];
myValuePointer[1] = (CParameterValue*)&m_lpMMFile4[CParameterDescHeaderSize/sizeof(short)];
CParameterDescHeader* descParamHeader[2];
descParamHeader[0] = (CParameterDescHeader *) m_lpMMFile3;
descParamHeader[1] = (CParameterDescHeader *) m_lpMMFile4;
// check some stuff to see if it's what and where we expect it
bool validParameterBlock[2] = {false, false};
for(int i = 0; i < 2; ++i)
{
lecDescType descType = descParamHeader[i]->descType;
int descVersion = descParamHeader[i]->descVersion;
int windowSize = descParamHeader[i]->windowSize;
int headerSize = descParamHeader[i]->headerSize;
if(descType == _parameterDescriptor && descVersion == 1 && 
headerSize == CParameterDescHeaderSize)
{
validParameterBlock[i] = true;
descParamHeader[i]->flags = LStatus_Valid; // we're going to put something there
printf("\n%d Valid parameter block!\n", i);
}
else
{
printf("\n%d unexpected descriptor type (%d), expected (%d) for parameter header",i, descParamHeader[i]->descType, _parameterDescriptor);
myValuePointer[i] = NULL;
}
}
// print the first few bytes of the input waveform
CWaveDescHeader *descHeader = (CWaveDescHeader *) m_lpMMFile1;
CWaveDescMiniHeader* descMiniHeader;
CWaveDescHeader *descHeader2 = (CWaveDescHeader *) m_lpMMFile2;
CWaveDescMiniHeader* descMiniHeader2;
if(checkInt16WformHeader(descHeader) && 
checkInt16WformHeader(descHeader2))
{
// protect against unterminated C-string ... jam a null char into the last available spot
descHeader->verUnit[47] = '\0';
descHeader->horUnit[47] = '\0';
descHeader2->verUnit[47] = '\0';
descHeader2->horUnit[47] = '\0';
int numSegments1 = descHeader->numSegments;
int numSegments2 = descHeader2->numSegments;
if (numSegments1 != numSegments2)
{
printf("\n mismatch in number of segments = %4d != %4d\n", 
numSegments1, numSegments2);
}
int numSegmentsCommon = min(numSegments1, numSegments2);
printf("\ntotal segments = %4d %4d\n", numSegments1, numSegments2);
printf("%6d %6d samples per segment\n", descHeader->numSamples, descHeader2->numSamples);
printf("vertical units: %s, horizontal units %s\n", descHeader->verUnit, 
descHeader->horUnit );
printf("0 vertical perStep: %14.5e, vertical offset %14.5e\n", descHeader->verGain, descHeader->verOffset );
printf("1 vertical perStep: %14.5e, vertical offset %14.5e\n", descHeader2->verGain, descHeader2->verOffset );
double horOffset = descHeader->horOffset;
printf("hor Offset = %14.5e, hor Interval = %14.5e\n", descHeader->horOffset, descHeader->horInterval);
// start and end of domain over which waveform spans. (note: numIntervals = numSamples - 1)
int numSamples = descHeader->numSamples;
int numSamples2 = descHeader2->numSamples;
int numSamplesCommon = min(numSamples, numSamples2);
// we set the domain to the common range, associated with the horizontal domain of input 1
double startOfDomain = descHeader->horOffset;
double endOfDomain = descHeader->horOffset + descHeader->horInterval * (numSamplesCommon - 1);
///////////////////////////////////////////////////////////////////////////////////////////////
// Here as an example we will calculate the Pearson Product-Moment Coefficient 
//
///////////////////////////////////////////////////////////////////////////////////////////////
if(validParameterBlock[0])
{
strncpy(descParamHeader[0]->verUnit, "", 47);// set the output units to none
strncpy(descParamHeader[0]->horUnit, "S", 47); // set the horizontal output units to seconds
descParamHeader[0]->verResolution = 0.01;
descParamHeader[0]->horResolution = descHeader->horResolution;// same as input resolution
}
short *m_lpWaveform = (short*)&m_lpMMFile1[CWaveDescHeaderSize / sizeof(short)]; 
short *m_lpWaveform2 = (short*)&m_lpMMFile2[CWaveDescHeaderSize / sizeof(short)];
for(int segment = 0; segment < numSegments1; ++segment)
{
if(segment > 0)
{
// for segments after the first one, some information may (and probably does) change.
descMiniHeader = (CWaveDescMiniHeader*)m_lpWaveform;
m_lpWaveform += CWaveDescMiniHeaderSize / sizeof(short);
descMiniHeader2 = (CWaveDescMiniHeader*)m_lpWaveform2;
m_lpWaveform2 += CWaveDescMiniHeaderSize / sizeof(short);
numSamples = descMiniHeader->numSamples;
numSamples2 = descMiniHeader2->numSamples;
numSamplesCommon = min(numSamples, numSamples2);
startOfDomain = descMiniHeader->horOffset;
endOfDomain = descMiniHeader->horOffset +  descHeader->horInterval * (numSamplesCommon - 1);
// check incomming hor offset and trigger time
double horOffset = descMiniHeader->horOffset;
lecTimeStamp trigTime = descMiniHeader->trigTime;
int segmentIndex = descMiniHeader->segmentIndex;
}
// // Use this code to assure yourself there's something interesting in the first few points of data
//for(int i = 0; i < 4; ++i)
//{
// printf("%f (%4d),", (m_lpWaveform[i] * descHeader->verGain) + descHeader->verOffset, m_lpWaveform[i]);
// printf("%f (%4d,", (m_lpWaveform2[i] * descHeader2->verGain) + descHeader2->verOffset, m_lpWaveform2[i]);
//}
//printf("\n");
// as an example ... compute the mean of all data values, while computing the abs value of the 
// integer values for the waveform in-place
double resultValue = 0.0;
if (numSamplesCommon > 1)
{
double sumX = 0.0;
double sumY = 0.0;
for(int i = 0; i < numSamplesCommon; ++i)
{
double xValue = (m_lpWaveform[i] * 
descHeader->verGain) + descHeader->verOffset;
sumX += xValue;
double yValue = (m_lpWaveform2[i] * 
descHeader2->verGain) + descHeader2->verOffset;
sumX += xValue;
sumY += yValue;
}
double meanX = sumX / (double) numSamplesCommon;
double meanY = sumY / (double) numSamplesCommon;
// second pass ... sorry, but we're not trying to be subtle
double sumdXdY = 0.0;
double sumdX = 0.0;
double sumdY = 0.0;
double sumdXdX = 0.0;
double sumdYdY = 0.0;
for(int i = 0; i < numSamplesCommon; ++i)
{
double xValue = (m_lpWaveform[i] * 
descHeader->verGain) + descHeader->verOffset;
double yValue = (m_lpWaveform2[i] * 
descHeader2->verGain) + descHeader2->verOffset;
double dX = xValue - meanX;
double dY = yValue - meanY;
sumdXdY += (dX * dY);
sumdXdX += (dX * dX);
sumdYdY += (dY * dY);
sumdX += dX;
sumdY += dY;
}
double varianceX = sumdXdX / (double)numSamplesCommon;
double varianceY = sumdYdY / (double)numSamplesCommon;
double varianceProduct = varianceX * varianceY;
if(varianceProduct > 0.0) resultValue = sumdXdY / ((double)(numSamplesCommon - 1) * sqrt(varianceProduct));
// now modify both input waveforms in place.
for(int i = 0; i < numSamplesCommon; ++i)
{
m_lpWaveform[i] = -(m_lpWaveform[i]);
m_lpWaveform2[i] = -abs(m_lpWaveform2[i]);
}
}
// for this example ... we install the average as a parameter result for each segment
for (int k=0;k<2;k++){
if(validParameterBlock[k] && myValuePointer[k] != NULL)
{
// for each parameter calculated (and there could have been more than 1 per waveform segment)
myValuePointer[k]->value = segment*(k-0.5); //resultValue;
myValuePointer[k]->startOfRegion = startOfDomain;
myValuePointer[k]->endOfRegion = endOfDomain;
myValuePointer[k]->flags = LStatus_Valid;
// move forward to the next parameter value
++myValuePointer[k];
++numParameterValues[k];
}
}

// advance to end of waveform 
m_lpWaveform += numSamples;
m_lpWaveform2 += numSamples2;
}


for (int k=0;k<2;k++){
descParamHeader[k]->numValues = numParameterValues[k];
char* endOfValues = (char*)myValuePointer[k]; 
sprintf(endOfValues,"end of values"); 
}


}
else
printf("\nunexpected descriptor type (%d), expected (%d)", descHeader->descType, _int16WformDescriptor);






// use to flag that the output is not valid, increasing performance when 
// it is not necessary to read data back into the DSO
//descHeader->flags |= LStatus_Invalid;
// flag that processing is complete
SetEvent(m_hProcessingComplete);
}
return 0;
}