Shop OBEX P1 Docs P2 Docs Learn Events
String Parsing/Converting — Parallax Forums

String Parsing/Converting

g3cwig3cwi Posts: 262
edited 2012-03-05 12:47 in Propeller 1
Dear all

I am using the GPS_IO_MINI to extract data from the NMEA information from my GPS. I need to use the velocity and GPS fix quality as numbers. A first attempt failed after which it occured to me that they were probably extracted as strings. First question - is that right?

If they are strings, I need to convert them into decimals. One is an integer (quality), the other is not (velocity). In practice I only need the integer part of the velocity though. Looking around I have found lots of objects that can turn strings into decimals but (newbie alert) they all "return a pointer" and I don't know how to use them. So the second question is where can I find a gentle walkthrough description of using methods that "return a pointer"? If you assume that I have no more programming knowledge than the average Cocker Spaniel you will have the level just right!

Oh and I also need some advice on just getting the integer part of the velocity out of the string!

Time for breakfast.

Regards from rainy England

Richard

Comments

  • kuronekokuroneko Posts: 3,623
    edited 2012-03-04 01:48
    Maybe this gets you started http://forums.parallax.com/showthread.php?138045-A-Simple-GPS-Problem. As for returning a pointer, that would be the wrong kind of method/object as you want a number from a string.
  • g3cwig3cwi Posts: 262
    edited 2012-03-04 02:45
    Thanks. I have included the Numbers object and have tried:


    value := numbers.FromStr(gps.speed, numbers#DEC)

    This returns nothing at all so I am still puzzled.

    Regards

    Richard
  • kuronekokuroneko Posts: 3,623
    edited 2012-03-04 03:01
    What about a minimal test case (or we're here all day/night)?
  • g3cwig3cwi Posts: 262
    edited 2012-03-04 03:06
    I rather thought that was the minimal test case! I am happily using the NMEA string for other things but now I need to do some calculations using the data.

    As I mentioned, I'm new to this...

    Regards

    Richard
  • kuronekokuroneko Posts: 3,623
    edited 2012-03-04 03:16
    OK, so what do you get for serial.str(gps.time) (serial being your PC debug connection)?

    Next would be to check serial.hex(gps.speed, 8), if that value is NULL then it's not available, otherwise (not equal NULL) what does serial.str(gps.speed) report?
    repeat
        waitcnt(clkfreq + cnt)
        if temp := gps.speed
          serial.str(temp)
        else
          serial.str(string("not available", 13))
    
  • g3cwig3cwi Posts: 262
    edited 2012-03-04 03:28
    gps.speed returns "0.00" and gps.valid returns "A".

    !5315.80N/00208.80W-0.00 Knots A

    I used the numbers object and then added 120 to the "speed" and "333" to the valid, this gave:

    <0x06>5<0xfa><0x80>F Knots 0N/

    where <0x06>5<0xfa><0x80>F is the speed and;

    0N/ is the valid.

    I am debugging over an APRS network and clearly it can't interpret these values as decimal numbers so something's wrong!

    Regards

    Richard
  • kuronekokuroneko Posts: 3,623
    edited 2012-03-04 03:50
    Quick and dirty. Not seeing your code makes it a bit tricky to spot what could be wrong.
    CON
      _clkmode = XTAL1|PLL16X
      _xinfreq = 5_000_000
              
    OBJ
     fds[2] : "FullDuplexSerial"
     GPS    : "GPS_IO_mini"
     n      : "Numbers"
    
    VAR
      long  stack[32]
      
    Pub GPS_Time | value, valid, s_addr
    
      cognew(helper, @stack{0})                             ' fake GPS emitter
      
      GPS.start(1, 9600)
      fds{0}.start(31, 30, %0000, 115200)
    
      repeat
        if valid := GPS.valid                               ' get valid state
          fds{0}.str(valid)                                 ' valid
          fds{0}.tx(32)
        else
          fds{0}.str(string("- "))                          ' not valid
        if s_addr := GPS.speed
          fds{0}.str(s_addr)                                ' print as string
          fds{0}.tx(32)
          value := n.FromStr(s_addr, n#DEC)                 ' convert to decimal
          fds{0}.dec(value)                                 ' print as decimal
          fds{0}.tx(13)
        else
          fds{0}.str(string("not available", 13))
        waitcnt(clkfreq + cnt)
    
    PRI helper
    
      fds[1].start(2, 1, %0000, 9600)
      repeat
        waitcnt(clkfreq*3 + cnt)
        fds[1].str(string("$GPRMC,081836,A,3751.6565,S,14507.3654,E,[COLOR="red"]010.0[/COLOR],360.0,130998,011.3,E*62", 13))
        
    DAT
    
    With the code above I send a fake GPS string every 3 seconds and get this output:
    - not available
    - not available
    - not available
    A 010.0 10
    A 010.0 10
    A 010.0 10
    ...
    
    Note that GPS.speed returns an address to a string. This string must not be manipulated in any way. After convertion you'll get a single long value which is all yours.
  • g3cwig3cwi Posts: 262
    edited 2012-03-04 03:59
    Brilliant - and much appreciated. I will experiment!

    Regards

    Richard
  • g3cwig3cwi Posts: 262
    edited 2012-03-04 06:39
    Hair tearing out time. Taking the advice above and using a similar format for the code, I have modified my code to use what I had hoped would be decimal values for gps.valid and gps.speed.
    CON
    
      _clkmode      = xtal1 + pll16x
      _xinfreq      = 5_000_000
    
      out   =       1  'useful definitions
      in    =       0
      on    =       1
      off   =       0
    
      'AX25 Modem Definitions
      XMTPIN = 27
      PTTPIN = 25
      TX_LVL = 10
    
      'LED PORT
      LED = 22
    
      'GPS Config
      GPS_RX   = 23    'NMEA data sent to this port from GPS
      GPS_Baud = 9_600 'Default for most GPS modules
    
      
    OBJ
    
      mdm   :  "ax25"        'generates AX25 packet
    
      gps   :  "GPS_IO_mini" 'Extracts data from GPS NMEA string
    
      Num   :  "Numbers"     'Used to convert strings into decimals
    
      Debug :  "Debug_LCD"
    
    
    VAR
    
      byte packet[255]
      byte packetcount
      LONG TX_DELAY 'used for timing
      Long valid_dec, speed_dec, speed_addr, valid_addr 'used in string to dec routine
    
    PUB main
    
        Debug.init(0, 9_600, 4) 'Pin 0, 9_600 baud, 4 lines
        Debug.backlight (true)
        Debug.
        dira~~   'sets all pins to outputs - this help avoid problems with RF
        dira[GPS_RX] := in   'GPS Port set to input
        
     
        mdm.start_simple(mdm#NONE, XMTPIN, PTTPIN, TX_LVL) 'Start AX25
        gps.start(GPS_Rx, GPS_Baud) 'Start GPS
    
        
        Repeat 5 'Boot indicator
        
          led_(on) 
          waitcnt((clkfreq/10)+cnt) 
          led_(off)
          waitcnt((clkfreq/10)+cnt) 
    
        repeat 'this is the main loop
        
          valid_addr := gps.valid 'get address of gps.valid
          valid_dec  := num.FromStr(valid_addr, num#DEC) 'convert to decimal
        
            if valid_dec > 0  'Don't send invalid packets
             
              led_(off) 
          
              clearPacket
    
              fillGPSPacket
    
              mdm.createPacket(@packet, 1)  '1 means no path included , 0 means full path
         
              mdm.sendPacket
    
              led_(on) 'Packet dispatched
              
              waitcnt((clkfreq/3)+cnt)
          
              led_(off)
    
              TX_DELAY := 120 'initialise TX hold off (seconds)
    
              Repeat until TX_DELAY < 1 'simple adaptive delay
              
                'Conversion from string to decimal
                speed_addr := gps.speed  'get address of gps.speed
                speed_dec  := Num.FromStr(speed_addr, Num#DEC) 'turn string into decimal
                speed_dec  := speed_dec * 100 'multiply by 100 to make integer
              
                IF speed_dec > 2500 'in Knots = 29MPH
                
                 TX_DELAY := TX_DELAY - 5 'keep reducing the delay if travelling fast
    
                waitcnt( clkfreq + cnt ) 'Wait 1 second
                
                TX_DELAY := TX_DELAY - 1 'Decrement delay
    
    
    
    
    PRI fillTestPacket
    
          addToPacket(string("!5315.60N/00207.70W-"), 20)
          addToPacket(string("***TEST***"), 10)
          'This allow remote debugging via APRS!
          addToPacket(gps.latitude, 7)
          addToPacket(gps.N_S, 1) 
          addToPacket(string("/"), 1)
    
          addToPacket(gps.longitude, 8)
          addToPacket(gps.E_W, 1) 
          addToPacket(string("-"), 1)
          
          addToPacket(string("Propellor Test 12345"), 20)
         
          addToPacket(0,1)          'end of packet
    
    PRI fillGPSPacket
    
          addToPacket(string("!"), 1)
          addToPacket(gps.latitude, 7)
          addToPacket(gps.N_S, 1) 
          addToPacket(string("/"), 1)
    
          addToPacket(gps.longitude, 8)
          addToPacket(gps.E_W, 1) 
          addToPacket(string("-"), 1)
          
          valid_addr := gps.valid
          valid_dec  := num.FromStr(valid_addr, num#DEC)
          valid_dec  := valid_dec + 333
          
          speed_addr := gps.speed
          speed_dec  := num.FromStr(speed_addr, num#DEC)
          speed_dec  := speed_dec * 100
          speed_dec  := speed_dec + 120
                
          'Your data payload goes here
          addToPacket(string("APRS implemented by G3CWI. "), 27)
          addToPacket(string(" Test data follows: "), 20)
          if speed_dec > 119
            addToPacket(string("Speed was 120"),13)
            
          addToPacket(string(" Knots "), 7)
          if valid_dec > 332
            addToPacket(string("Valid was 333+"),14)
           
          addToPacket(0,1)          'end of packet
    
    PUB led_(onoff)
    
          outa[led] := onoff
    
    PUB addToPacket(straddr, length) | pointer        'no zeros may be added, except at the end!
    
        pointer := straddr
    
        repeat length
           if (packetcount<256)
             packet[packetcount++] := byte[pointer++]
            
    
    PUB clearPacket | i
    
        repeat 255
            packet[i] := 0
            i++
            packetcount := 0
    
    

    This fails to work as hoped. I am really finding this whole address and content thing most confusing.

    Any ideas?

    Regards

    Richard
  • g3cwig3cwi Posts: 262
    edited 2012-03-04 12:44
    Still stuck. Spent all afternoon experimenting and getting nowhere.

    I want to change a floating point number into a string.

    FloatString has the perfect subroutine:

    FloatToFormat(single, width, dp) : stringptr

    However, I have been unable to figure out how to use it - the problem being that I don't understand
    a) how to get back the string pointer and then
    b) how to use it to get the string back.

    Assuming my number is stored so that:

    test := 123.456

    I then tried combinations of:

    test := fs.Floattoformat (test, 6, 2)
    Debug.str (test)

    I tried sprinkling @ signs around but still no joy. I am clearly mis-understanding the whole "address", Pointer, string thing.

    Help appreciated.

    Regards

    Richard
  • Mike GMike G Posts: 2,702
    edited 2012-03-04 13:40
    I'm not sure what is tripping you up. If it's going from a string to numbers or numbers to strings.

    Strings are encoded (we're dealing with ASCII encoding) byte arrays where each byte represents a character. Strings end with a zero. Called zero or null terminated.

    This is incorrect syntax
    test := 123.456
    

    This is correct syntax
    bytemove(@buff, string("123.456"), strsize(string("123.456")))
    


    Hopefully this example helps a bit. This is the first time I used Float32. Someone will correct me if I did not use the object correctly.
    CON
        _clkmode = xtal1 + pll16x
        _xinfreq = 5_000_000
    
    DAT
      buff  byte $0[10]
        
    OBJ
      pst    : "Parallax Serial Terminal"
      f32    : "Float32"
    
      
    PUB Main | integer, mantissa, ans
      pst.Start(115_200)
      f32.Start
      waitcnt((clkfreq / 1_000 * 1_000) + cnt)
    
      ' 2 * 3.1416 = 6.2832
      integer := 3 * 2
      mantissa := f32.FFloat(1416)
      mantissa := f32.FMul(mantissa, 2)
    
      'Write 6.2832
      pst.dec(integer)
      pst.char($2E) ' .
      pst.dec(mantissa)
      pst.char($0D) ' CR
      
      'Write a string
      bytemove(@buff, string("123.456"), strsize(string("123.456")))
      pst.str(@buff)
      pst.char($0D)  ' CR
    
  • g3cwig3cwi Posts: 262
    edited 2012-03-04 13:48
    Hi Mike

    Thanks for your interest. The number I have is of the form 123.456 and is a floating point decimal. Basically I need to round it to 0 decimal places (i.e. 123) and then use it as a string. That was the reason for looking at theFloattoFormat method from the FloatString Object. However as I don't really understand the terminology I don't know how to recover the pointer and how to use it to recover the string itself.

    Regards

    Richard
  • SteveWoodroughSteveWoodrough Posts: 190
    edited 2012-03-04 15:16
    Richard,
    I'm not much further along the trail than you are but I think I just walked your path last week. I've attached my sloppy code that is a work in progress. If I understand your issue correctly, essentially what you need to do is apply the Numbers object. I give an example in the code below. If I'm not mistaken, if you bring in a decimal number into the Prop, say 123.45, you will by default get 123 . I hope I'm not being captain obvious and this helps you out.

    Point of order to the experts out there: I noticed that the object GPS_IO_mini does NOT have a STOP method. Should it by convention have a Stop method and that Stop method be part of the Start method?
    Regards,
    Steve Woodrough

    {**************************************************
    *      GETTING GPS DATA 4 MARCH 2012              *
    *                                                 *
    ***************************************************
    THIS PROGRAM DEMONSTRATES GETTING GPS DATA, PARSING AND CONVERTING STRINGS TO NUMBERS AS WELL
    AS SENDING THE DATA TO EITHER THE PST OR LCD.
       
    }
    CON
     _clkmode        = xtal1 + pll16x
     _xinfreq        = 5_000_000
    
     RxPin = 1
       CR = 13                                               ' ASCII <CR>
       LF = 10                                               ' ASCII <LF>
    
    {  LCD ASCII Commands
    8 Cursor left
    9 Cursor right
    10 Cursor down (bottom line will wrap to top line)
    12 Clears the display
    13 goes to next line
    21 Turns display off
    22 Turns Display ON
    128 - 143 along line 0
    148 -163 along line 1
    }
    
    
              
    OBJ
     ss     : "Simple_Serial"
     fds    : "FullDuplexSerial"
     GPS    : "GPS_IO_mini"
     n      : "Numbers"
    VAR
    long Time, ShortTime
      
    Pub Main
    
        GPS_LCD                     'This sould display the GPS data to the LCD.  
    
    
    Pub GPS_LCD
    GPS.start(15,9600)               'Starts the GPS data parser to Rx data at 9600
    
    fds.start(-1,4,1,19_200)   ' Data to LCD via FDS (No Rx Pin, Tx on Pin 4, Mode 0 or 1, 19_200 Baud)     
     FDS.tx(22)                  'Turn LCD ON   
     waitcnt(clkfreq/20+cnt)
     FDS.tx(12)                  'Clears screen  
     
    'fds.start(31,30,0,57600)        'Sending data to PST via FDS   
    
    repeat
       fds.tx(12)                  'Clears screen
       fds.tx(128)                 'move cursor
       Display_Longitude 
       fds.tx(140)                 'move cursor
       Display_Speed
       
       fds.tx(148)                 'move cursor
       Display_Latitude
       fds.tx(160)
       Display_Heading
       
       waitcnt(clkfreq/2+cnt)
       
    Pub Display_Longitude
    
       fds.str(GPS.longitude)            'Transmits string pointer
        
    Pub Display_Latitude
    
        fds.str(GPS.latitude)            'Transmits string pointer
        
    Pub Display_Heading
    
        fds.str(GPS.Heading)            'Transmits string pointer
    
    Pub Display_Speed
    
        fds.str(GPS.Speed)            'Transmits string pointer    
       
    Pub Display_Time
        
        fds.str(String("GPS 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
    
    Pub GPS_String_to_PST|x                'This routine works just fine and sends the NMEA strings to the PST
     ss.init(15,-1,9600)             'Message in on pin 15, no TX (-1) and 9600 baud 
      
     fds.start(31,30,0,57600)       'Sending data to PST must be turned off to use VP terminal 
      
     repeat
       
       x:=ss.rx                     'Receive a byte on pin 3 from the GPS
    
      fds.tx(x)                    'Transmits data to PST works great w/o VP active 
      
    
    
    
    
  • kuronekokuroneko Posts: 3,623
    edited 2012-03-04 15:29
    g3cwi wrote: »
    Hair tearing out time. Taking the advice above and using a similar format for the code, I have modified my code to use what I had hoped would be decimal values for gps.valid and gps.speed.
    ...
    This fails to work as hoped.
    What did you expect to happen and what does happen? Note that the valid indicator (valid_addr) can be NULL so converting this will give you unexpected results. Furthermore, the returned string is "A" which can't be converted to decimal. So AFAIAC looking at NULL/not NULL should be enough for a valid indication. The speed retrieval looks OK apart from a NULL check (not sure if that field is mandatory).
  • g3cwig3cwi Posts: 262
    edited 2012-03-05 12:47
    Thanks all for your patience. I am now up and running. Part of the problem was my ineffective debug facilities. I now have a serial 4 x 20 LCD so I can actually see what is happening.

    'Till the next problem

    regards

    Richard
Sign In or Register to comment.