#include <windows.h>
#include <stdio.h>
#include <io.h>
#include <crtdbg.h>

#include "NDTSEnums.h"
#include "NDTSError.h"
#include "NDTSCommonHAL.h"

#include "PRX.h"
#include "PRXProtected.h"
#include "PRXInternal.h"
#include "PRXSonatestSerialMuxInternal.h"
#include "PRXStrings.h"

static float PRX_GetMaxFETLoadForShadow(PRX_Shadow *pReg_shadow);
static float PRX_GetFETLoadForShadow(PRX_Shadow *pReg_shadow, unsigned long channel);

PRX_API NDTS_Boolean CALL_CONV  PRX_PCIEnumerate(unsigned long *numberOfCards)
{
	static NDTS_Boolean isEnumerated = NDTS_BFalse;
	unsigned long d;

	if (isEnumerated)
	{
		*numberOfCards = prxs_found;
		return NDTS_BTrue;
	}

	// Initialise the pci PCI interface
	if (!NDTS_HAL_Initialise())
		return NDTS_BFalse;

	if (!NDTS_HAL_InitialiseCardType(0x10b5, 0x5602, NDTS_SIDPRX, &prxs_found))
		return NDTS_BFalse;

	if (prxs_found == 0)
	{
		*numberOfCards = 0;
		return NDTS_BTrue;
	}

	if (pRX_StateArray != NULL)
	{
		free(pRX_StateArray);
		pRX_StateArray = NULL;
	}

	pRX_StateArray = malloc(prxs_found * sizeof(PRX_State));
	if (pRX_StateArray == NULL)
	{
		NDTS_LogLine(NDTS_LLEmerg, "Out of memory file %s line %d", __FILE__, __LINE__);
	}

	memset(pRX_StateArray, 0, prxs_found * sizeof(PRX_State));
	for (d = 0; d < prxs_found; d++)
	{
		pRX_StateArray[d].isPciInitialised = NDTS_BFalse;
		pRX_StateArray[d].isDMAChannelOpen = NDTS_BFalse ;
		pRX_StateArray[d].diagnosticsFilename[0] = '\0';
		pRX_StateArray[d].isDiagnosticsMode = NDTS_BFalse;
		pRX_StateArray[d].isInitialised = NDTS_BFalse ;
		pRX_StateArray[d].errorCode = 0;
		pRX_StateArray[d].errorLogStringIndex = 0;
		pRX_StateArray[d].isManualCommit = NDTS_BFalse;
		pRX_StateArray[d].masterRepetitionTime = 1;
		pRX_StateArray[d].pLimitPulseWidthCallback = 0;
		pRX_StateArray[d].isSoftwareFETOverloadProtectionEnabled = NDTS_BFalse;
		pRX_StateArray[d].useSonatestMux = NDTS_BFalse;
	}

	*numberOfCards = prxs_found;
	isEnumerated = NDTS_BTrue;
	return NDTS_BTrue;
}

PRX_API NDTS_Boolean CALL_CONV PRX_Initialise(unsigned long card)
{
	NDTS_Boolean isOK ;
	PRX_State *pRX_State ;

	if (!PRX_CheckState(card))
		return NDTS_BFalse ;

	pRX_State = &pRX_StateArray[card];

	// Create the critical section, RQ_Lock(card) and inititalise SDK internal data structures
	if (!PRX_StructuresInitialise(card))
		return NDTS_BFalse;

	if (!PRX_PCIInitialise(card))
		return NDTS_BFalse;

	isOK = PRX_SystemFPGAInversionTest(card) ;
	if (!isOK)
		return NDTS_BFalse ;

	pRX_State->isInitialised = NDTS_BTrue ;

	if (!PRX_SetAutoResetForFETOverload(card, NDTS_BTrue))
		return NDTS_BFalse;

	return NDTS_BTrue ;
}

PRX_API NDTS_Boolean CALL_CONV PRX_Uninitialise(unsigned long card)
{
	NDTS_Boolean isOK ;
	PRX_State *pRX_State;

	if (!PRX_CheckState(card))
		return NDTS_BFalse ;

	pRX_State = &pRX_StateArray[card];

	if (!pRX_State->isInitialised)
	{
		PRX_HandlePRXError(PRX_Uninitialise_IsInitialisedFail) ;
		return NDTS_BFalse ;
	}

	PRX_Lock(card) ;

	if (!pRX_State->isPciInitialised)
	{
		PRX_HandlePRXError(PRX_Uninitialise_IsPciInitialisedFail) ;
		PRX_Unlock(card) ;
		return NDTS_BFalse ;
	}

	if (pRX_State->isDMAChannelOpen)
	{
		isOK = PRX_CloseDMAChannel(card) ;
		if (!isOK)
		{
			PRX_Unlock(card) ;
			return NDTS_BFalse ;
		}
	}

	isOK = 	NDTS_HAL_UninitialiseCard(NDTS_SIDPRX, card);
	if (!isOK)
	{
		PRX_Unlock(card) ;
		return NDTS_BFalse ;
	}

	pRX_State->isPciInitialised = NDTS_BFalse ;
	pRX_State->isInitialised = NDTS_BFalse ;

	PRX_Unlock(card) ;
	PRX_DeleteCriticalSection(card) ;

	return NDTS_BTrue ;
}

PRX_API NDTS_Boolean CALL_CONV PRX_GetIsInitialised(unsigned long card)
{
	PRX_State *pRX_State ;

	if (!PRX_CheckState(card))
		return NDTS_BFalse ;

	pRX_State = &pRX_StateArray[card];

	return pRX_State->isInitialised ;
}

PRX_API NDTS_Boolean CALL_CONV PRX_GetHVStatus(unsigned long card, unsigned long *flag)
{
	unsigned long status ;
	NDTS_Boolean isOK ;

	if (!PRX_CheckState(card))
		return NDTS_BFalse ;

	_ASSERTE(pRX_StateArray[card].useSonatestMux == NDTS_BFalse);

	isOK = PRX_GetSystemRegister(card, PRX_SYSTEM_STATUS_REGISTER, &status);
	if (!isOK)
		return NDTS_BFalse ;

	status &= 0x00000008;
	*flag = status >> 3;
	return NDTS_BTrue;
}

PRX_API NDTS_Boolean CALL_CONV PRX_SetGlobalRegs(unsigned long card, PRX_GlobalRegs *pRegs, NDTS_Boolean commit)
{
	PRX_State *pRX_State ;

	if (!PRX_CheckState(card))
		return NDTS_BFalse ;

	pRX_State = &pRX_StateArray[card];

	pRX_State->reg_shadow.globalRegs = *pRegs;

	if (commit)
	{
		return PRX_Commit(card);
	}
	return NDTS_BTrue;
}

PRX_API NDTS_Boolean CALL_CONV PRX_GetGlobalRegs(unsigned long card, PRX_GlobalRegs *pRegs)
{
	PRX_State *pRX_State ;

	if (!PRX_CheckState(card))
		return NDTS_BFalse ;

	pRX_State = &pRX_StateArray[card];

	*pRegs = pRX_State->reg_shadow.globalRegs;

	return NDTS_BTrue;
}

PRX_API NDTS_Boolean CALL_CONV PRX_SetPulseEchoEnable(unsigned long card, unsigned long flags)
{
	PRX_State *pRX_State ;

	if (!PRX_CheckState(card))
		return NDTS_BFalse ;

	pRX_State = &pRX_StateArray[card];

	if (flags > 0xff)
	{
		PRX_HandlePRXError(PRX_SetPulseEchoEnable_InvalidFlags) ;
		return NDTS_BFalse;
	}

	if (pRX_State->useSonatestMux) 
	{
		PRX_SonatestMuxSetPulseEchoEnable(flags);
		// Force all channels to Through Transmission when using Sonatest Mux
		pRX_State->reg_shadow.globalRegs.pulseEchoMask = 0;
	}
	else
	{
		pRX_State->reg_shadow.globalRegs.pulseEchoMask = flags;
	}

	return PRX_CommitConditional(card);
}

PRX_API NDTS_Boolean CALL_CONV PRX_GetPulseEchoEnable(unsigned long card, unsigned long *flags)
{
	PRX_State *pRX_State ;

	if (!PRX_CheckState(card))
		return NDTS_BFalse ;

	pRX_State = &pRX_StateArray[card];

	_ASSERTE(!pRX_State->useSonatestMux);  // This SDK function not valid with Sonatest Mux as reg_shadow is not up to date
	*flags = pRX_State->reg_shadow.globalRegs.pulseEchoMask;
	return NDTS_BTrue;
}

PRX_API NDTS_Boolean CALL_CONV PRX_SetExternalTrigger(unsigned long card, unsigned long flag)
{
	PRX_State *pRX_State ;

	if (!PRX_CheckState(card))
		return NDTS_BFalse ;

	pRX_State = &pRX_StateArray[card];

	pRX_State->reg_shadow.globalRegs.externalTrigger = flag ? 1 : 0;
	return PRX_CommitConditional(card);
}

PRX_API NDTS_Boolean CALL_CONV PRX_GetExternalTrigger(unsigned long card, unsigned long *flag)
{
	PRX_State *pRX_State ;

	if (!PRX_CheckState(card))
		return NDTS_BFalse ;

	pRX_State = &pRX_StateArray[card];

	*flag = pRX_State->reg_shadow.globalRegs.externalTrigger;
	return NDTS_BTrue;
}

PRX_API NDTS_Boolean CALL_CONV PRX_SetHVEnable(unsigned long card, unsigned long flag)
{
	PRX_State *pRX_State ;

	if (!PRX_CheckState(card))
		return NDTS_BFalse ;

	pRX_State = &pRX_StateArray[card];

	pRX_State->reg_shadow.globalRegs.highVoltageEnable = flag ? 1 : 0;

	if (pRX_State->useSonatestMux) PRX_SonatestMuxSetHVEnable(flag);

	return PRX_CommitConditional(card);
}

PRX_API NDTS_Boolean CALL_CONV PRX_GetHVEnable(unsigned long card, unsigned long *flag)
{
	PRX_State *pRX_State ;

	if (!PRX_CheckState(card))
		return NDTS_BFalse ;

	pRX_State = &pRX_StateArray[card];

	_ASSERTE(pRX_State->useSonatestMux == NDTS_BFalse);

	*flag = pRX_State->reg_shadow.globalRegs.highVoltageEnable;
	return NDTS_BTrue;
}

PRX_API NDTS_Boolean CALL_CONV PRX_HVPASetPowerEnable(unsigned long card, unsigned long flag)
{
	PRX_State *pRX_State ;

	if (!PRX_CheckState(card))
		return NDTS_BFalse ;

	pRX_State = &pRX_StateArray[card];

	pRX_State->reg_shadow.globalRegs.ultranPowerOnReq = flag ? 1 : 0;
	return PRX_CommitConditional(card);
}

PRX_API NDTS_Boolean CALL_CONV PRX_HVPAGetPowerEnable(unsigned long card, unsigned long *flag)
{
	PRX_State *pRX_State ;

	if (!PRX_CheckState(card))
		return NDTS_BFalse ;

	pRX_State = &pRX_StateArray[card];

	*flag = pRX_State->reg_shadow.globalRegs.ultranPowerOnReq;
	return NDTS_BTrue;
}


PRX_API NDTS_Boolean CALL_CONV PRX_SetHVLevel(unsigned long card, unsigned long level)
{
	PRX_State *pRX_State ;
	NDTS_Boolean isParamOK = NDTS_BTrue;
	NDTS_Boolean isOK = NDTS_BTrue;

	if (!PRX_CheckState(card))
		return NDTS_BFalse ;

	pRX_State = &pRX_StateArray[card];

	if (level > PRX_MAX_PULSER_VOLTAGE)
	{
		PRX_HandlePRXError(PRX_SetHVLevel_InvalidLevel) ;
		 isParamOK = NDTS_BFalse;
		 level = PRX_MAX_PULSER_VOLTAGE;

	}

	if (pRX_State->useSonatestMux) PRX_SonatestMuxSetHVLevel(level);

	pRX_State->reg_shadow.globalRegs.pulserVoltage = level;
	isOK = PRX_CommitConditional(card);
	return isOK && isParamOK;
}

PRX_API NDTS_Boolean CALL_CONV PRX_GetHVLevel(unsigned long card, unsigned long *level)
{
	PRX_State *pRX_State ;

	if (!PRX_CheckState(card))
		return NDTS_BFalse ;

	pRX_State = &pRX_StateArray[card];
	
	_ASSERTE(pRX_State->useSonatestMux == NDTS_BFalse);

	*level = pRX_State->reg_shadow.globalRegs.pulserVoltage;
	return NDTS_BTrue;
}

// Lowest level functions - read/write entire block sequence

PRX_API NDTS_Boolean CALL_CONV PRX_SetSequence(unsigned long card, unsigned long blocks, PRX_SequenceTableBlock *pRegs, NDTS_Boolean commit)
{
	PRX_State *pRX_State ;

	if (!PRX_CheckState(card))
		return NDTS_BFalse ;

	pRX_State = &pRX_StateArray[card];

	if (blocks >= SEQUENCE_TABLE_BLOCKS)
	{
		PRX_HandlePRXError(PRX_SetSequence_TooManyBlocks) ;
		return NDTS_BFalse;
	}

	memcpy(pRX_State->reg_shadow.sequence, pRegs, blocks * sizeof(PRX_SequenceTableBlock));

	if (commit)
	{
		return PRX_CommitConditional(card);
	}
	return NDTS_BTrue;
}

PRX_API NDTS_Boolean CALL_CONV PRX_GetSequence(unsigned long card, unsigned long blocks, PRX_SequenceTableBlock *pRegs)
{
	PRX_State *pRX_State ;

	if (!PRX_CheckState(card))
		return NDTS_BFalse ;

	pRX_State = &pRX_StateArray[card];

	if (blocks >= SEQUENCE_TABLE_BLOCKS)
	{
		PRX_HandlePRXError(PRX_GetSequence_TooManyBlocks) ;
		return NDTS_BFalse;
	}

	memcpy(pRegs, pRX_State->reg_shadow.sequence, blocks * sizeof(PRX_SequenceTableBlock));

	return NDTS_BTrue;
}

// Mid level functions - read/write one sequence block at a time

PRX_API NDTS_Boolean CALL_CONV PRX_SetSequenceBlock(unsigned long card, unsigned long block, PRX_SequenceTableBlock *pRegs, NDTS_Boolean commit)
{
	PRX_State *pRX_State ;

	if (!PRX_CheckState(card))
		return NDTS_BFalse ;

	pRX_State = &pRX_StateArray[card];

	if (block >= SEQUENCE_TABLE_BLOCKS)
	{
		PRX_HandlePRXError(PRX_InvalidBlockNumber) ;
		return NDTS_BFalse;
	}

	memcpy(&pRX_State->reg_shadow.sequence[block], pRegs, sizeof(PRX_SequenceTableBlock));

	if (commit)
	{
		return PRX_CommitConditional(card);
	}
	return NDTS_BTrue;
}

PRX_API NDTS_Boolean CALL_CONV PRX_GetSequenceBlock(unsigned long card, unsigned long block, PRX_SequenceTableBlock *pRegs)
{
	PRX_State *pRX_State ;

	if (!PRX_CheckState(card))
		return NDTS_BFalse ;

	pRX_State = &pRX_StateArray[card];

	if (block >= SEQUENCE_TABLE_BLOCKS)
	{
		PRX_HandlePRXError(PRX_InvalidBlockNumber) ;
		return NDTS_BFalse;
	}

	memcpy(pRegs, &pRX_State->reg_shadow.sequence[block], sizeof(PRX_SequenceTableBlock));

	return NDTS_BTrue;
}

// Higher level functions - read/write individual values to a sequence block.  These always commit imediately
// since they are designed for use by the UI

PRX_API NDTS_Boolean CALL_CONV PRX_SetTransmitPulseEnable(unsigned long card, unsigned long block, unsigned long flags)
{
	PRX_State *pRX_State ;

	if (!PRX_CheckState(card))
		return NDTS_BFalse ;

	pRX_State = &pRX_StateArray[card];

	if (block >= SEQUENCE_TABLE_BLOCKS)
	{
		PRX_HandlePRXError(PRX_InvalidBlockNumber) ;
		return NDTS_BFalse;
	}

	if (flags > 0xff)
	{
		PRX_HandlePRXError(PRX_SetTransmitPulseEnable_InvalidFlags) ;
		return NDTS_BFalse;
	}
	

	if (pRX_State->useSonatestMux)
	{
		PRX_SonatestMuxSetTransmitPulseEnable(block, flags);
		// Always transmit on channel 1 when using Sonatest Mux.
		pRX_State->reg_shadow.sequence[block].transmitPulseEnable = 1;
	}
	else
	{
		pRX_State->reg_shadow.sequence[block].transmitPulseEnable = flags;
	}

	pRX_State->reg_shadow.sequence[block].transmitPulseEnable = flags;
	return PRX_CommitConditional(card);
}

PRX_API NDTS_Boolean CALL_CONV PRX_GetTransmitPulseEnable(unsigned long card, unsigned long block, unsigned long *flags)
{
	PRX_State *pRX_State ;

	if (!PRX_CheckState(card))
		return NDTS_BFalse ;

	pRX_State = &pRX_StateArray[card];

	_ASSERTE(!pRX_State->useSonatestMux);  // This SDK function not valid with Sonatest Mux as reg_shadow is not up to date

	if (block >= SEQUENCE_TABLE_BLOCKS)
	{
		PRX_HandlePRXError(PRX_InvalidBlockNumber) ;
		return NDTS_BFalse;
	}

	*flags = pRX_State->reg_shadow.sequence[block].transmitPulseEnable;
	return NDTS_BTrue;
}

PRX_API NDTS_Boolean CALL_CONV PRX_SetStepSkipCount(unsigned long card, unsigned long block, unsigned long value)
{
	PRX_State *pRX_State ;

	if (!PRX_CheckState(card))
		return NDTS_BFalse ;

	pRX_State = &pRX_StateArray[card];

	if (block >= SEQUENCE_TABLE_BLOCKS)
	{
		PRX_HandlePRXError(PRX_SetStepSkipCount_InvalidCount) ;
		return NDTS_BFalse;
	}

	if (value > PRX_MAX_STEP_SKIP_COUNT)
	{
		PRX_HandlePRXError(PRX_SetReceiveChannel_InvalidChannel) ;
		return NDTS_BFalse;
	}

	pRX_State->reg_shadow.sequence[block].stepSkip = value;
	return PRX_CommitConditional(card);
}

PRX_API NDTS_Boolean CALL_CONV PRX_GetStepSkipCount(unsigned long card, unsigned long block, unsigned long *value)
{
	PRX_State *pRX_State ;

	if (!PRX_CheckState(card))
		return NDTS_BFalse ;

	pRX_State = &pRX_StateArray[card];

	if (block >= SEQUENCE_TABLE_BLOCKS)
	{
		PRX_HandlePRXError(PRX_InvalidBlockNumber) ;
		return NDTS_BFalse;
	}

	*value = pRX_State->reg_shadow.sequence[block].stepSkip;
	return NDTS_BTrue;
}

PRX_API NDTS_Boolean CALL_CONV PRX_SetReceiveChannel(unsigned long card, unsigned long block, unsigned long channel)
{
	PRX_State *pRX_State ;

	if (!PRX_CheckState(card))
		return NDTS_BFalse ;

	pRX_State = &pRX_StateArray[card];

	if (block >= SEQUENCE_TABLE_BLOCKS)
	{
		PRX_HandlePRXError(PRX_InvalidBlockNumber) ;
		return NDTS_BFalse;
	}

	if (channel > 7)
	{
		PRX_HandlePRXError(PRX_SetReceiveChannel_InvalidChannel) ;
		return NDTS_BFalse;
	}

	if (pRX_State->useSonatestMux)
	{
		PRX_SonatestMuxSetReceiveChannel(block, channel);
		// Always receive at the PRX on the first channel when using Sonatest Mux.
		pRX_State->reg_shadow.sequence[block].rxChannelSelect = 0;
	}
	else
	{
		pRX_State->reg_shadow.sequence[block].rxChannelSelect = channel;
	}
	return PRX_CommitConditional(card);
}

PRX_API NDTS_Boolean CALL_CONV PRX_GetReceiveChannel(unsigned long card, unsigned long block, unsigned long *channel)
{
	PRX_State *pRX_State ;

	if (!PRX_CheckState(card))
		return NDTS_BFalse ;

	pRX_State = &pRX_StateArray[card];
	
	_ASSERTE(!pRX_State->useSonatestMux);  // Shadow regs not up to date so function not supported.

	if (block >= SEQUENCE_TABLE_BLOCKS)
	{
		PRX_HandlePRXError(PRX_InvalidBlockNumber) ;
		return NDTS_BFalse;
	}

	*channel = pRX_State->reg_shadow.sequence[block].rxChannelSelect;
	return NDTS_BTrue;
}

PRX_API NDTS_Boolean CALL_CONV PRX_SetMpxGain(unsigned long card, unsigned long block, unsigned long gain)
{
	PRX_State *pRX_State ;

	if (!PRX_CheckState(card))
		return NDTS_BFalse ;

	pRX_State = &pRX_StateArray[card];

	if (block >= SEQUENCE_TABLE_BLOCKS)
	{
		PRX_HandlePRXError(PRX_InvalidBlockNumber) ;
		return NDTS_BFalse;
	}

	pRX_State->reg_shadow.sequence[block].mpxGain = gain ? 1 : 0;
	return PRX_CommitConditional(card);
}

PRX_API NDTS_Boolean CALL_CONV PRX_GetMpxGain(unsigned long card, unsigned long block, unsigned long *gain)
{
	PRX_State *pRX_State ;

	if (!PRX_CheckState(card))
		return NDTS_BFalse ;

	pRX_State = &pRX_StateArray[card];

	if (block >= SEQUENCE_TABLE_BLOCKS)
	{
		PRX_HandlePRXError(PRX_InvalidBlockNumber) ;
		return NDTS_BFalse;
	}

	*gain = pRX_State->reg_shadow.sequence[block].mpxGain;
	return NDTS_BTrue;
}

PRX_API NDTS_Boolean CALL_CONV PRX_SetNumberOfSequenceSteps(unsigned long card, unsigned long nSteps)
{
	PRX_State *pRX_State ;
	unsigned long i ;

	if (!PRX_CheckState(card))
		return NDTS_BFalse ;

	pRX_State = &pRX_StateArray[card];

	if (nSteps > SEQUENCE_TABLE_BLOCKS)
	{
		PRX_HandlePRXError(PRX_InvalidBlockNumber) ;
		return NDTS_BFalse;
	}

	for (i = 0; i < nSteps; i++)
	{
		pRX_State->reg_shadow.sequence[i].sequenceFlag = 1;
	}

	pRX_State->reg_shadow.sequence[nSteps].sequenceFlag = 0;

	pRX_State->numberOfSequenceSteps = nSteps;

	if (pRX_State->useSonatestMux) PRX_SonatestMuxSetNumberOfSequenceSteps(nSteps);

	return PRX_CommitConditional(card);
}

PRX_API NDTS_Boolean CALL_CONV PRX_GetNumberOfSequenceSteps(unsigned long card, unsigned long *pnSteps)
{
	PRX_State *pRX_State ;

	if (!PRX_CheckState(card))
		return NDTS_BFalse ;

	pRX_State = &pRX_StateArray[card];

	*pnSteps = pRX_State->numberOfSequenceSteps;

	return NDTS_BTrue;
}

PRX_API NDTS_Boolean CALL_CONV PRX_SetFineGain(unsigned long card, unsigned long block, unsigned long gain)
{
	PRX_State *pRX_State ;

	if (!PRX_CheckState(card))
		return NDTS_BFalse ;

	pRX_State = &pRX_StateArray[card];

	if (block >= SEQUENCE_TABLE_BLOCKS)
	{
		PRX_HandlePRXError(PRX_InvalidBlockNumber) ;
		return NDTS_BFalse;
	}

	if (gain > PRX_MAX_FINE_GAIN)
	{
		PRX_HandlePRXError(PRX_SetFineGain_InvalidGain) ;
		return NDTS_BFalse;
	}

	pRX_State->reg_shadow.sequence[block].fineGainControl = gain;
	return PRX_CommitConditional(card);
}

PRX_API NDTS_Boolean CALL_CONV PRX_GetFineGain(unsigned long card, unsigned long block, unsigned long *gain)
{
	PRX_State *pRX_State ;

	if (!PRX_CheckState(card))
		return NDTS_BFalse ;

	pRX_State = &pRX_StateArray[card];

	if (block >= SEQUENCE_TABLE_BLOCKS)
	{
		PRX_HandlePRXError(PRX_InvalidBlockNumber) ;
		return NDTS_BFalse;
	}

	*gain = pRX_State->reg_shadow.sequence[block].fineGainControl;
	return NDTS_BTrue;
}

PRX_API NDTS_Boolean CALL_CONV PRX_SetExternalTCG(unsigned long card, unsigned long block, unsigned long flag)
{
	PRX_State *pRX_State ;

	if (!PRX_CheckState(card))
		return NDTS_BFalse ;

	pRX_State = &pRX_StateArray[card];

	if (block >= SEQUENCE_TABLE_BLOCKS)
	{
		PRX_HandlePRXError(PRX_InvalidBlockNumber) ;
		return NDTS_BFalse;
	}

	pRX_State->reg_shadow.sequence[block].externalTCGSelect = flag ? 1 : 0;
	return PRX_CommitConditional(card);
}

PRX_API NDTS_Boolean CALL_CONV PRX_GetExternalTCG(unsigned long card, unsigned long block, unsigned long *flag)
{
	PRX_State *pRX_State ;

	if (!PRX_CheckState(card))
		return NDTS_BFalse ;

	pRX_State = &pRX_StateArray[card];

	if (block >= SEQUENCE_TABLE_BLOCKS)
	{
		PRX_HandlePRXError(PRX_InvalidBlockNumber) ;
		return NDTS_BFalse;
	}

	*flag = pRX_State->reg_shadow.sequence[block].externalTCGSelect;
	return NDTS_BTrue;
}

PRX_API NDTS_Boolean CALL_CONV PRX_SetBackendLowpassFilter(unsigned long card, unsigned long block, PRX_BackEndLowPassFilter value)
{
	PRX_State *pRX_State ;

	if (!PRX_CheckState(card))
		return NDTS_BFalse ;

	pRX_State = &pRX_StateArray[card];

	if (block >= SEQUENCE_TABLE_BLOCKS)
	{
		PRX_HandlePRXError(PRX_InvalidBlockNumber) ;
		return NDTS_BFalse;
	}

	if (value > PRX_MAX_BACKEND_FILTER_VALUE)
	{
		PRX_HandlePRXError(PRX_SetBackendLowpassFilter_InvalidLevel);
		return NDTS_BFalse;
	}

//	froglog("PRX_SetBackEndLowpassFilter App sent %d writing %d", value, 7-value) ;

	value = 7 - value; // back-end filter indices are reversed

	pRX_State->reg_shadow.sequence[block].backendLowpassFilter = value;
	return PRX_CommitConditional(card);
}

PRX_API NDTS_Boolean CALL_CONV PRX_GetBackendLowpassFilter(unsigned long card, unsigned long block, PRX_BackEndLowPassFilter *value)
{
	PRX_State *pRX_State ;

	if (!PRX_CheckState(card))
		return NDTS_BFalse ;

	pRX_State = &pRX_StateArray[card];

	if (block >= SEQUENCE_TABLE_BLOCKS)
	{
		PRX_HandlePRXError(PRX_InvalidBlockNumber) ;
		return NDTS_BFalse;
	}

	*value = 7 - pRX_State->reg_shadow.sequence[block].backendLowpassFilter;
	// back-end filter indices are reversed
	return NDTS_BTrue;
}

PRX_API NDTS_Boolean CALL_CONV PRX_SetBackendHighpassFilter(unsigned long card, unsigned long block, PRX_BackEndHighPassFilter value)
{
	PRX_State *pRX_State ;

	if (!PRX_CheckState(card))
		return NDTS_BFalse ;

	pRX_State = &pRX_StateArray[card];

	if (block >= SEQUENCE_TABLE_BLOCKS)
	{
		PRX_HandlePRXError(PRX_InvalidBlockNumber) ;
		return NDTS_BFalse;
	}

	if (value > PRX_MAX_BACKEND_FILTER_VALUE)
	{
		PRX_HandlePRXError(PRX_SetBackendHighpassFilter_InvalidLevel);
		return NDTS_BFalse;
	}

//	froglog("PRX_SetBackEndHighpassFilter App sent %d writing %d", 	value, 7-value) ;
	value = 7 - value; // back-end filter indices are reversed

	pRX_State->reg_shadow.sequence[block].backendHighpassFilter = value;
	return PRX_CommitConditional(card);
}

PRX_API NDTS_Boolean CALL_CONV PRX_GetBackendHighpassFilter(unsigned long card, unsigned long block, PRX_BackEndHighPassFilter *value)
{
	PRX_State *pRX_State ;

	if (!PRX_CheckState(card))
		return NDTS_BFalse ;

	pRX_State = &pRX_StateArray[card];

	if (block >= SEQUENCE_TABLE_BLOCKS)
	{
		PRX_HandlePRXError(PRX_InvalidBlockNumber) ;
		return NDTS_BFalse;
	}

	// back-end filter indices are reversed
	*value = 7 - pRX_State->reg_shadow.sequence[block].backendHighpassFilter;
	return NDTS_BTrue;
}

PRX_API NDTS_Boolean CALL_CONV PRX_SetFrontendLowpassFilter(unsigned long card, unsigned long block, PRX_FrontEndLowPassFilter value)
{
	PRX_State *pRX_State ;

	if (!PRX_CheckState(card))
		return NDTS_BFalse ;

	pRX_State = &pRX_StateArray[card];

	if (block >= SEQUENCE_TABLE_BLOCKS)
	{
		PRX_HandlePRXError(PRX_InvalidBlockNumber) ;
		return NDTS_BFalse;
	}

	if (value > PRX_MAX_FRONTEND_FILTER_VALUE)
	{
		PRX_HandlePRXError(PRX_SetFrontendLowpassFilter_InvalidLevel);
		return NDTS_BFalse;
	}
	
//	froglog("PRX_SetFrontendLowpassFilter App sent %d writing %d", 	value, pRX_State->frontEndLowPassLookup[value]) ;

	pRX_State->reg_shadow.sequence[block].frontendLowpassFilter = pRX_State->frontEndLowPassLookup[value] ;
	return PRX_CommitConditional(card);
}

PRX_API NDTS_Boolean CALL_CONV PRX_GetFrontendLowpassFilter(unsigned long card, unsigned long block, PRX_FrontEndLowPassFilter *value)
{
	PRX_State *pRX_State ;

	if (!PRX_CheckState(card))
		return NDTS_BFalse ;

	pRX_State = &pRX_StateArray[card];

	if (block >= SEQUENCE_TABLE_BLOCKS)
	{
		PRX_HandlePRXError(PRX_InvalidBlockNumber) ;
		return NDTS_BFalse;
	}

/*	froglog("PRX_GetFrontendLowpassFilter Reg contents %d returning %d", 
		pRX_State->reg_shadow.sequence[block].frontendLowpassFilter, 
		pRX_State->frontEndLowPassReverseLookup[pRX_State->reg_shadow.sequence[block].frontendLowpassFilter]) ;
*/
	*value = pRX_State->frontEndLowPassReverseLookup[pRX_State->reg_shadow.sequence[block].frontendLowpassFilter];
	return NDTS_BTrue;
}

PRX_API NDTS_Boolean CALL_CONV PRX_SetFrontendHighpassFilter(unsigned long card, unsigned long block, PRX_FrontEndHighPassFilter value)
{
	PRX_State *pRX_State ;

	if (!PRX_CheckState(card))
		return NDTS_BFalse ;

	pRX_State = &pRX_StateArray[card];

	if (block >= SEQUENCE_TABLE_BLOCKS)
	{
		PRX_HandlePRXError(PRX_InvalidBlockNumber) ;
		return NDTS_BFalse;
	}

	if (value > PRX_MAX_FRONTEND_FILTER_VALUE)
	{
		PRX_HandlePRXError(PRX_SetFrontendHighpassFilter_InvalidLevel);
		return NDTS_BFalse;
	}

//	froglog("PRX_SetFrontendHighpassFilter App sent %d writing %d", value, pRX_State->frontEndHighPassLookup[value]) ;

	pRX_State->reg_shadow.sequence[block].frontendHighpassFilter = pRX_State->frontEndHighPassLookup[value];
	return PRX_CommitConditional(card);
}

PRX_API NDTS_Boolean CALL_CONV PRX_GetFrontendHighpassFilter(unsigned long card, unsigned long block, PRX_FrontEndHighPassFilter *value)
{
	PRX_State *pRX_State ;

	if (!PRX_CheckState(card))
		return NDTS_BFalse ;

	pRX_State = &pRX_StateArray[card];

	if (block >= SEQUENCE_TABLE_BLOCKS)
	{
		PRX_HandlePRXError(PRX_InvalidBlockNumber) ;
		return NDTS_BFalse;
	}

	/*froglog("PRX_GetFrontendHighpassFilter Reg contents %d returning %d", 
		pRX_State->reg_shadow.sequence[block].frontendHighpassFilter, 
		pRX_State->frontEndHighPassReverseLookup[pRX_State->reg_shadow.sequence[block].frontendHighpassFilter]) ;
*/
	*value = pRX_State->frontEndHighPassReverseLookup[pRX_State->reg_shadow.sequence[block].frontendHighpassFilter];
	return NDTS_BTrue;
}


PRX_API NDTS_Boolean CALL_CONV PRX_SetInputImpedance(unsigned long card, unsigned long block, unsigned long value)
{
	PRX_State *pRX_State ;

	if (!PRX_CheckState(card))
		return NDTS_BFalse ;

	pRX_State = &pRX_StateArray[card];

	if (block >= SEQUENCE_TABLE_BLOCKS)
	{
		PRX_HandlePRXError(PRX_InvalidBlockNumber) ;
		return NDTS_BFalse;
	}

	if (value > PRX_MAX_IMPEDANCE_SETTING)
	{
		PRX_HandlePRXError(PRX_SetInputImpedance_InvalidLevel);
		return NDTS_BFalse;
	}

	if (pRX_State->useSonatestMux) 
	{
		PRX_SonatestMuxSetInputImpedance(block, value);
		// Force input impedance to 50 Ohms to match diff to single end converter
		pRX_State->reg_shadow.sequence[block].impedanceselect = 4;
	}
	else
	{
		pRX_State->reg_shadow.sequence[block].impedanceselect = value;
	}
	return PRX_CommitConditional(card);
}

PRX_API NDTS_Boolean CALL_CONV PRX_GetInputImpedance(unsigned long card, unsigned long block, unsigned long *value)
{
	PRX_State *pRX_State ;

	if (!PRX_CheckState(card))
		return NDTS_BFalse ;

	pRX_State = &pRX_StateArray[card];

	if (block >= SEQUENCE_TABLE_BLOCKS)
	{
		PRX_HandlePRXError(PRX_InvalidBlockNumber) ;
		return NDTS_BFalse;
	}

	// This SDK call is not supported with Sonatest Mux as input impedance is forced to 50 Ohm on PRX
	_ASSERTE(pRX_StateArray[card].useSonatestMux == NDTS_BFalse);

	*value = pRX_State->reg_shadow.sequence[block].impedanceselect;
	return NDTS_BTrue;
}

PRX_API NDTS_Boolean CALL_CONV PRX_SetCoarseGain(unsigned long card, unsigned long block, unsigned long value)
{
	PRX_State *pRX_State ;

	if (!PRX_CheckState(card))
		return NDTS_BFalse ;

	pRX_State = &pRX_StateArray[card];

	if (block >= SEQUENCE_TABLE_BLOCKS)
	{
		PRX_HandlePRXError(PRX_InvalidBlockNumber) ;
		return NDTS_BFalse;
	}

	if (value > PRX_MAX_COARSE_GAIN)
	{
		PRX_HandlePRXError(PRX_SetCoarseGain_InvalidLevel);
		return NDTS_BFalse;
	}

	pRX_State->reg_shadow.sequence[block].coarseGain = value;
	return PRX_CommitConditional(card);
}

PRX_API NDTS_Boolean CALL_CONV PRX_GetCoarseGain(unsigned long card, unsigned long block, unsigned long *value)
{
	PRX_State *pRX_State ;

	if (!PRX_CheckState(card))
		return NDTS_BFalse ;

	pRX_State = &pRX_StateArray[card];

	if (block >= SEQUENCE_TABLE_BLOCKS)
	{
		PRX_HandlePRXError(PRX_InvalidBlockNumber) ;
		return NDTS_BFalse;
	}

	*value = pRX_State->reg_shadow.sequence[block].coarseGain;
	return NDTS_BTrue;
}

PRX_API NDTS_Boolean CALL_CONV PRX_SetOversamplingDelay(unsigned long card, unsigned long block, unsigned long jiffies)
{
	PRX_State *pRX_State ;

	if (!PRX_CheckState(card))
		return NDTS_BFalse ;

	pRX_State = &pRX_StateArray[card];

	if (block >= SEQUENCE_TABLE_BLOCKS)
	{
		PRX_HandlePRXError(PRX_InvalidBlockNumber) ;
		return NDTS_BFalse;
	}

	if (jiffies > PRX_MAX_OVERSAMPLING_DELAY)
	{
		PRX_HandlePRXError(PRX_SetOversamplingDelay_InvalidLength);
		return NDTS_BFalse;
	}

	pRX_State->reg_shadow.sequence[block].oversamplingFineDelay = jiffies;
	return PRX_CommitConditional(card);
}

PRX_API NDTS_Boolean CALL_CONV PRX_GetOversamplingDelay(unsigned long card, unsigned long block, unsigned long *jiffies)
{
	PRX_State *pRX_State ;

	if (!PRX_CheckState(card))
		return NDTS_BFalse ;

	pRX_State = &pRX_StateArray[card];

	if (block >= SEQUENCE_TABLE_BLOCKS)
	{
		PRX_HandlePRXError(PRX_InvalidBlockNumber) ;
		return NDTS_BFalse;
	}

	*jiffies = pRX_State->reg_shadow.sequence[block].oversamplingFineDelay;
	return NDTS_BTrue;
}


PRX_API NDTS_Boolean CALL_CONV PRX_SetNumberOfOversamples(unsigned long card, unsigned long block, unsigned long numberOfOversamples)
{
	PRX_State *pRX_State ;

	if (!PRX_CheckState(card))
		return NDTS_BFalse ;

	pRX_State = &pRX_StateArray[card];

	if (block >= SEQUENCE_TABLE_BLOCKS)
	{
		PRX_HandlePRXError(PRX_InvalidBlockNumber) ;
		return NDTS_BFalse;
	}

	if (numberOfOversamples > PRX_MAX_OVERSAMPLING_SAMPLES)
	{
		PRX_HandlePRXError(PRX_SetNumberOfOversamples_InvalidNumberOfOversamples);
		return NDTS_BFalse;
	}

	pRX_State->reg_shadow.sequence[block].numberOfOversamples = numberOfOversamples - 1;
	return PRX_CommitConditional(card);
}

PRX_API NDTS_Boolean CALL_CONV PRX_GetNumberOfOversamples(unsigned long card, unsigned long block, unsigned long *numberOfOversamples)
{
	PRX_State *pRX_State ;

	if (!PRX_CheckState(card))
		return NDTS_BFalse ;

	pRX_State = &pRX_StateArray[card];

	if (block >= SEQUENCE_TABLE_BLOCKS)
	{
		PRX_HandlePRXError(PRX_InvalidBlockNumber) ;
		return NDTS_BFalse;
	}

	*numberOfOversamples = pRX_State->reg_shadow.sequence[block].numberOfOversamples + 1;
	return NDTS_BTrue;
}

PRX_API NDTS_Boolean CALL_CONV PRX_SetPulseWidth(unsigned long card, unsigned long block, unsigned long fiveNanoSecTicks)
{
	PRX_State *pRX_State ;

	if (!PRX_CheckState(card))
		return NDTS_BFalse ;

	pRX_State = &pRX_StateArray[card];

	if (block >= SEQUENCE_TABLE_BLOCKS)
	{
		PRX_HandlePRXError(PRX_InvalidBlockNumber) ;
		return NDTS_BFalse;
	}

	if (fiveNanoSecTicks > PRX_MAX_PULSE_WIDTH)
	{
		PRX_HandlePRXError(PRX_SetPulseWidth_InvalidWidth);
		return NDTS_BFalse;
	}

	pRX_State->reg_shadow.sequence[block].pulseWidth = fiveNanoSecTicks;

	// Allow the pulse width to pass to the PRX as normal - it is only used for FET duty cycle calcs in RQ/PRX combo.
	if (pRX_State->useSonatestMux) PRX_SonatestMuxSetPulseWidth(block, fiveNanoSecTicks);

	return PRX_CommitConditional(card);
}

PRX_API NDTS_Boolean CALL_CONV PRX_GetPulseWidth(unsigned long card, unsigned long block, unsigned long *fiveNanoSecticks)
{
	PRX_State *pRX_State ;

	if (!PRX_CheckState(card))
		return NDTS_BFalse ;

	pRX_State = &pRX_StateArray[card];

	if (block >= SEQUENCE_TABLE_BLOCKS)
	{
		PRX_HandlePRXError(PRX_InvalidBlockNumber) ;
		return NDTS_BFalse;
	}

	*fiveNanoSecticks = pRX_State->reg_shadow.sequence[block].pulseWidth;
	return NDTS_BTrue;
}

PRX_API NDTS_Boolean CALL_CONV PRX_SetPulseTrainLength(unsigned long card, unsigned long block, unsigned long value)
{
	PRX_State *pRX_State ;

	if (!PRX_CheckState(card))
		return NDTS_BFalse ;

	pRX_State = &pRX_StateArray[card];

	if (block >= SEQUENCE_TABLE_BLOCKS)
	{
		PRX_HandlePRXError(PRX_InvalidBlockNumber) ;
		return NDTS_BFalse;
	}

	if ((value - 1) > PRX_MAX_PULSE_TRAIN_LENGTH)
	{
		PRX_HandlePRXError(PRX_SetPulseTrainLength_InvalidLength);
		return NDTS_BFalse;
	}

	pRX_State->reg_shadow.sequence[block].pulseTrain = value - 1;
	return PRX_CommitConditional(card);
}

PRX_API NDTS_Boolean CALL_CONV PRX_GetPulseTrainLength(unsigned long card, unsigned long block, unsigned long *value)
{
	PRX_State *pRX_State ;

	if (!PRX_CheckState(card))
		return NDTS_BFalse ;

	pRX_State = &pRX_StateArray[card];

	if (block >= SEQUENCE_TABLE_BLOCKS)
	{
		PRX_HandlePRXError(PRX_InvalidBlockNumber) ;
		return NDTS_BFalse;
	}

	*value = pRX_State->reg_shadow.sequence[block].pulseTrain + 1;
	return NDTS_BTrue;
}

PRX_API NDTS_Boolean CALL_CONV PRX_SetMasterRepetitionTime(unsigned long card, unsigned long ticks)
{
	PRX_State *pRX_State ;

	if (!PRX_CheckState(card))
		return NDTS_BFalse ;

	pRX_State = &pRX_StateArray[card];

	pRX_State->masterRepetitionTime = ticks;
	// Do a commit to give the software pulser overload protection a chance to check the new rep rate.
	return PRX_CommitConditional(card);
}

PRX_API NDTS_Boolean CALL_CONV PRX_SetSoftwareFETOverloadProtectionEnabled(unsigned long card, NDTS_Boolean isSoftwareFETOverloadProtectionEnabled)
{
	PRX_State *pRX_State ;

	if (!PRX_CheckState(card))
		return NDTS_BFalse ;

	pRX_State = &pRX_StateArray[card];
	pRX_State->isSoftwareFETOverloadProtectionEnabled = isSoftwareFETOverloadProtectionEnabled;
	if (isSoftwareFETOverloadProtectionEnabled)
		PRX_Commit(card); 
	return NDTS_BTrue;
}

PRX_API NDTS_Boolean CALL_CONV PRX_GetSoftwareFETOverloadOutputMask(unsigned long card, unsigned long *softwareFETOverloadOutputMask)
{
	PRX_State *pRX_State ;

	if (!PRX_CheckState(card))
		return NDTS_BFalse ;

	pRX_State = &pRX_StateArray[card];

	*softwareFETOverloadOutputMask = pRX_State->softwareFETOverloadOutputMask;
	return NDTS_BTrue;
}


PRX_API NDTS_Boolean CALL_CONV PRX_SetLimitPulseWidthCallback(unsigned long card, PRX_limitPulseWidthCallback pfn)
{
	PRX_State *pRX_State ;

	if (!PRX_CheckState(card))
		return NDTS_BFalse ;

	pRX_State = &pRX_StateArray[card];

	pRX_State->pLimitPulseWidthCallback = pfn;
	return NDTS_BTrue;
}

PRX_API NDTS_Boolean CALL_CONV PRX_SetRepetitionTime(unsigned long card, unsigned long block, unsigned long ticks)
{
	PRX_State *pRX_State ;

	if (!PRX_CheckState(card))
		return NDTS_BFalse ;

	pRX_State = &pRX_StateArray[card];

	if (block >= SEQUENCE_TABLE_BLOCKS)
	{
		PRX_HandlePRXError(PRX_InvalidBlockNumber) ;
		return NDTS_BFalse;
	}

	if (ticks > PRX_MAX_REPETITION_DELAY)
	{
		PRX_HandlePRXError(PRX_SetRepetitionTime_InvalidLength);
		return NDTS_BFalse;
	}

	pRX_State->reg_shadow.sequence[block].repetitionDelay = ticks;
	return PRX_CommitConditional(card);
}

PRX_API NDTS_Boolean CALL_CONV PRX_GetRepetitionTime(unsigned long card, unsigned long block, unsigned long *ticks)
{
	PRX_State *pRX_State ;

	if (!PRX_CheckState(card))
		return NDTS_BFalse ;

	pRX_State = &pRX_StateArray[card];

	if (block >= SEQUENCE_TABLE_BLOCKS)
	{
		PRX_HandlePRXError(PRX_InvalidBlockNumber) ;
		return NDTS_BFalse;
	}

	*ticks = pRX_State->reg_shadow.sequence[block].repetitionDelay;
	return NDTS_BTrue;
}

BOOL APIENTRY DllMain(HANDLE hModule, DWORD ul_reason_for_call, LPVOID lpReserved)
{
	(void)lpReserved;
	(void)hModule;

    switch (ul_reason_for_call)
	{
		case DLL_PROCESS_ATTACH :
			break ;
		case DLL_PROCESS_DETACH :
			break ;
		case DLL_THREAD_ATTACH :
			break ;
		case DLL_THREAD_DETACH :
			break ;
    }

    return TRUE ;
}

PRX_API NDTS_Boolean CALL_CONV PRX_Commit(unsigned long card)
{
	unsigned long status ;
	NDTS_Boolean isOK ;
	unsigned long i;
	// 1. copy shadow memory to system FPGA memory
	// 2. hit the bit to transfer to control FPGA
	PRX_State *pRX_State ;

	if (!PRX_CheckState(card))
		return NDTS_BFalse ;

	pRX_State = &pRX_StateArray[card];

	if (pRX_State->isSoftwareFETOverloadProtectionEnabled)
		if (!PRX_LimitPulseWidth(card))
			return NDTS_BFalse;

	isOK = PRX_SetSystemRegister(card, PRX_SYSTEM_GLOBAL_CONFIG_REGISTER, *(unsigned long *)&pRX_State->reg_shadow.globalRegs) ;
	if (!isOK)
		return NDTS_BFalse ;

	for (i = 0; i < (sizeof(PRX_SequenceTableBlock) * SEQUENCE_TABLE_BLOCKS) / 4; i++)
	{
		unsigned long value = *((unsigned long*)(&(pRX_State->reg_shadow.sequence)) + i);
		if (!NDTS_HAL_SetWord(NDTS_SIDPRX, card, PRX_SEQUENCE_TABLE_OFFSET + i*4, value))
			return NDTS_BFalse ;
	}
	
	//	PRX_SequenceTableBlock *pTable = (PRX_SequenceTableBlock *)(pciState->pPCIBar2VirtualAddress + PRX_SEQUENCE_TABLE_OFFSET);
	//memcpy(pTable, pRX_State->reg_shadow.sequence, sizeof(PRX_SequenceTableBlock) * SEQUENCE_TABLE_BLOCKS) ;
	
	isOK = PRX_SetSystemRegister(card, PRX_SYSTEM_COMMAND_REGISTER, 0x00000008) ;
	if (!isOK)
		return NDTS_BFalse ;
	
	do 
	{
		Sleep(1);
		isOK = PRX_GetSystemRegister(card, PRX_SYSTEM_STATUS_REGISTER, &status);
		if (!isOK)
			return NDTS_BFalse ;
	} 
	while (status & 0x00000004);

	return NDTS_BTrue;
}


PRX_API NDTS_Boolean CALL_CONV PRX_SetManualCommit(unsigned long card, NDTS_Boolean isManual)
{
	PRX_State *pRX_State ;

	if (!PRX_CheckState(card))
		return NDTS_BFalse ;

	pRX_State = &pRX_StateArray[card];

	pRX_State->isManualCommit = isManual;
	return NDTS_BTrue;
}

PRX_API NDTS_Boolean CALL_CONV PRX_SetAutoResetForFETOverload(unsigned long card, NDTS_Boolean isAutoReset)
{
	PRX_State *pRX_State ;

	if (!PRX_CheckState(card))
		return NDTS_BFalse ;

	pRX_State = &pRX_StateArray[card];

	pRX_State->reg_shadow.globalRegs.autoResetOnOverload = (isAutoReset) ? 0 : 1;
	return PRX_CommitConditional(card);
}

PRX_API NDTS_Boolean CALL_CONV PRX_ResetFETOverload(unsigned long card)
{
	if (!PRX_CheckState(card))
		return NDTS_BFalse ;

	return PRX_SetSystemRegister(card, PRX_SYSTEM_COMMAND_REGISTER, 0x00000010) ;
}

PRX_API NDTS_Boolean CALL_CONV PRX_GetFETOverloadStatus(unsigned long card, NDTS_Boolean *isOverload)
{
	unsigned long status ;
	NDTS_Boolean isOK ;

	if (!PRX_CheckState(card))
		return NDTS_BFalse ;

	isOK = PRX_GetSystemRegister(card, PRX_SYSTEM_STATUS_REGISTER, &status);
	if (!isOK)
		return NDTS_BFalse ;

	status &= 0x00000010;
	*isOverload = status >> 4;
	return NDTS_BTrue;
}


/*********************************
 ** Daniel 14/11/06.  PRX_GetIsSafe has not been finished off or tested.  
 ** Tom says it is better to use the firmware or emulate what the firmware is doing
 ** The PRX_GetIsSafe calls from the interface functions
 ** were removed in revision 120.

// FROG the intention here is that a register shadow is presented to this function and it will return true
// if the safe operating area contstraints are met.
// Generally a register shadow should be prepared and checked before it is committed; if it fails the check
// then the old register shadow should be retained
PRX_API int CALL_CONV PRX_GetIsSafe(PRX_Shadow *pReg_shadow)
{
	float load = PRX_GetMaxFETLoadForShadow(pReg_shadow); 
	if (load > 1.0)
	{
		PRX_HandlePRXError(PRX_GetIsSafe_FETLoadTooLarge) ;
		return NDTS_BFalse;
	}
	else
	{
		return NDTS_BTrue;
	}
}

static float PRX_GetMaxFETLoadForShadow(PRX_Shadow *pReg_shadow)
{
	int channel;
	float maxLoad = 0.0;

	for (channel = 0; channel < PRX_CHANNELS; channel++)
	{
		float thisLoad = PRX_GetFETLoadForShadow(pReg_shadow, channel);
		if (thisLoad > maxLoad) maxLoad = thisLoad;
	}

	return maxLoad;
}

PRX_API float CALL_CONV PRX_GetFETLoad(unsigned long card, unsigned long channel)
{
	PRX_State *pRX_State = &pRX_StateArray[card];
	if (card >= prxs_found) return 0.0;

	return PRX_GetFETLoadForShadow(&pRX_State->reg_shadow, channel);
}

static float PRX_GetFETLoadForShadow(PRX_Shadow *pReg_shadow, unsigned long channel)
{
	float loadResistance = 50.0f;
	float power, pulserVoltage, DCPower, fetVoltageDrop, current, ticksForAllSequences;
	int sequenceSteps = 0;
	int step;
	int onTicks = 0;
	int stepCount = 0;
	static int logLevel = NDTS_LLNever;

	pulserVoltage = (float)pReg_shadow->globalRegs.pulserVoltage * (400.0f / (float)0xfff);

	// Calculate the power dissipation for the DC state (this is often very large)
	// For pulser voltages below 250V the load determines the current
	// For higher voltages the FET limits at 3.5A
	// The FET is claimed to have an on resistance of 5.0R but experimentally
	// it seems somewhat higher than this so we use 10.0R in our calculations.

	current = pulserVoltage / (loadResistance + 10.0f);

	if (current > 3.5f) 
	{
		current = 3.5f; // FET is current limiting
	}

	fetVoltageDrop = pulserVoltage - current * loadResistance;
	DCPower = fetVoltageDrop * current;

	// inspect the registers to find the number of channels
	while (pReg_shadow->sequence[stepCount].sequenceFlag != 0)
	{
		stepCount++;
		if (stepCount == SEQUENCE_TABLE_BLOCKS) break;
	}

	// Now calculate the duty cycle for this channel
	// Add up the excitation time for each time this channel is fired
	// and divide by the loop repetition time

	for (step = 0; step < stepCount; step++)
	{
		// is this channel fired in this step?  If so, add up the duty cycles
		if ((pReg_shadow->sequence[step].transmitPulseEnable & (1 << channel)) != 0)
		{
			onTicks += pReg_shadow->sequence[step].pulseWidth *
				pReg_shadow->sequence[step].pulseTrain + 1 *
				pReg_shadow->sequence[step].numberOfOversamples + 1;
		}

		sequenceSteps += pReg_shadow->sequence[step].numberOfOversamples + 1;
	}

	ticksForAllSequences = (float)(pReg_shadow->sequence[channel].repetitionDelay * sequenceSteps);
	power = DCPower * (float)onTicks / ticksForAllSequences;

	NDTS_LogLine(logLevel, "FET energy for channel %d: DCPower %f(W).  On Ticks %d.  Total ticks for all sequences %f. Power %f(mW)",
		channel, DCPower, onTicks, ticksForAllSequences, power*1000);

	return power;
	// conveniently the max power for the FETs is 1.0W so the power in watts is
	// equal to the proportion of the rated capacity
}
**** Commented out FET protection
****************************************/

// This is the version no. of the system FPGA and is not kept up to date in the firmware (22/11/06)
PRX_API NDTS_Boolean CALL_CONV PRX_GetSystemFPGAVersionNumber(unsigned long card, unsigned long *versionNumber)
{
	NDTS_Boolean isOK ;

	if (!PRX_CheckState(card))
		return NDTS_BFalse ;

	isOK = PRX_GetSystemRegister(card, PRX_SYSTEM_FPGA_VERSION_NUMBER, versionNumber) ;
	if (!isOK)
		return NDTS_BFalse ;

	return NDTS_BTrue ;
}

PRX_API PRX_ReturnCode  PRX_GetLastError()
{
	return NDTS_GetLastError() ;
}

PRX_API unsigned long PRX_ReturnNumberOfErrorLogLines()
{
	return NDTS_ReturnNumberOfErrorLogLines() ;
}

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

PRX_API NDTS_Boolean PRX_ClearErrorLog() 
{
	return NDTS_ClearErrorLog() ;
}

PRX_API NDTS_Boolean CALL_CONV PRX_InitialiseSonatestSerialMux(int iniFileValue)
{
	return PRX_InitialiseSonatestSerialMuxInternal(iniFileValue);
}

PRX_API void CALL_CONV PRX_SonatestSerialMuxSetGain(unsigned long oneBasedChannel, int gainIndB)
{
	PRX_SonatestSerialMuxSetGainInternal(oneBasedChannel, gainIndB);
}

PRX_API void CALL_CONV PRX_SonatestSerialMuxSetFilter(unsigned long oneBasedChannel, int filter)
{
	PRX_SonatestSerialMuxSetFilterInternal(oneBasedChannel, filter);
}

PRX_API void CALL_CONV PRX_SonatestSerialMuxSetVoltageClamp(unsigned long voltageClamp)
{
	PRX_SonatestSerialMuxSetVoltageClampInternal(voltageClamp);
}