Shop OBEX P1 Docs P2 Docs Learn Events
Whats a good formula to...... — Parallax Forums

Whats a good formula to......

radialrandyradialrandy Posts: 78
edited 2010-10-02 16:23 in Propeller 1
convert 00000000-11111111 to a 0-100 dec value in spin?

Comments

  • T ChapT Chap Posts: 4,224
    edited 2010-09-26 14:45
    If you are meaning that the 00000000-11111111 is a binary value, then

    x = %1111_1111

    You can use the underscore in a binary value like this

    00000000_11111111 but not the hyphen. SPIN ignores the "_" in a binary number.
  • Mike GreenMike Green Posts: 23,101
    edited 2010-09-26 15:02
    It depends on what you mean by "convert". If you mean that you have a binary value and you want to display it as a decimal value, then you'd use the .dec method in whatever display driver you're using. If you want to produce a string of characters suitable for writing to a disc file, some of the SD card drivers have the same .dec method.
  • radialrandyradialrandy Posts: 78
    edited 2010-09-26 15:03
    T Chap wrote: »
    If you are meaning that the 00000000-11111111 is a binary value, then

    x = %1111_1111

    You can use the underscore in a binary value like this

    00000000_11111111 but not the hyphen. SPIN ignores the "_" in a binary number.

    i mean i want to convert a binary value ranging from 00000000 to 11111111 to a dec value ranging from 1 to 100 that i can have as a varible in the program
  • radialrandyradialrandy Posts: 78
    edited 2010-09-26 15:06
    ill be reading a 0 to 100psi sensor that will be converted through a 8bit ADC. so i want to convert the 8 bit value to 0 to 100 in my program
  • radialrandyradialrandy Posts: 78
    edited 2010-09-26 15:08
    I guess I should have said scale instead of convert. so im looking for a scale calculation
  • T ChapT Chap Posts: 4,224
    edited 2010-09-26 15:08
    i mean i want to convert a binary value ranging from 00000000 to 11111111 to a dec value ranging from 1 to 100 that i can have as a varible in the program



    Oh, sorry about that.

    You don't convert anything still

    Say that X = 1, then that is the same as saying X = %00000001 or $01

    You can use the numbers interchangeably, no need to convert anthing except for display purposes on TV or LCD etc. In those cases, you display using ser.dec or ser.hex or ser.bin and show what you want to see as output.

    %000000000 = 0 = $00

    %11111111 = 255 == $FF there no conversion internally required.

    Oops. Just read your last addition.
  • TimmooreTimmoore Posts: 1,031
    edited 2010-09-26 15:08
    Its not very clear what you want

    1. Convert binary to dec
    2. Change the range of a variable

    1. You only need to convert when inputting or outputing the value in a variable is the same. So if you have a variable x and you have assign a binary value as T Chap explained then its also the same value in dec.

    2. If you want to change the range from 0-255 to 0-100 then multiply by the output range and divide by the input range so
    x_100 := (x_255 * 100)/255
  • Dr_AculaDr_Acula Posts: 5,484
    edited 2010-09-26 17:58
    To follow on from Timmoore, converting 0-255 to 0-100 would be to divide by 2.55. However, that assumes you are using floating point numbers. If you are using integers, then first multiply by 100 and then divide by 255. You can do that in two lines of code, or combine it to one line as Timmoore has done.
  • Ding-BattyDing-Batty Posts: 302
    edited 2010-09-26 21:22
    Timmoore wrote: »
    2. If you want to change the range from 0-255 to 0-100 then multiply by the output range and divide by the input range so
    x_100 := (x_255 * 100)/255

    I think you are close -- but radialrandy is converting from 256 values (0..255) to 101 values (0..100) so I think a more accurate computation would be:
    x_100 := (x_255 * 101)/256
    
    Both calculations, for x_255 == 255, give x_100 == 100, but for x_255 == 254, Timmoore's calculation gives x_100 == 99, while this one gives x_100 == 100.

    The more general conversion from x in [a..b] to y in [s..t] would be:
    y := ((x - a) * (t-s+1) / (b-a+1)) + s
    
    But I think this might be a bit of nit-picking, because:
    ill be reading a 0 to 100psi sensor that will be converted through a 8bit ADC. so i want to convert the 8 bit value to 0 to 100 in my program

    So the the maximum pressure signal from the pressure sensor might not correspond to the maximum measurable signal for the ADC, without careful calibration, and then there is always drift...
  • radialrandyradialrandy Posts: 78
    edited 2010-09-27 20:47
    thanks guys! exactly what I wanted to know. Thanks so much for the info.
  • radialrandyradialrandy Posts: 78
    edited 2010-09-27 20:56
    OK wait I am alittle confused on the use of "_"

    what exactly is the formula doing?
    x_100 := (x_255 * 101)/256

    wheres the binary number?
    I could not find info on the x_ in the prop book
  • Mike GreenMike Green Posts: 23,101
    edited 2010-09-27 21:10
    x_100 and x_255 are variable names. The "_" is treated like a letter for the purpose of making up a variable name. It also is allowed as a separator in numbers where it's ignored, but used to make a long string of digits more readable.

    The formula is the same as A := (B * 101) / 256

    It takes value B which ranges from 0 to 255 and converts it to a value A which ranges from 0 to 100 keeping all values proportional.
  • Ding-BattyDing-Batty Posts: 302
    edited 2010-09-27 21:11
    I think they are simply to distinguish two variables, one named "x_100" which holds a value in the range 0..100 and "x_255" that holds a value in the range 0..255. They could as easily be called "Fred" and "Ginger":
    Fred := (Ginger * 101) / 256
    
    This "convention" was intrduced in the post by Timmoore, but it might be a little clearer if it were completely written up as a method:
    PUB ConvertADCtoPSI( x_255 ) : x_100
    '' Convert an ADC 8-bit value to a PSI value in the 0..100 psi range
    '' x_255 -- the incoming ADC value, in the range 0..255
    '' Returns a value in the range 0..100
    
      x_100 := (x_255 * 101) / 256
    
  • Ding-BattyDing-Batty Posts: 302
    edited 2010-09-27 21:12
    Mike, you beat me to it :lol:
  • Phil Pilgrim (PhiPi)Phil Pilgrim (PhiPi) Posts: 23,514
    edited 2010-09-27 21:13
    Ding-Batty, Mike:

    Tim Moore's formula is correct for round-down (i.e. integer truncation):

    255 * 100 / 255 == 100.
    255 * 0 / 255 == 0.

    For 5/4 round-up/down, the correct forumula would be:

    x100 = int(x255 * (100 / 255) + 0.5)
    = int(x255 * (200 / 510) + (255 / 510))
    = int((x255 * 200 + 255) / 510)
    = (x255 * 200 + 255) / 510, using integer arithmetic.

    -Phil
  • Ding-BattyDing-Batty Posts: 302
    edited 2010-09-28 08:46
    Phil, I didn't mean to imply that Tim Moore's formula would not work -- if I did, I apologize.

    I was trying to point out the Tim's formula was not uniform over the input range -- it seems to be a little off at the high end.

    Maybe I can explain another way, with a simpler example all for the truncation (rounding down) case.

    Tim's formula is for the input range of [0..255] (call that [0..A]), and an output range of [0..100] (call that [0..B]):
    output := (input * B) / A  ' Tim Moore
    
    My formula for the same ranges is:
    output := (input * (B+1)) / (A+1)  ' Ding-Batty
    
    So, consider a slightly different input and output range: input of [0..99], and output of [0..9]. This is obviously (to me) a divide-by-ten, so that 90..99 should become 9, 80..89 should become 8, etc.

    Using Tim's approach, the function used would be:

    output == ( input * 9 ) / 99
    == input / 11

    This does correctly turn 99 / 11 --> 9, but 90 through 98 all become 98/99 --> 8!

    Using the approach I suggested:

    output == ( input * 10 ) / 100
    == input / 10

    Now, perhaps the problem is that "100" value, perhaps it is special, in that it is the upper limit of the range from the sensor. That would imply that the "255" input value was also somehow special But I think that all depends on the interpretation of the sensor signal as converted by the ADC, and from what I understand about ADC conversions, I think it should not be treated as special.

    The nearest common ADC I have is my "3-1/2 digit DMM" with a 20VDC input range. The meter can show me converted values from 00.00 to 19.99, plus it has a overrange indicator which means the input is 20 volts or greater.

    That maximum value of 19.99 really corresponds to voltages in the range 19.990000...V to 19.999999...V (up to, but below, 20V). The easy way to indicate this range is [19.99 .. 20.00) (that is math notation for a half-open interval, which includes the left number, all the way up to, but not including the right number).

    In the same way, for the 0..255 output of the ADC, each value, including 255, stands for a range of input values. The "special" limit value is really 256, which corresponds to the 20.00 for the DMM.

    Here's where we get the the second point I was trying to make. Radialrandy said that he is using a pressure sensor that goes up to 100psi. I don't know if that really means 0..99.9999... psi. Furthermore, I am sure that there is some variation in construction of the sensors, so that some can report pressures higher than 100psi. And there may be variation in the precision of the output between individual sensors, so that the same pressure produces different output signals from different sensors. So there needs to be calibration of the sensor and ADC combination, and only then is there enough information about the meaning of the input signal (0..255) to know which of the several conversions is most appropriate. This is the meaning of the "nit-picking" comment I originally made.

    One more possible misunderstanding on my part: if the ADC output value of "255" corresponds to the overflow value (i.e. all input values bigger than the max expected value are converted to this), then if the ADC is calibrated for 255 to be returned when 100.000 psi is reached, and not at any lower pressure, then Tim Moore's function would match mine, in that I would calculate: output := (input * 100) / 255 as well, because the basic ranges are [0..254] and [0..99], with 255 and 100 both handled as a special case. But that all depends on the real meaning of the values coming out of the ADC. (Clearly, I don't have much experience with real ADC chips...)

    BTW, you can call me Tom...
  • radialrandyradialrandy Posts: 78
    edited 2010-09-28 18:15
    thanks guys I have a much better understanding now. This pressure scale does not have to be perfect just close. I understand that the pressure sensor may not give perfect value so any of your recommendation will work great. I found that the sensor that I'm using actually has a span on 4.5V.
    .2 volts = 0psi
    4.7volts = 101.5psi

    So I'm going to have to modify the formulas examples that you all gave me alittle more.
    thanks for the good info.
    I'm leaning more and more every time you guys give your inputs.
  • radialrandyradialrandy Posts: 78
    edited 2010-10-02 15:04
    PUB sensor | ADC, T, dT
      dira[23..16]~                 'pins reading ADC of psi sensor
      dira[24]~~                    'output pin to WD/RDY tp start measurement
      outa[24]~~
      T := cnt
      dT := 800_000
      repeat
        T += dT
        waitcnt(T)                  
        outa[24]~
        waitcnt(40_000 + cnt)
        outa[24]~~
        waitcnt(40_000 + cnt)
        ADC := ina[23..16]          '0 to 255 binary reading from 8bit parallel ADC
        co2psi := (ADC * 101) / 256
    

    OK guys with your help I got this thing to scale a 0 to 255 reading to a 0 to 100 reading perfectly . Id like to go a step further and scale the 0 to 255 to 0 to 100 but in .5 incrementals. Is this doable?
  • Ding-BattyDing-Batty Posts: 302
    edited 2010-10-02 16:09
    ADC := ina[23..16]          '0 to 255 binary reading from 8bit parallel ADC
        co2psi := (ADC * 101) / 256
    
    OK guys with your help I got this thing to scale a 0 to 255 reading to a 0 to 100 reading perfectly . Id like to go a step further and scale the 0 to 255 to 0 to 100 but in .5 incrementals. Is this doable?

    You could probably figure this one out yourself, without our help. But here's how I'd approach it...

    You want the output range to be 0.0..100.0 in 0.5 increments. I'd consider the values to be doubled, i.e. 0.0..200.0 and divide at the end by 2. So I'd use something like this:
    ADC := ina[23..16]          '0 to 255 binary reading from 8bit parallel ADC
        co2psi_x_2 := (ADC * 201) / 256   ' 0..200
        co2psi_int   := co2psi_x_2 >> 1            ' divide by two to get 0..100
        if ( co2psi_x_2 & 1 )
            co2psi_frac := 5                        ' for 0.5, since the scaling was odd
        else
            co2psi_frac := 0
    
  • Phil Pilgrim (PhiPi)Phil Pilgrim (PhiPi) Posts: 23,514
    edited 2010-10-02 16:23
    One good measure of an integer scaling formula is to measure the root mean square (RMS) error of each scaled and rounded value vs. what the scaled value would have been in floating point, over the domain of interest. The RMS error is given by:
    ErrorRMS = sqrt(Σ(yi - ri)2 / n), where

    yi and ri are the rounded and real values, respectively, and
    n is the number of values in the range of interest.

    The smaller ErrorRMS is, the better the rounding formula.

    I'm assuming, for the sake of argument, that in this app, ri = i * 100 / 255, where i is the ADC reading in the range 0 to 255, making n equal to 256.

    So I wrote a little Perl script to compare Tim Moore's round-down formula, Ding-Batty's "Fred and Ginger" formula (with the 101), and my 5/4 round up/down formula. Here's the script:
    use strict;
    
    my @ssq = (0, 0, 0);
    my @names = ("timmoore:   ", "ding-batty: ", "phipi:      ");
    my @y;
    
    foreach my $x (0 .. 255) {
      $y[0] = int($x * 100 / 255);
      $y[1] = int($x * 101 / 256);
      $y[2] = int(($x * 200 + 255) / 510);
      my $real = $x * 100 / 255;
      foreach my $i (0 .. 2) {
        $ssq[$i] += ($y[$i] - $real) ** 2
      } 
    }
    print "RMS Errors\n\n";
    foreach my $i (0 .. 2) {
      print "$names[$i]", sprintf("%6.3f\n", sqrt($ssq[$i] / 256))
    }
    
    And here's the output from the script:
    RMS Errors
    
    timmoore:    0.568
    ding-batty:  0.391
    phipi:       0.288
    
    -Phil
Sign In or Register to comment.