Shop OBEX P1 Docs P2 Docs Learn Events
A Simple GPS Problem — Parallax Forums

A Simple GPS Problem

SteveWoodroughSteveWoodrough Posts: 190
edited 2012-03-30 11:13 in Propeller 1
I'm trying to get usable data parsed out of my GPS module, and I'm attempting to use a program borrowed from The Official Guide, Chapter Nine. For the purpose of the example I’m trying to display the GPS time value on the PST. I chose time since it changes and does not require me to move around to confirm an updated value.

My routine: GPS_String_to_PST works just fine, so I know the GPS works.

However, my routine: GPS_Time sends a bunch of repeated gibberish to the PST.

I'm sure this problem has been soleved a hundred times before, so if anybody has SIMPLE, one cog example where I can write Heading:=GPS.heading, I would apprciate it very much.


Thank You!
Steve

Comments

  • kuronekokuroneko Posts: 3,623
    edited 2012-02-18 17:44
    Currently you send the LSB of the time value to the terminal. Try fds.dec(time) instead.
  • SteveWoodroughSteveWoodrough Posts: 190
    edited 2012-02-19 09:15
    Thank You. Using FDS.dec(time) improved the text to show a 4 digit display vs nonsense. However, I'm still missing an important detail. The displayed value is only 4 digits and should be HHMMSS and is not incrementing over time as would be expected.
    Thank You!
    Steve


    '' *****************************
    '' GPS routines
    ''  (c) 2007 Perry James Mole
    ''  [email]pjm@ridge-communications.ca[/email]
    '' *****************************
    
    '' 05-13-2009 - JMH - Modified to accept input pin number assignments
    '' 05-14-2009 - JMH - Added functions to seperate the deg/min from lat/long
    
    
    ' PVH Comment - Excellent small GPS reader routines.
    ' $GPRMC  Recommended minimum data                 ie: $GPRMC,081836,A,3751.6565,S,14507.3654,E,000.0,360.0,130998,011.3,E*62
    ' $GPGGA  GPS Fix Data                             ie: $GPGGA,170834,4124.8963,N,08151.6838,W,1,05,1.5,280.2,M,-34.0,M,,,*75
    ' $PGRMZ  eTrex proprietary barametric altitude ft ie: $PGRMZ,453,f,2*18
    
    CON
    
      CR = 13                                               ' ASCII <CR>
      LF = 10                                               ' ASCII <LF>
      serXmit   = 0                                         ' Serial Transmit for UART - not used
      
    VAR  
       long gps_stack[10] 
       byte GPRMCb[68],GPGGAb[80],PGRMZb[40]   
       long GPRMCa[20],GPGGAa[20],PGRMZa[20]   
    
    
       byte gps_buff[80],Rx',cksum
       long cog,cptr,ptr,arg,j
       long Null[1]
       byte latlong_buf[10]
       
    OBJ
      uart :  "FullDuplexSerial_mini"
    
    PUB start(serRecvPin, baudRate) : okay
    
    '' Starts uart object (at baud specified) in a cog
    '' -- returns false if no cog available
    
      okay := uart.start(serRecvPin,serXmit,0,baudRate)
      return cog := cognew(readNEMA,@gps_stack) + 1 
    
    PUB readNEMA
      Null[0] := 0
      repeat
       longfill(gps_buff,20,0)
       repeat while Rx <>= "$"      ' wait for the $ to insure we are starting with
         Rx := uart.rx              '   a complete NMEA sentence 
       cptr := 0
    
       repeat while Rx <>= CR       '  continue to collect data until the end of the NMEA sentence 
         Rx := uart.rx              '  get character from Rx Buffer
         if Rx == ","
           gps_buff[cptr++] := 0    '  If "," replace the character with 0
         else
           gps_buff[cptr++] := Rx   '  else save the character   
       
       if gps_buff[2] == "G"             
         if gps_buff[3] == "G"            
           if gps_buff[4] == "A"            
               copy_buffer(@GPGGAb, @GPGGAa)
    
       if gps_buff[2] == "R"             
         if gps_buff[3] == "M"            
           if gps_buff[4] == "C"           
               copy_buffer(@GPRMCb, @GPRMCa)
                       
       if gps_buff[0] == "P"
        if gps_buff[1] == "G"  
         if gps_buff[2] == "R"
          if gps_buff[3] == "M"  
           if gps_buff[4] == "Z"
               copy_buffer(@PGRMZb, @PGRMZa)
                    
    pub copy_buffer ( buffer,args)
             bytemove(buffer,@gps_buff,cptr) '  copy received data to buffer
             ptr := buffer
             arg := 0
             repeat j from 0 to 78           ' build array of pointers
              if byte[ptr] == 0               ' to each
                 if byte[ptr+1] == 0           ' record
                    long[args][arg] := Null     ' in 
                 else                            ' the
                    long[args][arg] := ptr+1     ' data buffer
                 arg++
              ptr++
              
    ' now we just need to return the pointer to the desired record
              
    pub altitude
       return PGRMZa[0]
    
    pub valid
       return GPRMCa[1]
          
    pub speed
       return GPRMCa[6]
    
    pub heading
       return GPRMCa[7]
       
    pub date
       return GPRMCa[8]
        
    pub GPSaltitude
       return GPGGAa[8]
    
    pub time
       return GPGGAa[0]
    
    pub latitude
       return GPGGAa[1]
    
    pub latitude_degpart | byte_ptr
       byte_ptr := GPGGAa[1]
       latlong_buf[0] := byte[byte_ptr++]
       latlong_buf[1] := byte[byte_ptr++]
       latlong_buf[2] := 0
       return @latlong_buf
    
    pub latitude_minpart | byte_ptr, i, numchars
       byte_ptr := GPGGAa[1] + 2
       numchars := strsize(byte_ptr)
       repeat i from 0 to (numchars-1)
         latlong_buf[i] := byte[byte_ptr++]
    
       'Null terminate
       latlong_buf[i] := 0     
    
       return @latlong_buf
        
    pub N_S
       return GPGGAa[2]
         
    pub longitude
       return GPGGAa[3]
    
    pub longitude_degpart | byte_ptr
       byte_ptr := GPGGAa[3]
       latlong_buf[0] := byte[byte_ptr++]
       latlong_buf[1] := byte[byte_ptr++]
       latlong_buf[2] := byte[byte_ptr++]   
       latlong_buf[3] := 0
       return @latlong_buf
    
    pub longitude_minpart | byte_ptr, i, numchars
       byte_ptr := GPGGAa[3] + 3
       numchars := strsize(byte_ptr)
       repeat i from 0 to (numchars-1)
         latlong_buf[i] := byte[byte_ptr++]
    
       'Null terminate
       latlong_buf[i] := 0
       
       return @latlong_buf   
        
    pub E_W
       return GPGGAa[4]
    
    pub satellites
       return GPGGAa[6]
        
    pub hdop
       return GPGGAa[7]
       
    'pub vdop
    '   return GPGSAa[14]
    
  • kuronekokuroneko Posts: 3,623
    edited 2012-02-19 16:29
    OK, I just had a closer look. What this method returns is in fact a pointer to a part of the GPS string. In this case a fds.str(time) should do (untested). Whatever happened to (even brief) comments as to what the methods actually do?

    You may want to protect yourself against returned NULL pointers which simply means this part isn't available (not necessarily the time entry).

    Also, when you post code please wrap it in [noparse]
    
    [/noparse] tags.                        
  • SteveWoodroughSteveWoodrough Posts: 190
    edited 2012-02-19 16:49
    That worked. See, I told you it was simple! Thank You! Thank you also for the advice on bracketing code. I've not posted here for quite some time, and had forgotton the process. I fixed the earlier post.

    I agree about code comments. As much as I appreciate the Chapter 9 code being posted to the FTP site the occasional lack of comments leaves guys like me in the dark.

    I'll play with this tonight, but if I want the number values to the left of the decimal all I need to do is declare:

    Time:= fds.str(time)


    Likewise for the heading:
    Heading:=fds.str(Heading)

    Best Regards,
    Steve
  • kuronekokuroneko Posts: 3,623
    edited 2012-02-19 16:55
    I'll play with this tonight, but if I want the number values to the left of the decimal all I need to do is declare:

    Time:= fds.str(GPS.time)
    What do you expect to happen here? The time method returns a pointer to a (sub)string. fds.str() simply prints that string to whatever is connected. The returned value of the str method is in fact always 0. If you want a number based on the string you'll have to convert the result of GPS.time instead.
  • SteveWoodroughSteveWoodrough Posts: 190
    edited 2012-02-19 17:15
    Ok, we're close. What I have below works, to the extent that the time data is going to the PST, however, you are saying that the numercal value of time in this case is in fact 0 since the value shown on the PST is simply a string representation of the desired value.

    What is the best way to convert the string value to true number?

    Thank You
    Steve

    {**************************************************
    *      GETTING GPS DATA                           *
    *                                                 *
    ***************************************************
    The intent of this program is to practice extracting GPS Data from
    a data stream to PIN 1 of the Propeller at 9600 baud.
       
    }
    CON
     _clkmode        = xtal1 + pll16x
     _xinfreq        = 5_000_000
    
     RxPin = 1
       CR = 13                                               ' ASCII <CR>
       LF = 10                                               ' ASCII <LF>
              
    OBJ
      fds    : "FullDuplexSerial"
     GPS    : "GPS_IO_mini"
    VAR
    long Time
      
    Pub Main
    
        GPS_Time                     'This sould display the GPS time data to the PST.  
    
    
    Pub GPS_Time
    GPS.start(1,9600)               'Starts the GPS data parser to Rx data at 9600
    
    fds.start(31,30,0,57600)        'Sending data to PST    
    
    repeat
      Time:=GPS.time                 'Setting global variable time equal to gps.time  
      fds.str(time)                  'Transmits time data to PST works great w/o VP active  
                        
        fds.tx(CR)
        fds.tx(LF)
       ' waitcnt(clkfreq+cnt)
    
    
    
  • kuronekokuroneko Posts: 3,623
    edited 2012-02-19 17:32
    OBJ
      n: "Numbers"
    
    PUB null | addr, value
    
      addr  := string("204800")
      value := n.FromStr(addr, n#DEC)
    
    There are other conversion objects out there but this one comes with the PropTool so it's available. addr is equivalent to GPS.time.
  • kuronekokuroneko Posts: 3,623
    edited 2012-02-19 23:17
    Steve, the GPS_IO_mini object exhibits a minor issue when it attempts to clear the GPS buffer. It should really use the address of the buffer rather than a value stored at the beginning. Fortunately the fill length is set to 0 (parameters swapped) so it doesn't do any harm (but doesn't clear the buffer either).
    PUB readNEMA
      Null[0] := 0
      repeat
       [COLOR="red"]longfill(gps_buff,20,0)[/COLOR]
       repeat while Rx <>= "$"      ' wait for the $ to insure we are starting with
         Rx := uart.rx              '   a complete NMEA sentence 
       cptr := 0
       ...
    
    The line in question should read longfill(@gps_buff,0,20) and just to be on the safe side should really be a bytefill(@gps_buff,0,80). ATM the byte buffer is long aligned so the longfill will work. Alternatively you could simply null-terminate the received string which would void the requirement for clearing the buffer.

    The copy_buffer method looks slightly supicious as well as it scans 79 elements for arrays which are 68/80 and 40 in size.
  • SteveWoodroughSteveWoodrough Posts: 190
    edited 2012-02-20 03:56
    Kuroneko,
    Thank You for your prompt response. I think we can call this one solved, if I can just figure out how to re-label the post.

    I will make the code changes you suggest to the Read_NEMA method.

    Below is the code sample in the hopes that it helps the next person out.

    
    {**************************************************
    *      GETTING GPS DATA                           *
    *                                                 *
    ***************************************************
    The intent of this program is to practice extracting GPS Data from
    a data stream to PIN 1 of the Propeller at 9600 baud.
       
    }
    CON
     _clkmode        = xtal1 + pll16x
     _xinfreq        = 5_000_000
    
     RxPin = 1
       CR = 13                                               ' ASCII <CR>
       LF = 10                                               ' ASCII <LF>
              
    OBJ
     ss     : "Simple_Serial"
     fds    : "FullDuplexSerial"
     GPS    : "GPS_IO_mini"
     n      : "Numbers"
    VAR
    long Time, ShortTime
      
    Pub Main
    
        GPS_Time                     'This sould display the GPS time data to the PST.  
    
    
    
    Pub GPS_Time
    GPS.start(1,9600)               'Starts the GPS data parser to Rx data at 9600
    
    fds.start(31,30,0,57600)        'Sending data to PST via FDS   
    
    repeat
    
        fds.str(String("GPS Time as String = "))
        fds.str(GPS.time)            'Transmits string pointer to PST displaying the current time                    
        fds.tx(CR)
        fds.tx(LF)
    
        fds.str(String("GPS Time as Variable 'TIME' = ")) 'Converts the string value of time to a 10 digit decimal 
        Time := n.FromStr(GPS.time,10)                     'Using BASE 10 format
        fds.dec(Time)
    
        
        ShortTime:=Time-5                  'Just playing around and proving to myself that I actually have a real number
        fds.str(String(" ShortTime = "))   'Of course when "Time" ends with 00 the value of ShortTime ends in 95 but that
        fds.dec(ShortTime)                 'was not the point of the exercise
    
                        
        fds.tx(CR)
        fds.tx(LF)
         
        waitcnt(clkfreq+cnt)        'wait a second just to keep too much data from going to the PST
    
    
    
  • kuronekokuroneko Posts: 3,623
    edited 2012-02-20 05:04
    I think we can call this one solved, if I can just figure out how to re-label the post.
    Edit your original post in advanced mode then change the prefix.
  • PerryPerry Posts: 253
    edited 2012-03-30 11:13
    My apologies to all this was my fist SPIN effort !!


    Perry

    kuroneko wrote: »
    Steve, the GPS_IO_mini object exhibits a minor issue when it attempts to clear the GPS buffer. It should really use the address of the buffer rather than a value stored at the beginning. Fortunately the fill length is set to 0 (parameters swapped) so it doesn't do any harm (but doesn't clear the buffer either).
    PUB readNEMA
      Null[0] := 0
      repeat
       [COLOR="red"]longfill(gps_buff,20,0)[/COLOR]
       repeat while Rx <>= "$"      ' wait for the $ to insure we are starting with
         Rx := uart.rx              '   a complete NMEA sentence 
       cptr := 0
       ...
    
    The line in question should read longfill(@gps_buff,0,20) and just to be on the safe side should really be a bytefill(@gps_buff,0,80). ATM the byte buffer is long aligned so the longfill will work. Alternatively you could simply null-terminate the received string which would void the requirement for clearing the buffer.

    The copy_buffer method looks slightly supicious as well as it scans 79 elements for arrays which are 68/80 and 40 in size.
Sign In or Register to comment.