mirror of
https://github.com/RobTillaart/Arduino.git
synced 2024-10-03 18:09:02 -04:00
0.2.0 AverageAngle
This commit is contained in:
parent
f5feb9e053
commit
bbd3240c47
@ -6,7 +6,7 @@ jobs:
|
||||
lint:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/checkout@v3
|
||||
- uses: arduino/arduino-lint-action@v1
|
||||
with:
|
||||
library-manager: update
|
||||
|
@ -8,7 +8,7 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/checkout@v3
|
||||
- uses: ruby/setup-ruby@v1
|
||||
with:
|
||||
ruby-version: 2.6
|
||||
|
@ -10,7 +10,7 @@ jobs:
|
||||
test:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/checkout@v3
|
||||
- name: json-syntax-check
|
||||
uses: limitusus/json-syntax-check@v1
|
||||
with:
|
||||
|
@ -1,7 +1,7 @@
|
||||
//
|
||||
// FILE: AverageAngle.cpp
|
||||
// AUTHOR: Rob Tillaart
|
||||
// VERSION: 0.1.9
|
||||
// VERSION: 0.2.0
|
||||
// DATE: 2017-11-21
|
||||
// PURPOSE: class for averaging angles
|
||||
// URL: https://github.com/RobTillaart/AverageAngle
|
||||
@ -10,6 +10,9 @@
|
||||
#include "AverageAngle.h"
|
||||
|
||||
|
||||
const float AA_OVERFLOW_THRESHOLD = 10000;
|
||||
|
||||
|
||||
AverageAngle::AverageAngle(const enum AngleType type)
|
||||
{
|
||||
_type = type;
|
||||
@ -27,8 +30,20 @@ uint32_t AverageAngle::add(float alpha, float length)
|
||||
{
|
||||
alpha *= GRAD_TO_RAD; // (PI / 200.0);
|
||||
}
|
||||
_sumx += (cos(alpha) * length);
|
||||
_sumy += (sin(alpha) * length);
|
||||
float dx = cos(alpha);
|
||||
float dy = sin(alpha);
|
||||
if (length != 1.0)
|
||||
{
|
||||
dx *= length;
|
||||
dy *= length;
|
||||
}
|
||||
_sumx += dx;
|
||||
_sumy += dy;
|
||||
_error = AVERAGE_ANGLE_OK;
|
||||
if ((abs(_sumx) > AA_OVERFLOW_THRESHOLD) || (abs(_sumy) > AA_OVERFLOW_THRESHOLD))
|
||||
{
|
||||
_error = AVERAGE_ANGLE_OVERFLOW;
|
||||
}
|
||||
_count++;
|
||||
return _count;
|
||||
}
|
||||
@ -36,16 +51,17 @@ uint32_t AverageAngle::add(float alpha, float length)
|
||||
|
||||
void AverageAngle::reset()
|
||||
{
|
||||
_sumx = 0;
|
||||
_sumy = 0;
|
||||
_sumx = 0;
|
||||
_sumy = 0;
|
||||
_count = 0;
|
||||
_error = AVERAGE_ANGLE_OK;
|
||||
}
|
||||
|
||||
|
||||
uint32_t AverageAngle::count()
|
||||
{
|
||||
return _count;
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
float AverageAngle::getAverage()
|
||||
@ -60,6 +76,16 @@ float AverageAngle::getAverage()
|
||||
{
|
||||
angle *= RAD_TO_GRAD; // (200.0 / PI);
|
||||
}
|
||||
|
||||
// error reporting
|
||||
if (isnan(angle))
|
||||
{
|
||||
_error = AVERAGE_ANGLE_SINGULARITY;
|
||||
}
|
||||
else
|
||||
{
|
||||
_error = AVERAGE_ANGLE_OK;
|
||||
}
|
||||
return angle;
|
||||
}
|
||||
|
||||
@ -92,5 +118,13 @@ bool AverageAngle::setType(const enum AngleType type)
|
||||
}
|
||||
|
||||
|
||||
// -- END OF FILE --
|
||||
int AverageAngle::lastError()
|
||||
{
|
||||
int e = _error;
|
||||
_error = AVERAGE_ANGLE_OK;
|
||||
return e;
|
||||
}
|
||||
|
||||
|
||||
// -- END OF FILE --
|
||||
|
||||
|
@ -2,7 +2,7 @@
|
||||
//
|
||||
// FILE: AverageAngle.h
|
||||
// AUTHOR: Rob Tillaart
|
||||
// VERSION: 0.1.9
|
||||
// VERSION: 0.2.0
|
||||
// DATE: 2017-11-21
|
||||
// PURPOSE: class for averaging angles.
|
||||
|
||||
@ -10,12 +10,17 @@
|
||||
#include "math.h"
|
||||
#include "Arduino.h"
|
||||
|
||||
#define AVERAGE_ANGLE_LIB_VERSION (F("0.1.9"))
|
||||
#define AVERAGE_ANGLE_LIB_VERSION (F("0.2.0"))
|
||||
|
||||
#define GRAD_TO_RAD (PI / 200.0)
|
||||
#define RAD_TO_GRAD (200.0 / PI)
|
||||
|
||||
|
||||
#define AVERAGE_ANGLE_OK 0
|
||||
#define AVERAGE_ANGLE_OVERFLOW -10
|
||||
#define AVERAGE_ANGLE_SINGULARITY -20
|
||||
|
||||
|
||||
class AverageAngle
|
||||
{
|
||||
public:
|
||||
@ -26,7 +31,7 @@ public:
|
||||
uint32_t add(float alpha, float length = 1.0);
|
||||
void reset();
|
||||
uint32_t count();
|
||||
|
||||
|
||||
// if there are no angles added (count == 0) or
|
||||
// if the sum == (0,0) there is a singularity ==> no angle. NAN
|
||||
float getAverage();
|
||||
@ -39,12 +44,14 @@ public:
|
||||
AngleType type();
|
||||
bool setType(AngleType type);
|
||||
|
||||
int lastError();
|
||||
|
||||
private:
|
||||
AngleType _type;
|
||||
float _sumx;
|
||||
float _sumy;
|
||||
uint32_t _count;
|
||||
int _error;
|
||||
};
|
||||
|
||||
|
||||
|
@ -6,10 +6,19 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/)
|
||||
and this project adheres to [Semantic Versioning](http://semver.org/).
|
||||
|
||||
|
||||
## [0.2.0] - 2023-02-01
|
||||
- add experimental OVERFLOW detection in **add()**
|
||||
- add **lastError()**
|
||||
- OK, OVERFLOW, SINGULARITY
|
||||
- update GitHub actions
|
||||
- update license 2023
|
||||
- update readme.md
|
||||
- update keywords.txt
|
||||
|
||||
----
|
||||
|
||||
## [0.1.9] - 2022-12-07
|
||||
- fix #7 update readme.md
|
||||
-
|
||||
|
||||
|
||||
## [0.1.8] - 2022-10-29
|
||||
- add RP2040 support to build-CI.
|
||||
|
@ -1,6 +1,6 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2017-2022 Rob Tillaart
|
||||
Copyright (c) 2017-2023 Rob Tillaart
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
|
@ -28,9 +28,11 @@ The average angle is calculated by converting the added angle (in DEGREES, etc)
|
||||
These (x,y) are added to a (sumX, sumY) and divided by the number of angles added so far.
|
||||
|
||||
|
||||
#### Related to
|
||||
#### Related
|
||||
|
||||
- https://github.com/RobTillaart/Angle
|
||||
- https://github.com/RobTillaart/AngleConvertor
|
||||
- https://github.com/RobTillaart/AverageAngle
|
||||
- https://github.com/RobTillaart/runningAngle
|
||||
|
||||
|
||||
@ -47,22 +49,67 @@ These (x,y) are added to a (sumX, sumY) and divided by the number of angles adde
|
||||
|
||||
## Interface
|
||||
|
||||
- **AverageAngle(AngleType type = DEGREES)** constructor defaults to degrees.
|
||||
```cpp
|
||||
#include "AverageAngle.h"
|
||||
```
|
||||
|
||||
#### Constructor
|
||||
|
||||
- **AverageAngle(AngleType type = DEGREES)** constructor, defaults to degrees.
|
||||
- **AngleType type()** returns DEGREES, RADIANS or GRADIANS.
|
||||
- **void setType(AngleType type)** changes type DEGREES, RADIANS or GRADIANS.
|
||||
Type can be changed run time and still continue to add.
|
||||
- **void reset()** clears internal counters.
|
||||
|
||||
|
||||
#### Core
|
||||
|
||||
- **uint32_t add(float alpha, float length = 1.0)** add a new angle,
|
||||
optional with length other than 1.
|
||||
Returns the number of elements (count).
|
||||
- **void reset()** clears internal counters.
|
||||
- **uint32_t count()** the amount of angles added.
|
||||
If the internal sumx or sumy is >= 10000, the error **AVERAGE_ANGLE_OVERFLOW** is set.
|
||||
This indicates that the internal math is near or over its accuracy limits.
|
||||
- **uint32_t count()** the number of angles added.
|
||||
If count == 0, there is no average.
|
||||
- **float getAverage()** returns the average, unless count == 0
|
||||
- **float getAverage()** returns the average, unless count == 0
|
||||
or the internal sums == (0,0) in which case we have a singularity ==> NAN (not a number)
|
||||
If NAN the error **AVERAGE_ANGLE_SINGULARITY** is set.
|
||||
- **float getTotalLength()** the length of the resulting 'angle' when we see them as vectors.
|
||||
If count == 0 ==> total length = 0.
|
||||
- **float getAverageLength()** returns the average length of the angles added.
|
||||
If count == 0 ==> average length = 0.
|
||||
- **AngleType type()** returns DEGREES, RADIANS or GRADIANS.
|
||||
- **void setType(AngleType type)** changes type DEGREES, RADIANS or GRADIANS.
|
||||
Type can be changed run time and still continue to add.
|
||||
|
||||
|
||||
#### Error handling
|
||||
|
||||
- **int lastError()** return the last error detected.
|
||||
|
||||
| name | value |
|
||||
|:----------------------------|:-------:|
|
||||
| AVERAGE_ANGLE_OK | 0 |
|
||||
| AVERAGE_ANGLE_OVERFLOW | -10 |
|
||||
| AVERAGE_ANGLE_SINGULARITY | -20 |
|
||||
|
||||
|
||||
|
||||
#### Experimental Overflow
|
||||
|
||||
(since 0.2.0)
|
||||
|
||||
When the internal sumx or sumy is large (> 10000) the accuracy of the addition
|
||||
becomes critical, leading to serious errors in the average and length functions.
|
||||
To detect this the function **add()** sets the error **AVERAGE_ANGLE_OVERFLOW**.
|
||||
This error can be checked with **lastError()**.
|
||||
The function **add()** will add the new angle as good as possible.
|
||||
|
||||
Note this condition is independent of the **AngleType** as the internal math
|
||||
uses radians. The condition will be triggered faster when the length parameter
|
||||
is used.
|
||||
|
||||
The overflow threshold of 10000 can be patched in the .cpp file if needed.
|
||||
|
||||
As this feature is **experimental**, the trigger condition for overflow will
|
||||
probably be redesigned in the future. See future section below.
|
||||
|
||||
|
||||
## Gradians
|
||||
@ -72,6 +119,8 @@ There are 100 gradians in a right angle. A full circle = 400 gradians.
|
||||
|
||||
https://en.wikipedia.org/wiki/Gradian
|
||||
|
||||
See also AngleConvertor library.
|
||||
|
||||
|
||||
## Operation
|
||||
|
||||
@ -111,22 +160,36 @@ just change the type runtime.
|
||||
Serial.println(AA.getAverage());
|
||||
```
|
||||
|
||||
#### Warning
|
||||
|
||||
As the internal representation has a limited resolution (float) it can occur that when
|
||||
the internal values are large, one cannot add a very small angle any more.
|
||||
Did not encounter it yet myself.
|
||||
|
||||
|
||||
## Future
|
||||
|
||||
#### Must
|
||||
|
||||
- investigate if and how the internal math can be made more robust against overflow.
|
||||
- use double iso float (will work on certain platforms) (must)
|
||||
- uint32_t?
|
||||
- accuracy threshold depends on float/double usage. (sizeof(double)==8)
|
||||
- threshold depends on the units of length.
|
||||
if all add's are using 10000 as length they have equal weight.
|
||||
normalizing the weight? how? user responsibility?
|
||||
- get set threshold via API?
|
||||
- use of threshold versus error detection (sum - angle == previous or not)
|
||||
- split OVERFLOW error in X and Y
|
||||
|
||||
|
||||
#### Should
|
||||
|
||||
- add performance example
|
||||
- add overflow example
|
||||
- add singularity example
|
||||
|
||||
|
||||
#### Could
|
||||
|
||||
- add a USER AngleType, in which the user can map 0..360 degrees to any range.
|
||||
- float userFactor = 1.0; (default)
|
||||
- can even be negative?
|
||||
- use cases? e.g 0..4 quadrant?
|
||||
- maybe better for the AngleConvertor class.
|
||||
|
||||
#### Wont
|
||||
|
||||
|
@ -14,10 +14,19 @@ getAverage KEYWORD2
|
||||
getTotalLength KEYWORD2
|
||||
getAverageLength KEYWORD2
|
||||
|
||||
setType KEYWORD2
|
||||
|
||||
lastError KEYWORD2
|
||||
|
||||
|
||||
# Constants (LITERAL1)
|
||||
AVERAGE_ANGLE_LIB_VERSION LITERAL1
|
||||
|
||||
DEGREES LITERAL1
|
||||
RADIANS LITERAL1
|
||||
GRADIANS LITERAL1
|
||||
|
||||
AVERAGE_ANGLE_OK LITERAL1
|
||||
AVERAGE_ANGLE_OVERFLOW LITERAL1
|
||||
AVERAGE_ANGLE_SINGULARITY LITERAL1
|
||||
|
||||
|
@ -15,7 +15,7 @@
|
||||
"type": "git",
|
||||
"url": "https://github.com/RobTillaart/AverageAngle.git"
|
||||
},
|
||||
"version": "0.1.9",
|
||||
"version": "0.2.0",
|
||||
"license": "MIT",
|
||||
"frameworks": "arduino",
|
||||
"platforms": "*",
|
||||
|
@ -1,5 +1,5 @@
|
||||
name=AverageAngle
|
||||
version=0.1.9
|
||||
version=0.2.0
|
||||
author=Rob Tillaart <rob.tillaart@gmail.com>
|
||||
maintainer=Rob Tillaart <rob.tillaart@gmail.com>
|
||||
sentence=Library to average angles correctly around 0.
|
||||
|
@ -30,6 +30,7 @@ unittest_setup()
|
||||
fprintf(stderr, "AVERAGE_ANGLE_LIB_VERSION: %s\n", (char*) AVERAGE_ANGLE_LIB_VERSION);
|
||||
}
|
||||
|
||||
|
||||
unittest_teardown()
|
||||
{
|
||||
}
|
||||
@ -41,6 +42,11 @@ unittest(test_constants)
|
||||
assertEqualFloat(180.0 / PI, RAD_TO_DEG, 0.00001);
|
||||
assertEqualFloat(PI / 200.0, GRAD_TO_RAD, 0.00001);
|
||||
assertEqualFloat(200.0 / PI, RAD_TO_GRAD, 0.00001);
|
||||
|
||||
// ERROR CODES
|
||||
assertEqual(0, AVERAGE_ANGLE_OK);
|
||||
assertEqual(-10, AVERAGE_ANGLE_OVERFLOW);
|
||||
assertEqual(-20, AVERAGE_ANGLE_SINGULARITY);
|
||||
}
|
||||
|
||||
|
||||
@ -53,6 +59,10 @@ unittest(test_constructor)
|
||||
assertEqual(AverageAngle::DEGREES, dd.type());
|
||||
assertEqual(AverageAngle::RADIANS, rr.type());
|
||||
assertEqual(AverageAngle::GRADIANS, gg.type());
|
||||
|
||||
assertEqual(AVERAGE_ANGLE_OK, dd.lastError());
|
||||
assertEqual(AVERAGE_ANGLE_OK, rr.lastError());
|
||||
assertEqual(AVERAGE_ANGLE_OK, gg.lastError());
|
||||
}
|
||||
|
||||
|
||||
@ -118,4 +128,6 @@ unittest(test_gradians)
|
||||
|
||||
unittest_main()
|
||||
|
||||
// --------
|
||||
|
||||
// -- END OF FILE --
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user