Shop OBEX P1 Docs P2 Docs Learn Events
Need a bit more help on Strings2 - Page 2 — Parallax Forums

Need a bit more help on Strings2

2»

Comments

  • charleyshfcharleyshf Posts: 165
    edited 2010-10-12 12:15
    Okay, I removed the @ from those lines, and added
    PST.dec (Y_Mean[0])
    PST.dec (Y_Mean[1])
    PST.dec (Y_Mean[2])
    PST.Str (String(13))

    The result I get is 575532

    and I also added in:
    Y_Mean[0] := Y_Mean[0] * 100
    Y_Mean[1] := Y_Mean[1] * 10
    MeanResult := Y_Mean[0] + Y_Mean[1] + Y_Mean[0]
    PST.dec (MeanResult)
    PST.Str (String(13))

    that result varies, but I get numbers like 456 or 370, but remember the sensor(blackfin) I am using is returning mean values of Y,U, and V

    So a snapshot I get right now is PST is:

    Sending vm command...
    ##vmean 85 128 126
    565332
    466
  • T ChapT Chap Posts: 4,223
    edited 2010-10-12 12:16
    Please see the edits above. Sorry, this example assumed there would always be 3 digits returned, let me tweak it.
  • charleyshfcharleyshf Posts: 165
    edited 2010-10-12 12:21
    Okay, I made the changes, and this is the result that I get now:

    Sending vm command...
    ##vmean 90 129 127
    574832
    162

    I double checked my sensor by hooking it directly up to my pc using a 3.3vdc usb-serial adapter, and the vmean as above is correct.
  • T ChapT Chap Posts: 4,223
    edited 2010-10-12 12:25
    hex 57 is ascii 9
    hex 48 is ascii 0
    hex 32 is a space " "


    So now you need to convert the ascii values to decimal, sum the total for each number you need and you are set. I forgot to mention that to sum the values, they must not be in ascii form. You now have to parse each digit that you receive as ascii, convert it to integer ( a simple method is to subtract 48 from the decimal value you have above to get the integer value). Space (hex 32) is the delimeter.

    57 - 48 = 9
    48 - 48 = 0

    Scan the array looking for the givens to find the starting point of your values to derive from the string:

    If the array will always begin the same with ##vmean, then you have to have a way to account for whether 1,2, or 3 ascii digits follow the initial space. This assumes that a 4th ascii digit is not possible.

    Crude idea to get you started:
    If BFinMean[9] == $32   'find the space, then work backwards to assemble your number
      Y_mean[0] := BFinMean[8] 
    If BFinMean[10] == $32
      Y_mean[0] := BFinMean[8] 
      Y_mean[1] := BFinMean[9] 
    If BFinMean[11] == $32
      Y_mean[0] := BFinMean[8] 
      Y_mean[1] := BFinMean[9] 
      Y_mean[2] := BFinMean[10]
     '' convert each value to an integer(-48), multiply as need to get 10's, 100's, then sum
     '' start scanning where you left off for the next space work backwards for the number
    
    

    You may find an object that will convert an ascii string like "222" to an integer that will save a lot of work.
  • Invent-O-DocInvent-O-Doc Posts: 768
    edited 2010-10-12 12:54
    Another strategy is to go through the comma delimited string and replace "," with 0. Then you assign addresses to each of your variables to refer to. That way, the original string is still used and you are not copying bytes to everywhere"
    So LONG String1 = address of first string, String2 = second, etc. You can even do an array of longs if the number of parameters in the string is large. Just a thought.
  • bdickensbdickens Posts: 110
    edited 2010-10-12 14:33
    Ok. This basically has 2 routines.

    1) BuildTestRecord is a test data generator. It actually creates a 2 record data buffer. I used this to make sure my parsing routines worked.
    2) ParseRecord takes the buffer and decodes it. Most of it is straight byte work. The endian stuff gets in the way of the numbers. So the line
    InVar[idx] := (InDataBuffer[JDX] << 8) + InDataBuffer[JDX+1]
    takes the fact that the bytes come in MSB and need to be LSB. So I shift.

    Many of the string libraries only work on "like" defined strings. They either have to be LONG strings, or byte strings. At the end of the day, I decided it was easier to manipulate the array of bytes, rather than try to manipulate strings.

    So take a look, tinker with it and I'll answer whatever I can.
  • charleyshfcharleyshf Posts: 165
    edited 2010-10-12 16:28
    Hi again,,

    Thank you!! I was so involved with getting this to work right it didn't even hit me that the output was in hex..... I am still figuring this all out.. Something that I am trying to figure out how to ask right, after all this is done and working, it will be great for debugging problems, however when things are normally working properly and I don't need to debug the output am I still going to have to do all of this?, my intention is to use the variables to set pan/tilt servos accordingly (tracking an orange ball for example, or even following an object).

    The array I am working on now will always start with the output being the same (##vmean )

    Nowto get back to getting this working right...
    Thank you again..
    T Chap wrote: »
    hex 57 is ascii 9
    hex 48 is ascii 0
    hex 32 is a space " "


    So now you need to convert the ascii values to decimal, sum the total for each number you need and you are set. I forgot to mention that to sum the values, they must not be in ascii form. You now have to parse each digit that you receive as ascii, convert it to integer ( a simple method is to subtract 48 from the decimal value you have above to get the integer value). Space (hex 32) is the delimeter.

    57 - 48 = 9
    48 - 48 = 0

    Scan the array looking for the givens to find the starting point of your values to derive from the string:

    If the array will always begin the same with ##vmean, then you have to have a way to account for whether 1,2, or 3 ascii digits follow the initial space. This assumes that a 4th ascii digit is not possible.

    Crude idea to get you started:
    If BFinMean[9] == $32   'find the space, then work backwards to assemble your number
      Y_mean[0] := BFinMean[8] 
    If BFinMean[10] == $32
      Y_mean[0] := BFinMean[8] 
      Y_mean[1] := BFinMean[9] 
    If BFinMean[11] == $32
      Y_mean[0] := BFinMean[8] 
      Y_mean[1] := BFinMean[9] 
      Y_mean[2] := BFinMean[10]
     '' convert each value to an integer(-48), multiply as need to get 10's, 100's, then sum
     '' start scanning where you left off for the next space work backwards for the number
    
    

    You may find an object that will convert an ascii string like "222" to an integer that will save a lot of work.
  • T ChapT Chap Posts: 4,223
    edited 2010-10-12 16:43
    If you plan to use the data that comes in in the form of an ascii string, then you will have to always convert it to an integer to be able to do any math with it. I don't know how you can control a servo for one axis with several digits in ascii form that don't mean anything to the servo until they are converted into an integer form.

    You wont need to use any pst parts of course as they are for debugging only, just turn it off if you don't need it.
  • Bobb FwedBobb Fwed Posts: 1,119
    edited 2010-10-12 17:16
    This stores up to 10 comma delimited string values as decimal values.
    Something like this (using string2 -- my object):
    CON
    
      { ==[ CLOCK SET ]== }       
      _CLKMODE      = XTAL1 + PLL16X
      _XINFREQ      = 5_000_000                             ' 5MHz Crystal
    
      MAX_INPUT_LENGTH = 111                                ' maximum of 11 character strings, plus terminating byte
      MAX_VALUE_LENGTH = 12                                 ' 10 characters is 32 bits, 11 is for possible negatives, 12 is for the additional terminating byte
    
    OBJ
    
      STR   : "STRINGS2"         
      MATH  : "MATHFUNCS"
    
    VAR
    
      BYTE inputstr[MAX_INPUT_LENGTH]                       ' simulated input string from XBEE
      LONG value[10]                                        ' array of values
      BYTE temp_string[MAX_VALUE_LENGTH]                    ' temperarily store string
    
    PUB Main | tmp, offset1, offset2, idx
    
      tmp := string("123,654,3333,45775,34523,23,222222,1,877770") 
      bytemove(@inputstr, tmp, strsize(tmp) + 1)                                    ' store string in simulated input                
    
      idx := 0
      offset1 := 0                                                                  ' start at beginning of string
      REPEAT WHILE ((offset2 := STR.StrPos(@inputstr, string(","), offset1)) <> -1) ' repeat while there are still commas in the string
        bytemove(@temp_string, @inputstr + offset1, offset2 - offset1)              ' move value into temperary storage
        temp_string[offset2 - offset1] := 0                                         ' terminate string
        value[idx++] := MATH.get_numeric_value(@temp_string, 4)                     ' convert and store last value
        offset1 := offset2 + 1                                                      ' move offset
    
    
      bytemove(@temp_string, @inputstr + offset1, strsize(@inputstr + offset1))     ' move value into temperary storage
      temp_string[offset2 - offset1] := 0                                           ' terminate string
      value[idx] := MATH.get_numeric_value(@temp_string, 4)                         ' convert and store last value
    
      repeat
        waitcnt(0)                         
    
    Where MATHFUNCS has (but there is likely better/faster ways to do this):
    PUB get_numeric_value (strAddr, size) | i, val, strsz, negate
    '' Return numeric value from a string.
    
      val := 0
      i := 0
      strsz := strsize(strAddr)
    
      IF (byte[strAddr] == "-")
        negate := true
        i++
      ELSE
        negate := false
      
      REPEAT UNTIL (byte[strAddr][i] > 57 OR byte[strAddr][i] < 48 OR i => strsz) ' keep adding until byte is not a number of reached end of string
        val *= 10
        val += byte[strAddr][i++] - 48 
      
      CASE size                                                    ' limit output to a specific size (probably not needed in 99% of cases)
        1: RETURN val <# $FF
        2: RETURN val <# $FF_FF
        OTHER:
          IF (negate)
            val := !val + 1
          RETURN val
    
    But making a character position finder would be faster...like this one:
    PUB CharPos (strAddr, char, offset) | size, searchsize
    
      size := strsize(strAddr) + 1      
    
      REPEAT UNTIL (offset + searchsize > size)
        IF (byte[strAddr + offset++] == char)                                                 ' if char search found
          RETURN offset - 1                                                         ' return byte location
      RETURN -1
    
  • charleyshfcharleyshf Posts: 165
    edited 2010-10-13 06:28
    I have to apoloigize, but I am entirely lost at this point and my head is killing me..

    I made some changes to my code and it works until I receive a number under 100, IE normally I will receive:
    ##vmean 148 109 137

    and my results match
    BUT
    if I receive
    ##vmean 127 95 137

    my results match the first two sets of numbers (127 and 95), but the last set(137) comes back as 37, I am dropping the 1 on 137.

    I found the following code on the forums somewhere(can't remember who posted it at this point, time to take some advil)

    pub str2dec(pntr) | val, c

    val := 0 ' clear workspace

    repeat
    c := byte[pntr++] ' get character, point to next
    if (c => "0") and (c =< "9") ' digit?
    val := (val * 10) + (c - "0") ' yes, add to value
    else
    quit ' no, we're done

    return val



    I am really trying to get a grip on all of this, but it's just not hitting me....


    T Chap wrote: »
    hex 57 is ascii 9
    hex 48 is ascii 0
    hex 32 is a space " "


    So now you need to convert the ascii values to decimal, sum the total for each number you need and you are set. I forgot to mention that to sum the values, they must not be in ascii form. You now have to parse each digit that you receive as ascii, convert it to integer ( a simple method is to subtract 48 from the decimal value you have above to get the integer value). Space (hex 32) is the delimeter.

    57 - 48 = 9
    48 - 48 = 0

    Scan the array looking for the givens to find the starting point of your values to derive from the string:

    If the array will always begin the same with ##vmean, then you have to have a way to account for whether 1,2, or 3 ascii digits follow the initial space. This assumes that a 4th ascii digit is not possible.

    Crude idea to get you started:
    If BFinMean[9] == $32   'find the space, then work backwards to assemble your number
      Y_mean[0] := BFinMean[8] 
    If BFinMean[10] == $32
      Y_mean[0] := BFinMean[8] 
      Y_mean[1] := BFinMean[9] 
    If BFinMean[11] == $32
      Y_mean[0] := BFinMean[8] 
      Y_mean[1] := BFinMean[9] 
      Y_mean[2] := BFinMean[10]
     '' convert each value to an integer(-48), multiply as need to get 10's, 100's, then sum
     '' start scanning where you left off for the next space work backwards for the number
    
    

    You may find an object that will convert an ascii string like "222" to an integer that will save a lot of work.
  • T ChapT Chap Posts: 4,223
    edited 2010-10-13 08:11
    The '1' in 137 i s not even being read into the equation because you are staring your conversion at element 16, the 1 is at element 15 in this case.

    The explanation is very simple.
    Y_Result := str2dec(@BFinMean[ 8 ] )
    U_Result := str2dec(@BFinMean[ 12 ])
    V_Result := str2dec(@BFinMean[ 16 ])
    
    Under this assumption above, the SPACEs would ALWAYS have to fall at

    7, 11, and 15. But as you posted several different return values:

    ##vmean 111 222 333
    ##vmean 90 129 127

    you will see that the spaces DO NOT always fall at 7, 11 and 15. Which is why I created the series of IF statements yesterday that DETECT where the space falls, THEN it begins to decode the ASCII values into integers. Now, if the string decode works fine, then you are almost there. Just find a way to detect the space and THEN start running the decoder, you cannot start it with you plan because as you can see that if the first value is less that 3 digits, the second set of values are truncated:
        'assumes that a space is HERE
        Y_Mean[0] := BFinMean[8]
        Y_Mean[1] := BFinMean[9]
        Y_Mean[2] := BFinMean[10]
        'assumes that a space is HERE  only IF the preceding value was 3 digits!!!!!
        U_Mean[0] := BFinMean[12]
        U_Mean[1] := BFinMean[13]
        U_Mean[2] := BFinMean[14]
        'assumes that a space is HERE only IF the preceding value(s) were BOTH 3 digits!!!!!
        V_Mean[0] := BFinMean[16]
        V_Mean[1] := BFinMean[17]
        V_Mean[2] := BFinMean[18]
    
    
    There are certainly more elegant ways to do this, but for a brute force delimit that you can see very clearly:

    GIVEN that that the first space seems to ALWAYS fall at element 7 in the array, we start there and work forward looking for content and the space that indicates the number has terminated.


    [code]
    repeat 20 'random number used to clear the array
    MeanResult := " " ' make sure the element is not a digit
    i++

    '' This block shows sorting out the first set of numbers (1-3 digits)
    MeanResult[0] := BFinMean[ 8 ] ' we know this is true assuming that there must be at lease one digit returned, even if the digit is a 0
    If BFinMean[ 9 ] <> $32 'check to see if the value is NOT a space
    MeanResult[0] := BFinMean[ 9 ]
    If BFinMean[ 10 ] <> $32 'check to see if the value is NOT a space
    MeanResult[2] := BFinMean[ 10 ]

    The point of this illustration is that you have to find a method to determine how long the number is (1,2 or 3 digits), select only the actual digits, THEN convert to a number.
    This just takes some creative ways to chop the string up, I think there are easier ways to to it that what I know how to do :)
  • T ChapT Chap Posts: 4,223
    edited 2010-10-13 18:20
    This program will take 1 - 3 ascii digits that make up each mean number, and convert it to an integer. Firefox crashed 5 times trying to attach the file, so I just posted the whole thing instead.

    Edit: Corrected.
    CON
      _clkmode               = xtal1 + pll16x
      _xinfreq               = 5_000_000
     
        TAB                  = $09
        CR                   = $0D
        SP                   = $20
        LCD_Pin              = 7
        LCD_Baud             = 19200
        LCD_Lines            = 2
        Stop                 = 
    
        LCDpin               = 7
    
        On                  = 1
        Off                 = 0
      
    VAR
       byte indata[24]
       long resultvar[3]
    OBJ
        ser          :"FullDuplexSerial4PortParity1"
    PUB INIT
    
         StartSerial(9600)    'initial baud and port set up
         Backlight(on)
         StartSerial(28800)          'now change port speed to 28800
         cls
         stringtester
     
    Pub stringtester    | idx, val, c, arrayidx,i
    
       Test1   'set array vals
       'Test2
       'Test3
       'Test4
       i :=0
       arrayidx := 0
       idx := 8
       val := 0 
       go(0,0)   'lcd
       repeat 11
         c := indata[idx]      
         if (c => "0") and (c =< "9")         
           val :=  (c - "0") '      
           ser.dec(3, val)  ''lcd debug only
           if arrayidx == 0
            if i == 0
             resultvar[arrayidx] :=  resultvar[arrayidx]  + val
            if i == 1
             resultvar[arrayidx] := resultvar[arrayidx]*10 + val
            if i == 2
             resultvar[arrayidx] :=  resultvar[arrayidx]*10 + val
            i++
    
           if arrayidx == 1
            if i == 0
             resultvar[arrayidx] :=  resultvar[arrayidx] + val
            if i == 1
             resultvar[arrayidx] := resultvar[arrayidx]*10+ val
            if i == 2
             resultvar[arrayidx] :=  resultvar[arrayidx]*10 + val
            i++
    
           if arrayidx == 2
            if i == 0
             resultvar[arrayidx] :=  resultvar[arrayidx]  + val
            if i == 1
             resultvar[arrayidx] := resultvar[arrayidx]*10 + val
            if i == 2
             resultvar[arrayidx] :=  resultvar[arrayidx]*10 + val
            i++
    
         if indata[idx] == " "  'if space, bump to new number
           arrayidx++
           i:= 0
         idx++
       go(1,0)  'lcd
       ser.dec(3, resultvar[0])  'lcd
       ser.str(3, string(" "))
       ser.dec(3, resultvar[1])
       ser.str(3, string(" "))
       ser.dec(3, resultvar[2])
    PUB test1
       indata[0] := "#"
       indata[1] := "#"
       indata[2] := "v"
       indata[3] := "m"
       indata[4] := "e"
       indata[5] := "a"
       indata[6] := "n"
       indata[7] := " "     
       indata[8] := "1"   'element 8
       indata[9] := "2"
       indata[10] := "3"
       indata[11] := " "
       indata[12] := "4"
       indata[13] := "5"
       indata[14] := "6"
       indata[15] := " "
       indata[16] := "7"
       indata[17] := "8"
       indata[18] := "9"
    PUB test2
       indata[0] := "#"
       indata[1] := "#"
       indata[2] := "v"
       indata[3] := "m"
       indata[4] := "e"
       indata[5] := "a"
       indata[6] := "n"
       indata[7] := " "     
       indata[8] := "1"   'element 8
       indata[9] := "1"
       indata[10] := " "
       indata[11] := "2"
       indata[12] := "2"
       indata[13] := " "
       indata[14] := "3"
       indata[15] := "3"
    PUB test3
       indata[0] := "#"
       indata[1] := "#"
       indata[2] := "v"
       indata[3] := "m"
       indata[4] := "e"
       indata[5] := "a"
       indata[6] := "n"
       indata[7] := " "     
       indata[8] := "1"   'element 8
       indata[9] := " "
       indata[10] := "2"
       indata[11] := " "
       indata[12] := "3"
    
    PUB test4
       indata[0] := "#"
       indata[1] := "#"
       indata[2] := "v"
       indata[3] := "m"
       indata[4] := "e"
       indata[5] := "a"
       indata[6] := "n"
       indata[7] := " "     
       indata[8] := "1"   'element 8
       indata[9] := "1"  
       indata[10] := " "
       indata[11] := "2"
       indata[12] := "2"
       indata[13] := "2"
       indata[14] := " "
       indata[15] := "3"
    
    PUB Dus(dd)
        waitcnt(clkfreq/1_000_000 * dd + cnt)
    
    
    PUB CLS
        ser.cls(3)
    PUB StartSerial(baud)         'PUB AddPort(port,rxpin,txpin,ctspin,rtspin,rtsthreshold,mode,baudrate)
         Ser.init
         Ser.AddPort(0,4,3,-1,-1,0,0,baud)            'fpc 1
         'Ser.AddPort(1,6,5,-1,-1,0,0,9600)           'fpc 2  not used on NEW
         Ser.AddPort(1,25,24,-1,-1,0,0,9600)          'TEST RS485 port
         ser.addport(2, 15, 16, -1, -1, 0,0, 9600)    'mp3 vncl1-a was 16,15 on old board, now maybe reversed
         ser.addport(3, -1, 7, -1, -1, 0,0, 19200)    'LCD
         Ser.Start
    
    PUB Backlight(lite)
        if lite == on
           ser.tx(3,$11)
        elseif lite == off
           ser.tx(3, $12)
    
    PUB go(line,col) | lcdline
        if line == 0
          lcdline := $80
        if line == 1
          lcdline := $94
        ser.putc(3,lcdline + col)
    
    
    
    
    
  • charleyshfcharleyshf Posts: 165
    edited 2010-10-14 10:29
    Hello,

    @T_Chap,,

    Sorry I had to get away from this for a while, still trying to get a grasp on this.

    I tried the example you posted and made changes to the lcd part so that I am able to use PST and output to my screen. I am running this by itself and the result I get when I run it is:

    123456789123 456 789
    12345678912423 46056 79689
    1234567891242423 4606056 7969689
    123456789124242423 460606056 796969689
    123456789-460659465 -1184034200 -1907408935
  • T ChapT Chap Posts: 4,223
    edited 2010-10-14 10:44
    If you run any one of the Test values, the output should first be the ascii string values 123456789 the next set of values are the decimal values that should be identical but separated: 123 456 789

    It looks like you have a repeat that is accumulating to the result values which is why they keep getting larger each time. The first line of your result is correct. The subsequent lines are showing the correct first set of values 123456789 the next batch of 3 numbers are larger and keep getting larger each time because you are accumulating resultvar, meaning you should have stopped the repeat on the first pass. For clarification, it would be easier if you added a Carriage Return to put both sets of numbers on different lines instead of being al ran together.

    In short, 123456789123 456 789 this is technically correct.

    The first set of displayed numbers 123456789 are showing what ascii(digits 0 - 9 only) values it found in the string just for clarification, nothing else. The set of three numbers following: 123 456 789 are the actual decimal values after translating from ascii, the result. The result has 3 elements that you will use in your own code like below.

    Y_mean := resultvar[0]
    U_mean := resultvar[1]
    V_mean := resultvar[2]
  • charleyshfcharleyshf Posts: 165
    edited 2010-10-14 11:16
    Now I feel like a complete idiot(as if I didn't before!!). Didn't think if I am going to repeat it for testing, maybe I should clear out the values before I do that....

    Anyways THANK YOU so much for helping me try to understand this better. I am still learning as I do, and at least I finally get what I have to do, just have to understand how things are processed better.

    T Chap wrote: »
    If you run any one of the Test values, the output should first be the ascii string values 123456789 the next set of values are the decimal values that should be identical but separated: 123 456 789

    It looks like you have a repeat that is accumulating to the result values which is why they keep getting larger each time. The first line of your result is correct. The subsequent lines are showing the correct first set of values 123456789 the next batch of 3 numbers are larger and keep getting larger each time because you are accumulating resultvar, meaning you should have stopped the repeat on the first pass. For clarification, it would be easier if you added a Carriage Return to put both sets of numbers on different lines instead of being al ran together.

    In short, 123456789123 456 789 this is technically correct.

    The first set of displayed numbers 123456789 are showing what ascii(digits 0 - 9 only) values it found in the string just for clarification, nothing else. The set of three numbers following: 123 456 789 are the actual decimal values after translating from ascii, the result. The result has 3 elements that you will use in your own code like below.

    Y_mean := resultvar[0]
    U_mean := resultvar[1]
    V_mean := resultvar[2]
Sign In or Register to comment.