GPS Megathread

Correct me if I’m wrong, but where woudl you attach the GPS on the 9DoF? Serial RX? If that’s the case, I don’t really think that’s a good idea. For one it would be good if we used that for querying the device, instead of just listening to an endless stream of data. That’s not really a discussion for this topic, though, so…

Binary GPS output is something most of the higher end GPSes support. I do not, however, know if it is standard.

We won’t need the Rx on the 9dof, you doesn’t need to query for it. The Data of the binary format is well defined, you know where a message start, how much bytes it is and the checksum ensures correctness.
The GPS can output standard NEMA sentences. They will be parsed on the 9dof an translated into the binary format. The GPS data will have an own message number, so it’s possible to transmit the IMU and the GPS data at different rates. GPS normally outputs only at 9.6kBits

For example:
[IMU][IMU][IMU][IMU][GPS][IMU][IMU][IMU][IMU][GPS]…

This way you just need a little bit & byte shifting on the .netmf

I sadly doesn’t have a serial GPS module to implement and test this.

Maybe the unused SPI headers on the 9dof could used to attach a GPS module. Or the SPI could be used to connect to the Fezzes…

Why not just get a binary outputting GPS in the first place? :wink:

Because it’s challenging :wink:
If you own an u-Blox GPS, you can use the binary UBX format. Which IS COMPATIBLE to my binary 9dof :wink: It’s the same parser :P. ArduIMU format has only a different message preamble. So it’s easy to merge the u-Blox UBX messages to the IMU output.
That’s the reason I would connect the GPS to the 9dof.

Well designed, isn’t it :wink:

I have a question for the GPS gurus out there. Will a GPS acquire a cold start fix faster if it’s stationary versus moving? One would think a stationary unit would acquire a fix faster, but maybe being in motion doesn’t make any difference. Or maybe being in motion only extends fix time by 10% or so. Any information would be appreciated.

Yeah, they will generally come up faster if they are standing still…

In my experience, it’s much easier for a GPS to triangulate whilst stationary than whilst moving. Antenna orientation also has a lot to do with how quickly you get a lock too.

GS407 should be here today. SUP500 is going to Mark for a little repair. It had a little “incident” with bad firmware ::slight_smile:

With OSD, GPS, PWM input, PWM output, PID, flight managment, etc, I’m worried I won’t get the refresh frequency I need on one chip.

If that’s the case, Instead of using the 9DOF, I’m thinking about getting the UAV3 development board from Sparkfun (http://www.sparkfun.com/commerce/product_info.php?products_id=9980) and letting it handle GPS, IMU and PWM parsing, possibly PID calculations as well. Then I’d let the Panda handle flight planning and communication. I’d like the two with UART and run custom firmware on the dsPIC.

Any thoughts?

Anrejk,

My original idea for the UAV flight computer was to use a totally separate chip for flight stabilization, and NETMF for everything else.

You could always pick up a Netduino/Panda and erase the chip fully to remove NETMF, then you have a pretty damn good ARM dev board.

As for GPS: GS407 is going back, stupid thing won’t get a fix.

Hi Chris, I’ve tried your driver with the 66 Channel LS20031 GPS 10Hz Receiver
sku: GPS-08975 from sparkfun. I’m getting an exception, but am not sure what the exception is. It seems to work fine for a random amount of time, but then throws the exception. Please see the debug output below.

Fix Active
35.487057, 35.487057
0.02 MPH
GC: 2msec 19164 bytes used, 45216 bytes available
Type 0F (STRING ): 324 bytes
Type 11 (CLASS ): 1584 bytes
Type 12 (VALUETYPE ): 48 bytes
Type 13 (SZARRAY ): 1032 bytes
Type 15 (FREEBLOCK ): 45216 bytes
Type 17 (ASSEMBLY ): 12264 bytes
Type 18 (WEAKCLASS ): 48 bytes
Type 19 (REFLECTION ): 24 bytes
Type 1B (DELEGATE_HEAD ): 252 bytes
Type 1D (OBJECT_TO_EVENT ): 96 bytes
Type 1E (BINARY_BLOB_HEAD ): 252 bytes
Type 1F (THREAD ): 768 bytes
Type 20 (SUBTHREAD ): 96 bytes
Type 21 (STACK_FRAME ): 1152 bytes
Type 27 (FINALIZER_HEAD ): 120 bytes
Type 31 (IO_PORT ): 144 bytes
Type 34 (APPDOMAIN_HEAD ): 72 bytes
Type 36 (APPDOMAIN_ASSEMBLY ): 888 bytes
#### Exception System.ArgumentException - 0x00000000 (3) ####
#### Message:
#### System.Convert::ToInt32 [IP: 00dc] ####
#### ChrisSeto.GeoInfoSystems.MicroGPS.GPSHelper::IsValidChecksum [IP: 0057] ####
#### ChrisSeto.GeoInfoSystems.MicroGPS.MicroGPS::GPSSerialHandle_DataReceived [IP: 00bb] ####
#### System.IO.Ports.SerialPort::DataEventHandler [IP: 0012] ####
A first chance exception of type ‘System.ArgumentException’ occurred in mscorlib.dll
An unhandled exception of type ‘System.ArgumentException’ occurred in mscorlib.dll

The baud rate is 57600 and the gps is sending data at 5hz. Is it coming in too fast?
Thanks!

Are you sure the baud is set right?

I can only assume so… I get correct data about 80% of the time. You can see just above the exception there is some good data coming across.

Is what I am seeing an error where the checksum is not validating correctly?

Maybe my panda’s oscillator is off a tad. I haven’t tried fudging the baud rate yet to see if that corrects the issue but I will give that a shot.

Thanks for the help!

Hmm, what’s odd is that I use a very similar (LS20033) in testing…

try this:

/*
 * MicroGPS -- A very small GPS parser for NETMF
 *	Coded by Chris Seto 2010
 *	
 * This software is released under the Apache 2.0 license, copyright Chris Seto 2010
 * 
 * Some code derived from other sources, such as MFGps
 * */

using System;
using System.IO.Ports;
using System.Text;
using ChrisSeto.GeoInformationSystems;

namespace ChrisSeto.GeoInformationSystems
{
	/// <summary>
	/// GPS fix levels
	/// </summary>
	public enum FixType
	{
		Void,
		Active,
		Unknown,
		OneD,
		TwoD,
		ThreeD,
	}

	/// <summary>
	/// Update event
	/// </summary>
	/// <param name="rmc"></param>
	public delegate void RMCUpdateDelegate(RMCOutput rmc);

	/// <summary>
	/// $GPRMC Output
	/// </summary>
	public struct RMCOutput
	{
		/*
		 	$GPRMC, 050537.000, A, 3838.4594, N, 09035.6545, W, 2.04,  167.21, 231010, ,,     A  *7D
		    $GPRMC, 123519,     A, 4807.038,  N, 01131.000,  E, 022.4, 084.4,  230394, 003.1 ,W  *6A
		    0       1           2  3          4 5            6  7      8       9       10    11  12  

			Where:
				0 RMC          Recommended Minimum sentence C
				1 123519       Fix taken at 12:35:19 UTC
				2 A            Status A=active or V=Void.
				3/4 4807.038,N   Latitude 48 deg 07.038' N
				5/6 01131.000,E  Longitude 11 deg 31.000' E
				7 022.4        Speed over the ground in knots
				8 084.4        Track angle in degrees True
				9 230394       Date - 23rd of March 1994
				10/11 003.1,W      Magnetic Variation
				12 *6A          The checksum data, always begins with *
		 */

		/// <summary>
		/// Where we are going, degrees
		/// </summary>
		public double Heading;

		/// <summary>
		/// How fast we are going, knots
		/// </summary>
		public double Speed;

		/// <summary>
		/// Our current location
		/// </summary>
		public Location Coords;

		/// <summary>
		/// Our GPS fix status, Void or Active
		/// </summary>
		public FixType Fix;

		/// <summary>
		/// Construct, parse according to RMC
		/// </summary>
		/// <param name="rmcParams"></param>
		public RMCOutput(string[] rmcParams)
		{
			// Heading
			Heading = double.Parse(rmcParams[8]);

			// Speed
			Speed = double.Parse(rmcParams[7]);

			// Coords
			// Lat in degMinSec format
			double[] latDegMinSec = new double[3]
			{
				int.Parse(rmcParams[3].Substring(0, 2)),
				int.Parse(rmcParams[3].Substring(2, 2)),
				(GPSHelper.ToDouble(rmcParams[3].Substring(4, 5)) * 60),
			};

			// Lon in DegMinSec format
			double[] lonDegMinSec = new double[3]
			{
				int.Parse(rmcParams[5].Substring(0, 3)),
				int.Parse(rmcParams[5].Substring(3, 2)),
				(GPSHelper.ToDouble(rmcParams[5].Substring(5, 5)) * 60),
			};

			double finalLat = (latDegMinSec[0] + ((latDegMinSec[1] + (latDegMinSec[2] / 60.00F)) / 60.00F));
			double finalLon = (lonDegMinSec[0] + ((lonDegMinSec[1] + (lonDegMinSec[2] / 60.00F)) / 60.00F));

			// Solve for south/west being minus
			if (rmcParams[4] == "S")
				finalLat = -finalLat;

			if (rmcParams[6] == "W")
				finalLon = -finalLon;

			// Create the new location
			Coords = new Location(finalLat, finalLon, 0);

			// Fix status
			switch (rmcParams[2])
			{
				// Active
				case "A":
					Fix = FixType.Active;
					break;

				// Void
				case "V":
					Fix = FixType.Void;
					break;

				// Only included to make sure Fix is assigned
				default:
					Fix = FixType.Unknown;
					break;
			}
		}
	}

	/// <summary>
	/// Methods helpful to the parser and outside classes
	/// </summary>
	public static class GPSHelper
	{
		/// <summary>
		/// Verify checksum
		/// </summary>
		/// <param name="sentence"></param>
		/// <returns></returns>
		public static bool IsValidChecksum(string sentence)
		{
			if (sentence.IndexOf('*') > -1)
			{
				string validChecksum = sentence.Substring(sentence.IndexOf('*') + 1, 2);
				sentence = sentence.Substring(1, sentence.IndexOf('*') - 1);

				int checksum = 0;

				for (int i = 0; i < sentence.Length; i++)
					checksum ^= (int)sentence[i];

				return (checksum == Convert.ToInt32(validChecksum, 16));
			}
			else
				return true;
		}

		/// <summary>
		/// Append checksum to the end the input packet
		/// </summary>
		/// <param name="packet"></param>
		/// <returns></returns>
		public static string AppendChecksum(string packet)
		{
			// Checksum
			string hex = "0123456789ABCDEF";
			int checksum = 0;

			for (int i = 1; i < packet.Length; i++)
				checksum ^= (int)packet[i];

			// Generate the final packet
			return packet + "*" + new string(new char[] { hex[(checksum & 0xF0) >> 4], hex[checksum & 0x0F] }) + "\r\n";
		}

		/// <summary>
		/// Stolen from Mark Harris
		/// </summary>
		/// <param name="number"></param>
		/// <returns></returns>
		public static double ToDouble(string number)
		{
			bool isNegative;
			int characterPosition;

			int value = 0;
			int dec = 0;
			int mult = 1;

			if (number[0] == '-')
			{
				isNegative = true;
				characterPosition = 1;
			}
			else
			{
				isNegative = false;
				characterPosition = 0;
			}

			char c = '0';
			int stringLength = number.Length;

			for (; characterPosition < stringLength; characterPosition = characterPosition + 1)
			{
				c = number[characterPosition];

				if (c >= '0' && c <= '9')
					value = (value * 10) + (c - '0');
				else
					break;
			}

			if (characterPosition < stringLength && c == '.')
			{
				for (characterPosition = characterPosition + 1; characterPosition < stringLength; characterPosition = characterPosition + 1)
				{
					c = number[characterPosition];

					if (c >= '0' && c <= '9')
					{
						dec = (dec * 10) + (c - '0');
						mult = mult * 10;
					}
					else
						break;
				}
			}

			float full = value + ((float)dec / mult);

			return isNegative ? -full : full;
		}
	}

	/// <summary>
	/// GPS parser class
	/// </summary>
	class MicroGPS
	{
		/// <summary>
		/// GPS 
		/// </summary>
		public SerialPort GpsSerial;

		/// <summary>
		/// Event to call when we have new $GPRMC
		/// </summary>
		public event RMCUpdateDelegate RMCUpdate;

		/// <summary>
		/// Saved up messages
		/// </summary>
		private string messageBuffer = String.Empty;

		/// <summary>
		/// Construct, bind the serial port
		/// </summary>
		/// <param name="comPort"></param>
		/// <param name="baud"></param>
		public MicroGPS(string comPort, int baud)
		{
			// Bind the serial port
			GpsSerial = new SerialPort(comPort, baud);

			// Options..
			GpsSerial.ReadTimeout = 0;

			// Flush and open the port
			GpsSerial.Flush();
			GpsSerial.Open();

			// Subscribe to the data receive event
			GpsSerial.DataReceived += new SerialDataReceivedEventHandler(GPSSerialHandle_DataReceived);
		}

		/// <summary>
		/// Re-set baud after serial port is opened
		/// </summary>
		/// <param name="set"></param>
		public void SetBaud(int set)
		{
			GpsSerial.Close();
			GpsSerial.BaudRate = set;
			GpsSerial.Open();
		}
		
		/// <summary>
		/// Stop the handling of incoming data
		/// </summary>
		public void PauseReceiving()
		{
			GpsSerial.DataReceived -= GPSSerialHandle_DataReceived;
		}

		/// <summary>
		/// Restart the handling of incoming data
		/// </summary>
		public void RestartReceiving()
		{
			GpsSerial.DiscardInBuffer();
			GpsSerial.DataReceived += new SerialDataReceivedEventHandler(GPSSerialHandle_DataReceived);
		}

		/// <summary>
		/// Handle the data
		/// </summary>
		/// <param name="sender"></param>
		/// <param name="e"></param>
		void GPSSerialHandle_DataReceived(object sender, SerialDataReceivedEventArgs e)
		{
			byte[] response = new byte[GpsSerial.BytesToRead];
			string[] messageQueue = new string[10];
			string messageCheck;
			int k = -1;
			int sentenceLength = 0;

			GpsSerial.Read(response, 0, response.Length);

			try
			{
				messageBuffer += new string(UTF8Encoding.UTF8.GetChars(response));
			}
			finally
			{
			}

			int startIndex = messageBuffer.IndexOf("$");
			int newLine = messageBuffer.IndexOf("\r\n");

			if (newLine < startIndex)
			{
				messageBuffer = messageBuffer.Substring(startIndex, messageBuffer.Length - messageBuffer.Substring(0, startIndex).Length);

				startIndex = messageBuffer.IndexOf("$");
				newLine = messageBuffer.IndexOf("\r\n");
			}

			while (startIndex > -1 && newLine > -1)
			{
				messageCheck = messageBuffer.Substring(startIndex, newLine).Trim();

				if (GPSHelper.IsValidChecksum(messageCheck))
					messageQueue[++k] = messageCheck;

				// Remove the message from the running buffer
				sentenceLength = messageBuffer.Substring(startIndex, newLine).Length;
				messageBuffer = messageBuffer.Substring(sentenceLength, messageBuffer.Length - sentenceLength);

				startIndex = messageBuffer.IndexOf("$");
				newLine = (messageBuffer.Length > 3) ? messageBuffer.IndexOf("\r\n", 3) : -1;
			}

			// Iterate through the queue and parse the waiting sentences
            for (int i = 0; i < 10; i++)
            {
				// If we actually have something to parse
                if (messageQueue[i] != null && messageQueue[i] != String.Empty)
                {
					// Break the sentence up into tokens
                    string[] splitSentence = messageQueue[i].Split(',');

					switch (splitSentence[0])
					{
						// $GPRMC
						case "$GPRMC":
							if (RMCUpdate != null)
								RMCUpdate(new RMCOutput(splitSentence));
							break;

						// Unsupported
						default:
							break;
					}

					// Remove the message from the queue
                    messageQueue[i] = null;
                }
                else
                    break;
			}
		}


	}
}

Thanks Chris, but still no good.
Here is my debug output. It looks like I get two fixes, then an exception.

I had to change the header of your code to this to make it work with your original example, FYI
.{

using System;
using System.IO.Ports;
using System.Text;
using ChrisSeto.GeoInfoSystems.System;

namespace ChrisSeto.GeoInfoSystems.MicroGPS}

The debugging target runtime is loading the application assemblies and starting execution.
Ready.

‘Microsoft.SPOT.Debugger.CorDebug.dll’ (Managed): Loaded ‘C:\Program Files (x86)\Microsoft .NET Micro Framework\v4.1\Assemblies\le\mscorlib.dll’, Symbols loaded.
‘Microsoft.SPOT.Debugger.CorDebug.dll’ (Managed): Loaded ‘C:\Program Files (x86)\Microsoft .NET Micro Framework\v4.1\Assemblies\le\Microsoft.SPOT.Native.dll’, Symbols loaded.
‘Microsoft.SPOT.Debugger.CorDebug.dll’ (Managed): Loaded ‘C:\Program Files (x86)\Microsoft .NET Micro Framework\v4.1\Assemblies\le\Microsoft.SPOT.Hardware.dll’, Symbols loaded.
‘Microsoft.SPOT.Debugger.CorDebug.dll’ (Managed): Loaded ‘C:\Program Files (x86)\Microsoft .NET Micro Framework\v4.1\Assemblies\le\Microsoft.SPOT.IO.dll’, Symbols loaded.
‘Microsoft.SPOT.Debugger.CorDebug.dll’ (Managed): Loaded ‘C:\Program Files (x86)\Microsoft .NET Micro Framework\v4.1\Assemblies\le\System.IO.dll’, Symbols loaded.
‘Microsoft.SPOT.Debugger.CorDebug.dll’ (Managed): Loaded ‘C:\Program Files (x86)\Microsoft .NET Micro Framework\v4.1\Assemblies\le\Microsoft.SPOT.Hardware.SerialPort.dll’, Symbols loaded.
‘Microsoft.SPOT.Debugger.CorDebug.dll’ (Managed): Loaded ‘C:\Program Files (x86)\GHI Electronics\GHI NETMF v4.1 SDK\Assemblies\le\GHIElectronics.NETMF.System.dll’
‘Microsoft.SPOT.Debugger.CorDebug.dll’ (Managed): Loaded ‘C:\Users\Mac\Documents\Visual Studio 2010\Projects\GeoInfoSystems_2 -GHI\GeoInfoSystems\bin\Debug\le\GeoInfoSystems.exe’, Symbols loaded.
‘Microsoft.SPOT.Debugger.CorDebug.dll’ (Managed): Loaded ‘C:\Program Files (x86)\GHI Electronics\GHI NETMF v4.1 SDK\Assemblies\le\FEZPanda_GHIElectronics.NETMF.FEZ.dll’
‘Microsoft.SPOT.Debugger.CorDebug.dll’ (Managed): Loaded 'C:\Program Files (x86)\GHI Electronics\GHI NETMF v4.1 SDK\Assemblies\le\GHIElectronics.NETMF.Hardware.dll’
The thread ‘’ (0x2) has exited with code 0 (0x0).
GC: 2msec 24900 bytes used, 39480 bytes available
Type 0F (STRING ): 432 bytes
Type 11 (CLASS ): 1344 bytes
Type 12 (VALUETYPE ): 48 bytes
Type 13 (SZARRAY ): 972 bytes
Type 15 (FREEBLOCK ): 39480 bytes
Type 16 (CACHEDBLOCK ): 120 bytes
Type 17 (ASSEMBLY ): 12264 bytes
Type 18 (WEAKCLASS ): 48 bytes
Type 1B (DELEGATE_HEAD ): 252 bytes
Type 1D (OBJECT_TO_EVENT ): 96 bytes
Type 1E (BINARY_BLOB_HEAD ): 5868 bytes
Type 1F (THREAD ): 768 bytes
Type 20 (SUBTHREAD ): 96 bytes
Type 21 (STACK_FRAME ): 1368 bytes
Type 27 (FINALIZER_HEAD ): 120 bytes
Type 31 (IO_PORT ): 144 bytes
Type 34 (APPDOMAIN_HEAD ): 72 bytes
Type 36 (APPDOMAIN_ASSEMBLY ): 888 bytes
Fix Active
35.487047, 35.487047
0.01 MPH
GC: 2msec 24960 bytes used, 39420 bytes available
Type 0F (STRING ): 456 bytes
Type 11 (CLASS ): 1584 bytes
Type 12 (VALUETYPE ): 48 bytes
Type 13 (SZARRAY ): 1020 bytes
Type 15 (FREEBLOCK ): 39420 bytes
Type 17 (ASSEMBLY ): 12264 bytes
Type 18 (WEAKCLASS ): 48 bytes
Type 19 (REFLECTION ): 24 bytes
Type 1B (DELEGATE_HEAD ): 252 bytes
Type 1D (OBJECT_TO_EVENT ): 96 bytes
Type 1E (BINARY_BLOB_HEAD ): 5928 bytes
Type 1F (THREAD ): 768 bytes
Type 20 (SUBTHREAD ): 96 bytes
Type 21 (STACK_FRAME ): 1152 bytes
Type 27 (FINALIZER_HEAD ): 120 bytes
Type 31 (IO_PORT ): 144 bytes
Type 34 (APPDOMAIN_HEAD ): 72 bytes
Type 36 (APPDOMAIN_ASSEMBLY ): 888 bytes
Fix Active
35.487047, 35.487047
0.01 MPH
#### Exception System.ArgumentException - 0x00000000 (3) ####
#### Message:
#### System.Convert::ToInt32 [IP: 00dc] ####
#### ChrisSeto.GeoInfoSystems.MicroGPS.GPSHelper::IsValidChecksum [IP: 0057] ####
#### ChrisSeto.GeoInfoSystems.MicroGPS.MicroGPS::GPSSerialHandle_DataReceived [IP: 00bb] ####
#### System.IO.Ports.SerialPort::DataEventHandler [IP: 0012] ####
A first chance exception of type ‘System.ArgumentException’ occurred in mscorlib.dll
An unhandled exception of type ‘System.ArgumentException’ occurred in mscorlib.dll

Wow, Hmm…

I have never seen that Exception before… Do you have any other GPs units to test with? Can you find the offending line, put a break point a few lines up and see if you can track down what’s going on the the “locals” view?

Sorry to butt in - I’ve seen this with input routines where the data read from the serial port is not filtered (i.e. not checked for values < 0x20); The convert routine will explode.
In my case, there was a carriage return at the end of the string that was being included. After the split I tried to convert that substring to an integer - with try -catch. But it still blew up with the same system exception you are seeing.
I would suggest scanning the string and replacing characters < 0x20 with 0x20 before further processing.
At least for strings that are going to be converted.

<edit: conversion problems were always at end of string>

Set up the MicroGPS code with a 20031 to look at the data and found that sentences causing the exception contained no ‘*’, so the attempt to convert validChecksum was trying to convert stuff like “3$”, which doesn’t work. Thought it was the GC, but a lock on sentence inside IsValidChecksum didn’t help.

<edit: found the culprit on my system by printing the strings:

$GPGSV,3,2,1$GPGSV,3,1,12,08,80,086,15,05,67,252,22,07,49,059,26,42,286,247D
$GPGSV,3,2,12,10,39,165,28,30,150,13,11,095,02,08,221,35
7C
$GPGSV,3,3,12,15,07,283,15,21,06,335,03,04,032,06,02,020,74
$GPRMC,112951.600,A,4958.8371,N,00815.4093,E,0.07,84.64,1505$GPGGA,112951.800,4958.8371,N,00815.4093,E,1,5,1.90,192.6,M,47.9,M,5F
$GPGLL,4958.8371,N,00815.4093,E,112951.800,A,A
51
$GPGSA,A,3,02,08,26,05,15,2.11,1.90,0.93
0D
$GPGSV,3,1,12,08,80,086,15,05,67,252,22,07,49,059,26,42,286,247D
$GPGSV,3,2,12,10,39,165,28,30,150,13,11,095,02,08,221,35
7C
$GPGSV,3,3,12,15,07,283,15,21,06,335,03,04,032,06,02,020,7,N,0.14,K,A01

the last line fails because it finds a ‘star’ in the string which doesn’t precede the checksum.
The convert routine tries to convert “7,” in this case, which causes exception.
Possibly due to 57600 baud rate. Should be OK with lower values.

  • or -
    reducing the sentences transmitted from the GPs with:

// Only send GGA and RMC sentences
byte tx_data = Encoding.UTF8.GetBytes(“$PMTK314,0,1,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0*28\r\n”);
GPSSerialHandle.Write(tx_data, 0, tx_data.Length);

Looks to me that the serial port isn’t getting complete sentences - the first line looks like two $GPGSV lines mashed together. That might explain why some of the other data doesn’t seem right too.

Yes. Updating the S65 Shield was taking too much time occasionally. I changed the S65 update scheme and reduced the sentences being received. Mine seems to be OK now.