Merge pull request #11189 from klutvott123/move-telemetry-displayport-init
[betaflight.git] / lib / main / google / olc / olc.c
blobdf12775727fa4610955d989f42589c26e9609321
1 #include "olc.h"
2 #include <ctype.h>
3 #include <float.h>
4 #include <math.h>
5 #include <memory.h>
6 #include <stdio.h>
7 #include "olc_private.h"
9 #include "betaflight.h"
11 #define CORRECT_IF_SEPARATOR(var, info) \
12 do { \
13 (var) += (info)->sep_first >= 0 ? 1 : 0; \
14 } while (0)
16 // Information about a code, produced by analyse();
17 typedef struct CodeInfo {
18 // Original code.
19 const char* code;
20 // Total count of characters in the code including padding and separators.
21 int size;
22 // Count of valid digits (not including padding or separators).
23 int len;
24 // Index of the first separator in the code.
25 int sep_first;
26 // Index of the last separator in the code. (If there is only one, same as
27 // sep_first.)
28 int sep_last;
29 // Index of the first padding character in the code.
30 int pad_first;
31 // Index of the last padding character in the code. (If there is only one,
32 // same as pad_first.)
33 int pad_last;
34 } CodeInfo;
36 // Helper functions
37 static int analyse(const char* code, size_t size, CodeInfo* info);
38 static int is_short(CodeInfo* info);
39 static int is_full(CodeInfo* info);
40 static int decode(CodeInfo* info, OLC_CodeArea* decoded);
41 static size_t code_length(CodeInfo* info);
43 static double pow_neg(double base, double exponent);
44 static double compute_latitude_precision(int length);
45 static double normalize_longitude(double lon_degrees);
46 static double adjust_latitude(double lat_degrees, size_t length);
48 void OLC_GetCenter(const OLC_CodeArea* area, OLC_LatLon* center) {
49 center->lat = area->lo.lat + (area->hi.lat - area->lo.lat) / 2.0;
50 if (center->lat > kLatMaxDegrees) {
51 center->lat = kLatMaxDegrees;
54 center->lon = area->lo.lon + (area->hi.lon - area->lo.lon) / 2.0;
55 if (center->lon > kLonMaxDegrees) {
56 center->lon = kLonMaxDegrees;
60 size_t OLC_CodeLength(const char* code, size_t size) {
61 CodeInfo info;
62 analyse(code, size, &info);
63 return code_length(&info);
66 int OLC_IsValid(const char* code, size_t size) {
67 CodeInfo info;
68 return analyse(code, size, &info) > 0;
71 int OLC_IsShort(const char* code, size_t size) {
72 CodeInfo info;
73 if (analyse(code, size, &info) <= 0) {
74 return 0;
76 return is_short(&info);
79 int OLC_IsFull(const char* code, size_t size) {
80 CodeInfo info;
81 if (analyse(code, size, &info) <= 0) {
82 return 0;
84 return is_full(&info);
87 int OLC_Encode(const OLC_LatLon* location, size_t length, char* code,
88 int maxlen) {
89 // Limit the maximum number of digits in the code.
90 if (length > kMaximumDigitCount) {
91 length = kMaximumDigitCount;
93 // Adjust latitude and longitude so they fall into positive ranges.
94 double latitude = adjust_latitude(location->lat, length);
95 double longitude = normalize_longitude(location->lon);
97 // Build up the code here, then copy it to the passed pointer.
98 char fullcode[] = "12345678901234567";
100 // Compute the code.
101 // This approach converts each value to an integer after multiplying it by
102 // the final precision. This allows us to use only integer operations, so
103 // avoiding any accumulation of floating point representation errors.
105 // Multiply values by their precision and convert to positive without any
106 // floating point operations.
107 long long int lat_val = kLatMaxDegrees * 2.5e7;
108 long long int lng_val = kLonMaxDegrees * 8.192e6;
109 lat_val += latitude * 2.5e7;
110 lng_val += longitude * 8.192e6;
112 size_t pos = kMaximumDigitCount;
113 // Compute the grid part of the code if necessary.
114 if (length > kPairCodeLength) {
115 for (size_t i = 0; i < kGridCodeLength; i++) {
116 int lat_digit = lat_val % kGridRows;
117 int lng_digit = lng_val % kGridCols;
118 int ndx = lat_digit * kGridCols + lng_digit;
119 fullcode[pos--] = kAlphabet[ndx];
120 // Note! Integer division.
121 lat_val /= kGridRows;
122 lng_val /= kGridCols;
124 } else {
125 lat_val /= pow(kGridRows, kGridCodeLength);
126 lng_val /= pow(kGridCols, kGridCodeLength);
128 pos = kPairCodeLength;
129 // Compute the pair section of the code.
130 for (size_t i = 0; i < kPairCodeLength / 2; i++) {
131 int lat_ndx = lat_val % kEncodingBase;
132 int lng_ndx = lng_val % kEncodingBase;
133 fullcode[pos--] = kAlphabet[lng_ndx];
134 fullcode[pos--] = kAlphabet[lat_ndx];
135 // Note! Integer division.
136 lat_val /= kEncodingBase;
137 lng_val /= kEncodingBase;
138 if (i == 0) {
139 fullcode[pos--] = kSeparator;
142 // Replace digits with padding if necessary.
143 if (length < kSeparatorPosition) {
144 for (size_t i = length; i < kSeparatorPosition; i++) {
145 fullcode[i] = kPaddingCharacter;
147 fullcode[kSeparatorPosition] = kSeparator;
149 // Now copy the full code digits into the buffer.
150 size_t char_count = length + 1;
151 if (kSeparatorPosition + 1 > char_count) {
152 char_count = kSeparatorPosition + 1;
154 for (size_t i = 0; i < char_count; i++) {
155 code[i] = fullcode[i];
158 // Terminate the buffer.
159 code[char_count] = '\0';
161 return char_count;
164 int OLC_EncodeDefault(const OLC_LatLon* location, char* code, int maxlen) {
165 return OLC_Encode(location, kPairCodeLength, code, maxlen);
168 int OLC_Decode(const char* code, size_t size, OLC_CodeArea* decoded) {
169 CodeInfo info;
170 if (analyse(code, size, &info) <= 0) {
171 return 0;
173 return decode(&info, decoded);
176 int OLC_Shorten(const char* code, size_t size, const OLC_LatLon* reference,
177 char* shortened, int maxlen) {
178 CodeInfo info;
179 if (analyse(code, size, &info) <= 0) {
180 return 0;
182 if (info.pad_first > 0) {
183 return 0;
185 if (!is_full(&info)) {
186 return 0;
189 OLC_CodeArea code_area;
190 decode(&info, &code_area);
191 OLC_LatLon center;
192 OLC_GetCenter(&code_area, &center);
194 // Ensure that latitude and longitude are valid.
195 double lat = adjust_latitude(reference->lat, info.len);
196 double lon = normalize_longitude(reference->lon);
198 // How close are the latitude and longitude to the code center.
199 double alat = fabs(center.lat - lat);
200 double alon = fabs(center.lon - lon);
201 double range = alat > alon ? alat : alon;
203 // Yes, magic numbers... sob.
204 int start = 0;
205 const double safety_factor = 0.3;
206 const int removal_lengths[3] = {8, 6, 4};
207 for (int j = 0; j < sizeof(removal_lengths) / sizeof(removal_lengths[0]);
208 ++j) {
209 // Check if we're close enough to shorten. The range must be less than
210 // 1/2 the resolution to shorten at all, and we want to allow some
211 // safety, so use 0.3 instead of 0.5 as a multiplier.
212 int removal_length = removal_lengths[j];
213 double area_edge =
214 compute_latitude_precision(removal_length) * safety_factor;
215 if (range < area_edge) {
216 start = removal_length;
217 break;
220 int pos = 0;
221 for (int j = start; j < info.size && code[j] != '\0'; ++j) {
222 shortened[pos++] = code[j];
224 shortened[pos] = '\0';
225 return pos;
228 int OLC_RecoverNearest(const char* short_code, size_t size,
229 const OLC_LatLon* reference, char* code, int maxlen) {
230 CodeInfo info;
231 if (analyse(short_code, size, &info) <= 0) {
232 return 0;
234 // Check if it is a full code - then we just convert to upper case.
235 if (is_full(&info)) {
236 OLC_CodeArea code_area;
237 decode(&info, &code_area);
238 OLC_LatLon center;
239 OLC_GetCenter(&code_area, &center);
240 return OLC_Encode(&center, code_area.len, code, maxlen);
242 if (!is_short(&info)) {
243 return 0;
245 int len = code_length(&info);
247 // Ensure that latitude and longitude are valid.
248 double lat = adjust_latitude(reference->lat, len);
249 double lon = normalize_longitude(reference->lon);
251 // Compute the number of digits we need to recover.
252 size_t padding_length = kSeparatorPosition;
253 if (info.sep_first >= 0) {
254 padding_length -= info.sep_first;
257 // The resolution (height and width) of the padded area in degrees.
258 double resolution = pow_neg(kEncodingBase, 2.0 - (padding_length / 2.0));
260 // Distance from the center to an edge (in degrees).
261 double half_res = resolution / 2.0;
263 // Use the reference location to pad the supplied short code and decode it.
264 OLC_LatLon latlon = {lat, lon};
265 char encoded[256];
266 OLC_EncodeDefault(&latlon, encoded, 256);
268 char new_code[256];
269 int pos = 0;
270 for (int j = 0; encoded[j] != '\0'; ++j) {
271 if (j >= padding_length) {
272 break;
274 new_code[pos++] = encoded[j];
276 for (int j = 0; j < info.size && short_code[j] != '\0'; ++j) {
277 new_code[pos++] = short_code[j];
279 new_code[pos] = '\0';
280 if (analyse(new_code, pos, &info) <= 0) {
281 return 0;
284 OLC_CodeArea code_area;
285 decode(&info, &code_area);
286 OLC_LatLon center;
287 OLC_GetCenter(&code_area, &center);
289 // How many degrees latitude is the code from the reference?
290 if (lat + half_res < center.lat &&
291 center.lat - resolution > -kLatMaxDegrees) {
292 // If the proposed code is more than half a cell north of the reference
293 // location, it's too far, and the best match will be one cell south.
294 center.lat -= resolution;
295 } else if (lat - half_res > center.lat &&
296 center.lat + resolution < kLatMaxDegrees) {
297 // If the proposed code is more than half a cell south of the reference
298 // location, it's too far, and the best match will be one cell north.
299 center.lat += resolution;
302 // How many degrees longitude is the code from the reference?
303 if (lon + half_res < center.lon) {
304 center.lon -= resolution;
305 } else if (lon - half_res > center.lon) {
306 center.lon += resolution;
309 return OLC_Encode(&center, len + padding_length, code, maxlen);
312 // private functions
314 static int analyse(const char* code, size_t size, CodeInfo* info) {
315 memset(info, 0, sizeof(CodeInfo));
317 // null code is not valid
318 if (!code) {
319 return 0;
321 if (!size) {
322 size = strlen(code);
325 info->code = code;
326 info->size = size < kMaximumDigitCount ? size : kMaximumDigitCount;
327 info->sep_first = -1;
328 info->sep_last = -1;
329 info->pad_first = -1;
330 info->pad_last = -1;
331 int j = 0;
332 for (j = 0; j <= size && code[j] != '\0'; ++j) {
333 int ok = 0;
335 // if this is a padding character, remember it
336 if (!ok && code[j] == kPaddingCharacter) {
337 if (info->pad_first < 0) {
338 info->pad_first = j;
340 info->pad_last = j;
341 ok = 1;
344 // if this is a separator character, remember it
345 if (!ok && code[j] == kSeparator) {
346 if (info->sep_first < 0) {
347 info->sep_first = j;
349 info->sep_last = j;
350 ok = 1;
353 // only accept characters in the valid character set
354 if (!ok && get_alphabet_position(code[j]) >= 0) {
355 ok = 1;
358 // didn't find anything expected => bail out
359 if (!ok) {
360 return 0;
364 // so far, code only has valid characters -- good
365 info->len = j < kMaximumDigitCount ? j : kMaximumDigitCount;
367 // Cannot be empty
368 if (info->len <= 0) {
369 return 0;
372 // The separator is required.
373 if (info->sep_first < 0) {
374 return 0;
377 // There can be only one... separator.
378 if (info->sep_last > info->sep_first) {
379 return 0;
382 // separator cannot be the only character
383 if (info->len == 1) {
384 return 0;
387 // Is the separator in an illegal position?
388 if (info->sep_first > kSeparatorPosition || (info->sep_first % 2)) {
389 return 0;
392 // padding cannot be at the initial position
393 if (info->pad_first == 0) {
394 return 0;
397 // We can have an even number of padding characters before the separator,
398 // but then it must be the final character.
399 if (info->pad_first > 0) {
400 // Short codes cannot have padding
401 if (info->sep_first < kSeparatorPosition) {
402 return 0;
405 // The first padding character needs to be in an odd position.
406 if (info->pad_first % 2) {
407 return 0;
410 // With padding, the separator must be the final character
411 if (info->sep_last < info->len - 1) {
412 return 0;
415 // After removing padding characters, we mustn't have anything left.
416 if (info->pad_last < info->sep_first - 1) {
417 return 0;
421 // If there are characters after the separator, make sure there isn't just
422 // one of them (not legal).
423 if (info->len - info->sep_first - 1 == 1) {
424 return 0;
427 return info->len;
430 static int is_short(CodeInfo* info) {
431 if (info->len <= 0) {
432 return 0;
435 // if there is a separator, it cannot be beyond the valid position
436 if (info->sep_first >= kSeparatorPosition) {
437 return 0;
440 return 1;
443 // checks that the first character of latitude or longitude is valid
444 static int valid_first_character(CodeInfo* info, int pos, double kMax) {
445 if (info->len <= pos) {
446 return 1;
449 // Work out what the first character indicates
450 size_t firstValue = get_alphabet_position(info->code[pos]);
451 firstValue *= kEncodingBase;
452 return firstValue < kMax;
455 static int is_full(CodeInfo* info) {
456 if (info->len <= 0) {
457 return 0;
460 // If there are less characters than expected before the separator.
461 if (info->sep_first < kSeparatorPosition) {
462 return 0;
465 // check first latitude character, if any
466 if (!valid_first_character(info, 0, kLatMaxDegreesT2)) {
467 return 0;
470 // check first longitude character, if any
471 if (!valid_first_character(info, 1, kLonMaxDegreesT2)) {
472 return 0;
475 return 1;
478 static int decode(CodeInfo* info, OLC_CodeArea* decoded) {
479 // Create a copy of the code, skipping padding and separators.
480 char clean_code[256];
481 int ci = 0;
482 for (size_t i = 0; i < info->len + 1; i++) {
483 if (info->code[i] != kPaddingCharacter && info->code[i] != kSeparator) {
484 clean_code[ci] = info->code[i];
485 ci++;
488 clean_code[ci] = '\0';
490 // Initialise the values for each section. We work them out as integers and
491 // convert them to floats at the end. Using doubles all the way results in
492 // multiplying small rounding errors until they become significant.
493 int normal_lat = -kLatMaxDegrees * kPairPrecisionInverse;
494 int normal_lng = -kLonMaxDegrees * kPairPrecisionInverse;
495 int extra_lat = 0;
496 int extra_lng = 0;
498 // How many digits do we have to process?
499 size_t digits = strlen(clean_code) < kPairCodeLength ? strlen(clean_code)
500 : kPairCodeLength;
501 // Define the place value for the most significant pair.
502 int pv = pow(kEncodingBase, kPairCodeLength / 2);
503 for (size_t i = 0; i < digits - 1; i += 2) {
504 pv /= kEncodingBase;
505 normal_lat += get_alphabet_position(clean_code[i]) * pv;
506 normal_lng += get_alphabet_position(clean_code[i + 1]) * pv;
508 // Convert the place value to a float in degrees.
509 double lat_precision = (double)pv / kPairPrecisionInverse;
510 double lng_precision = (double)pv / kPairPrecisionInverse;
511 // Process any extra precision digits.
512 if (strlen(clean_code) > kPairCodeLength) {
513 // How many digits do we have to process?
514 digits = strlen(clean_code) < kMaximumDigitCount ? strlen(clean_code)
515 : kMaximumDigitCount;
516 // Initialise the place values for the grid.
517 int row_pv = pow(kGridRows, kGridCodeLength);
518 int col_pv = pow(kGridCols, kGridCodeLength);
519 for (size_t i = kPairCodeLength; i < digits; i++) {
520 row_pv /= kGridRows;
521 col_pv /= kGridCols;
522 int dval = get_alphabet_position(clean_code[i]);
523 int row = dval / kGridCols;
524 int col = dval % kGridCols;
525 extra_lat += row * row_pv;
526 extra_lng += col * col_pv;
528 // Adjust the precisions from the integer values to degrees.
529 lat_precision = (double)row_pv / kGridLatPrecisionInverse;
530 lng_precision = (double)col_pv / kGridLonPrecisionInverse;
532 // Merge the values from the normal and extra precision parts of the code.
533 // Everything is ints so they all need to be cast to floats.
534 double lat = (double)normal_lat / kPairPrecisionInverse +
535 (double)extra_lat / kGridLatPrecisionInverse;
536 double lng = (double)normal_lng / kPairPrecisionInverse +
537 (double)extra_lng / kGridLonPrecisionInverse;
538 decoded->lo.lat = lat;
539 decoded->lo.lon = lng;
540 decoded->hi.lat = lat + lat_precision;
541 decoded->hi.lon = lng + lng_precision;
542 decoded->len = strlen(clean_code);
543 return decoded->len;
546 static size_t code_length(CodeInfo* info) {
547 int len = info->len;
548 if (info->sep_first >= 0) {
549 --len;
551 if (info->pad_first >= 0) {
552 len = info->pad_first;
554 return len;
557 // Raises a number to an exponent, handling negative exponents.
558 static double pow_neg(double base, double exponent) {
559 if (exponent == 0) {
560 return 1;
563 if (exponent > 0) {
564 return pow(base, exponent);
567 return 1 / pow(base, -exponent);
570 // Compute the latitude precision value for a given code length. Lengths <= 10
571 // have the same precision for latitude and longitude, but lengths > 10 have
572 // different precisions due to the grid method having fewer columns than rows.
573 static double compute_latitude_precision(int length) {
574 // Magic numbers!
575 if (length <= kPairCodeLength) {
576 return pow_neg(kEncodingBase, floor((length / -2) + 2));
579 return pow_neg(kEncodingBase, -3) / pow(kGridRows, length - kPairCodeLength);
582 // Normalize a longitude into the range -180 to 180, not including 180.
583 static double normalize_longitude(double lon_degrees) {
584 while (lon_degrees < -kLonMaxDegrees) {
585 lon_degrees += kLonMaxDegreesT2;
587 while (lon_degrees >= kLonMaxDegrees) {
588 lon_degrees -= kLonMaxDegreesT2;
590 return lon_degrees;
593 // Adjusts 90 degree latitude to be lower so that a legal OLC code can be
594 // generated.
595 static double adjust_latitude(double lat_degrees, size_t length) {
596 if (lat_degrees < -kLatMaxDegrees) {
597 lat_degrees = -kLatMaxDegrees;
599 if (lat_degrees > kLatMaxDegrees) {
600 lat_degrees = kLatMaxDegrees;
602 if (lat_degrees < kLatMaxDegrees) {
603 return lat_degrees;
605 // Subtract half the code precision to get the latitude into the code area.
606 double precision = compute_latitude_precision(length);
607 return lat_degrees - precision / 2;