0.2.0 FastTrig

This commit is contained in:
rob tillaart 2022-12-03 12:59:56 +01:00
parent febfc3d3ad
commit e27fc5652e
12 changed files with 154 additions and 137 deletions

View File

@ -22,7 +22,7 @@ compile:
# - leonardo
- m4
- esp32
# - esp8266
- esp8266
# - mega2560
- rpipico

View File

@ -6,14 +6,23 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/)
and this project adheres to [Semantic Versioning](http://semver.org/).
## [0.2.0] - 2022-12-02
- merged PR #16 for ESP_IDF support
- added <stdint.h>
- added <stdbool.h>
- renamed isinTable to sinTable for readability (breaking change)
- edits code for readability
- fix itan(90) now returns NAN instead of 0.
----
## [0.1.11] - 2022-11-02
- add changelog.md
- add rp2040 to build-CI
- moved version info from readme.md to changelog
- no functional changes
## [0.1.10 2022-04-15
## [0.1.10] - 2022-04-15
- fix #12
- split .h in .h and .cpp Needed in case of more complex projects.

View File

@ -0,0 +1,4 @@
idf_component_register(SRCS "FastTrig.cpp"
INCLUDE_DIRS .)
target_compile_options(${COMPONENT_LIB} PRIVATE "-Wno-format")

View File

@ -1,7 +1,7 @@
//
// FILE: FastTrig.cpp
// AUTHOR: Rob Tillaart
// VERSION: 0.1.11
// VERSION: 0.2.0
// PURPOSE: Arduino library for a faster approximation of sin() and cos()
// DATE: 2011-08-18
// URL: https://github.com/RobTillaart/FastTrig
@ -11,9 +11,9 @@
#include "FastTrig.h"
// 91 x 2 bytes ==> 182 bytes
// use 65535.0 as divider
uint16_t isinTable16[] = {
// 91 x 2 bytes ==> 182 bytes
// use 65535.0 as divider
uint16_t sinTable16[] = {
0,
1145, 2289, 3435, 4572, 5716, 6853, 7989, 9125, 10255, 11385,
12508, 13631, 14745, 15859, 16963, 18067, 19165, 20253, 21342, 22417,
@ -28,8 +28,8 @@ uint16_t isinTable16[] = {
};
/* 0.1.4 table
uint16_t isinTable16[] = {
/* 0.1.4 table
uint16_t sinTable16[] = {
0,
1145, 2289, 3435, 4571, 5715, 6852, 7988, 9125, 10254, 11385,
12508, 13630, 14745, 15859, 16963, 18067, 19165, 20253, 21342, 22416,
@ -46,7 +46,7 @@ uint16_t isinTable16[] = {
// use 255.0 as divider
uint8_t isinTable8[] = {
uint8_t sinTable8[] = {
0, 4, 9, 13, 18, 22, 27, 31, 35, 40, 44,
49, 53, 57, 62, 66, 70, 75, 79, 83, 87,
91, 96, 100, 104, 108, 112, 116, 120, 124, 128,
@ -62,154 +62,147 @@ uint8_t isinTable8[] = {
///////////////////////////////////////////////////////
//
// GONIO LOOKUP
// GONIO LOOKUP
//
float isin(float f)
{
boolean pos = true; // positive
if (f < 0)
bool negative = (f < 0);
if (negative)
{
f = -f;
pos = !pos;
negative = true;
}
long x = f;
uint8_t r = (f - x) * 256;
long whole = f;
uint8_t remain = (f - whole) * 256;
if (x >= 360) x %= 360;
if (whole >= 360) whole %= 360;
int y = x; // 16 bit math is faster than 32 bit
int y = whole; // 16 bit math is faster than 32 bit
if (y >= 180)
{
y -= 180;
pos = !pos;
negative = !negative;
}
if (y >= 90)
{
y = 180 - y;
if (r != 0)
if (remain != 0)
{
r = 255 - r;
remain = 255 - remain;
y--;
}
}
// float v improves ~4% on avg error for ~60 bytes.
uint16_t v = isinTable16[y];
// float value improves ~4% on avg error for ~60 bytes.
uint16_t value = sinTable16[y];
// interpolate if needed
if (r > 0)
// interpolate if needed
if (remain > 0)
{
v = v + ((isinTable16[y + 1] - v) / 8 * r) /32; // == * r / 256
value = value + ((sinTable16[y + 1] - value) / 8 * remain) / 32; // == * remain / 256
}
float g = v * 0.0000152590219; // = /65535.0
if (pos) return g;
return -g;
float g = value * 0.0000152590219; // = / 65535.0
if (negative) return -g;
return g;
}
float icos(float x)
{
// prevent modulo math if x in 0..360
return isin(x - 270.0); // better than x + 90;
// prevent modulo math if x in 0..360
return isin(x - 270.0); // better than x + 90;
}
float itan(float f)
{
// reference
// return isin(f)/icos(f);
// reference
// return isin(f)/icos(f);
// idea is to divide two (interpolated) values from the table
// so no divide by 65535
// idea is to divide two (interpolated) values from the table
// so no divide by 65535
// FOLDING
bool mir = false;
bool neg = (f < 0);
if (neg) f = -f;
// FOLDING
bool mirror = false;
bool negative = (f < 0);
if (negative) f = -f;
long x = f;
float rem = f - x;
if (x >= 180) x %= 180;
float v = rem + x; // normalised value 0..179.9999
if (v > 90)
long whole = f;
float remain = f - whole;
if (whole >= 180) whole %= 180;
float value = remain + whole; // normalised value 0..179.9999
if (value > 90)
{
v = 180 - v;
neg = !neg;
mir = true;
value = 180 - value;
negative = !negative;
mirror = true;
}
uint8_t d = v;
if (d == 90) return 0;
uint8_t d = value;
if (d == 90) return NAN;
// COS FIRST
// COS FIRST
uint8_t p = 90 - d;
float co = isinTable16[p];
if (rem != 0)
float co = sinTable16[p];
if (remain != 0)
{
float delta = (isinTable16[p] - isinTable16[p - 1]);
if (mir) co = isinTable16[p - 1] + rem * delta;
else co = isinTable16[p] - rem * delta;
float delta = (sinTable16[p] - sinTable16[p - 1]);
if (mirror) co = sinTable16[p - 1] + remain * delta;
else co = sinTable16[p] - remain * delta;
}
else if (co == 0) return 0;
float si = isinTable16[d];
if (rem != 0) si += rem * (isinTable16[d + 1] - isinTable16[d]);
float si = sinTable16[d];
if (remain != 0) si += remain * (sinTable16[d + 1] - sinTable16[d]);
float ta = si/co;
if (neg) return -ta;
if (negative) return -ta;
return ta;
}
// some problem at 0 but at least we have a icot(x) cotangent.
// some problem at 0 but at least we have a icot(x) cotangent.
float icot(float f)
{
float t = itan(f);
if (t == 0) return NAN;
return 1.0 / t;
float ta = itan(f);
if (ta == 0) return NAN;
return 1.0 / ta;
}
// missing function...
// float cot(float f)
// {
// return 1.0/tan(f);
// }
///////////////////////////////////////////////////////
//
// INVERSE GONIO LOOKUP
// INVERSE GONIO LOOKUP
//
float iasin(float f)
{
bool neg = (f < 0);
if (neg)
bool negative = (f < 0);
if (negative)
{
f = -f;
neg = true;
negative = true;
}
uint16_t val = round(f * 65535);
uint16_t value = round(f * 65535);
uint8_t lo = 0;
uint8_t hi = 90;
while (hi - lo > 1)
{
uint8_t mi = (lo + hi) / 2;
if (isinTable16[mi] == val)
if (sinTable16[mi] == value)
{
if (neg) return -mi;
if (negative) return -mi;
return mi;
}
if (isinTable16[mi] < val) lo = mi;
if (sinTable16[mi] < value) lo = mi;
else hi = mi;
}
float delta = val - isinTable16[lo];
uint16_t range = isinTable16[hi] - isinTable16[lo];
float delta = value - sinTable16[lo];
uint16_t range = sinTable16[hi] - sinTable16[lo];
delta /= range;
if (neg) return -(lo + delta);
if (negative) return -(lo + delta);
return (lo + delta);
}
@ -220,11 +213,11 @@ float iacos(float f)
}
// PLACEHOLDER
// PLACEHOLDER
float iatan(float f)
{
return 0 * f;
}
// -- END OF FILE --
// -- END OF FILE --

View File

@ -2,7 +2,7 @@
//
// FILE: FastTrig.h
// AUTHOR: Rob Tillaart
// VERSION: 0.1.11
// VERSION: 0.2.0
// PURPOSE: Arduino library for a faster approximation of sin() and cos()
// DATE: 2011-08-18
// URL: https://github.com/RobTillaart/FastTrig
@ -11,14 +11,25 @@
// HISTORY: see changelog.md
#ifdef ESP_PLATFORM
#include <math.h>
#include <stdint.h>
#include <stdbool.h>
#else
#include "Arduino.h"
#endif
#define FAST_TRIG_LIB_VERSION (F("0.1.11"))
#define FAST_TRIG_LIB_VERSION (F("0.2.0"))
#ifdef __cplusplus
extern "C"
{
#endif
extern uint16_t sinTable16[];
extern uint8_t sinTable8[];
extern uint16_t isinTable16[];
extern uint8_t isinTable8[];
///////////////////////////////////////////////////////
//
@ -30,23 +41,25 @@ float icos(float x);
float itan(float f);
// some problem at 0 but at least we have a icot(x) cotangent.
// 0 returns NAN but we have a icot(x) cotangent.
float icot(float f);
// missing function...
// float cot(float f);
///////////////////////////////////////////////////////
//
// INVERSE GONIO LOOKUP
// INVERSE GONIO LOOKUP
//
float iasin(float f);
float iacos(float f);
// PLACEHOLDER no good implementation
// PLACEHOLDER no good implementation (do not use).
float iatan(float f);
// -- END OF FILE --
#ifdef __cplusplus
}
#endif
// -- END OF FILE --

View File

@ -44,10 +44,10 @@ This generator sketch can also generate tables with different resolution e.g. 24
So depending on the application these tables can be ideal, but verify they meet your requirements.
The lookup tables used by **isin()** can be used directly in your program, the names are:
- **isinTable16\[\]** index 0..90, values need to be (float) divided by 65535.0
- **isinTable8\[\]** index 0..90, values need to be (float) divided by 255.0
- **sinTable16\[\]** index 0..90, values need to be (float) divided by 65535.0
- **sinTable8\[\]** index 0..90, values need to be (float) divided by 255.0
The **isinTable8** is not really for doing accurate math,
The **sinTable8** is not really for doing accurate math,
however it is great to use in a LEDstrip or motor movements when less accuracy is needed.
Although the tables can be written to, it is advised not to do so.

View File

@ -68,7 +68,7 @@ void generate_bit_sin(int t)
Serial.print("uint");
Serial.print(t > 16 ? 32 : t > 8 ? 16 : 8);
Serial.print("_t isinTable");
Serial.print("_t sinTable");
Serial.print(t);
Serial.print("[] = {\n ");
for (int i = 0; i <= 90; i++)
@ -99,7 +99,7 @@ void generate_bit_cos(int t)
Serial.print("uint");
Serial.print(t > 16 ? 32 : t > 8 ? 16 : 8);
Serial.print("_t icosTable");
Serial.print("_t cosTable");
Serial.print(t);
Serial.print("[] = {\n ");
for (int i = 0; i <= 90; i++)
@ -130,7 +130,7 @@ void generate_bit_tan(int t)
Serial.print("uint");
Serial.print(t > 16 ? 32 : t > 8 ? 16 : 8);
Serial.print("_t itanTable");
Serial.print("_t tanTable");
Serial.print(t);
Serial.print("[] = {\n ");
for (int i = 0; i <= 90; i++)

View File

@ -32,7 +32,7 @@ void setup()
// print the table
for (int i = 0; i <= 90; i++)
{
Serial.print(isinTable16[i]);
Serial.print(sinTable16[i]);
Serial.print(", ");
if (i % 10 == 0) Serial.println();
}
@ -52,14 +52,14 @@ int optimize()
int rv = 0;
for (int i = 1; i < 90; i++) // for every angle
{
int t = isinTable16[i];
int t = sinTable16[i];
int idx = 0;
float minError = getError(i); // what is the current error
bool flag = false;
for (int j = -2; j <= 2; j++) // try if adjacent numbers in table give less error.
{
if (j == 0) continue;
isinTable16[i] = t + j;
sinTable16[i] = t + j;
float e = getError(i);
if (e < minError) // if less than we can update the table.
{
@ -70,8 +70,8 @@ int optimize()
}
}
if (flag) Serial.print('*'); // comment if you do not want see changes.
isinTable16[i] = t + idx;
Serial.print(isinTable16[i]);
sinTable16[i] = t + idx;
Serial.print(sinTable16[i]);
Serial.print(", ");
if (i % 10 == 0) Serial.println();
}

View File

@ -31,7 +31,7 @@
// tables generated with other sketch.
uint32_t isinTable24[] = {
uint32_t sinTable24[] = {
0,
292803, 585516, 878052, 1170319, 1462231, 1753697, 2044628, 2334937, 2624535, 2913333,
3201244, 3488179, 3774052, 4058776, 4342263, 4624427, 4905183, 5184445, 5462127, 5738146,
@ -45,7 +45,7 @@ uint32_t isinTable24[] = {
};
uint32_t isinTable20[] = {
uint32_t sinTable20[] = {
0,
18300, 36595, 54878, 73145, 91389, 109606, 127789, 145933, 164033, 182083,
200078, 218011, 235878, 253673, 271391, 289026, 306574, 324028, 341383, 358634,
@ -59,7 +59,7 @@ uint32_t isinTable20[] = {
};
uint16_t isinTable16[] = {
uint16_t sinTable16[] = {
0,
1144, 2287, 3430, 4571, 5712, 6850, 7987, 9121, 10252, 11380,
12505, 13625, 14742, 15854, 16962, 18064, 19161, 20251, 21336, 22414,
@ -93,9 +93,9 @@ void test_accuracy()
for (int i = 0; i <= 90; i++)
{
float a = sin(i * (PI / 180));
float b = (1.0 * isinTable16[i]) / 65535;
float c = (1.0 * isinTable20[i]) / 1048575;
// float c = (1.0 * isinTable24[i]) / 16777215;
float b = (1.0 * sinTable16[i]) / 65535;
float c = (1.0 * sinTable20[i]) / 1048575;
// float c = (1.0 * sinTable24[i]) / 16777215;
Serial.print(a, 8);
Serial.print('\t');
Serial.print(abs(a - b), 8);
@ -134,7 +134,7 @@ void test_performance()
start = micros();
for (int i = 0; i <= 90; i++)
{
sum += isinTable16[i] * (1 / 65535.0);
sum += sinTable16[i] * (1 / 65535.0);
}
Serial.println(micros() - start);
Serial.println(sum, 8);
@ -144,7 +144,7 @@ void test_performance()
start = micros();
for (int i = 0; i <= 90; i++)
{
sum += isinTable20[i] * (1 / 1048575.0);
sum += sinTable20[i] * (1 / 1048575.0);
}
Serial.println(micros() - start);
Serial.println(sum, 8);
@ -154,7 +154,7 @@ void test_performance()
start = micros();
for (int i = 0; i <= 90; i++)
{
sum += isinTable24[i] * (1 / 16777215.0);
sum += sinTable24[i] * (1 / 16777215.0);
}
Serial.println(micros() - start);
Serial.println(sum, 8);

View File

@ -15,7 +15,7 @@
"type": "git",
"url": "https://github.com/RobTillaart/FastTrig"
},
"version": "0.1.11",
"version": "0.2.0",
"license": "MIT",
"frameworks": "*",
"platforms": "*",

View File

@ -1,5 +1,5 @@
name=FastTrig
version=0.1.11
version=0.2.0
author=Rob Tillaart <rob.tillaart@gmail.com><pete.thompson@yahoo.com>
maintainer=Rob Tillaart <rob.tillaart@gmail.com>
sentence=Arduino library with interpolated lookup for sin() and cos()

View File

@ -39,55 +39,53 @@ unittest_teardown()
}
unittest(test_isinTable16)
unittest(test_sinTable16)
{
fprintf(stderr,"Table16 error is < 0.1%% \n");
const float degrees2radians = PI/180.0;
const float degrees2radians = PI / 180.0;
for (int i = 0; i < 91; i++)
{
assertEqualFloat(sin(i * degrees2radians), isinTable16[i] / 65535.0, 0.001);
assertEqualFloat(sin(i * degrees2radians), sinTable16[i] / 65535.0, 0.001);
}
}
unittest(test_isinTable8)
unittest(test_sinTable8)
{
fprintf(stderr,"Table8 error is < 1%% \n");
const float degrees2radians = PI/180.0;
fprintf(stderr,"Table8 error is < 1.0%% \n");
const float degrees2radians = PI / 180.0;
for (int i = 0; i < 91; i++)
{
assertEqualFloat(sin(i * degrees2radians), isinTable8[i] / 255.0, 0.01);
assertEqualFloat(sin(i * degrees2radians), sinTable8[i] / 255.0, 0.01);
}
}
unittest(test_max_error_table16)
{
fprintf(stderr,"Table16 max error: ");
const float degrees2radians = PI/180.0;
float m = 0;
const float degrees2radians = PI / 180.0;
float maxError = 0;
for (int i = 0; i < 91; i++)
{
float t = abs(sin(i * degrees2radians) - (isinTable16[i] / 65535.0));
if (t > m) m = t;
float t = abs(sin(i * degrees2radians) - (sinTable16[i] / 65535.0));
if (t > maxError) maxError = t;
}
fprintf(stderr,"%2.4f\n", m);
assertEqualFloat(0, m, 0.001);
fprintf(stderr,"Table16 max error: %2.5f\n", maxError);
assertEqualFloat(0, maxError, 0.001);
}
unittest(test_max_error_table8)
{
fprintf(stderr,"Table8 max error: ");
const float degrees2radians = PI/180.0;
float m = 0;
const float degrees2radians = PI / 180.0;
float maxError = 0;
for (int i = 0; i < 91; i++)
{
float t = abs(sin(i * degrees2radians) - (isinTable8[i] / 255.0));
if (t > m) m = t;
float t = abs(sin(i * degrees2radians) - (sinTable8[i] / 255.0));
if (t > maxError) maxError = t;
}
fprintf(stderr,"%2.4f\n", m);
assertEqualFloat(0, m, 0.01);
fprintf(stderr,"Table8 max error: %2.5f\n", maxError);
assertEqualFloat(0, maxError, 0.01);
}