Shop OBEX P1 Docs P2 Docs Learn Events
OBEX GPS NMEA Parser question — Parallax Forums

OBEX GPS NMEA Parser question

Dwayne DibbleyDwayne Dibbley Posts: 63
edited 2011-07-24 12:33 in Propeller 1
i am playing with Ryan David's GPS NMEA Parser from the OBEX.

I have my GPS connected to a quickstart board ( GPS coming in on P8 ) the default script just echo's the course to the terminal window. however this data does not seem correct see output from terminal:
84201886984201886984201886984201886984201886984201886984201886984201886984201886984201
8869842018869842018869842018869842018869842018869

any ideas as to why 842018869 is suppose to be my course?

is there an easyway to see if the NMEA data comming in is correct ie instead of echo'ing the course can i dump out the last NMEA string received?

sample from GPS plugged directly into terminal is as follows:
$GPGGA,184822.638,5023.3762,N,00354.4517,W,1,05,2.9,83.6,M,53.2,M,,0000*70

$GPRMC,184822.638,A,5023.3762,N,00354.4517,W,000.0,000.0,200711,,,A*70

Thanks

Comments

  • LeonLeon Posts: 7,620
    edited 2011-07-21 11:19
    There is a checksum you can use to check the data integrity. It's rather simplistic, but is better than nothing.
  • Duane DegnDuane Degn Posts: 10,588
    edited 2011-07-21 12:04
    Dwayne,

    Does Ryan's parser output the data as a floating point number? That's my guess.

    It would be helpful if you provided a link to the object.

    Duane
  • Dwayne DibbleyDwayne Dibbley Posts: 63
    edited 2011-07-21 12:06
    Duane Degn wrote: »
    Dwayne,

    Does Ryan's parser output the data as a floating point number? That's my guess.

    It would be helpful if you provided a link to the object.

    Duane

    heres the OBEX:

    http://obex.parallax.com/objects/579/

    Thanks
  • Phil Pilgrim (PhiPi)Phil Pilgrim (PhiPi) Posts: 23,514
    edited 2011-07-21 12:25
    842018869 in hex is 32303035. This represents the ASCII character string "5002", assuming little-endian form. Does that make more sense?

    -Phil
  • Dwayne DibbleyDwayne Dibbley Posts: 63
    edited 2011-07-21 12:34
    842018869 in hex is 32303035. This represents the ASCII character string "5002", assuming little-endian form. Does that make more sense?

    -Phil

    from the spin code :
    Debug.dec(long[pointer])                            'Echo course once a second
    

    so I was hoping it would be a 0-360 bearing assuming that's what is meant by course?

    Thanks
  • Phil Pilgrim (PhiPi)Phil Pilgrim (PhiPi) Posts: 23,514
    edited 2011-07-21 12:41
    It could be that "2005" is the correct order and that it's to be interpreted as 200.5. I'm sure that further experimentation with your unit while moving in different directions will unravel the mystery.

    -Phil
  • Dwayne DibbleyDwayne Dibbley Posts: 63
    edited 2011-07-21 12:58
    just to be on the safe side the gps is 3.3V TTL UART so the RX will plug OK into one of the propeller io pins ok ( ie i don't need a ttl converter for it to work ) ?

    Thanks
  • UnsoundcodeUnsoundcode Posts: 1,532
    edited 2011-07-21 14:42
    Hi Dwayne, I have just began a small GPS project and wrote this small template for recieving RMC data. What I decided to do was parse the string into a DAT block, I have not coded every data item in there as yet but did include the Lat and Long and a simple routine that converts minutes to fraction of degrees. I attach the spin file not to encourage you to use it as your final but more as a comparison to what you have now. The display I get is what I would expect, the GPS unit is a 3.3v with it's ttl tx output connected directly to Prop P1.

    Jeff T.
  • Dwayne DibbleyDwayne Dibbley Posts: 63
    edited 2011-07-22 09:51
    Hi Dwayne, I have just began a small GPS project and wrote this small template for recieving RMC data. What I decided to do was parse the string into a DAT block, I have not coded every data item in there as yet but did include the Lat and Long and a simple routine that converts minutes to fraction of degrees. I attach the spin file not to encourage you to use it as your final but more as a comparison to what you have now. The display I get is what I would expect, the GPS unit is a 3.3v with it's ttl tx output connected directly to Prop P1.

    Jeff T.

    Jeff that works fine, so i can confirm the GPS works and is outputting the correct data. So back to Ryan David's spin project as the bulk of the object is in ASM i haven't a clue how its parsing the data :(

    the code " return @Course " in the GPSDriver, is that what is returning the data back to the main code? i have tride changing it to " return @Satellites " but i dont get anything

    Thanks
  • Dwayne DibbleyDwayne Dibbley Posts: 63
    edited 2011-07-22 10:35
    making progress, i looked at Ryan's DacPaq code and found his output string were:
    Debug.tx(long[GPSBuff] >> 24)                    'Output course
          Debug.tx((long[GPSBuff] >> 16) & %1111_1111)
          Debug.tx((long[GPSBuff] >> 8) & %1111_1111)
          Debug.tx(".")
          Debug.tx(long[GPSBuff] & %1111_1111)
    

    so replaced the original code with this for the serial echo and now it generates 069.0 which look better :)

    changing debug.dec to debug.tx ( is this just outputting plain text instead of a decimal number ? )

    but still what does ((long[GPSBuff] >> 16) & %1111_1111) actually do with the GPSBuff? ( do i need the %1111_1111? )

    Thanks again
  • Phil Pilgrim (PhiPi)Phil Pilgrim (PhiPi) Posts: 23,514
    edited 2011-07-22 11:02
    Dwayne,

    Yes, you need the %1111_1111 if you do it that way, since the "&" is used to isolate individual characters. You could just as easily do it this way:
    Debug.tx(byte[GPSBuff][3])
    Debug.tx(byte[GPSBuff][2])
    Debug.tx(byte[GPSBuff][1])
    Debug.tx(".")
    Debug.tx(byte[GPSBuff])
    

    This is what I was alluding to above with my comment about "5002" vs. "2005" being 200.5 degrees. The characters are being stored by the parser in reverse byte order (big endian form) for some reason.

    -Phil
  • TorTor Posts: 2,010
    edited 2011-07-23 03:55
    but still what does ((long[GPSBuff] >> 16) & %1111_1111) actually do with the GPSBuff? ( do i need the %1111_1111? )
    This shifts the value 16 bits to the right. What the '& %1111_1111' does is to mask off the bits that don't belong to the (now) lowest 8 bits. In C this is more often written like '>> 16 & 0xff' which means the same. Imagine you start with a value
    %10101010_00110011_11101110_11101100
    You intend to get that '00110011' to end up in the rightmost 8 bits.
    Shifted 16 bits to the right two things happens:
    1) The 16 bits to the left end up to the right,
    2) and because the 'long' is a signed integer the leftmost 1 is sign-extended. So you get:
    %11111111_11111111_10101010_00110011
    (if that leftmost bit hadn't been 1 those bits to the left would all be 0, not 1).
    Then you do '& %1111_1111' and end up with
    %00000000_00000000_00000000_00110011
    which is what you want.

    -Tor
  • Dwayne DibbleyDwayne Dibbley Posts: 63
    edited 2011-07-23 04:17
    Thanks for that Tor & Phil i think i am understanding it a little clearer now :)

    i am trying to loop while the gps noes not have a fix and flash a led with:
    dira[16] := 1                   ' Set P16 to output 
      
      repeat while ((long[GPSBuff+16] >> 16) & %1111_1111) = 0     'loop untill gps quality <> 0 
          outa[16] := 1               ' Set P16 high 
          waitcnt(clkfreq+cnt)       ' Wait a moment
          outa[16] := 0               ' Set P16 low
          waitcnt(clkfreq+cnt)       ' Wait a moment  
    
      outa[16] := 1               ' Set P16 high
    

    but i get unexpected end of line with repeat while ((long[GPSBuff+16] >> 16) & %1111_1111) = 0

    thanks again
  • kuronekokuroneko Posts: 3,623
    edited 2011-07-23 04:31
    Either drop the = 0 at the end (and use repeat until) or replace it with == 0 (a single = is just for CONstants).

    Incidentally, ((long[GPSBuff+16] >> 16) & %1111_1111) is equivalent to byte[GPSBuff+18]. Maybe it's easier to read.
  • Dwayne DibbleyDwayne Dibbley Posts: 63
    edited 2011-07-23 05:38
    Thanks for that, thats the problem coming from a windows vb world :)
  • Dwayne DibbleyDwayne Dibbley Posts: 63
    edited 2011-07-23 11:42
    JonnyMac wrote: »

    Thats awesome, and works a treat out of the box :)

    regarding:
    waitcnt((clkfreq >> 3) + cnt)                               ' refresh 8x per second
    

    what is the best way to run as 10x? usually i would use:
    waitcnt(clkfreq/10 + cnt)
    

    Thanks alot
  • JonnyMacJonnyMac Posts: 9,208
    edited 2011-07-23 11:59
    Thats awesome, and works a treat out of the box

    Glad you like it; I wrote that code.

    When multiplying or dividing by numbers that are power of 2 I use shifts, otherwise, you've got use * or /

    Note, though, that the usage above is approximate. If you want EXACTLY 10 iterations per second, use a synchronized loop
    t := cnt
      repeat
        ' loop code here
        waitcnt(t += clkfreq/10)
    

    The caveat is that your loop code must consume less than your loop timing, otherwise you will miss the waitcnt target and be stuck until cnt rolls back to it.
  • Dwayne DibbleyDwayne Dibbley Posts: 63
    edited 2011-07-23 12:29
    OK seems to be running now a 10hz. just a question is it possible to pull all the data back from one gps string in one go? i gather as each pub is called it could possible be reading the time and position from two nmea strings? I would need time stamp ( 00:00:00.000 ) to be from the same string as the position for accuracy? or is that what it is doing with the buffer?

    Thanks
  • JonnyMacJonnyMac Posts: 9,208
    edited 2011-07-23 12:50
    A background cog simply listens for strings and them moves them into the appropriate buffers which are used by the various calls. As there is a least a second (with the GPS modules I've used) before a buffer is overwritten you are probably getting what you want from the same string. You might create a sync point in your program that watches for a change in the time field before pulling the other data.
  • Dwayne DibbleyDwayne Dibbley Posts: 63
    edited 2011-07-23 13:29
    rough timing of the loop is taking 11-13 ms so plenty of room with 100 ms to play with at 10hz

    Thanks of to play some more.
  • Dwayne DibbleyDwayne Dibbley Posts: 63
    edited 2011-07-24 07:21
    JonnyMac wrote: »
    A background cog simply listens for strings and them moves them into the appropriate buffers which are used by the various calls. As there is a least a second (with the GPS modules I've used) before a buffer is overwritten you are probably getting what you want from the same string. You might create a sync point in your program that watches for a change in the time field before pulling the other data.

    How would I pull back the mm.ssss part for the gps ? i need to convert the NMEA formatted lat and long to decimal degrees hopefully like this dd + (mm.ssss/60) so i can calculate distances between positions.

    Thanks
  • JonnyMacJonnyMac Posts: 9,208
    edited 2011-07-24 09:33
    If you only want the mm.ssss part then you could copy the result of s_latitude or s_longitude and parse out the section you want. That said the n_ (return numeric result) methods convert string data to numeric form and may be doing the work you want. You need to spend a little more time studying the object and the method comments (also in the documentation for the object).

    Note that there is also a public method called str2dec that returns the numeric value of n characters in a string.

    For example....
    gps.s_latitude                                                ' move "ddmm.ssss X" to object result buffer
      gps.rslt_copy(@workbuf)                                       ' copy gps buffer to local buffer
    
      ' string to decimal
    
      degrees := gps.str2dec(@workbuf[0], 2)
      minutes := gps.str2dec(@workbuf[2], 2)
      seconds := gps.str2dec(@workbuf[5], 5) * 60 / 10_000
    

    Note that there are n_ methods to do the work above, but doing it as shown -- on a local buffer -- ensures that each field comes from the same instance of the GPS sentence.

    One thing you might consider doing is adding a simple counter for each GPS string that is incremented every time a new string is moved from the GPS input to the object working buffers. By monitoring this counter one could easily tell if the system has received a new GPS sentence.
  • Dwayne DibbleyDwayne Dibbley Posts: 63
    edited 2011-07-24 12:09
    with the n_lats sub:
    pub n_lats
    
    '' Latitude seconds as decimal value, 0..59
    
      s_latitude                                                    ' get latitude
      
      return str2dec(@gpsrslt[5], 4) * 60 / 10_000                  ' return seconds                                                    
    

    how does it trim the result to only two characters? ie 22 is returned from an original value of 3735 instead of 22.41 ?

    Thanks
  • JonnyMacJonnyMac Posts: 9,208
    edited 2011-07-24 12:25
    how does it trim the result to only two characters? ie 22 is returned from an original value of 3735 instead of 22.41 ?

    Perhaps you're unaware that the Propeller uses integer math, hence the 0.41 part gets truncated by the division by 10,000. Note, too, that the method comment explicitly states that it returns a decimal (perhaps I should change that to integer) value in the range of 0 to 59. You can do that calculation manually (as I showed you before) and divide by 100 instead of 10,000; this will return 2241 which is seconds expressed in 1/100ths units.

    There are PASM and Spin Floating Point objects that may be helpful in your project. You can find them in ObEx.
  • Dwayne DibbleyDwayne Dibbley Posts: 63
    edited 2011-07-24 12:33
    Thanks Jon for the quick reply again.
Sign In or Register to comment.