GPS was functionality I integrated very early in the project since it was key to the whole project. I selected the Navspark-GL development board for my prototype. It's an Arduino compatible GPS/GLONASS module, which means it can be programmed using the Arduino environment, which is pretty cool. The "GLONASS" part means it can also use satellites in the Russians' equivalent of the US's GPS system, which means it can track even more satellites in more situations which makes for more reliable tracking.
Using the module for a simple prototype is easy. However, I started running into problems when I tried to do anything more complicated than simply reading the NMEA formatted messages coming from it's serial port. For instance, I wanted my main MCU to be able to change the update rate of the module. This is impossible because the module doesn't have an exposed serial receive line to send commands over. And even if it did, the firmware on the module only listens for commands on the serial port connected to the USB port. I ran into another problem too, but it requires some more background.
My project is meant to send output over Bluetooth, WiFi, etc. That data includes both NMEA messages from the GPS module as well as data formatted into RaceChrono compatible messages. The format used by RaceChrono requires each message to be time stamped with a NMEA formatted time stamp. The RaceChrono messages are intermingled with the GPS messages, but the time stamps for all the messages need to be using the same time base. For instance, two GPS messages are sent, the first with a time stamp of 1 and the second with a time stamp of 2. Between those two messages, a RaceChrono message is sent. The RaceChrono message must have a time stamp between 1 and 2. Where does that time stamp come from? The main MCU needs to have some sense of time, but it has to be kept in sync with the GPS time. How should that be done?
My first idea was to reprogram the GPS module with custom code. The new code would send a consistent "pulse" at the top of every second, which could be used my the main firmware to keep an internal timer synchronized. As an aside, the GPS module actually has an output pin that does this exact thing. But the problem with that signal is that it's only triggered during the time the module has a lock on at least 4 satellites. But the module takes 30 seconds or more after power up (typical for any GPS module) before it gets that lock, so there's 30 seconds or more of time the main MCU clock is out of sync with the GPS clock. In my opinion, that's no good. So my custom code would send a pulse every second irregardless of satellite lock.
When I tried to write code for the module to do this, I ran into serious problems. It became obvious the GPS library didn't provide support for anything like what I needed. During my search for a solution, I started asking Navspark (the company who makes the module) questions, and the answers started to worry me.
My intentions from pretty early on where to eventually replace the Navspark-GL development module with a S1216F8-GL module, also from Navspark. I thought the S1216F module was simply the "non-development" version of the module, made for mounting directly on a circuit board. I thought it had all the same capabilities of the development board. It turns out it doesn't. The S1216F module is actually made by SkyTraq and distributed by Navspark. The Navspark-GL is based on the same underlying chip set and code, but that's it. The S1216F module is effectively non-programmable, which means I can't change its behavior at all, other than through commands I can send it, which, ironically, I can't do with the development board. So I had to rethink how to do clock synchronization.
What I eventually came up with is pretty simple and it seems to work well enough. The GPS module sends it standard NMEA messages, and I parse them on the main MCU. I extract the current time stamp from any message that has one (mainly the RMC sentence). At that time, I also reset a timer that counts milliseconds. Whenever I need a time stamp, for example for a RaceChrono formatted message, I take the last known time stamp, add the current timer value (in milliseconds), and correct for second, minute, hour, day, month, and year rollover. The millisecond timer on the main MCU drifts a little in a non-predictable way, so it's possible that over periods greater than about 10 seconds (by my measure), the time stamps generated on the main MCU using this technique will not match the time on the GPS module (we're talking millisecond accuracy), but since I'm re-syncing the time on every GPS update (10, or even 20 times a second), the drift is not noticeable.
This solution to time synchronization is sort of brute force and one that I was trying to avoid since it eats some cycles to do the parsing and math, but it hasn't impacted performance as much as I feared. I'll have to keep an eye on it to be sure it stays that way. If it gets out of hand, one solution would be to only parse every N'th GPS time stamp such that I only synchronize every second.
That's it for this update. I'll eventually be writing about the IMU module once I get the kinks worked out of the library. That one has been causing me problems for more time than I like to admit. Meanwhile, I've been designing circuit boards and sending those off the board fabricator. I've been designing the next prototype as a series of pluggable modules. Each module contains all the circuitry for one major area of the final design and will allow me to do a full integration test of hardware and software before I start designing the final, production ready circuit board.