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