altos: Add ao_distance.c to compute cartesian distances on the globe
[fw/altos] / src / kernel / ao_distance.c
1 /*
2  * Copyright © 2014 Keith Packard <keithp@keithp.com>
3  *
4  * This program is free software; you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License as published by
6  * the Free Software Foundation; version 2 of the License.
7  *
8  * This program is distributed in the hope that it will be useful, but
9  * WITHOUT ANY WARRANTY; without even the implied warranty of
10  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
11  * General Public License for more details.
12  *
13  * You should have received a copy of the GNU General Public License along
14  * with this program; if not, write to the Free Software Foundation, Inc.,
15  * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
16  */
17
18 #include <ao.h>
19 #include <ao_distance.h>
20
21 static uint32_t
22 ao_dist(int32_t a, int32_t b)
23 {
24         int32_t d = a - b;
25         if (d < 0)
26                 d = -d;
27         return (uint32_t) ((int64_t) d * 111198 / 10000000);
28 }
29
30 static uint32_t
31 ao_lat_dist(int32_t lat_a, int32_t lat_b)
32 {
33         return ao_dist(lat_a, lat_b);
34 }
35
36 static const uint8_t cos_table[] = {
37    0, /*  0 */
38    0, /*  1 */
39    0, /*  2 */
40  255, /*  3 */
41  254, /*  4 */
42  253, /*  5 */
43  252, /*  6 */
44  251, /*  7 */
45  249, /*  8 */
46  247, /*  9 */
47  245, /* 10 */
48  243, /* 11 */
49  240, /* 12 */
50  238, /* 13 */
51  235, /* 14 */
52  232, /* 15 */
53  228, /* 16 */
54  225, /* 17 */
55  221, /* 18 */
56  217, /* 19 */
57  213, /* 20 */
58  209, /* 21 */
59  205, /* 22 */
60  200, /* 23 */
61  195, /* 24 */
62  190, /* 25 */
63  185, /* 26 */
64  180, /* 27 */
65  175, /* 28 */
66  169, /* 29 */
67  163, /* 30 */
68  158, /* 31 */
69  152, /* 32 */
70  145, /* 33 */
71  139, /* 34 */
72  133, /* 35 */
73  126, /* 36 */
74  120, /* 37 */
75  113, /* 38 */
76  106, /* 39 */
77  100, /* 40 */
78   93, /* 41 */
79   86, /* 42 */
80   79, /* 43 */
81   71, /* 44 */
82   64, /* 45 */
83   57, /* 46 */
84   49, /* 47 */
85   42, /* 48 */
86   35, /* 49 */
87   27, /* 50 */
88   20, /* 51 */
89   12, /* 52 */
90    5, /* 53 */
91    1, /* 54 */
92 };
93
94 static uint32_t
95 ao_lon_dist(int32_t lon_a, int32_t lon_b)
96 {
97         uint8_t         c = cos_table[lon_a >> 24];
98         uint32_t        lon_dist;
99
100         /* check if it's shorter to go the other way around */
101         if (lon_a < lon_b - 1800000000)
102                 lon_a += 3600000000;
103         lon_dist = ao_dist(lon_a, lon_b);
104         if (c) {
105                 if (lon_dist & 0x7f800000)
106                         lon_dist = (lon_dist >> 8) * c;
107                 else
108                         lon_dist = (lon_dist * (int16_t) c) >> 8;
109         }
110         return lon_dist;
111 }
112
113 static uint32_t sqr(uint32_t x) { return x * x; }
114
115 uint32_t
116 ao_distance(int32_t lat_a, int32_t lon_a, int32_t lat_b, int32_t lon_b)
117 {
118         uint32_t        lat_dist = ao_lat_dist(lat_a, lat_b);
119         uint32_t        lon_dist = ao_lon_dist(lon_a, lon_b);
120
121         return ao_sqrt (sqr(lat_dist) + sqr(lon_dist));
122 }