OpenCPN Partial API docs
Loading...
Searching...
No Matches
clock.cpp
Go to the documentation of this file.
1/**************************************************************************
2 * Copyright (C) 2010 by David S. Register *
3 * Copyright (C) 2010 by Pavel Kalian *
4 * *
5 * This program is free software; you can redistribute it and/or modify *
6 * it under the terms of the GNU General Public License as published by *
7 * the Free Software Foundation; either version 2 of the License, or *
8 * (at your option) any later version. *
9 * *
10 * This program is distributed in the hope that it will be useful, *
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
13 * GNU General Public License for more details. *
14 * *
15 * You should have received a copy of the GNU General Public License *
16 * along with this program; if not, see <https://www.gnu.org/licenses/>. *
17 **************************************************************************/
18
25#include "clock.h"
26
27#include <wx/wxprec.h>
28
29#ifndef WX_PRECOMP
30#include <wx/wx.h>
31#endif
32
33DashboardInstrument_Clock::DashboardInstrument_Clock(
34 wxWindow *parent, wxWindowID id, wxString title,
35 InstrumentProperties *Properties, DASH_CAP cap_flag, wxString format)
36 : DashboardInstrument_Single(parent, id, title, Properties, cap_flag,
37 format) {
38 // if format contains the string "LCL" then display time in local TZ
39 if (format.Contains("LCL"))
40 SetUtc(false);
41 else
42 SetUtc(true);
43 m_Properties = Properties;
44}
45
46void DashboardInstrument_Clock::SetData(DASH_CAP, double, wxString) {
47 // Nothing to do here but we want to override the default
48}
49
50void DashboardInstrument_Clock::SetUtcTime(wxDateTime data) {
51 m_data = GetDisplayTime(data);
52 Refresh();
53}
54
55wxString DashboardInstrument_Clock::GetDisplayTime(wxDateTime UTCtime) {
56 wxString result("---");
57 if (UTCtime.IsValid()) {
58 if (GetUtc()) {
59 result = UTCtime.FormatISOTime().Append(" UTC");
60 return result;
61 }
62 wxDateTime displayTime;
63 if (g_iUTCOffset != 0) {
64 wxTimeSpan offset(0, g_iUTCOffset * 30, 0);
65 displayTime = UTCtime.Add(offset);
66 } else {
67 displayTime = UTCtime.FromTimezone(wxDateTime::UTC);
68 }
69 result = displayTime.FormatISOTime().Append(" LCL");
70 }
71 return result;
72}
73
74DashboardInstrument_CPUClock::DashboardInstrument_CPUClock(
75 wxWindow *parent, wxWindowID id, wxString title,
76 InstrumentProperties *Properties, wxString format)
77 : DashboardInstrument_Clock(parent, id, title, Properties, OCPN_DBP_STC_LAT,
78 format) {
79 m_cap_flag.set(OCPN_DBP_STC_LON);
80 m_cap_flag.set(OCPN_DBP_STC_CLK);
81}
82
83void DashboardInstrument_CPUClock::SetData(DASH_CAP, double, wxString) {
84 // Nothing to do here but we want to override the default
85}
86
87void DashboardInstrument_CPUClock::SetUtcTime(wxDateTime data) {
88 m_data = wxDateTime::Now().FormatISOTime().Append(" CPU");
89 Refresh();
90}
91
92DashboardInstrument_Moon::DashboardInstrument_Moon(
93 wxWindow *parent, wxWindowID id, wxString title,
94 InstrumentProperties *Properties)
95 : DashboardInstrument_Clock(parent, id, title, Properties, OCPN_DBP_STC_CLK,
96 "%i/4 %c") {
97 m_cap_flag.set(OCPN_DBP_STC_LAT);
98 m_phase = -1;
99 m_radius = 14;
100 m_hemisphere = "";
101}
102
103wxSize DashboardInstrument_Moon::GetSize(int orient, wxSize hint) {
104 InitTitleSize();
105
106 int drawHeight = 10 + m_radius * 2;
107 InitTitleAndDataPosition(drawHeight);
108 int h = GetFullHeight(drawHeight);
109
110 if (orient == wxHORIZONTAL) {
111 return wxSize(DefaultWidth, wxMax(hint.y, h));
112 } else {
113 return wxSize(wxMax(hint.x, DefaultWidth), h);
114 }
115}
116
117void DashboardInstrument_Moon::SetData(DASH_CAP st, double value,
118 wxString format) {
119 if (st == OCPN_DBP_STC_LAT && !std::isnan(value)) {
120 m_hemisphere = (value < 0 ? "S" : "N");
121 }
122}
123
124void DashboardInstrument_Moon::Draw(wxGCDC *dc) {
125 if (m_phase == -1 || m_hemisphere == "") return;
126
127 wxSize sz = GetClientSize();
128 wxColour cl0, cl1, cl2;
129
130 dc->SetPen(*wxTRANSPARENT_PEN);
131 GetGlobalColor("DASHL", &cl0);
132 dc->SetBrush(cl0);
133 wxPoint points[3];
134 points[0].x = 5;
135 points[0].y = m_DataTop + m_radius * 2 + 6;
136 points[1].x = sz.x / 2;
137 points[1].y = m_DataTop + 10;
138 points[2].x = sz.x - 5;
139 points[2].y = m_DataTop + m_radius * 2 + 6;
140 dc->DrawPolygon(3, points, 0, 0);
141
142 int x = 2 + m_radius + (sz.x - m_radius - 2) / 8 * m_phase;
143 int y = m_DataTop + m_radius + 5;
144
145 /* Moon phases are seen upside-down on the southern hemisphere */
146 int startangle = (m_hemisphere == _("N") ? -90 : 90);
147
148 GetGlobalColor("DASH2", &cl0);
149 GetGlobalColor("DASH1", &cl1);
150 GetGlobalColor("DASHF", &cl2);
151
152 dc->SetBrush(cl0);
153 dc->DrawCircle(x, y, m_radius);
154 dc->SetBrush(cl1);
155
156 switch (m_phase) {
157 case 0:
158 dc->SetPen(cl2);
159 dc->SetBrush(*wxTRANSPARENT_BRUSH);
160 dc->DrawCircle(x, y, m_radius);
161 break;
162 case 1:
163 dc->DrawEllipticArc(x - m_radius, m_DataTop + 5, m_radius * 2,
164 m_radius * 2, startangle, startangle + 180);
165 dc->SetBrush(cl0);
166 dc->DrawEllipticArc(x - m_radius / 2, m_DataTop + 5, m_radius,
167 m_radius * 2, startangle, startangle + 180);
168 break;
169 case 2:
170 dc->SetBrush(cl1);
171 dc->DrawEllipticArc(x - m_radius, m_DataTop + 5, m_radius * 2,
172 m_radius * 2, startangle, startangle + 180);
173 break;
174 case 3:
175 // if( m_hemisphere == _("N") ) {
176 dc->DrawEllipticArc(x - m_radius / 2, m_DataTop + 5, m_radius,
177 m_radius * 2, -startangle, 180 - startangle);
178 dc->DrawEllipticArc(x - m_radius, m_DataTop + 5, m_radius * 2,
179 m_radius * 2, startangle, startangle + 180);
180 break;
181 case 4:
182 dc->DrawCircle(x, y, m_radius);
183 break;
184 case 5:
185 dc->DrawEllipticArc(x - m_radius, m_DataTop + 5, m_radius * 2,
186 m_radius * 2, -startangle, 180 - startangle);
187 dc->DrawEllipticArc(x - m_radius / 2, m_DataTop + 5, m_radius,
188 m_radius * 2, startangle, startangle + 180);
189 break;
190 case 6:
191 dc->DrawEllipticArc(x - m_radius, m_DataTop + 5, m_radius * 2,
192 m_radius * 2, -startangle, 180 - startangle);
193 break;
194 case 7:
195 dc->DrawEllipticArc(x - m_radius, m_DataTop + 5, m_radius * 2,
196 m_radius * 2, -startangle, 180 - startangle);
197 dc->SetBrush(cl0);
198 dc->DrawEllipticArc(x - m_radius / 2, m_DataTop + 5, m_radius,
199 m_radius * 2, -startangle, 180 - startangle);
200 break;
201 }
202 dc->SetPen(cl2);
203 dc->SetBrush(*wxTRANSPARENT_BRUSH);
204 dc->DrawCircle(x, y, m_radius);
205}
206
207void DashboardInstrument_Moon::SetUtcTime(wxDateTime data) {
208 if (data.IsValid()) {
209 m_phase = moon_phase(data.GetYear(), data.GetMonth() + 1, data.GetDay());
210 }
211}
212
213// Moon phase calculation
214int DashboardInstrument_Moon::moon_phase(int y, int m, int d) {
215 /*
216 calculates the moon phase (0-7), accurate to 1 segment.
217 0 => new moon.
218 4 => full moon.
219 */
220
221 int c, e;
222 double jd;
223 int b;
224
225 if (m < 3) {
226 y--;
227 m += 12;
228 }
229 ++m;
230 c = 365.25 * y;
231 e = 30.6 * m;
232 jd = c + e + d - 694039.09; /* jd is total days elapsed */
233 jd /= 29.53; /* divide by the moon cycle (29.53 days) */
234 b = jd; /* int(jd) -> b, take integer part of jd */
235 jd -= b; /* subtract integer part to leave fractional part of original jd */
236 b = jd * 8 + 0.5; /* scale fraction from 0-8 and round by adding 0.5 */
237 b = b & 7; /* 0 and 8 are the same so turn 8 into 0 */
238 return b;
239}
240
241#include <math.h>
242#include <time.h>
243
244#ifndef PI
245#define PI 3.1415926535897931160E0 /* pi */
246#endif
247#define DEGREE (PI / 180.0)
248#define RADIAN (180.0 / PI)
249#define TPI (2 * PI)
250
251// Zeniths for sunset/sunrise calculation
252#define ZENITH_OFFICIAL (90.0 + 50.0 / 60.0)
253#define ZENITH_CIVIL 96.0
254#define ZENITH_NAUTICAL 102.0
255#define ZENITH_ASTRONOMICAL 108.0
256
257// Convert decimal hours in hours and minutes
258wxDateTime convHrmn(double dhr) {
259 int hr, mn;
260 hr = (int)dhr;
261 mn = (dhr - (double)hr) * 60;
262 return wxDateTime(hr, mn);
263};
264
265DashboardInstrument_Sun::DashboardInstrument_Sun(
266 wxWindow *parent, wxWindowID id, wxString title,
267 InstrumentProperties *Properties, wxString format)
268 : DashboardInstrument_Clock(parent, id, title, Properties, OCPN_DBP_STC_LAT,
269 format) {
270 m_cap_flag.set(OCPN_DBP_STC_LON);
271 m_cap_flag.set(OCPN_DBP_STC_CLK);
272 m_lat = m_lon = 999.9;
273 m_dt = wxDateTime::Now().ToUTC();
274 m_sunrise = "---";
275 m_sunset = "---";
276}
277
278wxSize DashboardInstrument_Sun::GetSize(int orient, wxSize hint) {
279 InitTitleSize();
280 int w;
281 InitDataTextHeight("00:00:00 UTC", w);
282
283 int drawHeight =
284 m_DataTextHeight * 2 + m_DataTextHeight * g_TitleVerticalOffset;
285 InitTitleAndDataPosition(drawHeight);
286 int h = GetFullHeight(drawHeight);
287
288 if (orient == wxHORIZONTAL) {
289 return wxSize(wxMax(w + m_DataMargin, DefaultWidth), wxMax(hint.y, h));
290 } else {
291 return wxSize(wxMax(hint.x, wxMax(w + m_DataMargin, DefaultWidth)), h);
292 }
293}
294
295void DashboardInstrument_Sun::Draw(wxGCDC *dc) {
296 SetDataFont(dc);
297
298 int x1, x2;
299 x1 = x2 = m_DataMargin;
300
301 if (m_DataRightAlign) {
302 int w, h;
303 dc->GetTextExtent(m_sunrise, &w, &h, 0, 0);
304 x1 = GetClientSize().GetWidth() - w - m_DataMargin;
305 dc->GetTextExtent(m_sunset, &w, &h, 0, 0);
306 x2 = GetClientSize().GetWidth() - w - m_DataMargin;
307 }
308
309 dc->DrawText(m_sunrise, x1, m_DataTop);
310 dc->DrawText(m_sunset, x2, m_DataTop + m_DataTextHeight);
311}
312
313void DashboardInstrument_Sun::SetUtcTime(wxDateTime data) {
314 if (data.IsValid()) m_dt = data;
315
316 if ((m_lat != 999.9) && (m_lon != 999.9)) {
317 wxDateTime sunrise, sunset;
318 calculateSun(m_lat, m_lon, sunrise, sunset);
319 if (sunrise.GetYear() != 999)
320 m_sunrise = GetDisplayTime(sunrise);
321 else
322 m_sunrise = "---";
323 if (sunset.GetYear() != 999)
324 m_sunset = GetDisplayTime(sunset);
325 else
326 m_sunset = "---";
327 } else {
328 m_sunrise = "---";
329 m_sunset = "---";
330 }
331}
332
333void DashboardInstrument_Sun::SetData(DASH_CAP st, double data, wxString unit) {
334 if (!std::isnan(data)) {
335 if (st == OCPN_DBP_STC_LAT) {
336 m_lat = data;
337 } else if (st == OCPN_DBP_STC_LON) {
338 m_lon = data;
339 }
340 }
341}
342
343void DashboardInstrument_Sun::calculateSun(double latit, double longit,
344 wxDateTime &sunrise,
345 wxDateTime &sunset) {
346 /*
347 Source:
348 Almanac for Computers, 1990
349 published by Nautical Almanac Office
350 United States Naval Observatory
351 Washington, DC 20392
352
353 Inputs:
354 day, month, year: date of sunrise/sunset
355 latitude, longitude: location for sunrise/sunset
356 zenith: Sun's zenith for sunrise/sunset
357 offical = 90 degrees 50'
358 civil = 96 degrees
359 nautical = 102 degrees
360 astronomical = 108 degrees
361
362 NOTE: longitude is positive for East and negative for West
363 NOTE: the algorithm assumes the use of a calculator with the
364 trig functions in "degree" (rather than "radian") mode. Most
365 programming languages assume radian arguments, requiring back
366 and forth convertions. The factor is 180/pi. So, for instance,
367 the equation RA = atan(0.91764 * tan(L)) would be coded as RA
368 = (180/pi)*atan(0.91764 * tan((pi/180)*L)) to give a degree
369 answer with a degree input for L.
370
371 1. first calculate the day of the year
372
373 N1 = floor(275 * month / 9)
374 N2 = floor((month + 9) / 12)
375 N3 = (1 + floor((year - 4 * floor(year / 4) + 2) / 3))
376 N = N1 - (N2 * N3) + day - 30
377 */
378 int n = m_dt.GetDayOfYear();
379 /*
380 2. convert the longitude to hour value and calculate an approximate time
381
382 lngHour = longitude / 15
383
384 if rising time is desired:
385 t = N + ((6 - lngHour) / 24)
386 if setting time is desired:
387 t = N + ((18 - lngHour) / 24)
388 */
389 double lngHour = longit / 15;
390 double tris = n + ((6 - lngHour) / 24);
391 double tset = n + ((18 - lngHour) / 24);
392 /*
393
394 3. calculate the Sun's mean anomaly
395
396 M = (0.9856 * t) - 3.289
397 */
398 double mris = (0.9856 * tris) - 3.289;
399 double mset = (0.9856 * tset) - 3.289;
400 /*
401 4. calculate the Sun's true longitude
402
403 L = M + (1.916 * sin(M)) + (0.020 * sin(2 * M)) + 282.634
404 NOTE: L potentially needs to be adjusted into the range [0,360) by
405 adding/subtracting 360
406 */
407 double lris = mris + (1.916 * sin(DEGREE * mris)) +
408 (0.020 * sin(2 * DEGREE * mris)) + 282.634;
409 if (lris > 360) lris -= 360;
410 if (lris < 0) lris += 360;
411 double lset = mset + (1.916 * sin(DEGREE * mset)) +
412 (0.020 * sin(2 * DEGREE * mset)) + 282.634;
413 if (lset > 360) lset -= 360;
414 if (lset < 0) lset += 360;
415 /*
416 5a. calculate the Sun's right ascension
417
418 RA = atan(0.91764 * tan(L))
419 NOTE: RA potentially needs to be adjusted into the range [0,360) by
420 adding/subtracting 360
421 */
422 double raris = RADIAN * atan(0.91764 * tan(DEGREE * lris));
423 if (raris > 360) raris -= 360;
424 if (raris < 0) raris += 360;
425 double raset = RADIAN * atan(0.91764 * tan(DEGREE * lset));
426 if (raset > 360) raset -= 360;
427 if (raset < 0) raset += 360;
428 /*
429 5b. right ascension value needs to be in the same quadrant as L
430
431 Lquadrant = (floor( L/90)) * 90
432 RAquadrant = (floor(RA/90)) * 90
433 RA = RA + (Lquadrant - RAquadrant)
434 */
435 double lqris = (floor(lris / 90)) * 90;
436 double raqris = (floor(raris / 90)) * 90;
437 raris = raris + (lqris - raqris);
438 double lqset = (floor(lset / 90)) * 90;
439 double raqset = (floor(raset / 90)) * 90;
440 raset = raset + (lqset - raqset);
441 /*
442 5c. right ascension value needs to be converted into hours
443
444 RA = RA / 15
445 */
446 raris = raris / 15;
447 raset = raset / 15;
448 /*
449 6. calculate the Sun's declination
450
451 sinDec = 0.39782 * sin(L)
452 cosDec = cos(asin(sinDec))
453 */
454 double sinDecris = 0.39782 * sin(DEGREE * lris);
455 double cosDecris = cos(asin(sinDecris));
456 double sinDecset = 0.39782 * sin(DEGREE * lset);
457 double cosDecset = cos(asin(sinDecset));
458 /*
459 7a. calculate the Sun's local hour angle
460
461 cosH = (cos(zenith) - (sinDec * sin(latitude))) / (cosDec * cos(latitude))
462
463 if (cosH > 1)
464 the sun never rises on this location (on the specified date)
465 if (cosH < -1)
466 the sun never sets on this location (on the specified date)
467 */
468 double cosZenith = cos(DEGREE * ZENITH_OFFICIAL);
469 double coshris = (cosZenith - (sinDecris * sin(DEGREE * latit))) /
470 (cosDecris * cos(DEGREE * latit));
471 double coshset = (cosZenith - (sinDecset * sin(DEGREE * latit))) /
472 (cosDecset * cos(DEGREE * latit));
473 bool neverrises = false;
474 if (coshris > 1) neverrises = true;
475 if (coshris < -1)
476 neverrises = true; // nohal - it's cosine - even value lower than -1 is
477 // ilegal... correct me if i'm wrong
478 bool neversets = false;
479 if (coshset < -1) neversets = true;
480 if (coshset > 1)
481 neversets = true; // nohal - it's cosine - even value greater than 1 is
482 // ilegal... correct me if i'm wrong
483 /*
484 7b. finish calculating H and convert into hours
485
486 if if rising time is desired:
487 H = 360 - acos(cosH)
488 if setting time is desired:
489 H = acos(cosH)
490
491 H = H / 15
492 */
493 double hris = 360 - RADIAN * acos(coshris);
494 hris = hris / 15;
495 double hset = RADIAN * acos(coshset);
496 hset = hset / 15;
497 /*
498 8. calculate local mean time of rising/setting
499
500 T = H + RA - (0.06571 * t) - 6.622
501 */
502 tris = hris + raris - (0.06571 * tris) - 6.622;
503 tset = hset + raset - (0.06571 * tset) - 6.622;
504 /*
505 9. adjust back to UTC
506
507 UT = T - lngHour
508 NOTE: UT potentially needs to be adjusted into the range [0,24) by
509 adding/subtracting 24
510 */
511 double utris = tris - lngHour;
512 if (utris > 24) utris -= 24;
513 if (utris < 0) utris += 24;
514 double utset = tset - lngHour;
515 if (utset > 24) utset -= 24;
516 if (utset < 0) utset += 24;
517
518 sunrise = convHrmn(utris);
519 if (neverrises) sunrise.SetYear(999);
520 sunset = convHrmn(utset);
521 if (neversets) sunset.SetYear(999);
522 /*
523 Optional:
524 10. convert UT value to local time zone of latitude/longitude
525
526 localT = UT + localOffset
527 */
528}
A dashboard instrument that displays the GNSS clock time, if available.
Definition clock.h:48
Dashboard clock instrument.
DASH_CAP
Definition instrument.h:77
@ OCPN_DBP_STC_LON
Longitude.
Definition instrument.h:79
@ OCPN_DBP_STC_CLK
Clock.
Definition instrument.h:101
@ OCPN_DBP_STC_LAT
Latitude.
Definition instrument.h:78