Closed charlespax closed 9 years ago
The function GetTypKTemp()
in functions.cpp
divides an int32_t over an int32_t. This results in only integer values for the thermocouple temperature difference. The ambient temperature sensor gives decimal values. Combining these two values gives what we have observed: large temperature jumps of approximately one degree with small variations at each jump. This also explains why the displayed thermocouple temperatures always carry the same decimal number as the ambient temperature.
Check out this smoothness.
I'm working downward from the highest level...
In t400.ino
#53 double temperatures[SENSOR_COUNT];
creates a table containing the values to display. A double is floating point. I see a decimal number the display, so this appears to be good.
In t400.ino
#169 temperatures[channel] = convertTemperature(temperature + ambient);
writes a value to the specified table cell. When Celsius units are selected #86 double convertTemperature(double Celcius){}
just returns Celcius
. For simplicity, I'll just look at this case. #149 double temperature;
declares a floating point, so that should be good. In t400.ino
#54 double ambient = 0;
declares a floating point variable. Should be no problem. #151 ambient = ambientSensor.readTempC16(AMBIENT) / 16.0;
This is an integer value what gets divided by sixteen. Maybe something funky happens here, but probably not because I see a decimal value for ambient temperature on the display. AMBIENT
is a keyword defined in the MCP980X library keywords.txt
#26 AMBIENT LITERAL1
. In MCP980X.cpp
#45 int MCP980X::readTempC16(MCP980X_REGS_t reg){}
returns the Celsius temperature as an integer which is °C times 16.0.
So far there appears to be no problem with combining the ambient temperature and the thermocouple temperature. The issue must be with determining what the thermocouple temperature actually is. Let's take a look at temperature
.
In t400.ino
#163 temperature = GetTypKTemp(measuredVoltageUv);
converts measuredVoltageUv
to temperature using the lookup table. In functions.cpp
# 316 double GetTypKTemp(int32_t microVolts)
verifies microVolts
is within the expected range then returns LookedupValue
, which is defined by #322 double LookedupValue;
.
The problem could be in functions.cpp
at #330 LookedupValue = ((double)-270 + (i)*10) + ((10 *(microVolts - valueLow)) / ((valueHigh - valueLow)));
where all the numbers are integers except for (double-270)
. The problem may be from dividing integers rather than using floating point math.
Okay, I think that's the issue. I'm not really sure how all these variable types interact, so I wrote a quick c++ program.
// Use the lookup table in typek_constant.h to determine what the
// measured temperature should be. This does not include the
// junction temperature form the MCP9800.
#include <iostream>
using namespace std;
int main()
{
int32_t microVolts;
int32_t valueLow = -5354;
int32_t valueHigh = -5141;
double LookedupValue;
uint16_t i = 0;
cout << "Hypothetical uV: " << endl;
cin >> microVolts;
cout << "Corresponding i value: " << endl;
cin >> i;
LookedupValue = ((double)-270 + (i)*10) + ((10 *(microVolts - valueLow)) / ((valueHigh - valueLow)));
cout << "Calculated: " << LookedupValue << endl;
return 0;
}
I compile and execute to get the following output.
$ clear; g++ add.cpp; chmod +x a.out ;./a.out
Hypothetical uV:
-5200
Corresponding i value:
10
Calculated: -163
This code gives -163
whereas a hand calculate gives -162.769953...
.
Now I can change the code and cast all the integers at doubles.
...
LookedupValue = ((double)-270 + (i)*10) +
((10 *((double)microVolts - (double)valueLow)) / (((double)valueHigh - (double)valueLow)));
...
This gives the appropriate LookedupValue of -162.77
.
$ clear; g++ add.cpp; chmod +x a.out ;./a.out
Hypothetical uV:
-5200
Corresponding i value:
10
Calculated: -162.77
Maybe there is a better way to do this, but it looks like it will work. I'll test on the hardware.
Actually, I think we can just denominator as a double after the subtraction. Casting -270
as a double does not appear to do anything.
LookedupValue = (-270 + (i)*10) +
((10 *(microVolts - valueLow)) / (double)((valueHigh - valueLow)));
This gives
$ clear; g++ add.cpp; chmod +x a.out ;./a.out
Hypothetical uV:
-5200
Corresponding i value:
10
Calculated: -162.77
On the hardware this change works, but causes another problem. As you can see in this image we are getting much getter resolution on the readings.
However, there appears to be some kind of rollover when the displayed thermocouple temperature drops below the ambient temperature.
Changing the denominator from (double)((valueHigh - valueLow))
to ((double)(valueHigh - valueLow))
does not change anything
Well, it turns out that (double)-273
was really important. The only change to any code is in function.cpp
'LookedupValue = ((double)-270 + (i)10) + ((10 (microVolts - valueLow)) / ((double)(valueHigh - valueLow)));' Just cast the denominator as a double and all is well.
Check this out.
In the commit fc733ff559c312c488cb16a5921716677c6259d3 on electronics version 0.12 modified to the version 0.13 design (https://github.com/PaxInstruments/t400-electronics/issues/190) I still see the large jumps of approximately 1°C.
For the MCP3424 at 15 samples per second and 16-bit resolution we should see approximately 0.208 °C/LSB (0.0078 mV/LSB).
Here is the code I used in Soulver to calculate values.
I'm going to dig into the code and see what may be causing this. I'm guessing the ADC values are being truncated within the MCP2434 library. Possible taking uV to V and back to mV.