/****************************************************************************** * Additional info * **************************************************************************** * Changelog: * - v. 2.0 Optimize memory usage. Option to use binary search. * 2016-01-09 by Joakim Soderberg joakim.soderberg@gmail.com * - v. 1.0 Initial release) * 2014-04-24 by Albertas Mickėnas mic@wemakethings.net * * **************************************************************************** * Bugs, feedback, questions and modifications can be posted on the github page * on https://github.com/Miceuz/k-thermocouple-lib/ * **************************************************************************** * - LICENSE - * GNU GPL v2 (http://www.gnu.org/licenses/gpl.txt) * This program is free software. You can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as published * by the Free Software Foundation. * **************************************************************************** */ #include #include "thermocouple.h" #ifdef __AVR__ #include #else #undef KTHERM_WITH_PROGMEM #endif #ifdef KTHERM_TEST #include #endif /* * Values for this table should be multiplied with 1000 when used. * If we store the table as that instead, we need to use unsigned long * For example it would need (on an ATmega328): * 66 * 4 = 264 bytes * instead of: * 66 * 2 = 132 bytes */ #define TEMP_TABLE_SCALE 1000 static const signed int temp_tablek[] #ifdef KTHERM_WITH_PROGMEM PROGMEM #endif = { -250, -240, -230, -220, -210, -200, -190, -180, -170, -160, -150, -140, -130, -120, -110, -100, -90, -80, -70, -60, -50, -40, -30, -20, -10, 0, 10, 20, 30, 40, 50, 60, 79, 98, 116, 134, 139, 155, 172, 193, 212, 231, 250, 269, 288, 307, 326, 345, 364, 383, 402, 421, 440, 459, 478, 497, 516, 535, 554, 573, 592, 611, 630, 649, 668, 687, 706, 725, 744, 763, 782, 801, 820, 839, 858, 877, 896, 915, 934, 953, 972, 991, 1010, 1029, 1048, 1067, 1086, 1105, 1124, 1143, 1200 }; #define TEMP_TABLE_SIZEK (sizeof(temp_table) / sizeof(temp_table[0])) static const signed int volt_tablek[] #ifdef KTHERM_WITH_PROGMEM PROGMEM #endif = { -6404, -6344, -6262, -6158, -6035, -5891, -5730, -5550, -5354, -5141, -4913, -4669, -4411, -4138, -3852, -3554, -3243, -2920, -2587, -2243, -1889, -1527, -1156, -778, -392, 0, 397, 798, 1203, 1612, 2023, 2436, 3225, 4013, 4756, 5491, 5694, 6339, 7021, 7859, 8619, 9383, 10153, 10930, 11712, 12499, 13290, 14084, 14881, 15680, 16482, 17285, 18091, 18898, 19707, 20516, 21326, 22137, 22947, 23757, 24565, 25373, 26179, 26983, 27784, 28584, 29380, 30174, 30964, 31752, 32536, 33316, 34093, 34867, 35637, 36403, 37166, 37925, 38680, 39432, 40180, 40924, 41665, 42402, 43134, 43863, 44588, 45308, 46024, 46735, 48838 }; //T static const signed int temp_tablet[] #ifdef KTHERM_WITH_PROGMEM PROGMEM #endif = { -250, -240, -230, -220, -210, -200, -190, -180, -170, -160, -150, -140, -130, -120, -110, -100, -90, -80, -70, -60, -50, -40, -30, -20, -10, 0, 10, 20, 30, 40, 50, 60, 79, 98, 116, 134, 139, 155, 172, 193, 212, 231, 250, 269, 288, 307, 326, 345, 364, 383, 400 }; #define TEMP_TABLE_SIZET (sizeof(temp_tablet) / sizeof(temp_tablet[0])) static const signed int volt_tablet[] #ifdef KTHERM_WITH_PROGMEM PROGMEM #endif = { -6180, -6105, -6007, -5888, -5753, -5603, -5439, -5261, -5070, -4865, -4648, -4419, -4177, -3923, -3657, -3379, -3089, -2788, -2476, -2153, -1819, -1475, -1121, -757, -383, 0, 391, 790, 1196, 1612, 2036, 2468, 3312, 4185, 5036, 5491, 5910, 6956, 7823, 8917, 9930, 10962, 12013, 13082, 14168, 15270, 16387, 17518, 18665, 19825, 20872 }; #define VOLTAGE_TABLE_SIZEK (sizeof(volt_tablek) / sizeof(volt_tablek[0])) #define VOLTAGE_TABLE_SIZET (sizeof(volt_tablet) / sizeof(volt_tablet[0])) #define POINTS_COUNTK (VOLTAGE_TABLE_SIZEK - 1) #define POINTS_COUNTT (VOLTAGE_TABLE_SIZET - 1) static inline signed long getTableVal(const signed int *t, unsigned char i) { #ifdef KTHERM_WITH_PROGMEM return (unsigned long)pgm_read_word_near(t + i); #else return (signed long)t[i]; #endif } static inline signed long interpolate(signed long val, signed long rangeStart, signed long rangeEnd, signed long valStart, signed long valEnd) { signed long valDiff = valEnd - valStart; if (valDiff == 0) return 0; return rangeStart + (rangeEnd - rangeStart) * (val - valStart) / valDiff; } static inline signed long interpolateVoltagek(signed long temp, signed char i) { return interpolate(temp, getTableVal(volt_tablek, i - 1), getTableVal(volt_tablek, i), getTableVal(temp_tablek, i - 1) * TEMP_TABLE_SCALE, getTableVal(temp_tablek, i) * TEMP_TABLE_SCALE); } static inline signed long interpolateVoltaget(signed long temp, signed char i) { return interpolate(temp, getTableVal(volt_tablet, i - 1), getTableVal(volt_tablet, i), getTableVal(temp_tablet, i - 1) * TEMP_TABLE_SCALE, getTableVal(temp_tablet, i) * TEMP_TABLE_SCALE); } static inline signed long interpolateTemperaturek(signed long microvolts, signed char i) { return interpolate(microvolts, getTableVal(temp_tablek, i - 1) * TEMP_TABLE_SCALE, getTableVal(temp_tablek, i) * TEMP_TABLE_SCALE, getTableVal(volt_tablek, i - 1), getTableVal(volt_tablek, i)); } static inline signed long interpolateTemperaturet(signed long microvolts, signed char i) { return interpolate(microvolts, getTableVal(temp_tablet, i - 1) * TEMP_TABLE_SCALE, getTableVal(temp_tablet, i) * TEMP_TABLE_SCALE, getTableVal(volt_tablet, i - 1), getTableVal(volt_tablet, i)); } static inline unsigned char binarySearchK(const signed int *t, signed long val, signed long scale) { unsigned char first; unsigned char middle; unsigned char last; unsigned long cur; // Note that we skip the first item in the list. first = 1; last = POINTS_COUNTK - 2; middle = (first + last) / 2; while (first <= last) { cur = getTableVal(t, middle) * scale; if (val > cur) { first = middle + 1; } else if (val == cur) { return middle + 1; } else { last = middle - 1; } middle = (first + last) / 2; } return last + 1; } static inline unsigned char binarySearchT(const signed int *t, signed long val, signed long scale) { unsigned char first; unsigned char middle; unsigned char last; unsigned long cur; // Note that we skip the first item in the list. first = 1; last = POINTS_COUNTT - 2; middle = (first + last) / 2; while (first <= last) { cur = getTableVal(t, middle) * scale; if (val > cur) { first = middle + 1; } else if (val == cur) { return middle + 1; } else { last = middle - 1; } middle = (first + last) / 2; } return last + 1; } static inline unsigned char linearSearchK(const signed int *t, signed long val, signed long scale) { unsigned char i; for (i = 0; i < POINTS_COUNTK; i++) { if ((getTableVal(t, i) * scale) > val) { return i; } } return POINTS_COUNTK - 1; } static inline unsigned char linearSearchT(const signed int *t, signed long val, signed long scale) { unsigned char i; for (i = 0; i < POINTS_COUNTT; i++) { if ((getTableVal(t, i) * scale) > val) { return i; } } return POINTS_COUNTT - 1; } /** * Returns the index of the first point whose temperature * value is greater than argument **/ static inline unsigned char searchTempk(signed long temp) { #ifdef KTHERM_WITH_BINARY_SEARCH return binarySearch(temp_table, temp, TEMP_TABLE_SCALE); #else return linearSearchK(temp_tablek, temp, TEMP_TABLE_SCALE); #endif } static inline unsigned char searchTempt(signed long temp) { #ifdef KTHERM_WITH_BINARY_SEARCH return binarySearch(temp_table, temp, TEMP_TABLE_SCALE); #else return linearSearchT(temp_tablet, temp, TEMP_TABLE_SCALE); #endif } /** * Returns the index of the first point whose microvolts * value is greater than argument **/ static inline unsigned char searchMicrovoltsk(signed long microvolts) { #ifdef KTHERM_WITH_BINARY_SEARCH return binarySearch(volt_table, microvolts, 1); #else return linearSearchK(volt_tablek, microvolts, 1); #endif } static inline unsigned char searchMicrovoltst(signed long microvolts) { #ifdef KTHERM_WITH_BINARY_SEARCH return binarySearch(volt_table, microvolts, 1); #else return linearSearchT(volt_tablet, microvolts, 1); #endif } /** * Returns temperature as a function of the ambient temperature * and measured thermocouple voltage. * Currently only positive ambient temperature is supported **/ long thermocoupleConvertWithCJCompensationk(signed long microvoltsMeasured, signed long ambient) { // Convert ambient temp to microvolts // and add them to the thermocouple measured microvolts signed long microvolts = microvoltsMeasured + interpolateVoltagek(ambient, searchTempk(ambient)); // look up microvolts in The Table and interpolate return interpolateTemperaturek(microvolts, searchMicrovoltsk(microvolts)); } long thermocoupleConvertWithCJCompensationt(signed long microvoltsMeasured, signed long ambient) { // Convert ambient temp to microvolts // and add them to the thermocouple measured microvolts signed long microvolts = microvoltsMeasured + interpolateVoltaget(ambient, searchTempt(ambient)); // look up microvolts in The Table and interpolate return interpolateTemperaturet(microvolts, searchMicrovoltst(microvolts)); } /** * Returns temperature, equivalent to the voltage provided in microvolts */ long thermocoupleMvToCk(signed long microvolts) { return interpolateTemperaturek(microvolts, searchMicrovoltsk(microvolts)); } long thermocoupleMvToCt(signed long microvolts) { return interpolateTemperaturet(microvolts, searchMicrovoltst(microvolts)); } #ifdef KTHERM_TEST int main(int argc, char **argv) { unsigned long i = 0; if (argc < 2) { goto usage; } for (i = 1; i < argc; i++) { if (!strcmp(argv[i], "--temps")) { for (i = 0; i < 16383; i++) { unsigned long voltage = 5000000 / 16384 * i / 101; printf("%ld\n", thermocoupleMvToC(voltage)); } } else if (!strcmp(argv[i], "--microvolts")) { for (i = 0; i < 1280000; i+= 1000) { printf("%ld\n", interpolateVoltage(i, searchTemp(i))); } } else { fprintf(stderr, "Unknown option %s\n", argv[i]); goto usage; } } return 0; usage: printf("K-Type Thermocouple Library Tests\n"); printf(" Usage: %s [--microvolts] [--temps]\n", argv[0]); return -1; } #endif