/*
 * David Muller; Germán Alfaro
 * davehmuller@gmail.com; alfaro.germanevera@gmail.com
 *
 * 2/2014 Robert Glatts
 * rglatts@ucsd.edu
 */


/*
 * System.c contains a variety of functions related to sensors and basic system utilities.
 */

#include "System.h"

#include "IO.h"
#include "ADS1248_iso.h"
#include "ADS1248_noniso.h"
#include "MicroCAT.h"
#include "Optode.h"
#include "Sleep.h"
#include "SDCard.h"

#include "uartstdio.h"	// User local version with larger RX buffer

// Global variables
unsigned long timer_10ms;

/*****************************************************************************
*
* System tick interrupt handler. Provides 10 ms tick for various timing requirements.
* FatFs requires a timer tick every 10 ms for internal timing purposes.
*
*****************************************************************************/
void
SysTickHandler(void)
{
	disk_timerproc();	// FatFs
	timer_10ms++;			// For Timer_10ms()
}

/****************************************************************************
 * Timer_10ms()
 *
 * Returns value of 10 ms system tick variable. Can be used for general
 * purpose timing where a simple delay is not workable.
 * Call with "reset" true to reset timer to zero.
 * RCG 4/14
 ***************************************************************************/
unsigned long Timer_10ms(bool reset)
{
	if(reset) timer_10ms = 0;
	return timer_10ms;
}



/*
 * Retrieves the saved sys_data variables from their storage
 * location in EEPROM (when the device hibernates, sys_data variables
 * are stored in EEPROM).
 * If no value has been assigned to a sys_data variable, a default
 * value is assigned.
 */
void retrieveSysDataVariables()
{
	uint32_t ulStatus;

	//enable the EEPROM
	ROM_SysCtlPeripheralEnable(SYSCTL_PERIPH_EEPROM0);
	ulStatus = EEPROMInit();
	if( ulStatus != EEPROM_INIT_OK)
	{
		uprintf("EEPROM Initialization error.\n");
	}


	//read the sys_data struct out of EEPROM.  Last argument ensures # of bytes is multiple of 4
	EEPROMRead((uint32_t*) &sys_data, 0x400, (sizeof(sys_data) + 3) & ~3);
}

/*
 * batt_volt()
 * Returns main battery voltage on controller side of isolation diodes
 * RCG 4/14
 */
float batt_volt()
{
	float ADC_volts, batt_volt;

	ADC_volts = pollADS1248_noniso(0, 7, 1);
	batt_volt = ADC_volts * 11;  	// 11-to-1 divider
	return batt_volt;
}

/*
 * batt_volt_iso()
 * Returns isolated battery voltage
 * RCG 4/14
 */
float batt_volt_iso()
{
	float ADC_volts, batt_volt;

	ADC_volts = pollADS1248_iso(5, 6, 1, 1, 5);		// Chan 5-6, 1 trial, gain = 1, sps = 5 Hz
	batt_volt = ADC_volts * 11;  	// 11-to-1 divider
	return batt_volt;
}

/*
 * temp_int()
 * Returns internal temperature
 * RCG 4/14
 */
float controller_temp()
{
	float ADC_volts, temp;

	ADC_volts = pollADS1248_noniso(2, 7, 1); // Chan 2-7, 1 trial
	temp = (ADC_volts * 1000 - 480) / 15.6;
	return temp;
}


/*
 * depth()
 * Returns depth in meters from Honeywell MLH100PGB 100 psi pressure sensor
 * RCG 4/14
 */
float pressure(void)
{
	float ADCvolts, pressure;

	ADCvolts = pollADS1248_noniso(3, 7, 1);	// Result in volts

	pressure = ((ADCvolts * 3.2) - 0.5) * (100 / 4 / 1.45);	// 3.2-to-1 divider, 0.5-4.5 V span, 1.45 dBar/psi
	return pressure; 	// Pressure in dBar
}

// Calculates temperature from Durafet's thermistor. V_therm is the voltage from thermistor, measured using isolated 24-bit ADC.
// TCOffset is an user input, to correct thermistor to be more accurate.
float DuraFET_temp(float V_therm, float TCOffset)
{
	// Declare variables
	float Rtherm;
	float TC;

	// Thermistor coefficients
	float c0 = 340.9819863;
	float c1 = -9.10257E-05;
	float c2 = -95.08806667;
	float c3 = 0.965370274;


	// Calculate TC
	Rtherm = 20000 / ((3.3 / V_therm) - 1);
	TC = c0 + c1*Rtherm + c2*(log10(Rtherm)) + c3*(pow(log10(Rtherm), 3));

	// Apply offset to TC; TCOffset is an user input,
	// or derived during pH calibration process.
	TC = TC + TCOffset;
	return TC;
}

/*
* Calculate pH using external reference.
* Eext refers to the voltage from the external reference, measured using isolated 24-bit ADC.
* TC should be calculated using fucntion getDurafettemp.
* Salt refers to salinity, extracted from SBE 37.
* RCG 5/14
*/
float calc_pHext(float Eext, float TC, float salt)
{
	float TK;				// Temperature in Kelvin
	float S_T;				// Nernst Slope
	float Eoext;			// Calibration coefficient corrected to in situ temperature
	float Z; 				// Ionic Strength; Dickson 2007
	float SO4_tot; 			// Total sulfate concentration; Dickson 2007
	float mCl; 				// Molality of Chloride; Dickson 2007;
	float K_HSO4; 			// Bisulfate acidity constant; Dickson 1990.
	float DHconst;			// Debye-Huckel; Khoo et al. 1997
	float log10gamma_HCl;	// log10 of mean activity coefficient of HCl in seawater
	float pHext_free;		// pH on free scale calculated from external reference
	float pHext_tot;		// pH on total scale calculated from external reference

	// Start calculations
	TK = TC + 273.15;
	S_T = (R_ * TK) / F_ * LN10;
	Eoext = sys_data.Eo_ext_25C + dEoEXT_dt * (TC - 25);
	Z = 19.924 * salt / (1000 - 1.005 * salt); // Ionic strength
	SO4_tot = (0.14 / 96.062) * (salt / 1.80655); // molality sulfate from conservative relationship.
	mCl = 0.99889 / 35.453 * salt / 1.80655; // moallity chloride from conservative relationship.
	K_HSO4 =  exp(-4276.1/(TK)+141.328-23.093*log(TK)+(-13856/(TK)+324.57-47.986*log(TK))*sqrt(Z)+(35474/(TK)-771.54+114.723*log(TK))*Z-2698/(TK)*pow(sqrt(Z), 3)+1776/(TK)*pow(Z, 2)+log(1-0.001005*salt));
	DHconst = 0.00000343 * (pow(TC, 2)) + 0.00067524 * TC + 0.49172143; //Debye-Huckel
	log10gamma_HCl = 2*(-DHconst * sqrt(Z) / (1 + 1.394 * sqrt(Z)) + (0.08885 - 0.000111 * TC) * Z);

	pHext_free =  -( (Eoext - Eext) - S_T * (log10(mCl) + log10gamma_HCl) ) / S_T;
	pHext_tot = pHext_free - log10(1 + SO4_tot / K_HSO4); // pH from external reference electrode on total scale.
	return pHext_tot;

}

/*
* calc_pHint()
* Function to calculate pH using internal reference.
* Eint refers to the voltage from the internal reference, measured using the isolated 24-bit ADC.
* TC should be calculated using function getDurafettemp.
* RCG 5/14
*/
float calc_pHint(float Eint, float TC)
{
	float TK; 		// Temperature in Kelvin
	float S_T; 		// Nernest Slope
	float Eoint;  	// Calibration coefficient corrected to in situ temperature.
	float pHint;

	TK = TC + 273.15;
	S_T = (R_ * TK) / F_ * LN10;

	Eoint = sys_data.Eo_int_25C + dEoINT_dt * (TC - 25);
	pHint = (Eint - Eoint) / S_T;

	return pHint;
}

/*
 * ph_calib()
 * Calculates DuraFET pH sensorcalibration values
 * using either user entered values, or automatic
 * calibration using temperature and salinity derived
 * from a MicroCAT CTD.
 * RCG 5/14
 */
void ph_calib(void)
{
	int response, i, j, samp, lines;
	char buff[20];
	static float Vint=0, Vext=0, pHcalpt=0, tempC, salt;
	float ftemp, tempK, E0int, S_T, Z, SO4_tot, mCl, K_HSO4, pHint_free, dummy, Vtherm, TCOffset;
	float DHconst, log10gamma_HCl, mHfree, aHfree_aCl, E0ext, sqrt_Z, E0int25, E0ext25;	// beta_SO4,
	float StdDev[SD_VALUES][SD_DIFF_SQ+1], diff, sum;
	float Vint_SD, Vext_SD, Vtherm_SD, tempC_SD, salt_SD;


	// pH Calibration point
	while(1)
	{
		sprintf(buff, "\n\nEnter pH calibration point [%f]: ", pHcalpt);
		uprintf("%s", buff);
		if(getUserInput(buff, 20))
		{
			if(sscanf(buff, "%f", &ftemp) == 1)
			{
				if(ftemp >= 0 && ftemp <= 14)
				{
					pHcalpt = ftemp;
					break;
				}
				else uprintf("\nEnter between 0 and 14");
				continue;
			}
			else break;
		}
		else break;
	}

	// Automatic or manual calibration?
	response = yesOrNoMenuChoice("\n\nPerform automatic DuraFET calibration (Y/N) [N]? ", NO);

	// Manual calibration
	if(response == NO)
	{
		// Vint
		while(1)
		{
			sprintf(buff, "\n\nEnter Vint [%f]: ", Vint);
			uprintf("%s", buff);
			if(getUserInput(buff, 20))
			{
				if(sscanf(buff, "%f", &ftemp) == 1)
				{
					if(ftemp >= (-2) && ftemp <= 2)
					{
						Vint = ftemp;
						break;
					}
					else uprintf("Enter number between -2 and 2\n\n");
					continue;
				}
				else break;
			}
			else break;
		}

		// Vext
		while(1)
		{
			sprintf(buff, "\n\nEnter Vext [%f]: ", Vext);
			uprintf("%s", buff);
			if(getUserInput(buff, 20))
			{
				if(sscanf(buff, "%f", &ftemp) == 1)
				{
					if(ftemp >= (-2) && ftemp <= 2)
					{
						Vext = ftemp;
						break;
					}
					else uprintf("Enter number between -2 and 2\n\n");
					continue;
				}
				else break;
			}
			else break;
		}

		// Temperature
		while(1)
		{
			sprintf(buff, "\nEnter bath temperature [%f]: ", tempC);
			uprintf("%s", buff);
			if(getUserInput(buff, 20))
			{
				if(sscanf(buff, "%f", &ftemp) == 1)
				{
					if(ftemp >= 0 && ftemp <= 100)
					{
						tempC = ftemp;
						break;
					}
					else uprintf("\nEnter between 0 and 100");
					continue;
				}
				else break;
			}
			else break;
		}

		// Salinity
		while(1)
		{
			sprintf(buff, "\nEnter bath salinity [%f]: ", salt);
			uprintf("%s", buff);
			if(getUserInput(buff, 20))
			{
				if(sscanf(buff, "%f", &ftemp) == 1)
				{
					if(ftemp >= 0 && ftemp <= 100)
					{
						salt = ftemp;
						break;
					}
					else uprintf("\nEnter between 0 and 100");
					continue;
				}
				else break;
			}
			else break;
		}
	}

	// Automatic calibration
	else
	{
		// Clear standard deviation array
		for(j=0; j<SD_AVG; j++)
		{
			for(i=0; i<5; i++) StdDev[i][j] = 0;
		}

		j = 0;				// Reset standard deviation array counter
		lines = 20; 		// Force program to write column labels before first data line

		openMicroCAT();
		openADS1248_iso();
		ROM_SysCtlDelay(ONESEC);	// Let instruments and sensors settle
		uprintf("\n\nPlace DuraFET and MicroCAT in calibration bath.");
		uprintf("\nHit any key when values have stabilized...");
		UARTFlushRx();

		while(1)
		{
			Vint = pollADS1248_iso(1, 6, sys_data.sample_average, sys_data.Vint_gain, sys_data.Vint_sps);
			Vext = pollADS1248_iso(4, 6, sys_data.sample_average, 1, 5);
			Vtherm = pollADS1248_iso(0, 6, sys_data.sample_average, 1, 5);	// Chan 0-6, trials = user, gain = 1, sps = 5 Hz
//			TC = DuraFET_temp(Vtherm, sys_data.TCOffset);

			pollMicroCAT();
			ROM_SysCtlDelay(5*ONESEC);	// Wait for instruments to respond
			parseMicroCATData();	// Extract the data from response strings

			// Update salinity from MicroCAT data
			if( sscanf( MicroCAT_buff, "%f %f %f %s %s", &tempC, &dummy, &salt, buff, buff) != 5)
			{
				tempC = salt = 0;
			}

			// Store current sample values
			if(samp < SD_AVG) samp++;
			else samp = 0;
			StdDev[0][samp] = Vint;
			StdDev[1][samp] = Vext;
			StdDev[2][samp] = Vtherm;
			StdDev[3][samp] = tempC;
			StdDev[4][samp] = salt;


			// Find the mean of the last SD_AVG samples for each sensor
			for(i=0; i<SD_VALUES; i++)
			{
				sum = 0;

				for(j=0; j<SD_AVG; j++)
				{
					sum += StdDev[i][j];
				}

				StdDev[i][SD_MEAN] = sum / SD_AVG;	// Current mean for each sensor
			}

			// Find the difference squared for each of the last SD_AVG samples
			// Then find the sum of the squared differences
			for(i=0; i<SD_VALUES; i++)
			{
				sum = 0;

				for(j=0; j<SD_AVG; j++)
				{
					 diff = (StdDev[i][j] - StdDev[i][SD_MEAN]) ;	// Difference for each sample
					 sum += diff * diff;							// Sum of differences squared
				}

				StdDev[i][SD_DIFF_SQ] = sum;
			}

			// Take square root for each sensor
			Vint_SD = sqrt(StdDev[0][SD_DIFF_SQ]);
			Vext_SD = sqrt(StdDev[1][SD_DIFF_SQ]);
			Vtherm_SD = sqrt(StdDev[2][SD_DIFF_SQ]);
			tempC_SD = sqrt(StdDev[3][SD_DIFF_SQ]);
			salt_SD = sqrt(StdDev[4][SD_DIFF_SQ]);

			// Write column headers every 20 lines
			if(lines >= 20)
			{
				uprintf("\n\nVint\t\tVint-SD\t"
						"Vext\t\tVext-SD\t"
						"Vtherm\tVtherm-SD\t"
						"Temp\t\tTemp-SD\t"
						"Salinity\tSal-SD");

				lines = 1;		// Reset line counter
			}
			else lines++;

			// Write data line
			uprintf("\n%8.5f\t%8.5f\t"
					"%8.5f\t%8.5f\t"
					"%8.5f\t%8.5f\t"
					"%8.5f\t%8.5f\t"
					"%8.5f\t%8.5f",
					Vint, Vint_SD,
					Vext, Vext_SD,
					Vtherm, Vtherm_SD,
					tempC, tempC_SD,
					salt, salt_SD);

			// Key hit, so display current averaged values
			if(kbhit())
			{
				uprintf("\n\nAverage of last %d values", SD_AVG);

				Vint = StdDev[0][SD_MEAN];
				uprintf("\nVint = %f V  SD = %f", Vint, Vint_SD);

				Vext = StdDev[1][SD_MEAN];
				uprintf("\nVext = %f V  SD = %f", Vext, Vext_SD);

				Vtherm = StdDev[2][SD_MEAN];
				uprintf("\nVtherm = %f C  SD = %f", Vtherm, Vtherm_SD);

				tempC = StdDev[3][SD_MEAN];
				uprintf("\nMicroCAT Temperature = %f C  SD = %f", tempC, tempC_SD);

				salt = StdDev[4][SD_MEAN];
				uprintf("\nMicroCAT Salinity = %f  SD = %f", salt, salt_SD);

				response = yesOrNoMenuChoice("\n\nValues acceptable (Y/N) [N]? ", NO);
				if(response == NO) continue;
				else break;
			}
		}

		closeADS1248_iso();
		closeMicroCAT();
	}

	// Calculate E0int25 and E0ext25 using either manual or automatic values
	tempK = tempC + 273.15;  					// Convert temp from C to K
	S_T = (R_ * tempK) / F_ * log(10); 			// Nernst temp dependence
	E0int = Vint - S_T * pHcalpt; 				// Calc E0int from Nernst & pH @ calibration point
	E0int25 = E0int + dEoINT_dt * (25 - tempC);

	Z = 19.924 * salt / (1000 - 1.005 * salt); 			// Ionic strength, Dickson et al. 2007
	sqrt_Z = sqrt(Z);

	SO4_tot = (0.14 / 96.062) * (salt / 1.80655);  	// Total conservative sulfate
	mCl = 0.99889/35.453*salt/1.80655; 				// Conservative chloride

	// Bisulfate equilibrium const., Dickson et al. 2007
	K_HSO4 = exp( -4276.1 / tempK + 141.328 - 23.093 * log(tempK) \
	          + ( -13856 / tempK + 324.57 - 47.986 * log(tempK) ) * sqrt_Z \
	          + ( 35474 / tempK - 771.54 + 114.723 * log(tempK) ) * Z - 2698 / tempK * pow(Z, 1.5) \
	          + 1776 / tempK * Z * Z + log(1 - 0.001005 * salt) );

	pHint_free = pHcalpt + log10( 1 + SO4_tot / K_HSO4);
	DHconst = 0.00000343 * tempC * tempC + 0.00067524 * tempC + 0.49172143; //Debye-Huckel, Khoo et al. 1977
	log10gamma_HCl = 2 * (-DHconst * sqrt_Z / ( 1 + 1.394 * sqrt_Z ) + (0.08885 - 0.000111 * tempC) * Z);
	mHfree = pow(10, (-pHint_free) );
	aHfree_aCl = mHfree * mCl * pow( 10, (log10gamma_HCl) );
	E0ext = Vext + S_T * log10(aHfree_aCl);
	E0ext25 = E0ext + dEoEXT_dt * (25-tempC);

	TCOffset =  tempC - DuraFET_temp(Vtherm, 0);

	uprintf("\n\nEo Int @ 25C = %f", E0int25);
	uprintf("\nEo Ext @ 25C = %f", E0ext25);
	uprintf("\nTCOffset = %f", sys_data.TCOffset);

	response = yesOrNoMenuChoice("\n\nAccept and store calibration values(Y/N) [N]? ", NO);
	if(response == YES)
	{
		sys_data.Eo_int_25C = E0int25;
		sys_data.Eo_ext_25C = E0ext25;
		sys_data.TCOffset = TCOffset;

		uprintf("\nCalibration values stored");
	}

}

/*
 * Store the deployment parameters to the beginning of a new deployment.
 * Returns non-zero if there is a file error. Usually because user forgot to
 * install a SD card.
 * Modified RCG 5/14
 */
int writeDeploymentInformationHeader(const time_t currentTime, const time_t firstSample)
{
	struct tm *time_struct;
	char buff[TIMESTAMPLENGTH];
	FIL fileObject;		// File object
	FRESULT fresult;
	UINT bw;

	// Open a file
	fresult = f_open(&fileObject, sys_data.fileName, FA_READ |FA_WRITE |FA_OPEN_ALWAYS);
	if(fresult != FR_OK)
	{
		uprintf("f_open error: %s\n", StringFromFresult(fresult));
		return 1;
	}

	// Seek to the end, to append our file
	fresult = f_lseek(&fileObject, fileObject.fsize);
	if(fresult != FR_OK)
	{
		uprintf("f_lseek error: %s\n", StringFromFresult(fresult));
		return 1;
	}

	// Store current time
	time_struct = localtime(&currentTime);
	strftime(buff, sizeof(buff),"\"Current time\"\t%Y/%m/%d %H:%M:%S", time_struct);
	fresult = f_write(&fileObject, buff, strlen(buff), &bw);
	if(fresult != FR_OK)
	{
		uprintf("Current time f_write error: %s\n", StringFromFresult(fresult));
		return 1;
	}

	// Store first sample time
	time_struct = localtime(&firstSample);
	strftime(buff, sizeof(buff),"\n\"First sample\"\t%Y/%m/%d %H:%M:%S", time_struct);
	fresult = f_write(&fileObject, buff, strlen(buff), &bw);
	if(fresult != FR_OK)
	{
		uprintf("First sample time f_write error: %s\n", StringFromFresult(fresult));
		return 1;
	}

	// Store timezone
	if(sys_data.GMT) sd_fprintf(&fileObject, "\n\"Time zone\"\tGMT");
	else sd_fprintf(&fileObject, "\n\"Time zone\"\tLocal");


	// Store the tab delimited deployment header. (Excel treats quoted string as text)
	sd_fprintf(&fileObject, "\n\"File name\"\t%s", sys_data.fileName);
	sd_fprintf(&fileObject, "\n\"User initials\"\t%s", sys_data.user);
	sd_fprintf(&fileObject, "\n\"Sampling period (s)\"\t%u", sys_data.sampling_period);
	sd_fprintf(&fileObject, "\n\"pH Sample average\"\t%u", sys_data.sample_average);
	sd_fprintf(&fileObject, "\n\"Pump on time (s)\"\t%u", sys_data.pumpon_time);
	sd_fprintf(&fileObject, "\n\"Low battery voltage (V)\"\t%f", sys_data.low_batt_volt);
	sd_fprintf(&fileObject, "\n\"TCOffset\"\t%f", sys_data.TCOffset);
	sd_fprintf(&fileObject, "\n\"Eo_int_25C\"\t%f", sys_data.Eo_int_25C);
	sd_fprintf(&fileObject, "\n\"Eo_ext_25C\"\t%f", sys_data.Eo_ext_25C);
	sd_fprintf(&fileObject, "\n\"Default salinity (ppt)\"\t%f", sys_data.default_sal);
	sd_fprintf(&fileObject, "\n\"Sensor name\"\t%s", sys_data.sensor_name);
	sd_fprintf(&fileObject, "\n\"DuraFET SN\"\t%s", sys_data.durafet_SN);
	sd_fprintf(&fileObject, "\n\"CAP adapter SN\"\t%s", sys_data.capadap_SN);
	sd_fprintf(&fileObject, "\n\"ISE SN\"\t%s", sys_data.ISE_SN);
	sd_fprintf(&fileObject, "\n\"MicroCAT SN\"\t%s", sys_data.microcat_SN);
	sd_fprintf(&fileObject, "\n\"Pump SN\"\t%s", sys_data.pump_SN);
	sd_fprintf(&fileObject, "\n\"Pressure sensor full-scale (psi)\"\t%d", sys_data.press_full_scale);
	sd_fprintf(&fileObject, "\n\"Vint gain\"\t%d", sys_data.Vint_gain);
	sd_fprintf(&fileObject, "\n\"Vint sample rate (sps)\"\t%d", sys_data.Vint_sps);

	if(sys_data.test_mode) sd_fprintf(&fileObject, "\n\"Test mode (pump off)\"");
	else sd_fprintf(&fileObject, "\n\"Deploy mode (pump on)\"");

	// Store the tab delimited column headers
	sd_fprintf(&fileObject, "\n\n\"Sample #\"\t");
	sd_fprintf(&fileObject, "\"Sample Time\"\t");
	sd_fprintf(&fileObject, "\"Main Batt\"\t");
	sd_fprintf(&fileObject, "\"Vtherm\"\t");
	sd_fprintf(&fileObject, "\"Vtherm std\"\t");
	sd_fprintf(&fileObject, "\"Vint\"\t");
	sd_fprintf(&fileObject, "\"Vint Std\"\t");
	sd_fprintf(&fileObject, "\"Vext\"\t");
	sd_fprintf(&fileObject, "\"Vext Std\"\t");
	sd_fprintf(&fileObject, "\"Iso Batt\"\t");
	sd_fprintf(&fileObject, "\"Controller Temp\"\t");
	sd_fprintf(&fileObject, "\"pH Temp\"\t");
	sd_fprintf(&fileObject, "\"Pressure (dBar)\"\t");
	sd_fprintf(&fileObject, "\"pH Int\"\t");
	sd_fprintf(&fileObject, "\"pH Ext\"\t");
	sd_fprintf(&fileObject, "\"Counter Leak\"\t");
	sd_fprintf(&fileObject, "\"Substrate Leak\"\t");
	sd_fprintf(&fileObject, "\"P/N\"\t\"S/N\"\t\"Oxygen uM\"\t\"O2 Sat\"\t\"Temp\"\t\"Dphase\"\t\"Bphase\"\t\"Rphase\"\t\"Bamp\"\t\"Bpot\""
			"\t\"Ramp\"\t\"Raw Temp\"\t");
	sd_fprintf(&fileObject, "Temp (C)\"\t\"Conductivity\"\t\"Salinity\"\t\"Date\"\t\"Time\"\n");

	sys_data.next_gdata_fptr = fileObject.fptr;		// Store the file pointer for the first sample (for gdata)

	// Close the file
	fresult = f_close( &fileObject);
	if(fresult != FR_OK)
	{
		uprintf("f_close error: %s\n", StringFromFresult(fresult));
		return 1;
	}

	return 0;
}


/*
 * sample()
 * Take measurements and write them to SD card.
 * RCG 5/14
 */
void get_sample(void)
{
	char timestamp[TIMESTAMPLENGTH], buff[80];
	uint32_t timeout;
	//uint32_t ticks;
	struct tm *time_struct;
	float dummy;
	float main_batt_volt, iso_batt_volt, temp_press, con_temp;
	float SBE_sal;			// MicroCAT salinity
	float SBE_temp;			// MicroCAT temperature
	float Vtherm;			// DuraFET raw temperature voltage
	float Vtherm_std; 		// Std Dev for Durafet Thermistor
	float Vint;				// DuraFET internal reference voltage
	float Vint_std; 		// Std Dev for Durafet Internal Voltage
	float TC;				// Calculated DuraFET temperature
	float Vext_ref;			// External (AUX) reference voltage
	float Vext_std; 		// Std Dev for Durafet External Voltage
	float pHext;			// Calculated pH using external reference
	float pHint;			// Calculated pH using internal reference
	float counter_leak;		// DuraFET counter leakage current
	float substrate_leak;	// DuraFET substrate leakage current

	FIL fileObject;
	FRESULT fresult;

	if(sys_data.test_mode == 1) uprintf("\nSampling...");

	// Pump on time
	if(sys_data.test_mode == 1) uprintf("\nPump on (test mode)\n");		// Don't want the pump to run in test deployment
	else pump_on();

	timeout = ROM_HibernateRTCGet() + sys_data.pumpon_time;

	do
	{
		ROM_SysCtlDelay(100 * MILLISECOND);
		if( kbhit() && sys_data.output == VERBOSE) uprintf("\nSampling, please wait...");

	} while( ROM_HibernateRTCGet() < timeout );

	pump_off();
	if(sys_data.test_mode == 1) uprintf("Pump off\n");

	//get the time from the RTC
//	ticks = ROM_HibernateRTCGet();
	time_struct = localtime(&sys_data.nextWakeUp);
	strftime(timestamp, sizeof(timestamp),"%Y/%m/%d %H:%M:%S", time_struct);

	// Open and poll the microCat and Optode
	openMicroCAT();
	openOptode();
	pressureOn();
	openADS1248_noniso();
	openADS1248_iso();

	ROM_SysCtlDelay(800*MILLISECOND);	// Let instruments and sensors settle

	pollMicroCAT();
	pollOptode();

	ROM_SysCtlDelay(ONESEC);	// Wait for instruments to respond // changed from 2*ONESEC 4/27/2017 -TW

	closeMicroCAT();
	closeOptode();

	parseMicroCATData();	// Extract the data from response strings
	parseOptodeData();

	// Update salinity from MicroCAT data
	if( sscanf( MicroCAT_buff, "%f %f %f %s %s", &SBE_temp, &dummy, &SBE_sal, buff, buff) != 5)
	{
		if(sys_data.test_mode == 1) uprintf("\n\nMicroCAT read error! Setting salinity to default\n");
		SBE_sal = sys_data.default_sal;
		error_store("MicroCAT read error");
	}

	// Read the non-isolated sensors
	temp_press = pressure();
	con_temp = controller_temp();
	main_batt_volt = batt_volt();

	// Read the isolated sensors
	Vtherm = pollADS1248_iso(0, 6, sys_data.sample_average, 1, 20);
	Vtherm_std = sys_data.AD24_std;
	Vint = pollADS1248_iso(1, 6, sys_data.sample_average, sys_data.Vint_gain, 20); // sps used to be sys_data.Vint_sps YT
	Vint_std = sys_data.AD24_std;
	Vext_ref = pollADS1248_iso(4, 6, sys_data.sample_average, 1, 20);
	Vext_std = sys_data.AD24_std;
	iso_batt_volt = batt_volt_iso();
	counter_leak = pollADS1248_iso(3, 2, 1, 1, 5);		// Chan 3-2, 1 trial, gain = 1, sps = 5 Hz
	substrate_leak = pollADS1248_iso(7, 6, 1, 1, 5);	// Chan 7-6, 1 trial, gain = 1, sps = 5 Hz

	// Shutdown sensors
	pressureOff();
	closeADS1248_noniso();
	closeADS1248_iso();

	// Calculate the external and internal pH values

	TC = DuraFET_temp(Vtherm, 0);//sys_data.TCOffset);
	pHext = calc_pHext(Vext_ref, TC, SBE_sal);
	pHint = calc_pHint(Vint, TC);

	// Open a file
	fresult = f_open(&fileObject, sys_data.fileName, FA_READ |FA_WRITE |FA_OPEN_ALWAYS);
	if(fresult != FR_OK)
	{
		uprintf("f_open error: %s\n", StringFromFresult(fresult));
		error_store("f_open error");
	}

	// Seek to the end, to append our file
	fresult = f_lseek(&fileObject, fileObject.fsize);
	if(fresult != FR_OK)
	{
		uprintf("f_lseek error: %s\n", StringFromFresult(fresult));
		error_store("f_lseek error");
	}

	// Store sample to SD card file
	sd_fprintf(&fileObject, "#%07d\t", sys_data.current_sample);
	sd_fprintf(&fileObject, "%s\t", timestamp);
	sd_fprintf(&fileObject, "%5.2f\t", main_batt_volt);
	sd_fprintf(&fileObject, "%8.6f\t", Vtherm);
	sd_fprintf(&fileObject, "%8.6f\t", Vtherm_std);
	sd_fprintf(&fileObject, "%8.6f\t", Vint);
	sd_fprintf(&fileObject, "%8.6f\t", Vint_std);
	sd_fprintf(&fileObject, "%8.6f\t", Vext_ref);
	sd_fprintf(&fileObject, "%8.6f\t", Vext_std);
	sd_fprintf(&fileObject, "%5.2f\t", iso_batt_volt);
	sd_fprintf(&fileObject, "%5.2f\t", con_temp);
	sd_fprintf(&fileObject, "%6.3f\t", TC); //pH temp
	sd_fprintf(&fileObject, "%6.3f\t", temp_press);
	sd_fprintf(&fileObject, "%7.6f\t", pHint);
	sd_fprintf(&fileObject, "%7.6f\t", pHext);
	sd_fprintf(&fileObject, "%7.4f\t", counter_leak);
	sd_fprintf(&fileObject, "%7.4f\t", substrate_leak);
	sd_fprintf(&fileObject, "%s\t", Optode_buff); //add tab after optode data 4/27/15 -TW
	sd_fprintf(&fileObject, "%s\n", MicroCAT_buff);


	// Close the file
	fresult = f_close( &fileObject );
	if(fresult != FR_OK)
	{
		uprintf("f_close error: %s\n", StringFromFresult(fresult));
		error_store("f_close error");
	}

	// Print test mode data to screen
	if(sys_data.test_mode == 1)
	{
		uprintf("\n\nSample # \t\t%u", sys_data.current_sample);
		uprintf("\nTimestamp \t\t%s", timestamp);
		uprintf("\nSBE response\t%s", MicroCAT_buff);
		uprintf("\nSBE salinity \t%.4f", SBE_sal);
		uprintf("\nSBE temperature \t%.4f", SBE_temp);
		uprintf("\nOptode response\t%s", Optode_buff);
		uprintf("\nVtherm \t\t%.6f V", Vtherm);
		uprintf("\nVtherm std \t\t%.6f V", Vtherm_std);
		uprintf("\nVint \t\t\t%.6f V", Vint);
		uprintf("\nVint std \t\t\t%.6f V", Vint_std);
		uprintf("\nVext \t\t%.6f V", Vext_ref);
		uprintf("\nVext std \t\t%.6f V", Vext_std);
		uprintf("\npH internal \t%.6f", pHint);
		uprintf("\npH external \t%.6f", pHext);
		uprintf("\npH temperature\t%.4f C", TC);
		uprintf("\nCounter leak. \t%.4f", counter_leak);
		uprintf("\nSubstrate leak. \t%.4f", substrate_leak);
		uprintf("\nPressure \t\t%.3f dbar", temp_press);
		uprintf("\nController temp \t%.3f C", con_temp);
		uprintf("\nMain Battery \t%.3f V", main_batt_volt);
		uprintf("\nIsolated battery \t%.3f V", iso_batt_volt);
		uprintf("\nFile name \t\t%s\n", sys_data.fileName);
	}

	else	// Print the data in normal or verbose mode
	{
		uprintf("\n#%d\t", sys_data.current_sample);
		uprintf("%s\t", timestamp);
		uprintf("%.2f\t", main_batt_volt);
		uprintf("%8.5f\t", Vtherm);
		uprintf("%8.5f\t", Vtherm_std);
		uprintf("%8.6f\t", Vint);
		uprintf("%8.6f\t", Vint_std);
		uprintf("%8.6f\t", Vext_ref);
		uprintf("%8.6f\t", Vext_std);
		uprintf("%5.2f\t", iso_batt_volt);
		uprintf("%5.2f\t", con_temp);
		uprintf("%6.3f\t", TC);
		uprintf("%6.3f\t", temp_press);
		uprintf("%7.6f\t", pHext);
		uprintf("%7.6f\t", pHint);
		uprintf("%7.4f\t", counter_leak);
		uprintf("%7.4f\t", substrate_leak);
		uprintf("%s\t", Optode_buff);
		uprintf("%s", MicroCAT_buff);
	}

	sys_data.current_sample++;  // Increment the sample counter

	// Check for low battery voltage. Exit and sleep if too low.
	if( main_batt_volt < sys_data.low_batt_volt )
	{
		uprintf("\nLow battery voltage! Exiting deploy mode...\n");
		error_store("Low battery! Exited deploy mode");
		sleep(IDLE, 0);		// Sleep until woken by user
	}
}



/*
 * error_store()
 * Stores error messages with timestamp.
 * Appends a newline character to message.
 * Function is useful during deployment when console is disconnected
 * RCG 5/14
 */
void error_store(const char *error_buff)
{
	char timestamp[TIMESTAMPLENGTH];
	uint32_t ticks;
	struct tm *time_struct;
	FIL fil;		// File object
	FRESULT fresult;
	UINT bw;

	// Open a file
	fresult = f_open(&fil, "Error.txt", FA_WRITE |FA_OPEN_ALWAYS);
	if(fresult != FR_OK)
	{
		uprintf("Error.txt f_open error: %s\n", StringFromFresult(fresult));
	}

	// Seek to the end, to append our file
	fresult = f_lseek(&fil, fil.fsize);
	if(fresult != FR_OK)
	{
		uprintf("Error.txt f_lseek error: %s\n", StringFromFresult(fresult));
	}

	// Write timestamp to file
	ticks = ROM_HibernateRTCGet();
	time_struct = localtime(&ticks);
	strftime(timestamp, sizeof(timestamp),"\n%Y/%m/%d %H:%M:%S ", time_struct);

	fresult = f_write( &fil, timestamp, strlen(timestamp), &bw );
	if(fresult != FR_OK)
	{
		uprintf("Error.txt f_write error: %s\n", StringFromFresult(fresult));
	}

	// Write error message to file
	fresult = f_write( &fil, error_buff, strlen(error_buff), &bw );
	if(fresult != FR_OK)
	{
		uprintf("Error.txt f_write error: %s\n", StringFromFresult(fresult));
	}

	// Close the file
	fresult = f_close( &fil );
	if(fresult != FR_OK)
	{
		uprintf("Error.txt f_close error: %s\n", StringFromFresult(fresult));
	}

}

//*****************************************************************************
//
// The error routine that is called if the driver library encounters an error.
//
//*****************************************************************************
#ifdef DEBUG
void
__error__(char *pcFilename, uint32_t ui32Line)
{
}
#endif









