7 #include "olc_private.h"
9 #include "betaflight.h"
11 #define CORRECT_IF_SEPARATOR(var, info) \
13 (var) += (info)->sep_first >= 0 ? 1 : 0; \
16 // Information about a code, produced by analyse();
17 typedef struct CodeInfo
{
20 // Total count of characters in the code including padding and separators.
22 // Count of valid digits (not including padding or separators).
24 // Index of the first separator in the code.
26 // Index of the last separator in the code. (If there is only one, same as
29 // Index of the first padding character in the code.
31 // Index of the last padding character in the code. (If there is only one,
32 // same as pad_first.)
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
) {
62 analyse(code
, size
, &info
);
63 return code_length(&info
);
66 int OLC_IsValid(const char* code
, size_t size
) {
68 return analyse(code
, size
, &info
) > 0;
71 int OLC_IsShort(const char* code
, size_t size
) {
73 if (analyse(code
, size
, &info
) <= 0) {
76 return is_short(&info
);
79 int OLC_IsFull(const char* code
, size_t size
) {
81 if (analyse(code
, size
, &info
) <= 0) {
84 return is_full(&info
);
87 int OLC_Encode(const OLC_LatLon
* location
, size_t length
, char* code
,
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";
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
;
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
;
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';
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
) {
170 if (analyse(code
, size
, &info
) <= 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
) {
179 if (analyse(code
, size
, &info
) <= 0) {
182 if (info
.pad_first
> 0) {
185 if (!is_full(&info
)) {
189 OLC_CodeArea code_area
;
190 decode(&info
, &code_area
);
192 OLC_GetCenter(&code_area
, ¢er
);
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.
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]);
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
];
214 compute_latitude_precision(removal_length
) * safety_factor
;
215 if (range
< area_edge
) {
216 start
= removal_length
;
221 for (int j
= start
; j
< info
.size
&& code
[j
] != '\0'; ++j
) {
222 shortened
[pos
++] = code
[j
];
224 shortened
[pos
] = '\0';
228 int OLC_RecoverNearest(const char* short_code
, size_t size
,
229 const OLC_LatLon
* reference
, char* code
, int maxlen
) {
231 if (analyse(short_code
, size
, &info
) <= 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
);
239 OLC_GetCenter(&code_area
, ¢er
);
240 return OLC_Encode(¢er
, code_area
.len
, code
, maxlen
);
242 if (!is_short(&info
)) {
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
};
266 OLC_EncodeDefault(&latlon
, encoded
, 256);
270 for (int j
= 0; encoded
[j
] != '\0'; ++j
) {
271 if (j
>= padding_length
) {
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) {
284 OLC_CodeArea code_area
;
285 decode(&info
, &code_area
);
287 OLC_GetCenter(&code_area
, ¢er
);
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(¢er
, len
+ padding_length
, code
, maxlen
);
314 static int analyse(const char* code
, size_t size
, CodeInfo
* info
) {
315 memset(info
, 0, sizeof(CodeInfo
));
317 // null code is not valid
326 info
->size
= size
< kMaximumDigitCount
? size
: kMaximumDigitCount
;
327 info
->sep_first
= -1;
329 info
->pad_first
= -1;
332 for (j
= 0; j
<= size
&& code
[j
] != '\0'; ++j
) {
335 // if this is a padding character, remember it
336 if (!ok
&& code
[j
] == kPaddingCharacter
) {
337 if (info
->pad_first
< 0) {
344 // if this is a separator character, remember it
345 if (!ok
&& code
[j
] == kSeparator
) {
346 if (info
->sep_first
< 0) {
353 // only accept characters in the valid character set
354 if (!ok
&& get_alphabet_position(code
[j
]) >= 0) {
358 // didn't find anything expected => bail out
364 // so far, code only has valid characters -- good
365 info
->len
= j
< kMaximumDigitCount
? j
: kMaximumDigitCount
;
368 if (info
->len
<= 0) {
372 // The separator is required.
373 if (info
->sep_first
< 0) {
377 // There can be only one... separator.
378 if (info
->sep_last
> info
->sep_first
) {
382 // separator cannot be the only character
383 if (info
->len
== 1) {
387 // Is the separator in an illegal position?
388 if (info
->sep_first
> kSeparatorPosition
|| (info
->sep_first
% 2)) {
392 // padding cannot be at the initial position
393 if (info
->pad_first
== 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
) {
405 // The first padding character needs to be in an odd position.
406 if (info
->pad_first
% 2) {
410 // With padding, the separator must be the final character
411 if (info
->sep_last
< info
->len
- 1) {
415 // After removing padding characters, we mustn't have anything left.
416 if (info
->pad_last
< info
->sep_first
- 1) {
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) {
430 static int is_short(CodeInfo
* info
) {
431 if (info
->len
<= 0) {
435 // if there is a separator, it cannot be beyond the valid position
436 if (info
->sep_first
>= kSeparatorPosition
) {
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
) {
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) {
460 // If there are less characters than expected before the separator.
461 if (info
->sep_first
< kSeparatorPosition
) {
465 // check first latitude character, if any
466 if (!valid_first_character(info
, 0, kLatMaxDegreesT2
)) {
470 // check first longitude character, if any
471 if (!valid_first_character(info
, 1, kLonMaxDegreesT2
)) {
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];
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
];
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
;
498 // How many digits do we have to process?
499 size_t digits
= strlen(clean_code
) < kPairCodeLength
? strlen(clean_code
)
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) {
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
++) {
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
);
546 static size_t code_length(CodeInfo
* info
) {
548 if (info
->sep_first
>= 0) {
551 if (info
->pad_first
>= 0) {
552 len
= info
->pad_first
;
557 // Raises a number to an exponent, handling negative exponents.
558 static double pow_neg(double base
, double exponent
) {
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
) {
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
;
593 // Adjusts 90 degree latitude to be lower so that a legal OLC code can be
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
) {
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;