// CameraIO.cpp: implementation of the CCameraIO class.
//
// Copyright (c) 2000 Apogee Instruments Inc.
//////////////////////////////////////////////////////////////////////

#include <assert.h>
#include <sys/time.h>
#include <sys/resource.h>
#include <sys/ioctl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <string.h>
#include <sched.h>
#include <unistd.h>
#include <fcntl.h>
#define HANDLE int
#define FALSE 0
#define DWORD long
#define _ASSERT assert
#define REALTIME_PRIORITY_CLASS 1  
#define GetCurrentProcess getpid
#define LOBYTE(x) ((x) & 0xff)
#define HIBYTE(x) ((x >> 8) & 0xff)

#define MIRQ1	0x21
#define MIRQ2	0xA1

#include "time.h"
//#include "tcl.h"
//#include "ccd.h"
#include "CameraIO_Linux.h"
#include "ApogeeLinux.h"

const int NUM_POSITIONS = 6;
const int NUM_STEPS_PER_FILTER = 48;
const int STEP_DELAY = 10;

const unsigned char Steps[] = { 0x10, 0x30, 0x20, 0x60, 0x40, 0xc0, 0x80, 0x90 };
const int NUM_STEPS = sizeof ( Steps );

//////////////////////////////////////////////////////////////////////
// Construction/Destruction
//////////////////////////////////////////////////////////////////////

CCameraIO::CCameraIO()
{
	InitDefaults();

	m_TDI = false;

	m_Shutter = false;
	m_FilterPosition = 0;
	m_FilterStepPos = 0;

	m_WaitingforImage = false;
	m_WaitingforLine = false;

	m_WaitingforTrigger = false;
	m_Status = Camera_Status_Idle;
	m_CoolerStatus = Camera_CoolerStatus_Off;	

	m_ExposureBinX = 0;
	m_ExposureBinY = 0;		
	m_ExposureStartX = 0;
	m_ExposureStartY = 0;
	m_ExposureNumX = 0;
	m_ExposureNumY = 0;	
	m_ExposureColumns = 0; 
	m_ExposureRows = 0;
	m_ExposureSkipC = 0;
	m_ExposureSkipR = 0;	
	m_ExposureHFlush = 0;
	m_ExposureVFlush = 0;
	m_ExposureBIC = 0;
	m_ExposureBIR = 0;		
	m_ExposureAIC = 0;					
	m_ExposureRemainingLines = 0;
	m_ExposureAIR = 0;					

	m_RegShadow[ Reg_Command ] = 0;
	m_RegShadow[ Reg_Timer ] = 0;
	m_RegShadow[ Reg_VBinning ] = 0;
	m_RegShadow[ Reg_AICCounter ] = 0;
	m_RegShadow[ Reg_TempSetPoint ] = 0;
	m_RegShadow[ Reg_PixelCounter ] = 0;
	m_RegShadow[ Reg_LineCounter ] = 0;
	m_RegShadow[ Reg_BICCounter ] = 0;

	m_FastShutterBits_Mode = 0;
	m_FastShutterBits_Test = 0;
        m_IRTQMask = 0;
        saveIRQS = 0;

}

CCameraIO::~CCameraIO()
{

  //::close(fileHandle);
  close(fileHandle);
}

////////////////////////////////////////////////////////////
// System methods

int  GetPriorityClass ( HANDLE /*hProcess*/ )
{
    int i;
    i = sched_getscheduler(0);
    return(i);
}

int  SetPriorityClass ( HANDLE /*hProcess*/, int hPriority)
{
    int i;
    sched_param p;

    if (hPriority) {
       i = sched_setscheduler(0,SCHED_RR,&p);
    } else {
       i = sched_setscheduler(0,SCHED_OTHER,&p);
    }
    return(i);
}

void Sleep (int hTime)
{
    timespec t;
    t.tv_sec= 0;
    t.tv_nsec = hTime*1000000;
//    nanosleep(&t);
}



void ATLTRACE (char * /*msg*/)
{
}


void CCameraIO::Reset()
{
	unsigned short val = 0;
	Read( Reg_CommandReadback, val );	// Take snapshot of currrent status
	m_RegShadow[ Reg_Command ] = val;	// remember it in our write shadow
	
	// In case these were left on, turn them off
	m_RegShadow[ Reg_Command ] &= ~RegBit_FIFOCache;	// set bit to 0
	m_RegShadow[ Reg_Command ] &= ~RegBit_TDIMode;		// set bit to 0

	m_RegShadow[ Reg_Command ] |= RegBit_ResetSystem;	// set bit to 1
	Write( Reg_Command, m_RegShadow[ Reg_Command ] );
	
	m_RegShadow[ Reg_Command ] &= ~RegBit_ResetSystem;	// set bit to 0
	Write( Reg_Command, m_RegShadow[ Reg_Command ] );

	m_WaitingforImage = false;
	m_WaitingforLine = false;
	m_WaitingforTrigger = false;
}

void CCameraIO::AuxOutput( unsigned char val )
{	
	// clear bits to 0
	m_RegShadow[ Reg_TempSetPoint ] &= ~( RegBitMask_PortControl << RegBitShift_PortControl );
	
	// set our new bits
	m_RegShadow[ Reg_TempSetPoint ] |= val << RegBitShift_PortControl;
	
	Write( Reg_TempSetPoint, m_RegShadow[ Reg_TempSetPoint ] );
}

// Input reg is from 0 to 7, val is any 16 bit number
void CCameraIO::RegWrite( short reg, unsigned short val )
{
	Write( reg, val );
	
	// Update our shadow register
	switch ( reg )
	{
	case Reg_Command:
		m_RegShadow[ Reg_Command ] = val;
		break;
	case Reg_Timer:
		m_RegShadow[ Reg_Timer ] = val;
		break;
	case Reg_VBinning:
		m_RegShadow[ Reg_VBinning ] = val;
		break;
	case Reg_AICCounter:
		m_RegShadow[ Reg_AICCounter ] = val;
		break;
	case Reg_TempSetPoint:
		m_RegShadow[ Reg_TempSetPoint ] = val;
		break;
	case Reg_PixelCounter:
		m_RegShadow[ Reg_PixelCounter ] = val;
		break;
	case Reg_LineCounter:
		m_RegShadow[ Reg_LineCounter ] = val;
		break;
	case Reg_BICCounter:
		m_RegShadow[ Reg_BICCounter ] = val;
		break;
	default:
		_ASSERT( FALSE );	// application program bug
	}
}

// Input reg is from 8 to 12, returned val is any 16 bit number
void CCameraIO::RegRead( short reg, unsigned short& val )
{
	Read( reg, val );
}

bool CCameraIO::FilterHome()
{
  	HANDLE hProcess(0);
	DWORD Class(0);

	if ( m_HighPriority )
	{	// Store current process class and priority
		hProcess = GetCurrentProcess();
		Class = GetPriorityClass ( hProcess );
		SetPriorityClass ( hProcess, REALTIME_PRIORITY_CLASS );
	}

	// Find the home position
	m_FilterPosition = 0;
	int Safety = 0;
	for (int I = 0; I < NUM_POSITIONS * NUM_STEPS_PER_FILTER * 2; I++)
	{
		// Advance the filter one step
		m_FilterStepPos += 1;
		if (m_FilterStepPos >= NUM_STEPS) m_FilterStepPos = 0;
		unsigned char Step = Steps[ m_FilterStepPos ];
				
		AuxOutput( Step );
		Sleep ( STEP_DELAY );

		// Check for strobe
		unsigned short val = 0;
		Read( Reg_Status, val );
		if ( val & RegBit_GotTrigger )
		{
			// Cycle all the way around if it's on the first time
			if (I < NUM_STEPS_PER_FILTER) 
			{
				if (++Safety > NUM_STEPS_PER_FILTER * 2) 
				{
					// Restore normal priority
					if ( m_HighPriority ) SetPriorityClass ( hProcess, Class );
					return false;
				}
				I = 0;
				continue;
			}

			// Continue cycling until we get clear of the opto mirror
			for (int J = 0; J < NUM_STEPS_PER_FILTER; J++)
			{
				// Advance the filter one step
				m_FilterStepPos += 1;
				if (m_FilterStepPos >= NUM_STEPS) m_FilterStepPos = 0;
				unsigned char Step = Steps[ m_FilterStepPos ];
				
				AuxOutput( Step );
				Sleep ( STEP_DELAY );

				val = 0;
				Read( Reg_Status, val );
				if ( val & RegBit_GotTrigger )
				{
					Sleep ( 10 );
					
					val = 0;
					Read( Reg_Status, val );
					if ( val & RegBit_GotTrigger )
					{
						// Restore normal priority
						if ( m_HighPriority ) SetPriorityClass ( hProcess, Class );
						return true;
					}
				}
			}

			// Restore normal priority
			if ( m_HighPriority ) SetPriorityClass ( hProcess, Class );
			return true;
		}
	}

	// Restore normal priority
	if ( m_HighPriority ) SetPriorityClass ( hProcess, Class );
	return false;
}

void CCameraIO::FilterSet( short Slot )
{
	// Determine how far we have to move
	int Pos = Slot - m_FilterPosition;
	if (Pos < 0) Pos += NUM_POSITIONS;

	HANDLE hProcess(0);
	DWORD Class(0);

	if ( m_HighPriority )
	{	// Store current process class and priority
		hProcess = GetCurrentProcess();
		Class = GetPriorityClass ( hProcess );
		SetPriorityClass ( hProcess, REALTIME_PRIORITY_CLASS );
	}

	for (int I = 0; I < Pos; I++)
	{
		// Advance one position
		for (int J = 0; J < NUM_STEPS_PER_FILTER; J++)
		{
			m_FilterStepPos += 1;
			if (m_FilterStepPos >= NUM_STEPS) m_FilterStepPos = 0;
			unsigned char Step = Steps[ m_FilterStepPos ];
		
			AuxOutput( Step );
			Sleep ( STEP_DELAY );
		}
	}

	if ( m_HighPriority ) SetPriorityClass ( hProcess, Class );

	m_FilterPosition = Slot;
}

////////////////////////////////////////////////////////////
// Normal exposure methods

bool CCameraIO::Expose( double Duration, bool Light )
{
	if ( !m_TDI && ( Duration < m_MinExposure || Duration > m_MaxExposure ) ) return false;

	// Validate all input variables
	if ( m_Columns < 1 || m_Columns > MAXCOLUMNS ) return false;
	m_ExposureColumns = m_Columns; 
	
	if ( m_Rows < 1 || m_Rows > MAXROWS ) return false;
	m_ExposureRows = m_Rows;
	
	if ( m_SkipC < 0 ) return false;
	m_ExposureSkipC = m_SkipC;
	
	if ( m_SkipR < 0 ) return false;
	m_ExposureSkipR = m_SkipR;	
	
	if ( m_HFlush < 1 || m_HFlush > MAXHBIN ) return false;
	m_ExposureHFlush = m_HFlush;
	
	if ( m_VFlush < 1 || m_VFlush > MAXVBIN ) return false;
	m_ExposureVFlush = m_VFlush;

	if ( m_BIC < 1 || m_BIC > MAXCOLUMNS ) return false;
	m_ExposureBIC = m_BIC;
	
	if ( m_BIR < 1 || m_BIR > MAXROWS ) return false;
	m_ExposureBIR = m_BIR;		

	// Validate all input variables
	if ( m_BinX < 1 || m_BinX > MAXHBIN ) return false;
	m_ExposureBinX = m_BinX;

	if ( m_StartX < 0 || m_StartX >= MAXCOLUMNS ) return false;
	m_ExposureStartX = m_StartX;

	if ( m_NumX < 1 || m_NumX * m_BinX > m_ImgColumns ) return false;
	m_ExposureNumX = m_NumX;

	// Calculate BIC, RawPixelCount, AIC
	unsigned short BIC = m_ExposureBIC + m_ExposureStartX;		// unbinned columns
	unsigned short RawPixelCount = m_ExposureNumX * m_ExposureBinX;
	m_ExposureAIC = m_ExposureColumns - BIC - RawPixelCount;	// unbinned columns

	if ( m_BinY < 1 || m_BinY > MAXVBIN ) return false;
	m_ExposureBinY = m_BinY;		
		
	unsigned short VBin(0), row_offset(0);

	if ( m_TDI )
	{	// row_offset is the drift time in milliseconds when in TDI mode
		row_offset = (unsigned short) (Duration * 1000 + 0.5);
		Duration = 0.0;
	}
	else
	{
		if ( m_StartY < 0 || m_StartX >= MAXROWS ) return false;
		m_ExposureStartY = m_StartY;
			
		if ( m_NumY < 1 || m_NumY * m_BinY > m_ImgRows ) return false;
		m_ExposureNumY = m_NumY;	

		unsigned short BIR = m_ExposureBIR + m_ExposureStartY;		// unbinned rows
		if ( BIR >= MAXROWS ) return false;
		m_ExposureAIR = m_ExposureRows - BIR - m_ExposureNumY * m_ExposureBinY;	// unbinned rows

		if ( m_VFlush > BIR )
		{
			VBin = BIR;
			m_ExposureRemainingLines = 0;
		}
		else
		{
			VBin = m_VFlush;
			m_ExposureRemainingLines = BIR % VBin;	// unbinned rows
		}
		row_offset = BIR - m_ExposureRemainingLines; // unbinned rows
	}	
	
	StopFlushing();
	Reset();

	LoadColumnLayout( m_ExposureAIC, BIC, (unsigned short) m_ExposureNumX + m_ExposureSkipC );
	LoadTimerAndBinning( Duration, (unsigned short) m_ExposureHFlush, VBin );
	LoadLineCounter( row_offset );

	if ( m_TDI )
	{
		// Turn on TDI
		m_RegShadow[ Reg_Command ] |= RegBit_TDIMode;		// set bit to 1
		
		// Disable FIFO cache
		m_RegShadow[ Reg_Command ] &= ~RegBit_FIFOCache;	// set bit to 0

		// Set shutter override
		if ( Light )
			m_RegShadow[ Reg_Command ] |= RegBit_ShutterOverride;		// set bit to 1
		else
			m_RegShadow[ Reg_Command ] &= ~RegBit_ShutterOverride;		// set bit to 0

		Write( Reg_Command, m_RegShadow[ Reg_Command ] );
		
		// Update our status
		m_Shutter = Light;
		m_WaitingforTrigger = false;
		m_WaitingforLine = false;
	}
	else
	{
		// Set shutter
		if ( Light )
			m_RegShadow[ Reg_Command ] |= RegBit_ShutterEnable;		// set bit to 1
		else
			m_RegShadow[ Reg_Command ] &= ~RegBit_ShutterEnable;	// set bit to 0

		Write( Reg_Command, m_RegShadow[ Reg_Command ] );

		// Update our status
		unsigned short val = 0;
		Read( Reg_CommandReadback, val );
		if ( val & RegBit_ShutterOverride )
			m_Shutter = true;
		else
			m_Shutter = Light;

		if ( ( val & RegBit_TriggerEnable ) )
			m_WaitingforTrigger = true;
		else
			m_WaitingforTrigger = false;

		// Start the exposure
		m_RegShadow[ Reg_Command ] |= RegBit_StartTimer;	// set bit to 1
		Write( Reg_Command, m_RegShadow[ Reg_Command ] );
		
		m_RegShadow[ Reg_Command ] &= ~RegBit_StartTimer;	// set bit to 0
		Write( Reg_Command, m_RegShadow[ Reg_Command ] );
	
		m_WaitingforImage = true;
	}

	return true;
}

/*bool CCameraIO::BufferImage(char *bufferName )
{
    unsigned short *pImageData;
    bool status; 
    short cols,rows,hbin,vbin;
    short xSize, ySize;

    cols = m_NumX*m_BinX;
    rows = m_NumY*m_BinY;
    hbin = m_BinX;
    vbin = m_BinY;

    pImageData = (unsigned short *)CCD_locate_buffer(bufferName, 2 , cols, rows, hbin, vbin );
    if (pImageData == NULL) {
       return 0;
    }
    
    status = GetImage(pImageData, xSize, ySize);
    return status;
}*/

bool CCameraIO::GetImage( unsigned short* pImageData, short& xSize, short& ySize )
{
        int i;
	unsigned short BIC = m_ExposureBIC + m_ExposureStartX;

	// Update internal variables in case application did not poll read_Status
	m_WaitingforTrigger = false;
	m_WaitingforLine = false;

	if ( m_WaitingforImage )
	{	// In case application did not poll read_Status
		m_WaitingforImage = false;

		/////////////////////////////////////
		// Wait until camera is done flushing
		clock_t StopTime = clock() + long( m_Timeout * CLOCKS_PER_SEC );	// wait at most m_Timeout seconds
		while ( true )
		{
			unsigned short val = 0;
			Read( Reg_Status, val );
			if ( ( val & RegBit_FrameDone ) != 0 ) break;
			
			if ( clock() > StopTime ) return false;		// Timed out
		}
	}

//        MaskIrqs();

	/////////////////////////////////////
	// Update our internal status
	unsigned short val = 0;
	Read( Reg_CommandReadback, val );
	if ( !( val & RegBit_ShutterOverride ) ) m_Shutter = false;

	StopFlushing();
	LoadColumnLayout( m_ExposureAIC, BIC, (unsigned short) m_ExposureNumX + m_ExposureSkipC );

	if ( m_ExposureRemainingLines > 0 )
	{
		LoadTimerAndBinning( 0.0, m_ExposureHFlush, m_ExposureRemainingLines );

		/////////////////////////////////////
		// Clock out the remaining lines
		m_RegShadow[ Reg_Command ] |= RegBit_StartNextLine;		// set bit to 1
		Write( Reg_Command, m_RegShadow[ Reg_Command ] );
		
		m_RegShadow[ Reg_Command ] &= ~RegBit_StartNextLine;	// set bit to 0
		Write( Reg_Command, m_RegShadow[ Reg_Command ] );
		/////////////////////////////////////

		/////////////////////////////////////
		// Wait until camera is done clocking
		clock_t StopTime = clock() + CLOCKS_PER_SEC;	// wait at most one second
		while ( true )
		{
			unsigned short val = 0;
			Read( Reg_Status, val );
			if ( ( val & RegBit_LineDone ) != 0 ) break;	// Line done
			
			if ( clock() > StopTime )
			{
				Flush();
				return false;		// Timed out, no image available
			}
		}
	}

	LoadTimerAndBinning( 0.0, m_ExposureBinX, m_ExposureBinY );
	
	bool ret = false;	// assume failure

	// NB Application must have allocated enough memory or else !!!
	if ( pImageData != NULL )
	{
	        HANDLE hProcess(0);
		DWORD Class(0);
		
		if ( m_HighPriority )
		{	// Store current process class and priority
			hProcess = GetCurrentProcess();
			Class = GetPriorityClass ( hProcess );
			SetPriorityClass ( hProcess, REALTIME_PRIORITY_CLASS );
		}

		m_RegShadow[ Reg_Command ] |= RegBit_FIFOCache;		// set bit to 1
		Write( Reg_Command, m_RegShadow[ Reg_Command ] );

		long XPixels = long( m_ExposureNumX );
		long SkipPixels = long( m_ExposureSkipC );
		for (i = 0; i < m_ExposureSkipR; i++)
		{
			if ( ReadLine( SkipPixels, XPixels, pImageData ) ) break;
		}
		
		if ( i == m_ExposureSkipR )
		{	// We have skipped all the lines
			long YPixels = long( m_ExposureNumY );
			unsigned short* pLineBuffer = pImageData;
			for (i = 0; i < YPixels; i++)
			{
				if ( ReadLine( SkipPixels, XPixels, pLineBuffer ) ) break;
				pLineBuffer += XPixels;
			}

			if ( i == YPixels ) ret = true;		// We have read all the lines
		}
		
		m_RegShadow[ Reg_Command ] &= ~RegBit_FIFOCache;	// set bit to 0
		Write( Reg_Command, m_RegShadow[ Reg_Command ] );

		//Restore priority
		if ( m_HighPriority ) SetPriorityClass ( hProcess, Class );
	}

//        UnmaskIrqs();

	if ( ret )
	{	// We were successfull
		Flush( m_ExposureAIR );	// flush after imaging rows

		xSize = m_ExposureNumX;
		ySize = m_ExposureNumY;

		if ( m_DataBits == 16 )
		{	// Take care of two's complement converters
			unsigned short *Ptr = pImageData;
			short *Ptr2 = (short *) pImageData;
			long Size = m_ExposureNumX * m_ExposureNumY;
			for (i = 0; i < Size; i++)
			{
				*Ptr++ = (unsigned short) *Ptr2++ + 32768 ;
			}
		}

	}
	else
	{	// Something went wrong
		xSize = 0;
		ySize = 0;
	}
	
	Flush();		// start normal flushing
	
	return ret;
}

////////////////////////////////////////////////////////////
// Drift scan methods

bool CCameraIO::DigitizeLine()
{
	/////////////////////////////////////
	// All of these are done just in case
	// since they are called in Expose()
	StopFlushing();
	
	unsigned short BIC = m_ExposureBIC + m_ExposureStartX;
	LoadColumnLayout( m_ExposureAIC, BIC, (unsigned short) m_ExposureNumX + m_ExposureSkipC );
	LoadTimerAndBinning( 0.0, m_ExposureBinX, m_ExposureBinY );

	// Disable FIFO cache
	m_RegShadow[ Reg_Command ] &= ~RegBit_FIFOCache;	// set bit to 0
	/////////////////////////////////////

	/////////////////////////////////////
	// Clock out the line
	m_RegShadow[ Reg_Command ] |= RegBit_StartNextLine;		// set bit to 1
	Write( Reg_Command, m_RegShadow[ Reg_Command ] );
	
	m_RegShadow[ Reg_Command ] &= ~RegBit_StartNextLine;	// set bit to 0
	Write( Reg_Command, m_RegShadow[ Reg_Command ] );
	/////////////////////////////////////
	
	m_WaitingforLine = true;
	return true;
}

bool CCameraIO::GetLine( unsigned short* pLineData, short& xSize )
{
        int i;

	if ( m_WaitingforLine )
	{	// In case application did not poll read_Status
		m_WaitingforLine = false;
		
		/////////////////////////////////////
		// Wait until camera is done clocking
		clock_t StopTime = clock() + CLOCKS_PER_SEC;	// wait at most one second
		while ( true )
		{
			unsigned short val = 0;
			Read( Reg_Status, val );
			if ( ( val & RegBit_LineDone ) != 0 ) break;	// Line done
			
			if ( clock() > StopTime )
			{
				Flush();
				return false;		// Timed out, no line available
			}
		}
	}

	bool ret = false;	// assume failure

//        MaskIrqs();

	// NB Application must have allocated enough memory or else !!!
	if ( pLineData != NULL )
	{
	        HANDLE hProcess(0);
		DWORD Class(0);
		
		if ( m_HighPriority )
		{	// Store current process class and priority
			hProcess = GetCurrentProcess();
			Class = GetPriorityClass ( hProcess );
			SetPriorityClass ( hProcess, REALTIME_PRIORITY_CLASS );
		}

		long XPixels = long( m_ExposureNumX );
		long SkipPixels = long( m_ExposureSkipC );
		
		if ( ReadLine( SkipPixels, XPixels, pLineData ) )
		{	// Something went wrong
			xSize = 0;
			ret = false;
		}
		else
		{
			xSize = m_ExposureNumX;

			if ( m_DataBits == 16 )
			{	// Take care of two's complement converters
				unsigned short *Ptr = pLineData;
				short *Ptr2 = (short *) pLineData;
				long Size = m_ExposureNumX;
				for (i = 0; i < Size; i++)
				{
					*Ptr++ = (unsigned short) *Ptr2++ + 32768 ;
				}
			}
			
			ret = true;
		}
		
		//Restore priority
		if ( m_HighPriority ) SetPriorityClass ( hProcess, Class );
	}
	
//        UnmaskIrqs();
	return ret;
}

////////////////////////////////////////////////////////////
// Easy to use methods

bool CCameraIO::Snap( double Duration, bool Light, unsigned short* pImageData, short& xSize, short& ySize )
{
	// NB This also demonstrates how an application might use the
	// Expose and GetImage routines.

	bool ret = Expose( Duration, Light );
	if ( !ret  ) return false;

	if ( m_WaitingforTrigger )
	{
		Camera_Status stat;
		while ( true )
		{	// This will wait forever if no trigger happens
			stat = read_Status();
			if ( stat == Camera_Status_Exposing ) break;
			Sleep( 220 );	// dont bog down the CPU while polling
		}
		m_WaitingforTrigger = false;
	}

	// Only wait a time slightly greater than the duration of the exposure
	// but enough for the BIR to flush out
	clock_t StopTime = clock() + long( ( 1.2 * Duration + m_Timeout ) * CLOCKS_PER_SEC );
	while ( true )
	{
		Camera_Status stat = read_Status();
		if ( stat == Camera_Status_ImageReady ) break;
		
		if ( clock() > StopTime ) return false;	// Timed out, no image available
		Sleep( 220 );	// dont bog down the CPU while polling
	}

	return GetImage( pImageData, xSize, ySize );
}

////////////////////////////////////////////////////////////
// Camera Settings

Camera_Status CCameraIO::read_Status()
{
	unsigned short val = 0;
	Read( Reg_Status, val );

	if ( val & RegBit_Exposing )		//11.0
	{
		ATLTRACE( "Exposing\r\n" );
		m_WaitingforTrigger = false;
		m_Status = Camera_Status_Exposing;
	}

	else if ( m_WaitingforTrigger )
		m_Status = Camera_Status_Waiting;

	else if ( m_WaitingforImage && ( val & RegBit_FrameDone ) )	//11.11
	{
		ATLTRACE( "ImageReady\r\n" );
		m_WaitingforImage = false;
		m_Status = Camera_Status_ImageReady;
	}

	else if ( m_WaitingforLine && ( val & RegBit_LineDone ) )	//11.1
	{
		ATLTRACE( "LineReady\r\n" );
		m_WaitingforLine = false;
		m_Status = Camera_Status_LineReady;
	}
	else if ( m_WaitingforImage || m_WaitingforLine )
	{
		ATLTRACE( "Flushing\r\n" );
		m_Status = Camera_Status_Flushing;
	}
	else
		m_Status = Camera_Status_Idle;

	return m_Status;
}

bool CCameraIO::read_Present()
{
// This does not work on all cameras
/*
	m_RegShadow[ Reg_BICCounter ] |= RegBit_LoopbackTest;	// set bit to 1
	Write( Reg_BICCounter, m_RegShadow[ Reg_BICCounter ] );

	bool FailedLoopback = false;
	unsigned short val = 0;
	Read( Reg_Status, val );
	if ( !( val & RegBit_LoopbackTest ) ) FailedLoopback = true;

	m_RegShadow[ Reg_BICCounter ] &= ~RegBit_LoopbackTest;	// clear bit to 0
	Write( Reg_BICCounter, m_RegShadow[ Reg_BICCounter ] );

	Read( Reg_Status, val );
	if ( val & RegBit_LoopbackTest ) FailedLoopback = true;
*/

	unsigned short val = 0;
	Read( Reg_CommandReadback, val );	// Take snapshot of currrent status
	m_RegShadow[ Reg_Command ] = val;	// remember it in our write shadow
	
	bool TriggerEnabled = ( val & RegBit_TriggerEnable ) != 0;
		
	m_RegShadow[ Reg_Command ] &= ~RegBit_TriggerEnable;// clear bit to 0
	Write( Reg_Command, m_RegShadow[ Reg_Command ] );

	Read( Reg_CommandReadback, val );	// get currrent status
	if ( val & RegBit_TriggerEnable ) return false;

	m_RegShadow[ Reg_Command ] |= RegBit_TriggerEnable;	// set bit to 1
	Write( Reg_Command, m_RegShadow[ Reg_Command ] );

	Read( Reg_CommandReadback, val );	// get currrent status
	if ( !(val & RegBit_TriggerEnable) ) return false;

	m_RegShadow[ Reg_Command ] &= ~RegBit_TriggerEnable;// clear bit to 0
	Write( Reg_Command, m_RegShadow[ Reg_Command ] );

	Read( Reg_CommandReadback, val );	// get currrent status
	if ( val & RegBit_TriggerEnable ) return false;

	if ( TriggerEnabled )
	{	// Set it back the way it was
		m_RegShadow[ Reg_Command ] |= RegBit_TriggerEnable;	// set bit to 1
		Write( Reg_Command, m_RegShadow[ Reg_Command ] );
	}
	return true;
}

bool CCameraIO::read_Shutter()
{
	unsigned short regval = 0;
	Read( Reg_Status, regval );
	if ( !( regval & RegBit_Exposing ) )
	{	// We are not exposing, but might have finnshed an exposure
		// and have not called GetImage yet, so update our internal variable
		regval = 0;
		Read( Reg_CommandReadback, regval );
		if ( !( regval & RegBit_ShutterOverride ) )
			// The shutter override is not on, so the shutter must be closed
			m_Shutter = false;
	}

	return m_Shutter;
}

bool CCameraIO::read_ForceShutterOpen()
{
	unsigned short val = 0;
	Read( Reg_CommandReadback, val );
	return ( ( val & RegBit_ShutterOverride ) != 0 );
}

void CCameraIO::write_ForceShutterOpen( bool val )
{
	if ( val )
	{
		m_RegShadow[ Reg_Command ] |= RegBit_ShutterOverride;	// set bit to 1
		m_Shutter = true;	// shutter will open immediately now matter what is going on
	}
	else
	{
		m_RegShadow[ Reg_Command ] &= ~RegBit_ShutterOverride;	// clear bit to 0

		unsigned short regval = 0;
		Read( Reg_Status, regval );
		if ( ( regval & RegBit_Exposing ) )
		{	
			// Shutter will remain open if a Light frame is being taken
			// however if a dark frame was being exposed while the
			// override was on or the override is turned on during the exposure
			// and now is turned off (dumb idea but some app might do it!)
			// we must update our variable since the shutter will close
			// when override gets turned off below
			regval = 0;
			Read( Reg_CommandReadback, regval );
			if ( !( regval & RegBit_ShutterEnable ) ) m_Shutter = false;
		}
		else
		{	// Not currently exposing so shutter will close
			// once override is turned off, update our variable
			m_Shutter = false;
		}
	}
	Write( Reg_Command, m_RegShadow[ Reg_Command ] );
}



bool CCameraIO::read_LongCable()
{
	unsigned short val = 0;
	Read( Reg_CommandReadback, val );
	return ( ( val & RegBit_CableLength ) != 0 );
}

void CCameraIO::write_Shutter( bool val )			
{
	if ( val )
		m_RegShadow[ Reg_Command ] |= RegBit_ShutterEnable;	// set bit to 1
	else
		m_RegShadow[ Reg_Command ] &= ~RegBit_ShutterEnable;	// clear bit to 0

	Write( Reg_Command, m_RegShadow[ Reg_Command ] );
}

void CCameraIO::write_LongCable( bool val )			 
{ 
	if ( val ) 
		m_RegShadow[ Reg_Command ] |= RegBit_CableLength;	// set bit to 1 
	else 
		m_RegShadow[ Reg_Command ] &= ~RegBit_CableLength;	// clear bit to 0 
 
	Write( Reg_Command, m_RegShadow[ Reg_Command ] ); 
} 


short CCameraIO::read_Mode()
{
	return ( ( m_RegShadow[ Reg_LineCounter ] >> RegBitShift_Mode ) & RegBitMask_Mode );
}

void CCameraIO::write_Mode( short val )
{
	// clear bits to 0
	m_RegShadow[ Reg_LineCounter ] &= ~( RegBitMask_Mode << RegBitShift_Mode );
	
	// set our new bits
	m_RegShadow[ Reg_LineCounter ] |= ( (unsigned short) val  & RegBitMask_Mode ) << RegBitShift_Mode;
	
	Write( Reg_LineCounter, m_RegShadow[ Reg_LineCounter ] );
}

short CCameraIO::read_TestBits()
{
	return ( ( m_RegShadow[ Reg_BICCounter ] >> RegBitShift_Test ) & RegBitMask_Test );
}

void CCameraIO::write_TestBits( short val )
{
	// clear bits to 0
	m_RegShadow[ Reg_BICCounter ] &= ~( RegBitMask_Test << RegBitShift_Test );
	
	// set our new bits
	m_RegShadow[ Reg_BICCounter ] |= ( (unsigned short) val  & RegBitMask_Test ) << RegBitShift_Test;
	
	Write( Reg_BICCounter, m_RegShadow[ Reg_BICCounter ] );
}


short CCameraIO::read_Test2Bits()
{
	return ( ( m_RegShadow[ Reg_AICCounter ] >> RegBitShift_Test2 ) & RegBitMask_Test2 );
}

void CCameraIO::write_Test2Bits( short val )
{
	// clear bits to 0
	m_RegShadow[ Reg_AICCounter ] &= ~( RegBitMask_Test2 << RegBitShift_Test2 );
	
	// set our new bits
	m_RegShadow[ Reg_AICCounter ] |= ( (unsigned short) val  & RegBitMask_Test2 ) << RegBitShift_Test2;
	
	Write( Reg_AICCounter, m_RegShadow[ Reg_AICCounter ] );
}

bool CCameraIO::read_FastReadout()
{
	unsigned short val = 0;
	Read( Reg_CommandReadback , val );
	return ( ( val & RegBit_Focus ) != 0 );
}

void CCameraIO::write_FastReadout( bool val )
{
	if ( val )
		m_RegShadow[ Reg_Command ] |= RegBit_Focus;		// set bit to 1
	else
		m_RegShadow[ Reg_Command ] &= ~RegBit_Focus;	// clear bit to 0

	Write( Reg_Command, m_RegShadow[ Reg_Command ] );
}

bool CCameraIO::read_UseTrigger()
{
	unsigned short val = 0;
	Read( Reg_CommandReadback , val );
	return ( ( val & RegBit_TriggerEnable ) != 0 );
}

void CCameraIO::write_UseTrigger( bool val )
{
	if ( val )
		m_RegShadow[ Reg_Command ] |= RegBit_TriggerEnable;		// set bit to 1
	else
		m_RegShadow[ Reg_Command ] &= ~RegBit_TriggerEnable;	// clear bit to 0

	Write( Reg_Command, m_RegShadow[ Reg_Command ] );
}

////////////////////////////////////////////////////////////
// Cooler Settings

double CCameraIO::read_CoolerSetPoint()
{
	// Get the setting from the shadow registers
	short DACunits = short( ( m_RegShadow[ Reg_TempSetPoint ] >> RegBitShift_TempSetPoint ) & RegBitMask_TempSetPoint );
	return ( DACunits - m_TempCalibration ) / m_TempScale;
}

void CCameraIO::write_CoolerSetPoint( double val )
{
	// clear bits to 0
	m_RegShadow[ Reg_TempSetPoint ] &= ~( RegBitMask_TempSetPoint << RegBitShift_TempSetPoint );
	
	// Calculate DAC units from degrees Celcius
	unsigned short DACunits = (unsigned )( m_TempScale * val ) + m_TempCalibration ;

	// set our new bits
	m_RegShadow[ Reg_TempSetPoint ] |= ( DACunits & RegBitMask_TempSetPoint ) << RegBitShift_TempSetPoint;
	
	Write( Reg_TempSetPoint, m_RegShadow[ Reg_TempSetPoint ] );
}

Camera_CoolerStatus CCameraIO::read_CoolerStatus()
{
	unsigned short val = 0;
	Read( Reg_CommandReadback, val );

	if ( val & RegBit_CoolerEnable )				//12.15
	{
		unsigned short val2 = 0;
		Read( Reg_Status, val2 );
		
		if ( val & RegBit_CoolerShutdown )			//12.8
		{
			if ( val2 & RegBit_ShutdownComplete )	//11.6
				m_CoolerStatus = Camera_CoolerStatus_AtAmbient;
			else
				m_CoolerStatus = Camera_CoolerStatus_RampingToAmbient;
		}
		else
		{
			if ( val2 & RegBit_TempAtMax )			//11.5
				m_CoolerStatus = Camera_CoolerStatus_AtMax;
			else if ( val2 & RegBit_TempAtMin )		//11.4
				m_CoolerStatus = Camera_CoolerStatus_AtMin;
			else if ( val2 & RegBit_TempAtSetPoint )	//11.7
				m_CoolerStatus = Camera_CoolerStatus_AtSetPoint;
			// Check against last known cooler status
			else if ( m_CoolerStatus == Camera_CoolerStatus_AtSetPoint )
				m_CoolerStatus = Camera_CoolerStatus_Correcting;
			else
				m_CoolerStatus = Camera_CoolerStatus_RampingToSetPoint;
		}
	}
	else
		m_CoolerStatus = Camera_CoolerStatus_Off;

	return m_CoolerStatus;
}

Camera_CoolerMode CCameraIO::read_CoolerMode()
{
	unsigned short val = 0;
	Read( Reg_CommandReadback, val );

	if ( val & RegBit_CoolerShutdown )
		return Camera_CoolerMode_Shutdown;
	else if ( val & RegBit_CoolerEnable )
		return Camera_CoolerMode_On;
	else
		return Camera_CoolerMode_Off;
}

void CCameraIO::write_CoolerMode( Camera_CoolerMode val )
{
	switch ( val )
	{
	case Camera_CoolerMode_Off:
		m_RegShadow[ Reg_Command ] &= ~( RegBit_CoolerEnable );		// clear bit to 0
		m_RegShadow[ Reg_Command ] &= ~( RegBit_CoolerShutdown );	// clear bit to 0
		break;
	case Camera_CoolerMode_On:
		m_RegShadow[ Reg_Command ] |= RegBit_CoolerEnable;		// set bit to 1
		break;
	case Camera_CoolerMode_Shutdown:
		m_RegShadow[ Reg_Command ] |= RegBit_CoolerShutdown;	// set bit to 1
		break;
	default:
		return;
	}

	Write( Reg_Command, m_RegShadow[ Reg_Command ] );
}

double CCameraIO::read_Temperature()
{
	if ( m_TempScale == 0.0 )
		return 0.0;
	else
	{
		unsigned short val = 0;
		Read( Reg_TempData, val );

		short DACunits = short( ( val >> RegBitShift_TempData ) & RegBitMask_TempData );
	
		return ( DACunits - m_TempCalibration ) / m_TempScale;
	}
}

// Load line counter
void CCameraIO::LoadLineCounter( unsigned short rows ) 
{
	/////////////////////////////////////
	// Write out Line_Count  - in unbinned rows
	// clear bits to 0
	m_RegShadow[ Reg_LineCounter ] &= ~( RegBitMask_LineCounter << RegBitShift_LineCounter );
	// set our new bits
	m_RegShadow[ Reg_LineCounter ] |= ( rows & RegBitMask_LineCounter ) << RegBitShift_LineCounter;
	
	Write( Reg_LineCounter, m_RegShadow[ Reg_LineCounter ] );
	/////////////////////////////////////
}

// Load AIC, BIC and pixel count into registers
void CCameraIO::LoadColumnLayout( unsigned short aic, unsigned short bic, unsigned short pixels ) 
{
	/////////////////////////////////////
	// Write out AIC - in unbinned columns
	// clear bits to 0
	m_RegShadow[ Reg_AICCounter ] &= ~( RegBitMask_AICCounter << RegBitShift_AICCounter );
	// set our new bits
	m_RegShadow[ Reg_AICCounter ] |= ( aic & RegBitMask_AICCounter ) << RegBitShift_AICCounter;
	
	Write( Reg_AICCounter, m_RegShadow[ Reg_AICCounter ] );
	/////////////////////////////////////

	/////////////////////////////////////
	// Write out BIC - in unbinned columns
	// clear bits to 0
	m_RegShadow[ Reg_BICCounter ] &= ~( RegBitMask_BICCounter << RegBitShift_BICCounter );
	// set our new bits
	m_RegShadow[ Reg_BICCounter ] |= ( bic & RegBitMask_BICCounter ) << RegBitShift_BICCounter;
	
	Write( Reg_BICCounter, m_RegShadow[ Reg_BICCounter ] );
	/////////////////////////////////////

	/////////////////////////////////////
	// Write out pixel count - in binned columns
	// clear bits to 0
	m_RegShadow[ Reg_PixelCounter ] &= ~( RegBitMask_PixelCounter << RegBitShift_PixelCounter );
	// set our new bits
	m_RegShadow[ Reg_PixelCounter ] |= ( pixels & RegBitMask_PixelCounter ) << RegBitShift_PixelCounter;
	
	Write( Reg_PixelCounter, m_RegShadow[ Reg_PixelCounter ] );
	/////////////////////////////////////
}

// Load timer, vertical binning and horizontal binning in to registers
// If Duration parameter is 0 the current timer value will be retained.
// The VBin and HBin parameters are one based, the HBin value
// is converted to zero base inside this routine.
void CCameraIO::LoadTimerAndBinning( double Duration, unsigned short HBin, unsigned short VBin )
{
	/////////////////////////////////////
	// Write out HBin for flushing
	// clear bits to 0
	m_RegShadow[ Reg_PixelCounter ] &= ~( RegBitMask_HBinning << RegBitShift_HBinning );
	// set our new bits
	m_RegShadow[ Reg_PixelCounter ] |= ( ( HBin - 1 ) & RegBitMask_HBinning ) << RegBitShift_HBinning;
	
	Write( Reg_PixelCounter, m_RegShadow[ Reg_PixelCounter ] );
	/////////////////////////////////////

	/////////////////////////////////////
	// Write out VBin for flushing and Timer
	if ( Duration > 0.0 )
	{
		if ( Duration > m_MaxExposure ) Duration = m_MaxExposure;
		
		long valTimer;
		if ( m_FastShutter && Duration <= 1048.575 )
		{	// Automatically switch to high precision mode
			valTimer = long( ( Duration * 1000 ) + 0.5 );
			m_RegShadow[ Reg_LineCounter ] |= ( m_FastShutterBits_Mode & RegBitMask_Mode ) << RegBitShift_Mode;
			m_RegShadow[ Reg_BICCounter ] |= ( m_FastShutterBits_Test & RegBitMask_Test ) << RegBitShift_Test;
		}
		else
		{
			valTimer = long( ( Duration * 100 ) + 0.5 );
			if ( m_FastShutter )
			{	// Disable high precision mode
				m_RegShadow[ Reg_LineCounter ] &= ~( m_FastShutterBits_Mode & RegBitMask_Mode ) << RegBitShift_Mode;
				m_RegShadow[ Reg_BICCounter ] &= ~( m_FastShutterBits_Test & RegBitMask_Test ) << RegBitShift_Test;
			}
		}
		
		if ( m_FastShutter )
		{
			Write( Reg_LineCounter, m_RegShadow[ Reg_LineCounter ] );
			Write( Reg_BICCounter, m_RegShadow[ Reg_BICCounter ] );
		}

		if ( valTimer <= 0 ) valTimer = 1;		// Safety since firmware doesnt like zero

		unsigned short valTimerLow = (unsigned short) (valTimer & 0x0000FFFF);
		unsigned short valTimerHigh = (unsigned short) (valTimer >> 16);

		// Enable loading of timer values
		m_RegShadow[ Reg_Command ] |= RegBit_TimerLoad;	// set bit to 1
		Write( Reg_Command, m_RegShadow[ Reg_Command ] );

			// clear bits to 0
			m_RegShadow[ Reg_Timer ] = 0;
			// set our new bits
			m_RegShadow[ Reg_Timer ] |= ( valTimerLow & RegBitMask_Timer )<< RegBitShift_Timer;
			Write( Reg_Timer, m_RegShadow[ Reg_Timer ] );

			// clear bits to 0
			m_RegShadow[ Reg_VBinning ] = 0;

			// set our new bits
			m_RegShadow[ Reg_VBinning ] |= ( VBin & RegBitMask_VBinning ) << RegBitShift_VBinning;
			m_RegShadow[ Reg_VBinning ] |= ( valTimerHigh & RegBitMask_Timer2 ) << RegBitShift_Timer2;
			Write( Reg_VBinning, m_RegShadow[ Reg_VBinning ] );

		// Disable loading of timer values
		m_RegShadow[ Reg_Command ] &= ~RegBit_TimerLoad;	// set bit to 0
		Write( Reg_Command, m_RegShadow[ Reg_Command ] );
		/////////////////////////////////////
	}
	else
	{
		// clear bits to 0
		m_RegShadow[ Reg_VBinning ] &= ~( RegBitMask_VBinning << RegBitShift_VBinning );

		// set our new bits
		m_RegShadow[ Reg_VBinning ] |= ( VBin & RegBitMask_VBinning ) << RegBitShift_VBinning;
		Write( Reg_VBinning, m_RegShadow[ Reg_VBinning ] );
	}

}

// Start flushing the entire CCD (rows = -1) or a specific number or rows
void CCameraIO::Flush( short Rows )
{
	if ( Rows == 0 ) return;

	unsigned short AIC = (unsigned short) ( m_Columns - m_BIC - m_ImgColumns );
	unsigned short Pixels = (unsigned short) ( m_ImgColumns / m_HFlush );
	if ( m_ImgColumns % m_HFlush > 0 ) Pixels++;	// round up if necessary
	LoadColumnLayout( AIC, (unsigned short) m_BIC, Pixels );

	LoadTimerAndBinning( 0.0, m_HFlush, m_VFlush );

	if ( Rows > 0 )
	{		
		LoadLineCounter( (unsigned short) Rows );	
		StartFlushing();

		/////////////////////////////////////
		// Wait until camera is done flushing
		clock_t StopTime = clock() + long( m_Timeout * CLOCKS_PER_SEC );	// wait at most m_Timeout seconds
		while ( true )
		{
			unsigned short val = 0;
			Read( Reg_Status, val );
			if ( ( val & RegBit_FrameDone ) != 0 ) break;
			
			if ( clock() > StopTime ) break;		// Timed out
		}
	}
	else
	{
		LoadLineCounter( (unsigned short) m_ImgRows );
		StartFlushing();
	}
}

void CCameraIO::StartFlushing()
{
	/////////////////////////////////////
	// Start flushing
	m_RegShadow[ Reg_Command ] |= RegBit_StartFlushing;	// set bit to 1
	Write( Reg_Command, m_RegShadow[ Reg_Command ] );
	
	m_RegShadow[ Reg_Command ] &= ~RegBit_StartFlushing;// set bit to 0
	Write( Reg_Command, m_RegShadow[ Reg_Command ] );
	/////////////////////////////////////
}

void CCameraIO::StopFlushing()
{
	/////////////////////////////////////
	// Stop flushing
	m_RegShadow[ Reg_Command ] |= RegBit_StopFlushing;	// set bit to 1
	Write( Reg_Command, m_RegShadow[ Reg_Command ] );
	
	m_RegShadow[ Reg_Command ] &= ~RegBit_StopFlushing;	// set bit to 0
	Write( Reg_Command, m_RegShadow[ Reg_Command ] );
	/////////////////////////////////////
}





