//Author: LabJack
//April 16, 2008
//Example U3 helper class.
using System;
using System.Runtime.InteropServices;

namespace LabJack
{
	public struct U3_CALIBRATION_INFORMATION
	{
		public uint prodID;
		public double hardwareVersion; //helps to determine which calibration calculations
                                       //to use
		public bool highVoltage;       //indicates if the device is U3-HV
		public double ainSESlope;      //lvAINSESlope
		public double ainSEOffset;     //lvAINSEOffset
		public double ainDiffSlope;    //lvAINDiffSlope
		public double ainDiffOffset;   //lvAINDiffOffset
		public double []hvAINSlope;
		public double []hvAINOffset;
		public double []dacSlope;
		public double []dacOffset;
		public double tempSlope;
		public double tempSlopeLow;
		public double calTemp;
		public double vref;
		public double vref15;
		public double vreg;
	};

	/// <summary>
	/// Class for a U3 device.
	/// </summary>
	public class U3
	{
		public const UInt32 INVALID_HANDLE_VALUE = 0xffffffff;

		public const uint U3_PRODUCT_ID	   = 3;
		public const uint U3_MAX_DEVICES   = 9;

		public const uint U3_PIPE_EP1_OUT  = 0;
		public const uint U3_PIPE_EP1_IN   = 1;
		public const uint U3_PIPE_EP2_IN   = 3;

		public U3_CALIBRATION_INFORMATION calibrationInfo;

		[DllImport("labjackusb", EntryPoint = "LJUSB_OpenDevice", SetLastError = true, CharSet = CharSet.Auto)]
		public static extern UInt32 LJUSB_OpenDevice(uint DevNum, uint dwReserved, uint ProductID);
		
		[DllImport("labjackusb", EntryPoint = "LJUSB_BulkRead", SetLastError = true, CharSet = CharSet.Auto)]
		public static extern uint LJUSB_BulkRead(UInt32 hDevice, uint Pipe, [In, Out] byte[] pBuff, uint Count);
		
		[DllImport("labjackusb", EntryPoint = "LJUSB_BulkWrite", SetLastError = true, CharSet = CharSet.Auto)]
		public static extern uint LJUSB_BulkWrite(UInt32 hDevice, uint Pipe, [In, Out] byte[] pBuff, uint Count);

		[DllImport("labjackusb", EntryPoint = "LJUSB_CloseDevice", SetLastError = true, CharSet = CharSet.Auto)]
		public static extern void LJUSB_CloseDevice(UInt32 hDevice);

		[DllImport("labjackusb", EntryPoint = "LJUSB_GetDevCount", SetLastError = true, CharSet = CharSet.Auto)]
		public static extern uint LJUSB_GetDevCount(uint ProductID);
		
		[DllImport("labjackusb", EntryPoint = "LJUSB_AbortPipe", SetLastError = true, CharSet = CharSet.Auto)]
		public static extern bool LJUSB_AbortPipe(UInt32 hDevice, uint Pipe);

		/// <summary>
		///  This should be set when there is an errorcode in the response buffer.
		///  This is not set by this class on its own.
		/// </summary>
		public int errorcode;	
		
		/// <summary>
		///  This is set by the openUSBConnection function 
		/// </summary>
		public UInt32 handle;
		
		/// <summary>
		///  If any of the functions in this class run into an error, they will
		///  put the error description in this string.
		/// </summary>
		public string errorText;

		/// <summary>
		/// Constructor
		/// </summary> 
		public U3()
		{
			calibrationInfo = new U3_CALIBRATION_INFORMATION();
			calibrationInfo.prodID = 0;
			calibrationInfo.hardwareVersion = 0;
			calibrationInfo.highVoltage = false;
			calibrationInfo.ainSESlope = 0; 
			calibrationInfo.ainSEOffset = 0; 
			calibrationInfo.ainDiffSlope = 0; 
			calibrationInfo.ainDiffOffset = 0; 
			calibrationInfo.hvAINSlope = new double[4]{0, 0, 0, 0};
			calibrationInfo.hvAINOffset = new double[4]{0, 0, 0, 0};
			calibrationInfo.dacSlope = new double[2]{0, 0};
			calibrationInfo.dacOffset =  new double[2]{0, 0}; 
			calibrationInfo.tempSlope = 0; 
			calibrationInfo.tempSlopeLow = 0; 
			calibrationInfo.calTemp = 0; 
			calibrationInfo.vref = 0; 
			calibrationInfo.vref15 = 0;
			calibrationInfo.vreg = 0;
			errorcode = 0;
			handle = INVALID_HANDLE_VALUE;
		}

		
		/// <summary>
		/// Adds checksum to a data packet for normal command format.
		/// </summary>
		/// <param name="b">Data packet array for normal command.</param>
		/// <param name="n">Size of data packet.</param>
		public static void normalChecksum(ref byte []b, int n)
		{
			b[0] = U3.normalChecksum8(b,n);
		}


		/// <summary>
		/// Adds checksum to a data packet for extended command format.
		/// </summary>
		/// <param name="b">Data packet array for extended command.</param>
		/// <param name="n">Size of data packet.</param>
		public static void extendedChecksum(ref byte []b, int n)
		{
			ushort a;

			a = U3.extendedChecksum16(b,n);
			b[4] = (byte)(a & 0xff);
			b[5] = (byte)((a / 256) & 0xff);
			b[0] = U3.extendedChecksum8(b);
		}


		/// <summary>
		/// Calculates the Checksum8 for a normal command data packet.
		/// </summary>
		/// <param name="b">Data packet array for normal command.</param>
		/// <param name="n">Size of data packet.</param>
		/// <returns>The Checksum8 for a normal command data packet.</returns>
		public static byte normalChecksum8(byte []b, int n)
		{
			int i;
			ushort a, bb;
  
			//Sums bytes 1 to n-1 unsigned to a 2 byte value. Sums quotient and
			//remainder of 256 division.  Again, sums quotient and remainder of
			//256 division.
			for(i = 1, a = 0; i < n; i++) 
				a += (ushort)b[i];

			bb = (ushort)(a/256);
			a = (ushort)((a-256*bb)+bb);
			bb = (ushort)(a/256);

			return ((byte)((a-256*bb)+bb));
		}


		/// <summary>
		/// Calculates the Checksum16 for a extended command data packet.
		/// </summary>
		/// <param name="b">Data packet array for extended command.</param>
		/// <param name="n">Size of data packet.</param>
		/// <returns>Returns the Checksum16 for a extended command data packet.</returns>
		public static ushort extendedChecksum16(byte []b, int n)
		{
			int i;
			ushort a;

			//Sums bytes 6 to n-1 to a unsigned 2 byte value
			for(i = 6, a = 0; i < n; i++)
				a += (ushort)b[i];

			return a;
		}


		/// <summary>
		/// Calculates the Checksum8 for a extended command data packet.
		/// </summary>
		/// <param name="b">Data packet array for extended command.</param>
		/// <returns>The Checksum8 for a extended command data packet.</returns>
		public static byte extendedChecksum8(byte []b)
		{
			uint i, a, bb;

			//Sums bytes 1 to 5. Sums quotient and remainder of 256 division. Again, sums
			//quotient and remainder of 256 division.
			for(i=1,a=0;i<6;i++)
				a += (ushort)b[i];
  
			bb = (ushort)(a/256);
			a = (ushort)((a-256*bb)+bb);
			bb = (ushort)(a/256);
  
			return ((byte)((a-256*bb)+bb));  
		}


		/// <summary>
		/// Opens a U3.
		/// </summary>
		/// <param name="localID">The local ID of the U3 you want to open.</param>
		/// <returns>INVALID_HANDLE_VALUE or negaitive value on error, or a handle value on success</returns>
		public int openUSBConnection(int localID)
		{
			byte []buffer = new byte[26];
			ushort checksumTotal = 0;
			UInt32 hDevice = 0;
			uint numDevices = 0;

			try
			{
				numDevices = LJUSB_GetDevCount(U3_PRODUCT_ID);

				for(uint dev = 1;  dev <= numDevices; dev++)
				{
					hDevice = LJUSB_OpenDevice(dev, 0, U3_PRODUCT_ID);
					
					if(hDevice != INVALID_HANDLE_VALUE)
					{
						if(localID < 0)
						{
							this.handle = hDevice;
							return 0;
						}
						else
						{
							checksumTotal = 0;
	
							//setting up a ConfigU3 command
							buffer[1] = (byte)(0xF8);
							buffer[2] = (byte)(0x0A);
							buffer[3] = (byte)(0x08);

							for(int i = 6; i < 26; i++)
								buffer[i] = (byte)(0x00);

							extendedChecksum(ref buffer,26);

							if(LJUSB_BulkWrite(hDevice, U3.U3_PIPE_EP1_OUT, buffer, 38) != 38)
								throw new Exception("close");

							buffer = new byte[38];

							if(LJUSB_BulkRead(hDevice, U3.U3_PIPE_EP1_IN, buffer, 38) != 38)
								throw new Exception("close");
							
							checksumTotal = extendedChecksum16(buffer, 38);
							if( (byte)((checksumTotal / 256) & 0xff) != buffer[5])
								throw new Exception("close");

							if( (byte)(checksumTotal & 0xff) != buffer[4])
								throw new Exception("close");

							if( extendedChecksum8(buffer) != buffer[0])
								throw new Exception("close");

							if( buffer[1] != (byte)(0xF8) || buffer[2] != (byte)(0x10) || buffer[3] != (byte)(0x08) )
								throw new Exception("close");

							if( buffer[6] !=0 )
								throw new Exception("close");

							if((int)buffer[21] == localID)
							{
								this.handle = hDevice;
								return 0;
							}
							else 
							{
								LJUSB_CloseDevice(hDevice);
							}

						}	//else localID >= 0 end
	
					}  //if hDevice != INVALID_HANDLE_VALUE end

				} //for end

			}
			catch (System.Exception y)
			{
				if(y.ToString() == "close")
					LJUSB_CloseDevice(hDevice);
				
				errorText = "openUSBConnection error: Problem communicating with U3 device";
				return -3;
			}
	
			errorText = "openUSBConnection error: Could not find U3 device";
			return -2;
		}


		/// <summary>
		/// Closes a HANDLE to a U3 device.
		/// </summary>
		/// <returns> 0 on sucess or negative value on error.</returns>
		public int closeUSBConnection()
		{
			try
			{
				LJUSB_CloseDevice(this.handle);
			}
			catch (System.Exception)
			{
				errorText = "closeUSBConnection error: Could not close U3 device";
			    return -4;
			}

			return 0;
		}


		/// <summary>
		/// </summary>
		/// <returns>Returns the number of milliseconds that have elasped since the system was started.</returns>
		public static long getTickCount() 
		{
			return( (long)(System.Environment.TickCount & Int32.MaxValue)); 
		}


		/// <summary>
		/// Gets calibration information from memory blocks 0-4 of a U3, and sets the calibration
		/// information in the calibrationInfo structure.
		/// </summary>
		/// <returns>A negative value on error, 0 on success.</returns>
		public int getCalibrationInfo() 
		{			
			byte []sendBuffer; // = new byte[8];
			byte []recBuffer;  // = new byte[40];
			uint sentRec = 0;

			calibrationInfo.prodID = 0;

			/* sending ConfigU3 command to get hardware version and see if HV */

			sendBuffer = new byte[26];
			recBuffer = new byte[38];

			sendBuffer[1] = (byte)(0xF8);  //command byte
			sendBuffer[2] = (byte)(0x0A);  //number of data words
			sendBuffer[3] = (byte)(0x08);  //extended command number

			//setting WriteMask0 and all other bytes to 0 since we only want to read the response
			for(int i = 6; i < 26; i++)
				sendBuffer[i] = 0;

			U3.extendedChecksum(ref sendBuffer, 26);

			sentRec = U3.LJUSB_BulkWrite(handle, U3.U3_PIPE_EP1_OUT, sendBuffer, 26);
			if(sentRec < 26)
			{
				if(sentRec == 0)
					goto writeError0;
				else
					goto writeError1;
			}

			sentRec = LJUSB_BulkRead(handle, U3.U3_PIPE_EP1_IN, recBuffer, 38);
			if(sentRec < 38)
			{
				if(sentRec == 0)
					goto readError0;
				else
					goto readError1;
			}

			if(recBuffer[1] != (byte)(0xF8) || recBuffer[2] != (byte)(0x10) || recBuffer[3] != (byte)(0x08))
				goto commandByteError;

			calibrationInfo.hardwareVersion = recBuffer[14] + recBuffer[13]/100.0;
			calibrationInfo.highVoltage = (((recBuffer[37]&18) == 18)?true:false);


			/* reading block 0 from memory */

			sendBuffer = new byte[8];
			recBuffer = new byte[40];

			sendBuffer[1] = (byte)(0xF8);  //command byte
			sendBuffer[2] = (byte)(0x01);  //number of data words
			sendBuffer[3] = (byte)(0x2D);  //extended command number
			sendBuffer[6] = (byte)(0x00);
			sendBuffer[7] = (byte)(0x00);  //Blocknum = 0
			extendedChecksum(ref sendBuffer, 8);
  
			sentRec = U3.LJUSB_BulkWrite(handle, U3.U3_PIPE_EP1_OUT, sendBuffer, 8);
			if(sentRec < 8)
			{
				if(sentRec == 0)
					goto writeError0;
				else  
					goto writeError1;
			}	

			sentRec = U3.LJUSB_BulkRead(handle, U3.U3_PIPE_EP1_IN, recBuffer, 40);
			if(sentRec < 40)
			{
				if(sentRec == 0)
					goto readError0;
				else  
					goto readError1;
			}

			if(recBuffer[1] != (byte)(0xF8) || recBuffer[2] != (byte)(0x11) || recBuffer[3] != (byte)(0x2D))
				goto commandByteError;

			//block data starts on byte 8 of the buffer
			calibrationInfo.ainSESlope = FPbyteArrayToFPDouble(recBuffer, 8);
			calibrationInfo.ainSEOffset = FPbyteArrayToFPDouble(recBuffer, 16);
			calibrationInfo.ainDiffSlope = FPbyteArrayToFPDouble(recBuffer, 24);
			calibrationInfo.ainDiffOffset = FPbyteArrayToFPDouble(recBuffer, 32);
 
			/* reading block 1 from memory */			
			sendBuffer[7] = (byte)(0x01);    //Blocknum = 1
			extendedChecksum(ref sendBuffer, 8);
  
			sentRec = U3.LJUSB_BulkWrite(handle, U3.U3_PIPE_EP1_OUT, sendBuffer, 8);
			if(sentRec < 8)
			{
				if(sentRec == 0)
					goto writeError0;
				else  
					goto writeError1;
			}	

			sentRec= U3.LJUSB_BulkRead(handle, U3.U3_PIPE_EP1_IN, recBuffer, 40);

			if(sentRec < 40)
			{
				if(sentRec == 0)
					goto readError0;
				else  
					goto readError1;
			}

			if(recBuffer[1] != (byte)(0xF8) || recBuffer[2] != (byte)(0x11) || recBuffer[3] != (byte)(0x2D))
			{
				goto commandByteError;
			}
  
			calibrationInfo.dacSlope[0] = FPbyteArrayToFPDouble(recBuffer, 8);
			calibrationInfo.dacOffset[0] = FPbyteArrayToFPDouble(recBuffer, 16);
			calibrationInfo.dacSlope[1] = FPbyteArrayToFPDouble(recBuffer, 24);
			calibrationInfo.dacOffset[1] = FPbyteArrayToFPDouble(recBuffer, 32);

			/* reading block 2 from memory */
		
			sendBuffer[7] = (byte)(0x02);    //Blocknum = 2
			extendedChecksum(ref sendBuffer, 8);
  
			sentRec = U3.LJUSB_BulkWrite(handle, U3.U3_PIPE_EP1_OUT, sendBuffer, 8);
			if(sentRec < 8)
			{
				if(sentRec == 0)
					goto writeError0;
				else  
					goto writeError1;
			}	

			sentRec = U3.LJUSB_BulkRead(handle, U3.U3_PIPE_EP1_IN, recBuffer, 40);
			if(sentRec < 40)
			{
				if(sentRec == 0)
					goto readError0;
				else  
					goto readError1;
			}

			if(recBuffer[1] != (byte)(0xF8) || recBuffer[2] != (byte)(0x11) || recBuffer[3] != (byte)(0x2D))
			{
				goto commandByteError;
			}
  
			calibrationInfo.tempSlope = FPbyteArrayToFPDouble(recBuffer, 8);
			calibrationInfo.vref = FPbyteArrayToFPDouble(recBuffer, 16);
			calibrationInfo.vref15 = FPbyteArrayToFPDouble(recBuffer, 24);
			calibrationInfo.vreg = FPbyteArrayToFPDouble(recBuffer, 32);

			/* reading block 3 from memory */
			sendBuffer[7] = 3;    //Blocknum = 3
			U3.extendedChecksum(ref sendBuffer, 8);

			sentRec = U3.LJUSB_BulkWrite(handle, U3.U3_PIPE_EP1_OUT, sendBuffer, 8);
			if(sentRec < 8)
			{
				if(sentRec == 0)
					goto writeError0;
				else
					goto writeError1;
			}

			sentRec = U3.LJUSB_BulkRead(handle, U3.U3_PIPE_EP1_IN, recBuffer, 40);
			if(sentRec < 40)
			{
				if(sentRec == 0)
					goto readError0;
				else
					goto readError1;
			}

			if(recBuffer[1] != (byte)(0xF8) || recBuffer[2] != (byte)(0x11) || recBuffer[3] != (byte)(0x2D))
				goto commandByteError;

			for(int i = 0; i < 4; i++)
				calibrationInfo.hvAINSlope[i] = FPbyteArrayToFPDouble(recBuffer, 8 + i*8);

			/* reading block 4 from memory */
			sendBuffer[7] = 4;    //Blocknum = 4
			U3.extendedChecksum(ref sendBuffer, 8);

			sentRec = U3.LJUSB_BulkWrite(handle, U3.U3_PIPE_EP1_OUT, sendBuffer, 8);
			if(sentRec < 8)
			{
				if(sentRec == 0)
					goto writeError0;
				else
					goto writeError1;
			}

			sentRec = U3.LJUSB_BulkRead(handle, U3.U3_PIPE_EP1_IN, recBuffer, 40);
			if(sentRec < 40)
			{
				if(sentRec == 0)
					goto readError0;
				else
					goto readError1;
			}

			if(recBuffer[1] != (byte)(0xF8) || recBuffer[2] != (byte)(0x11) || recBuffer[3] != (byte)(0x2D))
				goto commandByteError;

			for(int i = 0; i < 4; i++)
				calibrationInfo.hvAINOffset[i] = FPbyteArrayToFPDouble(recBuffer, 8 + i*8);

			calibrationInfo.prodID = 3;

			return 0;
  
			writeError0: 
				errorText = "Error : getCalibrationInfo write failed";
			return -1;
  
			writeError1:
				errorText = "Error : getCalibrationInfo did not write all of the buffer";
			return -1;

			readError0:
				errorText = "Error : getCalibrationInfo read failed";
			return -1;
  
			readError1:
				errorText = "Error : getCalibrationInfo did not read all of the buffer";
			return -1;

			commandByteError:
				errorText = "Error : getCalibrationInfo received wrong command bytes for ReadMem";
			return -1;
		}


		/// <summary>
		/// Converts a fixed point byte array (starting a startIndex) to a floating point double value.  This function is used primarily by getCalibrationInfo.
		/// </summary>
		/// <param name="buffer">A ReadMem from calibration memory response buffer.</param>
		/// <param name="startIndex">The index in the buffer where to start the fixed point conversion.  Data starts at byte 8. </param>
		/// <returns>Returns a floating point double value.</returns>
		public double FPbyteArrayToFPDouble(byte []buffer, int startIndex) 
		{ 
			uint resultDec = 0;
			int resultWh = 0;
			int i;

			for(i = 0; i < 4; i++) 
			{
				resultDec += (uint)(buffer[startIndex + i] * Math.Pow(2, i*8)); 			
				resultWh += (int)(buffer[startIndex + i + 4] * Math.Pow(2, i*8));
			}

			return ( (double)resultWh + (double)(resultDec)/4294967296.0 );
		}


		/// <summary>
		/// Performs a simple check to determine if the caliInfo struct was set up by getCalibrationInfo.
		/// </summary>
		/// <returns>Returns false if calibrationInfo is not valid, or true if it is.</returns>
		public bool isCalibrationInfoValid()
		{
			if(calibrationInfo.prodID != 3)
				return false;
			return true;
		}
		
	
		/// <summary>
		/// Translates the binary analog input bytes read from the U3, to a voltage value
		/// (calibrated) in Volts. 
		/// //This function is for U3 hardware versions 1.20 and 1.21.  Function will also work
		/// for hardware version 1.30 U3-LV, but not U3-HV.
		/// </summary>
		/// <param name="dac1Enabled">If this is True, then it is indicated that DAC1 is enabled and analog voltage will be calculated with Vreg.  If this is False, then it is indicated that DAC1 is disabled and the analog voltage will be calculated with the AIN slopes and offsets.</param>
		/// <param name="negChannel">The negative channel of the differential analog reading.</param>
		/// <param name="bytesVoltage">The 2 byte voltage that needs to be converted.</param>
		/// <param name="analogVoltage">The converted analog voltage.</param>
		/// <returns>A negative value on error, 0 on success.</returns>
		public int binaryToCalibratedAnalogVoltage(bool dac1Enabled, byte negChannel, ushort bytesVoltage, ref double analogVoltage) 
		{
			if(isCalibrationInfoValid() == false)
				return -1;

			if(calibrationInfo.hardwareVersion >= 1.30)
			{
				if(calibrationInfo.highVoltage == true)
				{
					errorText = "binaryToCalibratedAnalogVoltage error: cannot handle U3-HV device.  Please use binaryToCalibratedAnalogVoltage_hw130 function.";
					return -1;
				}
				else
					return binaryToCalibratedAnalogVoltage_hw130(0, negChannel, bytesVoltage, ref analogVoltage);
			}

			if((negChannel >= 0 && negChannel <= 15) || negChannel == 30)
			{
				if(dac1Enabled == false)
					analogVoltage = calibrationInfo.ainDiffSlope*((double)bytesVoltage) + calibrationInfo.ainDiffOffset;
				else
					analogVoltage = (bytesVoltage/65536.0)*calibrationInfo.vreg*2.0 - calibrationInfo.vreg;
			}
			else if(negChannel == 31)
			{
				if(dac1Enabled == false)
					analogVoltage = calibrationInfo.ainSESlope*((double)bytesVoltage) + calibrationInfo.ainSEOffset;
				else
					analogVoltage = (bytesVoltage/65536.0)*calibrationInfo.vreg;
			}
			else
			{
				errorText = "binaryToCalibratedAnalogVoltage error: invalid negative channel.";
				return -1;
			}

			return 0;
		}


		/// <summary>
		/// Translates the binary analog input bytes read from the U3, to a voltage value
		/// (calibrated) in Volts.  Call getCalibrationInfo first to set up calibrationInfo.
		/// This function is for U3 hardware versions 1.30 (U3-LV/HV).
		/// </summary>
		/// <param name="positiveChannel">The positive channel of the differential analog reading.</param>
		/// <param name="negChannel">The negative channel of the differential analog reading.</param>
		/// <param name="bytesVoltage">The 2 byte voltage that needs to be converted.</param>
		/// <param name="analogVoltage">The converted analog voltage.</param>
		/// <returns>A negative value on error, 0 on success.</returns>
		public int binaryToCalibratedAnalogVoltage_hw130(byte positiveChannel, byte negChannel, ushort bytesVoltage, ref double analogVoltage)
		{
			if(isCalibrationInfoValid() == false)
				return -1;

			if(calibrationInfo.hardwareVersion < 1.30)
			{
				errorText = "binaryToCalibratedAnalogVoltage_hw130 error: cannot handle U3 hardware versions < 1.30 .  Please use binaryToCalibratedAnalogVoltage function.";
				return -1;
			}

			if((negChannel >= 0 && negChannel <= 15) || negChannel == 30)
			{
				if(calibrationInfo.highVoltage == false
					|| (calibrationInfo.highVoltage == true && positiveChannel >= 4 && negChannel >= 4))
					analogVoltage = calibrationInfo.ainDiffSlope*((double)bytesVoltage) + calibrationInfo.ainDiffOffset;
				else if(calibrationInfo.hardwareVersion >= 1.30 && calibrationInfo.highVoltage == true)
				{
					errorText = "binaryToCalibratedAnalogVoltage_hw130 error: invalid negative channel for U3-HV.";
					return -1;
				}
			}
			else if(negChannel == 31)
			{
				if(calibrationInfo.highVoltage == true && positiveChannel >= 0 && positiveChannel < 4)
					analogVoltage = calibrationInfo.hvAINSlope[positiveChannel]*((double)bytesVoltage) + calibrationInfo.hvAINOffset[positiveChannel];
				else
					analogVoltage = calibrationInfo.ainSESlope*((double)bytesVoltage) + calibrationInfo.ainSEOffset;
			}
			else
			{
				errorText = "binaryToCalibratedAnalogVoltage_hw130 error: invalid negative channel.";
				return -1;
			}

			return 0;
		}


		/// <summary>
		/// Translates a analog output voltage value (Volts) to a binary 8 bit value (calibrated)
		/// that can be sent to a U3.  Call getCalibrationInfo first to set up calibrationInfo.
		/// This function is for U3 hardware versions 1.20, 1.21 and 1.30, and does the same
		/// thing as the analogToCalibratedBinary8BitVoltage function.
		/// </summary>
		/// <param name="DACNumber">Channel number of the DAC.</param>
		/// <param name="analogVoltage">The analog voltage that will be converted to a byte value.</param>
		/// <param name="bytesVoltage">The converted 8 bit value.</param>
		/// <returns>A negative value on error, 0 on success.</returns>
		public int analogToCalibratedBinaryVoltage(int DACNumber, double analogVoltage, ref byte bytesVoltage) 
		{
			return analogToCalibratedBinary8BitVoltage(DACNumber, analogVoltage, ref bytesVoltage);
		}


		/// <summary>
		/// Translates a analog output voltage value (Volts) to a binary 8 bit value (calibrated)
		/// that can be sent to a U3.  Call getCalibrationInfo first to set up calibrationInfo.
		/// This function is for U3 hardware versions 1.20, 1.21 and 1.30.
		/// </summary>
		/// <param name="DACNumber">Channel number of the DAC.</param>
		/// <param name="analogVoltage">The analog voltage that will be converted to a byte value.</param>
		/// <param name="bytesVoltage8">The converted 8 bit value.</param>
		/// <returns>A negative value on error, 0 on success.</returns>
		public int analogToCalibratedBinary8BitVoltage(int DACNumber, double analogVoltage, ref byte bytesVoltage8) 
		{
			double tempBytesVoltage;
  
			switch(DACNumber) 
			{
				case 0:
					tempBytesVoltage = analogVoltage*calibrationInfo.dacSlope[0] + calibrationInfo.dacOffset[0];
					break;
				case 1:
					tempBytesVoltage = analogVoltage*calibrationInfo.dacSlope[1] + calibrationInfo.dacOffset[1];      
					break;
				default:
					errorText = "analogToCalibratedBinaryVoltage error: invalid DACNumber";
					return -1;
			}	

			//Checking to make sure bytesVoltage will be a value between 0 and 255, 
			//Too high of an analogVoltage (about 4.95 and above volts) or too low 
			//(below 0 volts) will cause a value not between 0 and 255.
			if(tempBytesVoltage < 0)
				tempBytesVoltage = 0;
			if(tempBytesVoltage > 255)  
				tempBytesVoltage = 255;
  
			bytesVoltage8 = (byte)tempBytesVoltage;

			return 0;
		}


		/// <summary>
		/// Translates a analog output voltage value (Volts) to a binary 16 bit value (calibrated)
		/// that can be sent to a U3-LV/HV.  Call getCalibrationInfo first to set up calibrationInfo.
		/// This function is for U3 hardware version 1.30 (U3-LV/HV).
		/// </summary>
		/// <param name="DACNumber">Channel number of the DAC.</param>
		/// <param name="analogVoltage">The analog voltage that will be converted to a byte value.</param>
		/// <param name="bytesVoltage16">The converted 16 bit value.</param>
		/// <returns>A negative value on error, 0 on success.</returns>
		public int analogToCalibratedBinary16BitVoltage(int DACNumber, double analogVoltage, ref ushort bytesVoltage16)
		{
			double tempBytesVoltage;

			if(isCalibrationInfoValid() == false)
				return -1;

			switch(DACNumber)
			{
				case 0:
					if(calibrationInfo.hardwareVersion < 1.30)
						tempBytesVoltage = analogVoltage*calibrationInfo.dacSlope[0] + calibrationInfo.dacOffset[0];
					else
						tempBytesVoltage = analogVoltage*calibrationInfo.dacSlope[0]*256 + calibrationInfo.dacOffset[0]*256;
					break;
				case 1:
					if(calibrationInfo.hardwareVersion < 1.30)
						tempBytesVoltage = analogVoltage*calibrationInfo.dacSlope[1] + calibrationInfo.dacOffset[1];
					else
						tempBytesVoltage = analogVoltage*calibrationInfo.dacSlope[1]*256 + calibrationInfo.dacOffset[1]*256;
					break;
				default:
					errorText = "analogToCalibratedBinaryVoltage error: invalid channelNumber.";
					return -1;
			}

			//Checking to make sure bytesVoltage will be a value between 0 and 255/65535.  Too
			//high of an analogVoltage (about 4.95 and above volts) or too low (below 0
			//volts) will cause a value not between 0 and 255/65535.
			if(tempBytesVoltage < 0)
				tempBytesVoltage = 0;
			if(tempBytesVoltage > 65535 && calibrationInfo.hardwareVersion >= 1.30)
				tempBytesVoltage = 65535;
			else if(tempBytesVoltage > 255 && calibrationInfo.hardwareVersion < 1.30)
				tempBytesVoltage = 255;

			bytesVoltage16 = (ushort)tempBytesVoltage;

			return 0;
		}


		/// <summary>
		/// Translates the binary analog bytes read from the U3, to a temperature value
		/// (calibrated) in Kelvins.  Call getCalibrationInfo first to set up calibrationInfo.
		/// </summary>
		/// <param name="bytesTemperature">The 2 byte binary temperature that needs to be converted.</param>
		/// <param name="analogTemperature">The converted Kelvins temperature.</param>
		/// <returns>A negative value on error, 0 on success.</returns>
		public int binaryToCalibratedAnalogTemperature(ushort bytesTemperature, ref double analogTemperature) 
		{			
			if(isCalibrationInfoValid() == false)
				return -1;

			analogTemperature = calibrationInfo.tempSlope*((double)bytesTemperature);
			return 0;
		}


		/// <summary>
		/// Translates the binary analog input bytes read from the U3, to a voltage value
		/// (uncalibrated) in Volts.  This function is for U3 hardware versions 1.20 and 1.21.
		/// </summary>
		/// <param name="dac1Enabled">If this is True, then it is indicated that DAC1 is enabled and analog voltage will be calculated with Vreg.  If this is False, then it is indicated that DAC1 is disabled and the analog voltage will be calculated with the AIN slopes and offsets.</param>
		/// <param name="negChannel">The negative channel of the differential analog reading.</param>
		/// <param name="bytesVoltage">The 2 byte voltage that needs to be converted.</param>
		/// <param name="analogVoltage">The converted analog voltage.</param>
		/// <returns>A negative value on error, 0 on success.</returns>
		public int binaryToUncalibratedAnalogVoltage(bool dac1Enabled, byte negChannel, ushort bytesVoltage, ref double analogVoltage) 
		{
			if( (negChannel >= 0 && negChannel <= 15) || negChannel == 30)
			{
				if(dac1Enabled == false)
					analogVoltage = (double)bytesVoltage*0.000074463 - 2.44;
				else
					analogVoltage = ((double)bytesVoltage/65536.0)*6.6 - 3.3;
			}  
			else if(negChannel == 31)
			{
				if(dac1Enabled == false)
					analogVoltage = (double)bytesVoltage*0.000037231;
				else
					analogVoltage = ((double)bytesVoltage/65536.0)*3.3;
			}
			else
			{
				errorText = "binaryToCalibratedAnalogVoltage error: invalid negative channel";
				return -1;
			}

			return 0;
		}


		/// <summary>
		/// Translates the binary analog input bytes read from the U3, to a voltage value
		/// (uncalibrated) in Volts.  This function is for U3 hardware versions 1.30 (U3-LV/HV).
		/// </summary>
		/// <param name="highVoltage">Set to 1 to indicate that U3-HV calculations should be used for the binary to voltage conversion, otherwise U3-LV voltage calculations will be used.</param>
		/// <param name="positiveChannel">The positive channel of the differential analog reading.</param>
		/// <param name="negChannel">The negative channel of the differential analog reading.</param>
		/// <param name="bytesVoltage">The 2 byte voltage that needs to be converted.</param>
		/// <param name="analogVoltage">The converted analog voltage.</param>
		/// <returns>A negative value on error, 0 on success.</returns>
		public int binaryToUncalibratedAnalogVoltage_hw130(bool highVoltage, byte positiveChannel, byte negChannel, ushort bytesVoltage, ref double analogVoltage)
		{
			if( (negChannel >= 0 && negChannel <= 15) || negChannel == 30)
			{
				if(highVoltage == false
					|| (highVoltage == true && positiveChannel >= 4 && negChannel >= 4))
					analogVoltage = (double)bytesVoltage*0.000074463 - 2.44;
				else if(highVoltage == true)
				{
					errorText = "binaryToCalibratedAnalogVoltage_hw130 error: invalid negative channel for U3-HV.";
					return -1;
				}
			}
			else if(negChannel == 31)
			{
				if(highVoltage == true)
					analogVoltage = (double)bytesVoltage*0.000314 - 10.3;
				else
					analogVoltage = (double)bytesVoltage*0.000037231;
			}
			else
			{
				errorText = "binaryToCalibratedAnalogVoltage_hw130 error: invalid negative channel.";
				return -1;
			}

			return 0;
		}


		/// <summary>
		/// Translates a DAC voltage value (Volts) to a binary 8 bit value (uncalibrated)
		/// that can be sent to a U3.
		/// This function is for U3 hardware versions 1.20 and 1.21, and does the same
		/// thing as the analogToUncalibratedBinary8BitVoltage function.
		/// </summary>
		/// <param name="analogVoltage">The analog voltage that needs to be converted.</param>
		/// <param name="bytesVoltage">The converted binary 8 bit value.</param>
		/// <returns>A negative value on error, 0 on success.</returns>
		public int analogToUncalibratedBinaryVoltage(double analogVoltage, ref byte bytesVoltage)
		{
			return analogToUncalibratedBinary8BitVoltage(analogVoltage, ref bytesVoltage);
		}


		/// <summary>
		/// Translates a DAC voltage value (Volts) to a binary 8 bit value (uncalibrated)
		/// that can be sent to a U3.
		/// This function is for U3 hardware versions 1.20 and 1.21.
		/// </summary>
		/// <param name="analogVoltage">The analog voltage that needs to be converted.</param>
		/// <param name="bytesVoltage8">The converted binary 8 bit value.</param>
		/// <returns>A negative value on error, 0 on success.</returns>
		public int analogToUncalibratedBinary8BitVoltage(double analogVoltage, ref byte bytesVoltage8)
		{
			double tempBytesVoltage;

			tempBytesVoltage = analogVoltage*51.717;

			//Checking to make sure bytesVoltage will be a value between 0 and 255.  Too
			//high of an analogVoltage (about 4.95 and above volts) or too low (below 0
			//volts) will cause a value not between 0 and 255.
			if(tempBytesVoltage < 0)
				tempBytesVoltage = 0;
			else if(tempBytesVoltage > 255)
				tempBytesVoltage = 255;

			bytesVoltage8 = (byte)tempBytesVoltage;

			return 0;
		}


		/// <summary>
		/// Translates a DAC voltage value (Volts) to a binary 16 bit value (uncalibrated)
		/// that can be sent to a U3-LV/HV.
		/// This function is for U3 hardware versions 1.30 (U3-LV/HV).
		/// </summary>
		/// <param name="analogVoltage">The analog voltage that needs to be converted.</param>
		/// <param name="bytesVoltage16">The converted binary 16 bit value.</param>
		/// <returns>A negative value on error, 0 on success.</returns>
		public int analogToUncalibratedBinary16BitVoltage(double analogVoltage, ref ushort bytesVoltage16)
		{
			double tempBytesVoltage;

			tempBytesVoltage = analogVoltage*51.717*256;

			//Checking to make sure bytesVoltage will be a value between 0 and 65535.  Too
			//high of an analogVoltage (about 4.95 and above volts) or too low (below 0
			//volts) will cause a value not between 0 and 65535.
			if(tempBytesVoltage < 0)
				tempBytesVoltage = 0;
			if(tempBytesVoltage > 65535)
				tempBytesVoltage = 65535;

			bytesVoltage16 = (ushort)tempBytesVoltage;

			return 0;
		}


		/// <summary>
		/// Translates the binary analog bytes read from the U3, to a temperature value
		/// (uncalibrated) in Kelvins.
		/// </summary>
		/// <param name="bytesTemperature">The 2 byte binary temperature that needs to be converted.</param>
		/// <param name="analogTemperature">The converted Kelvins temperature.</param>
		/// <returns>A negative value on error, 0 on success.</returns>
		public int binaryToUncalibratedAnalogTemperature(ushort bytesTemperature, ref double analogTemperature)
		{
			analogTemperature = (double)bytesTemperature*0.013021;
			return 0;
		}
	}
}