{"id":13894188,"url":"https://github.com/RobTillaart/AverageAngle","last_synced_at":"2025-07-17T09:31:05.472Z","repository":{"id":45443997,"uuid":"250286358","full_name":"RobTillaart/AverageAngle","owner":"RobTillaart","description":"Arduino library to calculate correctly the average of multiple angles","archived":false,"fork":false,"pushed_at":"2024-04-13T08:56:15.000Z","size":33,"stargazers_count":3,"open_issues_count":0,"forks_count":1,"subscribers_count":1,"default_branch":"master","last_synced_at":"2025-04-02T22:01:48.457Z","etag":null,"topics":["angle","arduino","math"],"latest_commit_sha":null,"homepage":"","language":"C++","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/RobTillaart.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":".github/FUNDING.yml","license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null},"funding":{"github":"RobTillaart","custom":"https://www.paypal.me/robtillaart"}},"created_at":"2020-03-26T14:48:39.000Z","updated_at":"2023-09-25T18:51:27.000Z","dependencies_parsed_at":"2023-01-24T20:31:06.958Z","dependency_job_id":"f8e7c71b-b16b-4f8b-b427-32158ce4f935","html_url":"https://github.com/RobTillaart/AverageAngle","commit_stats":null,"previous_names":[],"tags_count":10,"template":false,"template_full_name":null,"purl":"pkg:github/RobTillaart/AverageAngle","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/RobTillaart%2FAverageAngle","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/RobTillaart%2FAverageAngle/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/RobTillaart%2FAverageAngle/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/RobTillaart%2FAverageAngle/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/RobTillaart","download_url":"https://codeload.github.com/RobTillaart/AverageAngle/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/RobTillaart%2FAverageAngle/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":265589081,"owners_count":23793465,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["angle","arduino","math"],"created_at":"2024-08-06T18:01:26.088Z","updated_at":"2025-07-17T09:31:05.170Z","avatar_url":"https://github.com/RobTillaart.png","language":"C++","funding_links":["https://github.com/sponsors/RobTillaart","https://www.paypal.me/robtillaart"],"categories":["C++"],"sub_categories":[],"readme":"\n[![Arduino CI](https://github.com/RobTillaart/AverageAngle/workflows/Arduino%20CI/badge.svg)](https://github.com/marketplace/actions/arduino_ci)\n[![Arduino-lint](https://github.com/RobTillaart/AverageAngle/actions/workflows/arduino-lint.yml/badge.svg)](https://github.com/RobTillaart/AverageAngle/actions/workflows/arduino-lint.yml)\n[![JSON check](https://github.com/RobTillaart/AverageAngle/actions/workflows/jsoncheck.yml/badge.svg)](https://github.com/RobTillaart/AverageAngle/actions/workflows/jsoncheck.yml)\n[![GitHub issues](https://img.shields.io/github/issues/RobTillaart/AverageAngle.svg)](https://github.com/RobTillaart/AverageAngle/issues)\n\n[![License: MIT](https://img.shields.io/badge/license-MIT-green.svg)](https://github.com/RobTillaart/AverageAngle/blob/master/LICENSE)\n[![GitHub release](https://img.shields.io/github/release/RobTillaart/AverageAngle.svg?maxAge=3600)](https://github.com/RobTillaart/AverageAngle/releases)\n[![PlatformIO Registry](https://badges.registry.platformio.org/packages/robtillaart/library/AverageAngle.svg)](https://registry.platformio.org/libraries/robtillaart/AverageAngle)\n\n\n# AverageAngle\n\nArduino library to calculate correctly the average of multiple angles.\n\n\n## Description\n\nAverageAngle is a class to calculate the average of angles.\n\nThis is especially useful when angles are around 0 degrees, \ne.g. from a compass sensor or the resultant of a track.\nExample, the average angle of 359 and 1 is 0, not 179 (most of the time)\n\nFurthermore the AverageAngle can also include the **length (weight)** of the angle as if it is a vector. \nDefault this length is set to 1 so all angles are by default of equal weight.\n\nExample: The average angle of 359 (length = 2) and 1 (length = 1) is 359.something not zero.\n\nThe average angle is calculated by converting the added angle (in DEGREES, etc) to polar coordinates.\nThese (x,y) are added to a (sumX, sumY) and divided by the number of angles added so far. \n\n\n#### Related\n\n- https://github.com/RobTillaart/Angle\n- https://github.com/RobTillaart/AngleConvertor\n- https://github.com/RobTillaart/AverageAngle\n- https://github.com/RobTillaart/runningAngle\n\n\n#### AngleType\n\n- **enum AngleType { DEGREES, RADIANS, GRADIANS }** idem. \n\n|  value  |  name      |  range     |  notes  |\n|:-------:|:-----------|:----------:|:--------|\n|    0    |  DEGREES   |  0 .. 360  |\n|    1    |  RADIANS   |  0 .. 2PI  |\n|    2    |  GRADIANS  |  0 .. 400  |  100 GRADIANS == 90 DEGREES.\n\n\n## Interface\n\n```cpp\n#include \"AverageAngle.h\"\n```\n\n#### Constructor\n\n- **AverageAngle(AngleType type = DEGREES)** constructor, defaults to degrees.\n- **AngleType type()** returns DEGREES, RADIANS or GRADIANS.\n- **void setType(AngleType type)** changes type DEGREES, RADIANS or GRADIANS.\nType can be changed run time and still continue to add.\n- **void reset()** clears internal counters.\n\n\n#### Core\n\n- **uint32_t add(float alpha, float length = 1.0)** add a new angle, \noptional with length other than 1. \nReturns the number of elements added so far (count).\nIf the internal sumx or sumy is \u003e= 10000, the error **AVERAGE_ANGLE_OVERFLOW** is set. \nThis indicates that the internal math is near or over its accuracy limits.\n- **uint32_t count()** the number of angles added.\nIf count == 0, there is no average.\n- **float getAverage()** returns the average, unless count == 0\nor the internal sums == (0,0) in which case we have a singularity ==\u003e NAN (not a number)\nIf NAN the error **AVERAGE_ANGLE_SINGULARITY** is set. \n- **float getTotalLength()** the length of the resulting 'angle' when we see them as vectors.\nIf count == 0 ==\u003e total length = 0.\n- **float getAverageLength()** returns the average length of the angles added.\nIf count == 0 ==\u003e average length = 0.\n- **float getSumX()** get internal sumx counter. Rectangular coordinates.\n- **float getSumY()** get internal sumy counter. Rectangular coordinates.\n\n\n#### Error handling\n\n- **int lastError()**  return the last error detected.\n\n|  name                       |  value  |\n|:----------------------------|:-------:|\n|  AVERAGE_ANGLE_OK           |   0     |\n|  AVERAGE_ANGLE_OVERFLOW     |  -10    |\n|  AVERAGE_ANGLE_SINGULARITY  |  -20    |\n\n\n#### Experimental Overflow\n\n(since 0.2.0)\n\nWhen the internal sumx or sumy is large (\u003e 10000) the accuracy of the addition\nbecomes critical, leading to serious errors in the average and length functions.\nTo detect this the function **add()** sets the error **AVERAGE_ANGLE_OVERFLOW**.\nThis error can be checked with **lastError()**.\nThe function **add()** will add the new angle as good as possible.\n\nNote this condition is independent of the **AngleType** as the internal math \nuses radians. The condition will be triggered faster when the length parameter \nis used. \n\nThe overflow threshold of 10000 can be adjusted in the AverageAngle.cpp file if needed.\n\nAs this feature is **experimental**, the trigger condition for overflow will \nprobably be redesigned in the future. See future section below.\n\n\n## Gradians\n\nGradians a.k.a. **gon**, is a less often used unit for angles. \nThere are 100 gradians in a right angle. A full circle = 400 gradians.\n\nhttps://en.wikipedia.org/wiki/Gradian\n\nOther less used units for measuring angles:\n- https://github.com/RobTillaart/AngleConvertor\n\n\n## Operation\n\nIf you want to average 5 compass readings you can just add the angles and \ndo not use the length parameter.\n```cpp\n  AA.reset();\n  for (int i = 0; i \u003c 5; i++)\n  {\n    AA.add(compass.readHeading());\n    delay(100);    // e.g. compass read needs some time\n  }\n  Serial.println(AA.getAverage());\n```\n\n\nIf you want to average a track, e.g. 5 steps North, 3 steps west etc, \nyou need to include the length of each step.\n```cpp\n  AA.reset();\n  AA.add(90, 5);     //  5 steps north  assuming east = 0\n  AA.add(180, 3);    //  3 steps west\n  Serial.println(AA.getAverage());\n  Serial.println(AA.getTotalLength());\n```\n\n\nIf you want to average an angle in DEGREES and an angle in RADIANS,\njust change the type runtime.\n```cpp\n  AA.reset();\n  AA.setType(DEGREES);\n  AA.add(90);\n  AA.setType(RADIANS);\n  AA.add(PI/3);\n  AA.setType(DEGREES);\n  Serial.println(AA.getAverage());\n```\n\n## Future\n\n#### Must\n\n- investigate if and how the internal math can be made more robust against overflow.\n  - use double instead of float (will work on certain platforms) (must) =\u003e 0.3.0\n  - uint32_t?\n  - accuracy threshold depends on float / double usage.  (sizeof(double) == 8)\n  - threshold depends on the units of length. \n    if all add's are using 10000 as length they have equal weight.\n    normalizing the weight? how? user responsibility?\n  - get set threshold via API?\n  - use of threshold versus error detection (sum - angle == previous or not)\n  - split OVERFLOW error in X and Y\n\n\n#### Should\n\n- add performance example\n- add overflow example\n- add singularity example\n\n\n#### Could\n\n- **uint32_t addDegrees(float angle)** more explicit.\n- **uint32_t addRadians(float angle)** idem.\n- **uint32_t addGradians(float angle)** idem.\n\n\n#### Wont\n\n- add a USER AngleType, in which the user can map 0..360 degrees to any range.\n  - float userFactor = 1.0;  (default)\n  - can even be negative?\n  - use cases? e.g 0..4 quadrant?\n  - maybe better for the AngleConvertor class.\n\n\n## Support\n\nIf you appreciate my libraries, you can support the development and maintenance.\nImprove the quality of the libraries by providing issues and Pull Requests, or\ndonate through PayPal or GitHub sponsors.\n\nThank you,\n\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2FRobTillaart%2FAverageAngle","html_url":"https://awesome.ecosyste.ms/projects/github.com%2FRobTillaart%2FAverageAngle","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2FRobTillaart%2FAverageAngle/lists"}