OpenCPN Partial API docs
Loading...
Searching...
No Matches
navutil_base.cpp
Go to the documentation of this file.
1/***************************************************************************
2 * Copyright (C) 2010 by David S. Register *
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; either version 2 of the License, or *
7 * (at your option) any later version. *
8 * *
9 * This program is distributed in the hope that it will be useful, *
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
12 * GNU General Public License for more details. *
13 *
14 * You should have received a copy of the GNU General Public License *
15 * along with this program; if not, see <https://www.gnu.org/licenses/>. *
16 **************************************************************************/
17
24#include <iomanip>
25#include <sstream>
26
27#include <wx/datetime.h>
28#include <wx/math.h>
29#include <wx/string.h>
30#include <wx/translation.h>
31
32#include "model/navutil_base.h"
33#include "model/own_ship.h"
34#include "vector2D.h"
35
37static wxTimeSpan RoundToMinutes(const wxTimeSpan &span) {
38 auto minutes = span.GetMinutes() % 60;
39 auto seconds = span.GetSeconds() % 60;
40 if (seconds > 30) minutes += 1;
41 return wxTimeSpan(span.GetHours(), minutes, 0);
42}
43
44wxString toSDMM(int NEflag, double a, bool hi_precision) {
45 wxString s;
46 double mpy;
47 short neg = 0;
48 int d;
49 long m;
50 double ang = a;
51 char c = 'N';
52
53 if (a < 0.0) {
54 a = -a;
55 neg = 1;
56 }
57 d = (int)a;
58 if (neg) d = -d;
59 if (NEflag) {
60 if (NEflag == 1) {
61 c = 'N';
62
63 if (neg) {
64 d = -d;
65 c = 'S';
66 }
67 } else if (NEflag == 2) {
68 c = 'E';
69
70 if (neg) {
71 d = -d;
72 c = 'W';
73 }
74 }
75 }
76
77 switch (g_iSDMMFormat) {
78 case 0:
79 mpy = 600.0;
80 if (hi_precision) mpy = mpy * 1000;
81
82 m = (long)wxRound((a - (double)d) * mpy);
83
84 if (!NEflag || NEflag < 1 || NEflag > 2) // Does it EVER happen?
85 {
86 if (hi_precision)
87 s.Printf("%d%c %02ld.%04ld'", d, 0x00B0, m / 10000, m % 10000);
88 else
89 s.Printf("%d%c %02ld.%01ld'", d, 0x00B0, m / 10, m % 10);
90 } else {
91 if (hi_precision)
92 if (NEflag == 1)
93 s.Printf("%02d%c %02ld.%04ld' %c", d, 0x00B0, m / 10000,
94 (m % 10000), c);
95 else
96 s.Printf("%03d%c %02ld.%04ld' %c", d, 0x00B0, m / 10000,
97 (m % 10000), c);
98 else if (NEflag == 1)
99 s.Printf("%02d%c %02ld.%01ld' %c", d, 0x00B0, m / 10, (m % 10), c);
100 else
101 s.Printf("%03d%c %02ld.%01ld' %c", d, 0x00B0, m / 10, (m % 10), c);
102 }
103 break;
104 case 1:
105 if (hi_precision)
106 s.Printf("%03.6f",
107 ang); // cca 11 cm - the GPX precision is higher, but as we
108 // use hi_precision almost everywhere it would be a
109 // little too much....
110 else
111 s.Printf("%03.4f", ang); // cca 11m
112 break;
113 case 2:
114 m = (long)((a - (double)d) * 60);
115 mpy = 10.0;
116 if (hi_precision) mpy = mpy * 100;
117 long sec = (long)((a - (double)d - (((double)m) / 60)) * 3600 * mpy);
118
119 if (!NEflag || NEflag < 1 || NEflag > 2) // Does it EVER happen?
120 {
121 if (hi_precision)
122 s.Printf("%d%c %ld'%ld.%ld\"", d, 0x00B0, m, sec / 1000, sec % 1000);
123 else
124 s.Printf("%d%c %ld'%ld.%ld\"", d, 0x00B0, m, sec / 10, sec % 10);
125 } else {
126 if (hi_precision)
127 if (NEflag == 1)
128 s.Printf("%02d%c %02ld' %02ld.%03ld\" %c", d, 0x00B0, m, sec / 1000,
129 sec % 1000, c);
130 else
131 s.Printf("%03d%c %02ld' %02ld.%03ld\" %c", d, 0x00B0, m, sec / 1000,
132 sec % 1000, c);
133 else if (NEflag == 1)
134 s.Printf("%02d%c %02ld' %02ld.%ld\" %c", d, 0x00B0, m, sec / 10,
135 sec % 10, c);
136 else
137 s.Printf("%03d%c %02ld' %02ld.%ld\" %c", d, 0x00B0, m, sec / 10,
138 sec % 10, c);
139 }
140 break;
141 }
142 return s;
143}
144
145/**************************************************************************/
146/* Converts the speed to the units selected by user */
147/**************************************************************************/
148double toUsrSpeed(double kts_speed, int unit) {
149 double ret = NAN;
150 if (unit == -1) unit = g_iSpeedFormat;
151 switch (unit) {
152 case SPEED_KTS: // kts
153 ret = kts_speed;
154 break;
155 case SPEED_MPH: // mph
156 ret = kts_speed * 1.15078;
157 break;
158 case SPEED_KMH: // km/h
159 ret = kts_speed * 1.852;
160 break;
161 case SPEED_MS: // m/s
162 ret = kts_speed * 0.514444444;
163 break;
164 }
165 return ret;
166}
167
168/**************************************************************************/
169/* Converts the wind speed to the units selected by user */
170/**************************************************************************/
171double toUsrWindSpeed(double kts_wspeed, int unit) {
172 double ret = NAN;
173 if (unit == -1) unit = g_iWindSpeedFormat;
174 switch (unit) {
175 case WSPEED_KTS: // kts
176 ret = kts_wspeed;
177 break;
178 case WSPEED_MS: // m/s
179 ret = kts_wspeed * 0.514444444;
180 break;
181 case WSPEED_MPH: // mph
182 ret = kts_wspeed * 1.15078;
183 break;
184 case WSPEED_KMH: // km/h
185 ret = kts_wspeed * 1.852;
186 break;
187 }
188 return ret;
189}
190
191double toUsrDistance(double nm_distance, int unit) {
192 double ret = NAN;
193 if (unit == -1) unit = g_iDistanceFormat;
194 switch (unit) {
195 case DISTANCE_NMI: // Nautical miles
196 ret = nm_distance;
197 break;
198 case DISTANCE_MI: // Statute miles
199 ret = nm_distance * 1.15078;
200 break;
201 case DISTANCE_KM:
202 ret = nm_distance * 1.852;
203 break;
204 case DISTANCE_M:
205 ret = nm_distance * 1852;
206 break;
207 case DISTANCE_FT:
208 ret = nm_distance * 6076.12;
209 break;
210 case DISTANCE_FA:
211 ret = nm_distance * 1012.68591;
212 break;
213 case DISTANCE_IN:
214 ret = nm_distance * 72913.4;
215 break;
216 case DISTANCE_CM:
217 ret = nm_distance * 185200;
218 break;
219 }
220 return ret;
221}
222
223/**************************************************************************/
224/* Converts the temperature to the units selected by user */
225/**************************************************************************/
226double toUsrTemp(double cel_temp, int unit) {
227 double ret = NAN;
228 if (unit == -1) unit = g_iTempFormat;
229 switch (unit) {
230 case TEMPERATURE_C: // Celsius
231 ret = cel_temp;
232 break;
233 case TEMPERATURE_F: // Fahrenheit
234 ret = (cel_temp * 9.0 / 5.0) + 32;
235 break;
236 case TEMPERATURE_K:
237 ret = cel_temp + 273.15;
238 break;
239 }
240 return ret;
241}
242
243/**************************************************************************/
244/* Returns the abbreviation of user selected temperature unit */
245/**************************************************************************/
246wxString getUsrTempUnit(int unit) {
247 wxString ret;
248 if (unit == -1) unit = g_iTempFormat;
249 switch (unit) {
250 case TEMPERATURE_C: // Celsius
251 ret = _("C");
252 break;
253 case TEMPERATURE_F: // Fahrenheit
254 ret = _("F");
255 break;
256 case TEMPERATURE_K: // Kelvin
257 ret = _("K");
258 break;
259 }
260 return ret;
261}
262
263/**************************************************************************/
264/* Returns the abbreviation of user selected distance unit */
265/**************************************************************************/
266wxString getUsrDistanceUnit(int unit) {
267 wxString ret;
268 if (unit == -1) unit = g_iDistanceFormat;
269 switch (unit) {
270 case DISTANCE_NMI: // Nautical miles
271 ret = _("NMi");
272 break;
273 case DISTANCE_MI: // Statute miles
274 ret = _("mi");
275 break;
276 case DISTANCE_KM:
277 ret = _("km");
278 break;
279 case DISTANCE_M:
280 ret = _("m");
281 break;
282 case DISTANCE_FT:
283 ret = _("ft");
284 break;
285 case DISTANCE_FA:
286 ret = _("fa");
287 break;
288 case DISTANCE_IN:
289 ret = _("in");
290 break;
291 case DISTANCE_CM:
292 ret = _("cm");
293 break;
294 }
295 return ret;
296}
297
298/**************************************************************************/
299/* Returns the abbreviation of user selected speed unit */
300/**************************************************************************/
301wxString getUsrSpeedUnit(int unit) {
302 wxString ret;
303 if (unit == -1) unit = g_iSpeedFormat;
304 switch (unit) {
305 case SPEED_KTS: // kts
306 ret = _("kts");
307 break;
308 case SPEED_MPH: // mph
309 ret = _("mph");
310 break;
311 case SPEED_KMH:
312 ret = _("km/h");
313 break;
314 case SPEED_MS:
315 ret = _("m/s");
316 break;
317 }
318 return ret;
319}
320
321/**************************************************************************/
322/* Returns the abbreviation of user selected wind speed unit */
323/**************************************************************************/
324wxString getUsrWindSpeedUnit(int unit) {
325 wxString ret;
326 if (unit == -1) unit = g_iWindSpeedFormat;
327 switch (unit) {
328 case WSPEED_KTS: // kts
329 ret = _("kts");
330 break;
331 case WSPEED_MS:
332 ret = _("m/s");
333 break;
334 case WSPEED_MPH: // mph
335 ret = _("mph");
336 break;
337 case WSPEED_KMH:
338 ret = _("km/h");
339 break;
340 }
341 return ret;
342}
343
344wxString FormatDistanceAdaptive(double distance) {
345 wxString result;
347 double usrDistance = toUsrDistance(distance, unit);
348 if (usrDistance < 0.1 &&
349 (unit == DISTANCE_KM || unit == DISTANCE_MI || unit == DISTANCE_NMI)) {
350 unit = (unit == DISTANCE_MI) ? DISTANCE_FT : DISTANCE_M;
351 usrDistance = toUsrDistance(distance, unit);
352 }
353 wxString format;
354 if (usrDistance < 5.0) {
355 format = "%1.2f ";
356 } else if (usrDistance < 100.0) {
357 format = "%2.1f ";
358 } else if (usrDistance < 1000.0) {
359 format = "%3.0f ";
360 } else {
361 format = "%4.0f ";
362 }
363 result << wxString::Format(format, usrDistance) << getUsrDistanceUnit(unit);
364 return result;
365}
366
367/**************************************************************************/
368/* Converts the speed from the units selected by user to knots */
369/**************************************************************************/
370double fromUsrSpeed(double usr_speed, int unit, int default_val) {
371 double ret = NAN;
372
373 if (unit == -1) unit = default_val;
374 switch (unit) {
375 case SPEED_KTS: // kts
376 ret = usr_speed;
377 break;
378 case SPEED_MPH: // mph
379 ret = usr_speed / 1.15078;
380 break;
381 case SPEED_KMH: // km/h
382 ret = usr_speed / 1.852;
383 break;
384 case SPEED_MS: // m/s
385 ret = usr_speed / 0.514444444;
386 break;
387 }
388 return ret;
389}
390
391/**************************************************************************/
392/* Converts the distance from the units selected by user to NMi */
393/**************************************************************************/
394double fromUsrDistance(double usr_distance, int unit, int default_val) {
395 double ret = NAN;
396 if (unit == -1) {
397 // Use g_iDistanceFormat as default when default_val is -1
398 unit = (default_val == -1) ? g_iDistanceFormat : default_val;
399 }
400 switch (unit) {
401 case DISTANCE_NMI: // Nautical miles
402 ret = usr_distance;
403 break;
404 case DISTANCE_MI: // Statute miles
405 ret = usr_distance / 1.15078;
406 break;
407 case DISTANCE_KM:
408 ret = usr_distance / 1.852;
409 break;
410 case DISTANCE_M:
411 ret = usr_distance / 1852;
412 break;
413 case DISTANCE_FT:
414 ret = usr_distance / 6076.12;
415 break;
416 case DISTANCE_FA:
417 ret = usr_distance / 1012.68591;
418 break;
419 case DISTANCE_IN:
420 ret = usr_distance / 72913.4;
421 break;
422 case DISTANCE_CM:
423 ret = usr_distance / 185200;
424 break;
425 }
426 return ret;
427}
428
429double toUsrDepth(double m_depth, int unit) {
430 double ret = NAN;
431 if (unit == -1) unit = g_nDepthUnitDisplay;
432 switch (unit) {
433 case DEPTH_FT: // Feet
434 ret = m_depth / 0.3048;
435 break;
436 case DEPTH_M: // Meters
437 ret = m_depth;
438 break;
439 case DEPTH_FA:
440 ret = m_depth / 0.3048 / 6;
441 break;
442 }
443 return ret;
444}
445
446/**************************************************************************/
447/* Converts the depth from the units selected by user to Meters */
448/**************************************************************************/
449double fromUsrDepth(double usr_depth, int unit) {
450 double ret = NAN;
451 if (unit == -1) unit = g_nDepthUnitDisplay;
452 switch (unit) {
453 case DEPTH_FT: // Feet
454 ret = usr_depth * 0.3048;
455 break;
456 case DEPTH_M: // Feet
457 ret = usr_depth;
458 break;
459 case DEPTH_FA: // Fathoms
460 ret = usr_depth * 0.3048 * 6;
461 break;
462 }
463 return ret;
464}
465
466/**************************************************************************/
467/* Returns the abbreviation of user selected depth unit */
468/**************************************************************************/
469wxString getUsrDepthUnit(int unit) {
470 wxString ret;
471 if (unit == -1) unit = g_nDepthUnitDisplay;
472 switch (unit) {
473 case DEPTH_FT: // Feet
474 ret = _("ft");
475 break;
476 case DEPTH_M: // Meters
477 ret = _("m");
478 break;
479 case DEPTH_FA: // Fathoms
480 ret = _("fa");
481 break;
482 }
483 return ret;
484}
485
486double toUsrHeight(double m_height, int unit) {
487 double ret = NAN;
488 if (unit == -1) unit = g_iHeightFormat;
489 switch (unit) {
490 case HEIGHT_M: // Meters
491 ret = m_height;
492 break;
493 case HEIGHT_FT: // Feet
494 ret = m_height / 0.3048;
495 break;
496 }
497 return ret;
498}
499
500double fromUsrHeight(double usr_height, int unit) {
501 double ret = NAN;
502 if (unit == -1) unit = g_iHeightFormat;
503 switch (unit) {
504 case HEIGHT_M: // Meters
505 ret = usr_height;
506 break;
507 case HEIGHT_FT: // Feet
508 ret = usr_height * 0.3048;
509 break;
510 }
511 return ret;
512}
513
514wxString getUsrHeightUnit(int unit) {
515 wxString ret;
516 if (unit == -1) unit = g_iHeightFormat;
517 switch (unit) {
518 case HEIGHT_M: // Meters
519 ret = _("m");
520 break;
521 case HEIGHT_FT: // Feet
522 ret = _("ft");
523 break;
524 }
525 return ret;
526}
527
528//---------------------------------------------------------------------------------
529// Vector Stuff for Hit Test Algorithm
530//---------------------------------------------------------------------------------
531double vGetLengthOfNormal(pVector2D a, pVector2D b, pVector2D n) {
532 vector2D c, vNormal;
533 vNormal.x = 0;
534 vNormal.y = 0;
535 //
536 // Obtain projection vector.
537 //
538 // c = ((a * b)/(|b|^2))*b
539 //
540 c.x = b->x * (vDotProduct(a, b) / vDotProduct(b, b));
541 c.y = b->y * (vDotProduct(a, b) / vDotProduct(b, b));
542 //
543 // Obtain perpendicular projection : e = a - c
544 //
545 vSubtractVectors(a, &c, &vNormal);
546 //
547 // Fill PROJECTION structure with appropriate values.
548 //
549 *n = vNormal;
550
551 return (vVectorMagnitude(&vNormal));
552}
553
554double vDotProduct(pVector2D v0, pVector2D v1) {
555 double dotprod;
556
557 dotprod =
558 (v0 == NULL || v1 == NULL) ? 0.0 : (v0->x * v1->x) + (v0->y * v1->y);
559
560 return (dotprod);
561}
562
563pVector2D vAddVectors(pVector2D v0, pVector2D v1, pVector2D v) {
564 if (v0 == NULL || v1 == NULL)
565 v = (pVector2D)NULL;
566 else {
567 v->x = v0->x + v1->x;
568 v->y = v0->y + v1->y;
569 }
570 return (v);
571}
572
573pVector2D vSubtractVectors(pVector2D v0, pVector2D v1, pVector2D v) {
574 if (v0 == NULL || v1 == NULL)
575 v = (pVector2D)NULL;
576 else {
577 v->x = v0->x - v1->x;
578 v->y = v0->y - v1->y;
579 }
580 return (v);
581}
582
583double vVectorSquared(pVector2D v0) {
584 double dS;
585
586 if (v0 == NULL)
587 dS = 0.0;
588 else
589 dS = ((v0->x * v0->x) + (v0->y * v0->y));
590 return (dS);
591}
592
593double vVectorMagnitude(pVector2D v0) {
594 double dMagnitude;
595
596 if (v0 == NULL)
597 dMagnitude = 0.0;
598 else
599 dMagnitude = sqrt(vVectorSquared(v0));
600 return (dMagnitude);
601}
602
603const wxChar *ParseGPXDateTime(wxDateTime &dt, const wxChar *datetime) {
604 long sign, hrs_west, mins_west;
605 const wxChar *end;
606
607 // Skip any leading whitespace
608 while (isspace(*datetime)) datetime++;
609
610 // Skip (and ignore) leading hyphen
611 if (*datetime == '-') datetime++;
612
613 // Parse and validate ISO 8601 date/time string
614 if ((end = dt.ParseFormat(datetime, "%Y-%m-%dT%T")) != NULL) {
615 // Invalid date/time
616 if (*end == 0) return NULL;
617
618 // ParseFormat outputs in UTC if the controlling
619 // wxDateTime class instance has not been initialized.
620
621 // Date/time followed by UTC time zone flag, so we are done
622 else if (*end == 'Z') {
623 end++;
624 return end;
625 }
626
627 // Date/time followed by given number of hrs/mins west of UTC
628 else if (*end == '+' || *end == '-') {
629 // Save direction from UTC
630 if (*end == '+')
631 sign = 1;
632 else
633 sign = -1;
634 end++;
635
636 // Parse hrs west of UTC
637 if (isdigit(*end) && isdigit(*(end + 1)) && *(end + 2) == ':') {
638 // Extract and validate hrs west of UTC
639 wxString(end).ToLong(&hrs_west);
640 if (hrs_west > 12) return NULL;
641 end += 3;
642
643 // Parse mins west of UTC
644 if (isdigit(*end) && isdigit(*(end + 1))) {
645 // Extract and validate mins west of UTC
646 wxChar mins[3];
647 mins[0] = *end;
648 mins[1] = *(end + 1);
649 mins[2] = 0;
650 wxString(mins).ToLong(&mins_west);
651 if (mins_west > 59) return NULL;
652
653 // Apply correction
654 dt -= sign * wxTimeSpan(hrs_west, mins_west, 0, 0);
655 return end + 2;
656 } else
657 // Missing mins digits
658 return NULL;
659 } else
660 // Missing hrs digits or colon
661 return NULL;
662 } else
663 // Unknown field after date/time (not UTC, not hrs/mins
664 // west of UTC)
665 return NULL;
666 } else
667 // Invalid ISO 8601 date/time
668 return NULL;
669}
670
671wxString formatTimeDelta(wxTimeSpan span) {
672 using namespace std;
673 // wxTimeSpan is returns complete span in different units.
674 // FIXME: (leamas) Replace with sane std::chrono.
675 stringstream ss;
676 ss << setfill(' ');
677 if (span.GetHours() > 0) span = RoundToMinutes(span);
678 if (span.GetDays() > 0) ss << setw(2) << span.GetDays() << "d ";
679 if (span.GetHours() > 0) {
680 ss << setw(2) << span.GetHours() % 24 << _("H ");
681 ss << setw(2) << span.GetMinutes() % 60 << _("M");
682 } else {
683 ss << setw(2) << span.GetMinutes() % 60 << _("M ");
684 ss << setw(2) << span.GetSeconds() % 60 << _("S");
685 }
686 return ss.str();
687}
688
689wxString formatTimeDelta(wxDateTime startTime, wxDateTime endTime) {
690 wxString timeStr;
691 if (startTime.IsValid() && endTime.IsValid()) {
692 wxTimeSpan span = endTime - startTime;
693 return formatTimeDelta(span);
694 } else {
695 return _("N/A");
696 }
697}
698
699wxString formatTimeDelta(wxLongLong secs) {
700 wxString timeStr;
701
702 wxTimeSpan span(0, 0, secs);
703 return formatTimeDelta(span);
704}
705
706/****************************************************************************/
707// Modified from the code posted by Andy Ross at
708// http://www.mail-archive.com/flightgear-devel@flightgear.org/msg06702.html
709// Basically, it looks for a list of decimal numbers embedded in the
710// string and uses the first three as degree, minutes and seconds. The
711// presence of a "S" or "W character indicates that the result is in a
712// hemisphere where the final answer must be negated. Non-number
713// characters are treated as whitespace separating numbers.
714//
715// So there are lots of bogus strings you can feed it to get a bogus
716// answer, but that's not surprising. It does, however, correctly parse
717// all the well-formed strings I can thing of to feed it. I've tried all
718// the following:
719//
720// 37°54.204' N
721// N37 54 12
722// 37°54'12"
723// 37.9034
724// 122°18.621' W
725// 122w 18 37
726// -122.31035
727/****************************************************************************/
728double fromDMM(wxString sdms) {
729 wchar_t buf[64];
730 char narrowbuf[64];
731 int i, len, top = 0;
732 double stk[32], sign = 1;
733
734 // First round of string modifications to accomodate some known strange
735 // formats
736 wxString replhelper;
737 replhelper = wxString::FromUTF8("´·"); // UKHO PDFs
738 sdms.Replace(replhelper, ".");
739 replhelper =
740 wxString::FromUTF8("\"·"); // Don't know if used, but to make sure
741 sdms.Replace(replhelper, ".");
742 replhelper = wxString::FromUTF8("·");
743 sdms.Replace(replhelper, ".");
744
745 replhelper =
746 wxString::FromUTF8("s. š."); // Another example: cs.wikipedia.org
747 // (someone was too active translating...)
748 sdms.Replace(replhelper, "N");
749 replhelper = wxString::FromUTF8("j. š.");
750 sdms.Replace(replhelper, "S");
751 sdms.Replace("v. d.", "E");
752 sdms.Replace("z. d.", "W");
753
754 // If the string contains hemisphere specified by a letter, then '-' is for
755 // sure a separator...
756 sdms.UpperCase();
757 if (sdms.Contains("N") || sdms.Contains("S") || sdms.Contains("E") ||
758 sdms.Contains("W"))
759 sdms.Replace("-", " ");
760
761 wcsncpy(buf, sdms.wc_str(wxConvUTF8), 63);
762 buf[63] = 0;
763 len = wxMin(wcslen(buf), sizeof(narrowbuf) - 1);
764 ;
765
766 for (i = 0; i < len; i++) {
767 wchar_t c = buf[i];
768 if ((c >= '0' && c <= '9') || c == '-' || c == '.' || c == '+') {
769 narrowbuf[i] = c;
770 continue; /* Digit characters are cool as is */
771 }
772 if (c == ',') {
773 narrowbuf[i] = '.'; /* convert to decimal dot */
774 continue;
775 }
776 if ((c | 32) == 'w' || (c | 32) == 's')
777 sign = -1; /* These mean "negate" (note case insensitivity) */
778 narrowbuf[i] = 0; /* Replace everything else with nuls */
779 }
780
781 /* Build a stack of doubles */
782 stk[0] = stk[1] = stk[2] = 0;
783 for (i = 0; i < len; i++) {
784 while (i < len && narrowbuf[i] == 0) i++;
785 if (i != len) {
786 stk[top++] = atof(narrowbuf + i);
787 i += strlen(narrowbuf + i);
788 }
789 }
790
791 return sign * (stk[0] + (stk[1] + stk[2] / 60) / 60);
792}
793
794double toMagnetic(double deg_true) {
795 if (!std::isnan(gVar)) {
796 if ((deg_true - gVar) > 360.)
797 return (deg_true - gVar - 360.);
798 else
799 return ((deg_true - gVar) >= 0.) ? (deg_true - gVar)
800 : (deg_true - gVar + 360.);
801 } else {
802 if ((deg_true - g_UserVar) > 360.)
803 return (deg_true - g_UserVar - 360.);
804 else
805 return ((deg_true - g_UserVar) >= 0.) ? (deg_true - g_UserVar)
806 : (deg_true - g_UserVar + 360.);
807 }
808}
809
810double toMagnetic(double deg_true, double variation) {
811 double degm = deg_true - variation;
812 if (degm >= 360.)
813 return degm - 360.;
814 else
815 return degm >= 0. ? degm : degm + 360.;
816}
817
818// Function to remove invalid Windows filename characters from a wxString
819wxString SanitizeFileName(const wxString &input) {
820 // List of invalid characters for Windows filenames
821 static const wxString invalidChars = "<>:\"/\\|?*";
822 wxString result;
823 for (wxUniChar ch : input) {
824 if (invalidChars.Contains(ch))
825 result += '_';
826 else
827 result += ch;
828 }
829 return result;
830}
int g_iTempFormat
User-selected temperature unit format for display and input.
int g_iHeightFormat
User-selected height (vertical, above reference datum) unit format for display and input.
int g_nDepthUnitDisplay
User-selected depth (below surface) unit format for display and input.
int g_iSpeedFormat
User-selected speed unit format for display and input.
int g_iDistanceFormat
User-selected distance (horizontal) unit format for display and input.
const wxChar * ParseGPXDateTime(wxDateTime &dt, const wxChar *datetime)
This function parses a string containing a GPX time representation and returns a wxDateTime containin...
wxString getUsrHeightUnit(int unit)
Get the abbreviation for the preferred height unit.
double toUsrHeight(double m_height, int unit)
Convert height from meters to preferred height units.
double fromUsrDistance(double usr_distance, int unit, int default_val)
Convert distance from user units to nautical miles.
double fromUsrHeight(double usr_height, int unit)
Convert height from preferred height units to meters.
double toUsrDepth(double m_depth, int unit)
Convert a depth from meters to user display units.
double toUsrTemp(double cel_temp, int unit)
Convert a temperature from Celsius to user display units.
double toUsrDistance(double nm_distance, int unit)
Convert a distance from nautical miles (NMi) to user display units.
wxString FormatDistanceAdaptive(double distance)
Format a distance (given in nautical miles) using the current distance preference,...
Navigation Utility Functions without GUI dependencies.
double gVar
Magnetic variation in degrees.
Definition own_ship.cpp:32
Position, course, speed, etc.