core/weather/wind_dir.c
| Line | Branch | Exec | Source |
|---|---|---|---|
| 1 | /** | ||
| 2 | * @file wind_dir.c | ||
| 3 | * @brief The compass direction a wind is blowing from, as a bearing and as a sideways component. | ||
| 4 | * | ||
| 5 | * @ingroup lib_core | ||
| 6 | */ | ||
| 7 | #include "weather/wind_dir.h" | ||
| 8 | |||
| 9 | #include <ctype.h> | ||
| 10 | #include <stdbool.h> | ||
| 11 | #include <stddef.h> | ||
| 12 | |||
| 13 | /// How many compass points a direction can name, from N through NNW | ||
| 14 | #define POINT_COUNT 16 | ||
| 15 | |||
| 16 | /** | ||
| 17 | * @brief Whether two compass abbreviations are the same, ignoring case. | ||
| 18 | * | ||
| 19 | * @param a The first abbreviation. | ||
| 20 | * @param b The second abbreviation. | ||
| 21 | * @return Whether they match, case insensitive. | ||
| 22 | */ | ||
| 23 | 295 | static bool same_point(const char *a, const char *b) | |
| 24 | { | ||
| 25 |
4/4✓ Branch 0 taken 367 times.
✓ Branch 1 taken 43 times.
✓ Branch 2 taken 352 times.
✓ Branch 3 taken 15 times.
|
410 | while (*a && *b) |
| 26 | { | ||
| 27 |
2/2✓ Branch 0 taken 237 times.
✓ Branch 1 taken 115 times.
|
352 | if (toupper((unsigned char)*a) != toupper((unsigned char)*b)) |
| 28 | { | ||
| 29 | 237 | return false; | |
| 30 | } | ||
| 31 | 115 | a++; | |
| 32 | 115 | b++; | |
| 33 | } | ||
| 34 | |||
| 35 |
4/4✓ Branch 0 taken 43 times.
✓ Branch 1 taken 15 times.
✓ Branch 2 taken 31 times.
✓ Branch 3 taken 12 times.
|
58 | return *a == '\0' && *b == '\0'; |
| 36 | } | ||
| 37 | |||
| 38 | /** | ||
| 39 | * @brief Sine in thousandths, from a table every 15 degrees with the gaps interpolated. | ||
| 40 | * | ||
| 41 | * The watch has a trig lookup but this file is pure core and cannot reach it, and pulling float | ||
| 42 | * maths in for one component would cost more than the table does. Straight lines between | ||
| 43 | * fifteen-degree stops land within a percent of a real sine, which is finer than a caller | ||
| 44 | * working in whole pixels or whole percent can use. | ||
| 45 | * | ||
| 46 | * @param degrees The angle to take the sine of. Wrapped into 0 to 359 first, negative values | ||
| 47 | * included. | ||
| 48 | * @return The sine of the angle, times 1000. | ||
| 49 | */ | ||
| 50 | 366 | static int sin_milli(int degrees) | |
| 51 | { | ||
| 52 | static const short table[] = { | ||
| 53 | 0, 259, 500, 707, 866, 966, 1000, 966, 866, 707, 500, 259, 0, | ||
| 54 | -259, -500, -707, -866, -966, -1000, -966, -866, -707, -500, -259, 0, | ||
| 55 | }; | ||
| 56 | |||
| 57 | 366 | degrees %= 360; | |
| 58 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 366 times.
|
366 | if (degrees < 0) |
| 59 | { | ||
| 60 | ✗ | degrees += 360; | |
| 61 | } | ||
| 62 | |||
| 63 | 366 | int slot = degrees / 15; | |
| 64 | 366 | int into = degrees % 15; | |
| 65 | |||
| 66 | 366 | return table[slot] + ((table[slot + 1] - table[slot]) * into) / 15; | |
| 67 | } | ||
| 68 | |||
| 69 | 35 | int wind_bearing(const char *dir) | |
| 70 | { | ||
| 71 | static const char *const points[POINT_COUNT] = { | ||
| 72 | "N", "NNE", "NE", "ENE", "E", "ESE", "SE", "SSE", | ||
| 73 | "S", "SSW", "SW", "WSW", "W", "WNW", "NW", "NNW", | ||
| 74 | }; | ||
| 75 | |||
| 76 |
4/4✓ Branch 0 taken 34 times.
✓ Branch 1 taken 1 time.
✓ Branch 2 taken 1 time.
✓ Branch 3 taken 33 times.
|
35 | if (!dir || !dir[0]) |
| 77 | { | ||
| 78 | 2 | return -1; | |
| 79 | } | ||
| 80 | |||
| 81 |
2/2✓ Branch 0 taken 295 times.
✓ Branch 1 taken 2 times.
|
297 | for (int i = 0; i < POINT_COUNT; i++) |
| 82 | { | ||
| 83 |
2/2✓ Branch 1 taken 31 times.
✓ Branch 2 taken 264 times.
|
295 | if (same_point(points[i], dir)) |
| 84 | { | ||
| 85 | // 22.5 degrees a point, kept in whole degrees. the half degree it drops is far below | ||
| 86 | // what the reading is accurate to in the first place | ||
| 87 | 31 | return (i * 45) / 2; | |
| 88 | } | ||
| 89 | } | ||
| 90 | |||
| 91 | 2 | return -1; | |
| 92 | } | ||
| 93 | |||
| 94 | 367 | int wind_lean(int bearing) | |
| 95 | { | ||
| 96 |
2/2✓ Branch 0 taken 1 time.
✓ Branch 1 taken 366 times.
|
367 | if (bearing < 0) |
| 97 | { | ||
| 98 | 1 | return 0; | |
| 99 | } | ||
| 100 | |||
| 101 | // the bearing is where the wind comes *from*, so it blows towards bearing + 180 and the | ||
| 102 | // eastward part of that is -sin(bearing). a wind straight towards or away from the viewer | ||
| 103 | // has no eastward part at all, which is what makes this zero at due north and due south | ||
| 104 | 366 | return -sin_milli(bearing) / 10; | |
| 105 | } | ||
| 106 |