using System;
using Microsoft.SPOT;
using System.IO.Ports;
using System.Text;
using System.Threading;
using Microsoft.SPOT.Hardware;
using SensorModules;
using SWModules;

namespace HWModules
{
    public sealed class CTD1 : SBE41CTD
    {
        private CTD1()
            : base("CTD1", "COM1", 9600, Parity.None, 8, StopBits.One, 5000, 20, false, (byte)0)
        { }

        /// <summary>
        /// singleton implementation
        /// </summary>
        private static CTD1 instance;
        public static CTD1 Instance
        {
            get
            {
                if (instance == null)
                {
                    instance = new CTD1();
                }
                return instance;
            }
        }
    }

    public class SBE41CTD
    {
        private BuoyancyEngine buoyancyEngine;

        private static OutputPort DR = new OutputPort(GHI.Pins.G400S.Gpio.PA28, false);
        private static OutputPort Mode = new OutputPort(GHI.Pins.G400S.Gpio.PC29, true);

        public int samplePeriod { get; private set; }

        private static StringBuilder sbTemp = new StringBuilder(512);

        private static readonly StringBuilder startProfileCommand = new StringBuilder("startprofile5\r");
        private static byte[] startProfileCommandBytes = new byte[startProfileCommand.Length];

        private NativeUART IOPort;

        private static MSC1 msc;

        public SBE41CTD(string id, string portName, int baudRate, Parity parity, int dataBits, StopBits stopBits, int readTimeout, int writeTimeout, bool useSTX, byte STX)
        {
            IOPort = new NativeUART(id, QRecordType.SBE41, processMessage, portName, baudRate, parity, dataBits, stopBits, readTimeout, writeTimeout, useSTX, STX);

            IOPort.Open();
            IOPort.DiscardInBuffer();
            IOPort.DiscardOutBuffer();

            buoyancyEngine = BuoyancyEngine.Instance;

            SBE41Timer = new Timer(new TimerCallback(SBE41CommandModeTimerCallback), null, -1, -1);

            samplePeriod = MissionConfig.SBE41CommandModeSamplePeriod;

            powerCycleCount = 0;
        }

        public void init()
        {
            msc = MSC1.Instance;

            for (int i = 0; i < rawPressureDelayLineLength; i++)
                rawPressureDelayLine[i] = 0;

            Array.Clear(startProfileCommandBytes, 0, startProfileCommandBytes.Length);
            Array.Copy(UTF8Encoding.UTF8.GetBytes(startProfileCommand.ToString()), startProfileCommandBytes, startProfileCommand.Length);

            setCommandMode();

            //Setup SBE41 Options
            //First make sure it is awake
            sendCRLF();

            //Opening the port may force a PTS sample so wait for it here
            Thread.Sleep(5000);

            sendStopProfile();
            Thread.Sleep(1000);

            setPressureCutoff(MissionConfig.CpPressureCutoff);
            Thread.Sleep(1000);

            sendOutputPTSYesCommand();
            Thread.Sleep(1000);

            setTSWait();
            Thread.Sleep(1000);

            setBinParameters();
            Thread.Sleep(1000);
        }

        private static readonly byte[] dontAutoBinAvg = UTF8Encoding.UTF8.GetBytes("autobinavg=n\r");
        private static readonly byte[] doAutoBinAvg = UTF8Encoding.UTF8.GetBytes("autobinavg=y\r");
        private static readonly byte[] topBinInterval = UTF8Encoding.UTF8.GetBytes("top_bin_interval=2\r");
        private static readonly byte[] topBinSize = UTF8Encoding.UTF8.GetBytes("top_bin_size=2\r");
        private static readonly byte[] topBinMax = UTF8Encoding.UTF8.GetBytes("top_bin_max=10\r");
        private static readonly byte[] middleBinInterval = UTF8Encoding.UTF8.GetBytes("middle_bin_interval=2\r");
        private static readonly byte[] middleBinSize = UTF8Encoding.UTF8.GetBytes("middle_bin_size=2\r");
        private static readonly byte[] middleBinMax = UTF8Encoding.UTF8.GetBytes("middle_bin_max=20\r");
        private static readonly byte[] bottomBinInterval = UTF8Encoding.UTF8.GetBytes("bottom_bin_interval=2\r");
        private static readonly byte[] bottomBinSize = UTF8Encoding.UTF8.GetBytes("bottom_bin_size=2\r");
        private static readonly byte[] dontIncludeTransitionBin = UTF8Encoding.UTF8.GetBytes("includetransitionbin=n\r");
        private static readonly byte[] doIncludeNumScansPerBin = UTF8Encoding.UTF8.GetBytes("includenbin=y\r");
        private void setBinParameters()
        {
            EngrLogger.writeToColumns("Sending 11 bin setup commands");
            IOPort.Write(dontAutoBinAvg, 0, dontAutoBinAvg.Length);
            Thread.Sleep(500);
            IOPort.Write(topBinInterval, 0, topBinInterval.Length);
            Thread.Sleep(500);
            IOPort.Write(topBinSize, 0, topBinSize.Length);
            Thread.Sleep(500);
            IOPort.Write(topBinMax, 0, topBinMax.Length);
            Thread.Sleep(500);
            IOPort.Write(middleBinInterval, 0, middleBinInterval.Length);
            Thread.Sleep(500);
            IOPort.Write(middleBinSize, 0, middleBinSize.Length);
            Thread.Sleep(500);
            IOPort.Write(middleBinMax, 0, middleBinMax.Length);
            Thread.Sleep(500);
            IOPort.Write(bottomBinInterval, 0, bottomBinInterval.Length);
            Thread.Sleep(500);
            IOPort.Write(bottomBinSize, 0, bottomBinSize.Length);
            Thread.Sleep(500);
            IOPort.Write(dontIncludeTransitionBin, 0, dontIncludeTransitionBin.Length);
            Thread.Sleep(500);
            IOPort.Write(doIncludeNumScansPerBin, 0, doIncludeNumScansPerBin.Length);
            Thread.Sleep(500);
        }

        private Timer SBE41Timer;

        public void powerUp()
        {
            EngrLogger.Comment("Powering Up CTD");

            var mboard = MotherBoard.Instance;
            mboard.EnableChannelPower(MotherBoard.ChannelNames.CTDChannel);
        }

        public void powerDown()
        {
            EngrLogger.Comment("Powering Down CTD");

            var mboard = MotherBoard.Instance;
            mboard.DisableChannelPower(MotherBoard.ChannelNames.CTDChannel);
        }

        private bool commandModeTimerRunning;
        public void startCommandModeTimer(int timerPeriod)
        {
            //Start CTD command mode sample timer
            EngrLogger.Comment("CTD Starting Command Mode Timer");
            SBE41Timer.Change(500, timerPeriod / 2);
            samplePeriod = timerPeriod;
            commandModeTimerRunning = true;
        }

        public void stopCommandModeTimer()
        {
            EngrLogger.Comment("CTD Stopping Command Mode Timer");
            SBE41Timer.Change(-1, -1);
            commandModeTimerRunning = false;
        }

        public bool CommandModeTimerEnabled
        {
            get { return commandModeTimerRunning; }
        }


        private static readonly byte[] fpCommand = { 102, 112, 13 };   //fp\r
        public void sendFastPressure()
        {
            EngrLogger.Comment(0, "CTD Sending fast pressure");
            IOPort.Write(fpCommand, 0, fpCommand.Length);
        }

        private static readonly byte[] dsCommand = { 100, 115, 13 };   //ds\r
        public void sendDS()
        {
            EngrLogger.writeToColumns("Sending ds command");
            IOPort.Write(dsCommand, 0, dsCommand.Length);
        }

        private static readonly byte[] dcCommand = { 100, 99, 13 };    //dc\r 
        public void sendDC()
        {
            EngrLogger.writeToColumns("Sending dc command");
            IOPort.Write(dcCommand, 0, dcCommand.Length);
        }

        private static readonly byte[] getHDCommand = UTF8Encoding.UTF8.GetBytes("gethd\r");
        public void sendGetHD()
        {
            EngrLogger.writeToColumns("Sending gethd command");
            IOPort.Write(getHDCommand, 0, getHDCommand.Length);
        }

        private static byte[] getCDCommand = UTF8Encoding.UTF8.GetBytes("getcd\r");
        public void sendGetCD()
        {
            EngrLogger.writeToColumns("Sending getcd command");
            IOPort.Write(getCDCommand, 0, getCDCommand.Length);
        }

        private static readonly byte[] pumponCommand = { 112, 117, 109, 112, 111, 110, 13 };    // "pumpon"
        public void sendPumpon()
        {
            EngrLogger.writeToColumns("Sending pumpon command");
            IOPort.Write(pumponCommand, 0, pumponCommand.Length);
        }

        private static readonly byte[] pumpoffCommand = { 112, 117, 109, 112, 111, 102, 102, 13 };    // "pumpoff"
        public void sendPumpoff()
        {
            EngrLogger.writeToColumns("Sending dc command");
            IOPort.Write(pumpoffCommand, 0, pumpoffCommand.Length);
        }



        private static readonly byte[] qsCommand = { 113, 115, 13 };   //qs\r
        public void sendQS()
        {
            IOPort.Write(qsCommand, 0, qsCommand.Length);
        }

        public void resetSBE41()
        {
            EngrLogger.Comment("Sending SBE41 QSR command");
            sendQSR();
            Thread.Sleep(2000);    //TODO P2 seems like we should have a non-blocking thread.sleep
            sendCRLF();
        }

        public void powerCycleSBE41()
        {
            EngrLogger.Comment("Power Cycling SBE41");
            powerDown();
            Thread.Sleep(1000);//TODO P2 seems like we should have a non-blocking thread.sleep
            powerUp();
            Thread.Sleep(1000);
            sendCRLF();
        }

        private static readonly byte[] qsrCommand = { 113, 115, 114, 13 };    // qs\r
        public void sendQSR()
        {
            EngrLogger.writeToColumns("Sending qsr command");
            IOPort.Write(qsrCommand, 0, qsrCommand.Length);
        }

        //Note: this is just the header for the pcutoff command.
        private static StringBuilder pcutoff = new StringBuilder("pcutoff=");
        private void setPressureCutoff(double desiredPressureCutoff)
        {
            pcutoff.Append(desiredPressureCutoff.ToString());
            pcutoff.Append("\r");

            EngrLogger.writeToColumns(1, pcutoff.ToString());
            IOPort.Write(UTF8Encoding.UTF8.GetBytes(pcutoff.ToString()), 0, pcutoff.Length);
        }

        private static StringBuilder qs = new StringBuilder("qs");
        private void sendQSCommand()
        {
            IOPort.Write(UTF8Encoding.UTF8.GetBytes(qs.ToString()), 0, qs.Length);
        }

        private static byte[] outputptsYes = UTF8Encoding.UTF8.GetBytes("outputpts=y\r");
        private void sendOutputPTSYesCommand()
        {
            EngrLogger.writeToColumns("Sending outputPTSyes");
            IOPort.Write(outputptsYes, 0, outputptsYes.Length);
        }

        private static byte[] tsWait = UTF8Encoding.UTF8.GetBytes("tswait=2\r");
        private void setTSWait()
        {
            EngrLogger.writeToColumns("Sending tsWait");
            IOPort.Write(tsWait, 0, tsWait.Length);
        }

        private static StringBuilder outputptsNo = new StringBuilder("outputpts=n\r");
        private void sendOutputPTSNoCommand()
        {
            EngrLogger.writeToColumns("Sending outputPTSNo");
            IOPort.Write(UTF8Encoding.UTF8.GetBytes(outputptsNo.ToString()), 0, outputptsNo.Length);
        }

        public void startProfile()
        {
            IOPort.Write(startProfileCommandBytes, 0, startProfileCommandBytes.Length);
        }

        private static readonly byte[] stopProfileCommand = UTF8Encoding.UTF8.GetBytes("stopprofile\r");
        public void sendStopProfile()
        {
            EngrLogger.writeToColumns("Sending SBE41 stopProfile");
            IOPort.Write(stopProfileCommand, 0, stopProfileCommand.Length);
            SV.InCPMode = false;
        }

        private static readonly byte[] ddCommand = { 100, 100, 13 };    //100 = d,  13 = CR
        public void dumpData()
        {
            EngrLogger.writeToColumns("Sending dd command");
            IOPort.Write(ddCommand, 0, ddCommand.Length);
        }

        private static readonly byte[] daCommand = UTF8Encoding.UTF8.GetBytes("da\r");
        public void dumpBinnedData()
        {
            EngrLogger.writeToColumns("Sending da command");
            IOPort.Write(daCommand, 0, daCommand.Length);
        }

        private static readonly byte[] dahCommand = UTF8Encoding.UTF8.GetBytes("dah\r");
        public void dumpBinnedDataHex()
        {
            EngrLogger.writeToColumns("Sending dah command");
            IOPort.Write(dahCommand, 0, dahCommand.Length);
        }

        private static readonly byte[] binaverageCommand = UTF8Encoding.UTF8.GetBytes("binaverage\r");
        public void sendBinAverage()
        {
            EngrLogger.writeToColumns("Sending binaverage");
            IOPort.Write(binaverageCommand, 0, binaverageCommand.Length);
        }

        private static StringBuilder sendPTSHeader = new StringBuilder("Sending CTD PTS");
        private static readonly byte[] ptsCommand = { 112, 116, 115, 13 };    // pts\r
        public void sendPTS()
        {
            EngrLogger.writeToColumns(sendPTSHeader);
            IOPort.Write(ptsCommand, 0, ptsCommand.Length);
        }

        private static readonly byte[] crlfCommand = { 10, 13 };
        public void sendCRLF()
        {
            IOPort.Write(crlfCommand, 0, crlfCommand.Length);
        }

        public void exitCommandMode()
        {
            sendQSR();
            Thread.Sleep(1000);
            DR.Write(false);
            Mode.Write(true);
            Thread.Sleep(1000);
        }

        public void setCommandMode()
        {
            Mode.Write(false);
            Thread.Sleep(1);
            DR.Write(true);
        }

        private bool timeToSendRequest;
        private bool ptsSent;
        private void SBE41CommandModeTimerCallback(object state)
        {
            if (timeToSendRequest)
            {

                if (sendPtsOnNextRequest)
                {
                    if (!ptsSent)
                    {
                        sendPTS();
                        ptsSent = true;
                    }
                    else
                    {
                        ptsSent = false;
                        sendPtsOnNextRequest = false;
                    }
                }
                else
                {
                    sendFastPressure();
                }
                timeToSendRequest = false;
                return;
            }

            sendCRLF();
            timeToSendRequest = true;

        }

        private bool sendPtsOnNextRequest;
        public void OnNextRequest(bool sendPts)
        {
            sendPtsOnNextRequest = sendPts;
            ptsSent = false;
        }

        public struct PTSRecord
        {
            public float pressure;
            public float temperature;
            public float salinity;
            public DateTime timeStamp;
        }
        private static PTSRecord ptsRecord;
        //TODO P1 Probably needs to change to BuoyancyEngine.init

        public PTSRecord parse2(byte[] inBytes)
        {
            ptsRecord.pressure = float.MaxValue;
            ptsRecord.temperature = float.MaxValue;
            ptsRecord.salinity = float.MaxValue;
            ptsRecord.timeStamp = DateTime.Now;

            byte[] returnBytes = new byte[16];

            inSB.Clear();
            inSB.Append(SafeEncoding.GetChars(inBytes));

            replaceCRLF(ref inBytes);
            sbTemp.Clear();
            sbTemp.Append(SafeEncoding.GetChars(inBytes));

            if (isOneNumberOnly(inBytes))
            {
                #region Process pressure only
                //EngrLogger.Comment("CTD Parsing Pressure Only");
                try
                {
                    sbTemp.Clear();
                    sbTemp.Append(SafeEncoding.GetChars(inBytes));
                    ptsRecord.pressure = (float)double.Parse(sbTemp.ToString());
                    sendCRLF();
                    //Debug.Print("Parsed pressure value = " + parsedPressure.ToString("f2"));
                }
                catch
                {
                    EngrLogger.writeToColumns("[ERROR] parsing pressure only in SBE41CTD.parse2");
                    ptsRecord.pressure = float.MaxValue;
                }

                //TODO P2 This should really be at a higher level not buried down here is the parse method
                if (SciLogger.sampling(SciLogger.Instruments.Ctd) && !SciLogger.posted(SciLogger.Instruments.Ctd))
                {
                    if (ptsRecord.pressure != float.MaxValue)
                    {
                        sbTemp.Clear();
                        if (ptsRecord.pressure != float.MaxValue)
                            sbTemp.Append(ptsRecord.pressure.ToString("f2"));
                        else
                            sbTemp.Append("NaN");

                        sbTemp.Append("\tNaN\tNaN");

                        //SciLogger.postSample(SciLogger.Instruments.Ctd, UTF8Encoding.UTF8.GetBytes(sbTemp.ToString()));

                        sbTemp2.Clear();
                        sbTemp2.Append("CTD pressure only post: ");
                        sbTemp2.Append(sbTemp);
                        EngrLogger.writeToColumns(EngrLiterals.ColumnNums.dateTime, DateTime.Now, sbTemp2.Replace(Literals.tabChar, Literals.commaChar));
                    }
                }
                #endregion
            }
            else if (has2Commas(inBytes))
            {
                #region process PTS message
                //EngrLogger.Comment("CTD Parsing PTS");
                //If a CTD message has 2 commas in it, it is a pts message
                //but it might start with a S>

                if (inBytes[0] == 83)
                    inBytes[0] = 32;
                if (inBytes[1] == 62)
                    inBytes[1] = 32;

                //Get pressure from first field
                Array.Clear(returnBytes, 0, returnBytes.Length);
                Utils.getField(inBytes, 44, 0, ref returnBytes);
                try
                {
                    sbTemp.Clear();
                    sbTemp.Append(SafeEncoding.GetChars(returnBytes));
                    ptsRecord.pressure = (float)double.Parse(sbTemp.ToString());
                    //Debug.Print("Parsed pressure value in pts message = " + parsedPressure.ToString("f2"));
                }
                catch
                {
                    EngrLogger.writeToColumns("[ERROR] parsing pressure in SBE41CTD.parse2 2 commas");
                    ptsRecord.pressure = float.MaxValue;
                    //Debug.Print("[ERROR] parsing pressure value in pts message");
                }

                //Get temperature from second field
                Array.Clear(returnBytes, 0, returnBytes.Length);
                Utils.getField(inBytes, 44, 1, ref returnBytes);
                try
                {
                    sbTemp.Clear();
                    sbTemp.Append(SafeEncoding.GetChars(returnBytes));
                    ptsRecord.temperature = (float)double.Parse(sbTemp.ToString());
                    //Debug.Print("Parsed temperature value in pts message = " + parsedTemperature.ToString("f5"));
                }
                catch
                {
                    EngrLogger.writeToColumns("[ERROR] parsing temperature in SBE41CTD.parse2 2 commas");
                    ptsRecord.temperature = float.MaxValue;
                    //Debug.Print("[ERROR] parsing temperature value in pts message");
                }

                //Get salinity from third field
                Array.Clear(returnBytes, 0, returnBytes.Length);
                Utils.getField(inBytes, 44, 2, ref returnBytes);
                try
                {
                    sbTemp.Clear();
                    sbTemp.Append(SafeEncoding.GetChars(returnBytes));
                    ptsRecord.salinity = (float)double.Parse(sbTemp.ToString());
                    //Debug.Print("Parsed salinity value in pts message = " + parsedSalinity.ToString("f5"));
                }
                catch
                {
                    EngrLogger.writeToColumns("[ERROR] parsing salinity in SBE41CTD.parse2 2 commas");
                    ptsRecord.salinity = float.MaxValue;
                    //Debug.Print("[ERROR] parsing salinity value in pts message");
                }

                //TODO P2 This should really be at a higher level not buried down here is the parse method
                if (SciLogger.sampling(SciLogger.Instruments.Ctd) && !SciLogger.posted(SciLogger.Instruments.Ctd))
                {
                    if (ptsRecord.pressure != float.MaxValue)
                    {
                        sbTemp.Clear();
                        if (ptsRecord.pressure != float.MaxValue)
                            sbTemp.Append(ptsRecord.pressure.ToString("f2"));
                        else
                            sbTemp.Append("NaN");
                        sbTemp.Append(Literals.tabChar);

                        if (ptsRecord.temperature != float.MaxValue)
                            sbTemp.Append(ptsRecord.temperature.ToString("f4"));
                        else
                            sbTemp.Append("NaN");
                        sbTemp.Append(Literals.tabChar);

                        if (ptsRecord.salinity != float.MaxValue)
                            sbTemp.Append(ptsRecord.salinity.ToString("f3"));
                        else
                            sbTemp.Append("NaN");

                        SciLogger.setCTDValues(ptsRecord);
                        SciLogger.postSample(SciLogger.Instruments.Ctd, UTF8Encoding.UTF8.GetBytes(sbTemp.ToString()));

                        sbTemp2.Clear();
                        sbTemp2.Append("CTD pts post: ");
                        sbTemp2.Append(sbTemp);
                        EngrLogger.writeToColumns(EngrLiterals.ColumnNums.dateTime, DateTime.Now, sbTemp2.Replace(Literals.tabChar, Literals.commaChar));

                    }
                }
                #endregion
            }

            return (ptsRecord);
        }

        private static byte[] binaverageSamplesBA = UTF8Encoding.UTF8.GetBytes("samples =");
        private static byte[] binaverageNBinsBA = UTF8Encoding.UTF8.GetBytes("nbins =");
        private static byte[] retField = new byte[16];

        private static void checkForBinaverageData(byte[] inBytes)
        {
            if (GHI.Utilities.Arrays.Contains(inBytes, binaverageSamplesBA) >= 0)
            {
                Utils.getField(inBytes, (byte)Literals.commaChar, 0, ref retField);
                SciLogger.appendCPData(retField);
                SciLogger.appendCPData(Literals.spaceBytes);
            }
            else if (GHI.Utilities.Arrays.Contains(inBytes, binaverageNBinsBA) >= 0)
            {
                Utils.getField(inBytes, (byte)Literals.commaChar, 1, ref retField);
                SciLogger.appendCPData(retField);
                SciLogger.appendCPData(Literals.crlfBytes);
            }
        }


        private static bool gotDAH = false;
        private static byte[] uploadCompleteBA = UTF8Encoding.UTF8.GetBytes("upload complete");
        private static byte[] dahBA = UTF8Encoding.UTF8.GetBytes("S>dah");

        private static void checkForCPData(byte[] inBytes)
        {
            if (gotDAH)
            {
                if (GHI.Utilities.Arrays.Contains(inBytes, uploadCompleteBA) >= 0)
                {
                    SciLogger.writeCPData();
                    gotDAH = false;
                }
                else
                {
                    SciLogger.appendCPData(inBytes);
                }
            }

            if (GHI.Utilities.Arrays.Contains(inBytes, dahBA) >= 0)
                gotDAH = true;
        }

        private static bool dontParse(byte[] inBytes)
        {
            //Any message with something other than a space, CR, LF, negative sign, period, comma or numeric digit shouldn't get parsed
            for (int i = 0; i < inBytes.Length; i++)
            {
                if ((inBytes[i] == 32) || (inBytes[i] == 10) || (inBytes[i] == 13) || (inBytes[i] == 45) || (inBytes[i] == 44) || (inBytes[i] == 46) || ((inBytes[i] > 47) && (inBytes[i] < 58)))
                    break;
                else
                    return (true);
            }
            return (false);
        }

        private static void replaceCRLF(ref byte[] inBytes)
        {
            for (int i = 0; i < inBytes.Length; i++)
            {
                if (inBytes[i] == 10)
                    inBytes[i] = 32;

                if (inBytes[i] == 13)
                    inBytes[i] = 32;
            }
        }

        private static bool isOneNumberOnly(byte[] inBytes)
        {
            for (int i = 0; i < inSB.Length; i++)
            {
                //inSB can only contain - or . or space or be a numeric digit 
                if (inBytes[i] == 45 || inBytes[i] == 32 || inBytes[i] == 46 || ((inBytes[i] > 47) && (inBytes[i] < 58))) { }
                else
                    return (false);
            }
            return (true);
        }

        private static bool has2Commas(byte[] inBytes)
        {
            int numCommas = 0;

            for (int i = 0; i < inBytes.Length; i++)
            {
                if (inBytes[i] == 44)
                    numCommas++;
            }

            if (numCommas == 2)
                return (true);
            else
                return (false);
        }

        private static StringBuilder inSB = new StringBuilder(4096);
        private static StringBuilder sbTemp2 = new StringBuilder(512);

        private int numberOfCommas(byte[] inBytes)
        {
            int numCommas = 0;

            for (int i = 0; i < inBytes.Length; i++)
            {
                if (inBytes[i] == 44)
                    numCommas = numCommas + 1;
            }
            return (numCommas);
        }

        //See .m files in directory for non-CP Mode PFilter design parameters
        //for CP Mode using filter design and automation tool from SPTool with
        //Response type = lowpass, Design Method = FIR Window, Order = 10, Window = Blackman-Harris, Units = Hz, Fs = 1, Fc = .02
        private static double[][] PFilterCoef = new double[2][]
        {
            //These are for non CP mode
            new double[] {8.81131298055604e-06,	0.00211708177469335,	0.0240149216743935,	0.102119533086314,	0.225973798612221,
                          0.291531707078795,	0.225973798612221,	0.102119533086314,	0.0240149216743935,	0.00211708177469335,
                          8.81131298055604e-06},
            //These are for CP mode
            new double[] {1.57246287686607e-05, 0.00294876425891433, 0.0281799141639923, 0.106973234459911, 0.221807456935806,
                          0.280149811105215, 0.221807456935806, 0.106973234459911, 0.0281799141639923, 0.00294876425891433,
                          1.57246287686607e-05}
        };

        //Designed with for nonCP mode
        //d = fdesign.differentiator('N,Fp,Fst', 8, .1, .4);
        //Hd = design(d, 'equiripple');
        //and for CP mode
        //d = fdesign.differentiator('N,Fp,Fst', 8, .1/3, .4/3);
        //Hd = design(d, 'equiripple')

        private static double[][] VFilterCoef = new double[2][]
        {
            //These are for non CP mode
            new double[] {-0.035538709937166, 0.121018262495538, 0.110204653307495, 0.075228720456011, 0.0, 
                          -0.075228720456011, -0.110204653307495, -0.121018262495538, 0.035538709937166},
            //These are for CP mode
            new double[] {0.123601865482124, 0.000071541838701, 0.000051713468660, 0.000027758225109, 0.0,
                          -0.000027758225109, -0.000051713468660, -0.000071541838701, -0.123601865482124}
        };

        private static int[] numPCoef = new int[] { PFilterCoef[0].Length, PFilterCoef[1].Length };
        private static int[] numVCoef = new int[] { VFilterCoef[0].Length, VFilterCoef[1].Length };

        private static double accum = 0.0;
        //public double ctdSamplePeriod = double.NaN;

        private static int maxDelayLineLength = 64;

        private static double[] PFilterDelayLine = new double[maxDelayLineLength];
        private static double[] VFilterDelayLine = new double[maxDelayLineLength];
        private static double[] lpfPressureArray = new double[128];
        private static double[] lpfPressureArrayTimeStamps = new double[128];

        private static int filterIndex;

        public struct LPFResults
        {
            public double RawPressure;
            public double LPFPressure;
            public double LPFVelocity;
            public double DiffVelocity;
            public double Diff2Velocity;
            public double slope;
            public double timestamp;
        }
        private static LPFResults sbe41LPF;

        public LPFResults SBE41LPF
        {
            get { return sbe41LPF; }
            private set { sbe41LPF = value; }
        }
        private static int filterType = 0;

        //This is the number of points to use in the slope calculation from Numerical Recipes in C, Chapter 15;
        private static int ss = 40;

        //Variables for slope calculation
        private static double sx = 0.0;
        private static double sy = 0.0;
        private static double sxoss = 0.0;
        private static double st2 = 0.0;
        private static double b = 0.0;
        private static double t = 0.0;

        public LPFResults LPFPressure(double pressureSample, double timeStamp, int samplePeriod, bool initialize)
        {
            double dt;


            if (SV.InCPMode)
            {
                filterType = 1;
                dt = (double)samplePeriod / 1000.0;
            }
            else
            {
                filterType = 0;
                dt = (double)samplePeriod / 1000.0;
            }

            if (initialize)
            {
                EngrLogger.writeToColumns("Initializing SBE 41 LPF");
                for (filterIndex = 0; filterIndex < maxDelayLineLength; filterIndex++)
                {
                    PFilterDelayLine[filterIndex] = pressureSample;
                }
            }

            accum = 0.0;
            PFilterDelayLine[0] = pressureSample;
            for (filterIndex = 0; filterIndex < numPCoef[filterType]; filterIndex++)
            {
                accum = accum + (PFilterCoef[filterType][filterIndex] * PFilterDelayLine[filterIndex]);
            }
            sbe41LPF.RawPressure = pressureSample;
            sbe41LPF.LPFPressure = accum;
            sbe41LPF.DiffVelocity = (PFilterDelayLine[1] - PFilterDelayLine[0]) / dt;  //Don't need the -1.0 because we're subtracting  ( (t-1) - t0 )
            sbe41LPF.Diff2Velocity = (PFilterDelayLine[2] - PFilterDelayLine[0]) / (2 * dt);  //Don't need the -1.0 because we're subtracting  ( (t-1) - t0 )

            if (initialize)
            {
                for (int i = 0; i < 128; i++)
                {
                    lpfPressureArray[i] = sbe41LPF.LPFPressure;
                    lpfPressureArrayTimeStamps[i] = timeStamp - (i * dt);
                }
            }
            lpfPressureArray[0] = sbe41LPF.LPFPressure;
            lpfPressureArrayTimeStamps[0] = timeStamp;

            sx = 0.0;
            sy = 0.0;

            for (int i = 0; i < ss; i++)
            {
                sx = sx + lpfPressureArrayTimeStamps[i];
                sy = sy + lpfPressureArray[i];
                //Debug.Print(lpfPressureArrayTimeStamps[i].ToString("f2") + "  " + lpfPressureArray[i].ToString("f2"));
            }
            sxoss = sx / ss;

            st2 = 0;
            b = 0;
            for (int i = 0; i < ss; i++)
            {
                t = lpfPressureArrayTimeStamps[i] - sxoss;
                st2 = st2 + t * t;
                b = b + t * lpfPressureArray[i];
            }
            b = -b / st2;
            sbe41LPF.slope = b;

            if (initialize)
            {
                for (filterIndex = 0; filterIndex < maxDelayLineLength; filterIndex++)
                {
                    VFilterDelayLine[filterIndex] = sbe41LPF.LPFPressure;
                }
            }

            accum = 0.0;
            VFilterDelayLine[0] = sbe41LPF.LPFPressure;
            for (filterIndex = 0; filterIndex < numVCoef[filterType]; filterIndex++)
            {
                accum = accum + (VFilterCoef[filterType][filterIndex] * VFilterDelayLine[filterIndex]);
            }
            sbe41LPF.LPFVelocity = -1.0 * accum / dt;         //-1.0 because pressure increases positively down but velocity in the down direction needs to be negative

            //Console.WriteLine("Pressure = " + SBE41LPFResults.Pressure.ToString("F6") +
            //"   LPF Pressure = " + SBE41LPFResults.LPFPressure.ToString("f4") +
            //"   LPF Velocity = " + SBE41LPFResults.LPFVelocity.ToString("f4") +
            //"   Difference Velocity = " + SBE41LPFResults.diffVelocity.ToString("f4") +
            //"   Slope = " + SBE41LPFResults.slope.ToString("f4") +
            //"   CTD Sample Period = " + dt.ToString());

            //for (int i = 0; i < PFilterDelayLine.Length; i++)
            //    Console.WriteLilne(PFilterDelayLine[i].ToString("f4") + "   ");

            //Shift delay lines 1 sample
            for (filterIndex = maxDelayLineLength - 2; filterIndex >= 0; filterIndex--)
            {
                PFilterDelayLine[filterIndex + 1] = PFilterDelayLine[filterIndex];
            }
            for (filterIndex = maxDelayLineLength - 2; filterIndex >= 0; filterIndex--)
            {
                VFilterDelayLine[filterIndex + 1] = VFilterDelayLine[filterIndex];
            }
            for (int i = 126; i >= 0; i--)
            {
                lpfPressureArray[i + 1] = lpfPressureArray[i];
                lpfPressureArrayTimeStamps[i + 1] = lpfPressureArrayTimeStamps[i];
            }

            sbe41LPF.timestamp = timeStamp;

            return (sbe41LPF);
        }

        public void clearRawPresureDL()
        {
            for (int i = 0; i < rawPressureDelayLineLength; i++)
                rawPressureDelayLine[i] = 0.0;
        }

        private double pressureThreshold = 0.3;
        private int numPressuresToTest = 3;
        public bool checkForJustNegative()
        {
            bool isJustNegative = false;

            for (int i = 0; i < numPressuresToTest; i++)
            {
                //TODO P1 This means 3X 2.1 values in a row won't pass the test.  Decide what to do about this possibility
                if (rawPressureDelayLine[i] > pressureThreshold)
                    isJustNegative = true;
                else
                {
                    isJustNegative = false;
                    break;
                }
            }
            return isJustNegative;
        }

        public enum commands : int
        {
            none = 0,
            crlf,
            ds,
            dc,
            startProfile,
            stopProfile,
            binaverage,
            dd,
            da,
            dah
        }

        public static bool waitingForResponse = false;
        private static bool gotResponse = false;
        private static readonly StringBuilder dsResponse = new StringBuilder("real-time output is P");
        private static readonly StringBuilder dcResponse = new StringBuilder("POFFSET");
        private static readonly StringBuilder crlfResponse = new StringBuilder("S>");
        private static readonly StringBuilder startProfileResponse = new StringBuilder("profile started");
        private static readonly StringBuilder stopProfileResponse = new StringBuilder("profile stopped");
        private static readonly StringBuilder ddResponse = new StringBuilder("upload complete");
        private static readonly StringBuilder daResponse = new StringBuilder("upload complete");
        private static readonly StringBuilder dahResponse = new StringBuilder("upload complete");
        private static readonly StringBuilder binaverageResponse = new StringBuilder("done");
        private static byte[] expectedResponse = new byte[64];
        private static int expectedResponseLength = 0;
        private static int numRetries = 0;

        private const int defaultMaxNumRetries = 3;

        private static TimeSpan wfrTimeout = new TimeSpan(0, 0, 40);
        private static readonly TimeSpan defaultWFRTimeout = new TimeSpan(0, 0, 40);

        private static TimeSpan wfrStartTime = new TimeSpan();

        public int waitForResponse(commands commandNum, int maxNumRetries = defaultMaxNumRetries, int newWFRTimeoutSeconds = 0)
        {
            if (waitingForResponse)
            //Do this after the first call to this method
            {
                if (gotResponse)
                //success
                {
                    waitingForResponse = false;
                    numRetries = 0;
                    //Debug.Print("Got SBE41 Response");
                    return (1);
                }
                else if ((Microsoft.SPOT.Hardware.Utility.GetMachineTime() - wfrStartTime) > wfrTimeout)
                //if timeout has expired, try again up to the max number of retries
                {
                    if (numRetries > maxNumRetries)
                    {
                        waitingForResponse = false;
                        numRetries = 0;
                        gotResponse = false;

                        EngrLogger.writeToColumns("[ERROR] exceeded number of retries in SBE41CTD");

                        return (-1);
                    }
                    else
                    {
                        waitingForResponse = false;
                        //sbTemp.Clear();
                        //sbTemp.Append("SBE41 waitingForResponse try number ");
                        //sbTemp.Append(numRetries);
                        return (0);
                    }
                }
                else
                    return (0);
            }
            else
            //Do this the first time the method is called and after a timeout
            {
                if (maxNumRetries != defaultMaxNumRetries)
                {
                    sbTemp.Clear();
                    sbTemp.Append("Set new CTD WFR max retries to: ");
                    sbTemp.Append(maxNumRetries);
                    EngrLogger.writeToColumns(sbTemp);
                }

                if (newWFRTimeoutSeconds == 0)
                    wfrTimeout = defaultWFRTimeout;
                else
                {
                    wfrTimeout = new TimeSpan(0, 0, newWFRTimeoutSeconds);
                    sbTemp.Clear();
                    sbTemp.Append("Set new CTD WFR timeout to: ");
                    sbTemp.Append(wfrTimeout.ToString());
                    EngrLogger.writeToColumns(sbTemp);
                }

                Array.Clear(expectedResponse, 0, expectedResponse.Length);
                switch (commandNum)
                {
                    case commands.ds:
                        Array.Copy(UTF8Encoding.UTF8.GetBytes(dsResponse.ToString()), expectedResponse, dsResponse.Length);
                        expectedResponseLength = dsResponse.Length;
                        //Debug.Print(DateTime.Now.TimeOfDay.ToString() + " Sending ds command");
                        sendDS();
                        break;
                    case commands.dc:
                        Array.Copy(UTF8Encoding.UTF8.GetBytes(dcResponse.ToString()), expectedResponse, dcResponse.Length);
                        expectedResponseLength = dcResponse.Length;
                        //Debug.Print(DateTime.Now.TimeOfDay.ToString() + " Sending ds command");
                        sendDC();
                        break;
                    case commands.crlf:
                        Array.Copy(UTF8Encoding.UTF8.GetBytes(crlfResponse.ToString()), expectedResponse, crlfResponse.Length);
                        expectedResponseLength = crlfResponse.Length;
                        //Debug.Print(DateTime.Now.TimeOfDay.ToString() + " Sending SBE41 CR/LF");
                        sendCRLF();
                        break;
                    case commands.startProfile:
                        Array.Copy(UTF8Encoding.UTF8.GetBytes(startProfileResponse.ToString()), expectedResponse, startProfileResponse.Length);
                        expectedResponseLength = startProfileResponse.Length;
                        //Debug.Print(DateTime.Now.TimeOfDay.ToString() + " Sending SBE41 startProfile");
                        startProfile();
                        break;
                    case commands.stopProfile:
                        Array.Copy(UTF8Encoding.UTF8.GetBytes(stopProfileResponse.ToString()), expectedResponse, stopProfileResponse.Length);
                        expectedResponseLength = stopProfileResponse.Length;
                        //Debug.Print(DateTime.Now.TimeOfDay.ToString() + " Sending SBE41 stopProfile");
                        sendStopProfile();
                        break;
                    case commands.binaverage:
                        Array.Copy(UTF8Encoding.UTF8.GetBytes(binaverageResponse.ToString()), expectedResponse, binaverageResponse.Length);
                        expectedResponseLength = binaverageResponse.Length;
                        //Debug.Print(DateTime.Now.TimeOfDay.ToString() + " Sending SBE41 binaverage");
                        sendBinAverage();
                        break;
                    case commands.dd:
                        Array.Copy(UTF8Encoding.UTF8.GetBytes(ddResponse.ToString()), expectedResponse, ddResponse.Length);
                        expectedResponseLength = ddResponse.Length;
                        //Debug.Print(DateTime.Now.TimeOfDay.ToString() + " Sending SBE41 dd command");
                        dumpData();
                        break;
                    case commands.da:
                        Array.Copy(UTF8Encoding.UTF8.GetBytes(daResponse.ToString()), expectedResponse, daResponse.Length);
                        expectedResponseLength = daResponse.Length;
                        //Debug.Print(DateTime.Now.TimeOfDay.ToString() + " Sending SBE41 da command");
                        dumpBinnedData();
                        break;
                    case commands.dah:
                        Array.Copy(UTF8Encoding.UTF8.GetBytes(dahResponse.ToString()), expectedResponse, dahResponse.Length);
                        expectedResponseLength = dahResponse.Length;
                        //Debug.Print(DateTime.Now.TimeOfDay.ToString() + " Sending SBE41 dah command");
                        dumpBinnedDataHex();
                        break;
                }
                wfrStartTime = Microsoft.SPOT.Hardware.Utility.GetMachineTime();
                numRetries = numRetries + 1;
                gotResponse = false;
                waitingForResponse = true;
                return (0);
            }
        }

        public void checkResponse(byte[] response)
        {
            int msgLength = response.Length;

            //check for expectedResponse anywhere in the SBE41 response message
            if (waitingForResponse)
            {
                if (msgLength > expectedResponseLength)
                {
                    //start at the beginning of the qStructure byte array and test for the right character sequence
                    //then start at the 2nd character in the qStrucutre byte array and so on
                    //sbTemp.Clear();
                    //sbTemp.Append("Looking for SBE 41: ");
                    //sbTemp.Append(SafeEncoding.GetChars(expectedResponse));
                    //sbTemp.Append(" got: ");
                    //sbTemp.Append(SafeEncoding.GetChars(response, 0, Array.IndexOf(response, 0)));
                    //EngrLogger.writeToColumns(sbTemp);
                    if (GHI.Utilities.Arrays.Contains(response, 0, expectedResponse, 0, expectedResponseLength) >= 0)
                    {
                        gotResponse = true;
                        //Debug.Print("Correct response received");
                    }
                    //for (int j = 0; j < msgLength - expectedResponseLength; j++)
                    //{
                    //    for (int k = 0; k < expectedResponseLength; k++)
                    //    {
                    //        if (response[j + k] != expectedResponse[k])
                    //        {
                    //            gotResponse = false;
                    //            break;
                    //        }
                    //        else
                    //            gotResponse = true;
                    //    }
                    //    if (gotResponse)
                    //        break;
                    //}
                }
            }
        }

        public static double maxPressureThisProfile = double.NaN;
        private void checkMaxPressureThisProfile(double currentPressure)
        {
            if ((SV.CurrentState == CPFStates.anchor) || (SV.CurrentState == CPFStates.ascend) ||
                 (SV.CurrentState == CPFStates.deAnchor) || (SV.CurrentState == CPFStates.descend) ||
                 (SV.CurrentState == CPFStates.dumpCPData) || (SV.CurrentState == CPFStates.park) ||
                 (SV.CurrentState == CPFStates.startCP))
            {
                if (currentPressure > maxPressureThisProfile)
                    maxPressureThisProfile = currentPressure;
            }
        }

        public int getStuckPressureCount()
        {
            return (stuckPressureCount);
        }

        //Check for too many sequential identical pressure values in a given time period
        static private int stuckPressureCount = 0;
        private const int stuckPressureLimit = 20;
        public int powerCycleCount { get; set; }
        private static double lastPressure = double.MinValue;
        private bool isPressureStuck(double latestPressure)
        {
            //ctd.SBE41LPF.Pressure is initialized as double.NaN, so if SBE41 hasn't given a pressure yet, inPressure
            //will come in as double.NaN. Here we change double.NaN to (double)0 so logical evaluations conducted
            //below will not produce eroneous results (for example: 17.5 == double.NaN evaluates true).

            if (double.IsNaN(latestPressure))
                latestPressure = 0.0;

            if (latestPressure == lastPressure)
            {
                stuckPressureCount = stuckPressureCount + 1;
                sbTemp.Clear();
                sbTemp.Append("[ERROR]: stuck pressure count = ");
                sbTemp.Append(stuckPressureCount.ToString());
                sbTemp.Append("   power cycle count = ");
                sbTemp.Append(powerCycleCount.ToString());
                EngrLogger.Comment(sbTemp.ToString());
            }
            else
                stuckPressureCount = 0;

            lastPressure = latestPressure;

            if (stuckPressureCount > stuckPressureLimit)
            {
                EngrLogger.Comment("[ERROR]: Stuck pressure limit exceeded, power cycling CTD");
                powerCycleSBE41();
                init();
                powerCycleCount++;
                stuckPressureCount = 0;
                return true;
            }
            else
                return false;
        }

        private static readonly StringBuilder SBE41Header = new StringBuilder("SBE41 Message");
        private static readonly StringBuilder unParsedSBE41Header = new StringBuilder("Unparsed SBE41 Message");
        private static readonly StringBuilder parsingCPDataHeader = new StringBuilder("CP Message");
        private static readonly StringBuilder velPIDHeader = new StringBuilder("Vel PID Values");

        public LPFResults lpfResults = new LPFResults();
        private static BuoyancyEngine.PIDLogValues pidLogValues;

        private PTSRecord returnedPTSRecord = new PTSRecord();

        private const int rawPressureDelayLineLength = 32;
        private static double[] rawPressureDelayLine = new double[rawPressureDelayLineLength];

        public static bool runAscendPVPID = true;

        private static readonly byte[] fpResponseBytes = { 83, 62, 102, 112 }; // S>fp
        private static readonly byte[] sbe41ResponseBytes = { 83, 66, 69, 32, 52, 49 }; // SBE 41
        public void processMessage(QRecord qRecord)
        {
            int byteArrayLength = 0;

            bool pressureIsStuck = false;

            double parsedPressure = double.NaN;

            //error in SBE41 IO
            if (qRecord.errorCode < 0)
            {
                sbTemp.Clear();
                sbTemp.Append("[ERROR] in SBE41 Message: ");
                EngrLogger.writeToColumns(EngrLiterals.ColumnNums.dateTime, qRecord.timeStamp,
                    EngrLiterals.ColumnNums.state, qRecord.state,
                    EngrLiterals.ColumnNums.comment, sbTemp,
                    EngrLiterals.ColumnNums.CTDSalinity, qRecord.byteArray);
                return;
            }

            //Log the message
            EngrLogger.writeToColumns(0, EngrLiterals.ColumnNums.dateTime, qRecord.timeStamp,
                                          EngrLiterals.ColumnNums.state, qRecord.state,
                                          EngrLiterals.ColumnNums.comment, unParsedSBE41Header,
                                          EngrLiterals.ColumnNums.CTDSalinity, qRecord.byteArray);

            //Check for expected response
            if (SBE41CTD.waitingForResponse)
                checkResponse(qRecord.byteArray);

            //Ignore messages starting with "S>fp" or "SBE 41"
            if (Utils.CompareArray(qRecord.byteArray, 0, fpResponseBytes, 0, fpResponseBytes.Length))
            {
                return;
            }

            if (Utils.CompareArray(qRecord.byteArray, 0, sbe41ResponseBytes, 0, sbe41ResponseBytes.Length))
            {
                return;
            }

            if (SV.CurrentState == CPFStates.dumpCPData)
            {
                checkForBinaverageData(qRecord.byteArray);
                checkForCPData(qRecord.byteArray);
                EngrLogger.writeToColumns(EngrLiterals.ColumnNums.dateTime, qRecord.timeStamp,
                      EngrLiterals.ColumnNums.state, qRecord.state,
                      EngrLiterals.ColumnNums.comment, parsingCPDataHeader,
                      EngrLiterals.ColumnNums.CTDSalinity, qRecord.byteArray);
                return;
            }
            byteArrayLength = Array.IndexOf(qRecord.byteArray, 0);

            //Parse message, only fp and CP mode PTS responses will return a value, all other messages should return .NaN
            //returnedPTSRecord = parse(qRecord.byteArray);             //Replaced parse with parse2 to make building sciFile easier
            returnedPTSRecord = parse2(qRecord.byteArray);

            parsedPressure = returnedPTSRecord.pressure;

            pressureIsStuck = checkPressureIsStuck(parsedPressure);

            if ((parsedPressure > -5) && (parsedPressure < 5000) && !pressureIsStuck)
            {
                sbe41LPF.RawPressure = parsedPressure;

                SV.PressureTimeStamp = DateTime.Now;    //TODO P3 this really need to be the time stamp in the qRecord.timeStamp;
                SV.Pressure = returnedPTSRecord.pressure;

                checkMaxPressureThisProfile(parsedPressure);        //TODO P1 can we go into recovery mode right away if this is true

                for (int i = rawPressureDelayLineLength - 2; i >= 0; i--)
                    rawPressureDelayLine[i + 1] = rawPressureDelayLine[i];
                rawPressureDelayLine[0] = parsedPressure;


                lpfResults = LPFPressure(sbe41LPF.RawPressure, qRecord.timeStamp.Ticks / 10000000.0, samplePeriod, false);

                //TODO P2 How the PID loop gets called really needs more thought
                //TODO P2 the log values probably should get logged in PIDPlatformVelocity
                //TODO P2 the PID call to engrLogger should get stamped with the time the PID loop output data to the elmo.
                
                
                //This seems to be what I left when I stripped out the Microrider ascend glide code.  It seems increibly wrong like it only runs the PID loop in the ascend mode.
                //if (SV.RunPVPID)
                //{
                //    if (SV.CurrentState == CPFStates.ascend)
                //    {
                //        if (runAscendPVPID)     //Run PVPID if the pressure limit has not been tripped or if the PV limit is tripped after the pressure limit is tripped
                //        {
                //            pidLogValues = buoyancyEngine.runPvPID(SV.PVSetPoint, lpfResults.DiffVelocity);
                //            if (MissionConfig.EngrLoggerVerbosityLevel > 2)
                //                logPidMessage(qRecord);
                //        }
                //    }
                //}

                //This is a cut and paste from the pre-Microrider code
                if (SV.RunPVPID)
                {
                    pidLogValues = buoyancyEngine.runPvPID(SV.PVSetPoint, lpfResults.DiffVelocity);
                    if (MissionConfig.EngrLoggerVerbosityLevel > 2)
                        logPidMessage(qRecord);
                }

                if (MissionConfig.EngrLoggerVerbosityLevel > 2)
                    logSystemInfo(qRecord);
            }
        }


        private bool checkPressureIsStuck(double parsedPressure)
        {
            if (SV.CurrentState == CPFStates.ABDecBuocyFast || SV.CurrentState == CPFStates.ABDecBuocySlow
                                                            || SV.CurrentState == CPFStates.anchor ||
                                                            SV.CurrentState == CPFStates.ascend
                                                            || SV.CurrentState == CPFStates.deAnchor ||
                                                            SV.CurrentState == CPFStates.descend
                                                            || SV.CurrentState == CPFStates.park ||
                                                            SV.CurrentState == CPFStates.preMissionDelay
                                                            || SV.CurrentState == CPFStates.recovery
                                                            || SV.CurrentState == CPFStates.surfaceOps ||
                                                            SV.CurrentState == CPFStates.surfaceOpsSetBellows)
            {
                return isPressureStuck(parsedPressure);
            }
            return false;

        }

        private void logSystemInfo(QRecord qRecord)
        {
            EngrLogger.writeToColumns(EngrLiterals.ColumnNums.dateTime, qRecord.timeStamp,
                EngrLiterals.ColumnNums.state, qRecord.state,
                EngrLiterals.ColumnNums.comment, SBE41Header,
                EngrLiterals.ColumnNums.pressure, SV.Pressure,
                EngrLiterals.ColumnNums.bellowsPosition, SV.BellowsPosition,
                EngrLiterals.ColumnNums.pumpVolts, SV.busVoltage,
                EngrLiterals.ColumnNums.pumpMilliamps, SV.busAmps,
                EngrLiterals.ColumnNums.CTDSalinity, qRecord.byteArray,
                EngrLiterals.ColumnNums.SBE41Pressure, lpfResults.RawPressure,
                EngrLiterals.ColumnNums.SBE41LPFPressure, lpfResults.LPFPressure,
                EngrLiterals.ColumnNums.SBE41LPFVelocity, lpfResults.LPFVelocity,
                EngrLiterals.ColumnNums.SBE41DiffVelocity, lpfResults.DiffVelocity,
                EngrLiterals.ColumnNums.SBE41Diff2Velocity, lpfResults.Diff2Velocity,
                EngrLiterals.ColumnNums.SBE41Slope, lpfResults.slope,
                EngrLiterals.ColumnNums.CTDSamplePeriod,
                samplePeriod); //TODO P2 Should do this in a way that the message is time stamped at the source
        }

        private static void logPidMessage(QRecord qRecord)
        {
            EngrLogger.writeToColumns(EngrLiterals.ColumnNums.dateTime, pidLogValues.timeStamp,
                EngrLiterals.ColumnNums.state, qRecord.state,
                EngrLiterals.ColumnNums.comment, velPIDHeader,
                EngrLiterals.ColumnNums.pressure, SV.Pressure,
                EngrLiterals.ColumnNums.bellowsPosition, SV.BellowsPosition,
                EngrLiterals.ColumnNums.pumpVolts, SV.busVoltage,
                EngrLiterals.ColumnNums.pumpMilliamps, SV.busAmps,
                EngrLiterals.ColumnNums.PID_SP, pidLogValues.setPoint,
                EngrLiterals.ColumnNums.PID_PV, pidLogValues.PV,
                EngrLiterals.ColumnNums.PID_y1, pidLogValues.y1,
                EngrLiterals.ColumnNums.PID_y2, pidLogValues.y2,
                EngrLiterals.ColumnNums.PID_I, pidLogValues.I,
                EngrLiterals.ColumnNums.PID_v, pidLogValues.v,
                EngrLiterals.ColumnNums.PID_u, pidLogValues.u,
                EngrLiterals.ColumnNums.PID_uCPS, pidLogValues.uCPS,
                EngrLiterals.ColumnNums.PID_h,
                pidLogValues.h); //TODO P2 Should do this in a way that the message is time stamped at the source
        }
    }
}
