using System; using System.Text; using System.Threading; using GHI.Processor; using Microsoft.SPOT; using Microsoft.SPOT.Hardware; using SensorModules; using SWModules; namespace HWModules { public sealed class GPS1 : gpsUBlox { private GPS1() : base(new I2CDevice.Configuration(0x42, 20), 6) {} private static GPS1 instance; public static GPS1 Instance { get { return instance ?? (instance = new GPS1()); } } } public class gpsUBlox : I2CPortBase { public struct GPSPosition { public StringBuilder latSB; public StringBuilder lonSB; public int numSats; public StringBuilder gpsTimeOfFix; public DateTime cpfDateTimeOfFix; public TimeSpan gpsTTFF; public StringBuilder rawMessage; } public GPSPosition latestPosition = new GPSPosition(); public gpsUBlox(I2CDevice.Configuration config, int slotNum) : base(config, slotNum) { latestPosition.latSB = new StringBuilder(16); latestPosition.lonSB = new StringBuilder(16); latestPosition.numSats = -1; latestPosition.gpsTimeOfFix = new StringBuilder(16); latestPosition.gpsTTFF = new TimeSpan(); latestPosition.rawMessage = new StringBuilder(256); latestSVStatus.rawMessage = new StringBuilder(512); } private MotherBoard motherboard = MotherBoard.Instance; private static DateTime gpsPowerupTime = new DateTime(1900, 1, 1); public void powerUp() { EngrLogger.writeToColumns("Powering up GPS"); motherboard.EnableChannelPower(MotherBoard.ChannelNames.GPSChannel); gpsPowerupTime = DateTime.Now; } public void powerDown() { EngrLogger.writeToColumns("Powering down GPS"); motherboard.DisableChannelPower(MotherBoard.ChannelNames.GPSChannel); } private static DateTime pubx04DateTime = new DateTime(); private static StringBuilder pubx04ReturnSB = new StringBuilder(128); private static byte[] pubx04ReturnBytes = new byte[128]; private static readonly byte[] commandPUBX04 = UTF8Encoding.UTF8.GetBytes("$PUBX,04*37\r\n"); private static readonly byte[] bytesPUBX04 = UTF8Encoding.UTF8.GetBytes("UBX,04"); private static TimeSpan GPSminusRTCTimeSpan = new TimeSpan(); private static TimeSpan GPSminusLocalTimeSpan = new TimeSpan(); private static TimeSpan RTCminusLocalTimeSpan = new TimeSpan(); private static TimeSpan nanTimeSpan = new TimeSpan(long.MaxValue); public void synchToGPSTime() { GPSminusRTCTimeSpan = nanTimeSpan; GPSminusLocalTimeSpan = nanTimeSpan; for (int i = 0; i < 3; i++) { sbTemp.Clear(); sbTemp.Append("Response to GPS PUBX04 message"); Array.Clear(pubx04ReturnBytes, 0, pubx04ReturnBytes.Length); pubx04ReturnSB.Clear(); pubx04ReturnBytes = sendI2CCmd(commandPUBX04); EngrLogger.writeToColumns(EngrLiterals.ColumnNums.dateTime, DateTime.Now, EngrLiterals.ColumnNums.state, SV.CurrentState, EngrLiterals.ColumnNums.comment, sbTemp, EngrLiterals.ColumnNums.pressure, SV.Pressure, EngrLiterals.ColumnNums.GPS, pubx04ReturnBytes); if (GHI.Utilities.Arrays.Contains(pubx04ReturnBytes, bytesPUBX04) >= 0) { pubx04ReturnSB.Append(SafeEncoding.GetChars(pubx04ReturnBytes)); int hour = (pubx04ReturnSB[7] - 48) * 10 + (pubx04ReturnSB[8] - 48); int minute = (pubx04ReturnSB[9] - 48) * 10 + (pubx04ReturnSB[10] - 48); int second = (pubx04ReturnSB[11] - 48) * 10 + (pubx04ReturnSB[12] - 48); int day = (pubx04ReturnSB[17] - 48) * 10 + (pubx04ReturnSB[18] - 48); int month = (pubx04ReturnSB[19] - 48) * 10 + (pubx04ReturnSB[20] - 48); int year = 2000 + (pubx04ReturnSB[21] - 48) * 10 + (pubx04ReturnSB[22] - 48); //TODO P3 It sure would be nice to figure out a way to do this without using "new". But DateTime is immutable if (year >= 2018) { pubx04DateTime = new DateTime(year, month, day, hour, minute, second); GPSminusLocalTimeSpan = pubx04DateTime - DateTime.Now; Utility.SetLocalTime(pubx04DateTime); sbTemp.Clear(); sbTemp.Append(DateTime.Now.ToString("yyyy-MMM-ddTHH:mm:ssZ")); sbTemp.Append(Literals.tabChar); sbTemp.Append("GPS minus Local Time = "); sbTemp.Append(GPSminusLocalTimeSpan.ToString()); SciLogger.appendToMsgFooter(sbTemp); GPSminusRTCTimeSpan = pubx04DateTime - RealTimeClock.GetDateTime(); RealTimeClock.SetDateTime(pubx04DateTime); RealTimeClock.GetDateTime(); sbTemp.Clear(); sbTemp.Append(DateTime.Now.ToString("yyyy-MMM-ddTHH:mm:ssZ")); sbTemp.Append(Literals.tabChar); sbTemp.Append("GPS minus RTC Time = "); sbTemp.Append(GPSminusRTCTimeSpan.ToString()); SciLogger.appendToMsgFooter(sbTemp); sbTemp.Clear(); sbTemp.Append("Attempting GPS time synch, try number: ").Append(i); sbTemp.Append("; Set system time to: ").Append(pubx04DateTime); EngrLogger.writeToColumns(1, sbTemp); } else { RTCminusLocalTimeSpan = RealTimeClock.GetDateTime() - DateTime.Now; Utility.SetLocalTime(RealTimeClock.GetDateTime()); sbTemp.Clear(); sbTemp.Append(DateTime.Now.ToString("yyyy-MMM-ddTHH:mm:ssZ")); sbTemp.Append(Literals.tabChar); sbTemp.Append("RTC minus Local Time = "); sbTemp.Append(RTCminusLocalTimeSpan.ToString()); SciLogger.appendToMsgFooter(sbTemp); sbTemp.Clear().Append("Time from GPS <2018; Adjust from RTC: "); sbTemp.Append(RealTimeClock.GetDateTime()); sbTemp.Append(" + ").Append(RTCminusLocalTimeSpan); EngrLogger.writeToColumns(1, sbTemp); } break; } else { Utility.SetLocalTime(RealTimeClock.GetDateTime()); pubx04ReturnSB.Clear(); pubx04ReturnSB.Append(SafeEncoding.GetChars(pubx04ReturnBytes)); //Debug.Print("Try Number: " + i.ToString() + "; [ERROR] synching to GPS time with PUBX,04"); //Debug.Print(pubx04ReturnSB.ToString()); } } } private static DateTime gpsTryTime = new DateTime(1900, 1, 1); private static TimeSpan gpsTryTimeSpan = new TimeSpan(0, 0, 30); private static int gpsTryNum = 0; private static gpsUBlox.GPSPosition gpsPosition = new gpsUBlox.GPSPosition(); public int getGPSPosition(int maxNumTries) //TODO P2 consider using UBX-NAV-PVT message { if ((DateTime.Now - gpsTryTime) >= gpsTryTimeSpan) { sbTemp.Clear(); sbTemp.Append("Trying GPS get position try num: "); sbTemp.Append(gpsTryNum.ToString()); sbTemp.Append(" of "); sbTemp.Append(maxNumTries.ToString()); gpsPosition = getPUBX00Position(); if (gpsPosition.numSats >= 4) { gpsPosition.gpsTTFF = DateTime.Now - gpsPowerupTime; EngrLogger.writeToColumns("Succesful GPS getPosition"); return (gpsPosition.numSats); } else { gpsTryTime = DateTime.Now; gpsTryNum++; return (-1); } } else { if (gpsTryNum >= maxNumTries) { EngrLogger.writeToColumns("GPS get position exceeded max number of tries"); gpsTryNum = 0; return (-2); } else return (0); } } private static StringBuilder pubx00ReturnSB = new StringBuilder(128); private static byte[] pubx00ReturnBytes = new byte[128]; private static readonly byte[] commandPUBX00 = UTF8Encoding.UTF8.GetBytes("$PUBX,00*33\r\n"); private static readonly byte[] bytesPUBX00 = UTF8Encoding.UTF8.GetBytes("UBX,00"); public GPSPosition getPUBX00Position() { for (int i = 0; i < 3; i++) { pubx00ReturnBytes = sendI2CCmd(commandPUBX00); EngrLogger.writeToColumns(EngrLiterals.ColumnNums.dateTime, DateTime.Now, EngrLiterals.ColumnNums.state, SV.CurrentState, EngrLiterals.ColumnNums.comment, sbTemp, EngrLiterals.ColumnNums.pressure, SV.Pressure, EngrLiterals.ColumnNums.GPS, pubx00ReturnBytes); pubx00ReturnSB.Clear(); pubx00ReturnSB.Append(SafeEncoding.GetChars(pubx00ReturnBytes)); //Debug.Print("PUBX,00 Return: " + pubx00ReturnSB.ToString()); if (GHI.Utilities.Arrays.Contains(pubx00ReturnBytes, bytesPUBX00) >= 0) { sbTemp.Clear(); parsePUBX00(pubx00ReturnBytes, ref latestPosition); break; } else { sbTemp.Clear(); sbTemp.Append("[ERROR] Try Number: "); sbTemp.Append(i.ToString()); sbTemp.Append("; getting GPS Position with PUBX,00"); EngrLogger.writeToColumns(sbTemp); } } sbTemp.Clear(); sbTemp.Append("Got Position: lat = "); sbTemp.Append(latestPosition.latSB); sbTemp.Append(" lon = "); sbTemp.Append(latestPosition.lonSB); sbTemp.Append(" num sats = "); sbTemp.Append(latestPosition.numSats.ToString()); EngrLogger.writeToColumns(sbTemp); return (latestPosition); } public struct SVStatus { public StringBuilder rawMessage; } public SVStatus latestSVStatus = new SVStatus(); private static StringBuilder pubx03ReturnSB = new StringBuilder(512); private static byte[] pubx03ReturnBytes = new byte[512]; private static readonly byte[] commandPUBX03 = UTF8Encoding.UTF8.GetBytes("$PUBX,03*30\r\n"); private static readonly byte[] bytesPUBX03 = UTF8Encoding.UTF8.GetBytes("UBX,03"); public SVStatus getSatStatus() { Array.Clear(pubx03ReturnBytes, 0, pubx03ReturnBytes.Length); for (int i = 0; i < 3; i++) { pubx03ReturnBytes = sendI2CCmd(commandPUBX03); pubx03ReturnSB.Clear(); pubx03ReturnSB.Append(SafeEncoding.GetChars(pubx03ReturnBytes)); //Debug.Print("PUBX,03 Return: " + pubx03ReturnSB.ToString()); if (GHI.Utilities.Arrays.Contains(pubx03ReturnBytes, bytesPUBX03) >= 0) { sbTemp.Clear(); sbTemp.Append("GPS Sat Status: "); sbTemp.Append(pubx03ReturnSB); EngrLogger.writeToColumns(sbTemp); break; } else { sbTemp.Clear(); sbTemp.Append("Try Number: "); sbTemp.Append(i.ToString()); sbTemp.Append("; Error getting GPS Sat Status with PUBX,03"); EngrLogger.writeToColumns(sbTemp); } } latestSVStatus.rawMessage.Clear(); latestSVStatus.rawMessage.Append(SafeEncoding.GetChars(pubx03ReturnBytes, 0, 256)); return (latestSVStatus); } private static StringBuilder navorbReturnSB = new StringBuilder(512); private static byte[] navOrbReturnBytes = new byte[512]; private static readonly byte[] commandNavOrb = { 0xB5, 0x62, 0x01, 0X34, 0x00, 0x00, 0x35, 0xA0 }; private static readonly byte[] bytesNavOrb = { 0x01, 0x34 }; public byte[] getNavOrb() { Array.Clear(navOrbReturnBytes, 0, navOrbReturnBytes.Length); for (int i = 0; i < 3; i++) { Array.Clear(navOrbReturnBytes, 0, navOrbReturnBytes.Length); navOrbReturnBytes = sendI2CCmd(commandNavOrb); navorbReturnSB.Clear(); navorbReturnSB.Append(SafeEncoding.GetChars(navOrbReturnBytes)); //Debug.Print("PUBX,03 Return: " + pubx03ReturnSB.ToString()); if (GHI.Utilities.Arrays.Contains(navOrbReturnBytes, bytesNavOrb) >= 0) { sbTemp.Clear(); sbTemp.Append("GPS Sat Status: "); sbTemp.Append(pubx03ReturnSB); EngrLogger.writeToColumns(sbTemp); break; } else { sbTemp.Clear(); sbTemp.Append("Try Number: "); sbTemp.Append(i.ToString()); sbTemp.Append("; Error getting GPS Sat Status with PUBX,03"); EngrLogger.writeToColumns(sbTemp); } } latestSVStatus.rawMessage.Clear(); latestSVStatus.rawMessage.Append(SafeEncoding.GetChars(pubx03ReturnBytes, 0, 256)); return (navOrbReturnBytes); } private static readonly byte[] forceColdstartCmd = { 0xB5, 0x62, 0x06, 0x04, 0x04, 0x00, 0xFF, 0xB9, 0x02, 0x00, 0xC8, 0x8F }; private static byte[] coldstartReturnBytes = new byte[64]; public byte[] forceColdstart() { Array.Clear(coldstartReturnBytes, 0, coldstartReturnBytes.Length); coldstartReturnBytes = sendI2CCmd(forceColdstartCmd); return (coldstartReturnBytes); } public static byte[] latBytes = new byte[16]; public static byte[] lonBytes = new byte[16]; public static byte[] numSatsBytes = new byte[16]; private static byte[] fieldBytes = new byte[16]; private static byte[] decMinBytes = new byte[16]; private static byte[] degMinBytes = new byte[16]; public static StringBuilder positionSB = new StringBuilder(64); //TODO P1 need to make sure position fields have something in them even if we didn't get a GPS fix public void parsePUBX00(byte[] inBytes, ref GPSPosition position) { try { Array.Clear(fieldBytes, 0, fieldBytes.Length); Utils.getField(inBytes, comma, 2, ref fieldBytes); position.gpsTimeOfFix.Clear(); position.gpsTimeOfFix.Append(SafeEncoding.GetChars(fieldBytes)); position.cpfDateTimeOfFix = DateTime.Now; Array.Clear(fieldBytes, 0, fieldBytes.Length); Utils.getField(inBytes, comma, 3, ref fieldBytes); position.latSB.Clear(); position.latSB.Append(SafeEncoding.GetChars(fieldBytes)); position.latSB.Append(Literals.tabChar); Array.Clear(fieldBytes, 0, fieldBytes.Length); Utils.getField(inBytes, comma, 4, ref fieldBytes); position.latSB.Append(SafeEncoding.GetChars(fieldBytes)); Array.Clear(fieldBytes, 0, fieldBytes.Length); Utils.getField(inBytes, comma, 5, ref fieldBytes); position.lonSB.Clear(); position.lonSB.Append(SafeEncoding.GetChars(fieldBytes)); position.lonSB.Append(Literals.tabChar); Array.Clear(fieldBytes, 0, fieldBytes.Length); Utils.getField(inBytes, comma, 6, ref fieldBytes); position.lonSB.Append(SafeEncoding.GetChars(fieldBytes)); Array.Clear(fieldBytes, 0, fieldBytes.Length); Utils.getField(inBytes, comma, 18, ref fieldBytes); position.numSats = bytesToInt32(fieldBytes); position.rawMessage.Clear(); position.rawMessage.Append(SafeEncoding.GetChars(inBytes, 0, latestPosition.rawMessage.Capacity)); } catch { EngrLogger.writeToColumns("[ERROR] parsing GPS PUBX00 message"); position.latSB.Clear(); position.latSB.Append(Literals.nanString); position.lonSB.Clear(); position.lonSB.Append(Literals.nanString); position.numSats = -1; position.rawMessage.Clear(); position.rawMessage.Append(SafeEncoding.GetChars(inBytes, 0, latestPosition.rawMessage.Capacity)); } } private int bytesToInt32(byte[] inBytes) { int returnValue = 0; double power = 1; int numBytes = 1; bool didOneCalc = false; if (Array.IndexOf(inBytes, 0) > 3) numBytes = 4; else numBytes = Array.IndexOf(inBytes, 0); for (int i = numBytes - 1; i > -1; i--) { if ((inBytes[i] >= 48) && (inBytes[i] <= 57)) { returnValue = returnValue + (int)((inBytes[i] - 48) * power); power = power * 10; didOneCalc = true; } } if (didOneCalc) return (returnValue); else return (-1); } private const byte dPoint = (byte)'.'; private const byte comma = (byte)','; private const byte nullByte = (byte)'\0'; private const byte north = (byte)'N'; private const byte south = (byte)'S'; private const byte east = (byte)'E'; private const byte west = (byte)'W'; private static I2CDevice.I2CTransaction[] cmdXaction = new I2CDevice.I2CTransaction[1]; private static I2CDevice.I2CTransaction[] numBytesXAction = new I2CDevice.I2CTransaction[2]; private static I2CDevice.I2CTransaction[] readFFXAction = new I2CDevice.I2CTransaction[2]; private readonly byte[] numBytesAddr = new byte[2] { 0xFD, 0xFE }; private static byte[] numBytesValue = new byte[2]; private static byte[] dummyReadArray = new byte[2]; private static byte[] evk7Bytes = new byte[256]; private readonly byte[] ffAddr = new byte[1] { 0xFF }; private static byte[] ffValue = new byte[1]; private static StringBuilder sbTemp = new StringBuilder(128); private byte[] sendI2CCmd(byte[] command) { cmdXaction[0] = CreateWriteTransaction(command); //cmdXaction[1] = CreateReadTransaction(dummyReadArray); if (Execute(cmdXaction, 2000) == 0) EngrLogger.writeToColumns("[ERROR] Timeout error gpsUBlox sendI2CCmd method first Execute command"); //Create I2C transaction to read number of bytes available int evk7BytesIndex = 0; int numBytes = 0; numBytesXAction[0] = CreateWriteTransaction(numBytesAddr); numBytesXAction[1] = CreateReadTransaction(numBytesValue); Array.Clear(numBytesValue, 0, numBytesValue.Length); Array.Clear(evk7Bytes, 0, evk7Bytes.Length); for (int i = 0; i < 10; i++) { if (Execute(numBytesXAction, 2000) == 0) { EngrLogger.writeToColumns("[ERROR] Timeout gpsUBlox sendI2CCmd method second Execute command"); } else { numBytes = (256 * (int)numBytesValue[0]) + numBytesValue[1]; //Debug.Print("Register value: " + numBytesValue[0].ToString() + " " + numBytesValue[1]); if ((numBytes > 0) && (numBytes < 0xFFFF)) break; } Thread.Sleep(200); } //Read address FF until message is complete if ((numBytes > 0) && (numBytes < 0xFFFF)) { // Create I2C transaction to get buffer FF value readFFXAction[0] = CreateWriteTransaction(ffAddr); readFFXAction[1] = CreateReadTransaction(ffValue); evk7BytesIndex = 0; for (int i = 0; i < numBytes; i++) { if (Execute(readFFXAction, 2000) == 0) { EngrLogger.writeToColumns("[ERROR] Timeout error gpsUBlox sendI2CCmd method third Execute command"); } else { //Debug.Print("FF Value = " + ffValue[0].ToString() + " " + ((char)ffValue[0]).ToString()); if (evk7BytesIndex == 0) { if (ffValue[0] == 85) //Wait for "U" start of text character. The $P gets lost somewhere { evk7Bytes[evk7BytesIndex] = ffValue[0]; evk7BytesIndex = evk7BytesIndex + 1; } } else { if (ffValue[0] == 10) { evk7Bytes[evk7BytesIndex] = ffValue[0]; evk7BytesIndex = evk7BytesIndex + 1; break; } else { evk7Bytes[evk7BytesIndex] = ffValue[0]; evk7BytesIndex = evk7BytesIndex + 1; } } if (evk7BytesIndex >= evk7Bytes.Length) break; } } } return (evk7Bytes); } } }