Polynomial approximation for thermocouples - Page 1
Forum section: electronics / general

author: dannyf
author_rank: Super Contributor
timestamp: June 06, 2014, 12:46:04 am
content: 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.

author: dannyf
author_rank: Super Contributor
timestamp: June 06, 2014, 12:49:42 am
content: 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.

author: dannyf
author_rank: Super Contributor
timestamp: June 06, 2014, 12:54:35 am
content: 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.tabHere is the interesting part, at bottom of the page:Code:************************************* 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.431Range: 2.431 13.8209.8423321E+01 2.1315071E+026.9971500E+02 2.8510504E+02-8.4765304E+02 -5.2742887E+011.0052644E+03 9.9160804E+00-8.3345952E+02 -1.2965303E+004.5508542E+02 1.1195870E-01-1.5523037E+02 -6.0625199E-032.9886750E+01 1.8661696E-04-2.4742860E+00 -2.4878585E-06Error -0.02 -0.01Range: 0.03 0.02Essential, we need to implement two 8th order polynomial curves. For simplicity, we will implement it in floating point.

author: dannyf
author_rank: Super Contributor
timestamp: June 06, 2014, 01:05:20 am
content: 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://polynomial curvetypedef struct {double mV_LOW; //starting voltage for a curvedouble mV_HIGH; //ending voltage for a curvedouble coef; //coefficients, 0=constant, 1=1st order, 2=2nd order ...} TCCOEF_TYPEDEF; //polynomial curvesThe two curves for Type B TC would be something like this:Code://TC TypeB NIST polynomialconst 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};

author: dannyf
author_rank: Super Contributor
timestamp: June 06, 2014, 01:13:16 am
content: The polynomial function is fairly straight forward:Code://calculate a polynomial//coef: an array containing the coefficients. coef = constant, coef = 1st order, ...//order: the order of the polynomial to be calculated//mv: TC voltage output, in mv//return: temperature reading, in Celsiusdouble TC_mv2c(const double *coef, unsigned char order, double mv) {double mv_i=mv, tmp; //contains intermediate resultsunsigned char index; //index for the coefficientstmp = *coef++; //pick up the constantfor (index = 1; index <= order; index++) {tmp += (*coef++) * mv_i; //calculate mv^imv_i *= mv; //calculate my^i}return tmp; //return the temperature}So to use the curve, we would do something like this:Code:temperature = TC_mv2c(TCB_table.coef, 9, mv); //if mv is less than 2.431//ortemperature = TC_mv2c(TCB_table.coef, 9, mv); //if mv is greater than 2.431

author: dannyf
author_rank: Super Contributor
timestamp: June 06, 2014, 01:22:15 am
content: 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:enum TC_CURVE {TC_CURVE1 = 0,//thermocouple curve 1TC_CURVE2,//thermocouple curve 2TC_CURVE3,//thermocouple curve 3TC_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://macro used to calculate mv to temperature//Type B, nist (9th-order) polynomial#define TCB9_mv2c(mv) (((mv) > TCB_table.mV_HIGH)?TC_mv2c(TCB_table.coef, 9, mv):TC_mv2c(TCB_table.coef, 9, mv))So in your code, the mapping of themocouple output (mv) to temperature for Type B, NIST polynomial would beCode:Temperature = TCB9_mv2c(mv); //Type B, 9th order polynomial, mv to temperature conversionPerformance: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.

author: dannyf
author_rank: Super Contributor
timestamp: June 06, 2014, 01:31:34 am
content: 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://5th-order polynomial curvetypedef struct {double mV_LOW; //starting voltage for a curvedouble mV_HIGH; //ending voltage for a curvedouble coef; //coefficients, 0=constant, 1=1st order, 2=2nd order ...} TCCOEF5_TYPEDEF; //polynomial curvesTo 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: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://5th-order polynomial conversion function for Type B TC:#define TCB5_mv2c(mv)TC_mv2c(TCB5_table.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.

author: dannyf
author_rank: Super Contributor
timestamp: June 06, 2014, 01:39:50 am
content: 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.

author: retrolefty
author_rank: Super Contributor
timestamp: June 06, 2014, 01:42:57 am
content: 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.

author: mariush
author_rank: Super Contributor
timestamp: June 06, 2014, 01:44:48 am
content: Thank you for explaining it so well.As this thread is related to mythermometer 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) CelsiusA 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.

author: IanB
author_rank: Super Contributor
timestamp: June 06, 2014, 02:39:16 am
content: Quote from: dannyf on June 06, 2014, 01:13:16 amIt looks like your polynomial evaluation code is about twice as slow as it needs to be. Can you see why?

author: dannyf
author_rank: Super Contributor
timestamp: June 06, 2014, 10:45:11 am
content: QuoteThat'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).

author: amyk
author_rank: Super Contributor
timestamp: June 06, 2014, 11:58:25 am
content: 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.

author: miceuz
author_rank: Frequent Contributor
timestamp: June 06, 2014, 12:19:17 pm
content: 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-libSome writeup is here:http://wemakethings.net/2014/04/24/k-thermocouple-lib

author: dannyf
author_rank: Super Contributor
timestamp: June 06, 2014, 01:13:31 pm
content: QuoteEach 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.

author: AG6QR
author_rank: Frequent Contributor
timestamp: June 06, 2014, 03:45:13 pm
content: Quote from: IanB on June 06, 2014, 02:39:16 amHorner'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 writingAx**3 + Bx**2 + Cx + DTransform it into((Ax + B)x + C)x + Dhttp://en.wikipedia.org/wiki/Horner's_methodCode:y =coeff; // pre-load the high order coefficient// loop through remaining coefficientsfor (i = 1; i < order; i++) {y *= x;y += coeff;}

author: miceuz
author_rank: Frequent Contributor
timestamp: June 06, 2014, 03:52:01 pm
content: Quote from: dannyf on June 06, 2014, 01:13:31 pmYes, thanks for correcting me. Or I could at least shave off two bytes from the temperature value.

author: jaxbird
author_rank: Frequent Contributor
timestamp: June 06, 2014, 03:55:05 pm
content: Why not just use targeted lookup tables and piecewise-linear approximation (interpolation/extrapolation) like amyk suggested?

author: dannyf
author_rank: Super Contributor
timestamp: June 06, 2014, 04:44:15 pm
content: It has an advantage if floating point math isn't already used.QuoteYeah. Just remember that sometimes the values (temperature + mv) can go negative.

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

author: f5r5e5d
author_rank: Frequent Contributor
timestamp: June 08, 2014, 12:55:58 am
content: cubic spline interpolation for your lookup table isn't hard - I've done it in assembly language in fixed point - with variable knotsoptimum knot placement is harder - but that math can be done on your PC in Matlab or similar

author: dannyf
author_rank: Super Contributor
timestamp: June 08, 2014, 01:26:20 am
content: 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 that1. 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.

author: IanB
author_rank: Super Contributor
timestamp: June 08, 2014, 02:32:15 am
content: Quote from: dannyf on June 07, 2014, 11:42:34 pmI'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?

author: dannyf
author_rank: Super Contributor
timestamp: June 08, 2014, 01:35:14 pm
content: 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.

author: mrflibble
author_rank: Super Contributor
timestamp: June 08, 2014, 04:01:30 pm
content: Quote from: IanB on June 08, 2014, 02:32:15 amProbably it's multiplied by a factor of 1000 and then cast to integer, so as to use fixed point operations. Asclearlydocumented.Keeping the fractional part in that struct was probably just to make things interesting. Or maybe it's just a *need coffee* indicator.