- GPS-85 and GPS-86: An NMEA 0183 Sentence Parser and UI
- 07 Feb 2014 12:27:01 pm
- Last edited by CVSoft on 10 May 2014 03:34:17 pm; edited 11 times in total
Inspired by Kerm's progress with calculator-based GPS, I figured I should write something with TI-BASIC implementing a number of tools found in my preferred GPS program. Currently running on TI-86 and 68k, this sample code can slice the messages stored in a string into list elements, and the 68k version can display coordinates. Because z80 doesn't support null strings, nor can it create lists with string elements, it's much messier. It also doesn't help not having inString( in TI-86 BASIC.
Code:
Eventually I'll have it check the data inside and turn it into something readable by people. I still haven't decided how to get the data to the calculator, which will either be by the Calcnet2-86 released in the very very distant future or via direct linking with Get( commands.
I'm also unsure of how the multi-sentence GPGSV messages would be received. My current idea of the GPS-over-Cn2 receiver would place the most recent message in the buffer, and the calc would read the buffer until it finds a supported NMEA sentence. However, it's likely that one of the two-or-so GPGSV sentences would be overwritten before the calc can grab the other message. So currently I've decided to not implement the GPGSV features, so no live satellite map.
*bump*
May 2014:
5th: I've decided to finish this after finding it on my calculator and realizing how little effort is required to add basic functionality. I've also decided to throw in support for the default link protocol (Get command).
7th: I've fixed several bugs in the above message splicer (which shouldn't even work), and cheated a little in order to make sure that there's a "$" in front of every sentence. In addition, it's purely a subprogram; however, displaying the output isn't such a complex procedure anymore. Because TI-Z80 doesn't like empty strings, null data elements still need to be checked for.
8th:I'm starting a basic UI, which currently doesn't read the NMEA data and just displays some dummy values until I find a quick and effective way to deliver that data. Targeting TI-85 semicompatibility (since it won't get calcnet), I'm avoiding the graphscreen except for non-text things.
I wrote a subprogram for the sole purpose for translating GPGGA sentences into positions; it runs in about four seconds on the TI-86, and is a fairly hefty 534 bytes. However, it caches the data element positions, enabling very fast execution (1.5-ish seconds) if the positions in the NMEA 0183 sentence have been previously determined.
9th: I've decided to use the above GPGGA translator instead of the general-purpose subprogram I've included the source of here, since execution is much faster (see below).
TI-85: 1.55 seconds uncached, 0.48 seconds cached
TI-86: 5.45 seconds uncached, 1.95 seconds cached
The huge difference in speed (379%) results from the TI-86's RAM paging; the GPGGA calculator does a lot of variable access, and since these variables are not in the first 16 kB of RAM (I have some projects on here already, using 22 kB), the TI-86 uses additional computing time to access the data.
I spent some time doing more optimization of the GPGGA translator (along with a relatively blazing 83-series* port and a TI-68k port), and these are the new timings:
TI-84 Plus: 0.38 seconds uncached, 0.15 seconds cached
TI-85: 0.85 seconds uncached, 0.47 seconds cached
TI-86: 3.50 seconds uncached, 1.95 seconds cached
TI-89 Titanium: 0.38 seconds uncached, 0.20 seconds cached
* The "$" preceding each NMEA 0183 sentence needs to be replaced with a character that the TI-83 would allow in a string; nothing checks what that first character is, so it doesn't matter what it becomes.
10th: I'm still finding ways to optimize the GPGGA translator, shaving 0.1 seconds off of the TI-85's uncached time and 0.4 seconds off of the TI-86's uncached time. Making a similar optimization to the TI-84 Plus and TI-89 versions (but using inString to completely eliminate loops) should allow it to run almost instantaneously. I may have reached the optimization limit for the string-to-real component,
Current timings:
TI-84 Plus: 0.26 seconds uncached, 0.15 seconds cached
TI-85: 0.74 seconds uncached, 0.47 seconds cached
TI-86: 3.10 seconds uncached, 1.90 seconds cached
TI-89 Titanium: 0.30 seconds uncached, 0.20 seconds cached
Code:
PROGRAM:NMEA
:If sub(gpsdata,1,1)≠"$"
:"$"+gpsdata→gpsdata
:{0}→nmeaout
:2→y
:For(x,1,lngth gpsdata
:If sub(gpsdata,x,1)=","
:Then
:x+1→nmeaout(y)
:y+1→y
:End
:End
:If sub(gpsdata,lngth gpsdata-2,1)=="*"
:lngth gpsdata-1→nmeaout(y
:lngth gpsdata+2→nmeaout(y+1
:2→nmeaout(1
:{0}→nmeadiff
:For(x,1,dimL nmeaout-1
:nmeaout(x+1)-nmeaout(x)-1→nmeadiff(x
:End
Eventually I'll have it check the data inside and turn it into something readable by people. I still haven't decided how to get the data to the calculator, which will either be by the Calcnet2-86 released in the very very distant future or via direct linking with Get( commands.
I'm also unsure of how the multi-sentence GPGSV messages would be received. My current idea of the GPS-over-Cn2 receiver would place the most recent message in the buffer, and the calc would read the buffer until it finds a supported NMEA sentence. However, it's likely that one of the two-or-so GPGSV sentences would be overwritten before the calc can grab the other message. So currently I've decided to not implement the GPGSV features, so no live satellite map.
*bump*
May 2014:
5th: I've decided to finish this after finding it on my calculator and realizing how little effort is required to add basic functionality. I've also decided to throw in support for the default link protocol (Get command).
7th: I've fixed several bugs in the above message splicer (which shouldn't even work), and cheated a little in order to make sure that there's a "$" in front of every sentence. In addition, it's purely a subprogram; however, displaying the output isn't such a complex procedure anymore. Because TI-Z80 doesn't like empty strings, null data elements still need to be checked for.
8th:I'm starting a basic UI, which currently doesn't read the NMEA data and just displays some dummy values until I find a quick and effective way to deliver that data. Targeting TI-85 semicompatibility (since it won't get calcnet), I'm avoiding the graphscreen except for non-text things.
I wrote a subprogram for the sole purpose for translating GPGGA sentences into positions; it runs in about four seconds on the TI-86, and is a fairly hefty 534 bytes. However, it caches the data element positions, enabling very fast execution (1.5-ish seconds) if the positions in the NMEA 0183 sentence have been previously determined.
9th: I've decided to use the above GPGGA translator instead of the general-purpose subprogram I've included the source of here, since execution is much faster (see below).
TI-85: 1.55 seconds uncached, 0.48 seconds cached
TI-86: 5.45 seconds uncached, 1.95 seconds cached
The huge difference in speed (379%) results from the TI-86's RAM paging; the GPGGA calculator does a lot of variable access, and since these variables are not in the first 16 kB of RAM (I have some projects on here already, using 22 kB), the TI-86 uses additional computing time to access the data.
I spent some time doing more optimization of the GPGGA translator (along with a relatively blazing 83-series* port and a TI-68k port), and these are the new timings:
TI-84 Plus: 0.38 seconds uncached, 0.15 seconds cached
TI-85: 0.85 seconds uncached, 0.47 seconds cached
TI-86: 3.50 seconds uncached, 1.95 seconds cached
TI-89 Titanium: 0.38 seconds uncached, 0.20 seconds cached
* The "$" preceding each NMEA 0183 sentence needs to be replaced with a character that the TI-83 would allow in a string; nothing checks what that first character is, so it doesn't matter what it becomes.
10th: I'm still finding ways to optimize the GPGGA translator, shaving 0.1 seconds off of the TI-85's uncached time and 0.4 seconds off of the TI-86's uncached time. Making a similar optimization to the TI-84 Plus and TI-89 versions (but using inString to completely eliminate loops) should allow it to run almost instantaneously. I may have reached the optimization limit for the string-to-real component,
Current timings:
TI-84 Plus: 0.26 seconds uncached, 0.15 seconds cached
TI-85: 0.74 seconds uncached, 0.47 seconds cached
TI-86: 3.10 seconds uncached, 1.90 seconds cached
TI-89 Titanium: 0.30 seconds uncached, 0.20 seconds cached