/******************************************************************************
** Copyright 2015 MBARI - Monterey Bay Aquarium Research Institute
*******************************************************************************
*******************************************************************************
** Summary  : Routines to access Zeiss MMS-UV Spectrometer fitted with Cronin PIC based RS232 interface
** Filename : cronin.c
** Author   : Luke Coletti
** Project  :
** Version  : 1.0
** Compiler : MetroWerks Code Warrior v8.3
** Created  : 12/15/14
** Archived :
*******************************************************************************
** Modification History:
** 01/02/15 : CF2 Port, LJC
*******************************************************************************/

#include	<cfxpico.h>
#include	<stdio.h>
#include	<stdlib.h>
#include	<math.h>

#include	<msc_main.h>

extern uchar isus_spec_pwr;
extern uchar sys_message_enable;
extern ulong isus_sample_time;
extern ulong isus_spec_period;
extern float DataMean, DataStdDev;
extern float sysTemperature, sysHumidity;  


int SpecPwrOn( TUPort **tup )
{
	char ebuf[ERRBUFSIZE];
	RTCTimer TmOut;

	if( isus_spec_pwr == TRUE )
		return( TRUE );

	//turn on 5V power to Cronin MMS-UV spectrometer electronics
	PCAL6416_WritePortBit(PCAL6416_OUTPUT_PORT0, SPEC_PWR_ON, HIGH);
	
	DelayMilliSecs( 10 );
	
	//enable RS232 transmitters
	RegIO_SetBit( SPEC_232_ENABLE );

	//wait for SPEC_CTS to go low (asserted)
	//it never will if spectrometer isn't connected as the RS232 receiver output is high via internal pull-down on its input
	RTCCountdownTimerSetup(&TmOut, SPEC_CTS_TIMEOUT);       // timeout in 500ms
	do {
		if( RTCCountdownTimeout(&TmOut) == true ) {
			sprintf(ebuf, "Powerup Timeout, Spectrometer CTS never asserted");
			LogErrorMsg(ebuf, (int)SPEC_PWR_ON_FAILURE, sys_message_enable);
			*tup = 0;
			return( TIMEOUT );
		}
	} while( RegIO_RdWord() & SPEC_CMOS_CTS );

	//wait for SPEC_CTS to return back high (released)
	RTCCountdownTimerSetup(&TmOut, SPEC_CTS_TIMEOUT);       // timeout in 500ms
	do {
		if( RTCCountdownTimeout(&TmOut) == true ) {
			sprintf(ebuf, "Powerup Timeout, Spectrometer CTS never released");
			LogErrorMsg(ebuf, (int)SPEC_PWR_ON_FAILURE, sys_message_enable);
			*tup = 0;
			return( TIMEOUT );
		}
	} while( !(RegIO_RdWord() & SPEC_CMOS_CTS) );
		
	//set SPEC_RTC high (released state)
	PinSet( 15 );	

	*tup = TUOpen(TPUChanFromPin(35), TPUChanFromPin(37), 115200, 0);
	if( *tup == 0 ) {
		sprintf(ebuf, "Init Error, Spectrometer TPU UART");
		LogErrorMsg(ebuf, (int)SPEC_TPU_INIT_ERROR, sys_message_enable);
		return( ERROR );
	}

	Delay1ms();

	TURxFlush( *tup );

	isus_spec_pwr = TRUE;

	return( TRUE );

}

int SpecPwrOff( TUPort *tup )
{

	if( tup != 0 ) {
		TURxFlush( tup );
		TUClose( tup );
	}

	PinClear( 15 );
		
	RegIO_ClrBit( SPEC_232_ENABLE );

	PCAL6416_WritePortBit(PCAL6416_OUTPUT_PORT0, SPEC_PWR_ON, LOW);

	isus_spec_pwr = FALSE;

	return( TRUE );
}

int ScanSpectra( TUPort *tup, FILE *fptr, ushort scan_num, ushort *Data, uchar ScanType )
{
char   *ScanTypes[] = {"D", "S"};
char   ebuf[ERRBUFSIZE];
char   SpecCmd[128];
int    i, loop;
short  val1, val2; 
struct tm *t;
RTCTimer TmOut;

    if( isus_spec_pwr != TRUE )
	  return( ERROR );

    if( ( ScanType < 0 ) || ( ScanType > 1 ) )
      return( ERROR );
  
    //negate RTS output  
	PinSet( 15 );
	
	//set spec trigger mode to "MBARI CUSTOM"
    TUPuts(tup, "E5\r" );
	DelayMilliSecs( 50 );
	
	//set spec scan buffer depth
	sprintf(SpecCmd, "R%hu\r", scan_num );
	TUPuts( tup, SpecCmd );
	DelayMilliSecs( 50 );
	
	//set spec integration period
	sprintf(SpecCmd, "I%lu\r", isus_spec_period );
	TUPuts( tup, SpecCmd );
	DelayMilliSecs( 50 );	
  
    for( loop=0; loop<scan_num; loop++ ){
		
  	  HIH6130_ReadTempHumidity( &sysTemperature, &sysHumidity );
	
      isus_sample_time = RTCGetTime( NULL, NULL );            //get time at start of data collection
	
	  //start "automatic" scanning	
	  TUPuts( tup, "S\r" );
	
	  //wait for SPEC_CTS to go low (asserted)
	  RTCCountdownTimerSetup(&TmOut, SPEC_CTS_TIMEOUT);       // timeout in 500ms
	  do {
	  	  if( RTCCountdownTimeout(&TmOut) == true ) {
			sprintf(ebuf, "Scan Timeout, Spectrometer CTS never asserted");
			LogErrorMsg(ebuf, (int)SPEC_CTS_SCAN_ERROR, sys_message_enable);
			return( TIMEOUT );
		}
	  } while( RegIO_RdWord() & SPEC_CMOS_CTS );

	  //wait for SPEC_CTS to return back high (released)
	  RTCCountdownTimerSetup(&TmOut, SPEC_INT_TIMEOUT);       // timeout in 15 seconds
	  do {
		if( RTCCountdownTimeout(&TmOut) == true ) {
			sprintf(ebuf, "Scan Timeout, Spectrometer CTS never released");
			LogErrorMsg(ebuf, (int)SPEC_CTS_SCAN_ERROR, sys_message_enable);
			return( TIMEOUT );
		}
	  } while( !(RegIO_RdWord() & SPEC_CMOS_CTS) );
	

	  //get data, one byte at a time, using RTS/CTS handshaking	
	  TUPuts( tup, "g\r" );
	
	  //wait for SPEC_CTS to go low (asserted)
	  RTCCountdownTimerSetup(&TmOut, SPEC_CTS_TIMEOUT);       // timeout in 500ms
	  do {
	  	  if( RTCCountdownTimeout(&TmOut) == true ) {
			sprintf(ebuf, "Data Timeout, Spectrometer CTS never asserted");
			LogErrorMsg(ebuf, (int)SPEC_CTS_DATA_ERROR, sys_message_enable);
			return( TIMEOUT );
		}
	  } while( RegIO_RdWord() & SPEC_CMOS_CTS );
	  
	  
	  // MMS_UV Spectometer sends pixel 256 (400nm) first and pixel 1 (200nm) last.
	  // Load data into array in the opposite oder, i.e., SpecData[0] = 200nm 
	  TURxFlush( tup );
	  for( i = 255; i >= 0; i-- ){
	   
	   PinClear( 15 );	//assert RTS output
	   val1 = TURxGetByteWithTimeout( tup, SPEC_DAT_TIMEOUT );                     /* get high byte of value */
       PinSet( 15 );	//negate RTS output
	   
	   Delay10us();
	   
	   PinClear( 15 );	//assert RTS output
	   val2 = TURxGetByteWithTimeout( tup, SPEC_DAT_TIMEOUT );                     /* get low  byte of value */
       PinSet( 15 );	//negate RTS output
	   
       if( (val1 == -1) || (val2 == -1) ){
          //printf("Data Timeout, cell = %d\n", i);
		  sprintf(ebuf, "Data Timeout, Data Byte Read");
		  LogErrorMsg(ebuf, (int)SPEC_DAT_READ_ERROR, sys_message_enable);
		  return( TIMEOUT );
          }
		  
        Data[i+(loop*256)] = ((val1 << 8) & 0xFF00) | (val2 & 0x00FF);
		
		if( i == 255 )
		  Data[i+(loop*256)] = 0;	
        }
		
	  //wait for SPEC_CTS to return back high (released), all data has been sent!
	  RTCCountdownTimerSetup(&TmOut, SPEC_DAT_TIMEOUT);       // timeout in 500ms
	  do {
		if( RTCCountdownTimeout(&TmOut) == true ) {
			sprintf(ebuf, "Data Timeout, Spectrometer CTS never released");
			LogErrorMsg(ebuf, (int)SPEC_CTS_SCAN_ERROR, sys_message_enable);
			return( TIMEOUT );
		}
	  } while( !(RegIO_RdWord() & SPEC_CMOS_CTS) );

      if( ScanType == DARKCURRENT_SCAN )
        ShortVectorMeanStdDev(100, &Data[(loop*256)], &DataMean, &DataStdDev);
      else
        ShortVectorMeanStdDev(256-1, &Data[(loop*256)], &DataMean, &DataStdDev);

      if( fptr != FNULL ){
        t = localtime(&isus_sample_time);

        fprintf(fptr, "%s,%02d/%02d/%02d %02d:%02d:%02d,%.2f,%.2f,%.2f,", ScanTypes[ScanType], t->tm_mon+1, t->tm_mday, t->tm_year+1900, t->tm_hour, t->tm_min, t->tm_sec, sysTemperature, DataMean, DataStdDev);

        for(i = 0; i < 256; i++){
          if(i != 255)
            fprintf(fptr, "%hu,", Data[i+(loop*256)]);
          else
            fprintf(fptr, "%hu", Data[i+(loop*256)]);
          }
        fprintf(fptr, "\n");
        }
		
	} /* end loop */	

    return( TRUE );

}

int SingleScanSpectra( TUPort *tup, ulong scan_period, ushort *Data )
{
char   ebuf[ERRBUFSIZE];
char   SpecCmd[128];
int	   i;
short  val1, val2; 
RTCTimer TmOut;

    if( isus_spec_pwr != TRUE )
	  return( ERROR );

    //negate RTS output  
	PinSet( 15 );
	
	//set spec trigger mode to "MBARI CUSTOM"
    TUPuts(tup, "E5\r" );
	DelayMilliSecs( 50 );
	
	//set spec scan buffer depth
	sprintf(SpecCmd, "R1\r");
	TUPuts( tup, SpecCmd );
	DelayMilliSecs( 50 );
	
	//set spec integration period
	sprintf(SpecCmd, "I%lu\r", scan_period );
	TUPuts( tup, SpecCmd );
	DelayMilliSecs( 50 );	
  
	//start "automatic" scanning	
	TUPuts( tup, "S\r" );
	
	//wait for SPEC_CTS to go low (asserted)
	RTCCountdownTimerSetup(&TmOut, SPEC_CTS_TIMEOUT);       // timeout in 500ms
	do {
		if( RTCCountdownTimeout(&TmOut) == true ) {
			sprintf(ebuf, "Scan Timeout, Spectrometer CTS never asserted");
			LogErrorMsg(ebuf, (int)SPEC_CTS_SCAN_ERROR, sys_message_enable);
			return( TIMEOUT );
		}
	} while( RegIO_RdWord() & SPEC_CMOS_CTS );

	//wait for SPEC_CTS to return back high (released)
	RTCCountdownTimerSetup(&TmOut, SPEC_INT_TIMEOUT);       // timeout in 15 seconds
	do {
		if( RTCCountdownTimeout(&TmOut) == true ) {
			sprintf(ebuf, "Scan Timeout, Spectrometer CTS never released");
			LogErrorMsg(ebuf, (int)SPEC_CTS_SCAN_ERROR, sys_message_enable);
			return( TIMEOUT );
		}
	} while( !(RegIO_RdWord() & SPEC_CMOS_CTS) );
	

	//get data, one byte at a time, using RTS/CTS handshaking	
	TUPuts( tup, "g\r" );
	
	//wait for SPEC_CTS to go low (asserted)
	RTCCountdownTimerSetup(&TmOut, SPEC_CTS_TIMEOUT);       // timeout in 500ms
	do {
	  	if( RTCCountdownTimeout(&TmOut) == true ) {
			sprintf(ebuf, "Data Timeout, Spectrometer CTS never asserted");
			LogErrorMsg(ebuf, (int)SPEC_CTS_DATA_ERROR, sys_message_enable);
			return( TIMEOUT );
		}
	} while( RegIO_RdWord() & SPEC_CMOS_CTS );
	  
	  
	// MMS_UV Spectometer sends pixel 256 (400nm) first and pixel 1 (200nm) last.
	// Load data into array in the opposite oder, i.e., SpecData[0] = 200nm 
	TURxFlush( tup );
	for( i = 255; i >= 0; i-- ){
	   
	   PinClear( 15 );	//assert RTS output
	   val1 = TURxGetByteWithTimeout( tup, SPEC_DAT_TIMEOUT );                     /* get high byte of value */
       PinSet( 15 );	//negate RTS output
	   
	   Delay10us();
	   
	   PinClear( 15 );	//assert RTS output
	   val2 = TURxGetByteWithTimeout( tup, SPEC_DAT_TIMEOUT );                     /* get low  byte of value */
       PinSet( 15 );	//negate RTS output
	   
       if( (val1 == -1) || (val2 == -1) ){
          //printf("Data Timeout, cell = %d\n", i);
		  sprintf(ebuf, "Data Timeout, Data Byte Read");
		  LogErrorMsg(ebuf, (int)SPEC_DAT_READ_ERROR, sys_message_enable);
		  return( TIMEOUT );
          }
		  
        Data[i] = ((val1 << 8) & 0xFF00) | (val2 & 0x00FF);
		
		if( i == 255 )
		  Data[i] = 0;	
    }
		
	//wait for SPEC_CTS to return back high (released), all data has been sent!
	RTCCountdownTimerSetup(&TmOut, SPEC_DAT_TIMEOUT);       // timeout in 500ms
	do {
		if( RTCCountdownTimeout(&TmOut) == true ) {
			sprintf(ebuf, "Data Timeout, Spectrometer CTS never released");
			LogErrorMsg(ebuf, (int)SPEC_CTS_SCAN_ERROR, sys_message_enable);
			return( TIMEOUT );
		}
	} while( !(RegIO_RdWord() & SPEC_CMOS_CTS) );


    return( TRUE );

}

int TUGetsTmout(TUPort *tup, char *buf, ushort buflen, ushort seconds)
{
	register int c;
	register int nchars;
	RTCTimer TmOut;

	RTCCountdownTimerSetup( &TmOut, (1000000L * seconds) );         // set timeout

	nchars = 0;
	buf[0] = '\0';

	while(  RTCCountdownTimeout(&TmOut) != true ) {
		if ( TURxQueuedCount(tup) ) {
			switch( c = TURxGetByte(tup, false) ) {
			case '\n':
			case '\r':
				if(nchars > 0)
					return( nchars );
				else
					break;

			default:
				buf[nchars++] = (char)c;
				buf[nchars] = '\0';
				if ( nchars >= (buflen-1) )
					return( nchars );
				break;
			} // end switch
		} // end if
	} // end while

	if( RTCCountdownTimeout(&TmOut) == true )
		return( TIMEOUT );

	return( nchars );

}

//spectrometer command interpreter only breaks on ^M
int TUPuts(TUPort *tup, char *str)
{

	while( *str ) {
		if( (*str == '\n') || (*str == '\r') ) {
			TUTxPutByte(tup, '\r', FALSE);
			break;
		}
		TUTxPutByte(tup, *str++, FALSE);
	}

	TUTxWaitCompletion(tup);

	return( TRUE );

}