Skip to content

Commit

Permalink
[Rules] Add some basic math functions + documentation
Browse files Browse the repository at this point in the history
Moved `abs` to also be the more intuitive `abs(x)` notation.
Added:
* Exp
* Sq
* Round
  • Loading branch information
TD-er committed Jan 11, 2021
1 parent 80159a4 commit e7aa4b4
Show file tree
Hide file tree
Showing 4 changed files with 125 additions and 22 deletions.
81 changes: 78 additions & 3 deletions docs/source/Rules/Rules.rst
Original file line number Diff line number Diff line change
Expand Up @@ -801,15 +801,15 @@ Abs

Perform ABS on integer values.

Usage: ``{abs:<value>}``
Usage: ``abs(<value>)``

With:

* ``<value>`` The number to convert into an absolute value, if it is representing a valid numerical value.

For example:

* ``{abs:-1}`` Return the absolute value => 1
* ``abs(-1)`` Return the absolute value => 1

.. note::

Expand All @@ -820,7 +820,7 @@ For example:
on eventname do
let,1,%eventvalue1% // Don't change the value
let,2,{bitset:9:{abs:%eventvalue1%}} // Convert to positive and set bit '9'
let,2,{bitset:9:abs(%eventvalue1%)} // Convert to positive and set bit '9'
LogEntry,'Values {tobin:[int#1]} {tohex:[int#1]}'
LogEntry,'Values {tobin:[int#2]} {tohex:[int#2]}'
endon
Expand Down Expand Up @@ -855,6 +855,81 @@ With:
* ``<low>`` Lower end of range, if it is representing a valid numerical value.
* ``<high>`` Higher end of range, if it is representing a valid numerical value.

Math Functions
--------------

(Added: 2021-01-10)

ESPEasy also supports some math functions, like trigonometric functions, but also some more basic functions.

Basic Math Functions
^^^^^^^^^^^^^^^^^^^^

* ``log(x)`` Logarithm of x to base 10.
* ``ln(x)`` Natural logarithm of x.
* ``abs(x)`` Absolute value of x.
* ``exp(x)`` Exponential value, e^x.
* ``sqrt(x)`` Square root of x. (x^0.5)
* ``sq(x)`` Square of x, x^2.
* ``round(x)`` Rounds to the nearest integer, but rounds halfway cases away from zero (instead of to the nearest even integer).

Rules example:

.. code-block:: none
on eventname2 do
let,1,sq(%eventvalue1%)
let,2,sqrt([var#1])
let,3,=log(%eventvalue2%)
let,4,ln(%eventvalue2%)
LogEntry,'sqrt of [var#1] = [var#2]'
LogEntry,'log of %eventvalue2% = [var#3]'
LogEntry,'ln of %eventvalue2% = [var#4]'
endon
Called with event ``eventname2=1.234,100``

.. code-block:: none
213293 : Info : EVENT: eventname2=1.234,100
213307 : Info : ACT : let,1,sq(1.234)
213316 : Info : ACT : let,2,sqrt(1.522756)
213328 : Info : ACT : let,3,=log(100)
213337 : Info : ACT : let,4,ln(100)
213346 : Info : ACT : LogEntry,'sqrt of 1.522756 = 1.234'
213351 : Info : sqrt of 1.522756 = 1.234
213357 : Info : ACT : LogEntry,'log of 100 = 2'
213361 : Info : log of 100 = 2
213369 : Info : ACT : LogEntry,'ln of 100 = 4.60517018598809'
213374 : Info : ln of 100 = 4.60517018598809
Trigonometric Functions
^^^^^^^^^^^^^^^^^^^^^^^

Since the trigonometric functions add quite a bit to the compiled binary, these functions are not included in builds which have a flag defined to limit their build size.

All trigonometric functions are present in 2 versions, for angles in radian and with the ``_d`` suffix for angles in degree.

Radian Angle:

* ``sin(x)`` Sine of x (radian)
* ``cos(x)`` Cosine of x (radian)
* ``tan(x)`` Tangent of x (radian)
* ``arcSin(x)`` Arc Sine of x (radian)
* ``arcCos(x)`` Arc Cosine of x (radian)
* ``arcTan(x)`` Arc Tangent of x (radian)

Degree Angle:

* ``sin_d(x)`` Sine of x (degree)
* ``cos_d(x)`` Cosine of x (degree)
* ``tan_d(x)`` Tangent of x (degree)
* ``arcSin_d(x)`` Arc Sine of x (degree)
* ``arcCos_d(x)`` Arc Cosine of x (degree)
* ``arcTan_d(x)`` Arc Tangent of x (degree)




Expand Down
6 changes: 1 addition & 5 deletions src/src/ESPEasyCore/ESPEasyRules.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -566,11 +566,7 @@ bool parse_math_functions(const String& cmd_s_lower, const String& arg1, const S
if (!validDoubleFromString(arg1, farg1)) {
return false;
}
if (cmd_s_lower.equals(F("abs"))) {
// Turn number into positive value
// Syntax like {abs:-1} -> '1'
result = farg1 < 0.0 ? farg1 * -1.0 : farg1;
} else if (cmd_s_lower.equals(F("constrain"))) {
if (cmd_s_lower.equals(F("constrain"))) {
// Contrain a value X to be within range of A to B
// Syntax like {constrain:x:a:b} to constrain x in range a...b
if (validFloatFromString(arg2, farg2) && validFloatFromString(arg3, farg3)) {
Expand Down
28 changes: 28 additions & 0 deletions src/src/Helpers/Rules_calculate.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,11 @@ bool RulesCalculate_t::is_unary_operator(char c)
case UnaryOperator::Not:
case UnaryOperator::Log:
case UnaryOperator::Ln:
case UnaryOperator::Abs:
case UnaryOperator::Exp:
case UnaryOperator::Sqrt:
case UnaryOperator::Sq:
case UnaryOperator::Round:
case UnaryOperator::Sin:
case UnaryOperator::Cos:
case UnaryOperator::Tan:
Expand Down Expand Up @@ -121,8 +125,16 @@ double RulesCalculate_t::apply_unary_operator(char op, double first)
return log10(first);
case UnaryOperator::Ln:
return log(first);
case UnaryOperator::Abs:
return fabs(first);
case UnaryOperator::Exp:
return exp(first);
case UnaryOperator::Sqrt:
return sqrt(first);
case UnaryOperator::Sq:
return first * first;
case UnaryOperator::Round:
return round(first);
default:
break;
}
Expand Down Expand Up @@ -504,9 +516,21 @@ String toString(UnaryOperator op)
case UnaryOperator::Ln:
find = F("ln");
break;
case UnaryOperator::Abs:
find = F("abs");
break;
case UnaryOperator::Exp:
find = F("exp");
break;
case UnaryOperator::Sqrt:
find = F("sqrt");
break;
case UnaryOperator::Sq:
find = F("sq");
break;
case UnaryOperator::Round:
find = F("round");
break;
case UnaryOperator::Sin:
case UnaryOperator::Sin_d:
find = F("sin");
Expand Down Expand Up @@ -546,7 +570,11 @@ String RulesCalculate_t::preProces(const String& input)
preProcessReplace(preprocessed, UnaryOperator::Not);
preProcessReplace(preprocessed, UnaryOperator::Log);
preProcessReplace(preprocessed, UnaryOperator::Ln);
preProcessReplace(preprocessed, UnaryOperator::Abs);
preProcessReplace(preprocessed, UnaryOperator::Exp);
preProcessReplace(preprocessed, UnaryOperator::Sqrt);
preProcessReplace(preprocessed, UnaryOperator::Sq);
preProcessReplace(preprocessed, UnaryOperator::Round);
#ifdef USE_TRIGONOMETRIC_FUNCTIONS_RULES
// Try the "arc" functions first, or else "sin" is already replaced when "asin" is tried.
if (preprocessed.indexOf(F("sin")) != -1) {
Expand Down
32 changes: 18 additions & 14 deletions src/src/Helpers/Rules_calculate.h
Original file line number Diff line number Diff line change
Expand Up @@ -29,20 +29,24 @@ bool isError(CalculateReturnCode returnCode);
enum class UnaryOperator {
Not = '!',
Log = 192, // Start at some ASCII code we don't expect in the rules.
Ln,
Sqrt,
Sin,
Sin_d,
Cos,
Cos_d,
Tan,
Tan_d,
ArcSin,
ArcSin_d,
ArcCos,
ArcCos_d,
ArcTan,
ArcTan_d
Ln, // Natural logarithm
Abs, // Absolute value
Exp, // exponential value, e^x
Sqrt, // Square Root
Sq, // Square, x^2
Round, // Rounds to the nearest integer, but rounds halfway cases away from zero (instead of to the nearest even integer).
Sin, // Sine (radian)
Sin_d, // Sine (degree)
Cos, // Cosine (radian)
Cos_d, // Cosine (degree)
Tan, // Tangent (radian)
Tan_d, // Tangent (degree)
ArcSin, // Arc Sine (radian)
ArcSin_d, // Arc Sine (degree)
ArcCos, // Arc Cosine (radian)
ArcCos_d, // Arc Cosine (degree)
ArcTan, // Arc Tangent (radian)
ArcTan_d // Arc Tangent (degree)
};

void preProcessReplace(String & input,
Expand Down

0 comments on commit e7aa4b4

Please sign in to comment.