Converting math from Python, not confident with mixed datatypes

Rhodan

Active member
Joined
Apr 7, 2015
Messages
41
Programming Experience
10+
I've just translated some math done in Python to C# which calibrates the temperature reading from a BMP180 Temp/Pressure sensor and it appears to be working! The problem is that I'm not entirely confident it will work all the time or if I have to do some casting in there somewhere. Please take a look and see if you can spot any traps. I'll be amazed if this is fine, C# seems pretty type A when it comes to datatype mixing.

C#:
// _cal_ are of type int

        private double ReadTemp()
        {
            long UT, X1, X2, B5;
            double tempC;

            UT = ReadRawTemp(); // returns an int
            X1 = (((UT - _cal_AC6) * _cal_AC5) >>15);
            X2 = ((_cal_MC << 11 ) / (X1 + _cal_MD));
            B5 = X1 + X2;
            tempC = ((B5 + 8 ) >> 4) / 10d;
            return tempC;
        }

Thanks!
 
I definitely have problems with the math. I don't notice it with the temperature (though it is wrong - just not by much). Working on pressure has revealed huge differences in results.

The datasheet gives a breakdown of the math, what to put in and what should be coming out. Right from the start the numbers I'm getting are different. Just going to look at a little bit of the code since if I find out what's wrong there I'll probably be able to apply that to the rest.

C#:
long UT = 27808;
long UP = 23843;
ushort _cal_AC5 = 32767;
ushort _cal_AC6 = 23153;
short _cal_MC = -8711;
short _cal_MD = 2868;

X1 = ((UT - (long)_cal_AC6) * (long)_cal_AC5) >> 15;
X2 = (_cal_MC << 11) / (X1 + _cal_MD);
B5 = X1 + X2;
Debug.Print("Temp = " + (((B5 + 8) >> 4) / 10d).ToString());
B6 = B5 - 4000;
Debug.Print("X1 {0}, X2 {1}, B5 {2}, B6 {3}", X1, X2, B5, B6);

What I get is
Temp = 14.3
X1 4653, X2 -2372, B5 2281, B6 -1719

Temp should have been 15.0
X1 4743, X2 -2344, B5 2399

The datasheet shows the math as
X1=(UT-AC6) * AC5 /2[SUP]15[/SUP]
X2 = MC * 211 / (X1 + MD)
B5 = X1 + X2
T = (B5 +8)/2[SUP]4
[/SUP]
Not big differences in the numbers but once you get through calibrating the pressure the numbers are are in a whole different realm.

If you're interested, the datasheet can be found at https://cdn-shop.adafruit.com/datasheets/BST-BMP180-DS000-09.pdf
 
You UT and AC6 is not the same as the sample, does that matter?
 
OMG! I looked at the UT value a dozen times and didn't notice my 9 was actually a 0! Old eyes...

AC6 looks right? 23153 on the datasheet and 23153 in my code?

UT made the difference, now i can see why the pressure comes out nonsensical. Probably a problem with a badly typed formula.

Thank you!
 
Ok, found a few errors (brackets in wrong place etc). I also re-wrote it as a method that I just run locally on my PC since it's not actually accessing any Pi hardware for this. I laid it out so that I could match up the datasheet lines to my own, including the printed results. There are small differences which I'm thinking is maybe rounding differences between whatever language/compiler they are using and C#. The resulting pressure is 699.67 hPa (same as millibars) while the datasheet says it should be 699.64 hPa. In PSI that's 10.147855396 vs 10.147420283. I think I can live with the error, particularly since the BMP180's typical accuracy is only +/- 0.12 hPa anyway.

Still, it'd be nice to know for sure why the small differences.

C#:
        private void CalcPress()
        {
            long X1, X2, X3, B3, B4, B5, B6, B7, p;
            long UT = 27898;
            long UP = 23843;
            short _cal_AC1 = 408;
            short _cal_AC2 = -72;
            short _cal_AC3 = -14383;
            ushort _cal_AC4 = 32741;
            ushort _cal_AC5 = 32767;
            ushort _cal_AC6 = 23153;
            short _cal_B1 = 6190;
            short _cal_B2 = 4;
            short _cal_MB = -32768;
            short _cal_MC = -8711;
            short _cal_MD = 2868;
            short _bmpMode = 0;

            X1 = (UT - _cal_AC6) * _cal_AC5 >> 15;
            Debug.Print("X1 {0}", X1);
            X2 = (_cal_MC << 11) / (X1 + _cal_MD);
            Debug.Print("X2 {0}", X2);
            B5 = X1 + X2;
            Debug.Print("B5 {0}", B5);
            Debug.Print("T {0}\n", (B5 + 8) / 16);

            B6 = B5 - 4000;
            Debug.Print("B6 {0}", B6);
            X1 = (_cal_B2 * (B6 * B6) >> 12) >> 11;
            Debug.Print("X1 {0}", X1);
            X2 = (_cal_AC2 * B6) >> 11;
            Debug.Print("X2 {0}", X2);
            X3 = X1 + X2;
            Debug.Print("X3 {0}", X3);
            B3 = (((_cal_AC1 * 4 + X3) << _bmpMode) + 2) / 4;
            Debug.Print("B3 {0}", B3);
            X1 = _cal_AC3 * B6 >> 13;
            Debug.Print("X1 {0}", X1);
            X2 = (_cal_B1 * ((B6 * B6) >> 12)) >> 16;
            Debug.Print("X2 {0}", X2);
            X3 = ((X1 + X2) + 2) >> 2;
            Debug.Print("X3 {0}", X3);

            B4 = _cal_AC4 * (long)((ulong)(X3 + 32768)) >> 15;
            Debug.Print("B4 {0}", B4);
            B7 = ((long)(ulong)(UP - B3)) * (50000 >> _bmpMode);
            Debug.Print("B7 {0}", B7);
            if (B7 < 0x80000000)
            {
                p = (B7 * 2) / B4;
            }
            else
            {
                p = (B7 / B4) * 2;
            }
            Debug.Print("P {0}\n", p);
            X1 = (p >> 8) * (p >> 8);
            Debug.Print("X1 {0}", X1);
            X1 = (X1 * 3038) >> 16;
            Debug.Print("X1 {0}", X1);
            X2 = (-7357 * p) >> 16;
            Debug.Print("X2 {0}", X2);
            p = p + (X1 + X2 + 3791) / 16;
            Debug.Print("P {0}", p);

        }

And the printout. I put the datasheet values in brackets where mine differ.

X1 4744 (4743)
X2 -2343 (-2344)
B5 2401 (2399)
T 150

B6 -1599 (-1601)
X1 1
X2 56
X3 57
B3 422
X1 2807 (2810)
X2 58 (59)
X3 716 (717)
B4 33456 (33457)
B7 1171050000
P 70005 (7003)

X1 74529
X1 3454
X2 -7859
P 69967 (69964)
 
And finally, I actually managed to get the BMP180 working with C# and Mono using wiringPi.net!

As far as I can tell wiringPi.net's I2C functionality is incomplete as far as its 16bit reads are concerned, there's no overloads for endian-ness or signed/unsigned. Same with the writes but then I don't know enough about I2C to know if it's always big or little endian on writes (I assume it can be either). So I basically just had to use the single byte reads and sort out the sign/endian stuff manually. Good practice anyway!

I know all you did was point out AC1 being wrong but it was all I needed to get past that idjit-block!

Merci Beaucoup.
 
AC6 looks right? 23153 on the datasheet and 23153 in my code?
I meant AC5 somehow, your value 32767 is 32757 in sample. Glad you got things worked better out now.
 
Back
Top Bottom