Host C Specs


Directory: src/c/
Coverage: low: ≥ 0% medium: ≥ 75.0% high: ≥ 90.0%
Coverage Exec / Excl / Total
Lines: 96.0% 24 / 0 / 25
Functions: 100.0% 4 / 0 / 4
Branches: 95.5% 21 / 0 / 22

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