Author Topic: Polynomial approximation for thermocouples  (Read 14644 times)

0 Members and 1 Guest are viewing this topic.

Offline dannyfTopic starter

  • Super Contributor
  • ***
  • Posts: 8221
  • Country: 00
Polynomial approximation for thermocouples
« on: June 06, 2014, 12:46:04 am »
I mentioned this in a different thread and I thought I would share it here.

Thermocouples are widely used as temperature sensors, over a wide temperature range. The typical solution is to use a thermocouple amplifier that translates the TC voltage output into easily readable linear output. You can then adc the voltage to obtain the temperature information.

This approach is very fast. But it is expensive and not terribly flexible: if you change the thermocouple, you need to change the amplifier used.

================================
https://dannyelectronics.wordpress.com/
 

Offline dannyfTopic starter

  • Super Contributor
  • ***
  • Posts: 8221
  • Country: 00
Re: Polynomial approximation for thermocouples
« Reply #1 on: June 06, 2014, 12:49:42 am »
The alternative is to adc the output from TC, either after it is amplified by an opamp or directly if the adc has a built-in amplifier.

This approach is flexible - if you change thermocouple, you just load the corresponding code to read that thermocouple.

However, its implementation in an embedded application presents a couple of challenges.

1. the data is massive: depending on the TC used, the NIST provides tables in 1C increment. So for a 1000C coverage, you need 1000 data points. That's easily 4K or 8K space, depending on the format used. For a small chip, it is out of the question.

2. their output is non-linear. In particular, the output (in mv) to temperature (Celsius) conversion is quite non-linear. The data, as presented by NIST, is massive so using a look-up table is out of the question. Fortunately, NIST presents a polynomial approximation for those tables. But that approximation is 9 order so the calculation can be fairly massive, particularly if implemented in floating point.

Our goal is to find a compromise between speed and accuracy.
================================
https://dannyelectronics.wordpress.com/
 

Offline dannyfTopic starter

  • Super Contributor
  • ***
  • Posts: 8221
  • Country: 00
Re: Polynomial approximation for thermocouples
« Reply #2 on: June 06, 2014, 12:54:35 am »
First, we are going to implement the NIST 9-order polynomial (one of them is a 10-order polynomial so watch out, :)).

We are going to pick Type B TC - implementation of other types is highly similar if not identical.

Here is the NIST table for Type B TC: http://srdata.nist.gov/its90/download/type_b.tab

Here is the interesting part, at bottom of the page:

Code: [Select]
************************************
* This section contains coefficients of approximate inverse
* functions for type B thermocouples for the subranges of
* temperature and voltage listed below. The range of errors of
* the approximate inverse function for each subrange is also given.
* The coefficients are in units of °C and mV and are listed in
* the order of constant term up to the highest order.
* The equation is of the form t_90 = d_0 + d_1*E + d_2*E^2 + ...
*     + d_n*E^n,
* where E is in mV and t_90 is in °C.
*
*    Temperature        Voltage            Error
*      range              range            range
*      (°C)               (mV)             (° C)
*    250. to 700.       0.291 to 2.431    -0.02 to 0.03
*    700. to 1800.      2.431 to 13.820   -0.01 to 0.02
********************************************************
Inverse coefficients for type B:
 
Temperature   250.           700.
  Range:      700.          1820.
 
  Voltage    0.291          2.431
  Range:     2.431         13.820
 
         9.8423321E+01  2.1315071E+02
         6.9971500E+02  2.8510504E+02
        -8.4765304E+02 -5.2742887E+01
         1.0052644E+03  9.9160804E+00
        -8.3345952E+02 -1.2965303E+00
         4.5508542E+02  1.1195870E-01
        -1.5523037E+02 -6.0625199E-03
         2.9886750E+01  1.8661696E-04
        -2.4742860E+00 -2.4878585E-06
 
  Error      -0.02          -0.01
  Range:      0.03           0.02
 

Essential, we need to implement two 8th order polynomial curves. For simplicity, we will implement it in floating point.
================================
https://dannyelectronics.wordpress.com/
 

Offline dannyfTopic starter

  • Super Contributor
  • ***
  • Posts: 8221
  • Country: 00
Re: Polynomial approximation for thermocouples
« Reply #3 on: June 06, 2014, 01:05:20 am »
First, data structure.

Each polynomial should have its starting voltage (mV_LOW), ending voltage (mV_HIGH) so we know when to switch curves. It should also have an array containing the coefficients (9 of them + a constant).

So something like this:

Code: [Select]
//polynomial curve
typedef struct {
  double mV_LOW; //starting voltage for a curve
  double mV_HIGH; //ending voltage for a curve
  double coef[10];  //coefficients, 0=constant, 1=1st order, 2=2nd order ...
} TCCOEF_TYPEDEF; //polynomial curves

The two curves for Type B TC would be something like this:
Code: [Select]
//TC TypeB NIST polynomial
const TCCOEF_TYPEDEF TCB_table[]={
  {0.291,      2.431,      {9.8423321E+01,  6.9971500E+02, -8.4765304E+02, 1.0052644E+03, -8.3345952E+02, 4.5508542E+02, -1.5523037E+02,  2.9886750E+01, -2.4742860E+00, 0.000000000}}, //Type B, 250C - 700C
  {2.431,     13.820,     {2.1315071E+02,  2.8510504E+02, -5.2742887E+01, 9.9160804E+00, -1.2965303E+00, 1.1195870E-01,   -6.0625199E-03,   1.8661696E-04,  -2.4878585E-06,  0.000000000}} //Type B, 700C - 1800C
};


================================
https://dannyelectronics.wordpress.com/
 

Offline dannyfTopic starter

  • Super Contributor
  • ***
  • Posts: 8221
  • Country: 00
Re: Polynomial approximation for thermocouples
« Reply #4 on: June 06, 2014, 01:13:16 am »
The polynomial function is fairly straight forward:

Code: [Select]
//calculate a polynomial
//coef: an array containing the coefficients. coef[0] = constant, coef[1] = 1st order, ...
//order: the order of the polynomial to be calculated
//mv: TC voltage output, in mv
//return: temperature reading, in Celsius

double TC_mv2c(const double *coef, unsigned char order, double mv) {
  double mv_i=mv, tmp; //contains intermediate results
  unsigned char index; //index for the coefficients

  tmp = *coef++; //pick up the constant
  for (index = 1; index <= order; index++) {
    tmp += (*coef++) * mv_i; //calculate mv^i
    mv_i *= mv; //calculate my^i
  }
  return tmp; //return the temperature
}

So to use the curve, we would do something like this:

Code: [Select]
  temperature = TC_mv2c(TCB_table[0].coef, 9, mv); //if mv is less than 2.431
  //or
  temperature = TC_mv2c(TCB_table[1].coef, 9, mv); //if mv is greater than 2.431

================================
https://dannyelectronics.wordpress.com/
 

Offline dannyfTopic starter

  • Super Contributor
  • ***
  • Posts: 8221
  • Country: 00
Re: Polynomial approximation for thermocouples
« Reply #5 on: June 06, 2014, 01:22:15 am »
That's not very programmer friendly.

So we are going to simplify that.

First, we are going to add an enum type to switch the curve, based on the mv input:

Code: [Select]
enum TC_CURVE {
TC_CURVE1 = 0, //thermocouple curve 1
TC_CURVE2, //thermocouple curve 2
TC_CURVE3, //thermocouple curve 3
TC_CURVE4 //thermocouple curve 4
};

We defined just 4 curves here (for Type R, and Type S).

Then we are going to use a macro to simply the task of switching curves:

Code: [Select]
//macro used to calculate mv to temperature
//Type B, nist (9th-order) polynomial
#define TCB9_mv2c(mv)  (((mv) > TCB_table[TC_CURVE1].mV_HIGH)?TC_mv2c(TCB_table[TC_CURVE1].coef, 9, mv):TC_mv2c(TCB_table[TC_CURVE2].coef, 9, mv))

So in your code, the mapping of themocouple output (mv) to temperature for Type B, NIST polynomial would be

Code: [Select]
  Temperature = TCB9_mv2c(mv); //Type B, 9th order polynomial, mv to temperature conversion

Performance:
Depending on the curves used, typically within 0.05c from the (massive) table. 3000 cycles on a 8-bit macro and 300 cycles on an ARM CM3. Space requirement varies depending on if you already use floating point math.
================================
https://dannyelectronics.wordpress.com/
 

Offline dannyfTopic starter

  • Super Contributor
  • ***
  • Posts: 8221
  • Country: 00
Re: Polynomial approximation for thermocouples
« Reply #6 on: June 06, 2014, 01:31:34 am »
3000 cycles can be a lot on a slow 8-bit micro.

Many times, getting the absolute temperature right is not quite as important as getting the temperature stable. In those situations, we are happy to trade accuracy for speed.

Here, we are going to use 5th order polynomial for its good compromise between speed and accuracy: usually within a couple degrees and reasonably fast.

We are also using just one curve over the whole range.

Our data structure is slightly different:
Code: [Select]
//5th-order polynomial curve
typedef struct {
  double mV_LOW; //starting voltage for a curve
  double mV_HIGH; //ending voltage for a curve
  double coef[6];  //coefficients, 0=constant, 1=1st order, 2=2nd order ...
} TCCOEF5_TYPEDEF; //polynomial curves

To generate the 5th-order coefficients, we feed the NIST table to Excel. Using its linear regression function on the polynomial, we have the following curve for Type B TC:

Code: [Select]
const TCCOEF5_TYPEDEF TCB5_table[] = {
//mV_LOW, //mV_HIGH, //d_0 //d_1 //d_2 //d_3 //d_4 //d_5 //d_6 //d_7 //d_8 //d_9
//type b
    {0.291, 13.820, {1.93559957E+02, 2.87194132E+02, -4.24257073E+01, 4.84404652E+00,  -2.82072795E-01, 6.47250272E-03}}   //TC_CURVE_B1, Type B, 250 - 1800c
};

We can reuse the same polynomial calculation function, TC_mv2c(), but a different macro is needed:

Code: [Select]
//5th-order polynomial conversion function for Type B TC:
#define TCB5_mv2c(mv) TC_mv2c(TCB5_table[TC_CURVE1].coef, 5, mv)

So the user has the option, now, to use either TCB9_mv2c() to return the more accuracy but time-consumping NIST results, or TCB5_mv2c() to return the less accurate but faster 5th order polynomial conversion results.

================================
https://dannyelectronics.wordpress.com/
 

Offline dannyfTopic starter

  • Super Contributor
  • ***
  • Posts: 8221
  • Country: 00
Re: Polynomial approximation for thermocouples
« Reply #7 on: June 06, 2014, 01:39:50 am »
You can easily extend this to B/E/J/K/N/S/T thermocouples, or PT100/1000/CU sensors - they are almost carbon copies of the curves / macros used here.

If you do that, you will find the following:

1) the whole module will take about 2 - 3KB on an  8-bit macro, if every curve is linked in. Less so if the compiler can prune unused data.
2) the 5th order polynomial saves about 30 -  40% of time in execution over the 9th order polynomial. 2000 cycles are fairly doable on an 8-bitter.
3) Errors are usually 2 - 3 degrees over the whole range. However, at the two extremes, you do get larger errors. How large those errors are depends on the particular thermocouples and your quantification. The biggest errors tend to be on Type R and Type S thermocouples and they can be as large as 22 degrees - you cannot have your pie and eat it at the same time, :)
4) The whole thing can be written entirely in C so it is not platform dependent. Just drop in the .c and .h files into your project and compile.

Hope it helps.
================================
https://dannyelectronics.wordpress.com/
 

Offline retrolefty

  • Super Contributor
  • ***
  • Posts: 1648
  • Country: us
  • measurement changes behavior
Re: Polynomial approximation for thermocouples
« Reply #8 on: June 06, 2014, 01:42:57 am »
The trade-off should also consider the accuracy specification of the specific couples being used. At the refinery we used thousands and thousands of TCs and never considered them better then +/- 5 degrees F at the range we mostly worked at say 500-1000F. There are special lab type TC where the metal purity is better controlled and better accuracy can be had for a price.

 Over the last 10 years (before I retired) we mostly converted to RTD sensors (PT100) for new installations for ranges of 500F or below as their price had come down over time, and the total installed cost (conduit + labor) was not much more then TCs.
« Last Edit: June 06, 2014, 01:44:42 am by retrolefty »
 

Offline mariush

  • Super Contributor
  • ***
  • Posts: 5181
  • Country: ro
  • .
Re: Polynomial approximation for thermocouples
« Reply #9 on: June 06, 2014, 01:44:48 am »
Thank you for explaining it so well.

As this thread is related to my thermometer project thread.

In that project, I thought of doing it this way but ultimately avoided on purpose this polynomial approach because I didn't want to scare absolute beginners with that math.
Well, this was one major reason, the other was... i basically had a mcu with 28 KB of flash (16KB x 14 bit words) so having less than 1 KB of that used by a table was not a big problem.

I simply took the nist data and multiplied every 5c value by 1000 and added it to an array, therefore I had an array of 251 records, each an unsigned short (2 bytes).. at 1250c a k thermocouple gives 50.644 mV so in the table I simply have a 50644 (which is smaller than 65535 so all is fine in the world). So the array is using a bit more than 502 bytes in the flash memory.

edit: and i forgot to mention.. it just so happens that for 5c steps, for 0-1250c range, the difference between one step and the other is always under 255 mV, so the table can be further reduced to use  unsigned byte/char instead of unsigned short (2bytes), therefore using only 251 bytes. The mcu will simply have to do some extra additions as it goes down in the table, so it's trading flash space vs processing speed.

The microcontroller reads the volts value from the thermocouple amplifier, does a simple conversion to get the mV value of the thermocouple and then looks up in the table in which 5v interval that mV value drops. From there, it's a simple formula to figure out where the value falls between x Celsius and (x+5) Celsius
A simple table lookup is fast if the temperatures , I guess about 4-6 cycles for each range, but the lookup time increases linearly with the temperature because the whole table is parsed.
It can be optimized by jumping straight to some locations in the table but it will still take a lot of cycles to look up the range.

But it still may use much fewer cycles compared to the polynomial approach.. after all there's a lot of multiplications involving floating point numbers, and PIC16 microcontrollers aren't known for hardware multiplications.  Further, as you use double variables (each 3 bytes), that constant array uses about 160 bytes of flash memory, still 25% of my 500 something bytes (though i agree, the results using the polynomial approach should be more accurate)

PS. I see you added more posts while I typed this. I'll have to read what you posted and I may edit this post then.
« Last Edit: June 06, 2014, 01:51:04 am by mariush »
 

Online IanB

  • Super Contributor
  • ***
  • Posts: 12588
  • Country: us
Re: Polynomial approximation for thermocouples
« Reply #10 on: June 06, 2014, 02:39:16 am »
The polynomial function is fairly straight forward:

It looks like your polynomial evaluation code is about twice as slow as it needs to be. Can you see why?
 

Offline dannyfTopic starter

  • Super Contributor
  • ***
  • Posts: 8221
  • Country: 00
Re: Polynomial approximation for thermocouples
« Reply #11 on: June 06, 2014, 10:45:11 am »
Quote
the lookup time increases linearly with the temperature because the whole table is parsed.

That's not a very efficient process, because you are using a (forward) conversion table (temperature -> mv) for inverse conversion (mv -> temperature).

One way to speed it up significantly, while still retaining your look-up approach, is to use an inverse conversion table. For example, use the polynomials and calculate a table of temperatures indexed to a mv proxy. Store that table in the mcu and look up the temperature via the proxy.

Take Type B thermocouple for example. It has an output range of 0.291 -> 13.820mv. Let's say that you want cover the full range with a 100-point table. The mv proxy would then map 0.291 -> 13.820mv to 0 -> 99 indexing range and you canculate the corresponding temperature readings for that index.

To further speed it up for a given design, you can actually index the temperature to ADC readings.

Indexing to the mv proxy provides a more portability to the table; Indexing to the ADC readings provides the fastest execution (short of using a thermocouple amplifier).
================================
https://dannyelectronics.wordpress.com/
 

Offline amyk

  • Super Contributor
  • ***
  • Posts: 8553
Re: Polynomial approximation for thermocouples
« Reply #12 on: June 06, 2014, 11:58:25 am »
Depending on the thermocouple type, temperature range, and accuracy requirement a linear approximation could be sufficient; also, a piecewise-linear approximation can improve accuracy without requiring much more maths.
 

Offline miceuz

  • Frequent Contributor
  • **
  • Posts: 387
  • Country: lt
    • chirp - a soil moisture meter / plant watering alarm
Re: Polynomial approximation for thermocouples
« Reply #13 on: June 06, 2014, 12:19:17 pm »
I've went thru this recently, but the code size went out of control, I don't use floating point anywhere else in the firmware, I think it is somewhat +2k of flash memory for just adding floating point, and K type thermocouples have a funny exponential in their polynomial.

So I have switched to sparse lookup table approach, 65 data points (260 bytes) gave me the same accuracy as NIST polynomial, for a different type TC you'd have to have a different table.



I have the code available on my github: https://github.com/Miceuz/k-thermocouple-lib
Some writeup is here: http://wemakethings.net/2014/04/24/k-thermocouple-lib

Offline dannyfTopic starter

  • Super Contributor
  • ***
  • Posts: 8221
  • Country: 00
Re: Polynomial approximation for thermocouples
« Reply #14 on: June 06, 2014, 01:13:31 pm »
Quote
65 data points (260 bytes)

Each data point is 8 bytes  (two unsigned long types). 65 * 8 = ~500 bytes.

If you go with evenly sampled array and index the temperature to a mv proxy, you can half the storage needs.

================================
https://dannyelectronics.wordpress.com/
 

Offline AG6QR

  • Frequent Contributor
  • **
  • Posts: 867
  • Country: us
    • AG6QR Blog
Re: Polynomial approximation for thermocouples
« Reply #15 on: June 06, 2014, 03:45:13 pm »
It looks like your polynomial evaluation code is about twice as slow as it needs to be. Can you see why?

Horner's method does the same job using half the multiplications.  It's a common "trap for young players".  The notation you used in high school algebra class doesn't lead to an efficient way of evaluating polynomials via computers. Horner's method is always preferred over the straightforward way. 


Instead of writing
Ax**3 + Bx**2 + Cx + D
Transform it into
((Ax + B)x + C)x + D

http://en.wikipedia.org/wiki/Horner's_method

Code: [Select]
y =coeff[0]; // pre-load the high order coefficient
// loop through remaining coefficients
for (i = 1; i < order; i++) {
    y *= x;
    y += coeff[i];
}
 

Offline miceuz

  • Frequent Contributor
  • **
  • Posts: 387
  • Country: lt
    • chirp - a soil moisture meter / plant watering alarm
Re: Polynomial approximation for thermocouples
« Reply #16 on: June 06, 2014, 03:52:01 pm »
Quote
65 data points (260 bytes)

Each data point is 8 bytes  (two unsigned long types). 65 * 8 = ~500 bytes.

If you go with evenly sampled array and index the temperature to a mv proxy, you can half the storage needs.

Yes, thanks for correcting me. Or I could at least shave off two bytes from the temperature value.

Offline jaxbird

  • Frequent Contributor
  • **
  • Posts: 778
  • Country: 00
Re: Polynomial approximation for thermocouples
« Reply #17 on: June 06, 2014, 03:55:05 pm »
Why not just use targeted lookup tables and piecewise-linear approximation (interpolation/extrapolation) like amyk suggested?

Analog Discovery Projects: http://www.thestuffmade.com
Youtube random project videos: https://www.youtube.com/user/TheStuffMade
 

Offline dannyfTopic starter

  • Super Contributor
  • ***
  • Posts: 8221
  • Country: 00
Re: Polynomial approximation for thermocouples
« Reply #18 on: June 06, 2014, 04:44:15 pm »
It has an advantage if floating point math isn't already used.

Quote
Or I could at least shave off two bytes from the temperature value.

Yeah. Just remember that sometimes the values (temperature + mv) can go negative.
================================
https://dannyelectronics.wordpress.com/
 

Offline dannyfTopic starter

  • Super Contributor
  • ***
  • Posts: 8221
  • Country: 00
Re: Polynomial approximation for thermocouples
« Reply #19 on: June 07, 2014, 11:42:34 pm »
I made some progress in applying piecewise interpolation to approximate the nist curve.

Again, using Type B as an example.

General approach:

I would break down the entire curve into 64 points (63 pieces of linear lines), and within each line, 256 points are linearly interpolated, for a total of 16K points along the curve.

64 points are used as a compromise between accuracy and storage. Since we are using long types here, each curve takes about 64 * 4 byte = 250 bytes.

The reason to pick 256 points will be clear later on.

Data structure:

Roughly the same, but rather than storing coefficients, we now store data points:

Code: [Select]
typedef struct {
signed long uV_LOW; //low range of the input voltage, uv
signed long uV_HIGH; //high range of the input voltage, uv
signed long Cx1000[TC_POINTS]; //temperature points, in 0.001 Celsius
} TCCURVE_TYPEDEF;

Here is the curve for Type B:

Code: [Select]
/TC TYPE B curve
const TCCURVE_TYPEDEF TCB_curve ={
291, 13820, {
249916.0747, 323704.7308, 383399.8668, 435096.9286, 481439.2474, 523865.2027, 563318.3990, 600402.6075,
635512.0226, 669016.1792, 701160.7584, 732037.5081, 761835.4734, 790670.0534, 818641.4668, 845836.6596,
872331.0218, 898189.9269, 923470.1060, 948220.8699, 972485.1895, 996300.6468, 1019700.267, 1042713.24,
1065365.549, 1087680.500, 1109679.183, 1131380.850, 1152803.236, 1173962.825, 1194875.058, 1215554.514,
1236015.038, 1256269.853, 1276331.638, 1296212.593, 1315924.481, 1335478.664, 1354886.134, 1374157.53,
1393303.160, 1412333.021, 1431256.820, 1450084.002, 1468823.774, 1487485.140, 1506076.941, 1524607.89,
1543086.619, 1561521.724, 1579921.805, 1598295.511, 1616651.571, 1634998.822, 1653346.219, 1671702.834,
1690077.826, 1708480.392, 1726919.679, 1745404.660, 1763943.968, 1782545.676, 1801217.016, 1819964.035
}
};

The numbers are digitized using the NIST polynomial.

Breaking it down:

Each thermocouple output (uv) is taken to generate two numbers, i6 (6-bit type) to indicate which piece-wise section that uv value should be, and i256 (8-bit value) to indicate which point within that section we should interpolate to.

We do that with two macros:
Code: [Select]
//convert uv to index - 6bits:256bits. 6 bits of index and 256bits of interpolation
#define TCB_uv2index(uv) (((uv) - TCB_curve.uV_LOW) * ((TC_POINTS - 1) << 8) / (TCB_curve.uV_HIGH - TCB_curve.uV_LOW))

//256-point interpolation
#define TCB_itp(i6, i256) (((TCB_curve.Cx1000[i6+1] - TCB_curve.Cx1000[i6]) * i256) >> 8)

TCB_uv2index() generates a 6+8 bit value that contains i6 and i256.
TCB_itp() does the interpolation calculation.

Tie them together:

All of them is tied together with a function:

Code: [Select]
//convert uv to mC(=Cx1000)
signed long TCB_uv2c(signed long uv) {
unsigned char *i256 = &uv; //i256 points to uv - endian dependent
unsigned char *i6=i256+1; //i6 = piece-wise index, i256 = index within a piece

uv = TCB_uv2index(uv); //convert uv to 6+8bit index
return TCB_curve.Cx1000[*i6] + TCB_itp(*i6, *i256); //=piece-wise value + interpolation
}

*i6 and *i256 are two pointers that breaks down the 14-bit index (now contained in uv). This speeds up the execution considerably, at the expense of its endian-dependence.

Accuracy:

Here is the run for Type B over the entire temperature range.

Code: [Select]
uv =        291, TCB_uv2c(uv) =     249916, TCB9_mv2c(uv) =   249.9161
uv =        391, TCB_uv2c(uv) =     284215, TCB9_mv2c(uv) =   286.6775
uv =        491, TCB_uv2c(uv) =     318515, TCB9_mv2c(uv) =   319.1925
uv =        591, TCB_uv2c(uv) =     347255, TCB9_mv2c(uv) =   348.6309
uv =        691, TCB_uv2c(uv) =     375004, TCB9_mv2c(uv) =   375.7608
uv =        791, TCB_uv2c(uv) =     400362, TCB9_mv2c(uv) =   401.0867
uv =        891, TCB_uv2c(uv) =     424393, TCB9_mv2c(uv) =   424.9460
uv =        991, TCB_uv2c(uv) =     447043, TCB9_mv2c(uv) =   447.5751
uv =       1091, TCB_uv2c(uv) =     468586, TCB9_mv2c(uv) =   469.1500
uv =       1191, TCB_uv2c(uv) =     489393, TCB9_mv2c(uv) =   489.8108
uv =       1291, TCB_uv2c(uv) =     509281, TCB9_mv2c(uv) =   509.6737
uv =       1391, TCB_uv2c(uv) =     528642, TCB9_mv2c(uv) =   528.8361
uv =       1491, TCB_uv2c(uv) =     546981, TCB9_mv2c(uv) =   547.3782
uv =       1591, TCB_uv2c(uv) =     565201, TCB9_mv2c(uv) =   565.3645
uv =       1691, TCB_uv2c(uv) =     582439, TCB9_mv2c(uv) =   582.8455
uv =       1791, TCB_uv2c(uv) =     599822, TCB9_mv2c(uv) =   599.8616
uv =       1891, TCB_uv2c(uv) =     616174, TCB9_mv2c(uv) =   616.4483
uv =       1991, TCB_uv2c(uv) =     632494, TCB9_mv2c(uv) =   632.6412
uv =       2091, TCB_uv2c(uv) =     648206, TCB9_mv2c(uv) =   648.4791
uv =       2191, TCB_uv2c(uv) =     663911, TCB9_mv2c(uv) =   664.0013
uv =       2291, TCB_uv2c(uv) =     679061, TCB9_mv2c(uv) =   679.2353
uv =       2391, TCB_uv2c(uv) =     694002, TCB9_mv2c(uv) =   694.1690
uv =       2491, TCB_uv2c(uv) =     708638, TCB9_mv2c(uv) =   708.8216
uv =       2591, TCB_uv2c(uv) =     722991, TCB9_mv2c(uv) =   723.2098
uv =       2691, TCB_uv2c(uv) =     737274, TCB9_mv2c(uv) =   737.3565
uv =       2791, TCB_uv2c(uv) =     751126, TCB9_mv2c(uv) =   751.2740
uv =       2891, TCB_uv2c(uv) =     764876, TCB9_mv2c(uv) =   764.9744
uv =       2991, TCB_uv2c(uv) =     778279, TCB9_mv2c(uv) =   778.4685
uv =       3091, TCB_uv2c(uv) =     791653, TCB9_mv2c(uv) =   791.7668
uv =       3191, TCB_uv2c(uv) =     804764, TCB9_mv2c(uv) =   804.8789
uv =       3291, TCB_uv2c(uv) =     817766, TCB9_mv2c(uv) =   817.8138
uv =       3391, TCB_uv2c(uv) =     830432, TCB9_mv2c(uv) =   830.5800
uv =       3491, TCB_uv2c(uv) =     843074, TCB9_mv2c(uv) =   843.1854
uv =       3591, TCB_uv2c(uv) =     855461, TCB9_mv2c(uv) =   855.6376
uv =       3691, TCB_uv2c(uv) =     867880, TCB9_mv2c(uv) =   867.9433
uv =       3791, TCB_uv2c(uv) =     880007, TCB9_mv2c(uv) =   880.1091
uv =       3891, TCB_uv2c(uv) =     892027, TCB9_mv2c(uv) =   892.1411
uv =       3991, TCB_uv2c(uv) =     903916, TCB9_mv2c(uv) =   904.0449
uv =       4091, TCB_uv2c(uv) =     915767, TCB9_mv2c(uv) =   915.8260
uv =       4191, TCB_uv2c(uv) =     927433, TCB9_mv2c(uv) =   927.4892
uv =       4291, TCB_uv2c(uv) =     938938, TCB9_mv2c(uv) =   939.0393
uv =       4391, TCB_uv2c(uv) =     950400, TCB9_mv2c(uv) =   950.4807
uv =       4491, TCB_uv2c(uv) =     961679, TCB9_mv2c(uv) =   961.8175
uv =       4591, TCB_uv2c(uv) =     973043, TCB9_mv2c(uv) =   973.0535
uv =       4691, TCB_uv2c(uv) =     984113, TCB9_mv2c(uv) =   984.1925
uv =       4791, TCB_uv2c(uv) =     995183, TCB9_mv2c(uv) =   995.2378
uv =       4891, TCB_uv2c(uv) =    1006080, TCB9_mv2c(uv) =  1006.1926
uv =       4991, TCB_uv2c(uv) =    1016957, TCB9_mv2c(uv) =  1017.0602
uv =       5091, TCB_uv2c(uv) =    1027790, TCB9_mv2c(uv) =  1027.8432
uv =       5191, TCB_uv2c(uv) =    1038487, TCB9_mv2c(uv) =  1038.5445
uv =       5291, TCB_uv2c(uv) =    1049083, TCB9_mv2c(uv) =  1049.1666
uv =       5391, TCB_uv2c(uv) =    1059613, TCB9_mv2c(uv) =  1059.7120
uv =       5491, TCB_uv2c(uv) =    1070072, TCB9_mv2c(uv) =  1070.1831
uv =       5591, TCB_uv2c(uv) =    1080532, TCB9_mv2c(uv) =  1080.5821
uv =       5691, TCB_uv2c(uv) =    1090859, TCB9_mv2c(uv) =  1090.9112
uv =       5791, TCB_uv2c(uv) =    1101085, TCB9_mv2c(uv) =  1101.1723
uv =       5891, TCB_uv2c(uv) =    1111289, TCB9_mv2c(uv) =  1111.3674
uv =       5991, TCB_uv2c(uv) =    1121461, TCB9_mv2c(uv) =  1121.4985
uv =       6091, TCB_uv2c(uv) =    1131547, TCB9_mv2c(uv) =  1131.5673
uv =       6191, TCB_uv2c(uv) =    1141505, TCB9_mv2c(uv) =  1141.5756
uv =       6291, TCB_uv2c(uv) =    1151464, TCB9_mv2c(uv) =  1151.5251
uv =       6391, TCB_uv2c(uv) =    1161316, TCB9_mv2c(uv) =  1161.4173
uv =       6491, TCB_uv2c(uv) =    1171234, TCB9_mv2c(uv) =  1171.2541
uv =       6591, TCB_uv2c(uv) =    1180987, TCB9_mv2c(uv) =  1181.0367
uv =       6691, TCB_uv2c(uv) =    1190708, TCB9_mv2c(uv) =  1190.7668
uv =       6791, TCB_uv2c(uv) =    1200367, TCB9_mv2c(uv) =  1200.4459
uv =       6891, TCB_uv2c(uv) =    1209980, TCB9_mv2c(uv) =  1210.0753
uv =       6991, TCB_uv2c(uv) =    1219630, TCB9_mv2c(uv) =  1219.6564
uv =       7091, TCB_uv2c(uv) =    1229141, TCB9_mv2c(uv) =  1229.1907
uv =       7191, TCB_uv2c(uv) =    1238625, TCB9_mv2c(uv) =  1238.6794
uv =       7291, TCB_uv2c(uv) =    1248040, TCB9_mv2c(uv) =  1248.1239
uv =       7391, TCB_uv2c(uv) =    1257444, TCB9_mv2c(uv) =  1257.5254
uv =       7491, TCB_uv2c(uv) =    1266848, TCB9_mv2c(uv) =  1266.8853
uv =       7591, TCB_uv2c(uv) =    1276174, TCB9_mv2c(uv) =  1276.2047
uv =       7691, TCB_uv2c(uv) =    1285417, TCB9_mv2c(uv) =  1285.4849
uv =       7791, TCB_uv2c(uv) =    1294658, TCB9_mv2c(uv) =  1294.7271
uv =       7891, TCB_uv2c(uv) =    1303912, TCB9_mv2c(uv) =  1303.9324
uv =       7991, TCB_uv2c(uv) =    1313075, TCB9_mv2c(uv) =  1313.1020
uv =       8091, TCB_uv2c(uv) =    1322187, TCB9_mv2c(uv) =  1322.2371
uv =       8191, TCB_uv2c(uv) =    1331276, TCB9_mv2c(uv) =  1331.3388
uv =       8291, TCB_uv2c(uv) =    1340330, TCB9_mv2c(uv) =  1340.4082
uv =       8391, TCB_uv2c(uv) =    1349427, TCB9_mv2c(uv) =  1349.4463
uv =       8491, TCB_uv2c(uv) =    1358424, TCB9_mv2c(uv) =  1358.4543
uv =       8591, TCB_uv2c(uv) =    1367382, TCB9_mv2c(uv) =  1367.4333
uv =       8691, TCB_uv2c(uv) =    1376325, TCB9_mv2c(uv) =  1376.3842
uv =       8791, TCB_uv2c(uv) =    1385225, TCB9_mv2c(uv) =  1385.3080
uv =       8891, TCB_uv2c(uv) =    1394195, TCB9_mv2c(uv) =  1394.2059
uv =       8991, TCB_uv2c(uv) =    1403041, TCB9_mv2c(uv) =  1403.0788
uv =       9091, TCB_uv2c(uv) =    1411886, TCB9_mv2c(uv) =  1411.9277
uv =       9191, TCB_uv2c(uv) =    1420685, TCB9_mv2c(uv) =  1420.7535
uv =       9291, TCB_uv2c(uv) =    1429481, TCB9_mv2c(uv) =  1429.5572
uv =       9391, TCB_uv2c(uv) =    1438316, TCB9_mv2c(uv) =  1438.3398
uv =       9491, TCB_uv2c(uv) =    1447068, TCB9_mv2c(uv) =  1447.1023
uv =       9591, TCB_uv2c(uv) =    1455793, TCB9_mv2c(uv) =  1455.8454
uv =       9691, TCB_uv2c(uv) =    1464504, TCB9_mv2c(uv) =  1464.5702
uv =       9791, TCB_uv2c(uv) =    1473269, TCB9_mv2c(uv) =  1473.2776
uv =       9891, TCB_uv2c(uv) =    1481944, TCB9_mv2c(uv) =  1481.9684
uv =       9991, TCB_uv2c(uv) =    1490607, TCB9_mv2c(uv) =  1490.6436
uv =      10091, TCB_uv2c(uv) =    1499249, TCB9_mv2c(uv) =  1499.3041
uv =      10191, TCB_uv2c(uv) =    1507885, TCB9_mv2c(uv) =  1507.9506
uv =      10291, TCB_uv2c(uv) =    1516572, TCB9_mv2c(uv) =  1516.5841
uv =      10391, TCB_uv2c(uv) =    1525184, TCB9_mv2c(uv) =  1525.2055
uv =      10491, TCB_uv2c(uv) =    1533774, TCB9_mv2c(uv) =  1533.8157
uv =      10591, TCB_uv2c(uv) =    1542364, TCB9_mv2c(uv) =  1542.4154
uv =      10691, TCB_uv2c(uv) =    1550935, TCB9_mv2c(uv) =  1551.0057
uv =      10791, TCB_uv2c(uv) =    1559576, TCB9_mv2c(uv) =  1559.5872
uv =      10891, TCB_uv2c(uv) =    1568133, TCB9_mv2c(uv) =  1568.1610
uv =      10991, TCB_uv2c(uv) =    1576686, TCB9_mv2c(uv) =  1576.7278
uv =      11091, TCB_uv2c(uv) =    1585232, TCB9_mv2c(uv) =  1585.2886
uv =      11191, TCB_uv2c(uv) =    1593773, TCB9_mv2c(uv) =  1593.8443
uv =      11291, TCB_uv2c(uv) =    1602382, TCB9_mv2c(uv) =  1602.3956
uv =      11391, TCB_uv2c(uv) =    1610914, TCB9_mv2c(uv) =  1610.9435
uv =      11491, TCB_uv2c(uv) =    1619446, TCB9_mv2c(uv) =  1619.4890
uv =      11591, TCB_uv2c(uv) =    1627974, TCB9_mv2c(uv) =  1628.0328
uv =      11691, TCB_uv2c(uv) =    1636574, TCB9_mv2c(uv) =  1636.5759
uv =      11791, TCB_uv2c(uv) =    1645103, TCB9_mv2c(uv) =  1645.1192
uv =      11891, TCB_uv2c(uv) =    1653632, TCB9_mv2c(uv) =  1653.6636
uv =      11991, TCB_uv2c(uv) =    1662165, TCB9_mv2c(uv) =  1662.2101
uv =      12091, TCB_uv2c(uv) =    1670698, TCB9_mv2c(uv) =  1670.7595
uv =      12191, TCB_uv2c(uv) =    1679310, TCB9_mv2c(uv) =  1679.3127
uv =      12291, TCB_uv2c(uv) =    1687851, TCB9_mv2c(uv) =  1687.8708
uv =      12391, TCB_uv2c(uv) =    1696403, TCB9_mv2c(uv) =  1696.4347
uv =      12491, TCB_uv2c(uv) =    1704957, TCB9_mv2c(uv) =  1705.0052
uv =      12591, TCB_uv2c(uv) =    1713521, TCB9_mv2c(uv) =  1713.5833
uv =      12691, TCB_uv2c(uv) =    1722165, TCB9_mv2c(uv) =  1722.1699
uv =      12791, TCB_uv2c(uv) =    1730745, TCB9_mv2c(uv) =  1730.7659
uv =      12891, TCB_uv2c(uv) =    1739338, TCB9_mv2c(uv) =  1739.3723
uv =      12991, TCB_uv2c(uv) =    1747938, TCB9_mv2c(uv) =  1747.9898
uv =      13091, TCB_uv2c(uv) =    1756556, TCB9_mv2c(uv) =  1756.6193
uv =      13191, TCB_uv2c(uv) =    1765250, TCB9_mv2c(uv) =  1765.2618
uv =      13291, TCB_uv2c(uv) =    1773897, TCB9_mv2c(uv) =  1773.9179
uv =      13391, TCB_uv2c(uv) =    1782545, TCB9_mv2c(uv) =  1782.5884
uv =      13491, TCB_uv2c(uv) =    1791224, TCB9_mv2c(uv) =  1791.2740
uv =      13591, TCB_uv2c(uv) =    1799977, TCB9_mv2c(uv) =  1799.9754
uv =      13691, TCB_uv2c(uv) =    1808686, TCB9_mv2c(uv) =  1808.6932
uv =      13791, TCB_uv2c(uv) =    1817400, TCB9_mv2c(uv) =  1817.4278

Warning:
1) there is no boundary check.
2) endian-dependent.

When I get more time, I will get some sense of how it performs in terms of speed + storage vs. the other approaches.

================================
https://dannyelectronics.wordpress.com/
 

Offline f5r5e5d

  • Frequent Contributor
  • **
  • Posts: 349
Re: Polynomial approximation for thermocouples
« Reply #20 on: June 08, 2014, 12:55:58 am »
cubic spline interpolation for your lookup table isn't hard - I've done it in assembly language in fixed point - with variable knots

optimum knot placement is harder - but that math can be done on your PC in Matlab or similar
 

Offline dannyfTopic starter

  • Super Contributor
  • ***
  • Posts: 8221
  • Country: 00
Re: Polynomial approximation for thermocouples
« Reply #21 on: June 08, 2014, 01:26:20 am »
Rough numbers:

MDK: 135 cycles per calculation, vs. 300 cycles per floating point calculation;
XC16: 550 cycles per calculation;
XC8: 2300 cycles vs. 4500 cycles for floating point math (non-optimized).

Seems to be that
1. on 32-bit chips, using floating point math is a good compromise;
2. On 8-bit chips, using fixed point math works if you use 1 or 2 types of thermcouples, and you don't already use floating point math somewhere else in your code, or speed is critical - in that case, using a thermocouple amplifier is likely the way to go. Optimization makes no material difference for the 8-bit case.

You basically trade speed for space.
================================
https://dannyelectronics.wordpress.com/
 

Online IanB

  • Super Contributor
  • ***
  • Posts: 12588
  • Country: us
Re: Polynomial approximation for thermocouples
« Reply #22 on: June 08, 2014, 02:32:15 am »
Data structure:

Roughly the same, but rather than storing coefficients, we now store data points:

Code: [Select]
typedef struct {
signed long uV_LOW; //low range of the input voltage, uv
signed long uV_HIGH; //high range of the input voltage, uv
signed long Cx1000[TC_POINTS]; //temperature points, in 0.001 Celsius
} TCCURVE_TYPEDEF;

Here is the curve for Type B:

Code: [Select]
/TC TYPE B curve
const TCCURVE_TYPEDEF TCB_curve ={
291, 13820, {
249916.0747, 323704.7308, 383399.8668, 435096.9286, 481439.2474, 523865.2027, 563318.3990, 600402.6075,
635512.0226, 669016.1792, 701160.7584, 732037.5081, 761835.4734, 790670.0534, 818641.4668, 845836.6596,
872331.0218, 898189.9269, 923470.1060, 948220.8699, 972485.1895, 996300.6468, 1019700.267, 1042713.24,
1065365.549, 1087680.500, 1109679.183, 1131380.850, 1152803.236, 1173962.825, 1194875.058, 1215554.514,
1236015.038, 1256269.853, 1276331.638, 1296212.593, 1315924.481, 1335478.664, 1354886.134, 1374157.53,
1393303.160, 1412333.021, 1431256.820, 1450084.002, 1468823.774, 1487485.140, 1506076.941, 1524607.89,
1543086.619, 1561521.724, 1579921.805, 1598295.511, 1616651.571, 1634998.822, 1653346.219, 1671702.834,
1690077.826, 1708480.392, 1726919.679, 1745404.660, 1763943.968, 1782545.676, 1801217.016, 1819964.035
}
};

I'm not following here. You've declared your TC_POINTS data type as signed long, yet you are filling it apparently with floating point data? What is happening with the fractional parts after the decimal point?
 

Offline dannyfTopic starter

  • Super Contributor
  • ***
  • Posts: 8221
  • Country: 00
Re: Polynomial approximation for thermocouples
« Reply #23 on: June 08, 2014, 01:35:14 pm »
i profiled the code. TVB_uv2c() takes considerable amount of time to execute, being of long types.

A few ways to shorten that:

1) use int types and scale them;
2) record temperatures in offset -  reduce storage and increase speed;
3) for TCB_itp(), use pointers to perform the shift.
================================
https://dannyelectronics.wordpress.com/
 

Offline mrflibble

  • Super Contributor
  • ***
  • Posts: 2051
  • Country: nl
Re: Polynomial approximation for thermocouples
« Reply #24 on: June 08, 2014, 04:01:30 pm »
I'm not following here. You've declared your TC_POINTS data type as signed long, yet you are filling it apparently with floating point data? What is happening with the fractional parts after the decimal point?
Probably it's multiplied by a factor of 1000 and then cast to integer, so as to use fixed point operations. As clearly documented. ;) Keeping the fractional part in that struct was probably just to make things interesting. Or maybe it's just a *need coffee* indicator. :-//
 


Share me

Digg  Facebook  SlashDot  Delicious  Technorati  Twitter  Google  Yahoo
Smf