/****************************************************************************/
/* Copyright (c) 2000 MBARI                                                 */
/* MBARI Proprietary Information. All rights reserved.                      */
/****************************************************************************/
/* Summary  :                                                               */
/* Filename : Seabird25p.cc                                                 */
/* Author   :                                                               */
/* Project  :                                                               */
/* Version  : 0.1                                                           */
/* Created  : 02/07/2000                                                    */
/* Modified : 09/08/2014                                                    */
/* Modified : 04/08/2015                                                    */
/* Archived :                                                               */
/****************************************************************************/
/* Modification History:                                                    */
/* 09/08/2014 Renamed file from original Seabird.cc, and began refactoring  */
/*            so that this driver can support the seabird 25plus system.    */
/*                                                                          */
/* 04/08/2015 Refactored to decouple SBE25p interface from Task interface.  */
/*            This will allow use of the SBE25p interface without requiring */
/*            a direct serial interface. Henthorn                           */
/****************************************************************************/
#include <unistd.h>
#include <termios.h>
#include <fcntl.h>
#include <ioctl.h>
#include <errno.h>
#include <math.h>

#include <time.h>
#include <TimeP.h>
#include <math.h>
#include <string.h>
#include <FloatAttribute.h>
#include <AttributeParser.h>
#include <StringAttribute.h>
#include <IntegerAttribute.h>
#include "Seabird25p.h"
#include "Syslog.h"
#include "System.h"
#include "WorkSiteIF.h"

// The instName parameter should be a unique name for each instance
// of the seabird server/driver. The name "ctdDriver" is recognized by
// the rest of the system as the "main" source of CTD data and
// therefore should always be used as one of the instance names.
//
Seabird25p::Seabird25p(char *configFile, char *instName )
    : attributes(instName)
{
	char seabirdOutputName[256];

	// Use the instance name to create a unique shared memory area
	//
	_name = strdup(instName);
	strcpy(seabirdOutputName, instName);
	strcat(seabirdOutputName, SeabirdOutputName);
	_output = new Seabird25pOutput(seabirdOutputName, SharedData::ReadWrite);

	createCTDAttributes();
	loadCTDConfigFile( configFile );
	//added copy of config file to log directory 6/3/03 dg

	System::copyToLogDir( configFile);

	reportCTDAttributes();

	depthSensorRunning = False;

//	try {
//		depthSensor = new DepthSensorIF("depthSensor");
//		depthSensorRunning = True;
//	}
//	catch(SharedObjectClient::MissingServer e) {
		depthSensor_pres = 10.1;
		depthSensorRunning = False;
		Syslog::write("Seabird/%s -- DepthSensor server is not running. "
				"Using default pres of %lf", instName, depthSensor_pres);
//	}

	// == Initialize class variables ================================
	site_latitude = 0.0;
	start_depth   = 0.0;
	init_pressure = 0.0;

// == Configure the site latitude ===============================
//	try {
//		WorkSiteIF worksite("worksite");
//		site_latitude = worksite.latitude();
//		start_depth = worksite.getStartDepth();
//	}
//	catch(SharedObjectClient::MissingServer e) {
		site_latitude = 45.0;
		Syslog::write("Seabird25p.cc -- Worksite server is not running. "
				"Using default lat of %lf", site_latitude );
//	}
//	catch(...) {
//		Syslog::write("Seabird25p.cc  failed on instantiation of WorkSiteIF");
//	}

	Syslog::write("Seabird/%s -- constructing Log...\n", instName);
	try {
		_log = new Seabird25pLog(this, instName);
	}
	catch ( ... ) {
		throw Exception("Seabird.cc - CtdLog constructor failed\n");
	}

	_nBytes = 0;

	Syslog::write("Seabird/%s -- constructor succeeded\n", instName);
}

void Seabird25p::createCTDAttributes()
{
	attributes.add( new StringAttribute("t_coefs", "t_coefs", &_t_coefs) );


	attributes.add( new FloatAttribute("t_a","t_a", &T_A ) );
	attributes.add( new FloatAttribute("t_b","t_b", &T_B ) );
	attributes.add( new FloatAttribute("t_c","t_c", &T_C ) );
	attributes.add( new FloatAttribute("t_d","t_d", &T_D ) );

	attributes.add( new FloatAttribute("t_f0","t_f0", &T_F0 ) );

	attributes.add( new FloatAttribute("t_g","t_g", &T_G ) );
	attributes.add( new FloatAttribute("t_h","t_h", &T_H ) );
	attributes.add( new FloatAttribute("t_i","t_i", &T_I ) );
	attributes.add( new FloatAttribute("t_j","t_j", &T_J ) );

	attributes.add( new FloatAttribute("t_gf0","t_gf0", &T_GF0 ) );

	attributes.add( new StringAttribute("c_coefs", "c_coefs", &_c_coefs) );

	attributes.add( new FloatAttribute("c_a","c_a", &C_A ) );
	attributes.add( new FloatAttribute("c_b","c_b", &C_B ) );
	attributes.add( new FloatAttribute("c_c","c_c", &C_C ) );
	attributes.add( new FloatAttribute("c_d","c_d", &C_D ) );
	attributes.add( new FloatAttribute("c_m","c_m", &C_M ) );

	attributes.add( new FloatAttribute("c_g","c_g", &C_G ) );
	attributes.add( new FloatAttribute("c_h","c_h", &C_H ) );
	attributes.add( new FloatAttribute("c_i","c_i", &C_I ) );
	attributes.add( new FloatAttribute("c_j","c_j", &C_J ) );
	attributes.add( new FloatAttribute("c_pcor","c_pcor", &C_PCOR ) );
	attributes.add( new FloatAttribute("c_tcor","c_tcor", &C_TCOR ) );

  	attributes.add( new FloatAttribute("p_a0","p_a0", &P_A0 ) );
	attributes.add( new FloatAttribute("p_a1","p_a1", &P_A1 ) );
	attributes.add( new FloatAttribute("p_a2","p_a2", &P_A2 ) );

	attributes.add( new FloatAttribute("pt_a0","pt_a0", &PT_A0 ) );
	attributes.add( new FloatAttribute("pt_a1","pt_a1", &PT_A1 ) );
	attributes.add( new FloatAttribute("pt_a2","pt_a2", &PT_A2 ) );

	attributes.add( new FloatAttribute("ptc_a0","ptc_a0", &PTC_A0 ) );
	attributes.add( new FloatAttribute("ptc_a1","ptc_a1", &PTC_A1 ) );
	attributes.add( new FloatAttribute("ptc_a2","ptc_a2", &PTC_A2 ) );
	attributes.add( new FloatAttribute("ptc_b0","ptc_b0", &PTC_B0 ) );
	attributes.add( new FloatAttribute("ptc_b1","ptc_b1", &PTC_B1 ) );
	attributes.add( new FloatAttribute("ptc_b2","ptc_b2", &PTC_B2 ) );



	attributes.add( new FloatAttribute("eps","eps", &EPS ) );

	attributes.add( new FloatAttribute("SOc","SOc", &SOc ) );
	attributes.add( new FloatAttribute("BOc","BOc", &BOc ) );
	attributes.add( new FloatAttribute("Voff","Voff", &Voff ) );
	attributes.add( new FloatAttribute("TCor","TCor", &TCor ) );
	attributes.add( new FloatAttribute("PCor","PCor", &PCor ) );

	attributes.add( new IntegerAttribute("ScaleFactor", "ScaleFactor", &ScaleFactor, 0) );
	attributes.add( new FloatAttribute("DarkCounts", "DarkCounts", &DarkCounts, 0.0) );

	attributes.add( new IntegerAttribute("NUMVOLTAGES","NUMVOLTAGES",(long *)&NUMVOLTAGES) );

	attributes.add( new StringAttribute("v1name","v1name",&_output->data.v1name,"v1") );
	attributes.add( new StringAttribute("v2name","v2name",&_output->data.v2name,"v2") );
	attributes.add( new StringAttribute("v3name","v3name",&_output->data.v3name,"v3") );
	attributes.add( new StringAttribute("v4name","v4name",&_output->data.v4name,"v4") );
	attributes.add( new StringAttribute("v5name","v5name",&_output->data.v5name,"v5") );
	attributes.add( new StringAttribute("v6name","v6name",&_output->data.v6name,"v6") );
	attributes.add( new StringAttribute("v7name","v7name",&_output->data.v7name,"v7") );
	attributes.add( new StringAttribute("v8name","v8name",&_output->data.v8name,"v8") );
//	attributes.add( new StringAttribute("PressureConfig", "PressureConfig", &_pressConfigFlag) );

}

void Seabird25p::reportCTDAttributes()
{
	Syslog::write("CTD/%s -- Calibration constants:\n"
			"  Temp Sensor\n"
			"\tT_coefs= %s\n"
			"\tT_a    = %7.6le\n"
			"\tT_b    = %7.6le\n"
			"\tT_c    = %7.6le\n"
			"\tT_d    = %7.6le\n"
			"\tT_f0   = %lf\n"
			"\tT_g    = %7.6le\n"
			"\tT_h    = %7.6le\n"
			"\tT_i    = %7.6le\n"
			"\tT_j    = %7.6le\n"
			"\tT_gf0  = %lf\n"
			"  Conductivity Sensor\n"
			"\tT_coefs= %s\n"
			"\tC_a    = %7.6le\n"
			"\tC_b    = %7.6le\n"
			"\tC_c    = %lf\n"
			"\tC_d    = %7.6le\n"
			"\tC_m    = %4.3lf\n"
			"\tEPS    = %7.6le\n"
			"\tC_g    = %7.6le\n"
			"\tC_h    = %7.6le\n"
			"\tC_i    = %lf\n"
			"\tC_j    = %7.6le\n"
			"\tC_pcor  = %4.3lf\n"
			"\tC_tcor  = %4.3lf\n"
			"  Oxygen Sensor\n"
			"\tSOc    = %7.6le\n"
			"\tBOc    = %7.6le\n"
			"\tVoff   = %7.6le\n"
			"\tTCor   = %7.6le\n"
			"\tPCor   = %7.6le\n"
			"  Pressure Sensor\n"
			"\tP_A0   = %7.6le\n"
			"\tP_A1   = %7.6le\n"
			"\tP_A2   = %7.6le\n"
			"\tPT_A0  = %7.6le\n"
			"\tPT_A1  = %7.6le\n"
			"\tPT_A2  = %7.6le\n"
			"\tPTC_A0 = %7.6le\n"
			"\tPTC_A1 = %7.6le\n"
			"\tPTC_A2 = %7.6le\n"
			"\tPTC_B0 = %7.6le\n"
			"\tPTC_B1 = %7.6le\n"
			"\tPTC_B2 = %7.6le\n"

			,
			name(), _t_coefs,
			T_A, T_B, T_C, T_D, T_F0,
			T_G, T_H, T_I, T_J, T_GF0,
			_t_coefs,C_A, C_B, C_C, C_D, C_M, EPS,
			C_G, C_H, C_I, C_J, C_PCOR, C_TCOR,
			SOc, BOc, Voff, TCor, PCor,
			P_A0, P_A1, P_A2,PT_A0, PT_A1, PT_A2,
			PTC_A0, PTC_A1, PTC_A2, PTC_B0, PTC_B1, PTC_B2 );
}

void Seabird25p::loadCTDConfigFile( char *filename )
{
	Syslog::write("Seabird/%s -- Loading CTD constants from %s\n",
			name(), filename );
	AttributeParser::parse(filename, &attributes);
	// check that numvoltages is between 0 and 6, inclusively
	if ((NUMVOLTAGES > 8)||(NUMVOLTAGES<0))
	{
		Syslog::write("Seabird/%s::loadCTDConfigFile -- error in number of voltages",
				name());
		throw Exception("Seabird::loadCTDConfigFile -- error in number of voltages");
	}

}


Seabird25p::~Seabird25p()
{
	delete _output;
	delete _log ;
	delete _name ;
}


int Seabird25p::processRecord(unsigned char *record, int nBytes)
{
	Boolean debug = True;
	Boolean errorFlag = False;

	if (nBytes == 0)
	{
		dprintf("Seabird/%s: Null record", name());
		return DeviceIF::Ok;
	}

//	dprintf("Seabird/%s: Got record: %s", name(), record);

	char *reply = (char *)record;
	long iNum;
	float fNum;
	int nc, nconv;

	if(nBytes != ExpectedRecordBytes) {
		Syslog::write("Seabird/%s: error record length incorrect (%i)\n", name(),nBytes);
		return DeviceIF::Ok;
	}
	// Read in Temp Data
	nconv = sscanf(reply, "%8x%n", &iNum, &nc);
	if (nconv == 1  && nc == 8) {
		fNum = *((float*)&iNum);
		read_tfreq = (double)fNum;

		// dprintf("Seabird: tfreq: %f, temp: %f\n",read_tfreq,read_temp);
		clock_gettime(CLOCK_REALTIME,&temp_readtime);
	}
	else {
		Syslog::write("Seabird/%s: error sscanfing %d bytes in reply: %s\n",
				name(), nBytes, reply);
	}

	// Read in Conductivity
	nconv = sscanf(reply+8, "%8x%n",&iNum,&nc);
	if (nconv == 1 && nc == 8) {
		fNum = *((float*)&iNum);
		read_cfreq = (double)fNum;
	}
	else
	{
		Syslog::write("Seabird/%s: error sscanf'ing conductivity data string\n",
				name());
	}

	// Read in Pressure
	nconv = sscanf(reply+16, "%6x%n",&iNum,&nc);
	if (nconv == 1 && nc == 6) {
		read_pcounts = (double)iNum;
	}
	else
	{
		Syslog::write("Seabird/%s: error sscanf'ing pressure data string\n",
				name());
	}



	// Read in Pressure Temperature Counts

	nconv = sscanf(reply+22, "%6x%n",&iNum,&nc);
	if (nconv == 1 && nc == 6) {
		read_ptempcounts = (double)iNum;
	}
	else
	{
		Syslog::write("Seabird/%s: error sscanf'ing pressure temperature data string\n",
				name());
	}


	// Start Calculating Values

	TimeIF::TimeSpec t;

	// Calculate pressure


	if (depthSensorRunning) {
		depthSensor->pressure(&depthSensor_pres, &t);
		depthSensor_pres = 10*depthSensor_pres;
	}
	else {
		depthSensor_pres = calculate_Pres(read_pcounts, read_ptempcounts) ; //in dbar
	}
	//pres = 10*sensor(M_PRESSURE);
	read_temp = calculate_Temp(read_tfreq);
	read_cond = calculate_Cond(0.001*read_cfreq, read_temp, depthSensor_pres);
	//
	// rsm 28 Mar 07: Compute salinity from conductivity, temperature and
	// presssure:
	cndr      = 10.*read_cond/sw_c3515();
	salinity  = sw_salt( cndr, read_temp, depthSensor_pres);

	//Calculate Depth
	depth = corrected_depth ((depthSensor_pres - init_pressure));
    depth += start_depth;
	//Read in Voltages

	offset = 28;
	for (i=0;i< NUMVOLTAGES;i++)
	{
		nconv = sscanf(reply+offset,"%4x%n",&iNum,&nc);
		if ((nconv==1)&&(nc==4))
		{
			voltages[i] = ((double)iNum)/65536.0 * 5.000 ;
			//	Syslog::write("Seabird: value of %d is: %f\n",i+1,voltages[i]);
			//  dprintf("Seabird: value of %d is: %f\n",i+1,voltages[i]);
		}
		else
		{
			Syslog::write("Seabird/%s: error reading voltages\n", name());
			Syslog::write("Seabird: reply is %s\n", reply);
//			dprintf("Seabird/%s: error reading voltages\n", name());
			errorFlag = True;
		}
		offset+=4;
	}
	if (errorFlag) return DeviceIF::Error;

/* NOT ACTUALLY ENABLED, request to add submitted to seabird.
	// Read in Diagnostics
	int voutStatus, auxCurrCounts, sysCurrCounts, statusFlags, channel;
	nconv = sscanf(reply+offset,"%2x%2x%2x2x%n", &voutStatus, &auxCurrCounts, &sysCurrCounts, &statusFlags, &nc);
	if ((nconv==4)&&(nc==8)) {
		//Now check for Faults
		for (channel = 0; channel <4 ; channel ++) {
		    voutFaulted[channel] = (voutStatus & 1<<channel == 1);
			if (voutFaulted[channel]) {
				Syslog::write("Seabird/%s: Fault on Channel %i\n ", name(), (channel+1));
			}
		}
		// Record Vout enabled channels, remember channel 3 powers ports J3 and J6
		for (channel = 0; channel < 4; channel++) {
			voutEnabled[channel](voutStatus & 1<<(channel+4) == 1);
		}
		// Set Other Status Flags
		memoryFull      = statusFlags & 1<<0 ;
		batteryLow      = statusFlags & 1<<1 ;
		serialOverFlow1 = statusFlags & 1<<2 ;
		serialOverFlow2 = statusFlags & 1<<3 ;
		pumpEnabled     = statusFlags & 1<<4 ;
		genericError[0] = statusFlags & 1<<5 ;
		genericError[1] = statusFlags & 1<<6 ;
		genericError[2] = statusFlags & 1<<7 ;

		// Now output a composite message to syslog
		Syslog::write("Seabird/%s: STATUS: vout[enb:%i%i%i%i flt:] "
				"memFull[%i] battLow [%i] serOverFlow[%i%i] pumpEnb[%i] genErr[%i%i%i]\n",
				name(),
				voutEnabled[0], voutEnabled[1], voutEnabled[2], voutEnabled[3],
				voutFaulted[0], voutFaulted[1], voutFaulted[2], voutFaulted[3],
				memoryFull, batteryLow, serialOverFlow1, serialOverFlow2,pumpEnabled,
				genericError[0], genericError[1], genericError[2] );

	}
	else {
		Syslog::write("Seabird/%s: error reading diagnostics\n", name());
		Syslog::write("Seabird: reply is %s\n", reply);
		dprintf("Seabird/%s: error reading diagnostics\n", name());
		errorFlag = True;
	}
*/

	clock_gettime(CLOCK_REALTIME,&cond_readtime);

	_output->data.conductivity = read_cond;
	_output->data.temperature  = read_temp;
	_output->data.salinity     = salinity;
	_output->data.pressure     = depthSensor_pres;
	_output->data.depth        = depth;
	_output->data.cfreq = read_cfreq;
	_output->data.tfreq = read_tfreq;
	_output->data.temp_updateTime.tv_sec = temp_readtime.tv_sec;
	_output->data.temp_updateTime.tv_nsec = temp_readtime.tv_nsec;
	_output->data.cond_updateTime.tv_sec = cond_readtime.tv_sec;
	_output->data.cond_updateTime.tv_nsec = cond_readtime.tv_nsec;
	for(i=0;i<NUMVOLTAGES;i++)
	{
		_output->data.voltages[i] = voltages[i];
	}


	// empty these up
	// free(_output->data.v1name);
	//free(_output->data.v2name);
	//free(_output->data.v3name);
	//free(_output->data.v4name);
	//free(_output->data.v5name);
	//free(_output->data.v6name);


	//and now copy them
	//_output->data.v1name = strdup(v1name);
	//_output->data.v2name = strdup(v2name);
	//_output->data.v3name = strdup(v3name);
	//_output->data.v4name = strdup(v4name);
	//_output->data.v5name = strdup(v5name);
	//_output->data.v6name = strdup(v6name);


	try
	{
		_output->write();

	}
	catch (SharedData::AccessError error)
	{
		fprintf(stderr, "%s",error.msg);
		exit(1);
	}
	// now write to log file
	_log->write();
	time_t now = time((long*)0);
	if ((now - _lastFlush) > _flushInterval)
	{
		_lastFlush = now;
		_log->flush();
	}


#if defined (FLBS_PORT) && defined (BS_HACK)
	nconv = sscanf(reply+16,"%4x%n",&val1,&nc);
	if (nconv == 1 && nc == 4) {
		read_m_bs_voltage = 5000*val1/4095;

	}
#endif

	return 0;

}

void Seabird25p::standby(void)
{
}

void Seabird25p::halt(void)
{
}
/*-----------------------------------------------------------------------*
  (from Odyssey Code)

  First do temp conversion:

      lf = log( f0 / f Hz )

       T = 1 / (a + b*lf + c*lf^2 + d*lf^3)  - 273.15

  Next conductivity (uses temperature)

      C (S/m) = (a*(c kHz)^m + b*(c kHz)^2 + c + d*T) /
                (10*(1 + eps*P))
      where P is dbar and T is deg C
 *-----------------------------------------------------------------------*/


double Seabird25p::calculate_Temp(double f)
{
	double T;

	if (*_t_coefs == 'A') {
		f = ::log(T_F0/f);
		T = 1/(T_A + (T_B + (T_C + T_D*f)*f)*f) - 273.15;
	}
	else if (*_t_coefs == 'G') {
		f = ::log(T_GF0/f);
		T = 1/(T_G + (T_H + (T_I + T_J*f)*f)*f) - 273.15;
	}
	else {
		Syslog::write("Seabird25p::calculate_Temp(): no t_coefs set selected.\n");
		T = 0.0;
	}
	return T;
}

double Seabird25p::calculate_Cond(double f, double t, double p)
{
	double C;

	//  Syslog::write("Seabird debugging:\n");
	// Syslog::write("C_A = %f\n C_B = %f\nC_C = %f\nC_D = %f\nC_M = %f, EPS = %f\n", C_A, C_B, C_C, C_D, C_M, EPS);
	//Syslog::write("f  = %f, t = %f, p = %f\n",f,t,p);

	if(*_c_coefs == 'A') {
		C = (C_A*pow(f,C_M) + C_B*f*f +C_C +C_D*t)/(10*(1+EPS*p));
	}
	else if(*_c_coefs == 'G') {
		C = (C_G +(C_H +(C_I + C_J*f)*f)*f*f) / (1+C_TCOR*t+C_PCOR*p) ;
	}
	else {
		Syslog::write("Seabird25p::calculate_Cond(): no c_coefs set selected.\n");
		C=0;
	}
	//Syslog::write("C = %f\n",C);
	return C;

}
 double Seabird25p::calculate_Pres(double pc, double tc) {
	 double P, tv, t, x, n;

	 // Convert temp counts value to voltage
	 tv = 4.096 * (tc/16777216.0);
	 t = PT_A0 + (PT_A1 + PT_A2*tv)*tv;
	 x = pc - PTC_A0 - (PTC_A1 - PTC_A2*t)*t;
	 n = x  * PTC_B0 / (PTC_B0 - (PTC_B1 + PTC_B2*t)*t);
	 P = P_A0 + (P_A1 + P_A2*n)*n; //psia

	 P = (P - 14.7)*0.689476; //decibar (conversion from sbe25plus sheet)
//	 Syslog::write("P = %f | pc = %f | tc = %f",P,pc,tc);
	 return P;

 }
 double Seabird25p::corrected_depth(double dbar)
{
  double corrected, C, gr;
  static double st2;
  static int first=1;
  if (first) // calc & store sin^2(latitude)
    {
      st2 = sin ( site_latitude);
      st2 *= st2;
      first = 0;
    }

  C = (((-1.8200e-15*dbar + 2.279e-10)*dbar - 2.5212e-05)*dbar + 9.72659)
    * dbar;
  gr = 9.780318*((2.3600e-05*st2 + 5.2788e-3)*st2 + 1) + 1.092e-06*dbar;

  corrected = C/gr;
  return corrected;
}
//
// The following routines are ported from the CSIRO repository:
// http://www.marine.csiro.au/datacentre/ext_docs/CSIRO_MatLAB_Seawater_Library.pdf
//
double Seabird25p::sw_salt(double cndr, double T, double P)
{
	// SW_SALT    Salinity from cndr, T, P
	//=========================================================================
	// SW_SALT  $Id: Seabird25p.cc,v 1.5 2015/02/05 00:04:38 emartin Exp $
	//          Copyright (C) CSIRO, Phil Morgan 1993.
	//
	// USAGE: S = sw_salt(cndr,T,P)
	//
	// DESCRIPTION:
	//   Calculates Salinity from conductivity ratio. UNESCO 1983 polynomial.
	//
	// INPUT:
	//   cndr = Conductivity ratio     R =  C(S,T,P)/C(35,15(IPTS-68),0) [no units]
	//   T    = temperature [degree C (ITS-90)]
	//   P    = pressure    [db]
	//
	// OUTPUT:
	//   S    = salinity    [psu      (PSS-78)]
	//
	// AUTHOR:  Phil Morgan 93-04-17, Lindsay Pender (Lindsay.Pender@csiro.au)
	//
	// DISCLAIMER:
	//   This software is provided "as is" without warranty of any kind.
	//   See the file sw_copy.m for conditions of use and licence.
	//
	// REFERENCES:
	//    Fofonoff, P. and Millard, R.C. Jr
	//    Unesco 1983. Algorithms for computation of fundamental properties of
	//    seawater, 1983. _Unesco Tech. Pap. in Mar. Sci._, No. 44, 53 pp.
	//=========================================================================

	// Modifications
	// 03-12-12. Lindsay Pender, Converted to ITS-90.

	// CALLER: general purpose
	// CALLEE: sw_sals.m sw_salrt.m sw_salrp.m


	//----------------------------------
	// rsm 27 Mar 07 This version takes only scalar inputs.
	//----------------------------------
	//


	//-------
	// BEGIN
	//-------
	double R, rt, Rp, Rt, S;
	R  = cndr;
	rt = sw_salrt(T);
	Rp = sw_salrp(R,T,P);
	Rt = R/(Rp*rt);
	if( Rt <= 0. ) return 0.;
	S  = sw_sals(Rt,T);
	return S;
}

double Seabird25p::sw_salrt( double T)
{
	// SW_SALRT   Conductivity ratio   rt(T)     = C(35,T,0)/C(35,15,0)
	//=========================================================================
	// SW_SALRT  $Id: Seabird25p.cc,v 1.5 2015/02/05 00:04:38 emartin Exp $
	//           Copyright (C) CSIRO, Phil Morgan 1993.
	//
	// USAGE:  rt = sw_salrt(T)
	//
	// DESCRIPTION:
	//    Equation rt(T) = C(35,T,0)/C(35,15(IPTS-68),0) used in calculating
	//       salinity.
	//    UNESCO 1983 polynomial.
	//
	// INPUT:
	//   T = temperature [degree C (ITS-90)]
	//
	// OUTPUT:
	//   rt = conductivity ratio  [no units]
	//
	// AUTHOR:  Phil Morgan 93-04-17, Lindsay Pender (Lindsay.Pender@csiro.au)
	//
	// DISCLAIMER:
	//   This software is provided "as is" without warranty of any kind.
	//   See the file sw_copy.m for conditions of use and licence.
	//
	// REFERENCES:
	//    Fofonoff, P. and Millard, R.C. Jr
	//    Unesco 1983. Algorithms for computation of fundamental properties of
	//    seawater, 1983. _Unesco Tech. Pap. in Mar. Sci._, No. 44, 53 pp.
	//=========================================================================

	// Modifications
	// 03-12-12. Lindsay Pender, Converted to ITS-90.

	// CALLER: sw_salt
	// CALLEE: none

	// rt = rt(T) = C(35,T,0)/C(35,15,0)
	// Eqn (3) p.7 Unesco.

	double T68, c0, c1, c2, c3, c4, rt;

	T68 = T * 1.00024;

	c0 =  0.6766097;
	c1 =  2.00564e-2;
	c2 =  1.104259e-4;
	c3 = -6.9698e-7;
	c4 =  1.0031e-9;

	rt = c0 + (c1 + (c2 + (c3 + c4*T68)*T68)*T68)*T68;

	return rt;
}


double Seabird25p::sw_salrp( double R, double T, double P)
{
	// SW_SALRP   Conductivity ratio   Rp(S,T,P) = C(S,T,P)/C(S,T,0)
	//=========================================================================
	// SW_SALRP   $Id: Seabird25p.cc,v 1.5 2015/02/05 00:04:38 emartin Exp $
	//            Copyright (C) CSIRO, Phil Morgan 1993.
	//
	// USAGE:  Rp = sw_salrp(R,T,P)
	//
	// DESCRIPTION:
	//    Equation Rp(S,T,P) = C(S,T,P)/C(S,T,0) used in calculating salinity.
	//    UNESCO 1983 polynomial.
	//
	// INPUT: (All must have same shape)
	//   R = Conductivity ratio  R =  C(S,T,P)/C(35,15(IPTS-68),0) [no units]
	//   T = temperature [degree C (ITS-90)]
	//   P = pressure    [db]
	//
	// OUTPUT:
	//   Rp = conductivity ratio  Rp(S,T,P) = C(S,T,P)/C(S,T,0)  [no units]
	//
	// AUTHOR:  Phil Morgan 93-04-17, Lindsay Pender (Lindsay.Pender@csiro.au)
	//
	// DISCLAIMER:
	//   This software is provided "as is" without warranty of any kind.
	//   See the file sw_copy.m for conditions of use and licence.
	//
	// REFERENCES:
	//    Fofonoff, P. and Millard, R.C. Jr
	//    Unesco 1983. Algorithms for computation of fundamental properties of
	//    seawater, 1983. _Unesco Tech. Pap. in Mar. Sci._, No. 44, 53 pp.
	//=========================================================================

	// Modifications
	// 03-12-12. Lindsay Pender, Converted to ITS-90.

	// CALLER: sw_salt
	// CALLEE: none

	//-------------------
	// eqn (4) p.8 unesco.
	//-------------------

	double T68,d1,d2,d3,d4,e1,e2,e3,Rp;

	T68 = T * 1.00024;

	d1 =  3.426e-2;
	d2 =  4.464e-4;
	d3 =  4.215e-1;
	d4 = -3.107e-3;

	e1 =  2.070e-5;
	e2 = -6.370e-10;
	e3 =  3.989e-15;

	Rp = 1 + ( P*(e1 + e2*P + e3*P*P) )/
			(1 + d1*T68 + d2*T68*T68 +(d3 + d4*T68)*R);

	return Rp;
}

double Seabird25p::sw_sals(double Rt, double T)
{
	// SW_SALS    Salinity of sea water
	//=========================================================================
	// SW_SALS  $Id: Seabird25p.cc,v 1.5 2015/02/05 00:04:38 emartin Exp $
	//          Copyright (C) CSIRO, Phil Morgan 1993.
	//
	// USAGE:  S = sw_sals(Rt,T)
	//
	// DESCRIPTION:
	//    Salinity of sea water as a function of Rt and T.
	//    UNESCO 1983 polynomial.
	//
	// INPUT:
	//   Rt = Rt(S,T) = C(S,T,0)/C(35,T(IPTS-68),0)
	//   T  = temperature [degree C (ITS-90)]
	//
	// OUTPUT:
	//   S  = salinity    [psu      (PSS-78)]
	//
	// AUTHOR:  Phil Morgan 93-04-17, Lindsay Pender (Lindsay.Pender@csiro.au)
	//
	// DISCLAIMER:
	//   This software is provided "as is" without warranty of any kind.
	//   See the file sw_copy.m for conditions of use and licence.
	//
	// REFERENCES:
	//    Fofonoff, P. and Millard, R.C. Jr
	//    Unesco 1983. Algorithms for computation of fundamental properties of
	//    seawater, 1983. _Unesco Tech. Pap. in Mar. Sci._, No. 44, 53 pp.
	//=========================================================================

	// Modifications
	// 03-12-12. Lindsay Pender, Converted to ITS-90.

	// CALLER: sw_salt
	// CALLEE: none

	//--------------------------
	// eqn (1) & (2) p6,7 unesco
	//--------------------------

	double del_T68,a0,a1,a2,a3,a4,a5,b0,b1,b2,b3,b4,b5,k,Rtx,del_S,S;

	del_T68 = T * 1.00024 - 15;

	a0 =  0.0080;
	a1 = -0.1692;
	a2 = 25.3851;
	a3 = 14.0941;
	a4 = -7.0261;
	a5 =  2.7081;

	b0 =  0.0005;
	b1 = -0.0056;
	b2 = -0.0066;
	b3 = -0.0375;
	b4 =  0.0636;
	b5 = -0.0144;

	k  =  0.0162;

	Rtx   = sqrt(Rt);
	del_S = (del_T68 / (1+k*del_T68) ) *
			( b0 + (b1 + (b2+ (b3 + (b4 + b5*Rtx)*Rtx)*Rtx)*Rtx)*Rtx);

	S = a0 + (a1 + (a2 + (a3 + (a4 + a5*Rtx)*Rtx)*Rtx)*Rtx)*Rtx;

	S = S + del_S;

	return S;
}


double Seabird25p::sw_c3515(void)
{
	// SW_C3515   Conductivity at (35,15,0)
	//=========================================================================
	// SW_c3515  $Id: Seabird25p.cc,v 1.5 2015/02/05 00:04:38 emartin Exp $
	//       //   Copyright (C) CSIRO, Phil Morgan 1993.
	//
	// USAGE:  c3515 = sw_c3515
	//
	// DESCRIPTION:
	//   Returns conductivity at S=35 psu , T=15 C [ITPS 68] and P=0 db).
	//
	// INPUT: (none)
	//
	// OUTPUT:
	//   c3515  = Conductivity   [mmho/cm == mS/cm]
	//
	// AUTHOR:  Phil Morgan 93-04-17  (morgan@ml.csiro.au)
	//
	// DISCLAIMER:
	//   This software is provided "as is" without warranty of any kind.
	//   See the file sw_copy.m for conditions of use and licence.
	//
	// REFERENCES:
	//    R.C. Millard and K. Yang 1992.
	//    "CTD Calibration and Processing Methods used by Woods Hole
	//     Oceanographic Institution"  Draft April 14, 1992
	//    (Personal communication)
	//=========================================================================

	// CALLER: none
	// CALLEE: none
	//

	return 42.914;

	//-------------------------------------------------------------------------
}


#if 0
// Old correcting readRecord()
try {
	// Read data record from device
	_nBytes += _device->readUntil((char *)(record + _nBytes),
			(maxRecordBytes - _nBytes),
			(char *)recordTerminator,
			readTimeout);
}
catch (SerialDevice::TimedOut) {
	// OK, keep the bytes we've read and get the rest later
	//
	readError = True;
	Syslog::write("Seabird/%s::readRecord() - timed out after %d bytes, %s",
			name(), _nBytes, "correcting...");
	*nBytesRead = 0;
}
catch (SerialDevice::BufferFull) {
	readError = True;
	Syslog::write("%s::readRecord() - buf=", name(), _recordBuf);
	Syslog::write("%s::readRecord() - serial device buffer full", name());
	*nBytesRead = _nBytes = 0; // reset
}
catch (Exception e) {
	readError = True;
	Syslog::write("%s::readRecord() - caught exception:%s: abort",
			e.msg, name());
	*nBytesRead = _nBytes = 0; // reset
	throw;
}
#endif
