QB64.org Forum

Active Forums => QB64 Discussion => Topic started by: acjacques on October 18, 2019, 12:27:32 am

Title: How to get GPS sentences to variable numbers
Post by: acjacques on October 18, 2019, 12:27:32 am
Hi friends; I am trying to make an application that will use GPS informations like SOG (Speed over Ground) that is the field  after the  7th coma. How select and convert this field that is a string to a number variable with decimals  ?  The LEN os total string is not fixed.
   
A tipical GPS sentence is like the following serial stream:

$GPRMC,123519,A,4807.038,N,01131.000,E,022.4,084.4,230394,003.1,W*6A   (followed by CR +LF) 

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

There are any  code  already done  that could help me ?

Title: Re: How to get GPS sentences to variable numbers
Post by: SMcNeill on October 18, 2019, 03:17:13 am
Looks like it’s basically comma separated values.  Can’t you just use INPUT to get the data?
Title: Re: How to get GPS sentences to variable numbers
Post by: Petr on October 18, 2019, 06:17:58 am
Probably, you're asking about the way the chain breaks into comma-separated components, right? Try this simple way.

Code: QB64: [Select]
  1. a$ = "$GPRMC,123519,A,4807.038,N,01131.000,E,022.4,084.4,230394,003.1,W*6A"
  2. a$ = a$ + "," 'add next comma for next loop:
  3.  
  4.  
  5. 'break the string by commas
  6. DO UNTIL p = _INSTRREV(a$, ",")
  7.     oldp = p
  8.     p = INSTR(p + 1, a$, ",")
  9.     PRINT "position in string:"; p,
  10.     PRINT _TRIM$(MID$(a$, oldp + 1, p - oldp - 1)) 'this is your output
  11.  
  12.  
Title: Re: How to get GPS sentences to variable numbers
Post by: Bert22306 on October 18, 2019, 12:27:31 pm
I think that Steve has the quickest method. Since RMC has a fixed number of data fields, if you enter it as a comma-delimited file using INPUT, then QB64 will automatically know what the individual fields are.

Something like this:

INPUT #1, Header$, UTC, Status$, Lat, NorS$, Long, EorW$, and so on. All of those input parameters will then be available to you as individual parameters. That's the beauty of NMEA 0183 format.

In short, QB64 should be able to do automatically what Petr described.
Title: Re: How to get GPS sentences to variable numbers
Post by: acjacques on October 19, 2019, 02:35:50 pm
Thanks for all comments.
But since the NMEA sentences are a stream of characters with no fixed length how capture the whole sentence  ?
Title: Re: How to get GPS sentences to variable numbers
Post by: bplus on October 19, 2019, 03:12:54 pm
CRLF$ as you said, signals the end of a sentence, once that is detected in the stream by INSTR, you have a sentence to decode.

If you have a whole page (or long string) of sentences, you can use Split to parse the page into an array of sentences using the CRLF$ characters for delimiter and use Split again to parse the fields out of each array sentence using comma as the delimiter.

Split code and demo: https://www.qb64.org/forum/index.php?topic=1607.0

But I don't know "from a stream", maybe Input #1 could do that too, it does work that way from a text file.
Title: Re: How to get GPS sentences to variable numbers
Post by: FellippeHeitor on October 19, 2019, 04:12:30 pm
Thanks for all comments.
But since the NMEA sentences are a stream of characters with no fixed length how capture the whole sentence  ?

Aren’t they comma separated? There must be some separation.
Title: Re: How to get GPS sentences to variable numbers
Post by: Petr on October 19, 2019, 05:33:03 pm
There must be a defined sequence of data in the data stream as you wrote it. Does every stream start with $GPRMC? Or follow after the CR + LF? I don't know the specification of this data stream.

As you wrote:


     RMC          Recommended Minimum sentence C   -> is this STRING TYPE? How lenght? its STRING * 6? if is, then its first 6 bytes in stream,
     123519       Fix taken at 12:35:19 UTC               -> is this INTEGER or LONG numeric value? If INTEGER, so byte 7,8 is for this value, but if it is LONG type, then bytes 7 to 11 are this value,
     A            Status A=active or V=Void.                  -> is this _UNSIGNED _BYTE (or STRING * 1) type?
     4807.038                                                          -> is this SINGLE, or DOUBLE type? (4 bytes or 8 bytes)
     N   Latitude 48 deg 07.038' N                             -> STRING * 1?
     01131.000                                                        -> SINGLE or DOUBLE?
     E  Longitude 11 deg 31.000' E             
     022.4        Speed over the ground in knots
     084.4        Track angle in degrees True
     230394       Date - 23rd of March 1994
     003.1,W      Magnetic Variation
     *6A          The checksum data, always begins with *

If you find the data type in the stream, then simply follow these steps:

TYPE Stream
  RMC AS STRING * 6
  TIMEFIX AS LONG
  Status AS STRING * 1
.
.
.
END TYPE

DIM S as Stream

GET #1, ,S  (if #1 is stream channel)
Title: Re: How to get GPS sentences to variable numbers
Post by: acjacques on October 19, 2019, 05:42:24 pm
I need a code that deal  this "synchronizing"  byte to byte.   I am receiving  serial bytes then i need to start to "adquire" the whole GPS string only after  receive  the $
leading character and stop when receive  the CR+LF at the end.
All sentences  starts with $  and finish with CR +LF
Title: Re: How to get GPS sentences to variable numbers
Post by: Petr on October 19, 2019, 06:05:08 pm
IF program wait to dollar symbol to start, then first step is something as this (pseudocode)

TYPE Stream
 Value0 AS STRING * 1
 Value1 AS SINGLE
 Value2 AS DOUBLE
END TYPE

DIM S AS STREAM
DIM Start as STRING * 1
StreamEnd = CHR$ ( (how is ascii (CHR$) code for CR+LF?) )

OPEN Datastream FOR BINARY AS #1


DO UNTIL START = "$"
GET #1, ,START
LOOP
START = ""
GET #1, ,S 'read all values from stream at once
Title: Re: How to get GPS sentences to variable numbers
Post by: Petr on October 19, 2019, 06:18:06 pm
Try this TYPE (without warranty):

TYPE STREAM
RMC AS STRING * 5  'If the program has already read the dollar symbol, you do not read 6 characters but 5 because the dollar symbol has already been read
FixTaken AS LONG
Status as STRING * 1
Latitude AS SINGLE  'IF NOT WORK, REWRITE IT TO DOUBLE
LatitudeS AS STRING * 1
Longitude AS SINGLE 'IF NOT WORK, REWRITE IT TO DOUBLE
LongitudeS AS STRING * 1
Speed AS SINGLE
Track AS SINGLE
Date AS LONG
Variation AS SINGLE
VariationS AS STRING * 1
Checksum AS STRING * 4
END TYPE
Title: Re: How to get GPS sentences to variable numbers
Post by: SMcNeill on October 20, 2019, 12:33:04 am
whole$ = “”: byte$ = “ “
DO
    GET #1, ,byte$
    whole$ = whole$ + byte$
LOOP UNTIL byte$ = CHR$10)
whole$ = LEFT$(whole$, LEN(whole$) - 2)


The above will get your whole string, and strip off the CRLF, which you then parse with INSTR searching the commas, and placing the values into your relevant fields.
Title: Re: How to get GPS sentences to variable numbers
Post by: acjacques on October 20, 2019, 02:05:18 pm
Good. 
I do not understand the
whole$ = “”: byte$ = “ “       .
Could please explain what means  the extra characters ?
Title: Re: How to get GPS sentences to variable numbers
Post by: Bert22306 on October 20, 2019, 02:38:47 pm
Thanks for all comments.
But since the NMEA sentences are a stream of characters with no fixed length how capture the whole sentence  ?

NMEA 0183 strings start with $, then end with <cr><lf>. The data fields end with *, and always followed by two bytes of checksum.

So, why not run a loop, starting with $ until you reach *, reading character by character, then end (i.e. exit) the loop when you reach *, then do the XOR operation on the characters (bytes) you inputted, to see if the checksum is correct?

First, call the entire NMEA sentence a$. Then you separate that input string into its individual characters:

    FOR i = 1 TO 1000
        num(i) = ASC(a$, i)
        IF num(i) = 42 THEN EXIT FOR
    NEXT i
    numChar = i

The second statement breaks up the long string a$ into its separate characters. The array num(i) is the decimal value assigned to each of the ASCII characters in the NMEA RMC sentence. You exit that FOR loop when you reach character 42, which is *.

Now you know the length of that string of characters. numChar = i. (NMEA strings are at most 82 characters long, so even if the string is not compliant, 1000 should be more than enough.)

Then the checksum:

    FOR i = 2 TO numChar - 1
        total = total XOR num(i)
    NEXT i
    totalHexInit$ = HEX$(total)
    totalHex$ = RIGHT$("00" + totalHexInit$, 2)

You have to start with the character after $, end with the characters just before *, and the checksum is two bytes wide. Now you have the computed checksum, which you can check against the checksum characters sent in the RMC sentence, after *.
Title: Re: How to get GPS sentences to variable numbers
Post by: SMcNeill on October 20, 2019, 02:50:07 pm
Good. 
I do not understand the
whole$ = “”: byte$ = “ “       .
Could please explain what means  the extra characters ?

whole$ = “” — this erases whole$, in case you’ve used it before. 
byte$ = “ “ — this sets byte$ to be one character in size (CHR$(32)), so you read one byte at a time with the GET command, as you mentioned previously.
Title: Re: How to get GPS sentences to variable numbers
Post by: TempodiBasic on October 21, 2019, 06:41:31 pm
Hi acjacques
just to summarize all good tips coming from community...
here an idea to manage byte for byte the getting and the parsing of NMEA0183 sentence...

looking at here we can get more informations  https://en.wikipedia.org/wiki/NMEA_0183 (https://en.wikipedia.org/wiki/NMEA_0183)

Code: QB64: [Select]
  1. ' Steve's suggestion
  2. 'whole$ = "": byte$ = " "
  3. 'DO
  4. '   GET #1, , byte$
  5. '   whole$ = whole$ + byte$
  6. 'LOOP UNTIL byte$ = CHR$10)
  7. 'whole$ = LEFT$(whole$, LEN(whole$) - 2)
  8.  
  9. 'But if we GET one byte$ at time we can just separate each element of the sentence now!
  10.  
  11. 'But since the NMEA sentences are a stream of characters with no fixed length how capture the whole sentence  ?
  12.  
  13. 'so
  14. 'A tipical GPS sentence is like the following serial stream:
  15. '$GPRMC,123519,A,4807.038,N,01131.000,E,022.4,084.4,230394,003.1,W*6A   (followed by CR +LF)
  16. 'CONST CR = CHR$(13), LF = CHR$(10), Start = CHR$(36), CheckSum = CHR$(42), Comma = CHR$(44)
  17. CONST CR = "
  18. ", LF = "
  19. ", Start = "$", CheckSum = "*", Comma = ","
  20. DIM Byte AS STRING * 1, Sentence AS STRING, StringGPS(1 TO 100) AS STRING
  21. DIM Index AS INTEGER
  22.  
  23. IniT
  24. OPEN "I.txt" FOR BINARY AS #1
  25. SearchStartSentence
  26. GettingFields
  27. ShowResults
  28.  
  29.  
  30.  
  31. SUB IniT
  32.     IF _FILEEXISTS("I.txt") THEN KILL "I.txt" 'EXIT SUB
  33.     SHARED Sentence AS STRING, Byte AS STRING * 1
  34.     Sentence = "$GPRMC,123519,A,4807.038,N,01131.000,E,022.4,084.4,230394,003.1,W*6A" + CR + LF
  35.     OPEN "I.txt" FOR OUTPUT AS #1
  36.     PRINT #1, Sentence
  37.     CLOSE #1
  38.     OPEN "I.txt" FOR INPUT AS #1
  39.     LINE INPUT #1, Sentence
  40.     PRINT Sentence
  41.     CLOSE #1
  42.     Byte = ""
  43.     Sentence = ""
  44.     PRINT "press a key to elaborate NMEA 0183 sentence..": SLEEP
  45.  
  46. SUB ShowResults
  47.     SHARED Index AS INTEGER, StringGPS() AS STRING, Sentence AS STRING
  48.     DIM A AS INTEGER
  49.     LOCATE 3, 1: COLOR 14, 1: PRINT Sentence
  50.     COLOR 12, 1
  51.     FOR A = 1 TO Index
  52.         LOCATE , 10: PRINT StringGPS(A), "Index "; A
  53.     NEXT
  54.  
  55.  
  56. SUB GettingFields
  57.     SHARED Byte AS STRING * 1, Index AS INTEGER, StringGPS() AS STRING, Sentence AS STRING
  58.     DIM A AS INTEGER
  59.     WHILE Byte <> LF AND Byte <> CR
  60.         Index = Index + 1
  61.         WHILE Byte <> Comma AND Byte <> CheckSum AND Byte <> CR
  62.             StringGPS(Index) = StringGPS(Index) + Byte
  63.             Byte = TakeByte$
  64.         WEND
  65.         Byte = TakeByte$ ' read next character after delimitator "," or "*"  or CR
  66.     WEND
  67.     FOR A = 1 TO Index
  68.         Sentence = Sentence + "\" + StringGPS(A)
  69.     NEXT
  70.  
  71. SUB SearchStartSentence
  72.     SHARED Byte AS STRING * 1
  73.     WHILE Byte <> Start
  74.         Byte = TakeByte$
  75.     WEND
  76.  
  77. FUNCTION TakeByte$
  78.     DIM Byte AS STRING * 1
  79.     GET #1, , Byte
  80.     TakeByte$ = Byte
  81.  

this code example creates a txt file containing the example sentence posted by you here and terminated by CF+LF, if file doesn't exist and then go on to read byte for byte and translate the sentence in single fields until the checksum.
Naturally output is on the screen and the fields are in string so you can get value converting string to number using the right variable on the basis of the nature of the number got from sentence.

Thanks to read and try

PS Please don't rem CONST activated and don't unrem CONST deactivated... CHR$ is a internal function and QB64 doesn't like for now to use functions with CONST except some type of color functions if used in a choose manner