using System; using System.IO; using System.Text; using Microsoft.SPOT; using SWModules; using System.Threading; namespace HWModules { public static class SciLogger { private static CTD1 ctd1; private static FLBB1 flbb1; private static MSC1 msc1; private static Optode1 optode1; private static Radiometer1 radiometer1; public static event SendLineDelegate SendConsoleLine; private static FileQueue _fileQ; private static QRecord _qRecord; private static FileStream msgFile; private static StringBuilder msgFileName = new StringBuilder(64); private static FileStream logFile; private static StringBuilder logFileName = new StringBuilder(64); public static StringBuilder CurrentState { get; set; } private static byte[] colNames = UTF8Encoding.UTF8.GetBytes("$ Comment\tDate Time\tpres (dBar)\ttemp (degC)\tsal (ppt)\tTopt (degC)\tTPhase (deg)\tRPhase (deg)\tpH (V)\tNO3 (V)\tFSig (counts)\tBbSig (counts)\tTSig (counts)\tOCR Ch1 (uW/cm2/nm)\tOCR Ch2\tOCR Ch3\tOCR Ch4\r\n"); private static StringBuilder sbTemp = new StringBuilder(2056); private static StringBuilder sbTemp2 = new StringBuilder(2056); private static readonly byte[] CRLF = new byte[] { 13, 10 }; private const byte tab = (byte)Literals.tabChar; private static StringBuilder gpsPositionSB = new StringBuilder("Lon Lat numSats gpsFixTime TTFF:\t no fix"); //private static bool msgFileOpen = false; public static void openNewMsgFile() { if (ctd1 == null) { ctd1 = CTD1.Instance; flbb1 = FLBB1.Instance; msc1 = MSC1.Instance; optode1 = Optode1.Instance; radiometer1 = Radiometer1.Instance; _fileQ = new FileQueue(); } closeMsgFile(); msgFileFooter.Clear(); msgFileFooter.Append("$ Profile details\r\n"); msgFileFooter.Append("$ Date Time\tComment\tPres (dBar)\tBel Pos (mm)\tBus Volt\tBus Cur (mA)\r\n"); cpData.Clear(); cpData.Append("$ CP Data\r\n"); msgFileName.Clear(); msgFileName.Append(EngrLogger.fileNameBase); msgFileName.Append(".msg"); try { msgFile = new FileStream(msgFileName.ToString(), FileMode.OpenOrCreate, FileAccess.Write); if (!msgFileOpen) throw new Exception("File Open Error"); } catch { EngrLogger.writeToColumns("Error opening .msg File"); SV.ForceRecoveryMode = true; } } /// /// Closes the Science file, and enqueues it for transmission. /// public static void closeMsgFile() { if (!msgFileOpen) return; msgFile.Write(CRLF, 0, CRLF.Length); msgFile.Write(Encoding.UTF8.GetBytes(gpsPositionSB.ToString()), 0, gpsPositionSB.Length); msgFile.Write(CRLF, 0, CRLF.Length); writeMsgFooter(); EngrLogger.writeToColumns("Closing .msg File"); msgFile.Close(); } private static bool logFileOpen { get { if (logFile == null) return false; return logFile.CanWrite; } } private static bool msgFileOpen { get { if (msgFile == null) return false; return msgFile.CanWrite; } } public static void openNewLogFile() { if (logFileOpen) closeLogFile(); logFileName.Clear(); logFileName.Append(EngrLogger.fileNameBase); logFileName.Append(".log"); try { logFile = new FileStream(logFileName.ToString(), FileMode.OpenOrCreate, FileAccess.Write); if (!logFileOpen) { sbTemp.Clear(); sbTemp.Append("Error opening log file"); EngrLogger.writeToColumns(sbTemp); } } catch { EngrLogger.writeToColumns("Error opening .log File"); } } public static void closeLogFile() { if (logFile == null) return; if (!logFileOpen) return; EngrLogger.writeToColumns("Closing .log File"); logFile.Close(); } /// /// Allow other objects to add lines to SciFile Footer /// private static StringBuilder msgFileFooter = new StringBuilder(1024); public static void appendToMsgFooter(StringBuilder newLine) { msgFileFooter.Append(newLine); msgFileFooter.Append("\r\n"); } /// /// Write SciFile footer /// private static void writeMsgFooter() { EngrLogger.writeToColumns("Writing Footer to msg File"); msgFile.Write(Encoding.UTF8.GetBytes(msgFileFooter.ToString()), 0, msgFileFooter.Length); msgFile.Flush(); } /// /// Allow other objects to add lines to SciFile Header /// public static void appendToMsgHeader(StringBuilder newLine) { msgFileHeader.Append("$ "); msgFileHeader.Append(newLine); msgFileHeader.Append(Literals.crlfString); } private static StringBuilder msgFileHeader = new StringBuilder(1024); /// /// Write out the header to file /// public static void writeMsgHeader() { EngrLogger.writeToColumns("Writing Header to msg File"); msgFile.Write(SafeEncoding.UTF8.GetBytes(msgFileHeader.ToString()), 0, msgFileHeader.Length); msgFile.Write(colNames, 0, colNames.Length); msgFile.Flush(); } /// /// Append the cp data /// /// byte array containing the segment of CP data to be copied into the file. public static void appendCPData(byte[] inBytes) { sbTemp.Clear(); sbTemp.Append(SafeEncoding.GetChars(inBytes)); cpData.Append(sbTemp); sbTemp.Clear(); sbTemp.Append("Appending CP Data: "); sbTemp.Append(SafeEncoding.GetChars(inBytes)); EngrLogger.writeToColumns(sbTemp.Replace(Literals.tabChar, Literals.commaChar)); } private static StringBuilder cpData = new StringBuilder(2048); /// /// Write out all stored CP data to the file. /// public static void writeCPData() { EngrLogger.writeToColumns("Writing CP data to msg File"); appendCPData(UTF8Encoding.UTF8.GetBytes("$ End of CP data\r\n")); msgFile.Write(UTF8Encoding.UTF8.GetBytes(cpData.ToString()), 0, cpData.Length); msgFile.Flush(); } /// /// Sample types for byte array storage and output /// [Flags] public enum Instruments : uint { Ctd = 0x0001, Optode = 0x0002, Ph = 0x0004, NO3 = 0x0008, Flbb = 0x0010, Radiometer = 0x0020, Reserved1 = 0x0040, Reserved2 = 0x0080, Reserved3 = 0x0100, Reserved4 = 0x0200, Reserved5 = 0x0400, Reserved6 = 0x0800, Reserved8 = 0x1000, Reserved9 = 0x2000, Reserved10 = 0x4000, Reserved11 = 0x8000, AllInstruments = 0x3F, // modify when adding new instrument None = 0x0000, } /// /// Determine if an instrument should be sampled. /// /// /// /// public static bool sampling(Instruments instr) { return (instr & instrumentsToSample) > 0; } public static bool posted(Instruments instr) { return (instr & instrumentsPosted) > 0; } private static bool shouldPost(Instruments instr) { return ((instr & instrumentsToSample) > 0) && !posted(instr); } private static StringBuilder ctdSB = new StringBuilder(32); private static StringBuilder optodeSB = new StringBuilder(64); private static StringBuilder phSB = new StringBuilder(16); private static StringBuilder no3SB = new StringBuilder(16); private static StringBuilder flbbSB = new StringBuilder(16); private static StringBuilder radiometerSB = new StringBuilder(16); private static StringBuilder startStateSB = new StringBuilder(32); private static Instruments instrumentsToSample; private static Instruments instrumentsPosted; //private static bool gotCTD = false; //private static bool gotOptode = false; //private static bool gotPH = false; //private static bool gotNO3 = false; //private static bool gotFLBB = false; //private static bool gotRadiometer = false; //public static bool getCTD = false; //public static bool getOptode = false; //public static bool getPH = false; public static bool waitForCTDThenGetPH = false; //public static bool getNO3 = false; public static bool waitForCTDThenGetNO3 = false; //public static bool getFLBB = false; //public static bool getRadiometer = false; private static int missedCTD = 0; private static int missedOptode = 0; private static int missedPH = 0; private static int missedNO3 = 0; private static int missedFLBB = 0; private static int missedOCR = 0; //public static bool sampleInProgress = false; private static StringBuilder sampleStartTimeStamp = new StringBuilder(32); /// /// /// //TODO P2 need to make sure SciLogger is not active in recovery mode public static void startNewSampleCycle(Instruments instrumentsList) { if ((SV.CurrentState != CPFStates.anchor) && (SV.CurrentState != CPFStates.park) && (SV.CurrentState != CPFStates.ascend) && (SV.CurrentState != CPFStates.surfaceOps)) return; if (instrumentsPosted > 0) { EngrLogger.writeToColumns("SciLogger is writing previous sample before starting new one"); writeSample(); } instrumentsToSample = instrumentsList; instrumentsPosted = Instruments.None; EngrLogger.writeToColumns("Starting New SciLogger Sample Cycle"); startStateSB.Clear(); if (SV.CurrentState == CPFStates.anchor) startStateSB.Append("Anchor pt: "); else if (SV.CurrentState == CPFStates.park) startStateSB.Append("Park pt: "); else if (SV.CurrentState == CPFStates.surfaceOps) startStateSB.Append("Surface pt: "); else if (SV.CurrentState == CPFStates.ascend) startStateSB.Append("Ascend pt: "); else if (SV.CurrentState == CPFStates.descend) startStateSB.Append("Descend pt: "); else startStateSB.Append("Other state: "); if (sampling(Instruments.Ctd)) { ctdSB.Clear(); ctdSB.Append("NaN\tNaN\tNaN"); //Don't take a PTS sample in ascend mode because SBE41 is in CP mode and already outputting a PTS sample if (!SV.InCPMode) { ctd1.sendCRLF(); Thread.Sleep(1000); if (SV.CurrentState == CPFStates.surfaceOps) { EngrLogger.writeToColumns("Sci Logger sending FastPressure to CTD"); ctd1.sendFastPressure(); } else { EngrLogger.writeToColumns("Sci Logger sending PTS to CTD"); ctd1.sendPTS(); } } } if (sampling(Instruments.Optode)) { optodeSB.Clear(); optodeSB.Append("NaN\tNaN\tNaN"); EngrLogger.writeToColumns("Sci Logger sending sample Optode"); optode1.doSample(); } //TODO P2 need to create cases for pH only and NO3 only if (sampling(Instruments.Ph) || sampling(Instruments.NO3)) { phSB.Clear(); phSB.Append("NaN"); no3SB.Clear(); no3SB.Append("NaN"); waitForCTDThenGetPH = true; waitForCTDThenGetNO3 = true; EngrLogger.writeToColumns("Sci Logger set wait for CTD then sample pH and NO3 true"); } else { waitForCTDThenGetPH = false; waitForCTDThenGetNO3 = false; } if (sampling(Instruments.Flbb)) { flbbSB.Clear(); flbbSB.Append("NaN\tNaN\tNaN"); EngrLogger.writeToColumns("Sci Logger sending sample FLBB"); flbb1.sendRunCmd(); } if (sampling(Instruments.Radiometer)) { radiometerSB.Clear(); radiometerSB.Append("NaN\tNaN\tNaN\tNaN"); EngrLogger.writeToColumns("Sci Logger sending sample Radiometer"); radiometer1.sendPollSample(); } sampleStartTimeStamp.Clear(); sampleStartTimeStamp.Append(DateTime.Now.ToString("yyyy-MMM-ddTHH:mm:ssZ")); } public static void endSampleCycle() //TODO P1 Check if writeSample does exactly what this does only more correctly { EngrLogger.writeToColumns("Ending SciLogger Sample Cycle"); if (instrumentsToSample > 0) { EngrLogger.writeToColumns("SciLogger endSampleCycle is writing samples"); writeSample(); } instrumentsToSample = Instruments.None; instrumentsPosted = Instruments.None; waitForCTDThenGetPH = false; waitForCTDThenGetNO3 = false; } /// /// Write key science data to file. /// public static void writeSample() { if ((SV.CurrentState != CPFStates.anchor) && (SV.CurrentState != CPFStates.park) && (SV.CurrentState != CPFStates.surfaceOps) && (SV.CurrentState != CPFStates.ascend) && (SV.CurrentState != CPFStates.descend)) return; sbTemp.Clear(); sbTemp.Append(startStateSB).Append(Literals.tabChar); sbTemp.Append(sampleStartTimeStamp).Append(Literals.tabChar); sbTemp.Append(ctdSB).Append(Literals.tabChar); sbTemp.Append(optodeSB).Append(Literals.tabChar); sbTemp.Append(phSB).Append(Literals.tabChar); sbTemp.Append(no3SB).Append(Literals.tabChar); sbTemp.Append(flbbSB).Append(Literals.tabChar); sbTemp.Append(radiometerSB); sbTemp.Append(Literals.crlfString); msgFile.Write(Encoding.UTF8.GetBytes(sbTemp.ToString()), 0, sbTemp.Length); msgFile.Flush(); //Write out to Engrlogger sbTemp2.Clear(); sbTemp2.Append(sbTemp); sbTemp.Clear(); sbTemp.Append("Wrote to msg file: "); sbTemp.Append(sbTemp2); EngrLogger.writeToColumns(sbTemp.Replace(Literals.tabChar, Literals.commaChar)); //TODO P2 Eventually need to do something when missedXXX exceeds some value //Really need to do this inside a well executed commmand processor //object instantiated in each instrument if (sampling(Instruments.Ctd) && !posted(Instruments.Ctd)) { missedCTD++; sbTemp.Clear(); sbTemp.Append("ERROR: Missed CTD message "); sbTemp.Append(missedCTD); EngrLogger.writeToColumns(sbTemp); } if (sampling(Instruments.Optode) && !posted(Instruments.Optode)) { missedOptode++; sbTemp.Clear(); sbTemp.Append("ERROR: Missed Optode message "); sbTemp.Append(missedOptode); EngrLogger.writeToColumns(sbTemp); } if (sampling(Instruments.Ph) && !posted(Instruments.Ph)) { missedPH++; sbTemp.Clear(); sbTemp.Append("ERROR: Missed PH message "); sbTemp.Append(missedPH); EngrLogger.writeToColumns(sbTemp); } if (sampling(Instruments.NO3) && !posted(Instruments.NO3)) { missedNO3++; sbTemp.Clear(); sbTemp.Append("ERROR: Missed NO3 message "); sbTemp.Append(missedNO3); EngrLogger.writeToColumns(sbTemp); } if (sampling(Instruments.Flbb) && !posted(Instruments.Flbb)) { missedFLBB++; sbTemp.Clear(); sbTemp.Append("ERROR: Missed FLBB message "); sbTemp.Append(missedFLBB); EngrLogger.writeToColumns(sbTemp); } if (sampling(Instruments.Radiometer) && !posted(Instruments.Radiometer)) { missedOCR++; sbTemp.Clear(); sbTemp.Append("ERROR: Missed OCR message "); sbTemp.Append(missedOCR); EngrLogger.writeToColumns(sbTemp); } instrumentsToSample = Instruments.None; instrumentsPosted = Instruments.None; } private static float postedCTDPressure = float.MaxValue; private static float postedCTDTemperature = float.MaxValue; private static float postedCTDSalinity = float.MaxValue; private static DateTime postedCTDTimestamp = new DateTime(2000, 1, 1); public static void setCTDValues(CTD1.PTSRecord ptsRecord) { postedCTDPressure = ptsRecord.pressure; postedCTDTemperature = ptsRecord.temperature; postedCTDSalinity = ptsRecord.salinity; postedCTDTimestamp = ptsRecord.timeStamp; } private static byte[] returnBytes = new byte[32]; public static void postSample(Instruments sample, byte[] sampleBytes) { switch (sample) { case Instruments.Ctd: #region format CTD message if (waitForCTDThenGetNO3) { EngrLogger.writeToColumns("SciLogger: Setting get NO3 true"); waitForCTDThenGetNO3 = false; } if (waitForCTDThenGetPH) { EngrLogger.writeToColumns("SciLogger: Setting get PH true"); waitForCTDThenGetPH = false; } if (sampling(Instruments.Ph) || sampling(Instruments.NO3)) { EngrLogger.writeToColumns("SciLogger: Setting MSC CTD values"); msc1.SetCTDValues(Utils.dateTimeToPosixTime(postedCTDTimestamp), postedCTDTemperature, postedCTDSalinity, postedCTDPressure); EngrLogger.writeToColumns("SciLogger: Starting PHandNO3 sequence"); msc1.startPHandNO3SampleSequence(); msc1.SequencePHandNO3Samples(); } ctdSB.Clear(); ctdSB.Append(UTF8Encoding.UTF8.GetChars(sampleBytes)); instrumentsPosted |= Instruments.Ctd; break; #endregion case Instruments.Flbb: #region format FLBB message flbbSB.Clear(); sbTemp.Clear(); sbTemp.Append(Encoding.UTF8.GetChars(sampleBytes)); //Debug.Print("FLBB post: " + sbTemp.ToString()); //Get FL signal try { Array.Clear(returnBytes, 0, returnBytes.Length); Utils.getField(sampleBytes, 44, 3, ref returnBytes); flbbSB.Append(Encoding.UTF8.GetChars(returnBytes)); instrumentsPosted |= Instruments.Flbb; } catch { EngrLogger.writeToColumns("Error parsing flbb Topt"); } flbbSB.Append(Literals.tabChar); //Get BB signal try { Array.Clear(returnBytes, 0, returnBytes.Length); Utils.getField(sampleBytes, 44, 5, ref returnBytes); flbbSB.Append(UTF8Encoding.UTF8.GetChars(returnBytes)); } catch { EngrLogger.writeToColumns("Error parsing flbb TCPhase"); } flbbSB.Append(Literals.tabChar); //Get Temp signal try { Array.Clear(returnBytes, 0, returnBytes.Length); Utils.getField(sampleBytes, 44, 6, ref returnBytes); flbbSB.Append(UTF8Encoding.UTF8.GetChars(returnBytes)); } catch { EngrLogger.writeToColumns("Error parsing flbb TCPhase"); } //Debug.Print("new flbbSB: " + flbbSB.ToString()); break; #endregion case Instruments.NO3: #region format NO3 message no3SB.Clear(); no3SB.Append(UTF8Encoding.UTF8.GetChars(sampleBytes)); instrumentsPosted |= Instruments.NO3; break; #endregion case Instruments.Radiometer: #region format OCR message radiometerSB.Clear(); sbTemp.Clear(); sbTemp.Append(UTF8Encoding.UTF8.GetChars(sampleBytes)); //Debug.Print("ocr post: " + sbTemp.ToString()); //Get CH1 signal try { Array.Clear(returnBytes, 0, returnBytes.Length); Utils.getField(sampleBytes, 44, 1, ref returnBytes); radiometerSB.Append(UTF8Encoding.UTF8.GetChars(returnBytes)); instrumentsPosted |= Instruments.Radiometer; } catch { EngrLogger.writeToColumns("Error parsing ocr Ch1"); } radiometerSB.Append(Literals.tabChar); //Get CH2 signal try { Array.Clear(returnBytes, 0, returnBytes.Length); Utils.getField(sampleBytes, 44, 2, ref returnBytes); radiometerSB.Append(UTF8Encoding.UTF8.GetChars(returnBytes)); } catch { EngrLogger.writeToColumns("Error parsing ocr Ch2"); } radiometerSB.Append(Literals.tabChar); //Get CH3 signal try { Array.Clear(returnBytes, 0, returnBytes.Length); Utils.getField(sampleBytes, 44, 3, ref returnBytes); radiometerSB.Append(UTF8Encoding.UTF8.GetChars(returnBytes)); } catch { EngrLogger.writeToColumns("Error parsing ocr Ch3"); } radiometerSB.Append(Literals.tabChar); //Get CH4 signal try { Array.Clear(returnBytes, 0, returnBytes.Length); Utils.getField(sampleBytes, 44, 4, ref returnBytes); radiometerSB.Append(UTF8Encoding.UTF8.GetChars(returnBytes)); } catch { EngrLogger.writeToColumns("Error parsing ocr Ch4"); } //Debug.Print("new ocrSB: " + ocrSB.ToString()); break; #endregion case Instruments.Optode: #region format Optode message optodeSB.Clear(); sbTemp.Clear(); sbTemp.Append(UTF8Encoding.UTF8.GetChars(sampleBytes)); //Debug.Print("Optode post: " + sbTemp.ToString()); //Get Topt try { Array.Clear(returnBytes, 0, returnBytes.Length); Utils.getField(sampleBytes, 44, 3, ref returnBytes); optodeSB.Append(UTF8Encoding.UTF8.GetChars(returnBytes)); instrumentsPosted |= Instruments.Optode; } catch { EngrLogger.writeToColumns("Error parsing optode Topt"); } optodeSB.Append(Literals.tabChar); //Get TCPhase try { Array.Clear(returnBytes, 0, returnBytes.Length); Utils.getField(sampleBytes, 44, 5, ref returnBytes); optodeSB.Append(UTF8Encoding.UTF8.GetChars(returnBytes)); } catch { EngrLogger.writeToColumns("Error parsing optode TCPhase"); } optodeSB.Append(Literals.tabChar); //Get C2RPhase try { Array.Clear(returnBytes, 0, returnBytes.Length); Utils.getField(sampleBytes, 44, 7, ref returnBytes); optodeSB.Append(UTF8Encoding.UTF8.GetChars(returnBytes)); } catch { EngrLogger.writeToColumns("Error parsing optode C2RPhase"); } //Debug.Print("new optodeSB: " + optodeSB.ToString()); break; #endregion case Instruments.Ph: #region format pH message phSB.Clear(); phSB.Append(UTF8Encoding.UTF8.GetChars(sampleBytes)); instrumentsPosted |= Instruments.Ph; break; #endregion default: throw new Exception("Unknown sample type sent"); } //Check to see if everything that was supposed to post posted then write the sample if (instrumentsToSample == instrumentsPosted) { EngrLogger.writeToColumns("Everything posted, writing sample"); writeSample(); } } private static TimeSpan isDoneStartTime = new TimeSpan(); private static TimeSpan isDoneWaitSpan = new TimeSpan(0, 1, 0); //This should be set to the maximum time required to sample both pH and NO3 plus some margin private static bool firstCheck = true; public static int isPHandNO3Done() { if (firstCheck) { isDoneStartTime = Microsoft.SPOT.Hardware.Utility.GetMachineTime(); firstCheck = false; EngrLogger.writeToColumns("SciLogger isPHandNO3Done: first check"); } if (posted(Instruments.Ph) && posted(Instruments.NO3)) { firstCheck = true; EngrLogger.writeToColumns("SciLogger isPHandN03Done: got PH and NO3"); return 1; } if ((Microsoft.SPOT.Hardware.Utility.GetMachineTime() - isDoneStartTime) > isDoneWaitSpan) { //TODO P2 probably need an error handler here EngrLogger.writeToColumns("SciLogger isPHandNO3Done: timed out"); firstCheck = true; return -1; } return 0; } public static void writeMsgComment(StringBuilder inComment) { sbTemp.Clear(); sbTemp.Append("$ "); sbTemp.Append(inComment); sbTemp.Append("\r\n"); //Debug.Print("msg Comment: "); //Debug.Print(sbTemp.ToString()); msgFile.Write(UTF8Encoding.UTF8.GetBytes(sbTemp.ToString()), 0, sbTemp.Length); msgFile.Flush(); } public static void writeToLog(StringBuilder inLine) { sbTemp.Clear(); sbTemp.Append(inLine); sbTemp.Append("\r\n"); //Debug.Print("log Comment: "); //Debug.Print(sbTemp.ToString()); logFile.Write(UTF8Encoding.UTF8.GetBytes(sbTemp.ToString()), 0, sbTemp.Length); logFile.Flush(); } /// /// Print the current science file to the debugger /// public static void printLastMsgFile() { StreamReader sciFileStream; string fileLine; bool reopenFile; if (msgFileOpen) { closeMsgFile(); reopenFile = true; } else reopenFile = false; sciFileStream = new StreamReader(msgFileName.ToString()); while ((fileLine = sciFileStream.ReadLine()) != null) Debug.Print(fileLine); sciFileStream.Close(); if (reopenFile) msgFile = new FileStream(msgFileName.ToString(), FileMode.OpenOrCreate, FileAccess.Write); } /// /// Enqueue's a filename in the FIFO queue that the rudics thread watches and attempts to upload from. /// /// Name Must be full path, and valid, file should not be open. public static void EnqueueFileForXfer(string name) { _qRecord.byteArray = Encoding.UTF8.GetBytes(name); _qRecord.Update(); // Add filename to transfer buffer _fileQ.Enqueue(_qRecord); sbTemp.Clear().Append("Enqueueing File for Transfer: ").Append(name); EngrLogger.writeToColumns(sbTemp); } /// /// Returns the last file closed as a QRecord, with closing time in the date field. /// /// Thrown when attempting to dequeue from empty queue public static QRecord DequeueLastFileClosed { get { return _fileQ.Dequeue(); } } private static bool numberOfCharsInByteArray(int n, char testChar, byte[] byteArray) { int numChars = 0; int index = -1; for (int i = 0; i < n; i++) { index = Array.IndexOf(byteArray, (byte)testChar, index + 1); if (index < 0) break; else numChars++; } if (numChars >= n) return true; else return false; } public static void updateLatLon(gpsUBlox.GPSPosition position) { gpsPositionSB.Clear(); gpsPositionSB.Append("Lon Lat numSats gpsFixTime TTFF:\t"); gpsPositionSB.Append(position.lonSB); gpsPositionSB.Append(Literals.tabChar); gpsPositionSB.Append(position.latSB); gpsPositionSB.Append(Literals.tabChar); gpsPositionSB.Append(position.gpsTimeOfFix); gpsPositionSB.Append(Literals.tabChar); gpsPositionSB.Append(position.gpsTTFF); gpsPositionSB.Append(Literals.tabChar); } } }