From 15e37ef0bbefb3aa3bd925518b0686a8ab5ca06f Mon Sep 17 00:00:00 2001 From: Stephan Hadinger Date: Mon, 1 Jul 2019 18:20:43 +0200 Subject: [PATCH 1/2] Change converted double to float in rules, and replaced trigonometric functions from stdlib with smaller versions. --- sonoff/_changelog.ino | 1 + sonoff/support.ino | 10 +- sonoff/support_float.ino | 257 +++++++++++++++++++++++++++++++++++ sonoff/xdrv_09_timers.ino | 4 +- sonoff/xdrv_10_rules.ino | 72 +++++----- sonoff/xdrv_10_scripter.ino | 10 +- sonoff/xdrv_11_knx.ino | 2 +- sonoff/xnrg_01_hlw8012.ino | 6 +- sonoff/xnrg_02_cse7766.ino | 6 +- sonoff/xnrg_04_mcp39f501.ino | 8 +- sonoff/xnrg_07_ade7953.ino | 2 +- sonoff/xsns_02_analog.ino | 2 +- sonoff/xsns_34_hx711.ino | 6 +- sonoff/xsns_38_az7798.ino | 4 +- 14 files changed, 324 insertions(+), 66 deletions(-) create mode 100644 sonoff/support_float.ino diff --git a/sonoff/_changelog.ino b/sonoff/_changelog.ino index 0ebffba3b..a1871ad18 100644 --- a/sonoff/_changelog.ino +++ b/sonoff/_changelog.ino @@ -7,6 +7,7 @@ * Add define USE_DHT to my_user_config.h to save space in sonoff-basic.bin * Change TLS+AWS IoT optimization for speed, code and memory footprint * Add command SetOption40 0..250 to disable button functionality if activated for over 0.1 second. Needs SetOption1 1 and SetOption13 0 (#5449) + * Change converted double to float in rules, and replaced trigonometric functions from stdlib with smaller versions. * * 6.5.0.15 20190606 * Change pubsubclient MQTT_KEEPALIVE from 10 to 30 seconds in preparation of AWS IoT support diff --git a/sonoff/support.ino b/sonoff/support.ino index be441f167..ba0a7bbad 100644 --- a/sonoff/support.ino +++ b/sonoff/support.ino @@ -224,7 +224,7 @@ char* subStr(char* dest, char* str, const char *delim, int index) return sub; } -double CharToDouble(const char *str) +float CharToFloat(const char *str) { // simple ascii to double, because atof or strtod are too large char strbuf[24]; @@ -237,23 +237,23 @@ double CharToDouble(const char *str) if (*pt == '-') { sign = -1; } if (*pt == '-' || *pt=='+') { pt++; } // Skip any sign - double left = 0; + float left = 0; if (*pt != '.') { left = atoi(pt); // Get left part while (isdigit(*pt)) { pt++; } // Skip number } - double right = 0; + float right = 0; if (*pt == '.') { pt++; right = atoi(pt); // Decimal part while (isdigit(*pt)) { pt++; - right /= 10.0; + right /= 10.0f; } } - double result = left + right; + float result = left + right; if (sign < 0) { return -result; // Add negative sign } diff --git a/sonoff/support_float.ino b/sonoff/support_float.ino new file mode 100644 index 000000000..e7b7e62d6 --- /dev/null +++ b/sonoff/support_float.ino @@ -0,0 +1,257 @@ +/* + support_float.ino - support for Sonoff-Tasmota + + Copyright (C) 2019 Theo Arends + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +// All code adapted from: http://www.ganssle.com/approx.htm + +/// ======================================== +// The following code implements approximations to various trig functions. +// +// This is demo code to guide developers in implementing their own approximation +// software. This code is merely meant to illustrate algorithms. + +inline float sinf(float x) { return sin_52(x); } +inline float cosf(float x) { return cos_52(x); } +inline float tanf(float x) { return tan_56(x); } +inline float atanf(float x) { return atan_66(x); } +inline float asinf(float x) { return asinf1(x); } +inline float acosf(float x) { return acosf1(x); } +inline float sqrtf(float x) { return sqrt1(x); } + +// Math constants we'll use +double const f_pi=3.1415926535897932384626433; // f_pi +double const f_twopi=2.0*f_pi; // f_pi times 2 +double const f_two_over_pi= 2.0/f_pi; // 2/f_pi +double const f_halfpi=f_pi/2.0; // f_pi divided by 2 +double const f_threehalfpi=3.0*f_pi/2.0; // f_pi times 3/2, used in tan routines +double const f_four_over_pi=4.0/f_pi; // 4/f_pi, used in tan routines +double const f_qtrpi=f_pi/4.0; // f_pi/4.0, used in tan routines +double const f_sixthpi=f_pi/6.0; // f_pi/6.0, used in atan routines +double const f_tansixthpi=tan(f_sixthpi); // tan(f_pi/6), used in atan routines +double const f_twelfthpi=f_pi/12.0; // f_pi/12.0, used in atan routines +double const f_tantwelfthpi=tan(f_twelfthpi); // tan(f_pi/12), used in atan routines + +// ********************************************************* +// *** +// *** Routines to compute sine and cosine to 5.2 digits +// *** of accuracy. +// *** +// ********************************************************* +// +// cos_52s computes cosine (x) +// +// Accurate to about 5.2 decimal digits over the range [0, f_pi/2]. +// The input argument is in radians. +// +// Algorithm: +// cos(x)= c1 + c2*x**2 + c3*x**4 + c4*x**6 +// which is the same as: +// cos(x)= c1 + x**2(c2 + c3*x**2 + c4*x**4) +// cos(x)= c1 + x**2(c2 + x**2(c3 + c4*x**2)) +// +float cos_52s(float x) +{ +const float c1= 0.9999932946; +const float c2=-0.4999124376; +const float c3= 0.0414877472; +const float c4=-0.0012712095; + +float x2; // The input argument squared + +x2=x * x; +return (c1 + x2*(c2 + x2*(c3 + c4*x2))); +} + +// +// This is the main cosine approximation "driver" +// It reduces the input argument's range to [0, f_pi/2], +// and then calls the approximator. +// See the notes for an explanation of the range reduction. +// +float cos_52(float x){ + int quad; // what quadrant are we in? + + x=fmodf(x, f_twopi); // Get rid of values > 2* f_pi + if(x<0)x=-x; // cos(-x) = cos(x) + quad=int(x * (float)f_two_over_pi); // Get quadrant # (0 to 3) we're in + switch (quad){ + case 0: return cos_52s(x); + case 1: return -cos_52s((float)f_pi-x); + case 2: return -cos_52s(x-(float)f_pi); + case 3: return cos_52s((float)f_twopi-x); + } +} +// +// The sine is just cosine shifted a half-f_pi, so +// we'll adjust the argument and call the cosine approximation. +// +float sin_52(float x){ + return cos_52((float)f_halfpi-x); +} + +// ********************************************************* +// *** +// *** Routines to compute tangent to 5.6 digits +// *** of accuracy. +// *** +// ********************************************************* +// +// tan_56s computes tan(f_pi*x/4) +// +// Accurate to about 5.6 decimal digits over the range [0, f_pi/4]. +// The input argument is in radians. Note that the function +// computes tan(f_pi*x/4), NOT tan(x); it's up to the range +// reduction algorithm that calls this to scale things properly. +// +// Algorithm: +// tan(x)= x(c1 + c2*x**2)/(c3 + x**2) +// +float tan_56s(float x) +{ +const float c1=-3.16783027; +const float c2= 0.134516124; +const float c3=-4.033321984; + +float x2; // The input argument squared + +x2=x * x; +return (x*(c1 + c2 * x2)/(c3 + x2)); +} + +// +// This is the main tangent approximation "driver" +// It reduces the input argument's range to [0, f_pi/4], +// and then calls the approximator. +// See the notes for an explanation of the range reduction. +// Enter with positive angles only. +// +// WARNING: We do not test for the tangent approaching infinity, +// which it will at x=f_pi/2 and x=3*f_pi/2. If this is a problem +// in your application, take appropriate action. +// +float tan_56(float x){ + int octant; // what octant are we in? + + x=fmodf(x, (float)f_twopi); // Get rid of values >2 *f_pi + octant=int(x * (float)f_four_over_pi); // Get octant # (0 to 7) + switch (octant){ + case 0: return tan_56s(x *(float)f_four_over_pi); + case 1: return 1.0f/tan_56s(((float)f_halfpi-x) *(float)f_four_over_pi); + case 2: return -1.0f/tan_56s((x-(float)f_halfpi) *(float)f_four_over_pi); + case 3: return - tan_56s(((float)f_pi-x) *(float)f_four_over_pi); + case 4: return tan_56s((x-(float)f_pi) *(float)f_four_over_pi); + case 5: return 1.0f/tan_56s(((float)f_threehalfpi-x)*(float)f_four_over_pi); + case 6: return -1.0f/tan_56s((x-(float)f_threehalfpi)*(float)f_four_over_pi); + case 7: return - tan_56s(((float)f_twopi-x) *(float)f_four_over_pi); + } +} + +// ********************************************************* +// *** +// *** Routines to compute arctangent to 6.6 digits +// *** of accuracy. +// *** +// ********************************************************* +// +// atan_66s computes atan(x) +// +// Accurate to about 6.6 decimal digits over the range [0, f_pi/12]. +// +// Algorithm: +// atan(x)= x(c1 + c2*x**2)/(c3 + x**2) +// +float atan_66s(float x) +{ +const float c1=1.6867629106; +const float c2=0.4378497304; +const float c3=1.6867633134; + +float x2; // The input argument squared + +x2=x * x; +return (x*(c1 + x2*c2)/(c3 + x2)); +} + +// +// This is the main arctangent approximation "driver" +// It reduces the input argument's range to [0, f_pi/12], +// and then calls the approximator. +// +// +float atan_66(float x){ + float y; // return from atan__s function + bool complement= false; // true if arg was >1 + bool region= false; // true depending on region arg is in + bool sign= false; // true if arg was < 0 + + if (x <0 ){ + x=-x; + sign=true; // arctan(-x)=-arctan(x) + } + if (x > 1.0){ + x=1.0/x; // keep arg between 0 and 1 + complement=true; + } + if (x > (float)f_tantwelfthpi){ + x = (x-(float)f_tansixthpi)/(1+(float)f_tansixthpi*x); // reduce arg to under tan(f_pi/12) + region=true; + } + + y=atan_66s(x); // run the approximation + if (region) y+=(float)f_sixthpi; // correct for region we're in + if (complement)y=(float)f_halfpi-y; // correct for 1/x if we did that + if (sign)y=-y; // correct for negative arg + return (y); +} + +float asinf1(float x) { + float d = 1.0f - x*x; + if (d < 0.0f) { return nanf(""); } + return 2 * atan_66(x / (1 + sqrt1(d))); +} + +float acosf1(float x) { + float d = 1.0f - x*x; + if (d < 0.0f) { return nanf(""); } + float y = asinf1(sqrt1(d)); + if (x >= 0.0f) { + return y; + } else { + return (float)f_pi - y; + } +} + +// https://www.codeproject.com/Articles/69941/Best-Square-Root-Method-Algorithm-Function-Precisi +float sqrt1(const float x) +{ + union + { + int i; + float x; + } u; + u.x = x; + u.i = (1<<29) + (u.i >> 1) - (1<<22); + + // Two Babylonian Steps (simplified from:) + // u.x = 0.5f * (u.x + x/u.x); + // u.x = 0.5f * (u.x + x/u.x); + u.x = u.x + x/u.x; + u.x = 0.25f*u.x + x/u.x; + + return u.x; +} diff --git a/sonoff/xdrv_09_timers.ino b/sonoff/xdrv_09_timers.ino index 5b7dce6fb..4de82c817 100644 --- a/sonoff/xdrv_09_timers.ino +++ b/sonoff/xdrv_09_timers.ino @@ -487,7 +487,7 @@ bool TimerCommand(void) #ifdef USE_SUNRISE else if (CMND_LONGITUDE == command_code) { if (XdrvMailbox.data_len) { - Settings.longitude = (int)(CharToDouble(XdrvMailbox.data) *1000000); + Settings.longitude = (int)(CharToFloat(XdrvMailbox.data) *1000000); } char lbuff[33]; dtostrfd(((float)Settings.longitude) /1000000, 6, lbuff); @@ -495,7 +495,7 @@ bool TimerCommand(void) } else if (CMND_LATITUDE == command_code) { if (XdrvMailbox.data_len) { - Settings.latitude = (int)(CharToDouble(XdrvMailbox.data) *1000000); + Settings.latitude = (int)(CharToFloat(XdrvMailbox.data) *1000000); } char lbuff[33]; dtostrfd(((float)Settings.latitude) /1000000, 6, lbuff); diff --git a/sonoff/xdrv_10_rules.ino b/sonoff/xdrv_10_rules.ino index 61bb27796..e4da6939b 100644 --- a/sonoff/xdrv_10_rules.ino +++ b/sonoff/xdrv_10_rules.ino @@ -179,7 +179,7 @@ bool RulesRuleMatch(uint8_t rule_set, String &event, String &rule) } char rule_svalue[CMDSZ] = { 0 }; - double rule_value = 0; + float rule_value = 0; if (compare != COMPARE_OPERATOR_NONE) { String rule_param = rule_name.substring(pos + strlen(compare_operator)); for (uint32_t i = 0; i < MAX_RULE_VARS; i++) { @@ -225,7 +225,7 @@ bool RulesRuleMatch(uint8_t rule_set, String &event, String &rule) if (temp_value > -1) { rule_value = temp_value; } else { - rule_value = CharToDouble((char*)rule_svalue); // 0.1 - This saves 9k code over toFLoat()! + rule_value = CharToFloat((char*)rule_svalue); // 0.1 - This saves 9k code over toFLoat()! } rule_name = rule_name.substring(0, pos); // "CURRENT" } @@ -235,7 +235,7 @@ bool RulesRuleMatch(uint8_t rule_set, String &event, String &rule) JsonObject &root = jsonBuf.parseObject(event); if (!root.success()) { return false; } // No valid JSON data - double value = 0; + float value = 0; const char* str_value = root[rule_task][rule_name]; //AddLog_P2(LOG_LEVEL_DEBUG, PSTR("RUL: Task %s, Name %s, Value |%s|, TrigCnt %d, TrigSt %d, Source %s, Json %s"), @@ -248,7 +248,7 @@ bool RulesRuleMatch(uint8_t rule_set, String &event, String &rule) // Step 3: Compare rule (value) if (str_value) { - value = CharToDouble((char*)str_value); + value = CharToFloat((char*)str_value); int int_value = int(value); int int_rule_value = int(rule_value); switch (compare) { @@ -797,15 +797,15 @@ String RulesUnsubscribe(const char * data, int data_len) * Parse a number value * Input: * pNumber - A char pointer point to a digit started string (guaranteed) - * value - Reference a double variable used to accept the result + * value - Reference a float variable used to accept the result * Output: * pNumber - Pointer forward to next character after the number - * value - double type, the result value + * value - float type, the result value * Return: * true - succeed * false - failed */ -bool findNextNumber(char * &pNumber, double &value) +bool findNextNumber(char * &pNumber, float &value) { bool bSucceed = false; String sNumber = ""; @@ -818,7 +818,7 @@ bool findNextNumber(char * &pNumber, double &value) } } if (sNumber.length() > 0) { - value = CharToDouble(sNumber.c_str()); + value = CharToFloat(sNumber.c_str()); bSucceed = true; } return bSucceed; @@ -826,18 +826,18 @@ bool findNextNumber(char * &pNumber, double &value) /********************************************************************************************/ /* - * Parse a variable (like VAR1, MEM3) and get its value (double type) + * Parse a variable (like VAR1, MEM3) and get its value (float type) * Input: * pVarname - A char pointer point to a variable name string - * value - Reference a double variable used to accept the result + * value - Reference a float variable used to accept the result * Output: * pVarname - Pointer forward to next character after the variable - * value - double type, the result value + * value - float type, the result value * Return: * true - succeed * false - failed */ -bool findNextVariableValue(char * &pVarname, double &value) +bool findNextVariableValue(char * &pVarname, float &value) { bool succeed = true; value = 0; @@ -854,12 +854,12 @@ bool findNextVariableValue(char * &pVarname, double &value) if (sVarName.startsWith(F("VAR"))) { int index = sVarName.substring(3).toInt(); if (index > 0 && index <= MAX_RULE_VARS) { - value = CharToDouble(vars[index -1]); + value = CharToFloat(vars[index -1]); } } else if (sVarName.startsWith(F("MEM"))) { int index = sVarName.substring(3).toInt(); if (index > 0 && index <= MAX_RULE_MEMS) { - value = CharToDouble(Settings.mems[index -1]); + value = CharToFloat(Settings.mems[index -1]); } } else if (sVarName.equals(F("TIME"))) { value = MinutesPastMidnight(); @@ -891,15 +891,15 @@ bool findNextVariableValue(char * &pVarname, double &value) * - An expression enclosed with a pair of round brackets, (.....) * Input: * pointer - A char pointer point to a place of the expression string - * value - Reference a double variable used to accept the result + * value - Reference a float variable used to accept the result * Output: * pointer - Pointer forward to next character after next object - * value - double type, the result value + * value - float type, the result value * Return: * true - succeed * false - failed */ -bool findNextObjectValue(char * &pointer, double &value) +bool findNextObjectValue(char * &pointer, float &value) { bool bSucceed = false; while (*pointer) @@ -984,15 +984,15 @@ bool findNextOperator(char * &pointer, int8_t &op) * Calculate a simple expression composed by 2 value and 1 operator, like 2 * 3 * Input: * pointer - A char pointer point to a place of the expression string - * value - Reference a double variable used to accept the result + * value - Reference a float variable used to accept the result * Output: * pointer - Pointer forward to next character after next object - * value - double type, the result value + * value - float type, the result value * Return: * true - succeed * false - failed */ -double calculateTwoValues(double v1, double v2, uint8_t op) +float calculateTwoValues(float v1, float v2, uint8_t op) { switch (op) { @@ -1025,7 +1025,7 @@ double calculateTwoValues(double v1, double v2, uint8_t op) * expression - The expression to be evaluated * len - Length of the expression * Return: - * double - result. + * float - result. * 0 - if the expression is invalid * An example: * MEM1 = 3, MEM2 = 6, VAR2 = 15, VAR10 = 80 @@ -1045,17 +1045,17 @@ double calculateTwoValues(double v1, double v2, uint8_t op) * 2 + 1 * 3 / 2 */ -double evaluateExpression(const char * expression, unsigned int len) +float evaluateExpression(const char * expression, unsigned int len) { char expbuf[len + 1]; memcpy(expbuf, expression, len); expbuf[len] = '\0'; char * scan_pointer = expbuf; - LinkedList object_values; + LinkedList object_values; LinkedList operators; int8_t op; - double va; + float va; //Find and add the value of first object if (findNextObjectValue(scan_pointer, va)) { object_values.add(va); @@ -1154,7 +1154,7 @@ bool RulesCommand(void) else if ((CMND_RULETIMER == command_code) && (index > 0) && (index <= MAX_RULE_TIMERS)) { if (XdrvMailbox.data_len > 0) { #ifdef USE_EXPRESSION - double timer_set = evaluateExpression(XdrvMailbox.data, XdrvMailbox.data_len); + float timer_set = evaluateExpression(XdrvMailbox.data, XdrvMailbox.data_len); rules_timer[index -1] = (timer_set > 0) ? millis() + (1000 * timer_set) : 0; #else rules_timer[index -1] = (XdrvMailbox.payload > 0) ? millis() + (1000 * XdrvMailbox.payload) : 0; @@ -1202,7 +1202,7 @@ bool RulesCommand(void) } else if ((CMND_ADD == command_code) && (index > 0) && (index <= MAX_RULE_VARS)) { if (XdrvMailbox.data_len > 0) { - double tempvar = CharToDouble(vars[index -1]) + CharToDouble(XdrvMailbox.data); + float tempvar = CharToFloat(vars[index -1]) + CharToFloat(XdrvMailbox.data); dtostrfd(tempvar, Settings.flag2.calc_resolution, vars[index -1]); bitSet(vars_event, index -1); } @@ -1210,7 +1210,7 @@ bool RulesCommand(void) } else if ((CMND_SUB == command_code) && (index > 0) && (index <= MAX_RULE_VARS)) { if (XdrvMailbox.data_len > 0) { - double tempvar = CharToDouble(vars[index -1]) - CharToDouble(XdrvMailbox.data); + float tempvar = CharToFloat(vars[index -1]) - CharToFloat(XdrvMailbox.data); dtostrfd(tempvar, Settings.flag2.calc_resolution, vars[index -1]); bitSet(vars_event, index -1); } @@ -1218,7 +1218,7 @@ bool RulesCommand(void) } else if ((CMND_MULT == command_code) && (index > 0) && (index <= MAX_RULE_VARS)) { if (XdrvMailbox.data_len > 0) { - double tempvar = CharToDouble(vars[index -1]) * CharToDouble(XdrvMailbox.data); + float tempvar = CharToFloat(vars[index -1]) * CharToFloat(XdrvMailbox.data); dtostrfd(tempvar, Settings.flag2.calc_resolution, vars[index -1]); bitSet(vars_event, index -1); } @@ -1229,12 +1229,12 @@ bool RulesCommand(void) if (strstr(XdrvMailbox.data, ",") != nullptr) { // Process parameter entry char sub_string[XdrvMailbox.data_len +1]; - double valueIN = CharToDouble(subStr(sub_string, XdrvMailbox.data, ",", 1)); - double fromLow = CharToDouble(subStr(sub_string, XdrvMailbox.data, ",", 2)); - double fromHigh = CharToDouble(subStr(sub_string, XdrvMailbox.data, ",", 3)); - double toLow = CharToDouble(subStr(sub_string, XdrvMailbox.data, ",", 4)); - double toHigh = CharToDouble(subStr(sub_string, XdrvMailbox.data, ",", 5)); - double value = map_double(valueIN, fromLow, fromHigh, toLow, toHigh); + float valueIN = CharToFloat(subStr(sub_string, XdrvMailbox.data, ",", 1)); + float fromLow = CharToFloat(subStr(sub_string, XdrvMailbox.data, ",", 2)); + float fromHigh = CharToFloat(subStr(sub_string, XdrvMailbox.data, ",", 3)); + float toLow = CharToFloat(subStr(sub_string, XdrvMailbox.data, ",", 4)); + float toHigh = CharToFloat(subStr(sub_string, XdrvMailbox.data, ",", 5)); + float value = map_double(valueIN, fromLow, fromHigh, toLow, toHigh); dtostrfd(value, Settings.flag2.calc_resolution, vars[index -1]); bitSet(vars_event, index -1); } @@ -1254,7 +1254,7 @@ bool RulesCommand(void) return serviced; } -double map_double(double x, double in_min, double in_max, double out_min, double out_max) +float map_double(float x, float in_min, float in_max, float out_min, float out_max) { return (x - in_min) * (out_max - out_min) / (in_max - in_min) + out_min; } @@ -1302,4 +1302,4 @@ bool Xdrv10(uint8_t function) } #endif // Do not USE_SCRIPT -#endif // USE_RULES \ No newline at end of file +#endif // USE_RULES diff --git a/sonoff/xdrv_10_scripter.ino b/sonoff/xdrv_10_scripter.ino index 93ff63878..284128033 100644 --- a/sonoff/xdrv_10_scripter.ino +++ b/sonoff/xdrv_10_scripter.ino @@ -298,7 +298,7 @@ char *script; op++; if (*op!='"') { float fv; - fv=CharToDouble(op); + fv=CharToFloat(op); fvalues[nvars]=fv; vtypes[vars].bits.is_string=0; if (!vtypes[vars].bits.is_filter) vtypes[vars].index=nvars; @@ -670,7 +670,7 @@ char *isvar(char *lp, uint8_t *vtype,struct T_INDEX *tind,float *fp,char *sp,Jso if (isdigit(*lp) || (*lp=='-' && isdigit(*(lp+1))) || *lp=='.') { // isnumber - if (fp) *fp=CharToDouble(lp); + if (fp) *fp=CharToFloat(lp); if (*lp=='-') lp++; while (isdigit(*lp) || *lp=='.') { if (*lp==0 || *lp==SCRIPT_EOL) break; @@ -839,7 +839,7 @@ char *isvar(char *lp, uint8_t *vtype,struct T_INDEX *tind,float *fp,char *sp,Jso return lp+len; } } else { - if (fp) *fp=CharToDouble((char*)str_value); + if (fp) *fp=CharToFloat((char*)str_value); *vtype=NUM_RES; tind->bits.constant=1; tind->bits.is_string=0; @@ -2266,7 +2266,7 @@ int16_t Run_Scripter(const char *type, uint8_t tlen, char *js) { // mismatch was string, not number // get the string and convert to number lp=isvar(slp,&vtype,&ind,0,cmpstr,jo); - fvar=CharToDouble(cmpstr); + fvar=CharToFloat(cmpstr); } switch (lastop) { case OPER_EQU: @@ -2398,7 +2398,7 @@ int16_t Run_Scripter(const char *type, uint8_t tlen, char *js) { *dfvar=fparam; } else { // mismatch - *dfvar=CharToDouble(cmpstr); + *dfvar=CharToFloat(cmpstr); } } else { // string result diff --git a/sonoff/xdrv_11_knx.ino b/sonoff/xdrv_11_knx.ino index d0a2c9212..7a477bff8 100644 --- a/sonoff/xdrv_11_knx.ino +++ b/sonoff/xdrv_11_knx.ino @@ -1030,7 +1030,7 @@ bool KnxCommand(void) while ( i != KNX_Empty ) { KNX_addr.value = Settings.knx_GA_addr[i]; - float tempvar = CharToDouble(XdrvMailbox.data); + float tempvar = CharToFloat(XdrvMailbox.data); dtostrfd(tempvar,2,XdrvMailbox.data); knx.write_2byte_float(KNX_addr, tempvar); diff --git a/sonoff/xnrg_01_hlw8012.ino b/sonoff/xnrg_01_hlw8012.ino index 503d4e50f..73f31c91f 100644 --- a/sonoff/xnrg_01_hlw8012.ino +++ b/sonoff/xnrg_01_hlw8012.ino @@ -287,17 +287,17 @@ bool HlwCommand(void) } else if (CMND_POWERSET == energy_command_code) { if (XdrvMailbox.data_len && hlw_cf_power_pulse_length) { - Settings.energy_power_calibration = ((unsigned long)(CharToDouble(XdrvMailbox.data) * 10) * hlw_cf_power_pulse_length) / hlw_power_ratio; + Settings.energy_power_calibration = ((unsigned long)(CharToFloat(XdrvMailbox.data) * 10) * hlw_cf_power_pulse_length) / hlw_power_ratio; } } else if (CMND_VOLTAGESET == energy_command_code) { if (XdrvMailbox.data_len && hlw_cf1_voltage_pulse_length) { - Settings.energy_voltage_calibration = ((unsigned long)(CharToDouble(XdrvMailbox.data) * 10) * hlw_cf1_voltage_pulse_length) / hlw_voltage_ratio; + Settings.energy_voltage_calibration = ((unsigned long)(CharToFloat(XdrvMailbox.data) * 10) * hlw_cf1_voltage_pulse_length) / hlw_voltage_ratio; } } else if (CMND_CURRENTSET == energy_command_code) { if (XdrvMailbox.data_len && hlw_cf1_current_pulse_length) { - Settings.energy_current_calibration = ((unsigned long)(CharToDouble(XdrvMailbox.data)) * hlw_cf1_current_pulse_length) / hlw_current_ratio; + Settings.energy_current_calibration = ((unsigned long)(CharToFloat(XdrvMailbox.data)) * hlw_cf1_current_pulse_length) / hlw_current_ratio; } } else serviced = false; // Unknown command diff --git a/sonoff/xnrg_02_cse7766.ino b/sonoff/xnrg_02_cse7766.ino index c546571bb..f9d6d81e2 100644 --- a/sonoff/xnrg_02_cse7766.ino +++ b/sonoff/xnrg_02_cse7766.ino @@ -225,17 +225,17 @@ bool CseCommand(void) if (CMND_POWERSET == energy_command_code) { if (XdrvMailbox.data_len && power_cycle) { - Settings.energy_power_calibration = (unsigned long)(CharToDouble(XdrvMailbox.data) * power_cycle) / CSE_PREF; + Settings.energy_power_calibration = (unsigned long)(CharToFloat(XdrvMailbox.data) * power_cycle) / CSE_PREF; } } else if (CMND_VOLTAGESET == energy_command_code) { if (XdrvMailbox.data_len && voltage_cycle) { - Settings.energy_voltage_calibration = (unsigned long)(CharToDouble(XdrvMailbox.data) * voltage_cycle) / CSE_UREF; + Settings.energy_voltage_calibration = (unsigned long)(CharToFloat(XdrvMailbox.data) * voltage_cycle) / CSE_UREF; } } else if (CMND_CURRENTSET == energy_command_code) { if (XdrvMailbox.data_len && current_cycle) { - Settings.energy_current_calibration = (unsigned long)(CharToDouble(XdrvMailbox.data) * current_cycle) / 1000; + Settings.energy_current_calibration = (unsigned long)(CharToFloat(XdrvMailbox.data) * current_cycle) / 1000; } } else serviced = false; // Unknown command diff --git a/sonoff/xnrg_04_mcp39f501.ino b/sonoff/xnrg_04_mcp39f501.ino index 86bb81855..6171ef93a 100644 --- a/sonoff/xnrg_04_mcp39f501.ino +++ b/sonoff/xnrg_04_mcp39f501.ino @@ -604,7 +604,7 @@ bool McpCommand(void) if (CMND_POWERSET == energy_command_code) { if (XdrvMailbox.data_len && mcp_active_power) { - value = (unsigned long)(CharToDouble(XdrvMailbox.data) * 100); + value = (unsigned long)(CharToFloat(XdrvMailbox.data) * 100); if ((value > 100) && (value < 200000)) { // Between 1W and 2000W Settings.energy_power_calibration = value; mcp_calibrate |= MCP_CALIBRATE_POWER; @@ -614,7 +614,7 @@ bool McpCommand(void) } else if (CMND_VOLTAGESET == energy_command_code) { if (XdrvMailbox.data_len && mcp_voltage_rms) { - value = (unsigned long)(CharToDouble(XdrvMailbox.data) * 10); + value = (unsigned long)(CharToFloat(XdrvMailbox.data) * 10); if ((value > 1000) && (value < 2600)) { // Between 100V and 260V Settings.energy_voltage_calibration = value; mcp_calibrate |= MCP_CALIBRATE_VOLTAGE; @@ -624,7 +624,7 @@ bool McpCommand(void) } else if (CMND_CURRENTSET == energy_command_code) { if (XdrvMailbox.data_len && mcp_current_rms) { - value = (unsigned long)(CharToDouble(XdrvMailbox.data) * 10); + value = (unsigned long)(CharToFloat(XdrvMailbox.data) * 10); if ((value > 100) && (value < 80000)) { // Between 10mA and 8A Settings.energy_current_calibration = value; mcp_calibrate |= MCP_CALIBRATE_CURRENT; @@ -634,7 +634,7 @@ bool McpCommand(void) } else if (CMND_FREQUENCYSET == energy_command_code) { if (XdrvMailbox.data_len && mcp_line_frequency) { - value = (unsigned long)(CharToDouble(XdrvMailbox.data) * 1000); + value = (unsigned long)(CharToFloat(XdrvMailbox.data) * 1000); if ((value > 45000) && (value < 65000)) { // Between 45Hz and 65Hz Settings.energy_frequency_calibration = value; mcp_calibrate |= MCP_CALIBRATE_FREQUENCY; diff --git a/sonoff/xnrg_07_ade7953.ino b/sonoff/xnrg_07_ade7953.ino index 09566266b..c11a283ae 100644 --- a/sonoff/xnrg_07_ade7953.ino +++ b/sonoff/xnrg_07_ade7953.ino @@ -184,7 +184,7 @@ bool Ade7953Command(void) { bool serviced = true; - uint32_t value = (uint32_t)(CharToDouble(XdrvMailbox.data) * 100); // 1.23 = 123 + uint32_t value = (uint32_t)(CharToFloat(XdrvMailbox.data) * 100); // 1.23 = 123 if (CMND_POWERCAL == energy_command_code) { if (1 == XdrvMailbox.payload) { XdrvMailbox.payload = ADE7953_PREF; } diff --git a/sonoff/xsns_02_analog.ino b/sonoff/xsns_02_analog.ino index 9a0c94029..6dbfcc53e 100644 --- a/sonoff/xsns_02_analog.ino +++ b/sonoff/xsns_02_analog.ino @@ -151,7 +151,7 @@ bool AdcCommand(void) // Settings.adc_param_type = my_adc0; Settings.adc_param1 = strtol(subStr(sub_string, XdrvMailbox.data, ",", 2), nullptr, 10); Settings.adc_param2 = strtol(subStr(sub_string, XdrvMailbox.data, ",", 3), nullptr, 10); - Settings.adc_param3 = (int)(CharToDouble(subStr(sub_string, XdrvMailbox.data, ",", 4)) * 10000); + Settings.adc_param3 = (int)(CharToFloat(subStr(sub_string, XdrvMailbox.data, ",", 4)) * 10000); } else { // Set default values based on current adc type // AdcParam 2 // AdcParam 3 diff --git a/sonoff/xsns_34_hx711.ino b/sonoff/xsns_34_hx711.ino index 47a38f7d3..8a362ddfd 100644 --- a/sonoff/xsns_34_hx711.ino +++ b/sonoff/xsns_34_hx711.ino @@ -204,7 +204,7 @@ bool HxCommand(void) break; case 6: // WeightItem if (strstr(XdrvMailbox.data, ",") != nullptr) { - Settings.weight_item = (unsigned long)(CharToDouble(subStr(sub_string, XdrvMailbox.data, ",", 2)) * 10); + Settings.weight_item = (unsigned long)(CharToFloat(subStr(sub_string, XdrvMailbox.data, ",", 2)) * 10); } show_parms = true; break; @@ -444,7 +444,7 @@ void HandleHxAction(void) if (WebServer->hasArg("calibrate")) { WebGetArg("p1", stemp1, sizeof(stemp1)); - Settings.weight_reference = (!strlen(stemp1)) ? 0 : (unsigned long)(CharToDouble(stemp1) * 1000); + Settings.weight_reference = (!strlen(stemp1)) ? 0 : (unsigned long)(CharToFloat(stemp1) * 1000); HxLogUpdates(); @@ -471,7 +471,7 @@ void HxSaveSettings(void) char tmp[100]; WebGetArg("p2", tmp, sizeof(tmp)); - Settings.weight_item = (!strlen(tmp)) ? 0 : (unsigned long)(CharToDouble(tmp) * 10000); + Settings.weight_item = (!strlen(tmp)) ? 0 : (unsigned long)(CharToFloat(tmp) * 10000); HxLogUpdates(); } diff --git a/sonoff/xsns_38_az7798.ino b/sonoff/xsns_38_az7798.ino index 9bf97a424..5339a392f 100644 --- a/sonoff/xsns_38_az7798.ino +++ b/sonoff/xsns_38_az7798.ino @@ -182,7 +182,7 @@ void AzEverySecond(void) return; } response_substr[j] = 0; // add null terminator - az_temperature = CharToDouble((char*)response_substr); // units (C or F) depends on meter setting + az_temperature = CharToFloat((char*)response_substr); // units (C or F) depends on meter setting if(az_response[i] == 'C') { // meter transmits in degC az_temperature = ConvertTemp((float)az_temperature); // convert to degF, depending on settings } else { // meter transmits in degF @@ -232,7 +232,7 @@ void AzEverySecond(void) return; } response_substr[j] = 0; // add null terminator - az_humidity = ConvertHumidity(CharToDouble((char*)response_substr)); + az_humidity = ConvertHumidity(CharToFloat((char*)response_substr)); } } From d75b6ad8892f71f4ea69dfda865cc11e5afa7834 Mon Sep 17 00:00:00 2001 From: Stephan Hadinger Date: Mon, 1 Jul 2019 18:31:54 +0200 Subject: [PATCH 2/2] Moved FastPrecisePow and TaylorLog to sonoff_float.ino for consistency --- sonoff/support.ino | 60 --------------------------------------- sonoff/support_float.ino | 61 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 61 insertions(+), 60 deletions(-) diff --git a/sonoff/support.ino b/sonoff/support.ino index ba0a7bbad..41e5ffd3b 100644 --- a/sonoff/support.ino +++ b/sonoff/support.ino @@ -650,66 +650,6 @@ void ResetGlobalValues(void) } } -double FastPrecisePow(double a, double b) -{ - // https://martin.ankerl.com/2012/01/25/optimized-approximative-pow-in-c-and-cpp/ - // calculate approximation with fraction of the exponent - int e = abs((int)b); - union { - double d; - int x[2]; - } u = { a }; - u.x[1] = (int)((b - e) * (u.x[1] - 1072632447) + 1072632447); - u.x[0] = 0; - // exponentiation by squaring with the exponent's integer part - // double r = u.d makes everything much slower, not sure why - double r = 1.0; - while (e) { - if (e & 1) { - r *= a; - } - a *= a; - e >>= 1; - } - return r * u.d; -} - -float FastPrecisePowf(const float x, const float y) -{ -// return (float)(pow((double)x, (double)y)); - return (float)FastPrecisePow(x, y); -} - -double TaylorLog(double x) -{ - // https://stackoverflow.com/questions/46879166/finding-the-natural-logarithm-of-a-number-using-taylor-series-in-c - - if (x <= 0.0) { return NAN; } - double z = (x + 1) / (x - 1); // We start from power -1, to make sure we get the right power in each iteration; - double step = ((x - 1) * (x - 1)) / ((x + 1) * (x + 1)); // Store step to not have to calculate it each time - double totalValue = 0; - double powe = 1; - double y; - for (uint32_t count = 0; count < 10; count++) { // Experimental number of 10 iterations - z *= step; - y = (1 / powe) * z; - totalValue = totalValue + y; - powe = powe + 2; - } - totalValue *= 2; -/* - char logxs[33]; - dtostrfd(x, 8, logxs); - double log1 = log(x); - char log1s[33]; - dtostrfd(log1, 8, log1s); - char log2s[33]; - dtostrfd(totalValue, 8, log2s); - AddLog_P2(LOG_LEVEL_DEBUG, PSTR("input %s, log %s, taylor %s"), logxs, log1s, log2s); -*/ - return totalValue; -} - uint32_t SqrtInt(uint32_t num) { if (num <= 1) { diff --git a/sonoff/support_float.ino b/sonoff/support_float.ino index e7b7e62d6..fc2dccf04 100644 --- a/sonoff/support_float.ino +++ b/sonoff/support_float.ino @@ -17,6 +17,66 @@ along with this program. If not, see . */ +double FastPrecisePow(double a, double b) +{ + // https://martin.ankerl.com/2012/01/25/optimized-approximative-pow-in-c-and-cpp/ + // calculate approximation with fraction of the exponent + int e = abs((int)b); + union { + double d; + int x[2]; + } u = { a }; + u.x[1] = (int)((b - e) * (u.x[1] - 1072632447) + 1072632447); + u.x[0] = 0; + // exponentiation by squaring with the exponent's integer part + // double r = u.d makes everything much slower, not sure why + double r = 1.0; + while (e) { + if (e & 1) { + r *= a; + } + a *= a; + e >>= 1; + } + return r * u.d; +} + +float FastPrecisePowf(const float x, const float y) +{ +// return (float)(pow((double)x, (double)y)); + return (float)FastPrecisePow(x, y); +} + +double TaylorLog(double x) +{ + // https://stackoverflow.com/questions/46879166/finding-the-natural-logarithm-of-a-number-using-taylor-series-in-c + + if (x <= 0.0) { return NAN; } + double z = (x + 1) / (x - 1); // We start from power -1, to make sure we get the right power in each iteration; + double step = ((x - 1) * (x - 1)) / ((x + 1) * (x + 1)); // Store step to not have to calculate it each time + double totalValue = 0; + double powe = 1; + double y; + for (uint32_t count = 0; count < 10; count++) { // Experimental number of 10 iterations + z *= step; + y = (1 / powe) * z; + totalValue = totalValue + y; + powe = powe + 2; + } + totalValue *= 2; +/* + char logxs[33]; + dtostrfd(x, 8, logxs); + double log1 = log(x); + char log1s[33]; + dtostrfd(log1, 8, log1s); + char log2s[33]; + dtostrfd(totalValue, 8, log2s); + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("input %s, log %s, taylor %s"), logxs, log1s, log2s); +*/ + return totalValue; +} + // All code adapted from: http://www.ganssle.com/approx.htm /// ======================================== @@ -32,6 +92,7 @@ inline float atanf(float x) { return atan_66(x); } inline float asinf(float x) { return asinf1(x); } inline float acosf(float x) { return acosf1(x); } inline float sqrtf(float x) { return sqrt1(x); } +inline float powf(float x, float y) { return FastPrecisePow(x, y); } // Math constants we'll use double const f_pi=3.1415926535897932384626433; // f_pi