#include <windows.h>
#include <stdio.h>
#include <io.h>
#include <crtdbg.h>
#include "HLPRX.h"
#include "RQ.h"
#include "RQProtected.h"
#include "PRXProtected.h"
#include "SDKUtilities.h"
#include "HLPRXInternal.h"
#include "SnapshotProtected.h"
#include "DataCollectionError.h"
#include "NDTSErrorInternal.h"
//#include "HLPRXPrinterPort.h"

// Was 100.  1 works OK at Baoti 16 Sept 06
#define SFD_INTERFACE_MICRO_SLEEP 1;
int spareSamplesPerStep = 50;
short gainTweak[3]= {0,0,0};

// This module provides a common interface for the PRX and RQ DLLs
// The intention of this is to simplify the public API and to ensure
// that the two DLLs are kept in step with each other.  There are certain arcane
// algorithms that relate some settings.

// NOTE
// Currently this API supports one RapiDAQ card and a number of attached PRX cards.  It is assumed that
// all calls to the API relate to one application using one RapiDAQ card.
// It is also assumed that the corresponding PRX cards are used in ordinal sequence starting from 0
// i.e. channels 0 to 7 are on PRX card 0, 8 to 15 are on PRX card 1 etc.


// Common interface for API entry and exit
NDTS_Boolean saveStepAndPause(unsigned long newStep);
void restoreStepAndResume();

// Apps should call the new HLPRX_Initialise  (was HLPRX_SFDInitialise).  Labview and test apps do this.
// The old HLPRX_Initialise - now renamed HLPRX_InitialiseInternal is currently called directly by IW.  However, the rqIndex etc. should not be set by the app.

NDTS_Boolean CALL_CONV HLPRX_InitialiseInternal(unsigned long rqIndex, unsigned long prxCount, unsigned long *pPRXIndices)
{
	unsigned long i, step;
	unsigned long dspVer, perVer;

	RQ_GetHLPRXFirmwareRevisions(&dspVer, &perVer);

	NDTS_LogLine(NDTS_LLNotice, "Running HLSDK with  %s.  Dsp %x, Per %x", SDKUtilities_GetSubversionRevisionString(), dspVer, perVer) ;

	if (prxCount > 4) return NDTS_BFalse;

	// Multi card support is not correct.  We need a way to assign sequence steps to different PRX cards
	// at the moment, settings for a step would be written to all of the multiple cards.
	_ASSERTE(prxCount == 1);
	_ASSERTE(pPRXIndices[0] == 0);

	cardSet.rqIndex = rqIndex;
	cardSet.numberOfPRXCards = prxCount;
	cardSet.rqIndex = rqIndex;

	for (i = 0; i < prxCount; i++)
	{
		cardSet.prxIndices[i] = pPRXIndices[i];
	}

	cardSet.numberOfSteps = 1;
	cardSet.sonatestSerialMuxValue = RQ_GetSonatestSerialMuxValue();

	if (cardSet.sonatestSerialMuxValue != 0)
	{
		unsigned long channel;
		NDTS_Boolean isOk;

		if (!PRX_InitialiseSonatestSerialMux(RQ_GetSonatestSerialMuxValue()))
		{
			return NDTS_BFalse;
		}

		for (channel = 1; channel <= 8; channel++)
		{
			int	gain = RQ_GetSonatestSerialMuxGain(channel);
			int	filter = RQ_GetSonatestSerialMuxFilter(channel);
			int voltageClamp = RQ_GetSonatestSerialMuxVoltageClamp();
			PRX_SonatestSerialMuxSetGain(channel, gain);
			PRX_SonatestSerialMuxSetFilter(channel, filter);
			PRX_SonatestSerialMuxSetVoltageClamp(voltageClamp);
		}

		// Set a 10uS wide pulse so that the Sonatest mux can easily see the external trigger pulse.
		isOk = RQ_SetTransducerPulseWidth(cardSet.rqIndex, 2000);
		_ASSERTE(isOk);
	}

	if (!PRX_SetManualCommit(cardSet.prxIndices[0], NDTS_BTrue)) return NDTS_BFalse;
	for (i = 0; i < SEQUENCE_TABLE_BLOCKS; i++)
	{
		cardSet.range[i] = 4;  // Fill in the values for all steps so when the number of steps is increased, we have sensible defaults to use
		cardSet.delay[i] = 0;  // Very short range so it will work with any rep rate
		cardSet.stepLengthTotal[i] = spareSamplesPerStep;
		cardSet.filter[i] = HLPRX_BFNone;
		cardSet.gain[i] = 1;

		// impedance settings
		// bit 0 50r
		//     1 100r
		//     2 270r
		//     3 470r
		PRX_SetInputImpedance(cardSet.prxIndices[0], i, 1);

	}
	if (!PRX_Commit(cardSet.prxIndices[0])) 
		return NDTS_BFalse;
	if (!PRX_SetManualCommit(cardSet.prxIndices[0], NDTS_BFalse)) 
		return NDTS_BFalse;

	// C:\root\prx\sw\VBTestAppHLPRX.Net2003\bin\project1.exe
	// C:\InspectionWare\bin\fpnde.exe
	if (!PRX_SetSoftwareFETOverloadProtectionEnabled(cardSet.prxIndices[0], NDTS_BTrue)) 
		return NDTS_BFalse;
	if (!PRX_SetLimitPulseWidthCallback(cardSet.prxIndices[0], HLPRX_LimitPulseWidthCallback)) 
		return NDTS_BFalse;
	if (!PRX_ResetFETOverload(cardSet.prxIndices[0]))
		return NDTS_BFalse;
	if (!PRX_SetAutoResetForFETOverload(cardSet.prxIndices[0], NDTS_BTrue))
		return NDTS_BFalse;

	for (step=0; step < NDTS_MAXIMUM_NUMBER_OF_STEPS; step++)
	{
		if (!PRX_SetExternalTCG(cardSet.prxIndices[0], step, NDTS_BTrue)) 
			return NDTS_BFalse;
	}


	cardSet.repetitionPeriod = 100000000UL;
	cardSet.shadowPrxDigital = 0;
	cardSet.isSFDInterfaceBoardInitialised = NDTS_BFalse;
	
	// We require 100MSPS sampling for now, (the PRX always runs at this speed)
	if (!RQ_SetSamplingFrequency(cardSet.rqIndex, 100000000UL)) return NDTS_BFalse;
	if (!RQ_SetTriggerSelect(cardSet.rqIndex, RQ_TFreeRunning)) return NDTS_BFalse;

	HLPRX_SetOverSamplingAllSteps(1);

	NDTS_LogLine(NDTS_LLDebug, "HLPRX_Initialise completed");

	return NDTS_BTrue;
}


BOOL APIENTRY DllMain(HANDLE hModule, DWORD ul_reason_for_call, LPVOID lpReserved)
{
	(void)lpReserved; hModule; // Keep compiler happy

	switch (ul_reason_for_call)
	{
		case DLL_PROCESS_ATTACH :
			HLPRX_DLLInitialise() ;
			break ;
		case DLL_PROCESS_DETACH :
			HLPRX_DLLUninitialise() ;
			break ;
		case DLL_THREAD_ATTACH :
			break ;
		case DLL_THREAD_DETACH :
			break ;
    }
    return TRUE ;
}


HLPRX_API NDTS_Boolean CALL_CONV HLPRX_DLLInitialise()
{
	// Can't create threads or do anything that requires synchronisation in a DllMain call so 
	// do most of the init in HLPRX_Initialise.
	return NDTS_BTrue;
}

HLPRX_API NDTS_Boolean CALL_CONV HLPRX_Initialise()
{
	unsigned long pPRXIndices[1];
	unsigned long cardIndex = 0;
	unsigned long numPrxFound = 0;
	unsigned long step = 0;
	static NDTS_Boolean staticIsInitialised = NDTS_BFalse;
    
	if (staticIsInitialised)  
	{
		// Uninit -> Init just stop and start sequence to prevent mem leak whilst uninited
		return RQ_StartSequence(cardIndex);
	}
	

	pPRXIndices[0] = 0;

	if (!RQ_InitialiseSequenced(cardIndex))
		return NDTS_BFalse;

	if (!PRX_PCIEnumerate(&numPrxFound))
		return NDTS_BFalse;

	if (!PRX_Initialise(0))
		return NDTS_BFalse;

	if (!HLPRX_InitialiseInternal(cardIndex, 1, pPRXIndices))
		return NDTS_BFalse;

	if (!RQ_StopSequence(cardIndex))
		return NDTS_BFalse;

	if (!HLPRX_SetRepetitionRate(1000))
		return NDTS_BFalse;

	if (!RQ_SetScanStorageOptions(cardIndex, NDTS_SSMultipleAScanMultipleCScan))
		return NDTS_BFalse;

	if (!RQ_SetSampleStorageMode(cardIndex, NDTS_SS12Bit))
		return NDTS_BFalse;
	
	if (!PRX_SetExternalTrigger(cardIndex, 1))
		return NDTS_BFalse;

	if (!HLPRX_SetNumberOfSequenceSteps(8)) 
		return NDTS_BFalse;


	
	if (!HLPRX_SetPulserVoltageEnable(0))
		return NDTS_BFalse;

	if (!HLPRX_SetPulserVoltage(10))  
			return NDTS_BFalse;

	if (!PRX_SetPulseEchoEnable(cardIndex, 0xff))
		return NDTS_BFalse;

	if (!RQ_SetAnalogueInput(cardIndex, RQ_AIInput0))
		return NDTS_BFalse;

	if (!RQ_SetAnalogueInputRange(cardIndex, RQ_AG200mV))
		return NDTS_BFalse;

	if (!RQ_SetInputImpedance(cardIndex, RQ_II50Ohm))
		return NDTS_BFalse;

	if (!RQ_SetInputCoupling(cardIndex, RQ_ICDC))
		return NDTS_BFalse;

	for (step = 0; step < 8; step++)
	{
		if (!PRX_SetTransmitPulseEnable(cardIndex, step, (1 << step))) 
			return NDTS_BFalse;
		if (!PRX_SetReceiveChannel(cardIndex, step, step))
			return NDTS_BFalse;
		if (!HLPRX_SetDelay(step, 0))
			return NDTS_BFalse;
		if (!HLPRX_SetRange(step, 1000))
			return NDTS_BFalse;
	}
	if (!RQ_StartSequence(cardIndex))
		return NDTS_BFalse;

	for (step = 0; step < 8; step++)
	{
		if (!HLPRX_SetFilter(step, HLPRX_BFNone))
			return NDTS_BFalse;
	}

	staticIsInitialised = NDTS_BTrue;
	
	return NDTS_BTrue;
}


HLPRX_API NDTS_Boolean CALL_CONV HLPRX_Uninitialise(void)
{
	// Uninit -> Init just stop and start sequence to prevent mem leak whilst uninited
	if (!RQ_StopSequence(cardSet.rqIndex))
		return NDTS_BFalse;

	// There is some sort of problem with this followed by a full init.
	//if (!RQT_SnapshotUninitialise(cardSet.rqIndex)) return NDTS_BFalse;
	//if (!RQ_Uninitialise(cardSet.rqIndex)) return NDTS_BFalse;
	//if (!PRX_Uninitialise(0)) return NDTS_BFalse;
	return NDTS_BTrue;
}

HLPRX_API NDTS_Boolean CALL_CONV HLPRX_DLLUninitialise(void)
{
	DC_SnapshotUninitialise(cardSet.rqIndex);
	return NDTS_BTrue;
}

HLPRX_API NDTS_Boolean CALL_CONV HLPRX_SetRepetitionRate(unsigned long pulsesPerSecond)
{
	const unsigned long minPulsesPerSec = 160; // 17 bit register containing number of 5x clock periods at 100Mhz sample frequency
	NDTS_Boolean isOK, isParamOK;
	unsigned long samplingFrequency, numberOfPeriods;
	unsigned long step;
	unsigned long oversamples;

	isOK = RQ_GetSamplingFrequency(cardSet.rqIndex, &samplingFrequency) ;
	if (!isOK) return NDTS_BFalse;

	isOK = RQ_GetNumberOfOversamples(cardSet.rqIndex, &oversamples);
	if (!isOK) return NDTS_BFalse;

	isOK = NDTS_BTrue ;
	isParamOK = NDTS_BTrue ;

	if (pulsesPerSecond < minPulsesPerSec)
	{
		isParamOK = NDTS_BFalse;	
		pulsesPerSecond = minPulsesPerSec; 
	}
	
	numberOfPeriods = samplingFrequency / pulsesPerSecond;

	// stepLengthTotal takes into account over samples.  The rep rate is run separately for each over sample.
	// If requested rep rate can't be set, set highest possible and return false
	for (step = 0; step < cardSet.numberOfSteps; step++)
	{
		if (cardSet.stepLengthTotal[step] / oversamples > numberOfPeriods) 
		{
			isParamOK = NDTS_BFalse;	
			numberOfPeriods = cardSet.stepLengthTotal[step] / oversamples ;
		}
	}

	if (!isParamOK)
		NDTS_LogLine(NDTS_LLWarning, "Rep rate CLAMPED - set to %d", samplingFrequency / numberOfPeriods) ;

	RQ_Lock(cardSet.rqIndex);
	RQ_PauseSequence(cardSet.rqIndex);

	isOK = RQ_SetRepetitionRate(cardSet.rqIndex, samplingFrequency / numberOfPeriods);
	if (!isOK)
	{
		NDTS_LogLine(NDTS_LLWarning, "Rep rate not changed - call to RQ_SetRepetitionRate failed") ;
		isParamOK = NDTS_BFalse;
	}
	else
	{
		unsigned long i;
		cardSet.repetitionPeriod = numberOfPeriods;
		for (i = 0; i < cardSet.numberOfPRXCards; i++)
		{
		// This should never fail as it is just used by the PRX SDK for the pulser duty cycle protection calculations.
			isOK = PRX_SetMasterRepetitionTime(i, numberOfPeriods);
			_ASSERTE(isOK);
		}
	}

	RQ_ResumeSequence(cardSet.rqIndex);
	RQ_Unlock(cardSet.rqIndex);
	return isParamOK ;

}

HLPRX_API NDTS_Boolean CALL_CONV HLPRX_GetRepetitionRate(unsigned long *pulsesPerSecond)
{
	return RQ_GetRepetitionRate(cardSet.rqIndex, pulsesPerSecond) ;
}

// When oversampling, numberOfSamples is the number of samples that will be stored in memory, NOT the number of clock ticks.
// For example at 100Mhz clock, 4x oversampling, numberOfSamples = 400 means store four sets of 100 samples
// covering a range of 1uS in time at a resolution of 0.25uS.
HLPRX_API NDTS_Boolean CALL_CONV HLPRX_SetRange(unsigned long stepIndex, unsigned long sampleCountParam)
{
	// Here the card index is for the PRX card
	NDTS_Boolean isOK, isParamOK = NDTS_BTrue;
	unsigned long sampleCount ;
	unsigned long oversamples;

	if (!saveStepAndPause(stepIndex)) return NDTS_BFalse;

	// Check that range is OK for oversample count - clamp range if not
	isOK = RQ_GetNumberOfOversamples(cardSet.rqIndex, &oversamples);
	if (isOK == NDTS_BFalse)
		goto exit;

	// Check that this range is OK for the rep rate - if not clamp range 
	if (sampleCountParam + cardSet.delay[stepIndex] + spareSamplesPerStep*oversamples <= cardSet.repetitionPeriod*oversamples && 
		sampleCountParam < NDTS_MAXIMUM_NUMBER_OF_SAMPLES)
	{
		sampleCount = sampleCountParam ;
//		NDTS_LogLine(NDTS_LLWarning, Range set OK to %d for step %d", sampleCount, stepIndex) ;

	}
	else
	{
		sampleCount = cardSet.repetitionPeriod*oversamples - (cardSet.delay[stepIndex] + spareSamplesPerStep*oversamples)   ;
		isParamOK = NDTS_BFalse;
		if (sampleCount >= NDTS_MAXIMUM_NUMBER_OF_SAMPLES)
			sampleCount = NDTS_MAXIMUM_NUMBER_OF_SAMPLES-1;
		NDTS_LogLine(NDTS_LLWarning, "Range CLAMPED to %d for step %d", sampleCount, stepIndex) ;
	}

	isOK = RQ_SetNumberOfSamples(cardSet.rqIndex, sampleCount);
	if (isOK == NDTS_BFalse)
		goto exit;

	cardSet.range[stepIndex] = sampleCount;
	cardSet.stepLengthTotal[stepIndex] = cardSet.range[stepIndex] + cardSet.delay[stepIndex] + spareSamplesPerStep*oversamples;

	isOK = HLPRX_SetPrxRepetitionTimeFromCardSet(stepIndex);

exit:
	restoreStepAndResume();
	return isOK && isParamOK;
}

HLPRX_API NDTS_Boolean CALL_CONV HLPRX_GetRange(unsigned long stepIndex, unsigned long *sampleCount)
{
	*sampleCount = cardSet.range[stepIndex] ;
	return NDTS_BTrue ;
}

// When oversampling, sampleCountParam is at the oversampled rate, NOT sample frequency clock ticks.
// This is for consistency with HLPRX_SetRange and also the gate start / stop values.
// For example at 100Mhz clock, 4x oversampling, sampleCountParam = 400 means wait for four sets of 100 samples
// so the delay will be of 1uS.  The delay is truncated to a number of clock frequency ticks.

HLPRX_API NDTS_Boolean CALL_CONV HLPRX_SetDelay(unsigned long stepIndex, unsigned long sampleCountParam)
{
	NDTS_Boolean isOK, isParamOK = NDTS_BFalse;
	unsigned long oversamples;
	unsigned long sampleCount;

	if (!saveStepAndPause(stepIndex)) return NDTS_BFalse;

	isOK = RQ_GetNumberOfOversamples(cardSet.rqIndex, &oversamples);
	if (!isOK)
		goto exit;

	if (cardSet.range[stepIndex] + sampleCountParam + spareSamplesPerStep*oversamples <= cardSet.repetitionPeriod*oversamples)
	{
		sampleCount = sampleCountParam;
		isParamOK = NDTS_BTrue;
//		NDTS_LogLine(NDTS_LLWarning, SetDelay OK set to %d for step %d", sampleCount, stepIndex) ;
	}
	else
	{
		sampleCount = cardSet.repetitionPeriod*oversamples - (cardSet.range[stepIndex] + spareSamplesPerStep*oversamples) ;
		isParamOK = NDTS_BFalse;
		NDTS_LogLine(NDTS_LLWarning, "SetDelay CLAMPED to %d for step %d", sampleCount, stepIndex) ;
	}

	isOK = RQ_SetStartDelay(cardSet.rqIndex, sampleCount);
	if (isOK != NDTS_BTrue)
		goto exit;

	cardSet.delay[stepIndex] = sampleCount;
	cardSet.stepLengthTotal[stepIndex] = cardSet.range[stepIndex] + cardSet.delay[stepIndex] + spareSamplesPerStep*oversamples ;

	isOK = HLPRX_SetPrxRepetitionTimeFromCardSet(stepIndex);

exit:
	restoreStepAndResume(); 
	return isOK && isParamOK;
}

HLPRX_API NDTS_Boolean CALL_CONV HLPRX_GetDelay(unsigned long stepIndex, unsigned long *sampleCountParam)
{
	*sampleCountParam = cardSet.delay[stepIndex] ;
	return NDTS_BTrue ;
	
}
HLPRX_API NDTS_Boolean CALL_CONV HLPRX_SetNumberOfSequenceSteps(unsigned long numberOfSequenceSteps)
{
	unsigned long i;

	NDTS_Boolean isOK;
	// The RQ card and each PRX card should have the same idea about the number of steps

	RQ_Lock(cardSet.rqIndex);
	RQ_PauseSequence(cardSet.rqIndex);

	isOK = RQ_SetStepsInSequence(cardSet.rqIndex, numberOfSequenceSteps);
	if (isOK != NDTS_BTrue)
		goto exit;

	for (i = 0; i < cardSet.numberOfPRXCards; i++)
	{
		isOK = PRX_SetNumberOfSequenceSteps(cardSet.prxIndices[i], numberOfSequenceSteps);
		_ASSERTE(isOK);  // The asserts in this function should never go off.  If an error does need to be handled, need to restore the RQ_SetStepsInSequence and PRX_SetNumberOfSequenceSteps to the old number of steps
	}

	// Write shadow values if increasing the number of steps as the cards may not have received the values yet
	for (i=cardSet.numberOfSteps; i < numberOfSequenceSteps; i++)
	{
		// Don't do anything with the return value because it is possible for e.g. SetRange to get clamped if the user sets a long range for step 5, changes num steps to 1, ups the rep rate and then changes num steps back to 10
		isOK = HLPRX_SetRange(i, cardSet.range[i]);
		isOK = HLPRX_SetDelay(i, cardSet.delay[i]);
		isOK = HLPRX_SetFilter(i, cardSet.filter[i]);
		isOK = HLPRX_SetGain(i, cardSet.gain[i]);
	}

	cardSet.numberOfSteps = numberOfSequenceSteps;

exit:
	RQ_ResumeSequence(cardSet.rqIndex);
	RQ_Unlock(cardSet.rqIndex);

	return isOK;
}

HLPRX_API NDTS_Boolean CALL_CONV HLPRX_GetNumberOfSequenceSteps(unsigned long *pNumberOfSequenceSteps)
{
	*pNumberOfSequenceSteps =  cardSet.numberOfSteps;
	return NDTS_BTrue;
}

HLPRX_API NDTS_Boolean CALL_CONV HLPRX_ClearTCGCurve(unsigned long cardIndex)
{
	return RQ_ClearTCGCurve(cardIndex);
}

HLPRX_API NDTS_Boolean CALL_CONV HLPRX_SubmitTCGCurve(unsigned long cardIndex, unsigned long step, short *pCurve, unsigned long nPts, short gain)
{
	// Find the lowest point on the TCG curve and select the highest coarse gain adjustment that can
	// achieve this (this gives the best noise performance)
	// Then adjust the gain setting so that the RapiDAQ SDK creates an appropriately scaled TCG curve
#ifdef HL_COARSE_GAIN_CONTROL
	// This code is executed if HLPRX is handling coarse gain control (looks to the application as though we have
	// continuous gain adjustment from -30 to +110dB)
	unsigned long i;
	short minGain = pCurve[0];
	for (i = 1; i < nPts; i++)
	{
		if (pCurve[i] < minGain) minGain = pCurve[i];
	}

	minGain = minGain + gain; // this gives us the lowest absolute gain level

	if (minGain < 100)
	{
		// we need 40dB attenuation
		PRX_SetCoarseGain(cardIndex, step, PRX_GainMinus40dB);
		gain = gain + 300 + gainTweak[HLPRX_GTMinus40];
		// thus min gain of -30dB corresponds to a gain value of 0
	}
	else if (minGain < 300)
	{
		// we need 20dB attenutation
		PRX_SetCoarseGain(cardIndex, step, PRX_GainMinus20dB);
		gain = gain + 100 + gainTweak[HLPRX_GTMinus20];
	}
	else if (minGain < 500)
	{
		// we need 0dB attenutation
		PRX_SetCoarseGain(cardIndex, step, PRX_Gain0dB);
		gain -= 100;
		// No gain tweak for 0dB (this is the reference)
	}
	else
	{
		// we need +20dB gain
		PRX_SetCoarseGain(cardIndex, step, PRX_Gain20dB);
		gain = gain - 300 + gainTweak[HLPRX_GTPlus20];
	}
	return (RQ_SubmitTCGCurve(cardIndex, step, pCurve, nPts, gain));

#else
	//Run this code if coarse gain control is handled by the application
	//It is assumed here that each point on the TCG curve plus the gain value will be in the range 0..800cB


/*
	// Daniel at Hua Shan 10/5/06 - the cards aren't working well at the top end of the gain
	// Take 20dB off and tell InspectionWare to send between 20-80dB
	unsigned long i;
	for (i = 0; i < nPts; i++)
	{
		pCurve[i]  = pCurve[i] - 200;
	}
*/

	return (RQ_SubmitTCGCurve(cardIndex, step, pCurve, nPts, gain));
#endif
}

HLPRX_API NDTS_Boolean CALL_CONV HLPRX_SetEnhancedTCG(unsigned long cardIndex)
{
	return RQ_SetEnhancedTCG(cardIndex);
}

HLPRX_API NDTS_Boolean CALL_CONV HLPRX_SetGainTweak(HLPRX_GainTweak whichAttenuator, short level)
{
	if ((whichAttenuator > 2) || ((level < -64) || (level > 64)))
	{
		return NDTS_BFalse;
	}

	gainTweak[whichAttenuator] = level;
	return NDTS_BTrue;
}


HLPRX_API NDTS_Boolean CALL_CONV HLPRX_SetFilter(unsigned long step, HLPRX_BandpassFilter value) 
{

	NDTS_Boolean isOK;

	RQ_Lock(cardSet.rqIndex);
	RQ_PauseSequence(cardSet.rqIndex);

	isOK = HLPRX_SetBandpassFilterInternal(cardSet.rqIndex, step, value);

	if (!isOK)
		goto exit;
	cardSet.filter[step] = value;


exit:
	RQ_ResumeSequence(cardSet.rqIndex);
	RQ_Unlock(cardSet.rqIndex);	
	
	return isOK;
}

HLPRX_API NDTS_Boolean CALL_CONV HLPRX_GetFilter(unsigned long step, HLPRX_BandpassFilter *pValue) 
{
	*pValue = cardSet.filter[step];
	return NDTS_BTrue;
}



// From spreadsheet in technical\development\system flaw detector\filters - was HLPRX_SetBandpassFilter()
NDTS_Boolean  HLPRX_SetBandpassFilterInternal(unsigned long card, unsigned long block, HLPRX_BandpassFilter value) 
{
	NDTS_Boolean isOK;

	PRX_BackEndLowPassFilter BackendLowpass;
	PRX_BackEndHighPassFilter BackendHighpass;
	PRX_FrontEndLowPassFilter FrontendLowpass;
	PRX_FrontEndHighPassFilter FrontendHighpass;

	switch(value)
	{
	case HLPRX_BFPoint5Mhz:
		FrontendHighpass = PRX_FEHPPoint5Mhz ;  
		FrontendLowpass = PRX_FELP1Point5Mhz ;
		BackendHighpass = PRX_BEHP340Khz ;  
		BackendLowpass = PRX_BELP1Point6Mhz ;
		break;
	case HLPRX_BF1Mhz:  
		FrontendHighpass = PRX_FEHPPoint5Mhz ;
		FrontendLowpass = PRX_FELP1Point5Mhz ;
		BackendHighpass = PRX_BEHPPoint7Mhz ; 
		BackendLowpass = PRX_BELP3Mhz;
		break;
	case HLPRX_BF2Point5Mhz: 
		FrontendHighpass = PRX_FEHP1Point25Mhz ;
		FrontendLowpass = PRX_FELP3Point75Mhz ;
		BackendHighpass = PRX_BEHP1Point6Mhz ;  
		BackendLowpass = PRX_BELP3Mhz ; 
		break;
	case HLPRX_BF5Mhz: 
		FrontendHighpass = PRX_FEHP2Point5Mhz ;
		FrontendLowpass = PRX_FELP7Point5Mhz ;
		BackendHighpass = PRX_BEHP3Mhz ;
		BackendLowpass = PRX_BELP7Mhz ; 
		break;
	case HLPRX_BF10Mhz: 
		FrontendHighpass = PRX_FEHP5Mhz ;
		FrontendLowpass = PRX_FELP15Mhz ;
		BackendHighpass = PRX_BEHP3Mhz ;
		BackendLowpass = PRX_BELP16Mhz ;
		break;
	case HLPRX_BF15Mhz: 
		FrontendHighpass = PRX_FEHP7Point5Mhz ;
		FrontendLowpass = PRX_FELP22Point5Mhz ;
		BackendHighpass = PRX_BEHP3Mhz ;
		BackendLowpass = PRX_BELP32Mhz ;
		break;
	case HLPRX_BFNone:  
		FrontendHighpass = PRX_FEHPNone ;
		FrontendLowpass = PRX_FELPNone ;
		BackendHighpass = PRX_BEHP70Khz ;
		BackendLowpass = PRX_BELP32Mhz ;
		break;
	default:
		return (NDTS_BFalse) ;
		break ;
	}

	isOK = PRX_SetFrontendHighpassFilter(card, block, FrontendHighpass) ;
	if (!isOK)
	{
		return isOK ;
	}

	isOK = PRX_SetFrontendLowpassFilter(card, block, FrontendLowpass) ;
	if (!isOK)
	{
		return isOK ;
	}

	isOK = PRX_SetBackendHighpassFilter(card, block, BackendHighpass) ;
	if (!isOK)
	{
		return isOK ;
	}


	isOK = PRX_SetBackendLowpassFilter(card, block, BackendLowpass) ;
	if (!isOK)
	{
		return isOK ;
	}

	return NDTS_BTrue ;
}



HLPRX_API NDTS_Boolean CALL_CONV HLPRX_SetSFDInterfaceDigitalOutput(unsigned long value) 
{
	unsigned long data = 0;
	if (value == cardSet.shadowPrxDigital) return NDTS_BTrue;
	cardSet.shadowPrxDigital = value;

    if (value > 0xffff) return NDTS_BFalse;
	if (!cardSet.isSFDInterfaceBoardInitialised)
	{
		if (!HLPRX_InitialiseSFDInterfaceBoard()) return NDTS_BFalse;
	}
/*
	for(;;)
	{
		data = 0xffffffff;  // Set data for latch 1
		if (!RQ_SetPeripheralIOBank1(cardSet.rqIndex, data)) return NDTS_BFalse;
		RQ_BUSYMICROSLEEP(SFD_INTERFACE_MICRO_SLEEP ) ;

		data = 0;  // Set data for latch 1
		if (!RQ_SetPeripheralIOBank1(cardSet.rqIndex, data)) return NDTS_BFalse;
		RQ_BUSYMICROSLEEP(SFD_INTERFACE_MICRO_SLEEP ) ;

	}

	*/
	//NDTS_LogLine(NDTS_LLWarning, Digital outputs set to hex %x", value) ;
	data = 0xF0038000;  // Set all unused peripheral address and #WR,  Reset all data and control bits
    data |= value & 0xff;  // Set data for latch 1
	if (!RQ_SetPeripheralIOBank1(cardSet.rqIndex, data)) return NDTS_BFalse;
	RQ_BUSYMICROSLEEP(SFD_INTERFACE_MICRO_SLEEP ) ;

	data &= 0x2fffffff;	   // Set address for latch 1
	if (!RQ_SetPeripheralIOBank1(cardSet.rqIndex, data)) return NDTS_BFalse;
	RQ_BUSYMICROSLEEP(SFD_INTERFACE_MICRO_SLEEP ) ;

	data |= 0xf0000000;    // Reset to known state
	if (!RQ_SetPeripheralIOBank1(cardSet.rqIndex, data)) return NDTS_BFalse;
	RQ_BUSYMICROSLEEP(SFD_INTERFACE_MICRO_SLEEP ) ;
	
	data &= 0xf0038000;	   // Reset data and control lines
	if (!RQ_SetPeripheralIOBank1(cardSet.rqIndex, data)) return NDTS_BFalse;
	RQ_BUSYMICROSLEEP(SFD_INTERFACE_MICRO_SLEEP ) ;

	data |= value >> 8 ;	// Set data for latch 2
	data &= 0x3fffffff;		// Set address for latch 2
	if (!RQ_SetPeripheralIOBank1(cardSet.rqIndex, data)) return NDTS_BFalse;
	RQ_BUSYMICROSLEEP(SFD_INTERFACE_MICRO_SLEEP ) ;

	data |= 0xf0000000;    // Reset to known state
	if (!RQ_SetPeripheralIOBank1(cardSet.rqIndex, data)) return NDTS_BFalse;
	RQ_BUSYMICROSLEEP(SFD_INTERFACE_MICRO_SLEEP ) ;

	return NDTS_BTrue ;

}	

HLPRX_API NDTS_Boolean CALL_CONV HLPRX_SetSFDInterfaceAnalogueOutput(unsigned long analogueOutputNumber, unsigned long analogueValue) 
{
	unsigned long data = 0;
	if (analogueValue > 4095)  return NDTS_BFalse;
	if (!cardSet.isSFDInterfaceBoardInitialised)
	{
		if (!HLPRX_InitialiseSFDInterfaceBoard()) return NDTS_BFalse;
	}

	//  A NDTS_LogLine(NDTS_LLWarning, Analogue output %d set to %d", analogueOutputNumber, analogueValue) ;
	data = 0xF0038000;  // Set all unused peripheral address and #WR,  Reset all data and control bits
	if (!RQ_SetPeripheralIOBank1(cardSet.rqIndex, data)) return NDTS_BFalse;
	RQ_BUSYMICROSLEEP(SFD_INTERFACE_MICRO_SLEEP ) ;

    data |= analogueValue;  // Set the data

	if (analogueOutputNumber & 0x01) data |= 0x4000;  // The ADC address bits are reversed
	if (analogueOutputNumber & 0x02) data |= 0x2000;  
	if (analogueOutputNumber & 0x04) data |= 0x1000;  

	data &= 0xfffffff;// Select which ADC
	if (analogueOutputNumber & 0x08) data |= 0x10000000;  

	if (!RQ_SetPeripheralIOBank1(cardSet.rqIndex, data)) return NDTS_BFalse;
	RQ_BUSYMICROSLEEP(SFD_INTERFACE_MICRO_SLEEP ) ;

    data &= 0xfffeffff;  // Pull #WR
	if (!RQ_SetPeripheralIOBank1(cardSet.rqIndex, data)) return NDTS_BFalse;
	RQ_BUSYMICROSLEEP(SFD_INTERFACE_MICRO_SLEEP ) ;
	data |= 0x10000;  // Set #WR
	if (!RQ_SetPeripheralIOBank1(cardSet.rqIndex, data)) return NDTS_BFalse;
	RQ_BUSYMICROSLEEP(SFD_INTERFACE_MICRO_SLEEP ) ;

    data &= 0xffff7fff;  // Pull #LDAC
	if (!RQ_SetPeripheralIOBank1(cardSet.rqIndex, data)) return NDTS_BFalse;
	RQ_BUSYMICROSLEEP(SFD_INTERFACE_MICRO_SLEEP ) ;
	data |= 0x8000;  // Set #LDAC
	if (!RQ_SetPeripheralIOBank1(cardSet.rqIndex, data)) return NDTS_BFalse;
	RQ_BUSYMICROSLEEP(SFD_INTERFACE_MICRO_SLEEP ) ;

	return NDTS_BTrue ;
}

HLPRX_API NDTS_Boolean CALL_CONV HLPRX_SetOverSamplingAllSteps(unsigned long oversamplingMultiplier)
{
	NDTS_Boolean isOK;
	int i;
	unsigned long oldNumberOfOversamples;

	if (!saveStepAndPause(0)) return NDTS_BFalse;

	isOK = RQ_GetNumberOfOversamples(cardSet.rqIndex, &oldNumberOfOversamples);
	if (!isOK)
		goto exit;

	// Don't clear the A Scan buffer every call as this is slow - only clear it on the last call
	isOK = DC_SetClearAScanOnUpdate(cardSet.rqIndex, NDTS_BFalse);
	if (!isOK)
		goto exit;

	for (i = 0; i < NDTS_MAXIMUM_NUMBER_OF_STEPS - 1; i++)
	{
		isOK &= HLPRX_SetOverSamplingInternal(i, oversamplingMultiplier);
	}
	
	isOK = DC_SetClearAScanOnUpdate(cardSet.rqIndex, NDTS_BTrue);
	if (!isOK)
		goto exit;

	isOK &= HLPRX_SetOverSamplingInternal(NDTS_MAXIMUM_NUMBER_OF_STEPS - 1, oversamplingMultiplier);
	if (!isOK)
		goto exit;

	isOK = RQ_RewriteGates(cardSet.rqIndex, oldNumberOfOversamples, oversamplingMultiplier);

exit:
	restoreStepAndResume(); 

	return isOK;
}

HLPRX_API NDTS_Boolean CALL_CONV HLPRX_SetOverSampling(unsigned long step, unsigned long oversamplingMultiplier)
{
	// FROG it seems most sensible to produce N sets of gate data and select gate data
	// from them based on which is most 'interesting'.  The depth values will need
	// to be adjusted to (depth * N + overSampleIndex).
	// It might be sensible to trigger the overample 0 TCG normally then trigger on the
	// same sample for subsequent channels, so that a non-repeatable TCG trigger will
	// still give a reasonably good trace when the ascans are combined.

	unsigned long samplingFrequency, prxIndex;
	float jiffies, samplePeriodnS ;
	NDTS_Boolean isOK;
	unsigned long oldMultiplier;

	if (!saveStepAndPause(step)) return NDTS_BFalse;

	if (!RQ_GetSamplingFrequency(cardSet.rqIndex, &samplingFrequency)) return NDTS_BFalse;
	samplePeriodnS = 1000000000.0f / (float)samplingFrequency;
	jiffies = 2.0f * samplePeriodnS / (float)(oversamplingMultiplier);  // jiffies in 0.5nS steps

	isOK = RQ_GetNumberOfOversamples(cardSet.rqIndex, &oldMultiplier);
	if (!isOK)
		goto exit;

	isOK = RQ_SetNumberOfOversamples(cardSet.rqIndex, oversamplingMultiplier);
	if (!isOK)
		goto exit;

	for (prxIndex = 0; prxIndex < cardSet.numberOfPRXCards; prxIndex++)
	{
		isOK = PRX_SetNumberOfOversamples(cardSet.prxIndices[0], step, oversamplingMultiplier);
		if (!isOK)
			goto exit; 
		isOK = PRX_SetOversamplingDelay(cardSet.prxIndices[0], step, (unsigned long) jiffies);
		if (!isOK)
			goto exit; 
	}

	// FROG the number of AScans per BScan should be set (in the DSP FPGA) to the
	// total number of oversamples - 1
	isOK = RQ_SetNumberOfAScansPerBScan(cardSet.rqIndex, cardSet.numberOfSteps);
	if (!isOK)
		goto exit; 

	// We need to rewrite the gates too
	isOK = RQ_RewriteGates(cardSet.rqIndex, oldMultiplier, oversamplingMultiplier);
	if (!isOK)
		goto exit; 

exit:
	restoreStepAndResume(); 
	return isOK;

}
HLPRX_API NDTS_Boolean CALL_CONV HLPRX_GetOverSampling(unsigned long stepIndex, unsigned long *pOversamplingMultiplier)
{
	NDTS_Boolean isOK;

	if (!saveStepAndPause(stepIndex)) return NDTS_BFalse;

	isOK = RQ_GetNumberOfOversamples(cardSet.rqIndex, pOversamplingMultiplier);
	if (!isOK)
		goto exit;
exit:
	restoreStepAndResume(); 
	return isOK;

}


/* Interleaving produces data as below with the timing as below.  The B Scan Rep Rate only applies if free running.
A Over0 Seq0  A Over1 Seq0  A Over2 Seq0  A Over3 Seq0  A Over0 Seq1  A Over1 Seq1  A Over2 Seq1  A Over3 Seq1      A Over0 Seq0  A Over1 Seq0  A Over2 Seq0 A Over3 Seq0  A Over0 Seq1   A Over1 Seq1  A Over2 Seq1  A Over3 Seq1       A Over0 Seq0  
|A Scn rep rt |A Scn rep rt |A Scn rep rt |A Scn rep rt |A Scn rep rt |A Scn rep rt |A Scn rep rt |                 |A Scn rep rt |A Scn rep rt |A Scn rep rt |A Scn rep rt |A Scn rep rt |A Scn rep rt |A Scn rep rt |                  |
|--------------------------------------- B Scan Rep Rate -----------------------------------------------------------||--------------------------------------- B Scan Rep Rate -----------------------------------------------------------|

Cscan data is interleaved / oversampled in a similar way, i.e. the gate information is built from each individual A-scan not the composite high resolution scan.
*/


HLPRX_API NDTS_Boolean CALL_CONV HLPRX_TakeSnapshot(unsigned long *pNumberOfCapturedBScans) 
{
	*pNumberOfCapturedBScans = 0;

	if (!DC_SwapSnapshotDataBank(cardSet.rqIndex, pNumberOfCapturedBScans))
	{
		return NDTS_BTrue;
	}

/*
	{
		// Test code
		static int snapshots = 0;
		static int snapshotDecimator = 1000;
		static int bDecimator = 30;
		if (snapshots++ % snapshotDecimator == 0)
		{
			unsigned long b;
			NDTS_GateOutput ppGateData[10][3];
			long pEncoders[10];
			for (b=0; b < *pNumberOfCapturedBScans; b++)
			{
				NDTS_Boolean OK = HLPRX_GetGateAndEncoderDataFromSnapshot(b, (NDTS_GateOutput*)(ppGateData), 10,3, pEncoders, 10);
				if (b % bDecimator == 0)
				{
					NDTS_LogLine(NDTS_LLDebug, "B %d S0G0Amp %d, S3G2Amp %d, S5G0Dis %d, S7G1Amp %d,  e0 %d, e5 %d, OK %d", 
						b, 
						ppGateData[0][0].amplitudeMetric, 
						ppGateData[3][2].amplitudeMetric, 
						ppGateData[5][0].distanceMetric,
						ppGateData[7][1].amplitudeMetric, 
						pEncoders[0], 
						pEncoders[5], 
						OK);
				}
			}
		}
	}
*/
	return NDTS_BTrue;
}


// Remove after InspectionWare integration here!
//NDTS_Boolean HLPRX_ExtractDataFromSnapshot(unsigned long step, short *pBuffer, unsigned long scanBufferBytes, short *pAmplitude, short *pDepth)
//{
//	NDTS_Boolean isOk = DC_GetAScanAndGateDataFromSnapshot(cardSet.rqIndex, step, pBuffer, scanBufferBytes / 2, pAmplitude, pDepth);
//	return isOk;
//}


NDTS_Boolean HLPRX_GetAScanAndGateDataFromSnapshot(unsigned long step, short *pBuffer, unsigned long numberOfBufferValues, short *pAmplitude, short *pDepth)
{
	NDTS_Boolean isOk = DC_GetAScanAndGateDataFromSnapshot(cardSet.rqIndex, step, pBuffer, numberOfBufferValues, pAmplitude, pDepth);
	return isOk;
}

NDTS_Boolean HLPRX_GetGateAndEncoderDataFromSnapshot(unsigned long bScanIndex, NDTS_GateOutput *pGateData, int nSteps, int nGates, long *pEncoders, int nEncoders)
{
	if (bScanIndex >= DC_ReturnNumberOfBScansInSnapshot(cardSet.rqIndex))
	{
		NDTS_LogLine(NDTS_LLWarning, "HLPRX_GetGateAndEncoderDataFromSnapshot bScanIndex %d >= B Scans in Snapshot of %d", 
			bScanIndex, DC_ReturnNumberOfBScansInSnapshot(cardSet.rqIndex));
		return NDTS_BFalse;
	}
    
	if (!DC_GetFullBScanGateDataFromSnapshot(cardSet.rqIndex, bScanIndex, pGateData, nSteps, nGates))
		return NDTS_BFalse;

	if (!DC_GetEncoderDataFromSnapshot(cardSet.rqIndex, bScanIndex, pEncoders, nEncoders))
		return NDTS_BFalse;

	return NDTS_BTrue;
}


NDTS_Boolean HLPRX_GetGateDataFromSnapshot(unsigned long bScanIndex, unsigned long stepIndex, int *pGateAmpData, int *pGateToFData, int nGates)
{
	int j;

	NDTS_GateOutput gateData[NDTS_MAXIMUM_NUMBER_OF_GATES_COMPILE_TIME];

	if (bScanIndex >= DC_ReturnNumberOfBScansInSnapshot(cardSet.rqIndex))
	{
		NDTS_LogLine(NDTS_LLWarning, "HLPRX_GetGateDataFromSnapshot bScanIndex %d >= B Scans in Snapshot of %d", 
			bScanIndex, DC_ReturnNumberOfBScansInSnapshot(cardSet.rqIndex));
		return NDTS_BFalse;
	}

	if (!DC_GetOneStepGateDataFromSnapshot(cardSet.rqIndex, bScanIndex, stepIndex, gateData, nGates))
		return NDTS_BFalse;

	for(j = 0; j < nGates; j++)
	{
		if (gateData[j].wasAmplitudeMeasured)
			pGateAmpData[j] = gateData[j].amplitudeMetric;
		else
			pGateAmpData[j] = 0;

		if(gateData[j].wasDistanceMeasured)
			pGateToFData[j] = gateData[j].distanceMetric;
		else
			pGateToFData[j] = 0;
	}
	return NDTS_BTrue;
}

NDTS_Boolean HLPRX_GetEncoderDataFromSnapshot(unsigned long bScanIndex, long *pEncoders, int nEncoders)
{
	if (bScanIndex >= DC_ReturnNumberOfBScansInSnapshot(cardSet.rqIndex))
	{
		NDTS_LogLine(NDTS_LLWarning, "HLPRX_GetGateAndEncoderDataFromSnapshot bScanIndex %d >= B Scans in Snapshot of %d", 
			bScanIndex, DC_ReturnNumberOfBScansInSnapshot(cardSet.rqIndex));
		return NDTS_BFalse;
	}
    
	if (!DC_GetEncoderDataFromSnapshot(cardSet.rqIndex, bScanIndex, pEncoders, nEncoders))
		return NDTS_BFalse;

	return NDTS_BTrue;
}


/*NDTS_Boolean HLPRX_SetSnapshotParameters(unsigned long step, NDTS_SnapshotDataSelection selection, unsigned long gateIndex)
{
	// Deprecated remove after InspectionWare integration - use function below.
	int i = gateIndex;
	i=step;
	i=selection;

	return DC_SetAScanSelectionGate(cardSet.rqIndex, step, gateIndex);
}
*/

NDTS_Boolean HLPRX_ResetEncoder(unsigned long encoderIndex)
{
	return RQ_SetResetEncoderOutput(cardSet.rqIndex, encoderIndex);
}


NDTS_Boolean HLPRX_SetAScanSelectionGate(unsigned long step, unsigned long gateIndex)
{
	return DC_SetAScanSelectionGate(cardSet.rqIndex, step, gateIndex);
}

// The range, delay and gates values may need to be rewritten by the application following a change of 
// sample frequency.  Unlike altering the oversampling, the values are not updated to keep the scan looking the same.
// Adjusting the sampling frequency is not recommended:-
// The TCG lag will be worse.
// The firmware may have memory writing problems when setting the sample frequency to < 100Mhz.
HLPRX_API NDTS_Boolean CALL_CONV HLPRX_SetSamplingFrequency(unsigned long frequencyInHz)
{
	NDTS_Boolean isOK;
	unsigned long i;

	// The minimum sampling frequency is 50MHz as the minimum for that ADC clock is 25MHz and the sampling frequency is twice that.
	if (frequencyInHz > 100000000 || frequencyInHz < 50000000)
		return NDTS_BFalse;

	RQ_Lock(cardSet.rqIndex);
	RQ_PauseSequence(cardSet.rqIndex);

	isOK = RQ_SetSamplingFrequency(cardSet.rqIndex, frequencyInHz);
	if (isOK == NDTS_BFalse) 
		goto exit;

	// Update the range for each channel as the PRX has fixed 100MHz freq and the RQ freq has just changed.  Both cards have range registers.
	// All sorts of things can go wrong if the PRX range doesn't match the RQ range.
	for (i=0; i < cardSet.numberOfSteps; i++)
	{
		isOK = HLPRX_SetRange(i, cardSet.range[i]);
		if (isOK == NDTS_BFalse) 
			goto exit;
	}
exit:
	RQ_ResumeSequence(cardSet.rqIndex);
	RQ_Unlock(cardSet.rqIndex);
	return isOK;
}

// The pulseLength is in sampleClockPeriod / 2 steps.
// The RapiDAQ will generate a negative going pulse of the specified length.
// Applications normally specify the centre frequency of the probe.  
// (1) The application converts frequency to pulseLength using length = (1 / f) * sampleClockRate.
// (2) The probe seems to see the negative going pulse of one half of a waveform.
// (1) and (2) cancel each other out and the probe gets the frequency it expects.
// However, the negative going pulse on the scope appears half as wide as if the pulseLength were expressed in the sampleClockRate

// Since RQ and PRX may run at different frequencies.  RQ is the definitive one so we use our
// best approximation for PRX.  This is used for power calculations and is likely to be important only
// with long pulse widths where the rounding errors will be small.
HLPRX_API NDTS_Boolean CALL_CONV HLPRX_SetTransducerPulseWidth(unsigned long step, unsigned long pulseWidth)
{
	NDTS_Boolean isOk;
	unsigned long rqClockFreq;
	float pulseSecs;
	unsigned long prxTicks;

	if (!saveStepAndPause(step)) return NDTS_BFalse;

    isOk = RQ_GetSamplingFrequency(cardSet.rqIndex, &rqClockFreq);
	if (isOk == NDTS_BFalse) 
		goto exit;

	pulseSecs = (float)pulseWidth / ((float)rqClockFreq * 2.0f);
	prxTicks = (unsigned long)(pulseSecs * 200000000.0); // PRX pulse width is 5nS ticks

	// Leave the RapiDaq on a wide pulse if driving the Sonatest external Mux
	if (cardSet.sonatestSerialMuxValue == 0)
	{
		isOk = RQ_SetTransducerPulseWidth(cardSet.rqIndex, pulseWidth);
	}

	// The PRX takes the pulse width from the RQ but the PRX SDK uses the pulse width to calculate safe operating duty cycle.
	isOk &= PRX_SetPulseWidth(cardSet.prxIndices[0], step, prxTicks);

exit:
	restoreStepAndResume(); 
	return isOk ;
}

HLPRX_API NDTS_Boolean CALL_CONV HLPRX_GetTransducerPulseWidth(unsigned long stepIndex, unsigned long *pPulseWidth)
{
	NDTS_Boolean isOk;
	if (!saveStepAndPause(stepIndex)) return NDTS_BFalse;

	isOk = RQ_GetTransducerPulseWidth(cardSet.rqIndex, pPulseWidth);
	if (!isOk)
		goto exit;
exit:
	restoreStepAndResume(); 
	return isOk ;
}

// For users who aren't interested in TCG there's this function that writes a constant gain
// Since RQ will accept any number of points we just send 1 with a zero value
// and then submit the gain that we want
HLPRX_API NDTS_Boolean CALL_CONV HLPRX_SetGain(unsigned long stepIndex, short gain)
{
	NDTS_Boolean isOk;
	unsigned long stepLoop = 0;
	short dummy[1];

	if (gain > 800 || gain < 0)
	{
		return NDTS_BFalse;
	}

	if (!saveStepAndPause(stepIndex)) return NDTS_BFalse;

	cardSet.gain[stepIndex] = gain;

	// The entire TCG curve must be applied if any step changes - this is the way the firmware works.
	dummy[0] = 0;
	for (stepLoop = 0; stepLoop < cardSet.numberOfSteps; stepLoop++)
	{
		isOk = RQ_SubmitTCGCurve(cardSet.rqIndex, stepLoop, dummy, 1, cardSet.gain[stepLoop]);
		if (!isOk)
			goto exit;
	}

	isOk = RQ_SetEnhancedTCG(cardSet.rqIndex); 
	if (!isOk)
		goto exit;
exit:
	restoreStepAndResume(); 
	return isOk ;}

HLPRX_API NDTS_Boolean CALL_CONV HLPRX_GetGain(unsigned long stepIndex, short *pGain)
{
	*pGain = cardSet.gain[stepIndex];
	return NDTS_BTrue;
}


HLPRX_API NDTS_Boolean CALL_CONV HLPRX_SetPulserVoltage(unsigned long volts)
{
	unsigned long value = volts * 4095 / 400;  // Register range is 0..4095, volts range 0..400V
	NDTS_Boolean isOk;
	if (!saveStepAndPause(0)) return NDTS_BFalse;

	Sleep(50);
	isOk = PRX_SetHVLevel(cardSet.prxIndices[0], value);
	if (!isOk)
		goto exit;

exit:
	restoreStepAndResume(); 
	return isOk ;
}


HLPRX_API NDTS_Boolean CALL_CONV HLPRX_SetPulserVoltageEnable(NDTS_Boolean isEnabled)
{
	NDTS_Boolean isOk;
	if (!saveStepAndPause(0)) return NDTS_BFalse;

	isOk = PRX_SetHVEnable(cardSet.prxIndices[0], isEnabled);
	if (!isOk)
		goto exit;

exit:
	restoreStepAndResume(); 
	return isOk ;
}

HLPRX_API NDTS_Boolean CALL_CONV HLPRX_StartSequence()
{
	return 	RQ_StartSequence(cardSet.rqIndex);
}

HLPRX_API NDTS_Boolean CALL_CONV HLPRX_StopSequence()
{
	return 	RQ_StopSequence(cardSet.rqIndex);
}

HLPRX_API NDTS_Boolean CALL_CONV HLPRX_AddGate(unsigned long stepIndex, RQ_Gate *pGate)
{
	NDTS_Boolean isOk;
	if (!saveStepAndPause(stepIndex)) return NDTS_BFalse;

	isOk = RQ_AddGate(cardSet.rqIndex, pGate);
	if (isOk == NDTS_BFalse)
		goto exit;

exit:
	restoreStepAndResume(); 
	return isOk ;

}

HLPRX_API NDTS_Boolean CALL_CONV HLPRX_GetGate(unsigned long stepIndex, unsigned long gateIndex, RQ_Gate *pGate)
{
	NDTS_Boolean isOk;

	isOk = RQ_GetGate2(cardSet.rqIndex, stepIndex, gateIndex, pGate);
	if (isOk == NDTS_BFalse)
		goto exit;

exit:
	return isOk ;
}

HLPRX_API unsigned long CALL_CONV HLPRX_ReturnMaximumNumberOfGates()
{
	return RQ_ReturnMaximumNumberOfGates(NDTS_BTrue);  // True because sequenced firmware
}

// The HLPRX error log combines the error logs from the other DLL's
HLPRX_API unsigned long HLPRX_ReturnNumberOfErrorLogLines()
{
	return NDTS_ReturnNumberOfErrorLogLines() ;
}

HLPRX_API NDTS_Boolean HLPRX_GetErrorLogLine(unsigned long lineNumber, char *pMessageBuffer, unsigned long numberOfBytesInMessageBuffer)
{
	return NDTS_GetErrorLogLine(lineNumber, pMessageBuffer, numberOfBytesInMessageBuffer) ;
}


HLPRX_API NDTS_Boolean HLPRX_ClearErrorLog() 
{
	return NDTS_ClearErrorLog() ;
}

// Replace with HLPRX_SetRectication after IW integration
HLPRX_API NDTS_Boolean HLPRX_SetRectifierMode(RQ_Rectification mode) 
{
	return RQ_SetRectifierMode(cardSet.rqIndex, mode);
}

HLPRX_API NDTS_Boolean HLPRX_SetRectification(unsigned long stepIndex, RQ_Rectification mode) 
{
	NDTS_Boolean isOK;

	if (!saveStepAndPause(stepIndex)) return NDTS_BFalse;

	isOK = RQ_SetRectifierMode(cardSet.rqIndex, mode);
	if (!isOK)
		goto exit;

exit:
	restoreStepAndResume(); 
	return isOK;

}

HLPRX_API NDTS_Boolean HLPRX_GetRectification(unsigned long stepIndex, RQ_Rectification *pMode) 
{
	NDTS_Boolean isOK;

	if (!saveStepAndPause(stepIndex)) return NDTS_BFalse;

	isOK = RQ_GetRectifierMode(cardSet.rqIndex, pMode);
	if (!isOK)
		goto exit;

exit:
	restoreStepAndResume();
	return isOK;
}


// These functions will need updating for multi-PRX support.  
// At the moment, IW is also making lower level calls to the PRX / RQ SDK's directly

// A function similar to SetTransmitChannel could take a bit mask to allow multiple channels to fire at once 
// but parallel firing isn't too much use without parallel receiving.

HLPRX_API NDTS_Boolean HLPRX_GetNumberOfChannels(unsigned long *numberOfChannels) 
{
	*numberOfChannels = 8;
	return NDTS_BTrue;
}

HLPRX_API NDTS_Boolean HLPRX_SetTransmitChannel(unsigned long stepIndex, unsigned long channelIndex) 
{
	NDTS_Boolean isOK;

	if (!saveStepAndPause(stepIndex)) return NDTS_BFalse;

	isOK = PRX_SetTransmitPulseEnable(0, stepIndex, 1 << channelIndex);
	if (!isOK)
		goto exit;

exit:
	restoreStepAndResume();
	return isOK;
}

HLPRX_API NDTS_Boolean HLPRX_SetStepToParallelFire(unsigned long stepIndex) 
{
	NDTS_Boolean isOK;

	if (!saveStepAndPause(stepIndex)) return NDTS_BFalse;

	isOK = PRX_SetTransmitPulseEnable(0, stepIndex, 0xff);
	if (!isOK)
		goto exit;

exit:
	restoreStepAndResume();
	return isOK;
}

HLPRX_API NDTS_Boolean HLPRX_SetReceiveChannel(unsigned long stepIndex, unsigned long channelIndex) 
{
	NDTS_Boolean isOK;

	if (!saveStepAndPause(stepIndex)) return NDTS_BFalse;

	isOK = PRX_SetReceiveChannel(0, stepIndex, channelIndex);
	if (!isOK)
		goto exit;

exit:
	restoreStepAndResume();
	return isOK;
}

HLPRX_API NDTS_Boolean HLPRX_SetChannelPulseEchoOrThroughTransmission(unsigned long channelIndex, NDTS_Boolean isPulseEcho) 
{
	NDTS_Boolean isOK;
	unsigned long flags = 0;
	if (!PRX_GetPulseEchoEnable(0, &flags))
		return NDTS_BFalse;
	if (isPulseEcho)
		flags |= (1 << channelIndex);
	else
		flags &= ~(1 << channelIndex);

	if (!saveStepAndPause(channelIndex)) return NDTS_BFalse;

	isOK = PRX_SetPulseEchoEnable(0, flags);
	if (!isOK)
		goto exit;

exit:
	restoreStepAndResume();
	return isOK;
}




HLPRX_API NDTS_Boolean HLPRX_SetFullAScanCapture(NDTS_Boolean doFullAScanCapture)
{
	return DC_SetFullAScanCapture(cardSet.rqIndex, doFullAScanCapture);
}

HLPRX_API NDTS_Boolean CALL_CONV HLPRX_GetFullAScanDataFromSnapshot(unsigned long bScanIndex, unsigned long step, short *pBuffer, unsigned long numberOfBufferValues)
{
	return DC_GetFullAScanDataFromSnapshot(cardSet.rqIndex, bScanIndex, step, pBuffer, numberOfBufferValues);
}

HLPRX_API NDTS_Boolean CALL_CONV HLPRX_GetFullAScanDataFromSnapshotForAllBScans(unsigned long step, short *pBuffer, unsigned long numberOfBufferValues)
{
	unsigned long bScanIndex;
	unsigned long aScanRange = 	cardSet.range[step];
	short *pOffset = pBuffer;
	unsigned long bufValuesRemaining = numberOfBufferValues;

	//NDTS_LogLine(NDTS_LLEmerg, "Entering with %d b scans step %d numb buf %d\n", DC_ReturnNumberOfBScansInSnapshot(cardSet.rqIndex), step, numberOfBufferValues);

	for (bScanIndex = 0; bScanIndex < DC_ReturnNumberOfBScansInSnapshot(cardSet.rqIndex); bScanIndex++)
	{
        if (!DC_GetFullAScanDataFromSnapshot(cardSet.rqIndex, bScanIndex, step, pOffset, bufValuesRemaining))
		{
     		NDTS_LogLine(NDTS_LLEmerg, "Exiting on index %d\n", bScanIndex);
			return NDTS_BFalse;
		}
		pOffset += aScanRange;
		bufValuesRemaining -= aScanRange;
		if (bufValuesRemaining <=aScanRange)
		{
     		NDTS_LogLine(NDTS_LLEmerg, "Buf full - remain %d range %d Exiting on index %d\n", bufValuesRemaining, aScanRange,  bScanIndex);
			return NDTS_BFalse;
		}

	}
	return NDTS_BTrue;
}

// Non re-enterable saving and restoring of the current state, pausing and locking etc. that
// can be called by the API functions on entry and exit
#define STEP_NOT_ASSIGNED 9999
static unsigned long g_oldStep = STEP_NOT_ASSIGNED;
NDTS_Boolean saveStepAndPause(unsigned long newStep)
{
	_ASSERTE(g_oldStep == STEP_NOT_ASSIGNED);
	if (!RQ_GetSequenceStep(cardSet.rqIndex, &g_oldStep))
		return NDTS_BFalse;

	RQ_Lock(cardSet.rqIndex);
	RQ_PauseSequence(cardSet.rqIndex);

	if (!RQ_SetSequenceStep(cardSet.rqIndex, newStep))
	{
		restoreStepAndResume();
		return NDTS_BFalse;
	}

	g_oldStep = newStep;
	return NDTS_BTrue;
}

void restoreStepAndResume()
{
//	unsigned long  dummy;
	_ASSERTE(g_oldStep != STEP_NOT_ASSIGNED);
//	DC_SwapSnapshotDataBank(cardSet.rqIndex, NULL);

	RQ_SetSequenceStep(cardSet.rqIndex, g_oldStep);
	PRX_Commit(cardSet.rqIndex); // Dummy commit to reset the PRX
	RQ_ResumeSequence(cardSet.rqIndex);
	RQ_Unlock(cardSet.rqIndex);
	g_oldStep = STEP_NOT_ASSIGNED;
	//HLPRX_TakeSnapshot(&dummy);
}

