#include <stdio.h>
#include <io.h>
#include <windows.h>
#include <stdarg.h>
#include <time.h>
#include <crtdbg.h>
#include <math.h>

#include "NDTSCommonHAL.h"
#include "RQInternal.h"
#include "RQStrings.h"
#include "RQOfflineInternal.h"
#include "RQMemoryFactoryInternal.h"
#include "RQError.h"
#include "RQRawScan.h"
#include "NDTSError.h"
#include "NDTSCommonHALProtected.h"

//RQ_API PCI_State *pPCIStates = 0 ; // dynamically allocated array for RapiDAQ descriptors
RQ_API RQ_State *pBoardStates = 0 ;

NDTS_Boolean isEnumerated = NDTS_BFalse ;

RQ_GlobalState globalState = 
{
	NDTS_BFalse,
	0,
	0,
	0,
	0,
} ;


RQ_API NDTS_Boolean CALL_CONV RQ_InitialiseProtected(unsigned long cardIndex, RQ_AnalogueInputRange defaultInputRange, NDTS_Boolean isSequenced)
{
	NDTS_Boolean isOK ;
	NDTS_Boolean isSampleClockStabilised ;
	unsigned short TCGValue = 255 ;
	char pDSPFilename[256], pPerFilename[256] ;
	unsigned long samplingVersionNumber, samplingRevisionNumber,
				  peripheralVersionNumber, peripheralRevisionNumber ;
	RQ_State *pRQState ;
	int i, j, k ;
	int sfv, pfv;
	char path[256];

	if (cardIndex >= globalState.pciState.numberOfRapiDAQBoards) return NDTS_BFalse;

	if (globalState.pfnInitStatusCallback != 0)
	{
		(*globalState.pfnInitStatusCallback)(RQ_ISStart, RQ_ISSOK) ;
		(*globalState.pfnInitStatusCallback)(RQ_ISCheckDriversPresent, RQ_ISSChecking) ;
		(*globalState.pfnInitStatusCallback)(RQ_ISCheckDriversPresent, RQ_ISSOK) ;
	}

	pRQState = &pBoardStates[cardIndex];

	// Create the critical section, RQ_Lock(cardIndex) and inititalise SDK internal data structures

	if (isSequenced)
	{
		// Sequenced - either use the default values or override with ini file specified values
		sfv = RQ_GetDefaultValueInt(RQdef_DSPversion);
		pfv = RQ_GetDefaultValueInt(RQdef_PERversion);
	}
	else
	{
		// Non sequenced (i.e. WinRS) always use the firmware specified below
		// DSP FPGA version 0xx has gate 7 curved, 1xx has normal gate 7
		// DSP FPGA version d has trigger holdoff but no array normalisation
		// DSP FPGA version e has trigger holdoff and array normalisation
		// DSP FPGA version x0f has some bug fixes gates with more than 14 peaks are handled properly
		// and the gate trigger holdoff timer is not reset if additional peaks are encountered
		// DSP FPGA version 110 has an issue fixed where largest peak is not evaluated correctly if a gate ends
		// with the signal above the threshold
		// DSP FPGA version 111 includes (faulty) CRC check
		// DSP FPGA version 112 includes (possibly working) CRC check
		// peripheral FPGA 0x00d has jittery encoder fixed
		// DSP FPGA version 113 includes TCG normalisation fix (bug 426)
		// DSP FPGA version 114 includes a working CRC check on the A Scan data.
		// DSP FPGA version 115 includes CRC check on the gated data.
		// DSP FPGA version 116 Initial gates work March 06 - DO NOT USE - bad memory writes
		// DSP FPGA version 117 BScan Sync pulse - DO NOT USE - bad memory writes
		// DSP FPGA version 118 has memory write fixed - DO NOT USE - doesn't write encoder data correctly
		// DSP FPGA version 119 updated with zero crossing gate fix (bug 691) and bank swapping race condition (bug 693) - DO NOT USE - doesn't write encoder data correctly
		// Peripheral FPGA 0x00E Jonathans March 06 work ?
		// Peripheral FPGA 0x00F Jonathans March 06 work ?
		// Peripheral FPGA 0x010 Jonathans March 06 work with zero crossing gates fix (bug 691)
		// DSP FPGA version 11A has encoder memory write bug fix - DO NOT USE DDS write fails.
		// DSP FPGA version 11B has DDS write bug fixed - DO NOT USE - bad memory writes
		// DSP FPGA version 11C has another memory write fix
		// DSP FPGA version 11D has BScan clock pulse for Faro arm fix (untested)
		// DSP FPGA version 11E has data full lockup (bug 710) fix
		// DSP FPGA version 11F disables Arm sync pulse on startup until enabled with the appropriate bit
		// DSP FPGA version 120 Bug 716 work in progress.  Holds off encoder triggers whilst a BScan is being written.  Makes tearing in CScan much better but may not be fully fixed?
		// Peripheral FPGA 0x011 Allows Arm clicks to be stored in encoders 4 and 5.  
		// Peripheral FPGA 0x012 writes encoder values continuously to raw scans when free running.
		// Peripheral FPGA 0x013 has dual encoder support although this is not currently used in WinRS
		// PER_V014 Fix to dual encoder count that could count twice on a change between encoders.
		// PER_V015 Added 1/256 resolution fractional clicks per B-scan.
		// DSP_V121.BIN 19 Oct 06 Force encoder data to be output with A-Scan and C-Scan data for the last element of B-Scan under all conditions.
		// DSP_V122.BIN upto 64K samples of range.

		sfv = 0x122;
		pfv = 0x015;
	}

	if (RQ_StructuresInitialise(cardIndex, sfv, pfv) == NDTS_BFalse) return NDTS_BFalse;
	if (RQOffline_Initialise() == NDTS_BFalse) return NDTS_BFalse;

	pRQState->isInitialised = NDTS_BTrue ;

	// We report the card as initialised here so that if initialisation fails and we call RQ_Uninitialise then
	// the function will attempt to close the card down fully


	if (globalState.pfnInitStatusCallback != 0)
		(*globalState.pfnInitStatusCallback)(RQ_ISCheckPCICommunication, RQ_ISSChecking) ;

	// Initialise the pci PCI interface
	if (RQ_PCIInitialise(cardIndex) == NDTS_BFalse)
	{
		if (globalState.pfnInitStatusCallback != 0)
			(*globalState.pfnInitStatusCallback)(RQ_ISCheckPCICommunication, RQ_ISSFail) ;

		goto initFail;
	}

	if (globalState.pfnInitStatusCallback != 0)
		(*globalState.pfnInitStatusCallback)(RQ_ISCheckPCICommunication, RQ_ISSOK) ;

	// FROG current thinking is to put the BIN files in the same directory as RQ.DLL
	strcpy(path, globalState.iniFileName);
	path[strlen(path) - 6] = '\0'; // truncate to path

	sprintf(pDSPFilename, "%sdsp_v%03x.bin", path, (int) sfv) ;
	sprintf(pPerFilename, "%sper_v%03x.bin", path, (int) pfv) ;

	if (globalState.pfnInitStatusCallback != 0)
		(*globalState.pfnInitStatusCallback)(RQ_ISCheckFirmwarePresent, RQ_ISSChecking) ;

	if (RQ_DumpBinFiles(cardIndex, pDSPFilename, pPerFilename, 0) == NDTS_BFalse)
	{
		if (globalState.pfnInitStatusCallback != 0)
			(*globalState.pfnInitStatusCallback)(RQ_ISCheckFirmwarePresent, RQ_ISSFail) ;

		goto initFail;
	}

	if (globalState.pfnInitStatusCallback != 0)
		(*globalState.pfnInitStatusCallback)(RQ_ISCheckFirmwarePresent, RQ_ISSOK) ;


	Sleep(1) ;


	if (RQ_SetSystemRegister(cardIndex, RQ_SYSTEM_COMMAND_REGISTER, 0x00000011) == NDTS_BFalse)
		goto initFail;

	if (globalState.pfnInitStatusCallback != 0)
		(*globalState.pfnInitStatusCallback)(RQ_ISCheckFirmwareFunctioning, RQ_ISSChecking) ;

	if (RQ_SystemFPGAInversionTest(cardIndex) == NDTS_BFalse)
	{
		if (globalState.pfnInitStatusCallback != 0)
			(*globalState.pfnInitStatusCallback)(RQ_ISCheckFirmwareFunctioning, RQ_ISSFail) ;

		goto initFail;
	}

	if (globalState.pfnInitStatusCallback != 0)
		(*globalState.pfnInitStatusCallback)(RQ_ISCheckFirmwareFunctioning, RQ_ISSOK) ;

	if (RQ_SetTCGValues(cardIndex, &TCGValue, 1) == NDTS_BFalse) goto initFail;
	if (RQ_SetTriggerSelect(cardIndex, RQ_TInternal) == NDTS_BFalse) goto initFail;
	if (RQ_GetEEPROMvalues(cardIndex) == NDTS_BFalse) goto initFail;

	Sleep(1) ;

	k = 0 ;

	if (globalState.pfnInitStatusCallback != 0)
		(*globalState.pfnInitStatusCallback)(RQ_ISCheckSampleClockStabilisation, RQ_ISSChecking) ;

	do
	{
		j = 0 ;

		do
		{
			if (RQ_SetDDSRegister(cardIndex, 0x00000000, 0x0000001d) == NDTS_BFalse) goto initFail;
			if (RQ_SetDDSRegister(cardIndex, 0x00000044, 0x0000001e) == NDTS_BFalse) goto initFail;
			if (RQ_SetDDSRegister(cardIndex, 0x00000001, 0x0000001f) == NDTS_BFalse) goto initFail;
			if (RQ_SetDDSRegister(cardIndex, 0x00000050, 0x00000020) == NDTS_BFalse) goto initFail;
			if (RQ_SetSamplingFrequency(cardIndex, 66000000) == NDTS_BFalse) goto initFail;
			Sleep(1) ;
			if (RQ_GetHasSampleClockStabilised(cardIndex, &isSampleClockStabilised) == NDTS_BFalse) goto initFail;

			j++ ;
		}
		while (isSampleClockStabilised == NDTS_BFalse && j < 10) ;

		if (isSampleClockStabilised == NDTS_BFalse && j == 10)
		{
			RQ_HandleRQError(RQ_Initialise_SampleClockNotStabilised) ;
			isOK = NDTS_BFalse ;
			continue;
		}

		Sleep(1) ;

		isOK = RQ_SetPeripheralRegister(cardIndex, RQ_SENSOR_ARRAY_SETUP, 0) ;
		if (!isOK)
		{
			continue;
		}

		k++ ;
	}
	while (!isOK && (k < 10)) ;

	if (globalState.pfnInitStatusCallback != 0)
		(*globalState.pfnInitStatusCallback)(RQ_ISCheckSampleClockStabilisation, isOK ? RQ_ISSOK : RQ_ISSFail) ;

	// Check that the files have loaded and that the files have not been renamed - renamed files
	// are likely to cause problems e.g. with determining if CRC is enabled.
	isOK =  RQ_GetSamplingFirmwareVersion(cardIndex, &samplingVersionNumber) ;
	if (!isOK)
		goto initFail ;
	isOK = RQ_GetSamplingFirmwareRevision(cardIndex, &samplingRevisionNumber) ;
	if (!isOK)
		goto initFail ;
	isOK = RQ_GetPeripheralFirmwareVersion(cardIndex, &peripheralVersionNumber) ;
	if (!isOK)
		goto initFail ;
	isOK = RQ_GetPeripheralFirmwareRevision(cardIndex, &peripheralRevisionNumber) ;
	if (!isOK)
		goto initFail ;

	// The revision number contains an extra 1, e.g. File per_v213.bin register 2113, File Per_v00d.bin register 010d
	if (((unsigned long)sfv != ((samplingVersionNumber << 8)  | (samplingRevisionNumber & 0xff))) ||
		((unsigned long)pfv != ((peripheralVersionNumber << 8)  | (peripheralRevisionNumber & 0xff))))
	{
		RQ_HandleRQError(RQ_Initialise_FirmwareVersionDoesNotMatchFileName) ;
		goto initFail ;
	}

	RQ_ClearOutOfRangeAccessCount(cardIndex);

	// This needs to be zero to force SetPingPongSequence to do its malloc.
	// Without this init - uninit - init - uninit will crash as the second init
	// doesn't do a malloc and then the second uninit will attempt to free memory
	// that has already been free'd in the first uninit.
	{
		unsigned short pPingPongSequence[RQ_PINGPONG_SEQUENCE_LENGTH] ;
		unsigned short reversePingPongSequence[RQ_PINGPONG_SEQUENCE_LENGTH] ;

		pRQState->pPingPongSequence = 0;
		for (i = 0 ; i < RQ_PINGPONG_SEQUENCE_LENGTH ; i++)
		{
			pPingPongSequence[i] = (unsigned short)i ;
			reversePingPongSequence[i] = (unsigned short)i ;
		}
		// Must do after the PCI initialise
		RQ_SetPingPongSequence(cardIndex, pPingPongSequence, reversePingPongSequence) ;
	}

	if (isSequenced)
	{
		if (!RQ_SetStepsInSequence(cardIndex, 1)) goto initFail;
		if (!RQ_SetSequenceStep(cardIndex, 1)) goto initFail;
	}

	if (RQ_SetRepetitionRate(cardIndex, RQ_GetDefaultValueInt(RQdef_RepetitionRate)) == NDTS_BFalse) goto initFail;
	if (RQ_SetAnalogueInput(cardIndex, RQ_AIInput0) == NDTS_BFalse) goto initFail;
	{
		int rangeFromIniFile = RQ_GetDefaultValueInt(RQdef_AnalogueInputRange);
		int inputRange = (rangeFromIniFile == 999) ?  defaultInputRange : rangeFromIniFile;
		if (RQ_SetAnalogueInputRange(cardIndex, inputRange) == NDTS_BFalse) goto initFail;
	}
	if (RQ_SetZeroOffset(cardIndex, RQ_GetDefaultValueInt(RQdef_DCOffset)) == NDTS_BFalse) goto initFail;
	if (RQ_SetSensorArrayApertureSize(cardIndex, (RQ_ApertureSize)RQ_GetDefaultValueInt(RQdef_ApertureSize)) == NDTS_BFalse) goto initFail;

	if (globalState.pfnInitStatusCallback != 0)
		(*globalState.pfnInitStatusCallback)(RQ_ISEnd, RQ_ISSOK) ;

	return NDTS_BTrue ;

initFail:
	if (globalState.pfnInitStatusCallback != 0)
		(*globalState.pfnInitStatusCallback)(RQ_ISEnd, RQ_ISSFail) ;

	RQ_Uninitialise(cardIndex);
	return NDTS_BFalse;
}

// Intitialise the pci API to the cardIndex
NDTS_Boolean CALL_CONV  RQ_PCIEnumerateProtected()
{
	/*
	// Old
	RETURN_CODE PciReturnCode ;
	unsigned long devNum;
	DEVICE_LOCATION device;
	HANDLE hDevice;
	U32 d;
	RETURN_CODE rc;
	*/

	NDTS_Boolean isOK ;
	unsigned long d ;

	if (globalState.isEnumerated == NDTS_BTrue)
	{
		return NDTS_BTrue;
	}

	isOK = NDTS_HAL_InitialiseCardType(0x10b5, 0x5602, NDTS_SIDRapiDAQ, &globalState.pciState.numberOfRapiDAQBoards) ;
	if (!isOK)
	{
		return NDTS_BFalse ;
	}


	/*
	// Old
	devNum = FIND_AMOUNT_MATCHED;
	
	device.BusNumber = (U8) -1 ; 
	device.SlotNumber = (U8) -1 ; 
	device.VendorId = (U16) 0x10b5 ; // pci Vendor ID ; 
	device.DeviceId = (U16) -1 ; 
	device.SerialNumber[0] = 0 ; 
	
	PciReturnCode = PlxPciDeviceFind(&device, &devNum) ;
	if (PciReturnCode != ApiSuccess)
	{
		RQ_HandleRQError(RQ_Initialise_FindPCIDeviceFail) ;
		return NDTS_BFalse; // there's a problem so report no devices found
	}

	// Now attempt to open each device and see which are RapiDAQ cards
	for (d = 0; d < devNum; d++)
	{
		device.BusNumber = (U8) -1 ; 
		device.SlotNumber = (U8) -1 ; 
		device.VendorId = (U16) 0x10b5 ; // pci Vendor ID ; 
		device.DeviceId = (U16) -1 ; 
		device.SerialNumber[0] = 0 ; 
		PciReturnCode = PlxPciDeviceFind(&device, &d) ;
		if (PciReturnCode != ApiSuccess)
		{

			RQ_HandleRQError(RQ_Initialise_FindPCIDeviceFail) ;
			continue;
		}

		// Open the device and see what it is
		PciReturnCode = PlxPciDeviceOpen(&device, &hDevice) ;
		if (PciReturnCode != ApiSuccess)
		{

			RQ_HandleRQError(RQ_Initialise_PCIDeviceOpenFail) ;
			continue;
		}

		PlxSerialEepromPresent(hDevice, &rc);
		if (rc == ApiSuccess)
		{
			U32 secondaryId;
			const U16 secondary_id_offset_in_eeprom = 0x44;

			rc = PlxSerialEepromReadByOffset(hDevice, secondary_id_offset_in_eeprom, &secondaryId);

			if(rc != ApiSuccess)
			{
				// Couldn't read secondary device ID
			}
			else
			{
				if (secondaryId == NDTS_SIDRapiDAQ)
				{
					globalState.pciState.numberOfRapiDAQBoards++;
				}
				else if (secondaryId == RQ_SIDPRX)
				{
					globalState.pciState.numberOfPRXBoards++;
				}
				else
				{
					globalState.pciState.numberOfUninitialisedBoards++;
				}
			}
		}

		PciReturnCode = PlxPciDeviceClose(hDevice) ;
		if (PciReturnCode != ApiSuccess)
		{

			RQ_HandleRQError(RQ_Uninitialise_PCIDeviceCloseFail) ;
			continue;
		}
	}
	*/

	// Next we allocate memory for the cardIndex descriptors
	// Free the array if we have one already
	/*
	// Old
	if (pPCIStates != 0)
	{
		free(pPCIStates);
		pPCIStates = 0;
	}
	*/

	// Old
	//if (globalState.pciState.numberOfRapiDAQBoards == 0)
	//{
	//	globalState.isEnumerated = TRUE;
	//	return NDTS_BTrue; // no cards found - not necessarily an error
	//}

	// Old
	/*
	pPCIStates = malloc(globalState.pciState.numberOfRapiDAQBoards * sizeof(PCI_State));
	
	if (pPCIStates == 0)
	{
		// Malloc failed for RapiDAQ descriptor array
	}
	*/
	
	if (pBoardStates != 0)
	{
		free(pBoardStates);
		pBoardStates = 0;
	}

	pBoardStates = malloc(globalState.pciState.numberOfRapiDAQBoards * sizeof(RQ_State));
	if (pBoardStates == 0)
	{
		// Malloc failed for RapiDAQ state array
	}

	for (d = 0; d < globalState.pciState.numberOfRapiDAQBoards; d++)
	{
		pBoardStates[d].isDebugMode = NDTS_BTrue;
		pBoardStates[d].isDiagnosticsMode = NDTS_BFalse;
		pBoardStates[d].diagnosticsFilename[0] = '\0';
		pBoardStates[d].isPCIInitialised = NDTS_BFalse;
		pBoardStates[d].isDMAChannelOpen = NDTS_BFalse ;
		pBoardStates[d].isInitialised = NDTS_BFalse ;
		{
			RQ_AnalogueInputRange range;
			for (range = 0; range < RQ_NUMBER_OF_ANALOGUE_GAIN_VALUES; range++)
			{
				pBoardStates[d].isCalibrated[range] = NDTS_BFalse;
			}
		}
		pBoardStates[d].calibrationOffsets[0] = 0 ;
		pBoardStates[d].calibrationGains[0] = 0;
	}

	globalState.isEnumerated = TRUE;
	return NDTS_BTrue;
}

NDTS_Boolean CALL_CONV RQ_OpenDMAChannel(unsigned long cardIndex)
{
	/*
	// Old
	RETURN_CODE pciReturnCode ;
	PCI_State *pPCIState ;
	*/
	RQ_State *pRQState ;

	if (cardIndex >= globalState.pciState.numberOfRapiDAQBoards) return NDTS_BFalse;

	// Old
	//pPCIState = &pPCIStates[cardIndex];
	pRQState = &pBoardStates[cardIndex];

	if (!pRQState->isPCIInitialised)
	{
		RQ_HandleRQError(RQ_OpenDMAChannel_IsPCIInitialisedFail) ;
		return NDTS_BFalse ;
	}
	
	if (pRQState->isDMAChannelOpen)
	{
		RQ_HandleRQError(RQ_OpenDMAChannel_DMAChannelAlreadyOpen) ;
		return NDTS_BFalse ;
	}

	/*
	// Old
	pPCIState->dmaDesc.EnableReadyInput = 1 ;
	pPCIState->dmaDesc.EnableBTERMInput = 1 ;
	pPCIState->dmaDesc.EnableIopBurst = 1 ;
	pPCIState->dmaDesc.EnableWriteInvalidMode = 0 ;
	pPCIState->dmaDesc.EnableDmaEOTPin = 0 ;
	pPCIState->dmaDesc.DmaStopTransferMode = AssertBLAST;
	pPCIState->dmaDesc.HoldIopAddrConst = 0 ;
	pPCIState->dmaDesc.HoldIopSourceAddrConst = 0 ;
	pPCIState->dmaDesc.HoldIopDestAddrConst = 0 ;
	pPCIState->dmaDesc.DemandMode = 0 ;
	pPCIState->dmaDesc.EnableTransferCountClear = 0 ;
	pPCIState->dmaDesc.WaitStates = 0 ;
	pPCIState->dmaDesc.IopBusWidth = 2 ;   // 32-bit
	pPCIState->dmaDesc.EOTEndLink = 0 ;
	pPCIState->dmaDesc.ValidStopControl = 0 ;
	pPCIState->dmaDesc.ValidModeEnable = 0 ;
	pPCIState->dmaDesc.EnableDualAddressCycles = 0 ;
	pPCIState->dmaDesc.Reserved1 = 0 ;
	pPCIState->dmaDesc.TholdForIopWrites = 0 ;
	pPCIState->dmaDesc.TholdForIopReads = 0 ;
	pPCIState->dmaDesc.TholdForPciWrites = 0 ;
	pPCIState->dmaDesc.TholdForPciReads = 0 ;
	pPCIState->dmaDesc.EnableFlybyMode = 0 ;
	pPCIState->dmaDesc.FlybyDirection = 0 ;
	pPCIState->dmaDesc.EnableDoneInt = 1 ;
	pPCIState->dmaDesc.Reserved2 = 0 ;
	pPCIState->dmaDesc.DmaChannelPriority = Channel0Highest;

	pciReturnCode = PlxDmaBlockChannelOpen(pPCIState->hDevice, PrimaryPciChannel0, &pPCIState->dmaDesc) ;
	if (pciReturnCode != ApiSuccess)
	{
		RQ_HandleRQError(RQ_OpenDMAChannel_PCIDmaBlockChannelOpenFail) ;
		return NDTS_BFalse ;
	}
	*/

	NDTS_HAL_OpenDMAChannel(NDTS_SIDRapiDAQ, cardIndex) ;

	pRQState->isDMAChannelOpen = NDTS_BTrue ;
	return NDTS_BTrue ;
}

NDTS_Boolean CALL_CONV RQ_CloseDMAChannel(unsigned long cardIndex)
{
	/*
	// Old
	RETURN_CODE pciReturnCode ;
	PCI_State *pPCIState ;
	*/

	RQ_State *pRQState = RQ_ReturnBoardStatePointer(cardIndex) ;
	if (!pRQState) return NDTS_BFalse; 

	// Old
	//pPCIState = &pPCIStates[cardIndex] ;
	pRQState = &pBoardStates[cardIndex] ;

	if (!pRQState->isPCIInitialised)
	{
		RQ_HandleRQError(RQ_CloseDMAChannel_IsPCIInitialisedFail) ;
		return NDTS_BFalse ;
	}

	if (!pRQState->isDMAChannelOpen)
	{
		RQ_HandleRQError(RQ_CloseDMAChannel_IsDMAChannelOpenFail) ;
		return NDTS_BFalse ;
	}

	/*
	pciReturnCode = PlxDmaBlockChannelClose(pPCIState->hDevice, PrimaryPciChannel0) ;
	if (pciReturnCode != ApiSuccess)
	{
		RQ_HandleRQError(RQ_CloseDMAChannel_PCIDmaBlockChannelCloseFail) ;
		return NDTS_BFalse ;
	}
	*/

	NDTS_HAL_CloseDMAChannel(NDTS_SIDRapiDAQ, cardIndex) ;

	pRQState->isDMAChannelOpen = NDTS_BFalse ;

	return NDTS_BTrue ;
}
/*
void * memcpy(void * dst, void const * src, size_t len)
{
    long * plDst = (long *) dst;
    long const * plSrc = (long const *) src; 
    char * pcDst ;
    char const * pcSrc ;

    if (!((long)(src) & 0xFFFFFFFC) && !((long)(dst) & 0xFFFFFFFC))
    {
        while (len >= 4)
		{
            *plDst++ = *plSrc++;
            len -= 4;
        }
    } 

    pcDst = (char *) plDst;
    pcSrc = (char const *) plSrc;


    while (len--)
    {
        *pcDst++ = *pcSrc++;
    } 

    return (dst);
}
*/



NDTS_Boolean CALL_CONV RQ_ReadRawScanBScans(unsigned long cardIndex,
											RQ_RawScan *pRawScan,
											RQ_CardBlockDescription* pSourceBlockDescriptionBegin, 
											unsigned long numberOfDescriptions, 
											const unsigned long *pCRCTable)
{
	NDTS_Boolean isOK ;
	RQ_CRCState *pCRCState ;
	unsigned long sourceBlockOffset ;
	RQ_CardBlockDescription* pSourceBlockDescriptionIt ;
	RQ_MemoryHandle *pDestinationBlockIt ;
	unsigned long totalNumberOfBlocksRead = 0 ;
	unsigned long sourceAScanBaseAddressOffset, boardRawCRCWord ;

	// Preconditions.
	_ASSERTE(pRawScan) ;
	_ASSERTE(pSourceBlockDescriptionBegin) ;
	_ASSERTE(numberOfDescriptions > 0) ;
	_ASSERTE(pCRCTable) ;

	// Use the reserved field to record the offset of each block in the card raw scan.
	for(sourceBlockOffset = 0, pSourceBlockDescriptionIt = pSourceBlockDescriptionBegin ; 
		pSourceBlockDescriptionIt != pSourceBlockDescriptionBegin + numberOfDescriptions ;
		++pSourceBlockDescriptionIt)
	{
		pSourceBlockDescriptionIt->reserved = sourceBlockOffset ;

		sourceBlockOffset += pSourceBlockDescriptionIt->size 
			+ (pSourceBlockDescriptionIt->flags & RQ_CBDIsCRCCheckPresent ? 4 : 0) ;
	}

	pCRCState = &pBoardStates[cardIndex].crcState ;

	pSourceBlockDescriptionIt = pSourceBlockDescriptionBegin ;

	isOK = RQ_GetMemoryBlockList(RQ_ReturnRawScanInternal(pRawScan)->pDataA, &pDestinationBlockIt) ;
	if(!isOK) 
		return NDTS_BFalse ; 

	// Skip the raw scan header.
	pDestinationBlockIt++ ;

	do
	{
		unsigned long numberOfBlocksRead = 0 ;

		isOK = RQ_ReadRawScanBlocks(cardIndex,
			pSourceBlockDescriptionIt->reserved,
			pSourceBlockDescriptionIt,
			pSourceBlockDescriptionBegin + numberOfDescriptions,
			pDestinationBlockIt,
			pCRCTable,
			&numberOfBlocksRead) ;

		totalNumberOfBlocksRead += numberOfBlocksRead ;

		if(!isOK)
		{
			unsigned long aScanIndex, bScanIndex ;
			unsigned long sourceAScanSize ;
			// Old
			//char *pSourceAScanBaseAddress ;
			unsigned long boardCRC, ourCRC ;
			RQ_MemoryCheckCounterUpdateEvent event ;
			unsigned long rawScanBlocksPerAScan, rawScanBlocksPerBScan ;

			if(RQ_GetLastError(cardIndex) != RQ_ReadRawScanBlocks_BadBlockCRC) 
			{
				_ASSERTE(0) ;

				// Not a CRC error.
				return NDTS_BFalse ;
			}

			// Calculate the number of blocks per a-scan and b-scan.
			rawScanBlocksPerAScan = RQ_ReturnRawScanAScanMemoryBlockListSize(pRawScan) ;
			rawScanBlocksPerBScan = RQ_ReturnRawScanBScanMemoryBlockListSize(pRawScan) ;
			
			// Sync the iterators to point to the faulty block.
			pSourceBlockDescriptionIt += numberOfBlocksRead ;
			pDestinationBlockIt += numberOfBlocksRead ;
			sourceBlockOffset = pSourceBlockDescriptionIt->reserved ;

			// A block CRC error occured. Was it an a-scan (body)? (We don't care about b-scan CRC's) ...

			// Check the block is part of an a-scan and not the b-scan trailer.
			if(totalNumberOfBlocksRead % rawScanBlocksPerBScan == pRawScan->numberOfAScansInBScan * rawScanBlocksPerAScan)
			{
				// This is strange as b-scan trailers don't have CRC's (that we care about anyway).
				_ASSERTE(0) ;

				// Move on to the block following the faulty one.
				++totalNumberOfBlocksRead ;
				++pSourceBlockDescriptionIt ;
				++pDestinationBlockIt ;

				continue ;
			}

			// Check the block is an a-scan body and not an a-scan header.
			if((totalNumberOfBlocksRead % rawScanBlocksPerBScan) % rawScanBlocksPerAScan != 1) 
			{
				// This is strange as a-scan headers don't have CRC's.
				_ASSERTE(0) ;

				// Move on to the block following the faulty one.
				++totalNumberOfBlocksRead ;
				++pSourceBlockDescriptionIt ;
				++pDestinationBlockIt ;

				continue ;
			}

			// It was an a-scan ... 

			// Calculate which a-scan is faulty.
			aScanIndex = (totalNumberOfBlocksRead % rawScanBlocksPerBScan) / rawScanBlocksPerAScan ;
			bScanIndex = totalNumberOfBlocksRead / rawScanBlocksPerBScan ;

			// Old
			//pSourceAScanBaseAddress = (char *) (unsigned long *) pPCIStates[cardIndex].pPCIBar2VirtualAddress + sourceBlockOffset ;
			sourceAScanBaseAddressOffset = sourceBlockOffset ;
			sourceAScanSize = pRawScan->aScanSamplesThisStep[aScanIndex] * RQ_ReturnBytesPerSample(pRawScan->sampleSize) ;

			// Old
			//boardCRC = ~*(unsigned long *) (pSourceAScanBaseAddress + sourceAScanSize) ;
			isOK = NDTS_HAL_GetWord(NDTS_SIDRapiDAQ, cardIndex, sourceAScanBaseAddressOffset, &boardRawCRCWord) ;
			if (!isOK)
			{
				return NDTS_BFalse ;
			}
			boardCRC = ~boardRawCRCWord ;

			ourCRC = RQ_CalculateCRC32(~0ul, pCRCState->pCRCTable, (char *)(pDestinationBlockIt->p), sourceAScanSize) ;

			// This is a rip from code that (I assume) did work.
			if (boardCRC != ourCRC)
			{
				unsigned long ourNewCRC ;

				// Old ****
				//RQ_ByteCopy((char *)pDestinationBlockIt->p, pSourceAScanBaseAddress, sourceAScanSize) ;
				isOK = NDTS_HAL_BoardByteCopy(NDTS_SIDRapiDAQ, cardIndex, sourceAScanBaseAddressOffset, (char *)pDestinationBlockIt->p, sourceAScanSize) ;
				if (!isOK)
				{
					return NDTS_BFalse ;
				}

				ourNewCRC = RQ_CalculateCRC32(~0ul, pCRCState->pCRCTable, (char *)pDestinationBlockIt->p, sourceAScanSize) ;

				if (boardCRC == ourNewCRC)
				{
					pCRCState->statistics.aScanSampleCounter.numberOfRecovered++ ;
					event = RQ_MCCUERecovered ;
				}
				else if (ourCRC == ourNewCRC)
				{
					pCRCState->statistics.aScanSampleCounter.numberOfPseudoRecovered++ ;
					event = RQ_MCCUEPseudoRecovered ;
				}
				else
				{
					pCRCState->statistics.aScanSampleCounter.numberOfUnrecovered++ ;
					event = RQ_MCCUEUnrecovered ;
				}

				if (pCRCState->pCRCBadScanHook != 0)
				{
					RQ_BadAScanStatistics statistics ;

					statistics.indices.aScanIndex = aScanIndex ;
					statistics.indices.bScanIndex = bScanIndex ;

					// NB: B-scan data starts at memory address 0.
					statistics.memoryRange.startAddress = sourceBlockOffset ;
					statistics.memoryRange.endAddress = statistics.memoryRange.startAddress + pSourceBlockDescriptionIt->size ;

					statistics.event = event ;

					(*pCRCState->pCRCBadScanHook)(&statistics, 0, pCRCState->pCRCBadScanHookContext) ;
				}
			}

			// Move on to the block following the faulty one.
			++totalNumberOfBlocksRead ;
			++pSourceBlockDescriptionIt ;
			++pDestinationBlockIt ;
		}
	}
	while(totalNumberOfBlocksRead != numberOfDescriptions) ;

	pCRCState->statistics.aScanSampleCounter.numberOfGood += pRawScan->numberOfBScans * pRawScan->numberOfAScansInBScan ;

	// Verify a-scan headers.
	isOK = RQ_VerifyRawScanAScanHeaders(cardIndex, pRawScan) ;
	if (!isOK)
		return NDTS_BFalse ;

	return NDTS_BTrue ;
}

NDTS_Boolean CALL_CONV RQ_ReadRawScanBlocks(unsigned long cardIndex, 
											unsigned long sourceByteOffset, 
											RQ_CardBlockDescription* pSourceBlockDescriptionBegin, 
											RQ_CardBlockDescription* pSourceBlockDescriptionEnd, 
											RQ_MemoryHandle *pDestinationBlocks, 
											const unsigned long *pCRCTable, 
											unsigned long *pSourceBlocksRead)
{
	// Old
	//PCI_State *pPCIState ;
	RQ_State *pRQState ;
	unsigned long sourceByteEnd ;
	char *pTransferByteIt, *pTransferByteEnd ;
	RQ_CardBlockDescription* pSourceBlockDescriptionIt ;
	RQ_MemoryHandle *pDestinationBlockIt ;
	NDTS_Boolean isOK ;
	char *pCommonBuffer ;
	unsigned long numberOfCommonBufferBytes ;

	if (cardIndex >= globalState.pciState.numberOfRapiDAQBoards) return NDTS_BFalse;

	// Old
	//pPCIState = &pPCIStates[cardIndex];
	pRQState = &pBoardStates[cardIndex];

	if (!pRQState->isPCIInitialised)
	{
		_ASSERTE(0) ;

		RQ_HandleRQError(RQ_ReadRawScanBlocks_IsPCIInitialisedFail) ;
		return NDTS_BFalse ;
	}

	if (!pRQState->isDMAChannelOpen)
	{
		_ASSERTE(0) ;

		RQ_HandleRQError(RQ_ReadRawScanBlocks_IsDMAChannelOpenFail) ;
		return NDTS_BFalse ;
	}

	*pSourceBlocksRead = 0 ;

	isOK = NDTS_HAL_GetCommonBufferInfo(NDTS_SIDRapiDAQ, cardIndex, &pCommonBuffer, &numberOfCommonBufferBytes) ;
	if (!isOK)
		return NDTS_BFalse ;

	// Set card buffer byte iterators.
	sourceByteEnd = sourceByteOffset ;

	for(pSourceBlockDescriptionIt = pSourceBlockDescriptionBegin; pSourceBlockDescriptionIt != pSourceBlockDescriptionEnd; ++pSourceBlockDescriptionIt)
		sourceByteEnd += pSourceBlockDescriptionIt->size + (pSourceBlockDescriptionIt->flags & RQ_CBDCRCCheckIsPresent ? 4 : 0);

	// Iterate over the card buffer blocks.
	for(pSourceBlockDescriptionIt = pSourceBlockDescriptionBegin, 
		pTransferByteIt = pCommonBuffer, 
		pTransferByteEnd = pTransferByteIt,
		pDestinationBlockIt = pDestinationBlocks; 

		pSourceBlockDescriptionIt != pSourceBlockDescriptionEnd; 

		++pSourceBlockDescriptionIt, 
		++pDestinationBlockIt)
	{
		unsigned char *pDestinationByteIt, *pDestinationByteEnd ;
		unsigned long crcAccum ;
		unsigned char currentByte ;
		int isCRCCheckPresent ;

		crcAccum = ~0ul ; // Only used if the block has CRC check.
		isCRCCheckPresent = pSourceBlockDescriptionIt->flags & RQ_CBDCRCCheckIsPresent ;

		// Set destination byte iterators.
		pDestinationByteIt = pDestinationBlockIt->p ;
		pDestinationByteEnd = pDestinationByteIt + pSourceBlockDescriptionIt->size ; // This assumes the destination block is big enough.

		// Iterate over the bytes in the block buffer, filling them with the bytes in the transfer buffer.
		do
		{
			if(pTransferByteIt == pTransferByteEnd)
			{
				unsigned long transferCount = 0 ;

				// The transfer buffer is empty, so fill it.
				isOK = RQ_FetchRawScanBytes(cardIndex, sourceByteOffset, sourceByteEnd, &transferCount) ;
				if(!isOK) 
					return NDTS_BFalse ; 

				// Set source / transfer buffer iterators.
				sourceByteOffset += transferCount ;

				pTransferByteIt = pCommonBuffer ;
				pTransferByteEnd = pTransferByteIt + transferCount ;
			}

			currentByte = *pDestinationByteIt++ = *pTransferByteIt++ ;

			// CRC.
			if(isCRCCheckPresent)
				crcAccum = (crcAccum << 8) ^ pCRCTable[((unsigned long) (crcAccum >> 24) ^ currentByte) & 0xff] ;
		}
		while(pDestinationByteIt != pDestinationByteEnd) ;

		if(isCRCCheckPresent)
		{
			union 
			{
				unsigned char bytes[4] ;
				unsigned long unsignedLong ;
			} crc ;

			// Set destination byte iterators.
			pDestinationByteIt = crc.bytes ;
			pDestinationByteEnd = pDestinationByteIt + 4 ;

			// Iterate over the bytes in the block buffer, filling them with the bytes in the transfer buffer.
			do
			{
				if(pTransferByteIt == pTransferByteEnd)
				{
					unsigned long transferCount = 0 ;

					// The transfer buffer is empty, so fill it.
					isOK = RQ_FetchRawScanBytes(cardIndex, sourceByteOffset, sourceByteEnd, &transferCount) ;
					if(!isOK) 
						return NDTS_BFalse ; 

					// Set source / transfer buffer iterators.
					sourceByteOffset += transferCount ;

					pTransferByteIt = (char *) pCommonBuffer ;
					pTransferByteEnd = pTransferByteIt + transferCount ;
				}

				*pDestinationByteIt++ = *pTransferByteIt++ ;
			}
			while(pDestinationByteIt != pDestinationByteEnd) ;

			if(crcAccum != ~crc.unsignedLong)
			{
#ifdef _DEBUG
				//unsigned long crc = RQ_CalculateCRC32(~0ul, pCRCTable, (char*)(pDestinationBlockIt->p), pSourceBlockDescriptionIt->size) ;
				//assert((crcAccum != crc.unsignedLong)) ;
#endif

				RQ_HandleRQError(RQ_ReadRawScanBlocks_BadBlockCRC) ;
				return NDTS_BFalse ;
			}
		}

		// Another block successfully read.
		(*pSourceBlocksRead)++ ;
	}

	return NDTS_BTrue ;
}
// The compiler doesn't realise that the QueryPerformanceCounter(&countLargeA) line is always executed when countLargeA is used in the calculation
#pragma warning(push)
#pragma warning(disable : 4701) 

NDTS_Boolean CALL_CONV RQ_FetchRawScanBytes(unsigned long cardIndex, 
											unsigned long begin, 
											unsigned long end, 
											unsigned long *pTransferCount)
{
	//No more bytes left in the transfer buffer! Get some more ...
	// Old
	//PCI_State *pPCIState ;
	// Old
	//RETURN_CODE pciReturnCode ;
	static int debugMeasureTime = 0;  // Allows measurement of DMA timing - for debug use only.  
									 // Set debugMeasureTime to 1 in debugger and put breekpoint in appropriate place below
	LARGE_INTEGER freqLarge, countLargeA, countLargeB ;
	float megaBytesPerSecond ;
	unsigned char *pCommonBuffer ;
	unsigned long numberOfCommonBufferBytes ;
	NDTS_Boolean isOK ;

	isOK = NDTS_HAL_GetCommonBufferInfo(NDTS_SIDRapiDAQ, cardIndex, &pCommonBuffer, &numberOfCommonBufferBytes) ;
	if (!isOK)
		return NDTS_BFalse ;

	// Old
	//pPCIState = &pPCIStates[cardIndex];

	// We can't transfer everything at once so cap it.
	*pTransferCount = end - begin ;

	_ASSERTE(*pTransferCount > 0) ;
	if(*pTransferCount > numberOfCommonBufferBytes)
		*pTransferCount = numberOfCommonBufferBytes ;

	if (debugMeasureTime)
	{
		QueryPerformanceCounter(&countLargeA) ;
	}

	_ASSERTE(end < 0x00400000); 

	/*
	// Old
	// There are no more bytes left in the transfer buffer.
	pPCIState->dmaData.u.PciAddrLow			= pPCIState->pciBuffer.PhysicalAddr ;
	pPCIState->dmaData.PciAddrHigh			= 0x00000000 ;
	pPCIState->dmaData.LocalAddr			= begin ;
	pPCIState->dmaData.TransferCount		= *pTransferCount ;
	pPCIState->dmaData.LocalToPciDma		= 1 ;
	pPCIState->dmaData.TerminalCountIntr	= 0 ;

	pciReturnCode = PlxDmaBlockTransfer(pPCIState->hDevice, PrimaryPciChannel0, &pPCIState->dmaData, FALSE) ;
	if (pciReturnCode != ApiSuccess)
	{
		_ASSERTE(0) ;

		RQ_HandleRQError(RQ_FetchRawScanBytes_PCIDmaBlockTransferFail) ;
		return NDTS_BFalse ;
	}
	*/

	isOK = NDTS_HAL_PerformDMARead(NDTS_SIDRapiDAQ, cardIndex, begin, *pTransferCount) ;
	if (!isOK)
		return NDTS_BFalse ;

	if (debugMeasureTime)
	{
		float secs ; 
		QueryPerformanceFrequency(&freqLarge) ;
		QueryPerformanceCounter(&countLargeB) ;
		secs  = (float) (countLargeB.QuadPart - countLargeA.QuadPart) / (float) freqLarge.QuadPart ;
		megaBytesPerSecond = (float)(*pTransferCount) / secs /  1000000.0f  ;
	}
	return NDTS_BTrue ;
}
#pragma warning(pop)

NDTS_Boolean CALL_CONV RQ_SetCheckData(unsigned long cardIndex, unsigned long data)
{
	if (cardIndex >= globalState.pciState.numberOfRapiDAQBoards) return NDTS_BFalse;
	return RQ_SetSystemRegister(cardIndex, RQ_CHECK_REGISTER, data) ;
}

NDTS_Boolean CALL_CONV RQ_GetCheckData(unsigned long cardIndex, unsigned long *value)
{
	if (cardIndex >= globalState.pciState.numberOfRapiDAQBoards) return NDTS_BFalse;
	return RQ_GetRegister(cardIndex, RQ_CHECK_REGISTER, value) ;
}

NDTS_Boolean CALL_CONV RQ_SetProgramFPGAs(unsigned long cardIndex)
{
	if (cardIndex >= globalState.pciState.numberOfRapiDAQBoards) return NDTS_BFalse;
	return RQ_SetSystemRegister(cardIndex, RQ_SYSTEM_COMMAND_REGISTER, 0x00000001) ;
}

NDTS_Boolean CALL_CONV RQ_SetWriteEEPROM(unsigned long cardIndex)
{
	if (cardIndex >= globalState.pciState.numberOfRapiDAQBoards) return NDTS_BFalse;
	return RQ_SetSystemRegister(cardIndex, RQ_SYSTEM_COMMAND_REGISTER, 0x00000002) ;
}

NDTS_Boolean CALL_CONV RQ_SetReadEEPROM(unsigned long cardIndex)
{
	if (cardIndex >= globalState.pciState.numberOfRapiDAQBoards) return NDTS_BFalse;
	return RQ_SetSystemRegister(cardIndex, RQ_SYSTEM_COMMAND_REGISTER, 0x00000004) ;
}

NDTS_Boolean CALL_CONV RQ_GetReadEEPROM(unsigned long cardIndex, unsigned long *flag)
{
	unsigned long value ;
	NDTS_Boolean isOK ;

	if (cardIndex >= globalState.pciState.numberOfRapiDAQBoards) return NDTS_BFalse;

	isOK = RQ_GetSystemRegister(cardIndex, RQ_SYSTEM_COMMAND_REGISTER, &value) ;
	if (!isOK)
		return NDTS_BFalse ;

	*flag = (value & 0x00000004) != 0 ? 1 : 0 ;

	return NDTS_BTrue ;
}

NDTS_Boolean CALL_CONV RQ_GetFPGAProgramStatus(unsigned long cardIndex, NDTS_Boolean *isSuccess)
{
	unsigned long value ;
	NDTS_Boolean isOK ;

	if (cardIndex >= globalState.pciState.numberOfRapiDAQBoards) return NDTS_BFalse;

	isOK = RQ_GetSamplingRegister(cardIndex, RQ_SYSTEM_STATUS_REGISTER, &value) ;
	if (!isOK)
		return NDTS_BFalse ;
	
	*isSuccess = (value & 0x00000001) != 0 ? NDTS_BTrue : NDTS_BFalse ;

	return NDTS_BTrue ;
}

NDTS_Boolean CALL_CONV RQ_SetDDSRegister(unsigned long cardIndex, unsigned long dataRegister, unsigned long addressBus)
{
	unsigned long value ;
	int i ;
	NDTS_Boolean isOK ;

	if (cardIndex >= globalState.pciState.numberOfRapiDAQBoards) return NDTS_BFalse;

	if (dataRegister > 255)
	{
		RQ_HandleRQError(RQ_SetDDSRegister_InvalidRegisterValue) ;
		return NDTS_BFalse ;
	}

	if (addressBus > 63)
	{
		RQ_HandleRQError(RQ_SetDDSRegister_InvalidAddressBusValue) ;
		return NDTS_BFalse ;
	}

	for (i = 0 ; i < 5 ; i++)
	{
		isOK = RQ_GetSamplingRegister(cardIndex, RQ_DDS_CONTROL, &value) ;
		if (!isOK)
			return NDTS_BFalse ;

		value &= 0xffff8000 ;
		value |= dataRegister | (addressBus << 8) ;

		isOK = RQ_SetSamplingRegister(cardIndex, RQ_DDS_CONTROL, value) ;
		if (!isOK)
			return NDTS_BFalse ;

		isOK = RQ_DDSWait(cardIndex) ;
		if (!isOK)
			return NDTS_BFalse ;

		Sleep(1) ;
	}

	return NDTS_BTrue ; 
}

NDTS_Boolean CALL_CONV RQ_SetDDSReset(unsigned long cardIndex)
{
	unsigned long value ;
	NDTS_Boolean isOK ;

	if (cardIndex >= globalState.pciState.numberOfRapiDAQBoards) return NDTS_BFalse;

	isOK = RQ_GetSamplingRegister(cardIndex, RQ_DDS_CONTROL, &value) ;
	if (!isOK)
		return NDTS_BFalse ;
	
	value &= 0xffffbfff ;
	value |= 1 << 14 ;

	isOK = RQ_SetSamplingRegister(cardIndex, RQ_DDS_CONTROL, value) ;
	if (!isOK)
		return NDTS_BFalse ;

	return RQ_DDSWait(cardIndex) ;
}

NDTS_Boolean CALL_CONV RQ_GetDDSReset(unsigned long cardIndex, NDTS_Boolean *reset)
{
	unsigned long value ;
	NDTS_Boolean isOK ;

	if (cardIndex >= globalState.pciState.numberOfRapiDAQBoards) return NDTS_BFalse;

	isOK = RQ_GetSamplingRegister(cardIndex, RQ_DDS_CONTROL, &value) ;
	if (!isOK)
		return NDTS_BFalse ;
	
	value &= 0x00004000 ;
	*reset = (NDTS_Boolean) (value >> 14) ;

	return NDTS_BTrue ;
}

NDTS_Boolean CALL_CONV RQ_DDSWait(unsigned long cardIndex)
{
	NDTS_Boolean hasTimedOut ;
	LARGE_INTEGER freqLarge, countLargeA, countLargeB ;
	float numSeconds ;
	BOOL isWIN32OK ;
	NDTS_Boolean isOK ;
	unsigned long value ;
	int count ;
	//char string[16] ;
	
	if (cardIndex >= globalState.pciState.numberOfRapiDAQBoards) return NDTS_BFalse;

	isWIN32OK = QueryPerformanceFrequency(&freqLarge) ;
	if (!isWIN32OK)
	{
		RQ_HandleRQError(RQ_DDSWait_QueryTimerFail) ;
		return NDTS_BFalse ;
	}

	QueryPerformanceCounter(&countLargeA) ;

	count = 0 ;

	do
	{
		QueryPerformanceCounter(&countLargeB) ;

		isOK = RQ_GetSamplingRegister(cardIndex, RQ_DDS_CONTROL, &value) ;
		if (!isOK)
			return NDTS_BFalse ;

		numSeconds = (float) (countLargeB.QuadPart - countLargeA.QuadPart) / (float) freqLarge.QuadPart ;
		hasTimedOut = numSeconds > RQ_DDS_MAX_WAIT ? NDTS_BTrue : NDTS_BFalse ; 

		count++ ;
	}
	while ((value & 0x80000000) != 0 && !hasTimedOut) ;

	//sprintf(string, "Poll counts: %d\n", count) ;
	//OutputDebugString(string) ;

	if (hasTimedOut)
	{
		RQ_HandleRQError(RQ_DDSWait_TimeOut) ;
		return NDTS_BFalse ;
	}

	return NDTS_BTrue ;
}

NDTS_Boolean CALL_CONV RQ_SetResetTCGLoadCounter(unsigned long cardIndex)
{
	unsigned long value ;
	NDTS_Boolean isOK ;

	if (cardIndex >= globalState.pciState.numberOfRapiDAQBoards) 
		return NDTS_BFalse;

	isOK = RQ_GetSamplingRegister(cardIndex, RQ_TCG_CONFIGURE, &value) ;
	if (!isOK)
		return NDTS_BFalse ;
	
	value &= 0x7fffffff ;
	value |= 1 << 31 ;

	return RQ_SetSamplingRegister(cardIndex, RQ_TCG_CONFIGURE, value) ;
}

RQ_API NDTS_Boolean CALL_CONV RQ_IndicateSampleDataDownloaded(unsigned long cardIndex)
{
	unsigned long value;
	// Always read the sample status register by calling RQ_GetDataSamplingStatus() to preserve the clear on read bits
	RQ_GetDataSamplingStatus(cardIndex, &value) ;

	return RQ_SetSamplingRegister(cardIndex, RQ_SAMPLE_DATA_STATUS, value | 0x01) ;
}

RQ_API NDTS_Boolean CALL_CONV RQ_SetSamplingRegisterBits(unsigned long cardIndex, unsigned long offset, unsigned long mask)
{
	NDTS_Boolean isOK ;
	unsigned long value ;

	isOK = RQ_GetSamplingRegister(cardIndex, offset, &value) ;
	if(isOK)
		isOK = RQ_SetSamplingRegister(cardIndex, offset, value | mask) ;


	return isOK ;
}

RQ_API NDTS_Boolean CALL_CONV RQ_ClearSamplingRegisterBits(unsigned long cardIndex, unsigned long offset, unsigned long mask)
{
	NDTS_Boolean isOK ;
	unsigned long value ;

	RQ_Lock(cardIndex) ;

	isOK = RQ_GetSamplingRegister(cardIndex, offset, &value) ;
	if(isOK)
		isOK = RQ_SetSamplingRegister(cardIndex, offset, value & ~mask) ;

	RQ_Unlock(cardIndex) ;

	return isOK ;
}

RQ_API NDTS_Boolean CALL_CONV RQ_GetDataSamplingStatus(unsigned long cardIndex, unsigned long *pValue)
{
	RQ_State *pBoardState = RQ_ReturnBoardStatePointer(cardIndex) ;

	_ASSERTE(pValue) ;

	if (!pBoardState) 
		return NDTS_BFalse; 

	RQ_Lock(cardIndex) ;

	if(RQ_GetSamplingRegister(cardIndex, RQ_SAMPLE_DATA_STATUS, pValue) == NDTS_BFalse)
	{
		RQ_Unlock(cardIndex) ;
		return NDTS_BFalse ;
	}

	// New triggered occured.
	if(*pValue & 0x04)
		pBoardState->dataSamplingStatus |= 0x04 ;

	// Delayed trigger occured.
	if(*pValue & 0x08)
	{
		OutputDebugString("Delayed trigger occured\n") ;
		pBoardState->dataSamplingStatus |= 0x08 ;
	}

	// Data full error.
	if(*pValue & 0x10)
	{
		OutputDebugString("Data full error\n") ;
		pBoardState->dataSamplingStatus |= 0x10 ;
	}

	RQ_Unlock(cardIndex) ;

	return NDTS_BTrue ;
}

RQ_API NDTS_Boolean CALL_CONV RQ_SetDataSamplingStatus(unsigned long cardIndex, unsigned long value)
{
	RQ_Lock(cardIndex) ;

	if(RQ_SetSamplingRegister(cardIndex, RQ_SAMPLE_DATA_STATUS, value) == NDTS_BFalse)
	{
		RQ_Unlock(cardIndex) ;
		return NDTS_BFalse ;
	}

	RQ_Unlock(cardIndex) ;

	return NDTS_BTrue ;
}


RQ_API NDTS_Boolean CALL_CONV RQ_ClearDataSamplingStatusFlags(unsigned long cardIndex)
{
	RQ_State *pBoardState = RQ_ReturnBoardStatePointer(cardIndex) ;

	RQ_Lock(cardIndex) ;

	pBoardState->dataSamplingStatus = 0 ;
	
	RQ_Unlock(cardIndex) ;

	return NDTS_BTrue ;
}

NDTS_Boolean CALL_CONV RQ_ReturnDataSamplingStatusFlagSet(unsigned long cardIndex, unsigned long mask)
{
	RQ_State *pBoardState = RQ_ReturnBoardStatePointer(cardIndex) ;
	NDTS_Boolean result ;
	
	result = (pBoardState->dataSamplingStatus & mask) ? NDTS_BTrue : NDTS_BFalse ;

	// Clear the flag now we have explicly read it.
	pBoardState->dataSamplingStatus = pBoardState->dataSamplingStatus & ~mask ;

	return result ;
}

RQ_API NDTS_Boolean CALL_CONV RQ_GetNewTriggerOccured(unsigned long cardIndex, NDTS_Boolean *pValue)
{
	unsigned long tmp ;

	_ASSERTE(pValue) ;

	RQ_Lock(cardIndex) ;

	if(RQ_GetDataSamplingStatus(cardIndex, &tmp) == NDTS_BFalse)
	{
		RQ_Unlock(cardIndex) ;
		return NDTS_BFalse ;
	}

	*pValue = RQ_ReturnDataSamplingStatusFlagSet(cardIndex, 0x04) ;

	RQ_Unlock(cardIndex) ;

	return NDTS_BTrue ;
}

RQ_API NDTS_Boolean CALL_CONV RQ_GetDelayedTriggerOccured(unsigned long cardIndex, NDTS_Boolean *pValue)
{
	unsigned long tmp ;

	_ASSERTE(pValue) ;

	RQ_Lock(cardIndex) ;

	if(RQ_GetDataSamplingStatus(cardIndex, &tmp) == NDTS_BFalse)
	{
		RQ_Unlock(cardIndex) ;
		return NDTS_BFalse ;
	}

	*pValue = RQ_ReturnDataSamplingStatusFlagSet(cardIndex, 0x08) ;

	RQ_Unlock(cardIndex) ;

	return NDTS_BTrue ;
}

RQ_API NDTS_Boolean CALL_CONV RQ_GetDataFullError(unsigned long cardIndex, NDTS_Boolean *pValue)
{
	unsigned long tmp ;

	_ASSERTE(pValue) ;

	RQ_Lock(cardIndex) ;

	if(RQ_GetDataSamplingStatus(cardIndex, &tmp) == NDTS_BFalse)
	{
		RQ_Unlock(cardIndex) ;
		return NDTS_BFalse ;
	}

	*pValue = RQ_ReturnDataSamplingStatusFlagSet(cardIndex, 0x10) ;

	RQ_Unlock(cardIndex) ;

	return NDTS_BTrue ;
}

RQ_API NDTS_Boolean CALL_CONV RQ_SetAToDOutputPortFormat(unsigned long cardIndex, unsigned long outputPortFormat)
{
	unsigned long value ;
	RQ_State *pBoardState = RQ_ReturnBoardStatePointer(cardIndex) ;

	if (!pBoardState) 
		return NDTS_BFalse; 

	if (outputPortFormat != 0 && outputPortFormat != 1)
	{
		RQ_HandleRQError(RQ_SetAToDOutputPortFormat_InvalidPortFormat) ;
		return NDTS_BFalse ;
	}

	value = pBoardState->peripheralRegisterShadow;
	value &= 0xffffefff ;
	value |= outputPortFormat << 12 ;
	pBoardState->peripheralRegisterShadow = value;
	return RQ_SetPeripheralRegister(cardIndex, RQ_PERIPHERAL_HARDWARE_CONTROL, value) ;
}

RQ_API NDTS_Boolean CALL_CONV RQ_GetAToDOutputPortFormat(unsigned long cardIndex, unsigned long *outputPortFormat)
{
	unsigned long value ;

	RQ_State *pBoardState = RQ_ReturnBoardStatePointer(cardIndex) ;
	if (!pBoardState) return NDTS_BFalse; 

	value = pBoardState->peripheralRegisterShadow;
	value &= 0x00001000 ;
	*outputPortFormat = value >> 12 ;

	return NDTS_BTrue ;
}

RQ_API NDTS_Boolean CALL_CONV RQ_SetAToDOutputDataFormat(unsigned long cardIndex, unsigned long outputDataFormat)
{
	unsigned long value ;

	RQ_State *pBoardState = RQ_ReturnBoardStatePointer(cardIndex) ;
	if (!pBoardState) return NDTS_BFalse; 

	if (outputDataFormat != 0 && outputDataFormat != 1)
	{
		RQ_HandleRQError(RQ_SetAToDOutputDataFormat_InvalidDataFormat) ;
		return NDTS_BFalse ;
	}

	value = pBoardState->peripheralRegisterShadow;
	value &= 0xffffdfff ;
	value |= outputDataFormat << 13 ;
	pBoardState->peripheralRegisterShadow = value;
	return RQ_SetPeripheralRegister(cardIndex, RQ_PERIPHERAL_HARDWARE_CONTROL, value) ;
}

RQ_API NDTS_Boolean CALL_CONV RQ_GetAToDOutputDataFormat(unsigned long cardIndex, unsigned long *outputDataFormat)
{
	unsigned long value ;

	RQ_State *pBoardState = RQ_ReturnBoardStatePointer(cardIndex) ;
	if (!pBoardState) return NDTS_BFalse; 

	value = pBoardState->peripheralRegisterShadow;
	value &= 0x00002000 ;
	*outputDataFormat = value >> 13 ;

	return NDTS_BTrue ;
}

RQ_API NDTS_Boolean CALL_CONV RQ_SetAToDInputVoltageRange(unsigned long cardIndex, unsigned long inputVoltageRange)
{
	unsigned long value ;

	RQ_State *pBoardState = RQ_ReturnBoardStatePointer(cardIndex) ;
	if (!pBoardState) return NDTS_BFalse; 

	if (inputVoltageRange != 0 && inputVoltageRange != 1)
	{
		RQ_HandleRQError(RQ_SetAToDInputVoltageRange_InvalidInputVoltageRange) ;
		return NDTS_BFalse ;
	}

	value = pBoardState->peripheralRegisterShadow;
	value &= 0xffffbfff ;
	value |=  inputVoltageRange << 14 ;
	pBoardState->peripheralRegisterShadow = value;
	return RQ_SetPeripheralRegister(cardIndex, RQ_PERIPHERAL_HARDWARE_CONTROL, value) ;
}

RQ_API NDTS_Boolean CALL_CONV RQ_GetAToDInputVoltageRange(unsigned long cardIndex, unsigned long *inputVoltageRange)
{
	unsigned long value ;
	RQ_State *pBoardState = RQ_ReturnBoardStatePointer(cardIndex) ;
	if (!pBoardState) return NDTS_BFalse; 

	value = pBoardState->peripheralRegisterShadow;
	value &= 0x00004000 ;
	*inputVoltageRange = value >> 14 ;

	return NDTS_BTrue ;
}

RQ_API NDTS_Boolean CALL_CONV RQ_SetAToDVrefInputCharacteristics(unsigned long cardIndex, unsigned long inputCharacteristics)
{
	unsigned long value ;
	RQ_State *pBoardState = RQ_ReturnBoardStatePointer(cardIndex) ;
	if (!pBoardState) return NDTS_BFalse; 

	if (inputCharacteristics != 0 && inputCharacteristics != 1)
	{
		RQ_HandleRQError(RQ_SetAToDVrefInputCharacteristics_InvalidInputCharacteristics) ;
		return NDTS_BFalse ;
	}

	value = pBoardState->peripheralRegisterShadow;
	value &= 0xffff7fff ;
	value |= inputCharacteristics << 15 ;
	pBoardState->peripheralRegisterShadow = value;
	return RQ_SetPeripheralRegister(cardIndex, RQ_PERIPHERAL_HARDWARE_CONTROL, value) ;
}

RQ_API NDTS_Boolean CALL_CONV RQ_GetAToDVrefInputCharacteristics(unsigned long cardIndex, unsigned long *inputCharacteristics)
{
	unsigned long value ;
	RQ_State *pBoardState = RQ_ReturnBoardStatePointer(cardIndex) ;
	if (!pBoardState) return NDTS_BFalse; 

	value = pBoardState->peripheralRegisterShadow;
	value &= 0x00008000 ;
	*inputCharacteristics = value >> 15 ;

	return NDTS_BTrue ;
}

NDTS_Boolean CALL_CONV RQ_SetDDSKEY(unsigned long cardIndex, unsigned long dDSKEY)
{
	unsigned long value ;
	RQ_State *pBoardState = RQ_ReturnBoardStatePointer(cardIndex) ;
	if (!pBoardState) return NDTS_BFalse; 

	if (dDSKEY != 0 && dDSKEY != 1)
	{
		RQ_HandleRQError(RQ_SetDDSKEY_InvalidDDSKEY) ;
		return NDTS_BFalse ;
	}

	value = pBoardState->peripheralRegisterShadow;
	value &= 0xfffbffff ;
	value |= dDSKEY << 18 ;
	pBoardState->peripheralRegisterShadow = value;
	return RQ_SetPeripheralRegister(cardIndex, RQ_PERIPHERAL_HARDWARE_CONTROL, value) ;
}

NDTS_Boolean CALL_CONV RQ_GetDDSKEY(unsigned long cardIndex, unsigned long *dDSKEY)
{
	unsigned long value ;
	RQ_State *pBoardState = RQ_ReturnBoardStatePointer(cardIndex) ;
	if (!pBoardState) return NDTS_BFalse; 

	value = pBoardState->peripheralRegisterShadow;
	value &= 0x00040000 ;
	*dDSKEY = value >> 18 ;

	return NDTS_BTrue ;
}

NDTS_Boolean CALL_CONV RQ_SetDDSFSK(unsigned long cardIndex, unsigned long dDSFSK)
{
	unsigned long value ;
	RQ_State *pBoardState = RQ_ReturnBoardStatePointer(cardIndex) ;
	if (!pBoardState) return NDTS_BFalse; 

	if (dDSFSK != 0 && dDSFSK != 1)
	{
		RQ_HandleRQError(RQ_SetDDSKEY_InvalidDDSFSK) ;
		return NDTS_BFalse ;
	}

	value = pBoardState->peripheralRegisterShadow;
	value &= 0xfff7ffff ;
	value |= dDSFSK << 19 ;
	pBoardState->peripheralRegisterShadow = value;
	return RQ_SetPeripheralRegister(cardIndex, RQ_PERIPHERAL_HARDWARE_CONTROL, value) ;
}

NDTS_Boolean CALL_CONV RQ_GetDDSFSK(unsigned long cardIndex, unsigned long *dDSFSK)
{
	unsigned long value ;
	RQ_State *pBoardState = RQ_ReturnBoardStatePointer(cardIndex) ;
	if (!pBoardState) return NDTS_BFalse; 

	value = pBoardState->peripheralRegisterShadow;
	value &= 0x00080000 ;
	*dDSFSK = value >> 19 ;
	pBoardState->peripheralRegisterShadow = value;
	return RQ_GetPeripheralRegister(cardIndex, RQ_PERIPHERAL_HARDWARE_CONTROL, &value) ;
}

NDTS_Boolean CALL_CONV RQ_SetNumberOfPeriodsBetweenAScanTriggers(unsigned long cardIndex, unsigned long numberOfPeriods)
{
	unsigned long value ;
	NDTS_Boolean isOK ;

	if (cardIndex >= globalState.pciState.numberOfRapiDAQBoards) return NDTS_BFalse;

	if (numberOfPeriods == 0 || numberOfPeriods > 131072)
	{
		RQ_HandleRQError(RQ_SetNumberOfPeriodsBetweenAScanTriggers_InvalidNumberOfPeriods) ;
		return NDTS_BFalse ;
	}
	
	isOK = RQ_GetPeripheralRegister(cardIndex, RQ_ASCAN_CONTROL, &value) ;
	if (!isOK)
		return NDTS_BFalse ;

	value &= 0xfffe0000 ;
	value |= numberOfPeriods ;

	return RQ_SetPeripheralRegister(cardIndex, RQ_ASCAN_CONTROL, value) ;
}

NDTS_Boolean CALL_CONV RQ_GetNumberOfPeriodsBetweenAScanTriggers(unsigned long cardIndex, unsigned long *numberOfPeriods)
{
	unsigned long value ;
	NDTS_Boolean isOK ;

	if (cardIndex >= globalState.pciState.numberOfRapiDAQBoards) return NDTS_BFalse;

	isOK = RQ_GetPeripheralRegister(cardIndex, RQ_ASCAN_CONTROL, &value) ;
	if (!isOK)
		return NDTS_BFalse ;

	value &= 0x0001ffff ;
	*numberOfPeriods = value + 1 ;

	return NDTS_BTrue ;
}

NDTS_Boolean CALL_CONV RQ_SetSensorArrayDIUnitEnableOutputs(unsigned long cardIndex, NDTS_Boolean isEnabled)
{
	unsigned long version, value ;
	NDTS_Boolean isOK ;

	if (cardIndex >= globalState.pciState.numberOfRapiDAQBoards) return NDTS_BFalse;

	isOK = RQ_GetPeripheralFirmwareVersion(cardIndex, &version) ;
	if (!isOK)
		return NDTS_BFalse ;

	if (version != 0)
	{
		RQ_HandleRQError(RQ_SetSensorArrayDIUnitEnableOutputs_InvalidFirmwareVersion) ;
		return NDTS_BFalse ;
	}

	if (isEnabled != NDTS_BTrue && isEnabled != NDTS_BFalse)
	{
		RQ_HandleRQError(RQ_SetSensorArrayDIUnitEnableOutputs_InvalidEnableFlag) ;
		return NDTS_BFalse ;
	}

	isOK = RQ_GetPeripheralRegister(cardIndex, RQ_FOCUSING_UNIT_CONTROL, &value) ;
	if (!isOK)
		return NDTS_BFalse ;

	value &= 0xffffefff ;
	value |= (unsigned long) isEnabled << 12 ;

	return RQ_SetPeripheralRegister(cardIndex, RQ_FOCUSING_UNIT_CONTROL, value) ;
}

NDTS_Boolean CALL_CONV RQ_GetSensorArrayDIUnitEnableOutputs(unsigned long cardIndex, NDTS_Boolean *pIsEnabled)
{
	NDTS_Boolean isOK ;
	unsigned long version, value ;

	if (cardIndex >= globalState.pciState.numberOfRapiDAQBoards) return NDTS_BFalse;

	isOK = RQ_GetPeripheralFirmwareVersion(cardIndex, &version) ;
	if (!isOK)
		return NDTS_BFalse ;

	if (version != 0)
	{
		RQ_HandleRQError(RQ_GetSensorArrayDIUnitEnableOutputs_InvalidFirmwareVersion) ;
		return NDTS_BFalse ;
	}

	isOK = RQ_GetPeripheralRegister(cardIndex, RQ_FOCUSING_UNIT_CONTROL, &value) ;
	if (!isOK)
		return NDTS_BFalse ;

	*pIsEnabled = (value & 0x00001000) != 0 ? NDTS_BTrue : NDTS_BFalse ;

	return NDTS_BTrue ;
}

NDTS_Boolean CALL_CONV RQ_GetGateInternalIndex(unsigned long cardIndex, RQ_GateIndex gateIndex, unsigned long *pGateIndex)
{
	NDTS_Boolean isFound ;
	unsigned long i ;

	RQ_State *pRQState ;
	if (cardIndex >= globalState.pciState.numberOfRapiDAQBoards) return NDTS_BFalse;
	pRQState = &pBoardStates[cardIndex];

	i = 0 ;

	do
	{
		isFound = pRQState->gates[pRQState->currentSequenceStep][i].index == gateIndex ? NDTS_BTrue : NDTS_BFalse ;

		i++ ;
	}
	while (!isFound && i < RQ_ReturnMaximumNumberOfGates(pRQState->isSequence)) ;

	*pGateIndex = i - 1 ;

	return isFound ;
}

NDTS_Boolean CALL_CONV RQ_CheckSRAM(unsigned long cardIndex, RQ_ScanStorage scanStorage, unsigned long numberOfAScanSamples,
					                                         RQ_SampleSize sampleSize, unsigned long numberOfAScanPerBScan,
								                             unsigned long numberOfBScans, unsigned long numberOfGates)
{
	unsigned long numberOfAScanBytes, bytesPerSample, numberOfGateDataBytes, spaceLeft, crcBytes ;
	NDTS_Boolean isOK ;
	RQ_State *pRQState ;
	if (cardIndex >= globalState.pciState.numberOfRapiDAQBoards) return NDTS_BFalse;
	pRQState = &pBoardStates[cardIndex];

	crcBytes = ((pRQState->crcState.pCRCTable) ? 4 : 0) ;

	if ((int) scanStorage == -1)
	{
		isOK = RQ_GetScanStorageOptions(cardIndex, &scanStorage) ;
		if (!isOK)
			return NDTS_BFalse ;
	}

	if (numberOfAScanSamples == 0xffffffff)
	{
		// The function has never been re-written correctly for use with sequenced FPGAs.
		// If it were, the sequence step would need to be passed in and pRQState->aScanBytesPerBScan used for other steps
		isOK = RQ_GetNumberOfSamples(cardIndex, &numberOfAScanSamples) ;
		if (!isOK)
			return NDTS_BFalse ;
	}

	if ((int) sampleSize == -1)
	{
		isOK = RQ_GetSampleStorageMode(cardIndex, &sampleSize) ;
		if (!isOK)
			return NDTS_BFalse ;
	}

	bytesPerSample = sampleSize == RQ_SS12Bit ? 2 : 1 ;

	if (scanStorage == RQ_SSMultipleAScanMultipleCScan ||
		scanStorage == RQ_SSMultipleAScanNoCScan)
	{
		if (numberOfAScanPerBScan == 0xffffffff)
		{
			isOK = RQ_GetNumberOfAScansPerBScan(cardIndex, &numberOfAScanPerBScan) ;
			if (!isOK)
				return NDTS_BFalse ;
		}

		if (numberOfBScans == 0xffffffff)
		{
			isOK = RQ_GetNumberOfBScansPerBank(cardIndex, &numberOfBScans) ;
			if (!isOK)
				return NDTS_BFalse ;
		}
	}
	else if (scanStorage == RQ_SSOneAScanMultipleCScan)
	{
		numberOfAScanPerBScan = 1 ;
		numberOfBScans = 1 ;
	}
	else
		return NDTS_BFalse ;

	if (pRQState->isAutomaticBScansPerBank)
	{
		numberOfBScans = 1;
	}

	// Keep this in sync with RQ_UpdateSampleParameters()
	// This assumes the A Scan samples per step is the same for each step and so is not correct for sequenced.
	// However, there are only a max of 32 steps in the sequenced so much less chance of running out of memory.
	// It also assumes there is no oversampling.  Oversampling is also not currently implemented on non sequenced firmware.
	numberOfAScanBytes = ((4 + bytesPerSample * numberOfAScanSamples + crcBytes) *  numberOfAScanPerBScan + 
							RQ_NUMBER_OF_ENCODERS * 4 + crcBytes) * numberOfBScans;


	if (numberOfAScanBytes >= RQ_SRAM_BANK_SIZE)
	{
		RQ_HandleRQError(RQ_CheckSRAM_InsufficientSRAM) ;
		return NDTS_BFalse ;
	}

	if (scanStorage == RQ_SSMultipleAScanMultipleCScan ||
		scanStorage == RQ_SSOneAScanMultipleCScan)
	{
		if (numberOfGates == 0xffffffff)
		{
			isOK = RQ_GetNumberOfEnabledGates(cardIndex, &numberOfGates) ;
			if (!isOK)
				return NDTS_BFalse ;
		}
		// This doesn't take into account different numbers of gates per seqeunce or oversampling
		numberOfGateDataBytes = pRQState->gateBytesPerBScan * numberOfBScans ;
		spaceLeft = RQ_SRAM_BANK_SIZE - numberOfAScanBytes ;

		if (numberOfGateDataBytes > spaceLeft)
		{
			RQ_HandleRQError(RQ_CheckSRAM_InsufficientSRAM) ;
			return NDTS_BFalse ;
		}
	}

	return NDTS_BTrue ;
}


NDTS_Boolean CALL_CONV RQ_HandleSRAM(unsigned long cardIndex)
{
	unsigned long numberOfAScanBytes, 
		          numberOfGateDataBytes, numberOfAScanPerBScan, numberOfBScans, spaceLeft ;
	NDTS_Boolean isOK ;
	RQ_ScanStorage scanStorage ;

	RQ_State *pRQState ;
	if (cardIndex >= globalState.pciState.numberOfRapiDAQBoards) return NDTS_BFalse;
	pRQState = &pBoardStates[cardIndex];

	if (pRQState->isAutomaticBScansPerBank)
	{
		isOK = RQ_SetMaximumNumberOfBScansPerBank(cardIndex) ;
		if (!isOK)
			return NDTS_BFalse ;
	}

	isOK = RQ_GetScanStorageOptions(cardIndex, &scanStorage) ;
	if (!isOK)
		return NDTS_BFalse ;

	if (scanStorage == RQ_SSMultipleAScanMultipleCScan ||
		scanStorage == RQ_SSMultipleAScanNoCScan)
	{
		isOK = RQ_GetNumberOfAScansPerBScan(cardIndex, &numberOfAScanPerBScan) ;
		if (!isOK)
			return NDTS_BFalse ;

		isOK = RQ_GetNumberOfBScansPerBank(cardIndex, &numberOfBScans) ;
		if (!isOK)
			return NDTS_BFalse ;
	}
	else if (scanStorage == RQ_SSOneAScanMultipleCScan)
	{
		numberOfAScanPerBScan = 1 ;
		numberOfBScans = 1 ;
	}
	else
		return NDTS_BFalse ;

	numberOfAScanBytes = pRQState->aScanBytesPerBScan * numberOfBScans ;

	if (numberOfAScanBytes >= RQ_SRAM_BANK_SIZE)
	{
		RQ_HandleRQError(RQ_HandleSRAM_InsufficientSRAM) ;
		return NDTS_BFalse ;
	}

	isOK = RQ_SetCScanBaseSRAMAddress(cardIndex, numberOfAScanBytes) ;
	if (!isOK)
		return NDTS_BFalse ;

	if (scanStorage == RQ_SSMultipleAScanMultipleCScan ||
		scanStorage == RQ_SSOneAScanMultipleCScan)
	{
		numberOfGateDataBytes =  pRQState->gateBytesPerBScan * numberOfBScans ;

		spaceLeft = RQ_SRAM_BANK_SIZE - numberOfAScanBytes ;

		if (numberOfGateDataBytes > spaceLeft)
		{
			RQ_HandleRQError(RQ_HandleSRAM_InsufficientSRAM) ;
			return NDTS_BFalse ;
		}
	}

	return NDTS_BTrue ;
}

NDTS_Boolean CALL_CONV RQ_AddDiagnosticString(unsigned long cardIndex, unsigned long registerOffset, unsigned long registerValue)
{
	HANDLE fileHandle ;
	BOOL isWin32OK ;
	DWORD numBytesToWrite, numBytesWritten ;
	char string[512] ;


	RQ_State *pRQState = &pBoardStates[cardIndex];

	if (cardIndex >= globalState.pciState.numberOfRapiDAQBoards) return NDTS_BFalse;

	sprintf(string, "0x%04x:0x%08x\r\n", registerOffset, registerValue) ;

	fileHandle = CreateFile(pRQState->diagnosticsFilename, GENERIC_WRITE, 0, 0, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0) ;
	if (fileHandle == INVALID_HANDLE_VALUE)
		return NDTS_BFalse ;

	SetFilePointer(fileHandle, 0, 0, FILE_END) ;

	numBytesToWrite = strlen(string) ;

	isWin32OK = WriteFile(fileHandle, (LPCVOID) string, numBytesToWrite, &numBytesWritten, 0) ;
	if (isWin32OK == FALSE || numBytesWritten != numBytesToWrite)
		goto Error ;

	isWin32OK = CloseHandle(fileHandle) ;
	if (isWin32OK == FALSE)
		return NDTS_BFalse ;

	return NDTS_BTrue ;

Error:
	CloseHandle(fileHandle) ;
	return NDTS_BFalse ;
}

RQ_API NDTS_Boolean CALL_CONV RQ_SetRegister(unsigned long cardIndex, unsigned long offset, unsigned long value)
{
	NDTS_Boolean isOK ;
	//char string[512] ;

	// Old
	//PCI_State *pPCIState = &pPCIStates[cardIndex];
	RQ_State *pRQState = &pBoardStates[cardIndex];

	if (cardIndex >= globalState.pciState.numberOfRapiDAQBoards) return NDTS_BFalse;

	if (!pRQState->isPCIInitialised)
	{
		RQ_HandleRQError(RQ_SetRegister_IsPCIInitialisedFail) ;
		return NDTS_BFalse ;
	}

	isOK = offset - RQ_SYSTEM_FPGA_REGISTERS_START_OFFSET < RQ_SYSTEM_FPGA_REGISTERS_END_OFFSET ||
		   offset - RQ_SAMPLING_FPGA_REGISTERS_START_OFFSET < RQ_SAMPLING_FPGA_REGISTERS_END_OFFSET ||
		   offset - RQ_PERIPHERAL_FPGA_REGISTERS_START_OFFSET < RQ_PERIPHERAL_FPGA_REGISTERS_END_OFFSET ;

	if (isOK)
	{
		if (pRQState->isDiagnosticsMode)
			RQ_AddDiagnosticString(cardIndex, offset, value) ;

		// Old
		//*(volatile unsigned long *) (pPCIState->pPCIBar2VirtualAddress + RQ_REGISTERS_START_OFFSET + offset) = value ;
		isOK = NDTS_HAL_SetWord(NDTS_SIDRapiDAQ, cardIndex, RQ_REGISTERS_START_OFFSET + offset, value) ;
		if (!isOK)
		{
			return NDTS_BFalse ;
		}
	}
	else
	{
		RQ_HandleRQError(RQ_SetRegister_OffsetOutOfRange) ;
		return NDTS_BFalse ;
	}

	return NDTS_BTrue ;
}

/*
// Write 32 bits to the RAM, using 32, 16 or 8 bit accesses.
__forceinline NDTS_Boolean RQ_WriteWordToRAM(unsigned long cardIndex, unsigned long address, unsigned long value, RQ_RAMTestAccess accessType)
{
	PCI_State *pPCIState = &pPCIStates[cardIndex];
	RQ_State *pRQState = &pBoardStates[cardIndex];

 	if (cardIndex >= globalState.pciState.numberOfRapiDAQBoards) return NDTS_BFalse;

	if (!pRQState->isPCIInitialised)
	{
		RQ_HandleRQError(RQ_SetRegister_IsPCIInitialisedFail) ;
		return NDTS_BFalse ;
	}

	switch(accessType)
	{
	case RQ_RTA32bit:
	case RQ_RTA32bitSlow:
		*(volatile unsigned long *) (pPCIState->pPCIBar2VirtualAddress + address) = value ;
		break;
/* These don't work correctly.  They seem to be still reading and writing 32 bits.  It may be possible to write routines using 
  PlxBusIOPRead that work correctly

	case RQ_RTA16bit:
		*(volatile unsigned short *) (pPCIState->pPCIBar2VirtualAddress + address) = (unsigned short)(value & 0x0000ffff);
		*(volatile unsigned short *) (pPCIState->pPCIBar2VirtualAddress + address + 2) = (unsigned short)((value & 0xffff0000) >> 16);
		break;
	case RQ_RTA8bit:
		*((volatile unsigned char *) pPCIState->pPCIBar2VirtualAddress + address) = (unsigned char)(value & 0x000000ff);
		*((volatile unsigned char *) pPCIState->pPCIBar2VirtualAddress + address + 1) = (unsigned char)((value & 0x0000ff00) >> 8);
		*((volatile unsigned char *) pPCIState->pPCIBar2VirtualAddress + address + 2) = (unsigned char)((value & 0x00ff0000) >> 16);
		*((volatile unsigned char *) pPCIState->pPCIBar2VirtualAddress + address + 3) = (unsigned char)((value & 0xff000000) >> 24);
		break;*/
/*	default:
		NDTS_BFalse;
		break;
	}
	return NDTS_BTrue ;
}
*/

RQ_API NDTS_Boolean CALL_CONV RQ_GetRegister(unsigned long cardIndex, unsigned long offset, unsigned long *pValue)
{
	NDTS_Boolean isOK ;

	// Old
	//PCI_State *pPCIState = &pPCIStates[cardIndex];
	RQ_State *pRQState = &pBoardStates[cardIndex];

	// Preconditions.
	_ASSERTE(pValue) ;

	if (cardIndex >= globalState.pciState.numberOfRapiDAQBoards) 
		return NDTS_BFalse;

	if (!pRQState->isPCIInitialised)
	{
		RQ_HandleRQError(RQ_GetRegister_IsPCIInitialisedFail) ;
		return NDTS_BFalse ;
	}

	isOK = offset - RQ_SYSTEM_FPGA_REGISTERS_START_OFFSET < RQ_SYSTEM_FPGA_REGISTERS_END_OFFSET ||
		   offset - RQ_SAMPLING_FPGA_REGISTERS_START_OFFSET < RQ_SAMPLING_FPGA_REGISTERS_END_OFFSET ||
		   offset - RQ_PERIPHERAL_FPGA_REGISTERS_START_OFFSET < RQ_PERIPHERAL_FPGA_REGISTERS_END_OFFSET ;

	if (!isOK)
	{
		RQ_HandleRQError(RQ_GetRegister_OffsetOutOfRange) ;
		return NDTS_BFalse ;
	}
	else
	{
		// Old
		//*value = *(volatile unsigned long *) (pPCIState->pPCIBar2VirtualAddress + RQ_REGISTERS_START_OFFSET + offset) ;
		//return NDTS_BTrue ;

		return NDTS_HAL_GetWord(NDTS_SIDRapiDAQ, cardIndex, RQ_REGISTERS_START_OFFSET + offset, pValue) ;
	}
}

/*
RQ_API NDTS_Boolean CALL_CONV RQ_ReadWordFromRAM(unsigned long cardIndex, unsigned long address, unsigned long *value, RQ_RAMTestAccess accessType)
{
	PCI_State *pPCIState = &pPCIStates[cardIndex];
	RQ_State *pRQState = &pBoardStates[cardIndex];
	//unsigned short tempShort;
	//unsigned char tempChar;

	if (cardIndex >= globalState.pciState.numberOfRapiDAQBoards) return NDTS_BFalse;

	if (!pRQState->isPCIInitialised)
	{
		RQ_HandleRQError(RQ_GetRegister_IsPCIInitialisedFail) ;
		return NDTS_BFalse ;
	}

	switch(accessType)
	{
	case RQ_RTA32bit:
	case RQ_RTA32bitSlow:
		*value = *(volatile unsigned long *) (pPCIState->pPCIBar2VirtualAddress + address) ;
		break;
		/* See note in WriteWordToRAM.
	case RQ_RTA16bit:
		tempShort = *(volatile unsigned short*) (pPCIState->pPCIBar2VirtualAddress + address) ;
		*value = tempShort;
		tempShort = *(volatile unsigned short*) (pPCIState->pPCIBar2VirtualAddress + address + 2) ;
		*value = *value | (tempShort << 16) ;
		break;
	case RQ_RTA8bit:
		tempChar = *(volatile unsigned char*) (pPCIState->pPCIBar2VirtualAddress + address) ;
		*value = tempChar;
		tempChar = *(volatile unsigned char*) (pPCIState->pPCIBar2VirtualAddress + address + 1) ;
		*value = *value | (tempChar << 8);
		tempChar = *(volatile unsigned char*) (pPCIState->pPCIBar2VirtualAddress + address + 2) ;
		*value = *value | (tempChar << 16);
		tempChar = *(volatile unsigned char*) (pPCIState->pPCIBar2VirtualAddress + address + 3) ;
		*value = *value | (tempChar << 24);
		break; */
/*
	default:
		NDTS_BFalse;
		break;
	}
	return NDTS_BTrue ;
}
*/


NDTS_Boolean CALL_CONV RQ_SetSystemRegister(unsigned long cardIndex, unsigned long offset, unsigned long value)
{
	if (cardIndex >= globalState.pciState.numberOfRapiDAQBoards) return NDTS_BFalse;

	if (offset >= RQ_SYSTEM_FPGA_REGISTERS_END_OFFSET - RQ_SYSTEM_FPGA_REGISTERS_START_OFFSET)
	{
		RQ_HandleRQError(RQ_SetSystemRegister_OutOfRange) ;
		return NDTS_BFalse ;
	}

	return RQ_SetRegister(cardIndex, RQ_SYSTEM_FPGA_REGISTERS_START_OFFSET + offset, value) ;
}

NDTS_Boolean CALL_CONV RQ_GetSystemRegister(unsigned long cardIndex, unsigned long offset, unsigned long *value)
{
	if (cardIndex >= globalState.pciState.numberOfRapiDAQBoards) return NDTS_BFalse;

	if (offset >= RQ_SYSTEM_FPGA_REGISTERS_END_OFFSET - RQ_SYSTEM_FPGA_REGISTERS_START_OFFSET)
	{
		RQ_HandleRQError(RQ_GetSystemRegister_OutOfRange) ;
		return NDTS_BFalse ;
	}

	return RQ_GetRegister(cardIndex, offset, value) ;
}

RQ_API NDTS_Boolean CALL_CONV RQ_SetSamplingRegister(unsigned long cardIndex, unsigned long offset, unsigned long value)
{
	if (cardIndex >= globalState.pciState.numberOfRapiDAQBoards) return NDTS_BFalse;

	if (offset >= RQ_SAMPLING_FPGA_REGISTERS_END_OFFSET - RQ_SAMPLING_FPGA_REGISTERS_START_OFFSET)
	{
		RQ_HandleRQError(RQ_SetSamplingRegister_OutOfRange) ;
		return NDTS_BFalse ;
	}
	return RQ_SetRegister(cardIndex, RQ_SAMPLING_FPGA_REGISTERS_START_OFFSET + offset, value) ;
}

RQ_API NDTS_Boolean CALL_CONV RQ_GetSamplingRegister(unsigned long cardIndex, unsigned long offset, unsigned long *value)
{
	if (cardIndex >= globalState.pciState.numberOfRapiDAQBoards) 
		return NDTS_BFalse;

	if (offset >= RQ_SAMPLING_FPGA_REGISTERS_END_OFFSET - RQ_SAMPLING_FPGA_REGISTERS_START_OFFSET)
	{
		RQ_HandleRQError(RQ_GetSamplingRegister_OutOfRange) ;
		return NDTS_BFalse ;
	}

	return RQ_GetRegister(cardIndex, RQ_SAMPLING_FPGA_REGISTERS_START_OFFSET + offset, value) ;
}

NDTS_Boolean CALL_CONV RQ_SetPeripheralRegister(unsigned long cardIndex, unsigned long offset, unsigned long value)
{
	if (cardIndex >= globalState.pciState.numberOfRapiDAQBoards) return NDTS_BFalse;

	if (offset >= RQ_PERIPHERAL_FPGA_REGISTERS_END_OFFSET - RQ_PERIPHERAL_FPGA_REGISTERS_START_OFFSET)
	{
		RQ_HandleRQError(RQ_SetPeripheralRegister_OutOfRange) ;
		return NDTS_BFalse ;
	}

	return RQ_SetRegister(cardIndex, RQ_PERIPHERAL_FPGA_REGISTERS_START_OFFSET + offset, value) ;
}

NDTS_Boolean CALL_CONV RQ_GetPeripheralRegister(unsigned long cardIndex, unsigned long offset, unsigned long *value)
{
	if (cardIndex >= globalState.pciState.numberOfRapiDAQBoards) return NDTS_BFalse;

	if (offset >= RQ_PERIPHERAL_FPGA_REGISTERS_END_OFFSET - RQ_PERIPHERAL_FPGA_REGISTERS_START_OFFSET)
	{
		RQ_HandleRQError(RQ_GetPeripheralRegister_OutOfRange) ;
		return NDTS_BFalse ;
	}

	return RQ_GetRegister(cardIndex, RQ_PERIPHERAL_FPGA_REGISTERS_START_OFFSET + offset, value) ;
}


NDTS_Boolean CALL_CONV RQ_GetSRAMLong(unsigned long cardIndex, unsigned long offset, unsigned long *pValue)
{
	// Old
	//PCI_State *pPCIState = &pPCIStates[cardIndex];

	if (cardIndex >= globalState.pciState.numberOfRapiDAQBoards) return NDTS_BFalse;

	if (offset > RQ_REGISTERS_END_OFFSET)
	{
		RQ_HandleRQError(RQ_GetSRAMLong_OffsetOutOfRange) ;
		return NDTS_BFalse ;
	}

	return NDTS_HAL_GetWord(NDTS_SIDRapiDAQ, cardIndex, offset, pValue) ;

	// Old
	//*value = *(volatile unsigned long *) (pPCIState->pPCIBar2VirtualAddress + offset) ;
	//return NDTS_BTrue ;
}

NDTS_Boolean CALL_CONV RQ_SetSRAMLong(unsigned long cardIndex, unsigned long offset, unsigned long value)
{
	//PCI_State *pPCIState = &pPCIStates[cardIndex];

	if (cardIndex >= globalState.pciState.numberOfRapiDAQBoards) return NDTS_BFalse;

	if (offset > RQ_REGISTERS_END_OFFSET)
	{
		RQ_HandleRQError(RQ_GetSRAMLong_OffsetOutOfRange) ;
		return NDTS_BFalse ;
	}

	return NDTS_HAL_SetWord(NDTS_SIDRapiDAQ, cardIndex, offset, value) ;

	// Old
	//*(volatile unsigned long *) (pPCIState->pPCIBar2VirtualAddress + offset) = value ;
	//return NDTS_BTrue ;
}

NDTS_Boolean CALL_CONV RQ_SystemRAMTest(unsigned long cardIndex)
{
	NDTS_Boolean isOK ;
	unsigned long nCount, i, pLocalAddress ;
	unsigned long *pData ;

	if (cardIndex >= globalState.pciState.numberOfRapiDAQBoards) return NDTS_BFalse;

	isOK = RQ_OpenDMAChannel(cardIndex) ;
	
	if (!isOK)
		return NDTS_BFalse ;

	nCount = 0x40000 ;
	pData = malloc(sizeof(U32) * nCount) ;
	if (pData == 0)
	{
		return NDTS_BFalse;
	}
	//pData = new U32[nCount] ;

	pLocalAddress = RQ_SYSTEM_RAM_OFFSET ;

	for (i = 0 ; i < nCount ; i++)
		pData[i] = i ;

	isOK = NDTS_HAL_PerformDMATransfer(NDTS_SIDRapiDAQ, cardIndex, (void *) pData, pLocalAddress, nCount << 2, NDTS_BFalse) ;
	if (!isOK)
	{
		free(pData) ;
		return NDTS_BFalse ;
	}

	for (i = 0 ; i < nCount ; i++)
		pData[i] = 0xffffffff ;

	isOK = NDTS_HAL_PerformDMATransfer(NDTS_SIDRapiDAQ, cardIndex, (void *) pData, pLocalAddress, nCount << 2, NDTS_BTrue) ;
	if (!isOK)
	{
		free(pData) ;
		return NDTS_BFalse ;
	}

	i = 0 ; 

	do
	{
		if (pData[i] != i)
			isOK = NDTS_BFalse ;

		i++ ;
	}
	while (i < nCount && isOK) ;

	if (!isOK)
	{
		free(pData) ;
		return NDTS_BFalse ;
	}

	isOK = RQ_CloseDMAChannel(cardIndex) ;
	if (!isOK)
	{
		free(pData) ;
		return NDTS_BFalse ;
	}

	free(pData) ;
	return isOK ;
}

/*
// Returns false on error.  Returns true if test passed or failed
// use pParams->testPassed to determine if the test passed or failed

RQ_API NDTS_Boolean CALL_CONV RQ_RAMTest(unsigned long cardIndex, RQ_RAMTestParams *pParams) 
{
	NDTS_Boolean isOK ;
	unsigned long i, sizeInWords ;
	unsigned long *pDataFromCard, *pDataToCard ;
	int accessType, firstAccessType, lastAccessType;
	int testPattern, firstPattern, lastPattern ;
	unsigned long loopNumber;


	if (cardIndex >= globalState.pciState.numberOfRapiDAQBoards) return NDTS_BFalse;

	if (pParams->testType >= RQ_RTNumberOfValues || pParams->baseAddress >= RQ_REGISTERS_START_OFFSET || pParams->size >= RQ_REGISTERS_START_OFFSET )
	{
		return NDTS_BFalse ;
	}

	sizeInWords = pParams->size >> 2 ;
	pDataToCard = (unsigned long*)malloc( pParams->size) ;
	pDataFromCard = (unsigned long*)malloc( pParams->size) ;

	if (pDataToCard == 0 || pDataFromCard == 0)
	{
		return NDTS_BFalse;
	}
	isOK = NDTS_BTrue ;
	pParams->testPassed = NDTS_BTrue;

	if (pParams->testType==RQ_RTRunAllPatterns)
	{
		firstPattern = 0;
		lastPattern = pParams->testType-1 ;
	}
	else
	{
		firstPattern = pParams->testType;
		lastPattern = pParams->testType;
	}
	if (pParams->accessType==RQ_RTARunAllAccess)
	{
		firstAccessType = 0;
		lastAccessType = pParams->accessType-1 ;
	}
	else
	{
		firstAccessType = pParams->accessType;
		lastAccessType = pParams->accessType;
	}

	for (loopNumber=0; loopNumber < pParams->numLoops; loopNumber++)
	{
		for (testPattern = firstPattern; testPattern <= lastPattern; testPattern++)
		{
			switch(testPattern )
			{
			case RQ_RTaddressPattern:
				for (i = 0 ; i < sizeInWords ; i++)
				{
					pDataToCard[i] = i ;
				}
				break ;

			case RQ_RTallOnes:
				for (i = 0 ; i < sizeInWords ; i++)
				{
					pDataToCard[i] = 0xffffffff ;
				}
				break ;

			case RQ_RTallZeros:
				for (i = 0 ; i < sizeInWords ; i++)
				{
					pDataToCard[i] = 0x00000000 ;
				}
				break ;
			default:
				goto errorExit ;
			}

			for(accessType = firstAccessType; accessType <= lastAccessType; accessType++)
			{			
				for (i = 0 ; i < sizeInWords ; i++)
				{
					pDataFromCard[i] = 0xeeeeeeee ;
				}

				switch(accessType)
				{
				case RQ_RTADMA:
				*****
					isOK = RQ_PerformDMATransfer(cardIndex, (void *) pDataToCard, pParams->size, pParams->baseAddress, NDTS_BFalse) ;
					if (!isOK)
					{
						goto errorExit ;
					}
					isOK = RQ_PerformDMATransfer(cardIndex, (void *) pDataFromCard, pParams->size, pParams->baseAddress, NDTS_BTrue) ;
					if (!isOK)
					{
						goto errorExit ;
					}
					break;
				case RQ_RTA32bit:
				case RQ_RTA16bit:
				case RQ_RTA8bit:
					for (i=0 ; i<sizeInWords; i++)
					{
						RQ_WriteWordToRAM(cardIndex, (i << 2) + pParams->baseAddress, pDataToCard[i], accessType) ;
					}
					for (i=0 ; i<sizeInWords; i++)
					{
						RQ_ReadWordFromRAM(cardIndex, (i << 2) + pParams->baseAddress, (unsigned long*)&(pDataFromCard[i]), accessType) ;
					}
					break;
				case RQ_RTA32bitSlow:
					for (i=0 ; i<sizeInWords; i++)
					{
						RQ_WriteWordToRAM(cardIndex, (i << 2) + pParams->baseAddress, pDataToCard[i], accessType) ;
						RQ_BUSYMICROSLEEP(SLOW_WORDS_MICRO_SLEEP ) ;
					}
					for (i=0 ; i<sizeInWords; i++)
					{
						RQ_ReadWordFromRAM(cardIndex, (i << 2) + pParams->baseAddress, (unsigned long*)&(pDataFromCard[i]), accessType) ;
					}
					break;
				default:
					isOK = NDTS_BFalse ;
					goto errorExit;
				}
				for (i=0 ; i<sizeInWords; i++)
				{
					if (pDataToCard[i] != pDataFromCard[i])
					{
						pParams->testPassed = NDTS_BFalse ;
						pParams->failedAddress = pParams->baseAddress + (i<<2) ;
						pParams->failedData = pDataFromCard[i] ;
						pParams->failedAccessType = accessType;
						pParams->failedLoopNumber = loopNumber;
						pParams->failedTestType = testPattern;
						// Not an error if the test failed - caller must check testPassed
						goto errorExit ;
					}
				}
			}
		}
	}

errorExit:
	free(pDataFromCard) ;
	free(pDataToCard) ;
	return isOK ;
}
*/

// testParams can be 0 for normal operation
RQ_API NDTS_Boolean CALL_CONV RQ_DumpBinFiles(unsigned long cardIndex, char *fileNameA, char *fileNameB, RQ_DumpBinParams *testParams)
{
	unsigned int filesLength, i, j, numIntsToTransfer ;
	unsigned char *pFilesData ;
	unsigned long *pFilesDataAsInts, *pVerifyData = 0 ;
	unsigned char bit, swappedBit, swappedByte ;
	DWORD fileLengthA, fileLengthB, numBytesRead ;
	HANDLE hFileA, hFileB ;
	NDTS_Boolean isOK ;
	BOOL isWin32OK ;
	// Allow files to be uploaded whether or not the SDK is initialised and DMA opened

	pVerifyData = pVerifyData ;

	if (cardIndex >= globalState.pciState.numberOfRapiDAQBoards) return NDTS_BFalse;

	hFileA = CreateFile(fileNameA, GENERIC_READ, FILE_SHARE_READ, 0, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0) ;
	
	if (hFileA == INVALID_HANDLE_VALUE)
	{
		NDTS_LogLine(NDTS_LLEmerg, "Firmware file %s not found", fileNameA);
		RQ_HandleRQError(RQ_DumpBinFiles_SamplingFirmwareOpenFail) ;
		return NDTS_BFalse ;
	}

	fileLengthA = GetFileSize(hFileA, 0) ;

	hFileB = CreateFile(fileNameB, GENERIC_READ, FILE_SHARE_READ, 0, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0) ;
	
	if (hFileB == INVALID_HANDLE_VALUE)
	{
		NDTS_LogLine(NDTS_LLEmerg, "Firmware file %s not found", fileNameB);
		RQ_HandleRQError(RQ_DumpBinFiles_PeripheralFirmwareOpenFail) ;
		return NDTS_BFalse ;
	}

	fileLengthB = GetFileSize(hFileB, 0) ;

	filesLength = fileLengthA + fileLengthB ;

	pFilesData = malloc(sizeof(unsigned char) * filesLength) ; 
	if (pFilesData == 0)
	{
		return NDTS_BFalse;
	}

	pFilesDataAsInts = malloc(sizeof(unsigned long) * filesLength) ;
	if (pFilesDataAsInts == 0)
	{
		free(pFilesData);
		return NDTS_BFalse;
	}

	isWin32OK = ReadFile(hFileA, (VOID *) pFilesData, fileLengthA, &numBytesRead, 0) ;
	if (isWin32OK == 0 || fileLengthA != numBytesRead)
	{
		RQ_HandleRQError(RQ_DumpBinFiles_SamplingFirmwareReadFail) ;

		isOK = NDTS_BFalse ;
		CloseHandle(hFileA) ;
		goto exit ;
	}

	CloseHandle(hFileA) ;
	if (isWin32OK == 0)
	{
		isOK = NDTS_BFalse ;

		RQ_HandleRQError(RQ_DumpBinFiles_SamplingFirmwareCloseFail) ;
		goto exit ;
	}

	isWin32OK = ReadFile(hFileB, (VOID *) (pFilesData + fileLengthA), fileLengthB, &numBytesRead, 0) ;
	if (isWin32OK == 0 || fileLengthB != numBytesRead)
	{
		RQ_HandleRQError(RQ_DumpBinFiles_PeripheralFirmwareReadFail) ;

		isOK = NDTS_BFalse ;
		CloseHandle(hFileA) ;
		CloseHandle(hFileB) ;
		goto exit ;
	}

	isWin32OK = CloseHandle(hFileB) ;
	if (isWin32OK == 0)
	{
		RQ_HandleRQError(RQ_DumpBinFiles_PeripheralFirmwareCloseFail) ;
		isOK = NDTS_BFalse ;
		goto exit ;
	}

	for (i = 0 ; i < filesLength ; i++)
	{
		bit = 0x01 ;
		swappedBit = 0x80 ;
		swappedByte = 0 ;

		for (j = 0 ; j < 8 ; j++)
		{
			if ((pFilesData[i] & bit) != 0)
				swappedByte |= swappedBit ;

			bit <<= 1 ;
			swappedBit >>= 1 ;
		}

		pFilesDataAsInts[i] = (int) swappedByte ;
	}

	/* Assume DMA already open - this function can then 
	   be called both after and before initialisation */
//		isOK = RQ_OpenDMAChannel() ;
//		if (!isOK)
//			goto exit ;

	numIntsToTransfer = filesLength ;

	isOK = NDTS_BTrue; // Remove compiler warning about unint variable
	if (!testParams)
	{
		for (i=0 ; i<numIntsToTransfer; i++)
		{
			// Old
			//isOK = RQ_WriteWordToRAM(cardIndex, (i << 2) + RQ_SYSTEM_RAM_OFFSET, pFilesDataAsInts[i], RQ_RTA32bit) ;
			isOK = NDTS_HAL_SetWord(NDTS_SIDRapiDAQ, cardIndex, RQ_SYSTEM_RAM_OFFSET + (i << 2), pFilesDataAsInts[i]) ;
			if (!isOK) goto exit ;
			RQ_BUSYMICROSLEEP(SLOW_WORDS_MICRO_SLEEP ) ;
		}
	}
	else
		isOK = NDTS_BFalse ;

	/*
	// Test options used during hardware testing - 
	{
		pVerifyData = malloc(sizeof(unsigned long) * filesLength) ; 
		switch (testParams->accessType)
		{
		case RQ_RTADMA:
			if (!testParams->verifyOnly)
			{
			****
				isOK = RQ_PerformDMATransfer(cardIndex, (void *) pFilesDataAsInts, numIntsToTransfer << 2, RQ_SYSTEM_RAM_OFFSET, NDTS_BFalse) ;
				if (!isOK) goto exit ;
			} 
			isOK = RQ_PerformDMATransfer(cardIndex, (void *) pVerifyData, numIntsToTransfer << 2, RQ_SYSTEM_RAM_OFFSET, NDTS_BTrue) ;
			if (!isOK) goto exit ;
			break ;
		case RQ_RTA32bit:
		case RQ_RTA32bitSlow:
			if (!testParams->verifyOnly)
			{
				for (i=0 ; i<numIntsToTransfer; i++)
				{
					isOK = RQ_WriteWordToRAM(cardIndex, (i << 2) + RQ_SYSTEM_RAM_OFFSET, pFilesDataAsInts[i], testParams->accessType) ;			
					if (!isOK) goto exit ;
					if (testParams->accessType == RQ_RTA32bitSlow)
					{
						RQ_BUSYMICROSLEEP(SLOW_WORDS_MICRO_SLEEP ) ;
					}
				}
			}
			for (i=0 ; i<numIntsToTransfer; i++)
			{
				isOK = RQ_ReadWordFromRAM(cardIndex, (i << 2) + RQ_SYSTEM_RAM_OFFSET, (unsigned long*)&(pVerifyData[i]), testParams->accessType) ;
				if (!isOK) goto exit ;
			}
			break;
		default:
			isOK = NDTS_BFalse ;
			goto exit ;
			break ;
		}
		testParams->testPassed = NDTS_BTrue;
		for (i=0; i < numIntsToTransfer; i++)
		{
			if (pVerifyData[i] != pFilesDataAsInts[i])
			{
				testParams->testPassed = NDTS_BFalse;
				testParams->failedAddress = (i << 2) + RQ_SYSTEM_RAM_OFFSET;
				testParams->failedData = pVerifyData[i] ;
				// Comment out the goto exit and uncomment line below to trace all the errors
			//	filelog("fail at word no %d should be %x is %x\n", i,pFilesDataAsInts[i],pVerifyData[i] ) ;
				goto exit ;
			}
		}
	}
	*/
exit :
	free(pFilesData) ;
	free(pFilesDataAsInts) ;

	return isOK ;
}

NDTS_Boolean CALL_CONV RQ_SystemFPGAInversionTest(unsigned long cardIndex)
{
	unsigned long value, value2 ;
	NDTS_Boolean isOK ;

	if (cardIndex >= globalState.pciState.numberOfRapiDAQBoards) return NDTS_BFalse;

	value = 0x12345678 ;

	RQ_SetCheckData(cardIndex, value) ;
	isOK = RQ_GetCheckData(cardIndex, &value2) ;

	return isOK ? value == ~value2 ? NDTS_BTrue : NDTS_BFalse : NDTS_BFalse ;
}

NDTS_Boolean CALL_CONV RQ_CalibrateGetMean(unsigned long cardIndex, unsigned long numSamples, short *samples, short dToAValue, short *mean, NDTS_Boolean isOffset)
{
	unsigned long i ;
	long sigma ;
	NDTS_Boolean isOK ;

	if (cardIndex >= globalState.pciState.numberOfRapiDAQBoards) return NDTS_BFalse;

	if (isOffset)
	{
		isOK = RQ_SetCalibrationOffset(cardIndex, dToAValue) ;
		if (!isOK)
			return NDTS_BFalse ;
	}
	else
	{
		isOK = RQ_SetCalibrationGain(cardIndex, dToAValue) ;
		if (!isOK)
			return NDTS_BFalse ;
	}

	isOK = RQ_SetForceAScanTrigger(cardIndex) ;
	if (!isOK)
		return NDTS_BFalse ;

	isOK = RQ_SetRequestEarlySampleData(cardIndex) ;
	if (!isOK)
		return NDTS_BFalse ;

	isOK = RQ_GetAScan(cardIndex, samples, numSamples*2, 0, 0) ;
	if (!isOK)
		return NDTS_BFalse ;

	sigma = 0 ;

	for (i = 0 ; i < numSamples ; i++)
		sigma += (long) samples[2 + i] ;

	sigma /= (long) numSamples ;

	*mean = (short) sigma ;

	return NDTS_BTrue ;
}

/////////////////////////////////////////////
// Calibrate - work out calibrationGains and calibrationOffsets
// for this inputRange.  Applies ground to the input to 
// work out the offset then applies ref volt to work out gain.
// Writes the new values to EE if they have changed significantly
// Saves and restores state of the cardIndex.
//
// DGG: broke the calibrate an input value into a sub-function
// so that the hardware tester can get more information on 
// why a calibration failed.

NDTS_Boolean CALL_CONV RQ_Calibrate(unsigned long cardIndex, RQ_AnalogueInputRange inputRange)
{
	NDTS_Boolean isOK ;
	unsigned long numSamplesForCalibration, aToDInputVoltageRange, numberOfScansValue, stepsInSequence = 0, sequenceStep = 0, numberOfSamples ;
	RQ_AnalogueInput analogueInput ;
	RQ_AnalogueInputRange analogueGain ;
	RQ_ScanStorage scanStorageOptions;
	RQ_Trigger trigger ;
	NDTS_Boolean isDataAvailable ;
	short *samples ;
	RQ_State *pRQState ;
	short offset, gain;

	if (cardIndex >= globalState.pciState.numberOfRapiDAQBoards) return NDTS_BFalse;

	pRQState = &pBoardStates[cardIndex];

	isOK = RQ_GetAToDInputVoltageRange(cardIndex, &aToDInputVoltageRange) ;
	if (!isOK)
		return NDTS_BFalse ;

	isOK = RQ_GetAnalogueInput(cardIndex, &analogueInput) ;
	if (!isOK)
		return NDTS_BFalse ;

	isOK = RQ_GetAnalogueInputRange(cardIndex, &analogueGain) ;
	if (!isOK)
		return NDTS_BFalse ;

	isOK = RQ_GetTriggerSelect(cardIndex, &trigger) ;
	if (!isOK)
		return NDTS_BFalse ;

	isOK = RQ_GetNumberOfSamples(cardIndex, &numberOfSamples) ;
	if (!isOK)
		return NDTS_BFalse ;

	isOK = RQ_GetScanStorageOptions(cardIndex, &scanStorageOptions) ;
	if (!isOK)
		return NDTS_BFalse ;

	isOK = RQ_GetSamplingRegister(cardIndex, RQ_NUMBER_OF_SCANS, &numberOfScansValue) ;
	if (!isOK)
		return NDTS_BFalse ;

	if (pRQState->isSequence)
	{
		isOK = RQ_GetStepsInSequence(cardIndex, &stepsInSequence);
		if (!isOK)
			return NDTS_BFalse ;

		isOK = RQ_GetSequenceStep(cardIndex, &sequenceStep);
		if (!isOK)
			return NDTS_BFalse ;

		isOK = RQ_SetStepsInSequence(cardIndex, 1);
		if (!isOK)
			goto restoreValuesAndExit ;

		isOK = RQ_SetSequenceStep(cardIndex, 0);
		if (!isOK)
			goto restoreValuesAndExit ;
	}

	numSamplesForCalibration = 1000;
	isOK = RQ_SetNumberOfSamples(cardIndex, numSamplesForCalibration) ;
	if (!isOK)
		goto restoreValuesAndExit ;

	samples = malloc(sizeof(short) * numSamplesForCalibration) ;
	if (samples == 0)
	{
		RQ_HandleRQError(RQ_Calibrate_OutOfMemory) ;
		return NDTS_BFalse ;
	}

	isOK = RQ_SetTriggerSelect(cardIndex, RQ_TInternal) ;
	if (!isOK)
		goto restoreValuesAndExit ;

	isOK = RQ_SetScanStorageOptions(cardIndex, RQ_SSMultipleAScanNoCScan) ;
	if (!isOK)
		goto restoreValuesAndExit ;

	Sleep(1) ;

	isOK = RQ_GetIsSampleDataAvailable(cardIndex, &isDataAvailable) ;
	if (!isOK)
		goto restoreValuesAndExit ;

	if (isDataAvailable != NDTS_BFalse)
	{
		isOK = RQ_IndicateSampleDataDownloaded(cardIndex) ;
		if (!isOK)
			goto restoreValuesAndExit ;
	}

	isOK = RQ_GetIsSampleDataAvailable(cardIndex, &isDataAvailable) ;
	if (!isOK)
		goto restoreValuesAndExit ;

	if (isDataAvailable != NDTS_BFalse)
	{
		isOK = RQ_IndicateSampleDataDownloaded(cardIndex) ;
		if (!isOK)
			goto restoreValuesAndExit ;
	}

	isOK = RQ_SetAToDInputVoltageRange(cardIndex, 1) ;
	if (!isOK)
		goto restoreValuesAndExit ;


	isOK = RQ_CalibrateInput(cardIndex, inputRange, numSamplesForCalibration, samples, &offset, &gain);
	free(samples) ;
	if (!isOK)
		goto restoreValuesAndExit ;

	pRQState->calibrationFlags |= (1 << inputRange); // calibrated this range
	pRQState->isCalibrated[inputRange] = NDTS_BTrue ;

	// Now the calibration is complete we have the option to write the values
	// back to EE
	{
		NDTS_Boolean commit;
		unsigned long j;

		commit = RQ_WriteEEPROMifDrifted(cardIndex, RQ_EE_CFLAG_OFFSET, pRQState->calibrationFlags, 0);
		for (j = 0 ; j < RQ_NUMBER_OF_ANALOGUE_GAIN_VALUES - 1; j++)
		{
			if (pRQState->calibrationFlags & (1 << j))
			{
				commit |= RQ_WriteEEPROMifDrifted(cardIndex, RQ_EE_ZERO_OFFSET + j, pRQState->calibrationOffsets[j], 10);
				commit |= RQ_WriteEEPROMifDrifted(cardIndex, RQ_EE_GAIN_OFFSET + j, pRQState->calibrationGains[j], 10);
			}
		}
		// best write all the magic numbers etc just in case
		commit |= RQ_WriteEEPROMifDrifted(cardIndex, RQ_EE_MAGIC_OFFSET, RQ_EE_MAGIC_NUMBER_NEW, 0);
		commit |= RQ_WriteEEPROMifDrifted(cardIndex, RQ_EE_SERIAL_OFFSET, pRQState->cardSerialNumber, 0);

		if (commit)
		{
			// only do this if something changed
			NDTS_LogLine(NDTS_LLWarning, "Writing new calibration values to EEPROM");
			RQ_CommitEEPROM(cardIndex);
		}
	}

restoreValuesAndExit :
	isOK &= RQ_SetAToDInputVoltageRange(cardIndex, aToDInputVoltageRange) ;
	isOK &= RQ_SetAnalogueInput(cardIndex, analogueInput) ;
	isOK &= RQ_SetAnalogueInputRangeInternal(cardIndex, analogueGain) ;
	isOK &= RQ_SetTriggerSelect(cardIndex, trigger) ;
	isOK &= RQ_SetNumberOfSamples(cardIndex, numberOfSamples) ;
	isOK &= RQ_SetScanStorageOptions(cardIndex, scanStorageOptions) ;

	isOK &= RQ_SetSamplingRegister(cardIndex, RQ_NUMBER_OF_SCANS, numberOfScansValue) ;

	if (pRQState->isSequence)
	{
		isOK &= RQ_SetStepsInSequence(cardIndex, stepsInSequence);
		isOK &= RQ_SetSequenceStep(cardIndex, sequenceStep);
	}
	return isOK;
}


////////////////////////////////////////////////////////////////
// Calibrate a single input range (e.g. 100mV or 5V)
// returns RQ_OK
//         RQ_CalibrateOffsetComputationFail
//         RQ_CalibrateGainComputationFail
//   or an error passed up from an internal SDK function call.
// numSamples and samples* pass in the sample memory 
// that is used to calculate the mean A/D value.
// offset and gain are used to return the actual offset and gain
// calibration values.

RQ_API NDTS_Boolean CALL_CONV RQ_CalibrateInput(unsigned long cardIndex, RQ_AnalogueInputRange inp, unsigned long numSamples, short* samples, short* pOffset, short* pGain)
{
	unsigned long itrCount;
	float testValue = 0.0, oldTestValue = 0.0;
	short testMean = 0, oldMean = 0;
	short calibrationVoltageReferenceValues[RQ_NUMBER_OF_ANALOGUE_GAIN_VALUES] = {-1896, -1853, -1662, -1848, -1945, -1678} ;
	NDTS_Boolean isOK;
	RQ_State *pRQState ;
	NDTS_Boolean isOffset = NDTS_BTrue;
	int i;
	RQ_InitialisationStep step ;

	if (cardIndex >= globalState.pciState.numberOfRapiDAQBoards) return NDTS_BFalse;

	pRQState = &pBoardStates[cardIndex];

	for (i=0; i<2; i++)
	{
		float slope = 1.0;  // The slope of the graph of the mean against the input value we are hunting for.
		short referenceValue = 0;
		RQ_AnalogueInput inputSelection = RQ_AIInputGND;
		NDTS_Boolean hasConverged = NDTS_BFalse;

		if (isOffset)
		{
			inputSelection = RQ_AIInputGND;
			testValue = (float)pRQState->calibrationOffsets[inp];
			slope = 1.0;
			referenceValue = 0;
			step = RQ_ISCalibrateDCOffset ;
		}
		else
		{
			inputSelection =  RQ_AIInputCalibration ;
			testValue = (float)pRQState->calibrationGains[inp];
			// FT: Empirically we should adjust the zero offset by the deviation and the gain by
			// 6 times the deviation.
			slope = 6.0;
			referenceValue = calibrationVoltageReferenceValues[inp];
			isOK = RQ_SetCalibrationVoltage(cardIndex, (RQ_CalibrationVoltage) (RQ_NUMBER_OF_ANALOGUE_GAIN_VALUES - 1 - inp)) ;
			if (!isOK)
				NDTS_BFalse;

			step = RQ_ISCalibrateVoltageRange ;
		}

		if (globalState.pfnInitStatusCallback != 0)
			(*globalState.pfnInitStatusCallback)(step, RQ_ISSChecking) ;

		isOK = RQ_SetAnalogueInput(cardIndex, inputSelection) ;
		if (!isOK)
			return NDTS_BFalse;

		isOK = RQ_SetAnalogueInputRangeInternal(cardIndex, inp) ;
		if (!isOK)
			return NDTS_BFalse;

		itrCount = 0 ;
		testMean = 0;
		do
		{
			isOK = RQ_CalibrateGetMean(cardIndex, numSamples, samples, (short)testValue, &testMean, isOffset) ;
			if (!isOK)
				goto calibrate_fail ;

			// Adjust the slope if the rounding errors will not be too bad
			if (itrCount != 0 && abs(oldMean - testMean) > 5)
				slope = (oldTestValue - testValue) / (float)(oldMean - testMean) ;

			oldMean = testMean;
			oldTestValue = testValue;
			
			//NDTS_LogLine(NDTS_LLDebug, "Range %d %s Val %04.4f. Mean %05d. Target %05d. Slope %f.", inp, isOffset ? "Zero " : "Gain ",  testValue, testMean, referenceValue, slope);

			testValue = testValue - (slope * (float)(testMean - referenceValue) + 0.5f);

			// Make sure we always jump by a full value as the hardware only takes integer values
			if (floor(testValue) == floor(oldTestValue))
			{
				if (testValue < oldTestValue)
					testValue++;
				else
					testValue--;
			}
			
			// Daniel March 07 - If we have swung out of range, it is likely that the slope is too steep rather than it 
			// not being possible to calibrate the card.  There are some cards that have a slope of 0.16 for the zero instead of the 1.0 that is assumed.
			// Clamp the test value and loop - this often gives a better slope value and results in convergance.
			if (testValue < 0.0)
				testValue = 0.0;
			if (testValue > 4095.0)
				testValue = 4095.0;

			itrCount++ ;
			// Try for +- 2 - maybe this should depend on the slope because with e.g. a slope of 0.1, there is a chance
			// that it is not possible to get within 2 of the zero.
			if (abs(testMean - referenceValue) <= 2) 
				hasConverged = NDTS_BTrue;
		}
		while (!hasConverged && (itrCount < 10));

		// If all else fails, try a binary search.  This should never be needed if the above algorithm is correct.
		if (!hasConverged)
		{
			short chopStep;
			testValue = 2048;
			for (chopStep = 1024; chopStep > 0; chopStep /= 2)
			{

				isOK = RQ_CalibrateGetMean(cardIndex, numSamples, samples, (short)testValue, &testMean, isOffset) ;
				if (!isOK)
					goto calibrate_fail ;

				if (abs(testMean - referenceValue) < 2)
					break;
				
				testValue += chopStep * (((testMean - referenceValue) > 0) ? -1 : 1);
			}
			// The mean reported here will be the one from the previous iteration if chop is zero.
			NDTS_LogLine(NDTS_LLErr, "Range %d %s Calibrate done via binary chop.  Val %04.4f. Mean %05d. Target %05d. Chop %d.", 
					inp, isOffset ? "Zero " : "Gain ",  testValue, testMean, referenceValue, chopStep);
		}

		if (isOffset)
		{
			*pOffset = (short)testValue;	
			pRQState->calibrationOffsets[inp] = *pOffset;
		}
		else
		{
			*pGain = (short)testValue;	
			pRQState->calibrationGains[inp] = *pGain;
		}

		if (globalState.pfnInitStatusCallback != 0)
			(*globalState.pfnInitStatusCallback)(step, RQ_ISSOK) ;

		// Go round the loop again - this time doing the gain rather than the zero offset
		isOffset = NDTS_BFalse;
	}
	return NDTS_BTrue;

calibrate_fail :
	if (globalState.pfnInitStatusCallback != 0)
		(*globalState.pfnInitStatusCallback)(step, RQ_ISSFail) ;

	return NDTS_BFalse ;
}

NDTS_Boolean CALL_CONV RQ_SetCalibrationGain(unsigned long cardIndex, unsigned long dToAWord)
{
	LARGE_INTEGER freqLarge, countLargeA, countLargeB ;
	float numSeconds ;
	BOOL isWIN32OK ;
	unsigned long value ;
	NDTS_Boolean isWriteComplete ;
	NDTS_Boolean isOK, hasTimedOut ;

	if (cardIndex >= globalState.pciState.numberOfRapiDAQBoards) return NDTS_BFalse;

	if (dToAWord > 4095)
	{
		RQ_HandleRQError(RQ_SetCalibrationGain_InvalidDToAWord) ;
		return NDTS_BFalse ;
	}

	value = dToAWord + 0x00002000 ;

	isOK = RQ_SetPeripheralRegister(cardIndex, RQ_CALIBRATION_DATA, value) ;	
	if (!isOK)
		return NDTS_BFalse ;

	isWIN32OK = QueryPerformanceFrequency(&freqLarge) ;
	if (!isWIN32OK)
	{
		RQ_HandleRQError(RQ_SetCalibrationGain_TimerFail) ;
		return NDTS_BFalse ;
	}

	QueryPerformanceCounter(&countLargeA) ;

	do
	{
		QueryPerformanceCounter(&countLargeB) ;

		isOK = RQ_GetIsCalibrationWriteComplete(cardIndex, &isWriteComplete) ;
		if (!isOK)
			return NDTS_BFalse ;

		numSeconds = (float) (countLargeB.QuadPart - countLargeA.QuadPart) / (float) freqLarge.QuadPart ;
		hasTimedOut = numSeconds > RQ_CALIBRATION_WRITE_MAX_WAIT ; 
	}
	while (isWriteComplete == NDTS_BFalse && !hasTimedOut) ;

	if (hasTimedOut)
	{
		RQ_HandleRQError(RQ_SetCalibrationGain_TimeOut) ;
		return NDTS_BFalse ;
	}

	Sleep(CALIBRATION_SETTLE_TIME_MS) ;

	return NDTS_BTrue ;
}

NDTS_Boolean CALL_CONV RQ_SetCalibrationOffset(unsigned long cardIndex, unsigned long dToAWord)
{
	LARGE_INTEGER freqLarge, countLargeA, countLargeB ;
	float numSeconds ;
	BOOL isWIN32OK ;
	unsigned long value ;
	NDTS_Boolean isWriteComplete ;
	NDTS_Boolean isOK, hasTimedOut ;

	if (cardIndex >= globalState.pciState.numberOfRapiDAQBoards) return NDTS_BFalse;

	if (dToAWord > 4095)
	{
		RQ_HandleRQError(RQ_SetCalibrationOffset_InvalidDToAWord) ;
		return NDTS_BFalse ;
	}

	value = dToAWord + 0x00003000 ;

	isOK = RQ_SetPeripheralRegister(cardIndex, RQ_CALIBRATION_DATA, value) ;	
	if (!isOK)
		return NDTS_BFalse ;

	isWIN32OK = QueryPerformanceFrequency(&freqLarge) ;
	if (!isWIN32OK)
	{
		RQ_HandleRQError(RQ_SetCalibrationOffset_TimerFail) ;
		return NDTS_BFalse ;
	}

	QueryPerformanceCounter(&countLargeA) ;

	do
	{
		QueryPerformanceCounter(&countLargeB) ;

		isOK = RQ_GetIsCalibrationWriteComplete(cardIndex, &isWriteComplete) ;
		if (!isOK)
			return NDTS_BFalse ;

		numSeconds = (float) (countLargeB.QuadPart - countLargeA.QuadPart) / (float) freqLarge.QuadPart ;
		hasTimedOut = numSeconds > RQ_CALIBRATION_WRITE_MAX_WAIT ; 
	}
	while (isWriteComplete == NDTS_BFalse && !hasTimedOut) ;

	if (hasTimedOut)
	{
		RQ_HandleRQError(RQ_SetCalibrationOffset_TimeOut) ;
		return NDTS_BFalse ;
	}

	Sleep(CALIBRATION_SETTLE_TIME_MS) ;

	return NDTS_BTrue ;
}

NDTS_Boolean CALL_CONV RQ_GetCalibrationDToAWord(unsigned long cardIndex, unsigned long *dToAWord)
{
	unsigned long value ;
	NDTS_Boolean isOK ;

	if (cardIndex >= globalState.pciState.numberOfRapiDAQBoards) return NDTS_BFalse;

	isOK = RQ_GetPeripheralRegister(cardIndex, RQ_CALIBRATION_DATA, &value) ;
	if (!isOK)
		return NDTS_BFalse ;

	value &= 0x00000fff ;
	*dToAWord = value ;

	return NDTS_BTrue ;
}

NDTS_Boolean CALL_CONV RQ_GetIsCalibrationWriteComplete(unsigned long cardIndex, NDTS_Boolean *isComplete)
{
	unsigned long value ;
	NDTS_Boolean isOK ;
	
	if (cardIndex >= globalState.pciState.numberOfRapiDAQBoards) return NDTS_BFalse;

	isOK = RQ_GetSamplingRegister(cardIndex, RQ_CALIBRATION_DATA, &value) ;
	if (!isOK)
		return NDTS_BFalse ;
	
	*isComplete = (value & 0x80000000) == 0x00000000 ? NDTS_BTrue : NDTS_BFalse ;

	return NDTS_BTrue ;
}

NDTS_Boolean CALL_CONV RQ_SetCalibrationVoltage(unsigned long cardIndex, RQ_CalibrationVoltage calibrationVoltage)
{
	unsigned long value ;
//	NDTS_Boolean isOK ;
	RQ_State *pBoardState = RQ_ReturnBoardStatePointer(cardIndex) ;
	if (!pBoardState) return NDTS_BFalse; 

	if (calibrationVoltage != RQ_CV4096mV && calibrationVoltage != RQ_CV1899mV &&
		calibrationVoltage != RQ_CV902mV && calibrationVoltage != RQ_CV406mV &&
		calibrationVoltage != RQ_CV181mV && calibrationVoltage != RQ_CV92mV)
	{
		RQ_HandleRQError(RQ_SetCalibrationVoltage_InvalidCalibrationVoltage) ;
		return NDTS_BFalse ;
	}

	value = pBoardState->peripheralRegisterShadow;
	value &= 0xff8fffff ;
	value |= (unsigned long) calibrationVoltage << 20 ;
	pBoardState->peripheralRegisterShadow = value;
	return RQ_SetPeripheralRegister(cardIndex, RQ_PERIPHERAL_HARDWARE_CONTROL, value) ;
}

NDTS_Boolean CALL_CONV RQ_GetCalibrationVoltage(unsigned long cardIndex, RQ_CalibrationVoltage *calibrationVoltage)
{
	unsigned long value ;
	RQ_State *pBoardState = RQ_ReturnBoardStatePointer(cardIndex) ;
	if (!pBoardState) return NDTS_BFalse; 

	value = pBoardState->peripheralRegisterShadow;
	value &= 0x00700000 ;
	*calibrationVoltage = (RQ_CalibrationVoltage) (value >> 20) ;

	return NDTS_BTrue ;
}

NDTS_Boolean CALL_CONV RQ_SetSensorArrayNumberOfElements(unsigned long cardIndex, unsigned long numberOfElements)
{
	unsigned long value ;
	NDTS_Boolean isOK ;

	if (cardIndex >= globalState.pciState.numberOfRapiDAQBoards) return NDTS_BFalse;

	if (numberOfElements == 0 || numberOfElements > 128)
	{
		RQ_HandleRQError(RQ_SetSensorArrayNumberOfElements_InvalidNumberOfElements) ;
		return NDTS_BFalse ;
	}
	
	isOK = RQ_GetPeripheralRegister(cardIndex, RQ_SENSOR_ARRAY_SETUP, &value) ;
	if (!isOK)
		return NDTS_BFalse ;

	value &= 0xffffff80 ;
	value |= numberOfElements - 1 ;

	return RQ_SetPeripheralRegister(cardIndex, RQ_SENSOR_ARRAY_SETUP, value) ;
}

NDTS_Boolean CALL_CONV RQ_GetSensorArrayNumberOfElements(unsigned long cardIndex, unsigned long *numberOfElements)
{
	unsigned long value ;
	NDTS_Boolean isOK ;

	if (cardIndex >= globalState.pciState.numberOfRapiDAQBoards) return NDTS_BFalse;

	isOK = RQ_GetPeripheralRegister(cardIndex, RQ_SENSOR_ARRAY_SETUP, &value) ;
	if (!isOK)
		return NDTS_BFalse ;

	value &= 0x0000007f ;
	*numberOfElements = value + 1 ;

	return NDTS_BTrue ;
}

void CALL_CONV RQ_CreateCriticalSection(unsigned long cardIndex)
{
	RQ_State *pRQState ;

	if (cardIndex >= globalState.pciState.numberOfRapiDAQBoards) return;

	pRQState = &pBoardStates[cardIndex];

	InitializeCriticalSection(&pRQState->criticalSection) ;
	pRQState->lockEntryCount = 0;
	pRQState->pauseSequenceEntryCount = 0;	
	pRQState->sequenceState = RQ_SSStopped;
}

void CALL_CONV RQ_DeleteCriticalSection(unsigned long cardIndex)
{
	RQ_State *pRQState ;

	if (cardIndex >= globalState.pciState.numberOfRapiDAQBoards) return;

	pRQState = &pBoardStates[cardIndex];

	DeleteCriticalSection(&pRQState->criticalSection) ;
}

RQ_API void CALL_CONV RQ_Lock(unsigned long cardIndex)
{
	RQ_State *pRQState ;

	if (cardIndex >= globalState.pciState.numberOfRapiDAQBoards) return;

	pRQState = &pBoardStates[cardIndex] ;

	EnterCriticalSection(&pRQState->criticalSection) ;
	++pRQState->lockEntryCount ;
}

RQ_API void CALL_CONV RQ_Unlock(unsigned long cardIndex)
{
	RQ_State *pRQState ;

	if (cardIndex >= globalState.pciState.numberOfRapiDAQBoards) return;

	pRQState = &pBoardStates[cardIndex];

	--pRQState->lockEntryCount ;
	LeaveCriticalSection(&pRQState->criticalSection) ;
}

RQ_API void CALL_CONV RQ_PauseSequence(unsigned long cardIndex)
{
	RQ_State *pRQState ;
	if (cardIndex >= globalState.pciState.numberOfRapiDAQBoards) return;
	pRQState = &pBoardStates[cardIndex] ;

	if (!pRQState->isSequence || pRQState->sequenceState == RQ_SSStopped)
		return;

	if (++pRQState->pauseSequenceEntryCount == 1)
	{
		_ASSERTE(pRQState->sequenceState != RQ_SSPaused);
		RQ_StopSequence(cardIndex);
		pRQState->sequenceState = RQ_SSPaused;
	}
}

RQ_API void CALL_CONV RQ_ResumeSequence(unsigned long cardIndex)
{
	RQ_State *pRQState ;
	if (cardIndex >= globalState.pciState.numberOfRapiDAQBoards) return;
	pRQState = &pBoardStates[cardIndex] ;

	if (!pRQState->isSequence || pRQState->sequenceState == RQ_SSStopped)
		return;

	if (--pRQState->pauseSequenceEntryCount == 0)
	{
		_ASSERTE(pRQState->sequenceState != RQ_SSRunning);
		RQ_StartSequence(cardIndex);
		pRQState->sequenceState = RQ_SSRunning;
	}
}

// Create the critical section and inititalise the SDK's internal data structures
// RQ_Lock() is called as soon as the critical section is created to 
// prevent any other processes jumping in.  
// RQ_Unlock() is not called in this funciton.
// The caller must call RQ_Unlock() when initialisation
// has been completed

RQ_API NDTS_Boolean CALL_CONV RQ_StructuresInitialise(unsigned long cardIndex, RQ_SamplingFirmwareVersion sfv, RQ_PeripheralFirmwareVersion pfv)
{
	unsigned long i, j ;
	RQ_State *pRQState ;

	pfv = pfv ;

	if (cardIndex >= globalState.pciState.numberOfRapiDAQBoards) return NDTS_BFalse;

	if (pBoardStates == 0) return NDTS_BFalse; // can't initialise yet because static array isn't allocated
	
	pRQState = &pBoardStates[cardIndex];

	RQ_CreateCriticalSection(cardIndex) ;

	RQ_Lock(cardIndex) ;

	if (pRQState->isInitialised)
	{
		RQ_HandleRQError(RQ_Initialise_AlreadyInitialised) ;
		RQ_Unlock(cardIndex) ;
		return NDTS_BFalse ;
	}

		// Check the firmware version and set flags as appropriate
	if (sfv >= 0x200)
	{
		// Sequenced 
		pRQState->isSequence = NDTS_BTrue ;

		if ((sfv & 0xff) < 0x25 || (pfv & 0xff) < 0x1f || pfv  == 0x220)
		{
			RQ_HandleRQError(RQ_Initialise_FirmwareVersionTooOld) ;
			RQ_Unlock(cardIndex) ;
			return NDTS_BFalse ;
		}

		// No CRC in sequenced
		pRQState->crcState.pCRCTable = 0 ;
		pRQState->crcState.isGatesCRCPresent = NDTS_BFalse ;
	}
	else
	{ 
		// Non sequenced
		pRQState->isSequence = NDTS_BFalse ;

		if ((sfv & 0xff) < 0x21 || (pfv & 0xff) < 0x14)
		{
			RQ_HandleRQError(RQ_Initialise_FirmwareVersionTooOld) ;
			RQ_Unlock(cardIndex) ;
			return NDTS_BFalse ;
		}
		pRQState->crcState.pCRCTable = malloc(256 * sizeof(unsigned long)) ;
		RQ_GenerateCRCTable(pRQState->crcState.pCRCTable) ;
		pRQState->crcState.isGatesCRCPresent = NDTS_BTrue ;
	}

	pRQState->currentSequenceStep = 0 ;


	pRQState->peripheralRegisterShadow = (unsigned long)RQ_GetDefaultValueInt(RQdef_PeripheralRegister);
	pRQState->rqBscanTriggerControlShadow = 0x00070050;  // Default to no encoders for triggering as this is the value returned by PER_V014 following initialisation.
	pRQState->rqTriggerControlShadow = 0x10000;  // Default to trig by per FPGA, as for PER_V014 

	// Set pointers to zero early on so if the init fails, there is no crash when attempting to free the memory
	pRQState->pPingPongSequence = NULL; 
	pRQState->pTCGCurve = NULL;
	pRQState->numberOfTCGValues = 0;
	pRQState->pCurvedGateData = NULL;
	pRQState->zeroTweak[0] = 0;
	pRQState->zeroTweak[1] = 0;
	pRQState->zeroTweak[2] = 0;
	pRQState->zeroTweak[3] = 0;
	pRQState->zeroTweak[4] = 0;
	pRQState->zeroTweak[5] = 0;
	pRQState->inputRange = RQ_AG1V;
	for (j = 0; j < RQ_MAXIMUM_NUMBER_OF_STEPS; j++)
	{
		pRQState->oversampleCountThisStep[j] = 1;
	}

	for (j = 0; j < RQ_MAXIMUM_NUMBER_OF_ELEMENTS; j++)
	{
		for (i = 0 ; i < RQ_ReturnMaximumNumberOfGates(pRQState->isSequence); i++)
		{
			memset(&(pRQState->gates[j][i]), 0, sizeof(RQ_Gate)) ;

			pRQState->gates[j][i].index = (RQ_GateIndex) i ;
			// long startCount ;  - commented out gets set to zero by the memset above
			// long stopCount ;
			// unsigned long threshold ;
			pRQState->gates[j][i].triggerGate = RQ_GTAbsolute ;
			// RQ_GateThresholdPolarity thresholdPolarity ;
			// RQ_GateTriggerMode triggerMode ;
		    pRQState->gates[j][i].distanceMetric = RQ_GDMPeak;  // Default everything to peak so setting RQ_SGVAlways doesn't return error on default settings
			pRQState->gates[j][i].isEnabled = NDTS_BFalse ; 
			pRQState->gates[j][i].amplitudePeakSelect = RQ_GAPSLargestPeak;
			pRQState->gates[j][i].distancePeakSelect = RQ_GDPSLargestPeak;
			// RQ_StoreGateValue storeGateValue;  
			// RQ_SnapshotDataSelection snapshotSelection;
			// int reservedSetToZero0;  
			// int reservedSetToZero1;
			// int reservedSetToZero2;
			// int reservedSetToZero3;
			// int reservedSetToZero4;
		}
	}

	pRQState->numberOfElementsForFlipping = 0 ;

	// Statistics for CRC failures
	memset(&pRQState->crcState.statistics, 0, sizeof(RQ_CRCStatistics)) ;

	pRQState->numberOfAScansPerBScan = 1 ;  // Need to default to sensible value so its possible to set number of samples before setting number of A per B (steps)
	pRQState->numberOfBScansPerBank = 0 ;

	//sprintf(pRQState->diagnosticsFilename, "%s","RQ_diag.txt") ;
	RQ_GetDefaultValueString(RQdef_DiagnosticsFilename, pRQState->diagnosticsFilename);

	pRQState->numberToCheckEitherSide = 0;
	pRQState->proportionToTriggerOff = 1.0;
	pRQState->isAutomaticBScansPerBank = NDTS_BTrue ;
	pRQState->numberOfUpdateSampleParametersHooks = 0;
	pRQState->ppUpdateSampleParametersHooks = malloc(sizeof(void*) * MAX_SDK_HOOKS );
	pRQState->numberOfGetEncoderOutputHooks = 0;
	pRQState->ppGetEncoderOutputHooks = malloc(sizeof(void*) * MAX_SDK_HOOKS );
	pRQState->numberOfResetEncoderOutputHooks = 0;
	pRQState->ppResetEncoderOutputHooks = malloc(sizeof(void*) * MAX_SDK_HOOKS );
	pRQState->dataSamplingStatus = 0 ;

	// already done earlier
	//for (i = 0; i < RQ_MAXIMUM_NUMBER_OF_STEPS; i++)
	//{
	//	pRQState->oversampleCountThisStep[i] = RQ_OS1;
	//}

	for (i = 0; i < RQ_MAXIMUM_NUMBER_OF_ELEMENTS; i++)
	{
		pRQState->aScanOffsetThisStep[i] = 4;  // Default to four samples per step, zero is not allowed and will cause problems with the data transfer code
		pRQState->aScanSamplesThisStep[i] = 4;
		pRQState->gateCountThisStep[i] = 0;
		pRQState->gateOffsetThisStep[i] = 0;
	}

	pRQState->aScanBytesPerBScan = 0;
	pRQState->currentSequenceStep = 0;
	pRQState->pCurvedGateData = NULL;
	pRQState->gateBytesPerBScan = 0;
	pRQState->tweakMinus20 = (short)RQ_GetDefaultValueInt(RQdef_GainTweakMinus20);
	pRQState->tweakMinus40 = (short)RQ_GetDefaultValueInt(RQdef_GainTweakMinus40);
	pRQState->tweakPlus20 = (short)RQ_GetDefaultValueInt(RQdef_GainTweakPlus20);

	for (i=0; i <  NUMBER_OF_DIAGNOSTIC_STRINGS; i++)
	{
		sprintf(pRQState->ppDiagnosticStrings[i], "Test %d", i);
	}
	pRQState->bScansPerSecond = 1;
	// Fixed for sequence compatible firmware.
	pRQState->maxTCGPoints = 8192;

	RQ_Unlock(cardIndex) ;

	// All OK
	return NDTS_BTrue ;
}

RQ_API NDTS_Boolean CALL_CONV  RQ_PCIInitialise(unsigned long cardIndex)
{
	/*
	// Old
	RETURN_CODE pciReturnCode ;
	//U32 reqLimit ;
	NDTS_Boolean isOK ;
	unsigned long card;
	U32 dev;
	RETURN_CODE rc;
	//PCI_State *pPCIState ;
	*/

	NDTS_Boolean isOK ;
	RQ_State *pRQState ;

	if (cardIndex >= globalState.pciState.numberOfRapiDAQBoards) return NDTS_BFalse;

	// Old
	//pPCIState = &pPCIStates[cardIndex] ;
	pRQState = &pBoardStates[cardIndex] ;


//RQ_StructuresInitialise(cardIndex);
	// In the single-cardIndex scheme of things pRQState was static so we could initialise early on.
	// Now we malloc the state array and create structures for each cardIndex as the cardIndex is initialised, so this is the
	// earliest we can call this function.

	// Can't lock the cardIndex until RQ_StructuresInitialise() has been called so leave it unlocked for now

	//pPCIState = &pPCIStates[cardIndex];

	isOK = NDTS_HAL_InitialiseCard(NDTS_SIDRapiDAQ, cardIndex) ;
	if (!isOK)
	{
		return NDTS_BFalse ;
	}


	/*
		This now supports a case where multiple pci cards are present
	*/
	/*
	// Old
	dev = 0;
	card = 0;
	while (card < globalState.pciState.numberOfRapiDAQBoards)
	{
		pPCIState->device.BusNumber = (U8) -1 ; 
		pPCIState->device.SlotNumber = (U8) -1 ; 
		pPCIState->device.VendorId = (U16) 0x10b5 ; // pci Vendor ID ; 
		pPCIState->device.DeviceId = (U16) -1 ; 
		pPCIState->device.SerialNumber[0] = 0 ; 

		pciReturnCode = PlxPciDeviceFind(&pPCIState->device, &dev) ;
		if (pciReturnCode != ApiSuccess)
		{
			RQ_HandleRQError(RQ_Initialise_FindPCIDeviceFail) ;
			return NDTS_BFalse ;
		}

		pciReturnCode = PlxPciDeviceOpen(&pPCIState->device, &pPCIState->hDevice) ;
		if (pciReturnCode != ApiSuccess)
		{
			RQ_HandleRQError(RQ_Initialise_PCIDeviceOpenFail) ;
			return NDTS_BFalse ;
		}

		PlxSerialEepromPresent(pPCIState->hDevice, &rc);
		if (rc == ApiSuccess)
		{
			U32 secondaryId;
			const U16 secondary_id_offset_in_eeprom = 0x44;
			const U32 rapidaq_secondard_id = 0x52443851;

			rc = PlxSerialEepromReadByOffset(pPCIState->hDevice, secondary_id_offset_in_eeprom, &secondaryId) ;
			if(rc != ApiSuccess)
			{
				// Couldn't read secondary device ID
			}
			else
			{
				if (secondaryId == rapidaq_secondard_id)
				{
					if (card == cardIndex)
						break ;
					else
						card++ ;
				}
				dev++ ;
				pciReturnCode = PlxPciDeviceClose(pPCIState->hDevice) ;
				if (pciReturnCode != ApiSuccess)
				{
		
					RQ_HandleRQError(RQ_Uninitialise_PCIDeviceCloseFail) ;
				}
			}
		}
	}

	pciReturnCode = PlxPciBarMap(pPCIState->hDevice, 2, &pPCIState->pPCIBar2VirtualAddress) ;
	if (pciReturnCode != ApiSuccess)
	{
		RQ_HandleRQError(RQ_Initialise_PCIBarMapFail) ;
		return NDTS_BFalse ;
	}

    pciReturnCode = PlxPciCommonBufferProperties(pPCIState->hDevice, &pPCIState->pciBuffer) ;
	if (pciReturnCode != ApiSuccess)
	{
		RQ_HandleRQError(RQ_Initialise_PCICommonBufferPropertiesFail) ;
		return NDTS_BFalse ;
	}

	pciReturnCode = PlxPciCommonBufferMap(pPCIState->hDevice, &pPCIState->pCommonBuffer) ;
	if (pciReturnCode != ApiSuccess)
	{
		RQ_HandleRQError(RQ_Initialise_PCICommonBufferMapFail) ;
		return NDTS_BFalse ;
	}
	*/

	pRQState->isPCIInitialised = NDTS_BTrue ;	
	
	// Moved RQ_OpenDMAChannel to before the loading of the FPGA's
	// To allow RQ_DumpBinFiles() to assume DMA has been opened
	// and work whether or not the SDK has been initialised.
	isOK = RQ_OpenDMAChannel(cardIndex) ;
	if (!isOK)
		return NDTS_BFalse ;

	return NDTS_BTrue;
}

void CALL_CONV RQ_ExtractStringFromWhitespaceOrBrackets(char *inputString, char *outputString)
{
	int i, j ;

	i = 0 ;

	while (inputString[i] == 0x09 || inputString[i] == 0x20 || 
		   inputString[i] == 0x0d || inputString[i] == 0x0a || 
		   inputString[i] == '[' || inputString[i] == '(')
		i++ ;

	j = 0 ;

	do
	{
		outputString[j] = inputString[i] ;
		i++ ;
		j++ ;
	}
	while (inputString[i] != 0x09 && inputString[i] != 0x20 && 
		   inputString[i] != 0x0d && inputString[i] != 0x0a &&
		   inputString[i] != ']' && inputString[i] != ')' &&
		   inputString[i] != 0x00) ;

	outputString[j] = 0x00 ;
}

int CALL_CONV RQ_LoadFPGAFromFile(unsigned long cardIndex, HANDLE fileHandle)
{
	int i ;
	const int stringLength = 512 ;
	char string[512], propertyString[512], valueString[512] ;
	char *pStopChar ;
	unsigned long value ;
	DWORD numBytesRead ;
	BOOL isWin32OK ;
	NDTS_Boolean isOK ;
	unsigned long peripheralVersion ;

	if (cardIndex >= globalState.pciState.numberOfRapiDAQBoards) return 0;

	isOK = RQ_GetPeripheralFirmwareVersion(cardIndex, &peripheralVersion) ;
	if (!isOK)
		return 0 ;

	do
	{
		i = 0 ;

		do
		{
			isWin32OK = ReadFile(fileHandle, &string[i], 1, &numBytesRead, 0) ;
			if (!isWin32OK || numBytesRead != 1)
				return 2 ;

			i++ ;
		}
		while (string[i - 1] != '=' && string[i - 1] != '[' && i < stringLength) ;

		if (i == stringLength)
		{
			RQ_HandleRQError(RQ_LoadFPGAFromFile_OversizedStringLength) ;
			return 0 ;
		}

		if (string[i - 1] != '[')
		{
			string[i - 1] = 0 ;

			RQ_ExtractStringFromWhitespaceOrBrackets(string, propertyString) ;

			i = 0 ;

			do
			{
				isWin32OK = ReadFile(fileHandle, &string[i], 1, &numBytesRead, 0) ;
				if (!isWin32OK || numBytesRead != 1)
				{
					RQ_HandleRQError(RQ_LoadFPGAFromFile_ReadError) ;
					return 0 ;
				}

				i++ ;
			}
			while (string[i - 1] != 0x0a) ;

			string[i - 1] = 0 ;

			RQ_ExtractStringFromWhitespaceOrBrackets(string, valueString) ;

			value = strtoul(valueString, &pStopChar, 0) ;

			if (!(peripheralVersion == 0 && (_stricmp(valueString, RQ_SensorArrayEnableBScanModeString) == 0 ||
				                             _stricmp(valueString, RQ_SensorArrayDisableAperturePositionWrites) == 0 ||  
					  					     _stricmp(valueString, RQ_SensorArrayDisableApertureDelayWrites) == 0)))
			{
				isOK = RQ_SetValue(cardIndex, propertyString, value) ;
				if (!isOK)
					return 0 ;
			}
		}
	}
	while (string[i - 1] != '[') ;

	return 1 ;
}

int CALL_CONV RQ_LoadGatesFromFile(unsigned long cardIndex, HANDLE fileHandle)
{
	int i ;
	const int stringLength = 512 ;
	char string[512], propertyString[512], valueString[512],
		 gateIdentifierString[512] ;
	char *pStopChar, *pGatePrefixString ;
	unsigned long value ;
	RQ_Gate gate ;
	DWORD numBytesRead ;
	BOOL isWin32OK ;
	NDTS_Boolean isOK, isComplete ;

	if (cardIndex >= globalState.pciState.numberOfRapiDAQBoards) return 0;

	isComplete = NDTS_BFalse ;

	do
	{
		i = 0 ;

		do
		{
			isWin32OK = ReadFile(fileHandle, &string[i], 1, &numBytesRead, 0) ;
			if (!isWin32OK || numBytesRead != 1)
				return 2 ;

			i++ ;
		}
		while (string[i - 1] != ']' && i < stringLength) ;

		if (i == stringLength)
		{
			RQ_HandleRQError(RQ_LoadGatesFromFile_OversizedStringLength) ;
			return 0 ;
		}

		string[i - 1] = 0 ;

		RQ_ExtractStringFromWhitespaceOrBrackets(string, gateIdentifierString) ;

		pGatePrefixString = strstr(gateIdentifierString, RQ_GateIdentifierPrefixString) ;

		if (pGatePrefixString != 0)
		{
			i = strlen(gateIdentifierString) ;

			do
			{
				i-- ;
			}
			while (gateIdentifierString[i] >= '0' && gateIdentifierString[i] <= '9') ;

			gate.index = (RQ_GateIndex) atoi(&gateIdentifierString[i + 1]) ;

			do
			{
				i = 0 ;

				do
				{
					isWin32OK = ReadFile(fileHandle, &string[i], 1, &numBytesRead, 0) ;
					if (!isWin32OK || numBytesRead != 1)
					{
						RQ_HandleRQError(RQ_LoadGatesFromFile_ReadError) ;
						return 0 ;
					}

					i++ ;
				}
				while (string[i - 1] != '=' && string[i - 1] != '[' && i < stringLength) ;

				if (i == stringLength)
				{
					RQ_HandleRQError(RQ_LoadGatesFromFile_OversizedStringLength) ;
					return 0 ;
				}

				if (string[i - 1] != '[')
				{
					string[i - 1] = 0 ;

					RQ_ExtractStringFromWhitespaceOrBrackets(string, propertyString) ;

					i = 0 ;

					do
					{
						isWin32OK = ReadFile(fileHandle, &string[i], 1, &numBytesRead, 0) ;
						if (!isWin32OK || numBytesRead != 1)
						{
							RQ_HandleRQError(RQ_LoadGatesFromFile_ReadError) ;
							return 0 ;
						}

						i++ ;
					}
					while (string[i - 1] != 0x0a) ;

					string[i - 1] = 0 ;

					RQ_ExtractStringFromWhitespaceOrBrackets(string, valueString) ;

					value = strtoul(valueString, &pStopChar, 0) ;

					if (_stricmp(propertyString, RQ_GateStartString) == 0)
						gate.startCount = value ;
					else if (_stricmp(propertyString, RQ_GateEndString) == 0)
						gate.stopCount = value ;
					else if (_stricmp(propertyString, RQ_GateThresholdString) == 0)
						gate.threshold = value ;
					else if (_stricmp(propertyString, RQ_GateTriggerString) == 0)
						gate.triggerGate = (RQ_GateTrigger) value ;
					else if (_stricmp(propertyString, RQ_GateThresholdPolarityString) == 0)
						gate.thresholdPolarity = (RQ_GateThresholdPolarity) value ;
					else if (_stricmp(propertyString, RQ_GateTriggerModeString) == 0)
						gate.triggerMode = (RQ_GateTriggerMode) value ;
					else if (_stricmp(propertyString, RQ_GateDistanceMetricString) == 0)
						gate.distanceMetric = (RQ_GateDistanceMetric) value ;
					else if (_stricmp(propertyString, RQ_GateEnableString) == 0)
						gate.isEnabled = (NDTS_Boolean) value ;
					else if (_stricmp(propertyString, RQ_GateAmplitudePeakSelectString) == 0)
						gate.amplitudePeakSelect = (RQ_GateAmplitudePeakSelect) value ;
					else if (_stricmp(propertyString, RQ_GateDistancePeakSelectString) == 0)
						gate.distancePeakSelect = (RQ_GateDistancePeakSelect) value ;
				}
			}
			while (string[i - 1] != '[') ;

			isOK = RQ_AddGate(cardIndex, &gate) ;
			if (!isOK)
			{
				return NDTS_BFalse ;
			}
		}
		else
		{
			isComplete = NDTS_BTrue ;

			SetFilePointer(fileHandle, -(LONG) (strlen(string) + 1), 0, FILE_CURRENT) ;
		}
	}
	while (!isComplete) ;

	return 1 ;
}

int CALL_CONV RQ_LoadCurvedGateDataFromFile(unsigned long cardIndex, HANDLE fileHandle)
{
	int i ;
	const int stringLength = 512 ;
	char string[512], propertyString[512], valueString[512] ;
	char *pStopChar, *pCurvedGatePrefixString ;
	unsigned short curvedGateValues[RQ_CURVED_GATE_NUMBER_OF_VALUES] ;
	unsigned long value ;
	int valueIndex ;
	DWORD numBytesRead ;
	BOOL isWin32OK ;
	NDTS_Boolean isOK ;

	if (cardIndex >= globalState.pciState.numberOfRapiDAQBoards) return NDTS_BFalse;

	do
	{
		i = 0 ;

		do
		{
			isWin32OK = ReadFile(fileHandle, &string[i], 1, &numBytesRead, 0) ;
			if (!isWin32OK || numBytesRead != 1)
				goto EndOfFile ;

			i++ ;
		}
		while (string[i - 1] != '=' && string[i - 1] != '[' && i < stringLength) ;

		if (i == stringLength)
		{
			RQ_HandleRQError(RQ_LoadCurvedGateDataFromFile_OversizedStringLength) ;
			return 0 ;
		}

		if (string[i - 1] != '[')
		{
			string[i - 1] = 0 ;

			RQ_ExtractStringFromWhitespaceOrBrackets(string, propertyString) ;

			pCurvedGatePrefixString = strstr(propertyString, RQ_CurvedGateElementPrefixString) ;

			if (pCurvedGatePrefixString != 0)
			{
				i = strlen(propertyString) ;

				do
					i-- ;
				while (propertyString[i] >= '0' && propertyString[i] <= '9') ;

				valueIndex = atoi(&propertyString[i + 1]) ;

				if (valueIndex < 0 || valueIndex >= RQ_CURVED_GATE_NUMBER_OF_VALUES)
				{
					RQ_HandleRQError(RQ_LoadCurvedGateDataFromFile_InvalidSequencePosition) ;
					return 0 ;
				}

				i = 0 ;

				do
				{
					isWin32OK = ReadFile(fileHandle, &string[i], 1, &numBytesRead, 0) ;
					if (!isWin32OK || numBytesRead != 1)
					{
						RQ_HandleRQError(RQ_LoadCurvedGateDataFromFile_ReadError) ;
						return 0 ;
					}

					i++ ;
				}
				while (string[i - 1] != 0x0a) ;

				string[i - 1] = 0 ;

				RQ_ExtractStringFromWhitespaceOrBrackets(string, valueString) ;

				value = strtoul(valueString, &pStopChar, 0) ;

				if (value > RQ_CURVED_GATE_MAXIMUM_ELEMENT)
				{
					RQ_HandleRQError(RQ_LoadCurvedGateDataFromFile_InvalidValue) ;
					return 0 ;
				}

				curvedGateValues[valueIndex] = (unsigned short) value ;
			}
			else
			{
				RQ_HandleRQError(RQ_LoadCurvedGateDataFromFile_InvalidIndexPrefix) ;
				return 0 ;
			}
		}
	}
	while (string[i - 1] != '[') ;

	isOK = RQ_SetCurvedGateData(cardIndex, curvedGateValues) ? 1 : 0 ;

	return isOK ;

EndOfFile :
	isOK = RQ_SetCurvedGateData(cardIndex, curvedGateValues) ? 2 : 0 ;

	return isOK ;
}

/*
// Old
RQ_API NDTS_Boolean CALL_CONV  RQ_PciSetSecondaryID(unsigned long value)
{
	RETURN_CODE pciReturnCode ;
	unsigned long devNum;
	DEVICE_LOCATION device;
	HANDLE hDevice;
	U32 d;
    RETURN_CODE rc;

	devNum = FIND_AMOUNT_MATCHED;

	device.BusNumber = (U8) -1 ; 
	device.SlotNumber = (U8) -1 ; 
	device.VendorId = (U16) 0x10b5 ; // pci Vendor ID ; 
	device.DeviceId = (U16) -1 ; 
	device.SerialNumber[0] = 0 ; 
	
	pciReturnCode = PlxPciDeviceFind(&device, &devNum) ;
	if (pciReturnCode != ApiSuccess)
	{
		RQ_HandleRQError(RQ_Initialise_FindPCIDeviceFail) ;
		return NDTS_BFalse; // fail
	}

	// Now attempt to open each device in turn and check the secondary ID
	for (d = 0; d < devNum; d++)
	{
		device.BusNumber = (U8) -1 ; 
		device.SlotNumber = (U8) -1 ; 
		device.VendorId = (U16) 0x10b5 ; // pci Vendor ID ; 
		device.DeviceId = (U16) -1 ; 
		device.SerialNumber[0] = 0 ; 
		pciReturnCode = PlxPciDeviceFind(&device, &d) ;
		if (pciReturnCode != ApiSuccess)
		{
			continue;
		}

		// Open the device and see what it is
		pciReturnCode = PlxPciDeviceOpen(&device, &hDevice) ;
		if (pciReturnCode != ApiSuccess)
		{
			continue;
		}

		PlxSerialEepromPresent(hDevice, &rc);
		if (rc == ApiSuccess)
		{
			U32 secondaryId;
			const U16 secondary_id_offset_in_eeprom = 0x44;
			const U32 rapidaq_secondary_id = NDTS_SIDRapiDAQ; // RDAQ
			const U32 pulser_secondary_id = RQ_SIDPRX; // PLRX

			rc = PlxSerialEepromReadByOffset(hDevice, secondary_id_offset_in_eeprom, &secondaryId);
			if(rc != ApiSuccess)
			{
				continue; // problem with EEPROM
			}
			else
			{
				if (secondaryId == rapidaq_secondary_id)
				{
					// This is a RapiDAQ cardIndex
				}

				else if (secondaryId == pulser_secondary_id)
				{
					// This is a Pulser cardIndex
				}
				else
				{
					// Write the specified secondary ID to the first unrecognised cardIndex we see
					rc = PlxSerialEepromWriteByOffset(hDevice, secondary_id_offset_in_eeprom, value);
					pciReturnCode = PlxPciDeviceClose(hDevice) ;
					return NDTS_BTrue; // success
				}
			}
		}

		PlxPciDeviceClose(hDevice) ;
	}

	return NDTS_BFalse; // couldn't find a device to write to
}
*/

// In RS1 we stored the calibration values in EEPROM:
// word 1 contains the magic number 0x55aa1234
// words 2 to 7 contain the zero offsets for input ranges 0 to 5
// words 8 to 13 contain the gain trim values for input ranges 0 to 5
// words 14 and 15 are unused in RS1
// In RS2 we store the magic number 0x123455aa at offset 14
// word 15 contains the serial number for the cardIndex
// word 16 contains a bit mask which can be used with dodgy boards to prevent certain channels
// from being calibrated

RQ_API NDTS_Boolean CALL_CONV RQ_GetEEPROM(unsigned long cardIndex, unsigned long offset, unsigned long *pValue)
{
	// Old
	//PCI_State *pPCIState ;
	
	if (cardIndex >= globalState.pciState.numberOfRapiDAQBoards) return NDTS_BFalse;

	// Old
	//pPCIState = &pPCIStates[cardIndex];

	// We can only lock/unlock the cardIndex if it's initialised.
	// If the cardIndex isn't initialised then we can just read/write it

	return NDTS_HAL_GetWord(NDTS_SIDRapiDAQ, cardIndex, RQ_EEPROM_START_OFFSET + offset * 4, pValue) ;

	// Old
    //*value = *(volatile unsigned long *) (pPCIState->pPCIBar2VirtualAddress + RQ_EEPROM_START_OFFSET + offset * 4);
	//return NDTS_BTrue;
}

RQ_API NDTS_Boolean CALL_CONV RQ_SetEEPROM(unsigned long cardIndex, unsigned long offset, unsigned long value)
{
	// Old
	//PCI_State *pPCIState ;

	if (cardIndex >= globalState.pciState.numberOfRapiDAQBoards) return NDTS_BFalse;

	// Old
	//pPCIState = &pPCIStates[cardIndex];

	return NDTS_HAL_SetWord(NDTS_SIDRapiDAQ, cardIndex, RQ_EEPROM_START_OFFSET + offset * 4, value) ;

	// Old
    //*(volatile unsigned long *) (pPCIState->pPCIBar2VirtualAddress + RQ_EEPROM_START_OFFSET + offset * 4) = value;
	//return NDTS_BTrue;
}

RQ_API NDTS_Boolean CALL_CONV RQ_GetEEPROMvalues(unsigned long cardIndex)
{
	// Reads EEPROM values, asserts default values if nothing is found
	// This is typically called from RQ_Initialise before calibration is carried out

	unsigned long EEval;
	unsigned long range;

	RQ_State *pRQState = &pBoardStates[cardIndex];

	if (cardIndex >= globalState.pciState.numberOfRapiDAQBoards) return NDTS_BFalse;

	// Initialise these in case we can't get values from EE
	for (range = 0; range <= RQ_AG5V; range++)
	{
		pRQState->calibrationOffsets[range] = 0x800;
		pRQState->calibrationGains[range] = 0x800;
	}

	pRQState->cardSerialNumber = 0;
	pRQState->calibrationFlags = 0; // calibrate everything by default

	// NB if the first EE read succeeds then they all will
	if (RQ_GetEEPROM(cardIndex, RQ_EE_MAGIC_OFFSET, &EEval) != NDTS_BTrue)
	{
		return NDTS_BFalse;
	}

	if (EEval == RQ_EE_MAGIC_NUMBER_OLD)
	{
		// Card was set up using an old InitCard - write the new magic number and clear the calibration flags
		RQ_SetEEPROM(cardIndex, RQ_EE_MAGIC_OFFSET, RQ_EE_MAGIC_NUMBER_NEW);
		RQ_SetEEPROM(cardIndex, RQ_EE_CFLAG_OFFSET, 0);
		RQ_CommitEEPROM(cardIndex);
	}
	else if (EEval != RQ_EE_MAGIC_NUMBER_NEW)
	{
		return NDTS_BTrue; // nothing in EEPROM
	}

	RQ_GetEEPROM(cardIndex, RQ_EE_SERIAL_OFFSET, &EEval);
	pRQState->cardSerialNumber = EEval;

	RQ_GetEEPROM(cardIndex, RQ_EE_CFLAG_OFFSET, &EEval);
	pRQState->calibrationFlags = EEval;

	for (range = 0; range < RQ_AG5V; range++)
	{
		if (pRQState->calibrationFlags & (1 << range))
		{
			RQ_GetEEPROM(cardIndex, RQ_EE_ZERO_OFFSET + range, &EEval);
			pRQState->calibrationOffsets[range] = (short)EEval;
			RQ_GetEEPROM(cardIndex, RQ_EE_GAIN_OFFSET + range, &EEval);
			pRQState->calibrationGains[range] = (short)EEval;
		}
		else
		{
			pRQState->calibrationOffsets[range] = 0x800;
			pRQState->calibrationGains[range] = 0x800;
		}
	}

	return NDTS_BTrue;
}

// Returns NDTS_BTrue if the proposed value was written (i.e. a commit is needed)
// NDTS_BFalse otherwise (failed or value was close enough already)
// If the absolute difference between the current EE value and the proposed value
// is greater than the drift value, the new value will be written.
NDTS_Boolean RQ_WriteEEPROMifDrifted(unsigned long cardIndex, unsigned long offset, unsigned long value, int drift)
{
	unsigned long oldVal;

	if (cardIndex >= globalState.pciState.numberOfRapiDAQBoards) return NDTS_BFalse;
	if (RQ_GetEEPROM(cardIndex, offset, &oldVal) != NDTS_BTrue) return NDTS_BFalse;
	if (abs(value - oldVal) <= drift) return NDTS_BFalse; // value in EE is close enough
	return RQ_SetEEPROM(cardIndex, offset, value);
}

// After writing EE values we need to commit them
NDTS_Boolean RQ_CommitEEPROM(unsigned long cardIndex)
{
	unsigned long value;
	int count = 10000;
	RQ_SetSystemRegister(cardIndex, RQ_SYSTEM_COMMAND_REGISTER, 0x00000012); // initiate EE write

	do
	{
		if (RQ_GetSystemRegister(cardIndex, RQ_SYSTEM_COMMAND_REGISTER, &value) != NDTS_BTrue)
			count = 0; // abort
	} while ((value & 0x00000002) && (count-- > 0));

	return (count > 0) ? NDTS_BTrue : NDTS_BFalse;
}

NDTS_Boolean CALL_CONV RQ_SetPingPongElementRegister(unsigned long cardIndex, unsigned long sequencePosition, unsigned long unFlippedElement)
{
	unsigned long value ;
	unsigned short flippedElement ;
	NDTS_Boolean isOK ;
	RQ_State *pRQState = &pBoardStates[cardIndex];

	isOK = RQ_GetPeripheralRegister(cardIndex, RQ_PINGPONG_SEQUENCE_DATA, &value) ;
	if (!isOK)
		return NDTS_BFalse ;

	if (pRQState->numberOfElementsForFlipping)
		flippedElement = pRQState->numberOfElementsForFlipping - (unsigned short)unFlippedElement ;
	else
		flippedElement = (unsigned short) unFlippedElement ;

	value &= 0xffff8000 ;
	value |= flippedElement + (sequencePosition << 8) ;

	//filelogt("Ping pong position %d set to element %d\n", sequencePosition, flippedElement);

	isOK = RQ_SetPeripheralRegister(cardIndex, RQ_PINGPONG_SEQUENCE_DATA, value) ;

	return isOK ;
}

void CALL_CONV RQ_GenerateCRCTable(unsigned long *pCRCTable)
{ 
	unsigned long i, j, crcAccum ;
	const int polynomial = 0x04c11db7 ;

	for (j = 0 ; j < 256 ; j++)
	{ 
		crcAccum = j << 24 ;

		for (i = 0 ; i < 8 ; i++)
			crcAccum = (crcAccum & 0x80000000L) != 0 ? (crcAccum << 1) ^ polynomial : (crcAccum << 1) ;

		pCRCTable[j] = crcAccum ; 
	}
}

unsigned long CALL_CONV RQ_CalculateCRC32(unsigned long seed, const unsigned long *pCRCTable, const char *pData, unsigned long numberOfBytes)
{ 
	unsigned long i, j, crcAccum ;
	const char *p = pData ;

	crcAccum = seed ;

	for (j = 0 ; j < numberOfBytes ; j++)
	{ 
		i = ((unsigned long) (crcAccum >> 24) ^ *p++) & 0xff ;
		crcAccum = (crcAccum << 8) ^ pCRCTable[i] ; 
	}
	
	return crcAccum ; 
}

NDTS_Boolean CALL_CONV RQ_CheckAScanHeader(unsigned long aScanHeader, unsigned long expectedAScanIndex, NDTS_Boolean *pIsIdentifierPresent)
{
	NDTS_Boolean isOK ;

	isOK = aScanHeader == (0x41534e00 | expectedAScanIndex) ;

	if (pIsIdentifierPresent != 0)
		*pIsIdentifierPresent = (aScanHeader & 0xffffff00) == 0x41534e00 ;

	return isOK ;
}

NDTS_Boolean CALL_CONV RQ_CheckCScanHeader(unsigned long cScanHeader, unsigned long expectedCScanIndex, NDTS_Boolean *pIsIdentifierPresent)
{
	NDTS_Boolean isOK ;

	isOK = cScanHeader == (0x43534e00 | expectedCScanIndex) ;

	if (pIsIdentifierPresent != 0)
		*pIsIdentifierPresent = (cScanHeader & 0xffffff00) == 0x43534e00 ;

	return isOK ;
}



RQ_API NDTS_Boolean CALL_CONV RQ_SetGateAttributesRegister(unsigned long cardIndex, const RQ_Gate *pGate)
{
	unsigned long distMetricBits22to21 ;
	unsigned long zeroCrossingBit16 ;
	unsigned long value ;
	
	switch(pGate->distanceMetric)
	{
	case RQ_GDMLeadingFlank:
		distMetricBits22to21 = 0;
		zeroCrossingBit16 = 0;
		break;
	case RQ_GDMTrailingFlank:
		distMetricBits22to21 = 1;
		zeroCrossingBit16 = 0;
		break;
	case RQ_GDMPeak:
		distMetricBits22to21 = 2;
		zeroCrossingBit16 = 0;
		break;
	case RQ_GDMLeadingZeroCrossing:
		distMetricBits22to21 = 0;
		zeroCrossingBit16 = 1;
		break;
	case RQ_GDMTrailingZeroCrossing:
		distMetricBits22to21 = 1;
		zeroCrossingBit16 = 1;
		break;
	default:
		RQ_HandleRQError(RQ_SetGateAttributesRegister_BadDistanceMetric) ;
		return NDTS_BFalse ;
	}
	_ASSERTE(pGate->threshold >= 0 && pGate->threshold < 1<<11);
	_ASSERTE(pGate->triggerGate >= 0 && pGate->triggerGate < RQ_GTLastPlusOne);
	_ASSERTE(zeroCrossingBit16 >= 0 && zeroCrossingBit16 <= 1);
	_ASSERTE(pGate->thresholdPolarity >= 0 && pGate->thresholdPolarity  < RQ_GTPLastPlusOne); 
	_ASSERTE(pGate->triggerMode >= 0 && pGate->triggerMode  < RQ_GTMLastPlusOne); 
	_ASSERTE(distMetricBits22to21 << 21 >= 0 && distMetricBits22to21 < 1<<2); 
	_ASSERTE(pGate->isEnabled >= 0 && pGate->isEnabled  <= 1); 
	_ASSERTE(pGate->amplitudePeakSelect >= 0 && pGate->amplitudePeakSelect  < RQ_GAPSLastPlusOne); 
	_ASSERTE(pGate->distancePeakSelect >= 0 && pGate->distancePeakSelect  < RQ_GDPSLastPlusOne); 
	if (pGate->isEnabled)
	{
		value = (unsigned long) pGate->threshold | 
			(unsigned long) pGate->triggerGate << 12 |
			(unsigned long) zeroCrossingBit16 << 16 | 
			(unsigned long) pGate->storeGateValue << 17 | 
			(unsigned long) pGate->thresholdPolarity << 18 | 
			(unsigned long) pGate->triggerMode << 20 |
			(unsigned long) distMetricBits22to21 << 21 | 
			(unsigned long) pGate->isEnabled << 23 |
			(unsigned long) pGate->amplitudePeakSelect << 24 | 
			(unsigned long) pGate->distancePeakSelect << 28 ;
	}
	else
	{
		// Firmware DSP 0x227, PER 0x21F (and probably other revisions) has a problem where if a gate past the max number of gates (e.g. 6) is disabled
		// but has RQ_GDMPeak, it gets into a state where it writes data for the gate to the scan gate data store memory 
		value = 0;
	}


	return (RQ_SetPeripheralRegister(cardIndex, RQ_GATE_ATTRIBUTES, value));
}
RQ_API NDTS_Boolean CALL_CONV RQ_RawWriteGate(unsigned long cardIndex, const RQ_Gate *pGate)
{
	LARGE_INTEGER freqLarge, countLargeA, countLargeB ;
	float numSeconds ;
	NDTS_Boolean hasTimedOut, isOK  ;
	BOOL isWIN32OK ;
	unsigned long value = 0 ;
	RQ_State *pBoardState = RQ_ReturnBoardStatePointer(cardIndex) ;
	if (!pBoardState)
		return NDTS_BFalse;

	isOK = RQ_SetGateAttributesRegister(cardIndex, pGate) ;
	if (!isOK)
	{
		return NDTS_BFalse ;
	}

	isOK = RQ_SetPeripheralRegister(cardIndex, 
									RQ_GATE_START_STOP, 
									((pGate->stopCount -   RQ_ReturnGateSubtractValue(pGate)) & 0x0000ffff) << 16 | 
									((pGate->startCount -  RQ_ReturnGateSubtractValue(pGate)) & 0x0000ffff)) ;
	if (!isOK)
	{
		return NDTS_BFalse ;
	}

	isOK = RQ_GetPeripheralRegister(cardIndex, RQ_GATE_SETUP, &value) ;
	if (!isOK)
	{
		return NDTS_BFalse ;
	}

	value &= 0xfffffff0 ;
	value |= pGate->index ;

	isOK = RQ_SetPeripheralRegister(cardIndex, RQ_GATE_SETUP, value) ;
	if (!isOK)
	{
		return NDTS_BFalse ;
	}

	
	isWIN32OK = QueryPerformanceFrequency(&freqLarge) ;
	if (!isWIN32OK)
	{
		RQ_HandleRQError(RQ_AddGate_QueryTimerFail) ;
		return NDTS_BFalse ;
	}

	QueryPerformanceCounter(&countLargeA) ;

	do
	{
		QueryPerformanceCounter(&countLargeB) ;

		isOK = RQ_GetPeripheralRegister(cardIndex, RQ_GATE_SETUP, &value) ;
		if (!isOK)
		{
			return NDTS_BFalse ;
		}

		numSeconds = (float) (countLargeB.QuadPart - countLargeA.QuadPart) / (float) freqLarge.QuadPart ;
		hasTimedOut = numSeconds > RQ_GATE_SETUP_MAX_WAIT ? NDTS_BTrue : NDTS_BFalse ; 
	}
	while ((value & 0x80000000) != 0 && !hasTimedOut) ;

	if (hasTimedOut)
	{
		RQ_HandleRQError(RQ_AddGate_SetupTimeOut) ;
		return NDTS_BFalse ;
	}
	return NDTS_BTrue;
}

// Select each sequence step in turn, write the gate data for each one, then commit the whole lot
// in one go
NDTS_Boolean RQ_RawWriteGates(unsigned long cardIndex, unsigned long gateIndex)
{
	LARGE_INTEGER freqLarge, countLargeA, countLargeB ;
	float numSeconds ;
	NDTS_Boolean hasTimedOut ;
	BOOL isWIN32OK ;
	unsigned long step;
	RQ_Gate *pGate;
	unsigned long value;
	NDTS_Boolean isOK;
	unsigned cookedStart, cookedStop;

	RQ_State *pBoardState = RQ_ReturnBoardStatePointer(cardIndex) ;
	if (!pBoardState) return NDTS_BFalse; 

	for (step = 0; step < RQ_MAXIMUM_NUMBER_OF_STEPS; step++)
	{
		unsigned short gateSubtractValue ;
		pGate = &pBoardState->gates[step][gateIndex];
		cookedStart = pGate->startCount / pBoardState->oversampleCountThisStep[step];
		cookedStop  = pGate->stopCount / pBoardState->oversampleCountThisStep[step];

		if (pGate->index == 0)
		{
			gateSubtractValue = (cookedStart < 3) ? (unsigned short)cookedStart : 3;
		}
		else
		{
			gateSubtractValue = (cookedStart < 4) ? (unsigned short)cookedStart : 4;
		}

		// select step number
		isOK = RQ_SetSamplingRegister(cardIndex, RQ_SAMPLING_SEQUENCE_STEP_NUMBER, step) ;
		if (!isOK)
		{
			return NDTS_BFalse ;
		}
		isOK = RQ_SetPeripheralRegister(cardIndex, RQ_PERIPHERAL_SEQUENCE_STEP_NUMBER, step);
		if (!isOK)
		{
			return NDTS_BFalse ;
		}
		// write gate attributes
		isOK = RQ_SetGateAttributesRegister(cardIndex, pGate) ;	
		if (!isOK)
		{
			return NDTS_BFalse ;
		}		// we probably need to write the start-stop register too
		isOK = RQ_SetPeripheralRegister(cardIndex, RQ_GATE_START_STOP, ((cookedStop - gateSubtractValue) & 0x0000ffff) << 16 | 
			((cookedStart - gateSubtractValue) & 0x0000ffff)) ;
		if (!isOK)
		{
			return NDTS_BFalse ;
		}	                                                   

		/*
		filelogt("Step %d Writing gate attributes %x start/stop %x", step,  value,
		((pGate->stopCount - gateSubtractValue) & 0x0000ffff) << 16 | 
		((pGate->startCount - gateSubtractValue) & 0x0000ffff)) ;
		*/												   
	}

	// best set the step counter back how we found it
	isOK = RQ_SetSamplingRegister(cardIndex, RQ_SAMPLING_SEQUENCE_STEP_NUMBER, pBoardState->currentSequenceStep) ;
	if (!isOK)
	{
		return NDTS_BFalse ;
	}
	isOK = RQ_SetPeripheralRegister(cardIndex, RQ_PERIPHERAL_SEQUENCE_STEP_NUMBER, pBoardState->currentSequenceStep) ;
	if (!isOK)
	{
		return NDTS_BFalse ;
	}
	// Initiate the write operation
	isOK = RQ_GetPeripheralRegister(cardIndex, RQ_GATE_SETUP, &value) ;
	if (!isOK)
	{
		return NDTS_BFalse ;
	}

	value &= 0xfffffff0 ;
	value |= gateIndex ;

	isOK = RQ_SetPeripheralRegister(cardIndex, RQ_GATE_SETUP, value) ;
//	filelog("Writing gate setup %x ", value) ;

	if (!isOK)
	{
		return NDTS_BFalse ;
	}

	isWIN32OK = QueryPerformanceFrequency(&freqLarge) ;
	if (!isWIN32OK)
	{
		RQ_HandleRQError(RQ_AddGate_QueryTimerFail) ;
		return NDTS_BFalse ;
	}

	QueryPerformanceCounter(&countLargeA) ;

	do
	{
		QueryPerformanceCounter(&countLargeB) ;

		isOK = RQ_GetPeripheralRegister(cardIndex, RQ_GATE_SETUP, &value) ;
		if (!isOK)
		{
			return NDTS_BFalse ;
		}

		numSeconds = (float) (countLargeB.QuadPart - countLargeA.QuadPart) / (float) freqLarge.QuadPart ;
		hasTimedOut = numSeconds > RQ_GATE_SETUP_MAX_WAIT ? NDTS_BTrue : NDTS_BFalse ; 
	}
	while ((value & 0x80000000) != 0 && !hasTimedOut) ;

	if (hasTimedOut)
	{
		RQ_HandleRQError(RQ_AddGate_SetupTimeOut) ;
		return NDTS_BFalse ;
	}
	return NDTS_BTrue;
}


// The stride calculations for sequenced data is more complex for sequenced scans because the number of
// samples per Ascan may differ for each channel (Ascan in a Bscan).  The strides are calculated
// once in this function and stored in the state.  They are then looked up in the getStride.. functions.
// Non sequenced scans now also use these variables rather than the stride calculation functions.
//
// This needs to be called when certain parameters change: specifically
// storage mode
// tcg trigger 
// encoder triggered
// storage options 
// ascansperbscan 
// samples per ascan 

void RQ_UpdateSampleParameters(unsigned long cardIndex)
{
	unsigned long channel, totalAScanSamples, i; 
	RQ_SampleSize storageMode;
	RQ_ScanStorage storageOptions;
	unsigned long crcBytes = 0;
	RQ_State *pBoardState = RQ_ReturnBoardStatePointer(cardIndex) ;

#ifdef _DEBUG
	// We get away with changing the size of the store whilst free running on the SFD 
	// by checking things like the AScan range and writing the minimum of the 
	// size of the data in the raw scan and the size of store the SFD expects.
	// Generally should only change sample parameters when software triggered and 
	// no triggers outstanding.
	if (!pBoardState->isSequence)
	{
		RQ_Trigger triggerSelect ;
		RQ_GetTriggerSelect(cardIndex, &triggerSelect) ;
		// Should only change the shape of the store when idling.  See comment above about SFD.
		// We can also lose arm triggers by updating sample parameters when not idle.
		// When RQ_HandleSRAM() rewrites the number of bscans this 
		// resets the firmware and drops any raw scans in the buffer.
		// Can't check if encoder triggered becayse SetTriggerSelect calls us when going in and out of encoder mode.

		_ASSERTE(triggerSelect != RQ_TFreeRunning);
	}
#endif
	crcBytes = ((pBoardState->crcState.pCRCTable) ? 4 : 0) ;

	RQ_GetSampleStorageMode(cardIndex, &storageMode);
	pBoardState->numberOfBytesPerSample = (storageMode == RQ_SS12Bit) ? 2 : 1;

	RQ_GetScanStorageOptions(cardIndex, &storageOptions);

	for (channel = 0; channel < RQ_MAXIMUM_NUMBER_OF_ELEMENTS; channel++)
	{
		unsigned long actualChannel = pBoardState->isSequence ? channel : 0 ;
		RQ_GetNumberOfEnabledGatesThisChannel(cardIndex, actualChannel, &(pBoardState->numberOfActiveGatesThisChannel[channel]));
	}

	// Keep this in sync with RQ_CheckSRAM
	totalAScanSamples = 0;
	for (channel = 0; channel < pBoardState->numberOfAScansPerBScan; channel++)
	{
		totalAScanSamples += 4 + pBoardState->aScanSamplesThisStep[channel] * pBoardState->numberOfBytesPerSample + crcBytes;
	}

	pBoardState->aScanBytesPerBScan = totalAScanSamples + RQ_NUMBER_OF_ENCODERS * 4 + crcBytes;

	// Allow for extra oversampling headers
	if (pBoardState->isSequence)
	{
		for (channel = 0; channel < pBoardState->numberOfAScansPerBScan; channel++)
		{
			pBoardState->aScanBytesPerBScan += 4 * (pBoardState->oversampleCountThisStep[channel] - 1);
		}
	}
	else
	{
		pBoardState->aScanBytesPerBScan += 4 * (pBoardState->oversampleCountThisStep[0] - 1) * pBoardState->numberOfAScansPerBScan;
	}

	// update the output offsets for each gate
	pBoardState->gateOffsetThisStep[0] = 4;
	for (channel = 1; channel < pBoardState->numberOfAScansPerBScan; channel++)
	{
		pBoardState->gateOffsetThisStep[channel] = pBoardState->gateOffsetThisStep[channel-1] + /* offset for last channel */
			pBoardState->oversampleCountThisStep[pBoardState->isSequence ? channel-1 : 0] * (4 + /* header */
			                                                   4 * pBoardState->gateCountThisStep[channel-1]); /* size of last channel's data */
	}

	pBoardState->gateBytesPerBScan = pBoardState->gateOffsetThisStep[pBoardState->numberOfAScansPerBScan - 1] - 4 + 
			pBoardState->oversampleCountThisStep[pBoardState->isSequence ? pBoardState->numberOfAScansPerBScan -1 : 0] * (4 + /* header */
                  4 * pBoardState->gateCountThisStep[pBoardState->numberOfAScansPerBScan -1]) +  /* size of last channel's data */
		pBoardState->crcState.isGatesCRCPresent * 4 + RQ_NUMBER_OF_ENCODERS * 4;

	RQ_HandleSRAM(cardIndex) ; 

	for (i=0; i<pBoardState->numberOfUpdateSampleParametersHooks; i++)
		(*pBoardState->ppUpdateSampleParametersHooks[i])(cardIndex);
}

RQ_API NDTS_Boolean CALL_CONV RQ_SetEnhancedTCG(unsigned long cardIndex)
{
	unsigned long i;
	NDTS_Boolean retval;
	unsigned short adjustedValues[TCG_HOLDING_BUFFER_SIZE];
	unsigned long step, oldStep, regvalue, point = 0;


	RQ_State *pBoardState = RQ_ReturnBoardStatePointer(cardIndex) ;
	if (!pBoardState) return NDTS_BFalse; 

	if (!pBoardState->isSequence)
	{
		// The TCG point ratio is set per sequence step and there may be other sequence compatible assumptions.
		_ASSERTE(pBoardState->isSequence);
		RQ_HandleRQError(RQ_SetSeqenceStep_NotSequenceCompatibleFirmware) ;
		return NDTS_BFalse;
	}

	// HLPRX now takes care of the attenuators, so we can just write out the TCG values
	// NB clip these if they go above the max gain that we can provide

	// In sequence mode we have to disable each step
	RQ_GetSequenceStep(cardIndex, &oldStep);
	for (step = 0; step < RQ_MAXIMUM_NUMBER_OF_STEPS; step++)
	{
		RQ_SetSequenceStep(cardIndex, step);
		RQ_GetSamplingRegister(cardIndex, RQ_TCG_CONFIGURE, &regvalue);
		regvalue &= 0xfffffffd; // disable
		RQ_SetSamplingRegister(cardIndex, RQ_TCG_CONFIGURE, regvalue);
	}

	// Always use 32 curves of 8K points in sequenced firmware
	retval = RQ_SetTCGStartInternal(cardIndex, 1) ; 

	if (retval == NDTS_BFalse) return retval;

	// convert from 0.1cB counts to DAC counts
	for (i = 0; i < TCG_HOLDING_BUFFER_SIZE; i++)
	{
		int decidBAdjusted = pBoardState->tcgHoldingBuffer[i];
		short dacValue = (short)(decidBAdjusted * 255 / 800);
		if (dacValue < 0) dacValue = 0;
		if (dacValue > 255) dacValue = 255;
		adjustedValues[i] = (unsigned char)dacValue;
	}

	// The dead time between one ascan and the next should adopt the gain value for the start
	// of the next AScan - this gives plenty of time for the gain to stabilise.
	// maxTCGPoints is larger for sequenced so max step needs to be restricted to prevent mem overwrite.
	// (This function is currently only valid for sequence anyway)
	for (step = 0; step < (unsigned long)((pBoardState->isSequence) ? RQ_MAXIMUM_NUMBER_OF_STEPS: RQ_MAXIMUM_NUMBER_OF_ELEMENTS); step++)
	{
		unsigned short leadInValue = adjustedValues[pBoardState->maxTCGPoints * step];
		unsigned long previousStep = (step == 0 ? pBoardState->numberOfAScansPerBScan - 1 : step - 1);
		for (point = previousStep * pBoardState->maxTCGPoints + pBoardState->aScanSamplesThisStep[previousStep]/4 ;
				point < (previousStep + 1) * pBoardState->maxTCGPoints; 
				point++)
		{
			adjustedValues[point] = leadInValue;
		}
	}

	retval = RQ_SetTCGValuesNormalised(cardIndex, adjustedValues, TCG_HOLDING_BUFFER_SIZE);
	
	if (retval == NDTS_BFalse) return retval;

	retval = RQ_SetTCGNormalisedFinish(cardIndex);

	for (step = 0; step < RQ_MAXIMUM_NUMBER_OF_STEPS; step++)
	{
		RQ_SetSequenceStep(cardIndex, step);
		RQ_GetSamplingRegister(cardIndex, RQ_TCG_CONFIGURE, &regvalue);
		regvalue &= 0x7ffffffd;
		regvalue |= 0x00000002; // enable
		RQ_SetSamplingRegister(cardIndex, RQ_TCG_CONFIGURE, regvalue);
	}

	RQ_SetSequenceStep(cardIndex, oldStep);

	return retval;
}

// The intention is that the TCG curve has a max gain of 0dB and a min of -80
// An overall gain adjustment can be provided which is added to the tcg values.
// Both are in 0.1dB steps
// The DLL maintains a TCG table internally, which is populated by several calls to RQ_SubmitTCGCurve
// and then committed to the RA card by a call to RQ_SetEnhancedTCG.

RQ_API NDTS_Boolean CALL_CONV RQ_ClearTCGCurve(unsigned long cardIndex)
{
	// clear down the tcg image
	// NB this should be done once the channel ranges have been set up
	unsigned long i, step, oldStep;
	RQ_State *pBoardState = RQ_ReturnBoardStatePointer(cardIndex) ;

	if (!pBoardState->isSequence)
	{
		// Assumes a max of 32 steps for 32 curves of 8K points so this function is no use for non sequenced DI unit software
		_ASSERTE(pBoardState->isSequence);
		RQ_HandleRQError(RQ_SetSeqenceStep_NotSequenceCompatibleFirmware) ;
		return NDTS_BFalse;
	}

	if (!RQ_GetSequenceStep(cardIndex, &oldStep))
		return NDTS_BFalse;

	for (i = 0; i < TCG_HOLDING_BUFFER_SIZE; i++)
		pBoardState->tcgHoldingBuffer[i] = 0;
	
	// The subsampling depends on the number of samples in the AScan
	for (step = 0; step < RQ_MAXIMUM_NUMBER_OF_STEPS; step++)
	{
		RQ_SetSequenceStep(cardIndex, step);
		if (!RQ_SetTCGDecimation(cardIndex))
		{
			return NDTS_BFalse;
		}
	}
	if (!RQ_SetSequenceStep(cardIndex, oldStep))
		return NDTS_BFalse;

	return NDTS_BTrue;
}
	

RQ_API NDTS_Boolean CALL_CONV RQ_SubmitTCGCurve(unsigned long cardIndex, unsigned long channelIndex, short *pCurve, unsigned long nPts, short gain)
{
	// We assume that the number of point we get is to span the ascan
	// So the application can provide (range/4) points (optimal) or (range) or anything else
	short *pDest;
	unsigned long i, destValues, tCGPointRatio, oldStep;
	RQ_State *pBoardState = RQ_ReturnBoardStatePointer(cardIndex) ;

	if (!RQ_GetSequenceStep(cardIndex, &oldStep))
		return NDTS_BFalse;
	if (!RQ_SetSequenceStep(cardIndex, channelIndex))
		return NDTS_BFalse;

	if (!RQ_SetTCGDecimation(cardIndex))
		return NDTS_BFalse;

	pDest = &(pBoardState->tcgHoldingBuffer[channelIndex * pBoardState->maxTCGPoints]);

	if (!RQ_GetTCGPointRatio(cardIndex, &tCGPointRatio))
		return NDTS_BFalse;

	destValues = pBoardState->aScanSamplesThisStep[channelIndex] / tCGPointRatio;  

	if (nPts > destValues)
	{
		for (i = 0; i < nPts; i++)
		{
			unsigned long destIndex = i * destValues / nPts;
			pDest[destIndex] = pCurve[i] + gain;
		}
	}
	else
	{
		for (i = 0; i < destValues; i++)
		{
			unsigned long srcIndex = i * nPts / destValues;
			pDest[i] = pCurve[srcIndex] + gain;
		}
	}

	for (i = destValues; i < pBoardState->maxTCGPoints; i++)
	{
		// pad the table, ideally with the starting value for the next point
		pDest[i] = gain;
	}
	
	if (!RQ_SetSequenceStep(cardIndex, oldStep))
		return NDTS_BFalse;

	return NDTS_BTrue;
}

NDTS_Boolean RQ_CopyStateToRawScan(unsigned long cardIndex, RQ_RawScan *pRawScan, NDTS_Boolean copyLiveNumberOfBScans) 
{
	RQ_RawScanProtected *pRawInternal = RQ_ReturnRawScanInternal(pRawScan);
	unsigned long step, packedGate, unpackedGate ;
	RQ_State *pRQState ;

	// Preconditions.
	_ASSERTE(pRawScan) ;

	if (cardIndex >= globalState.pciState.numberOfRapiDAQBoards) 
		return NDTS_BFalse;

	pRQState = &pBoardStates[cardIndex];

	pRawScan->numberOfAScansInBScan =  pRQState->numberOfAScansPerBScan ;

	if (copyLiveNumberOfBScans && !RQ_GetNumberOfBScansInSRAM(cardIndex, &(pRawScan->numberOfBScans)))
		return NDTS_BFalse ;

	if (!RQ_GetScanStorageOptions(cardIndex, &(pRawScan->scanStorage)))	
		return NDTS_BFalse ;

	if (!RQ_GetSampleStorageMode(cardIndex, &(pRawScan->sampleSize))) 	
		return NDTS_BFalse ;

	pRawScan->cardIndex		= cardIndex ;
	pRawScan->isSequence	= pRQState->isSequence ;
	//memcpy(pRawScan->oversampleCountThisStep, pRQState->oversampleCountThisStep, sizeof(pRQState->oversampleCountThisStep));

	memcpy(&(pRawScan->aScanSamplesThisStep), pRQState->aScanSamplesThisStep, sizeof(pRQState->aScanSamplesThisStep)) ;

	// Bug 549 - the raw scan must have its own copy of the ping pong table as it can change.
	memcpy(&(pRawInternal->RPPS), pRQState->pReversePingPongSequenceSource, sizeof(pRawInternal->RPPS)) ;

	pRawInternal->gateBytesPerBScan			= pRQState->gateBytesPerBScan ;
	pRawInternal->aScanBytesPerBScan		= pRQState->aScanBytesPerBScan ;
	pRawInternal->numberToCheckEitherSide	= pRQState->numberToCheckEitherSide ;
	pRawInternal->proportionToTriggerOff	= pRQState->proportionToTriggerOff ;

	pRawInternal->isCRCCheckPresent			= (pRQState->crcState.pCRCTable) ? 1 : 0 ;

	memcpy(&(pRawInternal->aScanOffsetThisStep), pRQState->aScanOffsetThisStep, sizeof(pRQState->aScanOffsetThisStep)) ;
	memcpy(&(pRawInternal->gateOffsetThisStep),  pRQState->gateOffsetThisStep, sizeof(pRQState->gateOffsetThisStep)) ;
	memcpy(&(pRawInternal->storeGateValue),      pRQState->storeGateValue, sizeof(pRQState->storeGateValue)) ;
	memcpy(&(pRawInternal->gateCountThisStep),   pRQState->gateCountThisStep, sizeof(pRQState->gateCountThisStep)) ;
	memcpy(&(pRawInternal->oversampleCountThisStep), pRQState->oversampleCountThisStep, sizeof(pRQState->oversampleCountThisStep));

	for (step=0; step < RQ_MAXIMUM_NUMBER_OF_ELEMENTS; step++)
	{
		RQ_Gate gateDefault;

		memset(&(gateDefault), 0, sizeof(RQ_Gate)) ;
		gateDefault.isEnabled = NDTS_BFalse ;
		packedGate = 0 ;

		for (unpackedGate = 0 ; unpackedGate < RQ_ReturnMaximumNumberOfGates(pRQState->isSequence); unpackedGate++)
		{
			if (pRQState->gates[step][unpackedGate].isEnabled == 1)
			{
				pRawScan->ppGates[step][unpackedGate] = pRQState->gates[step][unpackedGate] ;
				packedGate++ ;
			}
			else
				pRawScan->ppGates[step][unpackedGate] = gateDefault;
		}

		pRawScan->numberOfGates[step] = packedGate ;
	}

	return NDTS_BTrue ;
}

NDTS_Boolean RQ_IsInternalTrigger(RQ_Trigger triggerSelect)
{
	switch (triggerSelect)
	{
	case RQ_TExternal :
		return NDTS_BFalse ;
	case RQ_TInternal : case RQ_TEncoder : case RQ_TFreeRunning : 
		return NDTS_BTrue;
	default:
		return NDTS_BTrue ;
	}
}

NDTS_Boolean RQ_SetNumberOfBScansPerBankInternal(unsigned long cardIndex, unsigned long numberOfScans)
{
	unsigned long value ;
	NDTS_Boolean isOK ;
	
	if (cardIndex >= globalState.pciState.numberOfRapiDAQBoards) return NDTS_BFalse;

	if (numberOfScans == 0 || numberOfScans > RQ_SRAM_MAXIMUM_NUMBER_OF_BSCANS)
	{
		RQ_HandleRQError(RQ_SetNumberOfBScansPerBankInternal_InvalidNumber) ;
		return NDTS_BFalse ;
	}

	isOK = RQ_CheckSRAM(cardIndex, (RQ_ScanStorage) -1, 0xffffffff, (RQ_SampleSize) -1,
		                 0xffffffff, numberOfScans, 0xffffffff) ;
	if (!isOK)
		return NDTS_BFalse ;

	isOK = RQ_GetSamplingRegister(cardIndex, RQ_NUMBER_OF_SCANS, &value) ;
	if (!isOK)
		return NDTS_BFalse ;
	
	value &= 0xff8000ff ;
	value |= (numberOfScans - 1) << 8 ;

	isOK = RQ_SetSamplingRegister(cardIndex, RQ_NUMBER_OF_SCANS, value) ;
	if (!isOK)
		return NDTS_BFalse ;

	pBoardStates[cardIndex].numberOfBScansPerBank = numberOfScans;

	return isOK ;
}



NDTS_Boolean RQ_SetMaximumNumberOfBScansPerBank(unsigned long cardIndex)
{
	unsigned long maximumNumberOfBScansPerBank, numberOfAScanPerBScan, areMultipleAScansPresent ;
	NDTS_Boolean isOK ;
	RQ_ScanStorage scanStorage ;
	RQ_State *pBoardState = RQ_ReturnBoardStatePointer(cardIndex) ;
	if (!pBoardState) return NDTS_BFalse; 

	if (!pBoardState->isAutomaticBScansPerBank)
	{
		RQ_HandleRQError(RQ_SetMaximumNumberOfBScansPerBank_NotAutomatic) ;
		isOK = NDTS_BFalse ;
	}

	isOK = RQ_GetScanStorageOptions(cardIndex, &scanStorage) ;
	if (!isOK)
	{
		return NDTS_BFalse ;
	}

	areMultipleAScansPresent = (scanStorage == RQ_SSMultipleAScanNoCScan || scanStorage == RQ_SSMultipleAScanMultipleCScan) ? 1 : 0 ;

	isOK = RQ_GetNumberOfAScansPerBScan(cardIndex, &numberOfAScanPerBScan) ;
	if (!isOK)
	{
		return NDTS_BFalse ;
	}

	if (areMultipleAScansPresent)
	{
		maximumNumberOfBScansPerBank = RQ_SRAM_BANK_SIZE  / (pBoardState->aScanBytesPerBScan + pBoardState->gateBytesPerBScan) ;
	}
	else
	{
		maximumNumberOfBScansPerBank = (RQ_SRAM_BANK_SIZE - pBoardState->aScanBytesPerBScan ) / (pBoardState->gateBytesPerBScan) ;
	}

	_ASSERTE(maximumNumberOfBScansPerBank > 0);

	if (maximumNumberOfBScansPerBank > RQ_SRAM_MAXIMUM_NUMBER_OF_BSCANS)
		maximumNumberOfBScansPerBank = RQ_SRAM_MAXIMUM_NUMBER_OF_BSCANS ;

	isOK = RQ_SetNumberOfBScansPerBankInternal(cardIndex, maximumNumberOfBScansPerBank) ;
	if (!isOK)
	{
		return NDTS_BFalse ;
	}
	return NDTS_BTrue ;
}

unsigned long RQ_ReturnNumberOfRapiDAQBoards() 
{
	return globalState.pciState.numberOfRapiDAQBoards;
}


RQ_State *RQ_ReturnBoardStatePointer(unsigned long cardIndex) 
{
	if (cardIndex >= globalState.pciState.numberOfRapiDAQBoards) 
	{
		RQ_HandleRQError(RQ_ReturnBoardStatePointer_InvalidCardIndex) ;
		return 0;
	}
	if (!pBoardStates[cardIndex].isInitialised)
	{
		RQ_HandleRQError(RQ_ReturnBoardStatePointer_InvalidCardIndex) ;
		return 0;
	}
	return &pBoardStates[cardIndex] ;
}

NDTS_Boolean CALL_CONV RQ_SetUpdateSampleParametersHook(unsigned long cardIndex, void (*pFN)(unsigned long cardIndex))
{
	RQ_State *pBoardState = RQ_ReturnBoardStatePointer(cardIndex) ;
	if (!pBoardState) return NDTS_BFalse; 

	if (pBoardState->numberOfUpdateSampleParametersHooks >= MAX_SDK_HOOKS )
	{
		RQ_HandleRQError(RQ_SetHook_NoHooksLeft) ;
		return NDTS_BFalse ;
	}

	pBoardState->ppUpdateSampleParametersHooks[pBoardState->numberOfUpdateSampleParametersHooks++] = pFN;

	return NDTS_BTrue ;
}

NDTS_Boolean CALL_CONV RQ_SetGetEncoderOutputHook(unsigned long cardIndex, void (*pFN)(unsigned long cardIndex, unsigned long encoderIndex, long *pPosition))
{
	RQ_State *pBoardState = RQ_ReturnBoardStatePointer(cardIndex) ;
	if (!pBoardState) return NDTS_BFalse; 

	if (pBoardState->numberOfGetEncoderOutputHooks >= MAX_SDK_HOOKS )
	{
		RQ_HandleRQError(RQ_SetHook_NoHooksLeft) ;
		return NDTS_BFalse ;
	}

	pBoardState->ppGetEncoderOutputHooks[pBoardState->numberOfGetEncoderOutputHooks++] = pFN;

	return NDTS_BTrue ;
}

NDTS_Boolean CALL_CONV RQ_SetResetEncoderOutputHook(unsigned long cardIndex, void (*pFN)(unsigned long cardIndex, unsigned long encoderIndex))
{
	RQ_State *pBoardState = RQ_ReturnBoardStatePointer(cardIndex) ;
	if (!pBoardState) return NDTS_BFalse; 

	if (pBoardState->numberOfResetEncoderOutputHooks >= MAX_SDK_HOOKS )
	{
		RQ_HandleRQError(RQ_SetHook_NoHooksLeft) ;
		return NDTS_BFalse ;
	}

	pBoardState->ppResetEncoderOutputHooks[pBoardState->numberOfResetEncoderOutputHooks++] = pFN;

	return NDTS_BTrue ;
}

NDTS_Boolean RQ_ReadRawScanGateValues(unsigned long cardIndex,
									   RQ_RawScan *pRawScan, 
									   RQ_CardBlockDescription *pDataBCardBlockDescriptions, 
									   unsigned long numberOfDescriptions)
{
	unsigned long cScanBaseSRAMAddress, numberOfBlocksRead, numberOfCScanDesynchronisations, numberOfCorruptCScanHeaders,
		          aScanIndex, bScanIndex, numberOfOversamples/*, numberOfBScansInBank*/, oversampleIndex,
				  cardCScanBaseAddressOffset, cardCScanHeader, cardCScanRecordAddressOffset ;
	unsigned char *pOurCScanBaseAddress, /* *pCardCScanBaseAddress,*/ *pOurCScanRecordAddress/*,  *pCardCScanRecordAddress*/ ;
	RQ_State *pBoardState = RQ_ReturnBoardStatePointer(cardIndex);
	NDTS_Boolean isOK, isCScanHeaderOK, isCScanHeaderIDPresent, isCScanHeaderRecoverAttempted ;
//	unsigned long long previousNumberOfUnrecoveredCScanSamples  ;
	RQ_MemoryHandle *pDestinationBlockIt ;


	isOK = RQ_GetCScanBaseSRAMAddress(cardIndex, &cScanBaseSRAMAddress) ;
	if (!isOK)
		return NDTS_BFalse ;

	/*
	isOK = RQ_GetNumberOfBScansInSRAM(cardIndex, &numberOfBScansInBank) ;
	if (!isOK)
		return NDTS_BFalse ;

	if (numberOfBScansInBank != pRawScan->numberOfBScans)
	{
		// I think this is happening in non thread WinRS when it takes so long to malloc the data
		// structure that the card has filled the other bank and then filled our bank since the bank swap
		// at the start of getRawScan.
		RQ_HandleRQError(RQ_GetRawScan_RawScanNotLargeEnoughForData) ;
		return NDTS_BFalse ;
	}
	*/
	isOK = RQ_GetMemoryBlockList(RQ_ReturnRawScanInternal(pRawScan)->pDataB, &pDestinationBlockIt) ;
	if(!isOK) 
		return NDTS_BFalse ; 

	//  Get gate results data from card into scattered memory 
	isOK = RQ_ReadRawScanBlocks(cardIndex, 
		cScanBaseSRAMAddress, 
		pDataBCardBlockDescriptions, 
		pDataBCardBlockDescriptions + numberOfDescriptions, 
		pDestinationBlockIt + 1,  // Skip over header pointer, one void*
		pBoardState->crcState.pCRCTable, 
		&numberOfBlocksRead) ;
	if (!isOK)
		return NDTS_BFalse ;

	if (numberOfBlocksRead != numberOfDescriptions)
	{
		// I don't think this should happen ie RQ_ReadRawScanBlocks() should have returned false.
		_ASSERTE(0) ;

		RQ_HandleRQError(RQ_GetRawScan_NotAllBlocksDMAOK) ;
		return NDTS_BFalse ;
	}

	// Check the gated data header
	// Old
	//pCardCScanBaseAddress = (unsigned char *) pPCIStates[cardIndex].pPCIBar2VirtualAddress + cScanBaseSRAMAddress ;
	cardCScanBaseAddressOffset = cScanBaseSRAMAddress ;

	numberOfCScanDesynchronisations = 0 ;
	numberOfCorruptCScanHeaders = 0 ;

	/* OverSampling (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.
	*/


	for (bScanIndex = 0 ; bScanIndex < pRawScan->numberOfBScans ; bScanIndex++)
	{
		RQ_MemoryHandle *pGateValuesBody ;
		int sampleIndex = 0;

		isOK = RQ_GetRawScanGateValuesBody(pRawScan, bScanIndex, &pGateValuesBody) ;
		if(!isOK) 
			return NDTS_BFalse ; 

		pOurCScanBaseAddress = pGateValuesBody->p ;
		for (aScanIndex = 0 ; aScanIndex < pRawScan->numberOfAScansInBScan; aScanIndex++)
		{
			RQ_Gate *pGates = pRawScan->ppGates[aScanIndex];
	
			numberOfOversamples = pBoardState->oversampleCountThisStep[pBoardState->isSequence ? aScanIndex : 0];
			for (oversampleIndex = 0; oversampleIndex < numberOfOversamples; oversampleIndex++)
			{
				pOurCScanRecordAddress = pOurCScanBaseAddress + 
					oversampleIndex * (4 + 4 * pRawScan->numberOfGates[aScanIndex]) + 
					pBoardState->gateOffsetThisStep[aScanIndex] - 4 ;
				// Old
				cardCScanRecordAddressOffset = cardCScanBaseAddressOffset +
					oversampleIndex * (4 + 4 * pRawScan->numberOfGates[aScanIndex]) + 
					pBoardState->gateOffsetThisStep[aScanIndex] - 4 ;
				/*
				pCardCScanRecordAddress = pCardCScanBaseAddress + 
					oversampleIndex * (4 + 4 * pRawScan->numberOfGates[aScanIndex]) + 
					pBoardState->gateOffsetThisStep[aScanIndex] - 4 ;
					*/

				isOK = NDTS_HAL_GetWord(NDTS_SIDRapiDAQ, cardIndex, cardCScanRecordAddressOffset, &cardCScanHeader) ;
				if (!isOK)
				{
					return NDTS_BFalse ;
				}

				isCScanHeaderOK = RQ_CheckCScanHeader(cardCScanHeader, sampleIndex, &isCScanHeaderIDPresent) ;
				if (!isCScanHeaderOK)
				{
					*(unsigned long *) pOurCScanRecordAddress = cardCScanHeader ;

					isCScanHeaderOK = RQ_CheckCScanHeader(*(unsigned long *) pOurCScanRecordAddress, sampleIndex, &isCScanHeaderIDPresent) ;

					isCScanHeaderRecoverAttempted = NDTS_BTrue ;
				}
				else
					isCScanHeaderRecoverAttempted = NDTS_BFalse ;

				if (!isCScanHeaderOK)
					if (isCScanHeaderIDPresent)
					{
						numberOfCScanDesynchronisations++ ;
						pBoardState->crcState.statistics.cScanHeaderCounter.numberOfDesynchronisations++ ;
					}
					else
					{
						numberOfCorruptCScanHeaders++ ;
						pBoardState->crcState.statistics.cScanHeaderCounter.numberOfUnrecovered++ ;
					}
				else if (!isCScanHeaderRecoverAttempted)
					pBoardState->crcState.statistics.cScanHeaderCounter.numberOfGood++ ;
				else
					pBoardState->crcState.statistics.cScanHeaderCounter.numberOfRecovered++ ;

				RQ_AdjustGateDepths(pGates, pOurCScanRecordAddress);
				sampleIndex++;
			}
		}

		// Old
		//pCardCScanBaseAddress += pBoardState->gateBytesPerBScan ;
		cardCScanBaseAddressOffset += pBoardState->gateBytesPerBScan ;
	}

	if(numberOfCorruptCScanHeaders > 0)
	{
		RQ_HandleRQError(RQ_GetRawScan_InternalDataFormatError) ;
		return NDTS_BFalse ;
	}
	else if(numberOfCScanDesynchronisations > 0)
	{
		RQ_HandleRQError(RQ_GetRawScan_CScanDesynchronisation) ;
		return NDTS_BFalse ;
	}
/*
	previousNumberOfUnrecoveredCScanSamples = pBoardState->crcState.statistics.cScanSampleCounter.numberOfUnrecovered ;
	if (previousNumberOfUnrecoveredCScanSamples !=  pBoardState->crcState.statistics.cScanSampleCounter.numberOfUnrecovered) 
	{
		RQ_HandleRQError(RQ_GetRawScan_CRCSubSystemInternalError) ;
		return NDTS_BFalse ;
	}
*/
	return NDTS_BTrue ;
}

NDTS_Boolean RQ_VerifyRawScanAScanHeaders(unsigned long cardIndex, RQ_RawScan *pRawScan)
{
	unsigned long numberOfAScanDesynchronisations, numberOfCorruptAScanHeaders, bScanIndex,
		          cardAScanBaseAddressOffset, cardBScanBaseAddressOffset, cardAScanHeader ;
	//unsigned char *pCardBScanBaseAddress, *pCardAScanBaseAddress;
	RQ_State *pBoardState = RQ_ReturnBoardStatePointer(cardIndex);
	NDTS_Boolean isOK ;

	// Old
	//pCardBScanBaseAddress = (unsigned char *) pPCIStates[cardIndex].pPCIBar2VirtualAddress ;
	cardBScanBaseAddressOffset = 0 ;

	numberOfCorruptAScanHeaders = numberOfAScanDesynchronisations = 0 ;

	for (bScanIndex = 0 ; bScanIndex < pRawScan->numberOfBScans ; bScanIndex++)
	{
		RQ_MemoryHandle *pAScanHeaders ;
		unsigned long *pOurAScanHeaderAddress ;
		unsigned long aScanIndex ;

		isOK = RQ_GetRawScanAScanHeader(pRawScan, 0, bScanIndex,  &pAScanHeaders) ;
		if(!isOK) 
			return NDTS_BFalse ; 

		pOurAScanHeaderAddress = (unsigned long*)(pAScanHeaders->p) ;

		for (aScanIndex = 0 ; aScanIndex < pRawScan->numberOfAScansInBScan; aScanIndex++)
		{
			NDTS_Boolean isAScanHeaderOK, isAScanHeaderIDPresent, isAScanHeaderRecoverAttempted ;

			// Old
			//pCardAScanBaseAddress = pCardBScanBaseAddress + pBoardState->aScanOffsetThisStep[aScanIndex] - 4; // Point to header rather than data
			cardAScanBaseAddressOffset = cardBScanBaseAddressOffset + pBoardState->aScanOffsetThisStep[aScanIndex] - 4; // Point to header rather than data

			isOK = NDTS_HAL_GetWord(NDTS_SIDRapiDAQ, cardIndex, cardAScanBaseAddressOffset, &cardAScanHeader) ;
			if (!isOK)
			{
				return NDTS_BFalse ;
			}

			// Check the a-scan header we read from the card.
			isAScanHeaderOK = RQ_CheckAScanHeader(cardAScanHeader, aScanIndex, &isAScanHeaderIDPresent) ;
			if (!isAScanHeaderOK)
			{
				*pOurAScanHeaderAddress = cardAScanHeader ;

				// It's wrong, so check the a-scan header on the card.
				isAScanHeaderOK = RQ_CheckAScanHeader(*pOurAScanHeaderAddress, aScanIndex, &isAScanHeaderIDPresent) ;

				isAScanHeaderRecoverAttempted = NDTS_BTrue ;
			}
			else
				isAScanHeaderRecoverAttempted = NDTS_BFalse ;

			if (!isAScanHeaderOK)
				if (isAScanHeaderIDPresent)
				{
					numberOfAScanDesynchronisations++ ;
					pBoardState->crcState.statistics.aScanHeaderCounter.numberOfDesynchronisations++ ;
				}
				else
				{
					numberOfCorruptAScanHeaders++ ;
					pBoardState->crcState.statistics.aScanHeaderCounter.numberOfUnrecovered++ ;
				}
			else if (!isAScanHeaderRecoverAttempted)
				pBoardState->crcState.statistics.aScanHeaderCounter.numberOfGood++ ;
			else
				pBoardState->crcState.statistics.aScanHeaderCounter.numberOfRecovered++ ;

			// We know that the next a-scan header is actually immediately after the current a-scan header.
			// Old - not sure what this line is for: seems redundant
			//pCardAScanBaseAddress++ ;
			pOurAScanHeaderAddress++ ;

		}

		// Old
		//pCardBScanBaseAddress += pBoardState->aScanBytesPerBScan ;
		cardBScanBaseAddressOffset += pBoardState->aScanBytesPerBScan ;
	}

	if(numberOfCorruptAScanHeaders > 0)
	{
		RQ_HandleRQError(RQ_GetRawScan_InternalDataFormatError) ;
		return NDTS_BFalse ;
	}
	else if(numberOfAScanDesynchronisations > 0)
	{
		RQ_HandleRQError(RQ_GetRawScan_AScanDesynchronisation) ;
		return NDTS_BFalse ;
	}

	return NDTS_BTrue;
}


// Doesn't RQ_Lock(),  wait for the bank swap or indicateSampleDownloaded
/* OverSampling (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.
*/

NDTS_Boolean CALL_CONV RQ_GetAScanNoBankSwap(unsigned long cardIndex, short *pData, unsigned long numberOfBufferValues, unsigned long aScanIndex, unsigned long bScanIndex)
{
	NDTS_Boolean isOK ;
	unsigned long numberOfBytesPerSample, bytesToTransfer, localAddress ;
	RQ_SampleSize sampleSize ;
	RQ_State *pBoardState = RQ_ReturnBoardStatePointer(cardIndex) ;
	unsigned short sampleCount, samplesPerOversample, numberOfOversamples;

	if (!pBoardState) return NDTS_BFalse; 

	if (!RQ_GetSampleStorageMode(cardIndex, &sampleSize)) return NDTS_BFalse ;

	numberOfBytesPerSample = sampleSize == RQ_SS12Bit ? 2 : 1 ;
	sampleCount = (short)pBoardState->aScanSamplesThisStep[aScanIndex];
	numberOfOversamples = (short)pBoardState->oversampleCountThisStep[pBoardState->isSequence ? aScanIndex : 0];
	samplesPerOversample = sampleCount / numberOfOversamples;

	localAddress = pBoardState->aScanOffsetThisStep[aScanIndex] + pBoardState->aScanBytesPerBScan * bScanIndex ;

	if (pBoardState->oversampleCountThisStep[pBoardState->isSequence ? aScanIndex : 0] == 1)
	{
		bytesToTransfer = sampleCount * numberOfBytesPerSample;
		if (numberOfBufferValues < sampleCount)
		{
			bytesToTransfer = numberOfBufferValues << 1 ;
		}
		// copy straight to destination
		isOK = NDTS_HAL_PerformDMATransfer(NDTS_SIDRapiDAQ, cardIndex, pData, localAddress, bytesToTransfer, NDTS_BTrue) ;
{
	/*
	// Old. This will no longer compile. 
	static char lastAscanIdx;
		if ((((char *)(pPCIStates[cardIndex].pPCIBar2VirtualAddress))[localAddress - 3] == 'N') &&
		    (((char *)(pPCIStates[cardIndex].pPCIBar2VirtualAddress))[localAddress - 2] == 'S') &&
		    (((char *)(pPCIStates[cardIndex].pPCIBar2VirtualAddress))[localAddress - 1] == 'A'))
		{
			lastAscanIdx = 
		    ((char *)(pPCIStates[cardIndex].pPCIBar2VirtualAddress))[localAddress - 4];
		}
		else
		{
			lastAscanIdx = 99; // bad header
		}
		*/
}
	}
	else
	{
		int sample, oversample;
		short *pDest = pData;
		int offsetPerOversample = samplesPerOversample + 2;
		short aScanBuffer[RQ_SRAM_MAXIMUM_NUMBER_OF_SAMPLES + 20 * 2]; // max samples + 20 headers in the worst case
		int maxIters;

		bytesToTransfer = sampleCount * numberOfBytesPerSample + pBoardState->oversampleCountThisStep[pBoardState->isSequence ? aScanIndex : 0] * 4 - 4;
		
		// copy to holding buffer than unravel the oversamples

		isOK = NDTS_HAL_PerformDMATransfer(NDTS_SIDRapiDAQ, cardIndex, aScanBuffer, localAddress, bytesToTransfer, NDTS_BTrue) ;

		maxIters = samplesPerOversample < numberOfBufferValues / numberOfOversamples ? samplesPerOversample : numberOfBufferValues / numberOfOversamples ;

		for (sample = 0; sample < maxIters ; sample++)
		{
			// Somewhat counterintuitively the oversample with the longest delay is the
			// one that we put first.  Draw some pictures and satisfy yourself that
			// this is the right thing to do

			// Somewhat anticounterintuitively this is wrong although it was working before.
			// Perhaps something else has been changed that I don't know about...
			// maybe not
			for (oversample = numberOfOversamples - 1; oversample >= 0; oversample--)
			//for (oversample = 0; oversample < numberOfOversamples; oversample++)
			{
				*pDest++ = aScanBuffer[oversample * offsetPerOversample + sample];
			}
		}

		for ( ; sample < (int) numberOfBufferValues ; sample++)
			*pDest++ = 0 ;
	}

	if (!isOK)
	{
		RQ_HandleRQError(RQ_GetSamples_PerformDMATransferFail) ;
		RQ_IndicateSampleDataDownloaded(cardIndex) ;
		return NDTS_BFalse ;
	}

	return NDTS_BTrue;
}

unsigned long RQ_ReturnMaxBScanRepRate(unsigned long cardIndex)
{
	RQ_State *pBoardState = RQ_ReturnBoardStatePointer(cardIndex) ;
	if (!pBoardState) 
		return 0; 

	return pBoardState->repetitionRate / (pBoardState->numberOfAScansPerBScan + 1);  // Always round slower
}

// The default values are returned from the INI file (if present).  If the file doesn't exist
// or the value isn't defined we use a hard-coded default instead
// We have a different function for each data type

int RQ_GetDefaultValueInt(RQdefaultEnum index)
{
	char val[100];
	char defaultString[] = "default";
	int value = 0;

	GetPrivateProfileString("RQ", RQDefaultStrings[index], defaultString, val, 100, globalState.iniFileName);

	if (strcmp(val, defaultString) == 0)
	{
		value = RQdefIntValues[index]; // use the hard-coded default value
	}
	else
	{
		if ((val[0] == '0') && (toupper(val[1]) == 'X'))
		{
			// read a hex value
			sscanf(val, "%x", &value);
		}
		else
		{
			// read a decimal value
			value = atoi(val);
		}
	}

	return value;
}

void RQ_GetDefaultValueString(RQdefaultEnum index, char *pDest)
{
	char val[100];
	char defaultString[] = "default";

	GetPrivateProfileString("RQ", RQDefaultStrings[index], defaultString, val, 100, globalState.iniFileName);

	if (strcmp(val, defaultString) == 0)
	{
		strcpy(pDest, RQdefStringValues[index]); // use the hard-coded default value
	}
	else
	{
		strcpy(pDest, val);
	}
}


RQ_API NDTS_Boolean CALL_CONV RQ_InitialiseSequenced(unsigned long cardIndex)
{
	// IntitialiseSequenced should only ever be called via the HLSDK and is in RQ_Protected.h
	// Firmware is determined by the constant but can be overridden in the ini file
	return RQ_InitialiseProtected(cardIndex, RQ_AG200mV, NDTS_BTrue) ;
}


RQ_API void RQ_GetLogLevel(NDTS_LogLevel *pFile, NDTS_LogLevel *pApp, NDTS_LogLevel *pDebugger)
{
	NDTS_GetLogLevel(pFile, pApp, pDebugger);
}

RQ_API void RQ_SetLogLevel(NDTS_LogLevel file, NDTS_LogLevel app, NDTS_LogLevel debugger)
{
	NDTS_SetLogLevel(file, app, debugger);
}

NDTS_Boolean RQ_SetTCGStartInternal(unsigned long cardIndex, unsigned long tCGModeMask)
{
	NDTS_Boolean isOK ;
	unsigned long regvalue;
	RQ_State *pBoardState = RQ_ReturnBoardStatePointer(cardIndex) ;
	if (!pBoardState) return NDTS_BFalse; 

	// 00 - 1 curve with max
	if (tCGModeMask > 3)
	{
		RQ_HandleRQError(RQ_SetTCGNormalisedStart_InvalidTCGModeMask) ;
		RQ_Unlock(cardIndex) ;
		return NDTS_BFalse ;
	}

	RQ_Lock(cardIndex) ;

	isOK = RQ_GetSamplingRegister(cardIndex, RQ_TCG_CONFIGURE, &regvalue);
	if (!isOK)
	{
		RQ_Unlock(cardIndex) ;
		return NDTS_BFalse ;
	}

	regvalue &= 0xfffffffd; // disable

	isOK = RQ_SetSamplingRegister(cardIndex, RQ_TCG_CONFIGURE, regvalue);
	if (!isOK)
	{
		RQ_Unlock(cardIndex) ;
		return NDTS_BFalse ;
	}

	regvalue &= 0xfffffff3;
	regvalue |= 0x80000000; // reset load counter
	regvalue |= tCGModeMask << 2;

	isOK = RQ_SetSamplingRegister(cardIndex, RQ_TCG_CONFIGURE, regvalue);
	if (!isOK)
	{
		RQ_Unlock(cardIndex) ;
		return NDTS_BFalse ;
	}

	RQ_Unlock(cardIndex) ;

	return NDTS_BTrue ;
}

NDTS_Boolean RQ_SetTCGPointRatio(unsigned long cardIndex, unsigned long tCGPointRatio)
{
	NDTS_Boolean isOK ;
	unsigned long regvalue;
	RQ_State *pBoardState = RQ_ReturnBoardStatePointer(cardIndex) ;
	if (!pBoardState) return NDTS_BFalse; 

	if(tCGPointRatio > 16)
	{
		RQ_HandleRQError(RQ_SetTCGNormalisedStart_TCGPointRatio) ;
		return NDTS_BFalse ;
	}

	isOK = RQ_GetSamplingRegister(cardIndex, RQ_TCG_CONFIGURE, &regvalue);
	if (!isOK)
	{
		return NDTS_BFalse ;
	}

	regvalue &= 0xffffff0f;
	regvalue |= (tCGPointRatio - 1) << 4;

	isOK = RQ_SetSamplingRegister(cardIndex, RQ_TCG_CONFIGURE, regvalue);
	if (!isOK)
	{
		return NDTS_BFalse ;
	}
	return NDTS_BTrue ;
}

NDTS_Boolean RQ_GetTCGPointRatio(unsigned long cardIndex, unsigned long *tCGPointRatio)
{
	NDTS_Boolean isOK ;
	unsigned long regvalue;
	RQ_State *pBoardState = RQ_ReturnBoardStatePointer(cardIndex) ;
	if (!pBoardState) return NDTS_BFalse; 

	isOK = RQ_GetSamplingRegister(cardIndex, RQ_TCG_CONFIGURE, &regvalue);
	if (!isOK)
	{
		return NDTS_BFalse ;
	}

	*tCGPointRatio = ((regvalue & 0x00f0) >> 4) + 1;

	return NDTS_BTrue ;
}


NDTS_Boolean RQ_SetTCGDecimation(unsigned long cardIndex)
{
	unsigned long step = 0;
	NDTS_Boolean isOK;
	RQ_State *pBoardState = RQ_ReturnBoardStatePointer(cardIndex) ;
	if (!pBoardState) return NDTS_BFalse; 

	// November 06 - IW sends a curved divided by 4 and so far 4 samples of resolution has been acceptable.
	// Ranges from 32K to 64K samples require a divider of 8 to fit in the 8K buffer.
	if (!RQ_GetSequenceStep(cardIndex, &step))
		return NDTS_BFalse;

	if (pBoardState->aScanSamplesThisStep[step] > 32767)
	{
		_ASSERTE(pBoardState->aScanSamplesThisStep[step] < 65536);
		isOK = RQ_SetTCGPointRatio(cardIndex, 8);
	}
	else
	{
		isOK = RQ_SetTCGPointRatio(cardIndex, 4);
	}
	return isOK;
}

unsigned short RQ_ReturnGateSubtractValue(const RQ_Gate *pGate)
{
    if (pGate->index == 0)
    {
        if (pGate->startCount < 3)
        {
            return (unsigned short)pGate->startCount ;
        }
        else
        {
            return 3 ;
        }
    }
    else
    {
        if (pGate->startCount < 4)
        {
            return (unsigned short)pGate->startCount ;
        }
        else
        {
            return 4 ;
        }
    }
}

// Bug 838 - gate depth is one sample too late on peak metric
void RQ_AdjustGateDepths(RQ_Gate *pGates, unsigned char *pOurCScanRecordAddress)
{
	int gateIndex = 0, activeGateIndex = 0;

	for (gateIndex = 0; gateIndex < RQ_MAXIMUM_NUMBER_OF_GATES_COMPILE_TIME; gateIndex++)
	{
		if (pGates[gateIndex].isEnabled)
		{
			activeGateIndex++;
			if (pGates[gateIndex].distanceMetric == RQ_GDMPeak)
			{
				RQ_GateOutput* pGateOutput = (RQ_GateOutput*)(pOurCScanRecordAddress + activeGateIndex * sizeof(RQ_GateOutput));
				if (pGateOutput->wasDistanceMeasured)
				{
					_ASSERTE(pGateOutput->distanceMetric > 0);
					pGateOutput->distanceMetric--;
				}
			}
		}
	}
}

RQ_API int RQ_GetSonatestSerialMuxValue()
{
	return 	RQ_GetDefaultValueInt(RQdef_SonatestSerialMux);
}

RQ_API int RQ_GetSonatestSerialMuxGain(unsigned long oneBasedChannel)
{
	return 	RQ_GetDefaultValueInt(RQdef_SonatestSerialMuxGain1 + oneBasedChannel - 1);
}

RQ_API int RQ_GetSonatestSerialMuxFilter(unsigned long oneBasedChannel)
{
	return 	RQ_GetDefaultValueInt(RQdef_SonatestSerialMuxFilter1 + oneBasedChannel - 1);
}

RQ_API int RQ_GetSonatestSerialMuxVoltageClamp()
{
	return 	RQ_GetDefaultValueInt(RQdef_SonatestSerialMuxVoltageClamp);
}



NDTS_Boolean RQ_SetAnalogueInputRangeInternal(unsigned long cardIndex, RQ_AnalogueInputRange analogueGain)
{
	unsigned long value ;
	NDTS_Boolean isOK;
	RQ_State *pBoardState = RQ_ReturnBoardStatePointer(cardIndex) ;
	if (!pBoardState) return NDTS_BFalse; 

	value = pBoardState->peripheralRegisterShadow;
	
	value &= 0xfffff03f ;
	value |= 1 << ((unsigned long) analogueGain + 6) ;

	isOK = RQ_SetPeripheralRegister(cardIndex, RQ_PERIPHERAL_HARDWARE_CONTROL, value) ;
	pBoardState->peripheralRegisterShadow = value;

	return isOK;
}

// Only use this for testing.  It causes the entire PC to lock if an out of range
// address access is made to the card.
RQ_API NDTS_Boolean CALL_CONV RQ_SetOnlyRespondToInRangeAccess(unsigned long cardIndex)
{
	NDTS_Boolean isOK ;
	RQ_State *pBoardState = RQ_ReturnBoardStatePointer(cardIndex) ;
	if (!pBoardState) return NDTS_BFalse; 

	RQ_Lock(cardIndex) ;

	// This register is write only and is not shadowed.
	// A zero is no action to most of the other bits.
	isOK = RQ_SetSystemRegister(cardIndex, RQ_SYSTEM_COMMAND_REGISTER, (1 << 8)) ;

	RQ_Unlock(cardIndex) ;

	return isOK ;
}
