OpenCPN Partial API docs
Loading...
Searching...
No Matches
dashboard_pi.cpp
1/***************************************************************************
2 * $Id: dashboard_pi.cpp, v1.0 2010/08/05 SethDart Exp $
3 *
4 * Project: OpenCPN
5 * Purpose: Dashboard Plugin
6 * Author: Jean-Eudes Onfray
7 * expanded: Bernd Cirotzki 2023 (special colour design)
8 *
9 ***************************************************************************
10 * Copyright (C) 2010 by David S. Register *
11 * *
12 * This program is free software; you can redistribute it and/or modify *
13 * it under the terms of the GNU General Public License as published by *
14 * the Free Software Foundation; either version 2 of the License, or *
15 * (at your option) any later version. *
16 * *
17 * This program is distributed in the hope that it will be useful, *
18 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
20 * GNU General Public License for more details. *
21 * *
22 * You should have received a copy of the GNU General Public License *
23 * along with this program; if not, write to the *
24 * Free Software Foundation, Inc., *
25 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
26 ***************************************************************************
27 */
28
29#include <wx/wxprec.h>
30
31#ifndef WX_PRECOMP
32#include <wx/wx.h>
33#endif // precompiled headers
34
35#include <assert.h>
36#include <cmath>
37// xw 2.8
38#include <wx/filename.h>
39#include <wx/fontdlg.h>
40
41#include <typeinfo>
42#include "dashboard_pi.h"
43#include "icons.h"
44#include "wx/jsonreader.h"
45#include "wx/jsonwriter.h"
46#include "N2KParser.h"
47#include "../../../gui/include/gui/ocpn_fontdlg.h"
48#include "manual.h"
49
50wxFontData *g_pFontTitle;
51wxFontData *g_pFontData;
52wxFontData *g_pFontLabel;
53wxFontData *g_pFontSmall;
54
55wxFontData g_FontTitle;
56wxFontData g_FontData;
57wxFontData g_FontLabel;
58wxFontData g_FontSmall;
59
60wxFontData *g_pUSFontTitle;
61wxFontData *g_pUSFontData;
62wxFontData *g_pUSFontLabel;
63wxFontData *g_pUSFontSmall;
64
65wxFontData g_USFontTitle;
66wxFontData g_USFontData;
67wxFontData g_USFontLabel;
68wxFontData g_USFontSmall;
69
70int g_iDashSpeedMax;
71int g_iDashCOGDamp;
72int g_iDashSpeedUnit;
73int g_iDashSOGDamp;
74int g_iDashAWADamp;
75int g_iDashAWSDamp;
76int g_iDashDepthUnit;
77int g_iDashDistanceUnit;
78int g_iDashWindSpeedUnit;
79int g_iUTCOffset;
80double g_dDashDBTOffset;
81bool g_bUseInternSumLog;
82double g_dSumLogNM;
83double d_tripNM;
84int logCount, confprint;
85bool g_bDBtrueWindGround;
86double g_dHDT;
87double g_dSOG, g_dCOG;
88int g_iDashTempUnit;
89int g_dashPrefWidth, g_dashPrefHeight;
90
91wxColor g_BackgroundColor;
92bool g_ForceBackgroundColor;
93wxAlignment g_TitleAlignment;
94double g_TitleVerticalOffset;
95int g_iTitleMargin;
96bool g_bShowUnit;
97wxAlignment g_DataAlignment;
98int g_iDataMargin;
99int g_iInstrumentSpacing;
100
101PI_ColorScheme aktuellColorScheme;
102#if !defined(NAN)
103static const long long lNaN = 0xfff8000000000000;
104#define NAN (*(double *)&lNaN)
105#endif
106
107#ifdef __OCPN__ANDROID__
108#include "qdebug.h"
109#endif
110
111// the class factories, used to create and destroy instances of the PlugIn
112
113extern "C" DECL_EXP opencpn_plugin *create_pi(void *ppimgr) {
114 return (opencpn_plugin *)new dashboard_pi(ppimgr);
115}
116
117extern "C" DECL_EXP void destroy_pi(opencpn_plugin *p) { delete p; }
118
119#ifdef __OCPN__ANDROID__
120
121QString qtStyleSheet =
122 "QScrollBar:horizontal {\
123border: 0px solid grey;\
124background-color: rgb(240, 240, 240);\
125height: 35px;\
126margin: 0px 1px 0 1px;\
127}\
128QScrollBar::handle:horizontal {\
129background-color: rgb(200, 200, 200);\
130min-width: 20px;\
131border-radius: 10px;\
132}\
133QScrollBar::add-line:horizontal {\
134border: 0px solid grey;\
135background: #32CC99;\
136width: 0px;\
137subcontrol-position: right;\
138subcontrol-origin: margin;\
139}\
140QScrollBar::sub-line:horizontal {\
141border: 0px solid grey;\
142background: #32CC99;\
143width: 0px;\
144subcontrol-position: left;\
145subcontrol-origin: margin;\
146}\
147QScrollBar:vertical {\
148border: 0px solid grey;\
149background-color: rgb(240, 240, 240);\
150width: 35px;\
151margin: 1px 0px 1px 0px;\
152}\
153QScrollBar::handle:vertical {\
154background-color: rgb(200, 200, 200);\
155min-height: 20px;\
156border-radius: 10px;\
157}\
158QScrollBar::add-line:vertical {\
159border: 0px solid grey;\
160background: #32CC99;\
161height: 0px;\
162subcontrol-position: top;\
163subcontrol-origin: margin;\
164}\
165QScrollBar::sub-line:vertical {\
166border: 0px solid grey;\
167background: #32CC99;\
168height: 0px;\
169subcontrol-position: bottom;\
170subcontrol-origin: margin;\
171}\
172QCheckBox {\
173spacing: 25px;\
174}\
175QCheckBox::indicator {\
176width: 30px;\
177height: 30px;\
178}\
179";
180
181#endif
182
183#ifdef __OCPN__ANDROID__
184#include <QtWidgets/QScroller>
185#endif
186
187//---------------------------------------------------------------------------------------------------------
188//
189// Dashboard PlugIn Implementation
190//
191//---------------------------------------------------------------------------------------------------------
192// !!! WARNING !!!
193// do not change the order, add new instruments at the end, before
194// ID_DBP_LAST_ENTRY! otherwise, for users with an existing opencpn.ini file,
195// their instruments are changing !
196enum {
197 ID_DBP_I_POS,
198 ID_DBP_I_SOG,
199 ID_DBP_D_SOG,
200 ID_DBP_I_COG,
201 ID_DBP_D_COG,
202 ID_DBP_I_STW,
203 ID_DBP_I_HDT,
204 ID_DBP_D_AW,
205 ID_DBP_D_AWA,
206 ID_DBP_I_AWS,
207 ID_DBP_D_AWS,
208 ID_DBP_D_TW,
209 ID_DBP_I_DPT,
210 ID_DBP_D_DPT,
211 ID_DBP_I_TMP,
212 ID_DBP_I_VMG,
213 ID_DBP_D_VMG,
214 ID_DBP_I_RSA,
215 ID_DBP_D_RSA,
216 ID_DBP_I_SAT,
217 ID_DBP_D_GPS,
218 ID_DBP_I_PTR,
219 ID_DBP_I_GPSUTC,
220 ID_DBP_I_SUN,
221 ID_DBP_D_MON,
222 ID_DBP_I_ATMP,
223 ID_DBP_I_AWA,
224 ID_DBP_I_TWA,
225 ID_DBP_I_TWD,
226 ID_DBP_I_TWS,
227 ID_DBP_D_TWD,
228 ID_DBP_I_HDM,
229 ID_DBP_D_HDT,
230 ID_DBP_D_WDH,
231 ID_DBP_I_VLW1,
232 ID_DBP_I_VLW2,
233 ID_DBP_D_MDA,
234 ID_DBP_I_MDA,
235 ID_DBP_D_BPH,
236 ID_DBP_I_FOS,
237 ID_DBP_M_COG,
238 ID_DBP_I_PITCH,
239 ID_DBP_I_HEEL,
240 ID_DBP_D_AWA_TWA,
241 ID_DBP_I_GPSLCL,
242 ID_DBP_I_CPULCL,
243 ID_DBP_I_SUNLCL,
244 ID_DBP_I_ALTI,
245 ID_DBP_D_ALTI,
246 ID_DBP_I_VMGW,
247 ID_DBP_I_HUM,
248 ID_DBP_D_STW,
249 ID_DBP_I_WCC,
250 ID_DBP_LAST_ENTRY // this has a reference in one of the routines; defining a
251 // "LAST_ENTRY" and setting the reference to it, is one
252 // codeline less to change (and find) when adding new
253 // instruments :-)
254};
255
256bool IsObsolete(int id) {
257 switch (id) {
258 case ID_DBP_D_AWA:
259 return true;
260 default:
261 return false;
262 }
263}
264
265wxString getInstrumentCaption(unsigned int id) {
266 switch (id) {
267 case ID_DBP_I_POS:
268 return _("Position");
269 case ID_DBP_I_SOG:
270 return _("SOG");
271 case ID_DBP_D_SOG:
272 return _("Speed SOG");
273 case ID_DBP_D_STW:
274 return _("Speed STW");
275 case ID_DBP_I_COG:
276 return _("COG");
277 case ID_DBP_M_COG:
278 return _("Mag COG");
279 case ID_DBP_D_COG:
280 return _("GNSS Compass");
281 case ID_DBP_D_HDT:
282 return _("True Compass");
283 case ID_DBP_I_STW:
284 return _("STW");
285 case ID_DBP_I_HDT:
286 return _("True HDG");
287 case ID_DBP_I_HDM:
288 return _("Mag HDG");
289 case ID_DBP_D_AW:
290 case ID_DBP_D_AWA:
291 return _("App. Wind Angle & Speed");
292 case ID_DBP_D_AWA_TWA:
293 return _("App & True Wind Angle");
294 case ID_DBP_I_AWS:
295 return _("App. Wind Speed");
296 case ID_DBP_D_AWS:
297 return _("App. Wind Speed");
298 case ID_DBP_D_TW:
299 return _("True Wind Angle & Speed");
300 case ID_DBP_I_ALTI:
301 return _("Altitude");
302 case ID_DBP_D_ALTI:
303 return _("Altitude Trace");
304 case ID_DBP_I_DPT:
305 return _("Depth");
306 case ID_DBP_D_DPT:
307 return _("Depth");
308 case ID_DBP_D_MDA:
309 return _("Barometric pressure");
310 case ID_DBP_I_MDA:
311 return _("Barometric pressure");
312 case ID_DBP_I_TMP:
313 return _("Water Temp.");
314 case ID_DBP_I_ATMP:
315 return _("Air Temp.");
316 case ID_DBP_I_AWA:
317 return _("App. Wind Angle");
318 case ID_DBP_I_TWA:
319 return _("True Wind Angle");
320 case ID_DBP_I_TWD:
321 return _("True Wind Direction");
322 case ID_DBP_I_TWS:
323 return _("True Wind Speed");
324 case ID_DBP_D_TWD:
325 return _("True Wind Dir. & Speed");
326 case ID_DBP_I_VMG:
327 return _("VMG");
328 case ID_DBP_D_VMG:
329 return _("VMG");
330 case ID_DBP_I_VMGW:
331 return _("VMG Wind");
332 case ID_DBP_I_RSA:
333 return _("Rudder Angle");
334 case ID_DBP_D_RSA:
335 return _("Rudder Angle");
336 case ID_DBP_I_SAT:
337 return _("GNSS in use");
338 case ID_DBP_D_GPS:
339 return _("GNSS Status");
340 case ID_DBP_I_PTR:
341 return _("Cursor");
342 case ID_DBP_I_GPSUTC:
343 return _("GNSS Clock");
344 case ID_DBP_I_SUN:
345 return _("Sunrise/Sunset");
346 case ID_DBP_D_MON:
347 return _("Moon phase");
348 case ID_DBP_D_WDH:
349 return _("Wind history");
350 case ID_DBP_D_BPH:
351 return _("Barometric history");
352 case ID_DBP_I_VLW1:
353 return _("Trip Log");
354 case ID_DBP_I_VLW2:
355 return _("Sum Log");
356 case ID_DBP_I_FOS:
357 return _("From Ownship");
358 case ID_DBP_I_PITCH:
359 return _("Pitch");
360 case ID_DBP_I_HEEL:
361 return _("Heel");
362 case ID_DBP_I_GPSLCL:
363 return _("Local GNSS Clock");
364 case ID_DBP_I_CPULCL:
365 return _("Local CPU Clock");
366 case ID_DBP_I_SUNLCL:
367 return _("Local Sunrise/Sunset");
368 case ID_DBP_I_HUM:
369 return _("Humidity");
370 case ID_DBP_I_WCC:
371 return _("Windlass");
372 }
373 return _T("");
374}
375
376void getListItemForInstrument(wxListItem &item, unsigned int id) {
377 item.SetData(id);
378 item.SetText(getInstrumentCaption(id));
379 switch (id) {
380 case ID_DBP_I_POS:
381 case ID_DBP_I_SOG:
382 case ID_DBP_I_COG:
383 case ID_DBP_M_COG:
384 case ID_DBP_I_STW:
385 case ID_DBP_I_HDT:
386 case ID_DBP_I_HDM:
387 case ID_DBP_I_AWS:
388 case ID_DBP_I_DPT:
389 case ID_DBP_I_MDA:
390 case ID_DBP_I_TMP:
391 case ID_DBP_I_ATMP:
392 case ID_DBP_I_TWA:
393 case ID_DBP_I_TWD:
394 case ID_DBP_I_TWS:
395 case ID_DBP_I_AWA:
396 case ID_DBP_I_VMG:
397 case ID_DBP_I_VMGW:
398 case ID_DBP_I_RSA:
399 case ID_DBP_I_SAT:
400 case ID_DBP_I_PTR:
401 case ID_DBP_I_GPSUTC:
402 case ID_DBP_I_GPSLCL:
403 case ID_DBP_I_CPULCL:
404 case ID_DBP_I_SUN:
405 case ID_DBP_I_SUNLCL:
406 case ID_DBP_I_VLW1:
407 case ID_DBP_I_VLW2:
408 case ID_DBP_I_FOS:
409 case ID_DBP_I_PITCH:
410 case ID_DBP_I_HEEL:
411 case ID_DBP_I_ALTI:
412 case ID_DBP_I_HUM:
413 case ID_DBP_I_WCC:
414 item.SetImage(0);
415 break;
416 case ID_DBP_D_SOG:
417 case ID_DBP_D_STW:
418 case ID_DBP_D_COG:
419 case ID_DBP_D_AW:
420 case ID_DBP_D_AWA:
421 case ID_DBP_D_AWS:
422 case ID_DBP_D_TW:
423 case ID_DBP_D_AWA_TWA:
424 case ID_DBP_D_TWD:
425 case ID_DBP_D_DPT:
426 case ID_DBP_D_MDA:
427 case ID_DBP_D_VMG:
428 case ID_DBP_D_RSA:
429 case ID_DBP_D_GPS:
430 case ID_DBP_D_HDT:
431 case ID_DBP_D_MON:
432 case ID_DBP_D_WDH:
433 case ID_DBP_D_BPH:
434 case ID_DBP_D_ALTI:
435 item.SetImage(1);
436 break;
437 }
438}
439
440/* These two function were taken from gpxdocument.cpp */
441int GetRandomNumber(int range_min, int range_max) {
442 long u = (long)wxRound(
443 ((double)rand() / ((double)(RAND_MAX) + 1) * (range_max - range_min)) +
444 range_min);
445 return (int)u;
446}
447
448// RFC4122 version 4 compliant random UUIDs generator.
449wxString GetUUID(void) {
450 wxString str;
451 struct {
452 int time_low;
453 int time_mid;
454 int time_hi_and_version;
455 int clock_seq_hi_and_rsv;
456 int clock_seq_low;
457 int node_hi;
458 int node_low;
459 } uuid;
460
461 uuid.time_low = GetRandomNumber(
462 0, 2147483647); // FIXME: the max should be set to something like
463 // MAXINT32, but it doesn't compile un gcc...
464 uuid.time_mid = GetRandomNumber(0, 65535);
465 uuid.time_hi_and_version = GetRandomNumber(0, 65535);
466 uuid.clock_seq_hi_and_rsv = GetRandomNumber(0, 255);
467 uuid.clock_seq_low = GetRandomNumber(0, 255);
468 uuid.node_hi = GetRandomNumber(0, 65535);
469 uuid.node_low = GetRandomNumber(0, 2147483647);
470
471 /* Set the two most significant bits (bits 6 and 7) of the
472 * clock_seq_hi_and_rsv to zero and one, respectively. */
473 uuid.clock_seq_hi_and_rsv = (uuid.clock_seq_hi_and_rsv & 0x3F) | 0x80;
474
475 /* Set the four most significant bits (bits 12 through 15) of the
476 * time_hi_and_version field to 4 */
477 uuid.time_hi_and_version = (uuid.time_hi_and_version & 0x0fff) | 0x4000;
478
479 str.Printf(_T("%08x-%04x-%04x-%02x%02x-%04x%08x"), uuid.time_low,
480 uuid.time_mid, uuid.time_hi_and_version, uuid.clock_seq_hi_and_rsv,
481 uuid.clock_seq_low, uuid.node_hi, uuid.node_low);
482
483 return str;
484}
485
486wxString MakeName() { return _T("DASH_") + GetUUID(); }
487
488//---------------------------------------------------------------------------------------------------------
489//
490// PlugIn initialization and de-init
491//
492//---------------------------------------------------------------------------------------------------------
493
494dashboard_pi::dashboard_pi(void *ppimgr)
495 : wxTimer(this), opencpn_plugin_18(ppimgr) {
496 // Create the PlugIn icons
497 initialize_images();
498 // Initialize the infinite impulse response (IIR) filters
499 mCOGFilter.setType(IIRFILTER_TYPE_DEG);
500 mAWAFilter.setType(IIRFILTER_TYPE_DEG);
501 mAWSFilter.setType(IIRFILTER_TYPE_LINEAR);
502}
503
504dashboard_pi::~dashboard_pi(void) {
505 delete _img_dashboard_pi;
506 delete _img_dashboard;
507 delete _img_dial;
508 delete _img_instrument;
509 delete _img_minus;
510 delete _img_plus;
511}
512
514 AddLocaleCatalog(_T("opencpn-dashboard_pi"));
515
516 mVar = NAN;
517 mPriPosition = 99;
518 mPriCOGSOG = 99;
519 mPriHeadingT = 99; // True heading
520 mPriHeadingM = 99; // Magnetic heading
521 mPriVar = 99;
522 mPriDateTime = 99;
523 mPriAWA = 99; // Relative wind
524 mPriTWA = 99; // True wind
525 mPriWDN = 99; // True hist. wind
526 mPriMDA = 99; // Air press
527 mPriDepth = 99;
528 mPriSTW = 99;
529 mPriWTP = 99; // Water temp
530 mPriATMP = 99; // Air temp
531 mPriSatStatus = 99;
532 mPriSatUsed = 99;
533 mSatsInView = 0;
534 mPriAlt = 99;
535 mPriRSA = 99; // Rudder angle
536 mPriPitchRoll = 99; // Pitch and roll
537 mPriHUM = 99; // Humidity
538 m_config_version = -1;
539 mHDx_Watchdog = 2;
540 mHDT_Watchdog = 2;
541 mSatsUsed_Wdog = 2;
542 mSatStatus_Wdog = 2;
543 m_PriN2kTalker = 2;
544 mVar_Watchdog = 2;
545 mMWVA_Watchdog = 2;
546 mMWVT_Watchdog = 2;
547 mDPT_DBT_Watchdog = 2; // Depth
548 mSTW_Watchdog = 2;
549 mWTP_Watchdog = 2;
550 mRSA_Watchdog = 2;
551 mVMG_Watchdog = 2;
552 mVMGW_Watchdog = 2;
553 mUTC_Watchdog = 2;
554 mATMP_Watchdog = 2;
555 mWDN_Watchdog = 2;
556 mMDA_Watchdog = 2;
557 mPITCH_Watchdog = 2;
558 mHEEL_Watchdog = 2;
559 mALT_Watchdog = 2;
560 mLOG_Watchdog = 2;
561 mTrLOG_Watchdog = 2;
562 mHUM_Watchdog = 2;
563 mWCC_Watchdog = 2;
564
565 g_pFontTitle = new wxFontData();
566 g_pFontTitle->SetChosenFont(
567 wxFont(10, wxFONTFAMILY_SWISS, wxFONTSTYLE_ITALIC, wxFONTWEIGHT_NORMAL));
568
569 g_pFontData = new wxFontData();
570 g_pFontData->SetChosenFont(
571 wxFont(14, wxFONTFAMILY_SWISS, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_NORMAL));
572
573 g_pFontLabel = new wxFontData();
574 g_pFontLabel->SetChosenFont(
575 wxFont(8, wxFONTFAMILY_SWISS, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_NORMAL));
576
577 g_pFontSmall = new wxFontData();
578 g_pFontSmall->SetChosenFont(
579 wxFont(8, wxFONTFAMILY_SWISS, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_NORMAL));
580
581 g_pUSFontTitle = &g_USFontTitle;
582 g_pUSFontData = &g_USFontData;
583 g_pUSFontLabel = &g_USFontLabel;
584 g_pUSFontSmall = &g_USFontSmall;
585
586 m_pauimgr = GetFrameAuiManager();
587 m_pauimgr->Connect(wxEVT_AUI_PANE_CLOSE,
588 wxAuiManagerEventHandler(dashboard_pi::OnPaneClose), NULL,
589 this);
590
591 // Get a pointer to the opencpn configuration object
592 m_pconfig = GetOCPNConfigObject();
593
594 // And load the configuration items
595 LoadConfig();
596
597 // This PlugIn needs a toolbar icon
598 // m_toolbar_item_id = InsertPlugInTool( _T(""), _img_dashboard,
599 // _img_dashboard, wxITEM_CHECK,
600 // _("Dashboard"), _T(""), NULL, DASHBOARD_TOOL_POSITION, 0, this
601 // );
602
603 wxString shareLocn = *GetpSharedDataLocation() + _T("plugins") +
604 wxFileName::GetPathSeparator() + _T("dashboard_pi") +
605 wxFileName::GetPathSeparator() + _T("data") +
606 wxFileName::GetPathSeparator();
607
608 wxString normalIcon = shareLocn + _T("Dashboard.svg");
609 wxString toggledIcon = shareLocn + _T("Dashboard_toggled.svg");
610 wxString rolloverIcon = shareLocn + _T("Dashboard_rollover.svg");
611
612 // For journeyman styles, we prefer the built-in raster icons which match the
613 // rest of the toolbar.
614 if (GetActiveStyleName().Lower() != _T("traditional")) {
615 normalIcon = _T("");
616 toggledIcon = _T("");
617 rolloverIcon = _T("");
618 }
619
620 m_toolbar_item_id = InsertPlugInToolSVG(
621 _T(""), normalIcon, rolloverIcon, toggledIcon, wxITEM_CHECK,
622 _("Dashboard"), _T(""), NULL, DASHBOARD_TOOL_POSITION, 0, this);
623
624 ApplyConfig();
625
626 // If we loaded a version 1 config setup, convert now to version 2
627 if (m_config_version == 1) {
628 SaveConfig();
629 }
630
631 d_tripNM = 0.0;
632 logCount = confprint = 0;
633 // If use internal sumlog update its value
634 if (g_bUseInternSumLog) UpdateSumLog(false);
635
636 // initialize NavMsg listeners
637 //-----------------------------
638
639 // Rudder data PGN 127245
640 wxDEFINE_EVENT(EVT_N2K_127245, ObservedEvt);
641 NMEA2000Id id_127245 = NMEA2000Id(127245);
642 listener_127245 = GetListener(id_127245, EVT_N2K_127245, this);
643 Bind(EVT_N2K_127245, [&](ObservedEvt ev) { HandleN2K_127245(ev); });
644
645 // Roll Pitch PGN 127257
646 wxDEFINE_EVENT(EVT_N2K_127257, ObservedEvt);
647 NMEA2000Id id_127257 = NMEA2000Id(127257);
648 listener_127257 = GetListener(id_127257, EVT_N2K_127257, this);
649 Bind(EVT_N2K_127257, [&](ObservedEvt ev) { HandleN2K_127257(ev); });
650
651 // Speed through water PGN 128259
652 wxDEFINE_EVENT(EVT_N2K_128259, ObservedEvt);
653 NMEA2000Id id_128259 = NMEA2000Id(128259);
654 listener_128259 = GetListener(id_128259, EVT_N2K_128259, this);
655 Bind(EVT_N2K_128259, [&](ObservedEvt ev) { HandleN2K_128259(ev); });
656
657 // Depth Data PGN 128267
658 wxDEFINE_EVENT(EVT_N2K_128267, ObservedEvt);
659 NMEA2000Id id_128267 = NMEA2000Id(128267);
660 listener_128267 = GetListener(id_128267, EVT_N2K_128267, this);
661 Bind(EVT_N2K_128267, [&](ObservedEvt ev) { HandleN2K_128267(ev); });
662
663 // Distance log
664 wxDEFINE_EVENT(EVT_N2K_128275, ObservedEvt);
665 NMEA2000Id id_128275 = NMEA2000Id(128275);
666 listener_128275 = GetListener(id_128275, EVT_N2K_128275, this);
667 Bind(EVT_N2K_128275, [&](ObservedEvt ev) { HandleN2K_128275(ev); });
668
669 // Windlass count
670 wxDEFINE_EVENT(EVT_N2K_128777, ObservedEvt);
671 NMEA2000Id id_128777 = NMEA2000Id(128777);
672 listener_128777 = GetListener(id_128777, EVT_N2K_128777, this);
673 Bind(EVT_N2K_128777, [&](ObservedEvt ev) { HandleN2K_128777(ev); });
674
675 // GNSS Position Data PGN 129029
676 wxDEFINE_EVENT(EVT_N2K_129029, ObservedEvt);
677 NMEA2000Id id_129029 = NMEA2000Id(129029);
678 listener_129029 = GetListener(id_129029, EVT_N2K_129029, this);
679 Bind(EVT_N2K_129029, [&](ObservedEvt ev) { HandleN2K_129029(ev); });
680
681 // GNSS Satellites in View PGN 129540
682 wxDEFINE_EVENT(EVT_N2K_129540, ObservedEvt);
683 NMEA2000Id id_129540 = NMEA2000Id(129540);
684 listener_129540 = GetListener(id_129540, EVT_N2K_129540, this);
685 Bind(EVT_N2K_129540, [&](ObservedEvt ev) { HandleN2K_129540(ev); });
686
687 // Wind PGN 130306
688 wxDEFINE_EVENT(EVT_N2K_130306, ObservedEvt);
689 NMEA2000Id id_130306 = NMEA2000Id(130306);
690 listener_130306 = GetListener(id_130306, EVT_N2K_130306, this);
691 Bind(EVT_N2K_130306, [&](ObservedEvt ev) { HandleN2K_130306(ev); });
692
693 // Envorinment PGN 130310
694 wxDEFINE_EVENT(EVT_N2K_130310, ObservedEvt);
695 NMEA2000Id id_130310 = NMEA2000Id(130310);
696 listener_130310 = GetListener(id_130310, EVT_N2K_130310, this);
697 Bind(EVT_N2K_130310, [&](ObservedEvt ev) { HandleN2K_130310(ev); });
698
699 // Envorinment PGN 130313
700 wxDEFINE_EVENT(EVT_N2K_130313, ObservedEvt);
701 NMEA2000Id id_130313 = NMEA2000Id(130313);
702 listener_130313 = GetListener(id_130313, EVT_N2K_130313, this);
703 Bind(EVT_N2K_130313, [&](ObservedEvt ev) { HandleN2K_130313(ev); });
704
705 Start(1000, wxTIMER_CONTINUOUS);
706
710}
711
713 SaveConfig();
714 if (IsRunning()) // Timer started?
715 Stop(); // Stop timer
716
717 for (size_t i = 0; i < m_ArrayOfDashboardWindow.GetCount(); i++) {
718 DashboardWindow *dashboard_window =
719 m_ArrayOfDashboardWindow.Item(i)->m_pDashboardWindow;
720 if (dashboard_window) {
721 m_pauimgr->DetachPane(dashboard_window);
722 dashboard_window->Close();
723 dashboard_window->Destroy();
724 m_ArrayOfDashboardWindow.Item(i)->m_pDashboardWindow = NULL;
725 }
726 }
727
728 for (size_t i = 0; i < m_ArrayOfDashboardWindow.GetCount(); i++) {
729 DashboardWindowContainer *pdwc = m_ArrayOfDashboardWindow.Item(i);
730 delete pdwc;
731 }
732
733 // delete g_pFontTitle;
734 // delete g_pFontData;
735 // delete g_pFontLabel;
736 // delete g_pFontSmall;
737
738 return true;
739}
740
741double GetJsonDouble(wxJSONValue &value) {
742 double d_ret = 0;
743 if (value.IsDouble()) {
744 d_ret = value.AsDouble();
745 return d_ret;
746 } else if (value.IsLong()) {
747 int i_ret = value.AsLong();
748 d_ret = i_ret;
749 return d_ret;
750 }
751 return nan("");
752}
753
754void dashboard_pi::Notify() {
755 SendUtcTimeToAllInstruments(mUTCDateTime);
756 for (size_t i = 0; i < m_ArrayOfDashboardWindow.GetCount(); i++) {
757 DashboardWindow *dashboard_window =
758 m_ArrayOfDashboardWindow.Item(i)->m_pDashboardWindow;
759 if (dashboard_window) {
760 dashboard_window->Refresh();
761#ifdef __OCPN__ANDROID__
762 wxWindowList list = dashboard_window->GetChildren();
763 wxWindowListNode *node = list.GetFirst();
764 for (size_t i = 0; i < list.GetCount(); i++) {
765 wxWindow *win = node->GetData();
766 // qDebug() << "Refresh Dash child:" << i;
767 win->Refresh();
768 node = node->GetNext();
769 }
770#endif
771 }
772 }
773 // Manage the watchdogs
774
775 mHDx_Watchdog--;
776 if (mHDx_Watchdog <= 0) {
777 mHdm = NAN;
778 mPriHeadingM = 99;
779 SendSentenceToAllInstruments(OCPN_DBP_STC_HDM, mHdm, _T("\u00B0"));
780 mHDx_Watchdog = gps_watchdog_timeout_ticks;
781 }
782
783 mHDT_Watchdog--;
784 if (mHDT_Watchdog <= 0) {
785 mPriHeadingT = 99;
786 SendSentenceToAllInstruments(OCPN_DBP_STC_HDT, NAN, _T("\u00B0T"));
787 mHDT_Watchdog = gps_watchdog_timeout_ticks;
788 }
789
790 mVar_Watchdog--;
791 if (mVar_Watchdog <= 0) {
792 mVar = NAN;
793 mPriVar = 99;
794 SendSentenceToAllInstruments(OCPN_DBP_STC_HMV, NAN, _T("\u00B0T"));
795 mVar_Watchdog = gps_watchdog_timeout_ticks;
796 }
797
798 mSatsUsed_Wdog--;
799 if (mSatsUsed_Wdog <= 0) {
800 mPriSatUsed = 99;
801 mSatsInUse = 0;
802 SendSentenceToAllInstruments(OCPN_DBP_STC_SAT, NAN, _T(""));
803 mSatsUsed_Wdog = gps_watchdog_timeout_ticks;
804 }
805 m_PriN2kTalker--;
806 if (m_PriN2kTalker < -1e6) m_PriN2kTalker = 0;
807
808 mSatStatus_Wdog--;
809 if (mSatStatus_Wdog <= 0) {
810 SAT_INFO sats[4];
811 for (int i = 0; i < 4; i++) {
812 sats[i].SatNumber = 0;
813 sats[i].SignalToNoiseRatio = 0;
814 }
815 SendSatInfoToAllInstruments(0, 1, wxEmptyString, sats);
816 SendSatInfoToAllInstruments(0, 2, wxEmptyString, sats);
817 SendSatInfoToAllInstruments(0, 3, wxEmptyString, sats);
818 mPriSatStatus = 99;
819 mSatStatus_Wdog = gps_watchdog_timeout_ticks;
820 }
821 // Set Satellite Status data from the same source as OCPN use for position
822 // Get the identifiers
823 std::vector<std::string> PriorityIDs = GetActivePriorityIdentifiers();
824 // Get current satellite priority identifier = item 4
825 // Exclude "address" after ':' that may equal Protokoll
826 std::string satID = PriorityIDs[4].substr(0, PriorityIDs[4].find(':'));
827 if (satID.find("nmea0183") != std::string::npos)
828 mPriSatStatus = 3; // GSV
829 else if (satID.find("ignal") != std::string::npos)
830 mPriSatStatus = 2; // SignalK
831 else if (satID.find("N2k") != std::string::npos) {
832 prioN2kPGNsat = PriorityIDs[4];
833 mPriSatStatus = 1; // N2k
834 }
835
836 mMWVA_Watchdog--;
837 if (mMWVA_Watchdog <= 0) {
838 SendSentenceToAllInstruments(OCPN_DBP_STC_AWA, NAN, _T("-"));
839 SendSentenceToAllInstruments(OCPN_DBP_STC_AWS, NAN, _T("-"));
840 mPriAWA = 99;
841 mMWVA_Watchdog = gps_watchdog_timeout_ticks;
842 }
843
844 mMWVT_Watchdog--;
845 if (mMWVT_Watchdog <= 0) {
846 SendSentenceToAllInstruments(OCPN_DBP_STC_TWA, NAN, _T("-"));
847 SendSentenceToAllInstruments(OCPN_DBP_STC_TWS, NAN, _T("-"));
848 SendSentenceToAllInstruments(OCPN_DBP_STC_TWS2, NAN, _T("-"));
849 mPriTWA = 99;
850 mMWVT_Watchdog = gps_watchdog_timeout_ticks;
851 }
852
853 mDPT_DBT_Watchdog--;
854 if (mDPT_DBT_Watchdog <= 0) {
855 mPriDepth = 99;
856 SendSentenceToAllInstruments(OCPN_DBP_STC_DPT, NAN, _T("-"));
857 mDPT_DBT_Watchdog = gps_watchdog_timeout_ticks;
858 }
859
860 mSTW_Watchdog--;
861 if (mSTW_Watchdog <= 0) {
862 mPriSTW = 99;
863 SendSentenceToAllInstruments(OCPN_DBP_STC_STW, NAN, _T("-"));
864 mSTW_Watchdog = gps_watchdog_timeout_ticks;
865 }
866
867 mWTP_Watchdog--;
868 if (mWTP_Watchdog <= 0) {
869 mPriWTP = 99;
870 SendSentenceToAllInstruments(OCPN_DBP_STC_TMP, NAN, "-");
871 mWTP_Watchdog = no_nav_watchdog_timeout_ticks;
872 }
873 mRSA_Watchdog--;
874 if (mRSA_Watchdog <= 0) {
875 mPriRSA = 99;
876 SendSentenceToAllInstruments(OCPN_DBP_STC_RSA, NAN, "-");
877 mRSA_Watchdog = gps_watchdog_timeout_ticks;
878 }
879 mVMG_Watchdog--;
880 if (mVMG_Watchdog <= 0) {
881 SendSentenceToAllInstruments(OCPN_DBP_STC_VMG, NAN, "-");
882 mVMG_Watchdog = gps_watchdog_timeout_ticks;
883 }
884 mVMGW_Watchdog--;
885 if (mVMGW_Watchdog <= 0) {
886 SendSentenceToAllInstruments(OCPN_DBP_STC_VMGW, NAN, "-");
887 mVMGW_Watchdog = gps_watchdog_timeout_ticks;
888 }
889 mUTC_Watchdog--;
890 if (mUTC_Watchdog <= 0) {
891 mPriDateTime = 99;
892 mUTC_Watchdog = gps_watchdog_timeout_ticks;
893 }
894 mATMP_Watchdog--;
895 if (mATMP_Watchdog <= 0) {
896 SendSentenceToAllInstruments(OCPN_DBP_STC_ATMP, NAN, "-");
897 mPriATMP = 99;
898 mATMP_Watchdog = gps_watchdog_timeout_ticks;
899 }
900 mWDN_Watchdog--;
901 if (mWDN_Watchdog <= 0) {
902 SendSentenceToAllInstruments(OCPN_DBP_STC_TWD, NAN, _T("-"));
903 mPriWDN = 99;
904 mWDN_Watchdog = no_nav_watchdog_timeout_ticks;
905 }
906 mMDA_Watchdog--;
907 if (mMDA_Watchdog <= 0) {
908 SendSentenceToAllInstruments(OCPN_DBP_STC_MDA, NAN, _T("-"));
909 mPriMDA = 99;
910 mMDA_Watchdog = gps_watchdog_timeout_ticks;
911 }
912 mPITCH_Watchdog--;
913 if (mPITCH_Watchdog <= 0) {
914 mPriPitchRoll = 99;
915 SendSentenceToAllInstruments(OCPN_DBP_STC_PITCH, NAN, _T("-"));
916 mPITCH_Watchdog = gps_watchdog_timeout_ticks;
917 }
918 mHEEL_Watchdog--;
919 if (mHEEL_Watchdog <= 0) {
920 mPriPitchRoll = 99;
921 SendSentenceToAllInstruments(OCPN_DBP_STC_HEEL, NAN, _T("-"));
922 mHEEL_Watchdog = gps_watchdog_timeout_ticks;
923 }
924 mALT_Watchdog--;
925 if (mALT_Watchdog <= 0) {
926 mPriAlt = 99;
927 SendSentenceToAllInstruments(OCPN_DBP_STC_ALTI, NAN, _T("-"));
928 mALT_Watchdog = gps_watchdog_timeout_ticks;
929 }
930
931 mLOG_Watchdog--;
932 if (mLOG_Watchdog <= 0) {
933 if (g_bUseInternSumLog) {
934 UpdateSumLog(false);
935 } else {
936 SendSentenceToAllInstruments(OCPN_DBP_STC_VLW2, NAN, _T("-"));
937 mLOG_Watchdog = no_nav_watchdog_timeout_ticks;
938 }
939 }
940 mTrLOG_Watchdog--;
941 if (mTrLOG_Watchdog <= 0) {
942 SendSentenceToAllInstruments(OCPN_DBP_STC_VLW1, NAN, _T("-"));
943 mTrLOG_Watchdog = no_nav_watchdog_timeout_ticks;
944 }
945 mHUM_Watchdog--;
946 if (mHUM_Watchdog <= 0) {
947 mPriHUM = 99;
948 SendSentenceToAllInstruments(OCPN_DBP_STC_HUM, NAN, _T("-"));
949 mHUM_Watchdog = no_nav_watchdog_timeout_ticks;
950 }
951 mWCC_Watchdog--;
952 if (mWCC_Watchdog <= 0) {
953 SendSentenceToAllInstruments(OCPN_DBP_STC_WCC, NAN, _T("-"));
954 mWCC_Watchdog = no_nav_watchdog_timeout_ticks;
955 }
956}
957
958int dashboard_pi::GetAPIVersionMajor() { return MY_API_VERSION_MAJOR; }
959
960int dashboard_pi::GetAPIVersionMinor() { return MY_API_VERSION_MINOR; }
961
962int dashboard_pi::GetPlugInVersionMajor() { return PLUGIN_VERSION_MAJOR; }
963
964int dashboard_pi::GetPlugInVersionMinor() { return PLUGIN_VERSION_MINOR; }
965
966wxBitmap *dashboard_pi::GetPlugInBitmap() { return _img_dashboard_pi; }
967
968wxString dashboard_pi::GetCommonName() { return _("Dashboard"); }
969
971 return _("Dashboard PlugIn for OpenCPN");
972}
973
975 return _(
976 "Dashboard PlugIn for OpenCPN\n\
977Provides navigation instrument display from NMEA source.");
978}
979
980void dashboard_pi::SendSentenceToAllInstruments(DASH_CAP st, double value,
981 wxString unit) {
982 for (size_t i = 0; i < m_ArrayOfDashboardWindow.GetCount(); i++) {
983 DashboardWindow *dashboard_window =
984 m_ArrayOfDashboardWindow.Item(i)->m_pDashboardWindow;
985 if (dashboard_window)
986 dashboard_window->SendSentenceToAllInstruments(st, value, unit);
987 }
988 if (st == OCPN_DBP_STC_HDT) {
989 g_dHDT = value;
990 }
991 if (st == OCPN_DBP_STC_SOG) {
992 g_dSOG = value;
993 }
994 if (st == OCPN_DBP_STC_COG) {
995 g_dCOG = value;
996 }
997}
998
999void dashboard_pi::SendUtcTimeToAllInstruments(wxDateTime value) {
1000 for (size_t i = 0; i < m_ArrayOfDashboardWindow.GetCount(); i++) {
1001 DashboardWindow *dashboard_window =
1002 m_ArrayOfDashboardWindow.Item(i)->m_pDashboardWindow;
1003 if (dashboard_window) dashboard_window->SendUtcTimeToAllInstruments(value);
1004 }
1005}
1006
1007void dashboard_pi::SendSatInfoToAllInstruments(int cnt, int seq, wxString talk,
1008 SAT_INFO sats[4]) {
1009 for (size_t i = 0; i < m_ArrayOfDashboardWindow.GetCount(); i++) {
1010 DashboardWindow *dashboard_window =
1011 m_ArrayOfDashboardWindow.Item(i)->m_pDashboardWindow;
1012 if (dashboard_window)
1013 dashboard_window->SendSatInfoToAllInstruments(cnt, seq, talk, sats);
1014 }
1015}
1016
1017// NMEA 0183 N0183.....
1018void dashboard_pi::SetNMEASentence(wxString &sentence) {
1019 m_NMEA0183 << sentence;
1020
1021 if (m_NMEA0183.PreParse()) {
1022 if (m_NMEA0183.LastSentenceIDReceived == _T("DBT")) {
1023 if (mPriDepth >= 5) {
1024 if (m_NMEA0183.Parse()) {
1025 /*
1026 double m_NMEA0183.Dbt.DepthFeet;
1027 double m_NMEA0183.Dbt.DepthMeters;
1028 double m_NMEA0183.Dbt.DepthFathoms;
1029 */
1030 double depth = NAN;
1031 if (!std::isnan(m_NMEA0183.Dbt.DepthMeters))
1032 depth = m_NMEA0183.Dbt.DepthMeters;
1033 else if (!std::isnan(m_NMEA0183.Dbt.DepthFeet))
1034 depth = m_NMEA0183.Dbt.DepthFeet * 0.3048;
1035 else if (!std::isnan(m_NMEA0183.Dbt.DepthFathoms))
1036 depth = m_NMEA0183.Dbt.DepthFathoms * 1.82880;
1037 if (!std::isnan(depth)) depth += g_dDashDBTOffset;
1038 if (!std::isnan(depth)) {
1039 SendSentenceToAllInstruments(
1040 OCPN_DBP_STC_DPT,
1041 toUsrDistance_Plugin(depth / 1852.0, g_iDashDepthUnit),
1042 getUsrDistanceUnit_Plugin(g_iDashDepthUnit));
1043 mPriDepth = 5;
1044 mDPT_DBT_Watchdog = gps_watchdog_timeout_ticks;
1045 }
1046 }
1047 }
1048 }
1049
1050 else if (m_NMEA0183.LastSentenceIDReceived == _T("DPT")) {
1051 if (mPriDepth >= 4) {
1052 if (m_NMEA0183.Parse()) {
1053 /*
1054 double m_NMEA0183.Dpt.DepthMeters
1055 double m_NMEA0183.Dpt.OffsetFromTransducerMeters
1056 */
1057 double depth = m_NMEA0183.Dpt.DepthMeters;
1058 if (!std::isnan(m_NMEA0183.Dpt.OffsetFromTransducerMeters)) {
1059 depth += m_NMEA0183.Dpt.OffsetFromTransducerMeters;
1060 }
1061 depth += g_dDashDBTOffset;
1062 if (!std::isnan(depth)) {
1063 SendSentenceToAllInstruments(
1064 OCPN_DBP_STC_DPT,
1065 toUsrDistance_Plugin(depth / 1852.0, g_iDashDepthUnit),
1066 getUsrDistanceUnit_Plugin(g_iDashDepthUnit));
1067 mPriDepth = 4;
1068 mDPT_DBT_Watchdog = gps_watchdog_timeout_ticks;
1069 }
1070 }
1071 }
1072 }
1073 // TODO: GBS - GPS Satellite fault detection
1074 else if (m_NMEA0183.LastSentenceIDReceived == _T("GGA")) {
1075 if (0) // debug output
1076 printf("GGA mPriPosition=%d mPriSatUsed=%d \tnSat=%d alt=%3.2f\n",
1077 mPriPosition, mPriSatUsed,
1078 m_NMEA0183.Gga.NumberOfSatellitesInUse,
1079 m_NMEA0183.Gga.AntennaAltitudeMeters);
1080 if (mPriAlt >= 3 && (mPriPosition >= 1 || mPriSatUsed >= 1)) {
1081 if (m_NMEA0183.Parse()) {
1082 if (m_NMEA0183.Gga.GPSQuality > 0 &&
1083 m_NMEA0183.Gga.NumberOfSatellitesInUse >= 5) {
1084 // Altimeter, takes altitude from gps GGA message, which is
1085 // typically less accurate than lon and lat.
1086 double alt = m_NMEA0183.Gga.AntennaAltitudeMeters;
1087 SendSentenceToAllInstruments(OCPN_DBP_STC_ALTI, alt, _T("m"));
1088 mPriAlt = 3;
1089 mALT_Watchdog = gps_watchdog_timeout_ticks;
1090 }
1091 }
1092 }
1093 if (mPriPosition >= 4 || mPriSatUsed >= 3) {
1094 if (m_NMEA0183.Parse()) {
1095 if (m_NMEA0183.Gga.GPSQuality > 0) {
1096 if (mPriPosition >= 4) {
1097 mPriPosition = 4;
1098 double lat, lon;
1099 float llt = m_NMEA0183.Gga.Position.Latitude.Latitude;
1100 int lat_deg_int = (int)(llt / 100);
1101 float lat_deg = lat_deg_int;
1102 float lat_min = llt - (lat_deg * 100);
1103 lat = lat_deg + (lat_min / 60.);
1104 if (m_NMEA0183.Gga.Position.Latitude.Northing == South)
1105 lat = -lat;
1106 SendSentenceToAllInstruments(OCPN_DBP_STC_LAT, lat, _T("SDMM"));
1107
1108 float lln = m_NMEA0183.Gga.Position.Longitude.Longitude;
1109 int lon_deg_int = (int)(lln / 100);
1110 float lon_deg = lon_deg_int;
1111 float lon_min = lln - (lon_deg * 100);
1112 lon = lon_deg + (lon_min / 60.);
1113 if (m_NMEA0183.Gga.Position.Longitude.Easting == West) lon = -lon;
1114 SendSentenceToAllInstruments(OCPN_DBP_STC_LON, lon, _T("SDMM"));
1115 }
1116 if (mPriSatUsed >= 3) {
1117 mSatsInUse = m_NMEA0183.Gga.NumberOfSatellitesInUse;
1118 SendSentenceToAllInstruments(OCPN_DBP_STC_SAT, mSatsInUse,
1119 _T (""));
1120 mPriSatUsed = 3;
1121 mSatsUsed_Wdog = gps_watchdog_timeout_ticks;
1122 }
1123
1124 // if( mPriDateTime >= 4 ) {
1125 // // Not in use, we need the date too.
1126 // //mPriDateTime = 4;
1127 // //mUTCDateTime.ParseFormat( m_NMEA0183.Gga.UTCTime.c_str(),
1128 // _T("%H%M%S") );
1129 //}
1130 }
1131 }
1132 }
1133 }
1134
1135 else if (m_NMEA0183.LastSentenceIDReceived == _T("GLL")) {
1136 if (mPriPosition >= 3) {
1137 if (m_NMEA0183.Parse()) {
1138 if (m_NMEA0183.Gll.IsDataValid == NTrue) {
1139 double lat, lon;
1140 float llt = m_NMEA0183.Gll.Position.Latitude.Latitude;
1141 int lat_deg_int = (int)(llt / 100);
1142 float lat_deg = lat_deg_int;
1143 float lat_min = llt - (lat_deg * 100);
1144 lat = lat_deg + (lat_min / 60.);
1145 if (m_NMEA0183.Gll.Position.Latitude.Northing == South) lat = -lat;
1146 SendSentenceToAllInstruments(OCPN_DBP_STC_LAT, lat, _T("SDMM"));
1147
1148 float lln = m_NMEA0183.Gll.Position.Longitude.Longitude;
1149 int lon_deg_int = (int)(lln / 100);
1150 float lon_deg = lon_deg_int;
1151 float lon_min = lln - (lon_deg * 100);
1152 lon = lon_deg + (lon_min / 60.);
1153 if (m_NMEA0183.Gll.Position.Longitude.Easting == West) lon = -lon;
1154 SendSentenceToAllInstruments(OCPN_DBP_STC_LON, lon, _T("SDMM"));
1155
1156 mPriPosition = 3;
1157 }
1158
1159 // if( mPriDateTime >= 5 ) {
1160 // // Not in use, we need the date too.
1161 // //mPriDateTime = 5;
1162 // //mUTCDateTime.ParseFormat( m_NMEA0183.Gll.UTCTime.c_str(),
1163 // _T("%H%M%S") );
1164 //}
1165 }
1166 }
1167 }
1168
1169 else if (m_NMEA0183.LastSentenceIDReceived == _T("GSV")) {
1170 if (mPriSatStatus >= 3 || mPriSatUsed >= 5) {
1171 if (m_NMEA0183.Parse()) {
1172 if (m_NMEA0183.Gsv.MessageNumber == 1) {
1173 // NMEA0183 recommend to not repeat SatsInView
1174 // in subsequent messages
1175 mSatsInView = m_NMEA0183.Gsv.SatsInView;
1176
1177 if (mPriSatUsed >= 5) {
1178 SendSentenceToAllInstruments(OCPN_DBP_STC_SAT,
1179 m_NMEA0183.Gsv.SatsInView, _T (""));
1180 mPriSatUsed = 5;
1181 mSatsUsed_Wdog = gps_watchdog_timeout_ticks;
1182 }
1183 }
1184
1185 if (mPriSatStatus >= 3) {
1186 SendSatInfoToAllInstruments(
1187 mSatsInView, m_NMEA0183.Gsv.MessageNumber, m_NMEA0183.TalkerID,
1188 m_NMEA0183.Gsv.SatInfo);
1189 mPriSatStatus = 3;
1190 mSatStatus_Wdog = gps_watchdog_timeout_ticks;
1191 }
1192 }
1193 }
1194 }
1195
1196 else if (m_NMEA0183.LastSentenceIDReceived == _T("HDG")) {
1197 if (mPriVar >= 3 || mPriHeadingM >= 3 || mPriHeadingT >= 7) {
1198 if (m_NMEA0183.Parse()) {
1199 if (mPriVar >= 3) {
1200 // Any device sending VAR=0.0 can be assumed to not really know
1201 // what the actual variation is, so in this case we use WMM if
1202 // available
1203 if ((!std::isnan(m_NMEA0183.Hdg.MagneticVariationDegrees)) &&
1204 0.0 != m_NMEA0183.Hdg.MagneticVariationDegrees) {
1205 mPriVar = 3;
1206 if (m_NMEA0183.Hdg.MagneticVariationDirection == East)
1207 mVar = m_NMEA0183.Hdg.MagneticVariationDegrees;
1208 else if (m_NMEA0183.Hdg.MagneticVariationDirection == West)
1209 mVar = -m_NMEA0183.Hdg.MagneticVariationDegrees;
1210 SendSentenceToAllInstruments(OCPN_DBP_STC_HMV, mVar,
1211 _T("\u00B0"));
1212 }
1213 }
1214 if (mPriHeadingM >= 3) {
1215 if (!std::isnan(m_NMEA0183.Hdg.MagneticSensorHeadingDegrees)) {
1216 mPriHeadingM = 3;
1217 mHdm = m_NMEA0183.Hdg.MagneticSensorHeadingDegrees;
1218 SendSentenceToAllInstruments(OCPN_DBP_STC_HDM, mHdm,
1219 _T("\u00B0"));
1220 }
1221 }
1222 if (!std::isnan(m_NMEA0183.Hdg.MagneticSensorHeadingDegrees))
1223 mHDx_Watchdog = gps_watchdog_timeout_ticks;
1224
1225 // If Variation is available, no higher priority HDT is
1226 // available, then calculate and propagate calculated HDT
1227 if (!std::isnan(m_NMEA0183.Hdg.MagneticSensorHeadingDegrees)) {
1228 if (!std::isnan(mVar) && (mPriHeadingT >= 7)) {
1229 mPriHeadingT = 7;
1230 double heading = mHdm + mVar;
1231 if (heading < 0)
1232 heading += 360;
1233 else if (heading >= 360.0)
1234 heading -= 360;
1235 SendSentenceToAllInstruments(OCPN_DBP_STC_HDT, heading,
1236 _T("\u00B0"));
1237 mHDT_Watchdog = gps_watchdog_timeout_ticks;
1238 }
1239 }
1240 }
1241 }
1242 }
1243
1244 else if (m_NMEA0183.LastSentenceIDReceived == _T("HDM")) {
1245 if (mPriHeadingM >= 4 || mPriHeadingT >= 5) {
1246 if (m_NMEA0183.Parse()) {
1247 if (mPriHeadingM >= 4) {
1248 if (!std::isnan(m_NMEA0183.Hdm.DegreesMagnetic)) {
1249 mPriHeadingM = 4;
1250 mHdm = m_NMEA0183.Hdm.DegreesMagnetic;
1251 SendSentenceToAllInstruments(OCPN_DBP_STC_HDM, mHdm,
1252 _T("\u00B0M"));
1253 mHDx_Watchdog = gps_watchdog_timeout_ticks;
1254 }
1255 }
1256
1257 // If Variation is available, no higher priority HDT is
1258 // available, then calculate and propagate calculated HDT
1259 if (!std::isnan(m_NMEA0183.Hdm.DegreesMagnetic)) {
1260 if (!std::isnan(mVar) && (mPriHeadingT >= 5)) {
1261 mPriHeadingT = 5;
1262 double heading = mHdm + mVar;
1263 if (heading < 0)
1264 heading += 360;
1265 else if (heading >= 360.0)
1266 heading -= 360;
1267 SendSentenceToAllInstruments(OCPN_DBP_STC_HDT, heading,
1268 _T("\u00B0"));
1269 mHDT_Watchdog = gps_watchdog_timeout_ticks;
1270 }
1271 }
1272 }
1273 }
1274 }
1275
1276 else if (m_NMEA0183.LastSentenceIDReceived == _T("HDT")) {
1277 if (mPriHeadingT >= 3) {
1278 if (m_NMEA0183.Parse()) {
1279 if (!std::isnan(m_NMEA0183.Hdt.DegreesTrue)) {
1280 SendSentenceToAllInstruments(
1281 OCPN_DBP_STC_HDT, m_NMEA0183.Hdt.DegreesTrue, _T("\u00B0T"));
1282 mPriHeadingT = 3;
1283 mHDT_Watchdog = gps_watchdog_timeout_ticks;
1284 }
1285 }
1286 }
1287 } else if (m_NMEA0183.LastSentenceIDReceived ==
1288 _T("MTA")) { // Air temperature
1289 if (mPriATMP >= 3) {
1290 if (m_NMEA0183.Parse()) {
1291 mPriATMP = 3;
1292 SendSentenceToAllInstruments(
1293 OCPN_DBP_STC_ATMP,
1294 toUsrTemp_Plugin(m_NMEA0183.Mta.Temperature, g_iDashTempUnit),
1295 getUsrTempUnit_Plugin(g_iDashTempUnit));
1296 mATMP_Watchdog = gps_watchdog_timeout_ticks;
1297 }
1298 }
1299 } else if (m_NMEA0183.LastSentenceIDReceived == _T("MDA") &&
1300 (mPriMDA >= 5 || mPriATMP >= 5 || mPriHUM >= 4)) {
1301 // Barometric pressure || HUmidity || Air temp
1302 if (m_NMEA0183.Parse()) {
1303 // TODO make posibilyti to select between Bar or InchHg
1304 /*
1305 double m_NMEA0183.Mda.Pressure;
1306 wxString m_NMEA0183.Mda.UnitOfMeasurement;
1307 */
1308 if (mPriMDA >= 5 && m_NMEA0183.Mda.Pressure > .8 &&
1309 m_NMEA0183.Mda.Pressure < 1.1) {
1310 SendSentenceToAllInstruments(
1311 OCPN_DBP_STC_MDA, m_NMEA0183.Mda.Pressure * 1000, _T("hPa"));
1312 mPriMDA = 5;
1313 mMDA_Watchdog = no_nav_watchdog_timeout_ticks;
1314 }
1315 if (mPriATMP >= 5) {
1316 double airtemp = m_NMEA0183.Mda.AirTemp;
1317 if (!std::isnan(airtemp) && airtemp < 999.0) {
1318 SendSentenceToAllInstruments(
1319 OCPN_DBP_STC_ATMP, toUsrTemp_Plugin(airtemp, g_iDashTempUnit),
1320 getUsrTempUnit_Plugin(g_iDashTempUnit));
1321 mATMP_Watchdog = no_nav_watchdog_timeout_ticks;
1322 mPriATMP = 5;
1323 }
1324 }
1325 if (mPriHUM >= 4) {
1326 double humidity = m_NMEA0183.Mda.Humidity;
1327 if (!std::isnan(humidity)) {
1328 SendSentenceToAllInstruments(OCPN_DBP_STC_HUM, humidity, "%");
1329 mHUM_Watchdog = no_nav_watchdog_timeout_ticks;
1330 mPriHUM = 4;
1331 }
1332 }
1333 }
1334
1335 } else if (m_NMEA0183.LastSentenceIDReceived == _T("MTW")) {
1336 if (mPriWTP >= 4) {
1337 if (m_NMEA0183.Parse()) {
1338 mPriWTP = 4;
1339 SendSentenceToAllInstruments(
1340 OCPN_DBP_STC_TMP,
1341 toUsrTemp_Plugin(m_NMEA0183.Mtw.Temperature, g_iDashTempUnit),
1342 getUsrTempUnit_Plugin(g_iDashTempUnit));
1343 mWTP_Watchdog = no_nav_watchdog_timeout_ticks;
1344 }
1345 }
1346
1347 } else if (m_NMEA0183.LastSentenceIDReceived == _T("VLW")) {
1348 if (m_NMEA0183.Parse()) {
1349 /*
1350 double m_NMEA0183.Vlw.TotalMileage;
1351 double m_NMEA0183.Vlw.TripMileage;
1352 */
1353 SendSentenceToAllInstruments(
1354 OCPN_DBP_STC_VLW1,
1355 toUsrDistance_Plugin(m_NMEA0183.Vlw.TripMileage,
1356 g_iDashDistanceUnit),
1357 getUsrDistanceUnit_Plugin(g_iDashDistanceUnit));
1358 mTrLOG_Watchdog = no_nav_watchdog_timeout_ticks;
1359
1360 SendSentenceToAllInstruments(
1361 OCPN_DBP_STC_VLW2,
1362 toUsrDistance_Plugin(m_NMEA0183.Vlw.TotalMileage,
1363 g_iDashDistanceUnit),
1364 getUsrDistanceUnit_Plugin(g_iDashDistanceUnit));
1365 mLOG_Watchdog = no_nav_watchdog_timeout_ticks;
1366 }
1367
1368 }
1369 // NMEA 0183 standard Wind Direction and Speed, with respect to north.
1370 else if (m_NMEA0183.LastSentenceIDReceived == _T("MWD")) {
1371 if (mPriWDN >= 6) {
1372 if (m_NMEA0183.Parse()) {
1373 // Option for True vs Magnetic
1374 wxString windunit;
1375 if (!std::isnan(m_NMEA0183.Mwd.WindAngleTrue)) {
1376 // if WindAngleTrue is available, use it ...
1377 SendSentenceToAllInstruments(
1378 OCPN_DBP_STC_TWD, m_NMEA0183.Mwd.WindAngleTrue, _T("\u00B0"));
1379 mPriWDN = 6;
1380 // MWD can be seldom updated by the sensor. Set prolonged watchdog
1381 mWDN_Watchdog = no_nav_watchdog_timeout_ticks;
1382 } else if (!std::isnan(m_NMEA0183.Mwd.WindAngleMagnetic)) {
1383 // Make it true and use if variation is available
1384 if (!std::isnan(mVar)) {
1385 double twd = m_NMEA0183.Mwd.WindAngleMagnetic;
1386 twd += mVar;
1387 if (twd > 360.) {
1388 twd -= 360;
1389 } else if (twd < 0.) {
1390 twd += 360;
1391 }
1392 SendSentenceToAllInstruments(OCPN_DBP_STC_TWD, twd, _T("\u00B0"));
1393 mPriWDN = 6;
1394 mWDN_Watchdog = no_nav_watchdog_timeout_ticks;
1395 }
1396 }
1397 SendSentenceToAllInstruments(
1398 OCPN_DBP_STC_TWS,
1399 toUsrSpeed_Plugin(m_NMEA0183.Mwd.WindSpeedKnots,
1400 g_iDashWindSpeedUnit),
1401 getUsrSpeedUnit_Plugin(g_iDashWindSpeedUnit));
1402 SendSentenceToAllInstruments(
1403 OCPN_DBP_STC_TWS2,
1404 toUsrSpeed_Plugin(m_NMEA0183.Mwd.WindSpeedKnots,
1405 g_iDashWindSpeedUnit),
1406 getUsrSpeedUnit_Plugin(g_iDashWindSpeedUnit));
1407 mMWVT_Watchdog = gps_watchdog_timeout_ticks;
1408 // m_NMEA0183.Mwd.WindSpeedms
1409 }
1410 }
1411 }
1412 // NMEA 0183 standard Wind Speed and Angle, in relation to the vessel's
1413 // bow/centerline.
1414 else if (m_NMEA0183.LastSentenceIDReceived == _T("MWV")) {
1415 if (mPriAWA >= 4 || mPriTWA >= 5 || mPriWDN >= 5) {
1416 if (m_NMEA0183.Parse()) {
1417 if (m_NMEA0183.Mwv.IsDataValid == NTrue) {
1418 // MWV windspeed has different units. Form it to knots to fit
1419 // "toUsrSpeed_Plugin()"
1420 double m_wSpeedFactor = 1.0; // knots ("N")
1421 if (m_NMEA0183.Mwv.WindSpeedUnits == _T("K"))
1422 m_wSpeedFactor = 0.53995; // km/h > knots
1423 if (m_NMEA0183.Mwv.WindSpeedUnits == _T("M"))
1424 m_wSpeedFactor = 1.94384; // m/s > knots
1425
1426 if (m_NMEA0183.Mwv.Reference ==
1427 _T("R")) // Relative (apparent wind)
1428 {
1429 if (mPriAWA >= 4) {
1430 mPriAWA = 4;
1431 wxString m_awaunit;
1432 double m_awaangle;
1433 if (m_NMEA0183.Mwv.WindAngle > 180) {
1434 m_awaunit = _T("\u00B0L");
1435 m_awaangle = 180.0 - (m_NMEA0183.Mwv.WindAngle - 180.0);
1436 } else {
1437 m_awaunit = _T("\u00B0R");
1438 m_awaangle = m_NMEA0183.Mwv.WindAngle;
1439 }
1440 SendSentenceToAllInstruments(
1441 OCPN_DBP_STC_AWA, mAWAFilter.filter(m_awaangle), m_awaunit);
1442 SendSentenceToAllInstruments(
1443 OCPN_DBP_STC_AWS,
1445 mAWSFilter.filter(m_NMEA0183.Mwv.WindSpeed) *
1446 m_wSpeedFactor,
1447 g_iDashWindSpeedUnit),
1448 getUsrSpeedUnit_Plugin(g_iDashWindSpeedUnit));
1449 mMWVA_Watchdog = gps_watchdog_timeout_ticks;
1450 }
1451
1452 // If we have true HDT, COG, and SOG
1453 // then using simple vector math, we can calculate true wind
1454 // direction and speed. If there is no higher priority source for
1455 // WDN, then do so here, and update the appropriate instruments.
1456 if (mPriWDN >= 8) {
1457 CalculateAndUpdateTWDS(
1458 m_NMEA0183.Mwv.WindSpeed * m_wSpeedFactor,
1459 m_NMEA0183.Mwv.WindAngle);
1460 mPriWDN = 8;
1461 mWDN_Watchdog = no_nav_watchdog_timeout_ticks;
1462 mMWVT_Watchdog = gps_watchdog_timeout_ticks;
1463 }
1464 } else if (m_NMEA0183.Mwv.Reference ==
1465 _T("T")) // Theoretical (aka True)
1466 {
1467 if (mPriTWA >= 5) {
1468 mPriTWA = 5;
1469 wxString m_twaunit;
1470 double m_twaangle;
1471 bool b_R = false;
1472 if (m_NMEA0183.Mwv.WindAngle > 180) {
1473 m_twaunit = _T("\u00B0L");
1474 m_twaangle = 180.0 - (m_NMEA0183.Mwv.WindAngle - 180.0);
1475 } else {
1476 m_twaunit = _T("\u00B0R");
1477 m_twaangle = m_NMEA0183.Mwv.WindAngle;
1478 b_R = true;
1479 }
1480 SendSentenceToAllInstruments(OCPN_DBP_STC_TWA, m_twaangle,
1481 m_twaunit);
1482
1483 if (mPriWDN >= 7) {
1484 // MWV has wind angle relative to the bow.
1485 // Wind history use angle relative to north.
1486 // If no TWD with higher priority is present
1487 // and true heading is available calculate it.
1488 if (g_dHDT < 361. && g_dHDT >= 0.0) {
1489 double g_dCalWdir = (m_NMEA0183.Mwv.WindAngle) + g_dHDT;
1490 if (g_dCalWdir > 360.) {
1491 g_dCalWdir -= 360;
1492 } else if (g_dCalWdir < 0.) {
1493 g_dCalWdir += 360;
1494 }
1495 SendSentenceToAllInstruments(OCPN_DBP_STC_TWD, g_dCalWdir,
1496 _T("\u00B0"));
1497 mPriWDN = 7;
1498 mWDN_Watchdog = no_nav_watchdog_timeout_ticks;
1499 }
1500 }
1501
1502 SendSentenceToAllInstruments(
1503 OCPN_DBP_STC_TWS,
1504 toUsrSpeed_Plugin(m_NMEA0183.Mwv.WindSpeed * m_wSpeedFactor,
1505 g_iDashWindSpeedUnit),
1506 getUsrSpeedUnit_Plugin(g_iDashWindSpeedUnit));
1507 SendSentenceToAllInstruments(
1508 OCPN_DBP_STC_TWS2,
1509 toUsrSpeed_Plugin(m_NMEA0183.Mwv.WindSpeed * m_wSpeedFactor,
1510 g_iDashWindSpeedUnit),
1511 getUsrSpeedUnit_Plugin(g_iDashWindSpeedUnit));
1512 mMWVT_Watchdog = gps_watchdog_timeout_ticks;
1513 }
1514 }
1515 }
1516 }
1517 }
1518 }
1519
1520 else if (m_NMEA0183.LastSentenceIDReceived == _T("RMC")) {
1521 if (mPriPosition >= 5 || mPriCOGSOG >= 3 || mPriVar >= 4 ||
1522 mPriDateTime >= 3) {
1523 if (m_NMEA0183.Parse()) {
1524 if (m_NMEA0183.Rmc.IsDataValid == NTrue) {
1525 if (mPriPosition >= 5) {
1526 mPriPosition = 5;
1527 double lat, lon;
1528 float llt = m_NMEA0183.Rmc.Position.Latitude.Latitude;
1529 int lat_deg_int = (int)(llt / 100);
1530 float lat_deg = lat_deg_int;
1531 float lat_min = llt - (lat_deg * 100);
1532 lat = lat_deg + (lat_min / 60.);
1533 if (m_NMEA0183.Rmc.Position.Latitude.Northing == South)
1534 lat = -lat;
1535 SendSentenceToAllInstruments(OCPN_DBP_STC_LAT, lat, _T("SDMM"));
1536
1537 float lln = m_NMEA0183.Rmc.Position.Longitude.Longitude;
1538 int lon_deg_int = (int)(lln / 100);
1539 float lon_deg = lon_deg_int;
1540 float lon_min = lln - (lon_deg * 100);
1541 lon = lon_deg + (lon_min / 60.);
1542 if (m_NMEA0183.Rmc.Position.Longitude.Easting == West) lon = -lon;
1543 SendSentenceToAllInstruments(OCPN_DBP_STC_LON, lon, _T("SDMM"));
1544 }
1545
1546 if (mPriCOGSOG >= 3) {
1547 mPriCOGSOG = 3;
1548 if (!std::isnan(m_NMEA0183.Rmc.SpeedOverGroundKnots)) {
1549 SendSentenceToAllInstruments(
1550 OCPN_DBP_STC_SOG,
1552 mSOGFilter.filter(m_NMEA0183.Rmc.SpeedOverGroundKnots),
1553 g_iDashSpeedUnit),
1554 getUsrSpeedUnit_Plugin(g_iDashSpeedUnit));
1555 }
1556
1557 if (!std::isnan(m_NMEA0183.Rmc.TrackMadeGoodDegreesTrue)) {
1558 SendSentenceToAllInstruments(
1559 OCPN_DBP_STC_COG,
1560 mCOGFilter.filter(m_NMEA0183.Rmc.TrackMadeGoodDegreesTrue),
1561 _T("\u00B0"));
1562 }
1563 if (!std::isnan(m_NMEA0183.Rmc.TrackMadeGoodDegreesTrue) &&
1564 !std::isnan(m_NMEA0183.Rmc.MagneticVariation)) {
1565 double dMagneticCOG;
1566 if (m_NMEA0183.Rmc.MagneticVariationDirection == East) {
1567 dMagneticCOG =
1568 mCOGFilter.get() - m_NMEA0183.Rmc.MagneticVariation;
1569 if (dMagneticCOG < 0.0) dMagneticCOG = 360.0 + dMagneticCOG;
1570 } else {
1571 dMagneticCOG =
1572 mCOGFilter.get() + m_NMEA0183.Rmc.MagneticVariation;
1573 if (dMagneticCOG > 360.0) dMagneticCOG = dMagneticCOG - 360.0;
1574 }
1575 SendSentenceToAllInstruments(OCPN_DBP_STC_MCOG, dMagneticCOG,
1576 _T("\u00B0M"));
1577 }
1578 }
1579
1580 if (mPriVar >= 4) {
1581 // Any device sending VAR=0.0 can be assumed to not really know
1582 // what the actual variation is, so in this case we use WMM if
1583 // available
1584 if ((!std::isnan(m_NMEA0183.Rmc.MagneticVariation)) &&
1585 0.0 != m_NMEA0183.Rmc.MagneticVariation) {
1586 mPriVar = 4;
1587 if (m_NMEA0183.Rmc.MagneticVariationDirection == East)
1588 mVar = m_NMEA0183.Rmc.MagneticVariation;
1589 else if (m_NMEA0183.Rmc.MagneticVariationDirection == West)
1590 mVar = -m_NMEA0183.Rmc.MagneticVariation;
1591 mVar_Watchdog = gps_watchdog_timeout_ticks;
1592
1593 SendSentenceToAllInstruments(OCPN_DBP_STC_HMV, mVar,
1594 _T("\u00B0"));
1595 }
1596 }
1597
1598 if (mPriDateTime >= 3) {
1599 mPriDateTime = 3;
1600 wxString dt = m_NMEA0183.Rmc.Date + m_NMEA0183.Rmc.UTCTime;
1601 mUTCDateTime.ParseFormat(dt.c_str(), _T("%d%m%y%H%M%S"));
1602 mUTC_Watchdog = gps_watchdog_timeout_ticks;
1603 }
1604 }
1605 }
1606 }
1607 }
1608
1609 else if (m_NMEA0183.LastSentenceIDReceived == _T("RSA")) {
1610 if (mPriRSA >= 3) {
1611 if (m_NMEA0183.Parse()) {
1612 if (m_NMEA0183.Rsa.IsStarboardDataValid == NTrue) {
1613 SendSentenceToAllInstruments(
1614 OCPN_DBP_STC_RSA, m_NMEA0183.Rsa.Starboard, _T("\u00B0"));
1615 } else if (m_NMEA0183.Rsa.IsPortDataValid == NTrue) {
1616 SendSentenceToAllInstruments(OCPN_DBP_STC_RSA, -m_NMEA0183.Rsa.Port,
1617 _T("\u00B0"));
1618 }
1619 mRSA_Watchdog = gps_watchdog_timeout_ticks;
1620 mPriRSA = 3;
1621 }
1622 }
1623 }
1624
1625 else if (m_NMEA0183.LastSentenceIDReceived == _T("VHW")) {
1626 if (mPriHeadingT >= 4 || mPriHeadingM >= 5 || mPriSTW >= 3) {
1627 if (m_NMEA0183.Parse()) {
1628 if (mPriHeadingT >= 4) {
1629 if (!std::isnan(m_NMEA0183.Vhw.DegreesTrue)) {
1630 mPriHeadingT = 4;
1631 SendSentenceToAllInstruments(
1632 OCPN_DBP_STC_HDT, m_NMEA0183.Vhw.DegreesTrue, _T("\u00B0T"));
1633 mHDT_Watchdog = gps_watchdog_timeout_ticks;
1634 }
1635 }
1636 if (mPriHeadingM >= 5) {
1637 if (!std::isnan(m_NMEA0183.Vhw.DegreesMagnetic)) {
1638 mPriHeadingM = 5;
1639 SendSentenceToAllInstruments(OCPN_DBP_STC_HDM,
1640 m_NMEA0183.Vhw.DegreesMagnetic,
1641 _T("\u00B0M"));
1642 mHDx_Watchdog = gps_watchdog_timeout_ticks;
1643 }
1644 }
1645 if (mPriSTW >= 3) {
1646 double stw_kn = NAN;
1647 if (!std::isnan(m_NMEA0183.Vhw.Knots))
1648 stw_kn = m_NMEA0183.Vhw.Knots;
1649 else if (!std::isnan(m_NMEA0183.Vhw.KilometersPerHour))
1650 stw_kn = m_NMEA0183.Vhw.KilometersPerHour * 0.53995;
1651
1652 if (!std::isnan(stw_kn)) {
1653 SendSentenceToAllInstruments(
1654 OCPN_DBP_STC_STW, toUsrSpeed_Plugin(stw_kn, g_iDashSpeedUnit),
1655 getUsrSpeedUnit_Plugin(g_iDashSpeedUnit));
1656 mSTW_Watchdog = gps_watchdog_timeout_ticks;
1657 mPriSTW = 3;
1658 }
1659 }
1660 }
1661 }
1662 }
1663
1664 else if (m_NMEA0183.LastSentenceIDReceived == _T("VTG")) {
1665 if (mPriCOGSOG >= 2) {
1666 if (m_NMEA0183.Parse()) {
1667 mPriCOGSOG = 2;
1668 // Special check for unintialized values, as opposed to zero values
1669 if (!std::isnan(m_NMEA0183.Vtg.SpeedKnots)) {
1670 SendSentenceToAllInstruments(
1671 OCPN_DBP_STC_SOG,
1672 toUsrSpeed_Plugin(mSOGFilter.filter(m_NMEA0183.Vtg.SpeedKnots),
1673 g_iDashSpeedUnit),
1674 getUsrSpeedUnit_Plugin(g_iDashSpeedUnit));
1675 }
1676 // Vtg.SpeedKilometersPerHour;
1677 if (!std::isnan(m_NMEA0183.Vtg.TrackDegreesTrue)) {
1678 SendSentenceToAllInstruments(
1679 OCPN_DBP_STC_COG,
1680 mCOGFilter.filter(m_NMEA0183.Vtg.TrackDegreesTrue),
1681 _T("\u00B0"));
1682 }
1683 }
1684
1685 /*
1686 m_NMEA0183.Vtg.TrackDegreesMagnetic;
1687 */
1688 }
1689 }
1690 /* NMEA 0183 Relative (Apparent) Wind Speed and Angle. Wind angle in
1691 * relation
1692 * to the vessel's heading, and wind speed measured relative to the moving
1693 * vessel. */
1694 else if (m_NMEA0183.LastSentenceIDReceived == _T("VWR")) {
1695 if (mPriAWA >= 3) {
1696 if (m_NMEA0183.Parse()) {
1697 if (m_NMEA0183.Vwr.WindDirectionMagnitude < 200) {
1698 mPriAWA = 3;
1699
1700 wxString awaunit;
1701 awaunit = m_NMEA0183.Vwr.DirectionOfWind == Left ? _T("\u00B0L")
1702 : _T("\u00B0R");
1703 SendSentenceToAllInstruments(OCPN_DBP_STC_AWA,
1704 m_NMEA0183.Vwr.WindDirectionMagnitude,
1705 awaunit);
1706 SendSentenceToAllInstruments(
1707 OCPN_DBP_STC_AWS,
1708 toUsrSpeed_Plugin(m_NMEA0183.Vwr.WindSpeedKnots,
1709 g_iDashWindSpeedUnit),
1710 getUsrSpeedUnit_Plugin(g_iDashWindSpeedUnit));
1711 mMWVA_Watchdog = gps_watchdog_timeout_ticks;
1712 /*
1713 double m_NMEA0183.Vwr.WindSpeedms;
1714 double m_NMEA0183.Vwr.WindSpeedKmh;
1715 */
1716 }
1717
1718 // If we have true HDT, COG, and SOG
1719 // then using simple vector math, we can calculate true wind direction
1720 // and speed. If there is no higher priority source for WDN, then do
1721 // so here, and update the appropriate instruments.
1722 if (mPriWDN >= 9) {
1723 double awa = m_NMEA0183.Vwr.WindDirectionMagnitude;
1724 if (m_NMEA0183.Vwr.DirectionOfWind == Left)
1725 awa = 360. - m_NMEA0183.Vwr.WindDirectionMagnitude;
1726 CalculateAndUpdateTWDS(m_NMEA0183.Vwr.WindSpeedKnots, awa);
1727 mPriWDN = 9;
1728 mMWVT_Watchdog = gps_watchdog_timeout_ticks;
1729 mWDN_Watchdog = no_nav_watchdog_timeout_ticks;
1730 }
1731 }
1732 }
1733 }
1734 /* NMEA 0183 True wind angle in relation to the vessel's heading, and true
1735 * wind speed referenced to the water. True wind is the vector sum of the
1736 * Relative (apparent) wind vector and the vessel's velocity vector relative
1737 * to the water along the heading line of the vessel. It represents the wind
1738 * at the vessel if it were
1739 * stationary relative to the water and heading in the same direction. */
1740 else if (m_NMEA0183.LastSentenceIDReceived == _T("VWT")) {
1741 if (mPriTWA >= 4) {
1742 if (m_NMEA0183.Parse()) {
1743 if (m_NMEA0183.Vwt.WindDirectionMagnitude < 200) {
1744 mPriTWA = 4;
1745 wxString vwtunit;
1746 vwtunit = m_NMEA0183.Vwt.DirectionOfWind == Left ? _T("\u00B0L")
1747 : _T("\u00B0R");
1748 SendSentenceToAllInstruments(OCPN_DBP_STC_TWA,
1749 m_NMEA0183.Vwt.WindDirectionMagnitude,
1750 vwtunit);
1751 SendSentenceToAllInstruments(
1752 OCPN_DBP_STC_TWS,
1753 toUsrSpeed_Plugin(m_NMEA0183.Vwt.WindSpeedKnots,
1754 g_iDashWindSpeedUnit),
1755 getUsrSpeedUnit_Plugin(g_iDashWindSpeedUnit));
1756 mMWVT_Watchdog = gps_watchdog_timeout_ticks;
1757 /*
1758 double m_NMEA0183.Vwt.WindSpeedms;
1759 double m_NMEA0183.Vwt.WindSpeedKmh;
1760 */
1761 }
1762 }
1763 }
1764 }
1765
1766 else if (m_NMEA0183.LastSentenceIDReceived ==
1767 _T("XDR")) { // Transducer measurement
1768 /* XDR Transducer types
1769 * AngularDisplacementTransducer = 'A',
1770 * TemperatureTransducer = 'C',
1771 * LinearDisplacementTransducer = 'D',
1772 * FrequencyTransducer = 'F',
1773 * HumidityTransducer = 'H',
1774 * ForceTransducer = 'N',
1775 * PressureTransducer = 'P',
1776 * FlowRateTransducer = 'R',
1777 * TachometerTransducer = 'T',
1778 * VolumeTransducer = 'V'
1779 */
1780
1781 if (m_NMEA0183.Parse()) {
1782 wxString xdrunit;
1783 double xdrdata;
1784 for (int i = 0; i < m_NMEA0183.Xdr.TransducerCnt; i++) {
1785 xdrdata = m_NMEA0183.Xdr.TransducerInfo[i].MeasurementData;
1786 // XDR Airtemp
1787 if (m_NMEA0183.Xdr.TransducerInfo[i].TransducerType == _T("C")) {
1788 if (m_NMEA0183.Xdr.TransducerInfo[i]
1789 .TransducerName.MakeUpper()
1790 .Contains(_T("AIR")) ||
1791 m_NMEA0183.Xdr.TransducerInfo[i].TransducerName == _T("Te") ||
1792 m_NMEA0183.Xdr.TransducerInfo[i].TransducerName ==
1793 _T("ENV_OUTAIR_T") ||
1794 m_NMEA0183.Xdr.TransducerInfo[i].TransducerName ==
1795 _T("ENV_OUTSIDE_T")) {
1796 if (mPriATMP >= 4) {
1797 mPriATMP = 4;
1798 SendSentenceToAllInstruments(
1799 OCPN_DBP_STC_ATMP,
1800 toUsrTemp_Plugin(xdrdata, g_iDashTempUnit),
1801 getUsrTempUnit_Plugin(g_iDashTempUnit));
1802 mATMP_Watchdog = no_nav_watchdog_timeout_ticks;
1803 continue;
1804 }
1805 } // Water temp
1806 if (m_NMEA0183.Xdr.TransducerInfo[i]
1807 .TransducerName.MakeUpper()
1808 .Contains("WATER") ||
1809 m_NMEA0183.Xdr.TransducerInfo[i].TransducerName == "WTHI") {
1810 if (mPriWTP >= 3) {
1811 mPriWTP = 3;
1812 SendSentenceToAllInstruments(
1813 OCPN_DBP_STC_TMP,
1815 m_NMEA0183.Xdr.TransducerInfo[i].MeasurementData,
1816 g_iDashTempUnit),
1817 getUsrTempUnit_Plugin(g_iDashTempUnit));
1818 mWTP_Watchdog = no_nav_watchdog_timeout_ticks;
1819 continue;
1820 }
1821 }
1822 }
1823 // XDR Pressure
1824 if (m_NMEA0183.Xdr.TransducerInfo[i].TransducerType == "P") {
1825 if (m_NMEA0183.Xdr.TransducerInfo[i]
1826 .TransducerName.MakeUpper()
1827 .Contains(_T("BARO")) &&
1828 mPriMDA >= 4) {
1829 if (m_NMEA0183.Xdr.TransducerInfo[i].UnitOfMeasurement == "B") {
1830 xdrdata *= 1000;
1831 SendSentenceToAllInstruments(OCPN_DBP_STC_MDA, xdrdata,
1832 _T("hPa"));
1833 mPriMDA = 4;
1834 mMDA_Watchdog = no_nav_watchdog_timeout_ticks;
1835 continue;
1836 }
1837 }
1838 }
1839 // XDR Pitch (=Nose up/down) or Heel (stb/port)
1840 if (m_NMEA0183.Xdr.TransducerInfo[i].TransducerType == _T("A")) {
1841 if (m_NMEA0183.Xdr.TransducerInfo[i].TransducerName.Contains(
1842 _T("PTCH")) ||
1843 m_NMEA0183.Xdr.TransducerInfo[i].TransducerName.Contains(
1844 _T("PITCH"))) {
1845 if (mPriPitchRoll >= 3) {
1846 if (m_NMEA0183.Xdr.TransducerInfo[i].MeasurementData > 0) {
1847 xdrunit = _T("\u00B0\u2191") + _("Up");
1848 } else if (m_NMEA0183.Xdr.TransducerInfo[i].MeasurementData <
1849 0) {
1850 xdrunit = _T("\u00B0\u2193") + _("Down");
1851 xdrdata *= -1;
1852 } else {
1853 xdrunit = _T("\u00B0");
1854 }
1855 SendSentenceToAllInstruments(OCPN_DBP_STC_PITCH, xdrdata,
1856 xdrunit);
1857 mPITCH_Watchdog = gps_watchdog_timeout_ticks;
1858 mPriPitchRoll = 3;
1859 continue;
1860 }
1861 }
1862 // XDR Heel
1863 if (m_NMEA0183.Xdr.TransducerInfo[i].TransducerName.Contains(
1864 "ROLL")) {
1865 if (mPriPitchRoll >= 3) {
1866 if (m_NMEA0183.Xdr.TransducerInfo[i].MeasurementData > 0) {
1867 xdrunit = _T("\u00B0\u003E") + _("Stbd");
1868 } else if (m_NMEA0183.Xdr.TransducerInfo[i].MeasurementData <
1869 0) {
1870 xdrunit = _T("\u00B0\u003C") + _("Port");
1871 xdrdata *= -1;
1872 } else {
1873 xdrunit = _T("\u00B0");
1874 }
1875 SendSentenceToAllInstruments(OCPN_DBP_STC_HEEL, xdrdata,
1876 xdrunit);
1877 mHEEL_Watchdog = gps_watchdog_timeout_ticks;
1878 mPriPitchRoll = 3;
1879 continue;
1880 }
1881 }
1882 // XDR Rudder Angle
1883 if (m_NMEA0183.Xdr.TransducerInfo[i]
1884 .TransducerName.MakeUpper()
1885 .Contains("RUDDER")) {
1886 if (mPriRSA > 4) {
1887 SendSentenceToAllInstruments(OCPN_DBP_STC_RSA, xdrdata,
1888 _T("\u00B0"));
1889 mRSA_Watchdog = gps_watchdog_timeout_ticks;
1890 mPriRSA = 4;
1891 continue;
1892 }
1893 }
1894 }
1895 // Depth sounding and Windlass rode count
1896 if ((m_NMEA0183.Xdr.TransducerInfo[i].TransducerType == "D")) {
1897 bool good_depth = false;
1898 if (m_NMEA0183.Xdr.TransducerInfo[i].TransducerName == "XDHI" &&
1899 mPriDepth >= 6) {
1900 good_depth = true;
1901 mPriDepth = 6;
1902 } else if (m_NMEA0183.Xdr.TransducerInfo[i].TransducerName ==
1903 "XDLO" &&
1904 mPriDepth >= 7) {
1905 good_depth = true;
1906 mPriDepth = 7;
1907 }
1908 // XDR Windlass chain counter
1909 else if (m_NMEA0183.Xdr.TransducerInfo[i].TransducerName ==
1910 _T("WINDLASS") ||
1911 m_NMEA0183.Xdr.TransducerInfo[i].TransducerName ==
1912 _T("WINDLASS#0")) {
1913 wxString unit =
1914 m_NMEA0183.Xdr.TransducerInfo[i].UnitOfMeasurement;
1915 if (unit == wxEmptyString) unit = "m";
1916 unit.MakeLower();
1917 SendSentenceToAllInstruments(OCPN_DBP_STC_WCC, xdrdata, unit);
1918 mWCC_Watchdog = no_nav_watchdog_timeout_ticks;
1919 }
1920
1921 if (good_depth) {
1922 wxString unit = m_NMEA0183.Xdr.TransducerInfo[i]
1923 .UnitOfMeasurement.MakeLower();
1924 if (unit == "m") {
1925 double depth = NAN;
1926 depth = xdrdata;
1927 if (!std::isnan(depth)) {
1928 depth += g_dDashDBTOffset;
1929 SendSentenceToAllInstruments(
1930 OCPN_DBP_STC_DPT,
1931 toUsrDistance_Plugin(depth / 1852.0, g_iDashDepthUnit),
1932 getUsrDistanceUnit_Plugin(g_iDashDepthUnit));
1933 mDPT_DBT_Watchdog = gps_watchdog_timeout_ticks;
1934 continue;
1935 }
1936 }
1937 }
1938 } // Humidity
1939 if (m_NMEA0183.Xdr.TransducerInfo[i].TransducerType == "H") {
1940 if (mPriHUM >= 3) {
1941 if (m_NMEA0183.Xdr.TransducerInfo[i].UnitOfMeasurement == "P") {
1942 SendSentenceToAllInstruments(OCPN_DBP_STC_HUM, xdrdata, "%");
1943 mPriHUM = 3;
1944 mHUM_Watchdog = no_nav_watchdog_timeout_ticks;
1945 }
1946 }
1947 }
1948 }
1949 }
1950 } else if (m_NMEA0183.LastSentenceIDReceived == _T("ZDA")) {
1951 if (mPriDateTime >= 2) {
1952 if (m_NMEA0183.Parse()) {
1953 mPriDateTime = 2;
1954 /*
1955 wxString m_NMEA0183.Zda.UTCTime;
1956 int m_NMEA0183.Zda.Day;
1957 int m_NMEA0183.Zda.Month;
1958 int m_NMEA0183.Zda.Year;
1959 int m_NMEA0183.Zda.LocalHourDeviation;
1960 int m_NMEA0183.Zda.LocalMinutesDeviation;
1961 */
1962 wxString dt;
1963 dt.Printf(_T("%4d%02d%02d"), m_NMEA0183.Zda.Year,
1964 m_NMEA0183.Zda.Month, m_NMEA0183.Zda.Day);
1965 dt.Append(m_NMEA0183.Zda.UTCTime);
1966 mUTCDateTime.ParseFormat(dt.c_str(), _T("%Y%m%d%H%M%S"));
1967 mUTC_Watchdog = gps_watchdog_timeout_ticks;
1968 }
1969 }
1970 }
1971 }
1972 // Process an AIVDO message
1973 else if (sentence.Mid(1, 5).IsSameAs(_T("AIVDO"))) {
1975 if (DecodeSingleVDOMessage(sentence, &gpd, &m_VDO_accumulator)) {
1976 if (!std::isnan(gpd.Lat))
1977 SendSentenceToAllInstruments(OCPN_DBP_STC_LAT, gpd.Lat, _T("SDMM"));
1978
1979 if (!std::isnan(gpd.Lon))
1980 SendSentenceToAllInstruments(OCPN_DBP_STC_LON, gpd.Lon, _T("SDMM"));
1981
1982 SendSentenceToAllInstruments(
1983 OCPN_DBP_STC_SOG,
1984 toUsrSpeed_Plugin(mSOGFilter.filter(gpd.Sog), g_iDashSpeedUnit),
1985 getUsrSpeedUnit_Plugin(g_iDashSpeedUnit));
1986 SendSentenceToAllInstruments(OCPN_DBP_STC_COG, mCOGFilter.filter(gpd.Cog),
1987 _T("\u00B0"));
1988 if (!std::isnan(gpd.Hdt)) {
1989 SendSentenceToAllInstruments(OCPN_DBP_STC_HDT, gpd.Hdt, _T("\u00B0T"));
1990 mHDT_Watchdog = gps_watchdog_timeout_ticks;
1991 }
1992 }
1993 }
1994}
1995
1996/* Calculate True Wind speed and direction from AWS and AWA
1997 * This algorithm requires HDT, SOG, and COG, which are maintained as
1998 * globals elsewhere. Also, update all instruments tagged with OCPN_DBP_STC_TWD,
1999 * OCPN_DBP_STC_TWS, and OCPN_DBP_STC_TWS2
2000 */
2001void dashboard_pi::CalculateAndUpdateTWDS(double awsKnots, double awaDegrees) {
2002 if (!std::isnan(g_dHDT)) {
2003 // Apparent wind velocity vector, relative to head-up
2004 double awsx = awsKnots * cos(awaDegrees * PI / 180.);
2005 double awsy = awsKnots * sin(awaDegrees * PI / 180.);
2006
2007 // Ownship velocity vector, relative to head-up
2008 double bsx = 0;
2009 double bsy = 0;
2010 if ((!std::isnan(g_dSOG)) && (!std::isnan(g_dCOG))) {
2011 bsx = g_dSOG * cos((g_dCOG - g_dHDT) * PI / 180.);
2012 bsy = g_dSOG * sin((g_dCOG - g_dHDT) * PI / 180.);
2013 ;
2014 }
2015
2016 // "True" wind is calculated by vector subtraction
2017 double twdx = awsx - bsx;
2018 double twdy = awsy - bsy;
2019
2020 // Calculate the speed (magnitude of the vector)
2021 double tws = pow(((twdx * twdx) + (twdy * twdy)), 0.5);
2022
2023 // calculate the True Wind Angle
2024 double twd = atan2(twdy, twdx) * 180. / PI;
2025 if (twd < 0)
2026 SendSentenceToAllInstruments(OCPN_DBP_STC_TWA, -twd, _T("\u00B0L"));
2027 else
2028 SendSentenceToAllInstruments(OCPN_DBP_STC_TWA, twd, _T("\u00B0R"));
2029
2030 // Calculate the True Wind Direction, by re-orienting to the ownship HDT
2031 double twdc = twd + g_dHDT;
2032
2033 // Normalize
2034 if (twdc < 0) twdc += 360.;
2035 if (twdc > 360.) twdc -= 360;
2036
2037 // Update the instruments
2038 // printf("CALC: %4.0f %4.0f\n", tws, twdc);
2039 SendSentenceToAllInstruments(OCPN_DBP_STC_TWD, twdc, _T("\u00B0"));
2040
2041 SendSentenceToAllInstruments(OCPN_DBP_STC_TWS,
2042 toUsrSpeed_Plugin(tws, g_iDashWindSpeedUnit),
2043 getUsrSpeedUnit_Plugin(g_iDashWindSpeedUnit));
2044 SendSentenceToAllInstruments(OCPN_DBP_STC_TWS2,
2045 toUsrSpeed_Plugin(tws, g_iDashWindSpeedUnit),
2046 getUsrSpeedUnit_Plugin(g_iDashWindSpeedUnit));
2047 }
2048}
2049
2050// NMEA2000, N2K
2051//...............
2052
2053// Rudder data PGN 127245
2054void dashboard_pi::HandleN2K_127245(ObservedEvt ev) {
2055 NMEA2000Id id_127245(127245);
2056 std::vector<uint8_t> v = GetN2000Payload(id_127245, ev);
2057
2058 // Get a uniqe ID to prioritize source(s)
2059 unsigned char source_id = v.at(7);
2060 char ss[4];
2061 sprintf(ss, "%d", source_id);
2062 std::string ident = std::string(ss);
2063 std::string source = GetN2000Source(id_127245, ev);
2064 source += ":" + ident;
2065
2066 if (mPriRSA >= 1) {
2067 if (mPriRSA == 1) {
2068 // We favor first received after last WD
2069 if (source != prio127245) return;
2070 } else {
2071 // First time use after WD time out.
2072 prio127245 = source;
2073 }
2074
2075 double RudderPosition, AngleOrder;
2076 unsigned char Instance;
2077 tN2kRudderDirectionOrder RudderDirectionOrder;
2078
2079 // Get rudder position
2080 if (ParseN2kPGN127245(v, RudderPosition, Instance, RudderDirectionOrder,
2081 AngleOrder)) {
2082 if (!N2kIsNA(RudderPosition)) {
2083 double m_rudangle = GEODESIC_RAD2DEG(RudderPosition);
2084 SendSentenceToAllInstruments(OCPN_DBP_STC_RSA, m_rudangle,
2085 _T("\u00B0"));
2086 mRSA_Watchdog = gps_watchdog_timeout_ticks;
2087 mPriRSA = 1;
2088 }
2089 }
2090 }
2091}
2092
2093// Roll Pitch data PGN 127257
2094void dashboard_pi::HandleN2K_127257(ObservedEvt ev) {
2095 NMEA2000Id id_127257(127257);
2096 std::vector<uint8_t> v = GetN2000Payload(id_127257, ev);
2097
2098 // Get a uniqe ID to prioritize source(s)
2099 unsigned char source_id = v.at(7);
2100 char ss[4];
2101 sprintf(ss, "%d", source_id);
2102 std::string ident = std::string(ss);
2103 std::string source = GetN2000Source(id_127257, ev);
2104 source += ":" + ident;
2105
2106 if (mPriPitchRoll >= 1) {
2107 if (mPriPitchRoll == 1) {
2108 // We favor first received after last WD
2109 if (source != prio127257) return;
2110 } else {
2111 // First time use after WD time out.
2112 prio127257 = source;
2113 }
2114
2115 unsigned char SID;
2116 double Yaw, Pitch, Roll;
2117
2118 // Get roll and pitch
2119 if (ParseN2kPGN127257(v, SID, Yaw, Pitch, Roll)) {
2120 if (!N2kIsNA(Pitch)) {
2121 double m_pitch = GEODESIC_RAD2DEG(Pitch);
2122 wxString p_unit = _T("\u00B0\u2191") + _("Up");
2123 if (m_pitch < 0) {
2124 p_unit = _T("\u00B0\u2193") + _("Down");
2125 m_pitch *= -1;
2126 }
2127 SendSentenceToAllInstruments(OCPN_DBP_STC_PITCH, m_pitch, p_unit);
2128 mPITCH_Watchdog = gps_watchdog_timeout_ticks;
2129 mPriPitchRoll = 1;
2130 }
2131 if (!N2kIsNA(Roll)) {
2132 double m_heel = GEODESIC_RAD2DEG(Roll);
2133 wxString h_unit = _T("\u00B0\u003E") + _("Stbd");
2134 if (m_heel < 0) {
2135 h_unit = _T("\u00B0\u003C") + _("Port");
2136 m_heel *= -1;
2137 }
2138 SendSentenceToAllInstruments(OCPN_DBP_STC_HEEL, m_heel, h_unit);
2139 mHEEL_Watchdog = gps_watchdog_timeout_ticks;
2140 mPriPitchRoll = 1;
2141 }
2142 }
2143 }
2144}
2145
2146void dashboard_pi::HandleN2K_128267(ObservedEvt ev) {
2147 NMEA2000Id id_128267(128267);
2148 std::vector<uint8_t> v = GetN2000Payload(id_128267, ev);
2149
2150 // Get a uniqe ID to prioritize source(s)
2151 unsigned char source_id = v.at(7);
2152 char ss[4];
2153 sprintf(ss, "%d", source_id);
2154 std::string ident = std::string(ss);
2155 std::string source = GetN2000Source(id_128267, ev);
2156 source += ":" + ident;
2157
2158 if (mPriDepth >= 1) {
2159 if (mPriDepth == 1) {
2160 if (source != prio128267) return;
2161 } else {
2162 prio128267 = source;
2163 }
2164
2165 unsigned char SID;
2166 double DepthBelowTransducer, Offset, Range;
2167
2168 // Get water depth
2169 if (ParseN2kPGN128267(v, SID, DepthBelowTransducer, Offset, Range)) {
2170 if (!N2kIsNA(DepthBelowTransducer)) {
2171 double depth = DepthBelowTransducer;
2172 // Set prio to sensor's offset
2173 if (!std::isnan(Offset) && !N2kIsNA(Offset))
2174 depth += Offset;
2175 else
2176 (depth += g_dDashDBTOffset);
2177
2178 SendSentenceToAllInstruments(
2179 OCPN_DBP_STC_DPT,
2180 toUsrDistance_Plugin(depth / 1852.0, g_iDashDepthUnit),
2181 getUsrDistanceUnit_Plugin(g_iDashDepthUnit));
2182 mPriDepth = 1;
2183 mDPT_DBT_Watchdog = gps_watchdog_timeout_ticks;
2184 }
2185 }
2186 }
2187}
2188
2189void dashboard_pi::HandleN2K_128275(ObservedEvt ev) {
2190 NMEA2000Id id_128275(128275);
2191 std::vector<uint8_t> v = GetN2000Payload(id_128275, ev);
2192 uint16_t DaysSince1970;
2193 double SecondsSinceMidnight;
2194 uint32_t Log, TripLog;
2195
2196 // Get log & Trip log
2197 if (ParseN2kPGN128275(v, DaysSince1970, SecondsSinceMidnight, Log, TripLog)) {
2198 if (!N2kIsNA(Log)) {
2199 double m_slog = METERS2NM((double)Log);
2200 SendSentenceToAllInstruments(
2201 OCPN_DBP_STC_VLW2, toUsrDistance_Plugin(m_slog, g_iDashDistanceUnit),
2202 getUsrDistanceUnit_Plugin(g_iDashDistanceUnit));
2203 mLOG_Watchdog = no_nav_watchdog_timeout_ticks;
2204 }
2205 }
2206 if (!N2kIsNA(TripLog)) {
2207 double m_tlog = METERS2NM((double)TripLog);
2208 SendSentenceToAllInstruments(
2209 OCPN_DBP_STC_VLW1, toUsrDistance_Plugin(m_tlog, g_iDashDistanceUnit),
2210 getUsrDistanceUnit_Plugin(g_iDashDistanceUnit));
2211 mTrLOG_Watchdog = no_nav_watchdog_timeout_ticks;
2212 }
2213}
2214
2215void dashboard_pi::HandleN2K_128259(ObservedEvt ev) {
2216 NMEA2000Id id_128259(128259);
2217 std::vector<uint8_t> v = GetN2000Payload(id_128259, ev);
2218
2219 // Get a uniqe ID to prioritize source(s)
2220 unsigned char source_id = v.at(7);
2221 char ss[4];
2222 sprintf(ss, "%d", source_id);
2223 std::string ident = std::string(ss);
2224 std::string source = GetN2000Source(id_128259, ev);
2225 source += ":" + ident;
2226
2227 if (mPriSTW >= 1) {
2228 if (mPriSTW == 1) {
2229 if (source != prio128259) return;
2230 } else {
2231 prio128259 = source;
2232 }
2233
2234 unsigned char SID;
2235 double WaterReferenced, GroundReferenced;
2236 tN2kSpeedWaterReferenceType SWRT;
2237
2238 // Get speed through water
2239 if (ParseN2kPGN128259(v, SID, WaterReferenced, GroundReferenced, SWRT)) {
2240 if (!N2kIsNA(WaterReferenced)) {
2241 double stw_knots = MS2KNOTS(WaterReferenced);
2242 SendSentenceToAllInstruments(
2243 OCPN_DBP_STC_STW, toUsrSpeed_Plugin(stw_knots, g_iDashSpeedUnit),
2244 getUsrSpeedUnit_Plugin(g_iDashSpeedUnit));
2245 mPriSTW = 1;
2246 mSTW_Watchdog = gps_watchdog_timeout_ticks;
2247 }
2248 }
2249 }
2250}
2251
2252void dashboard_pi::HandleN2K_128777(ObservedEvt ev) {
2253 NMEA2000Id id_128777(128777);
2254 std::vector<uint8_t> v = GetN2000Payload(id_128777, ev);
2255 // Get Windlass rode count
2256 unsigned char SID;
2257 unsigned char WindlassIdentifier;
2258 double RodeCounterValue;
2259 double WindlassLineSpeed;
2260 tN2kWindlassMotionStates WindlassMotionStatus;
2261 tN2kRodeTypeStates RodeTypeStatus;
2262 tN2kAnchorDockingStates AnchorDockingStatus;
2263 tN2kWindlassOperatingEvents WindlassOperatingEvents;
2264
2265 if (ParseN2kPGN128777(v, SID, WindlassIdentifier, RodeCounterValue,
2266 WindlassLineSpeed, WindlassMotionStatus, RodeTypeStatus,
2267 AnchorDockingStatus, WindlassOperatingEvents)) {
2268 if (!N2kIsNA(RodeCounterValue)) {
2269 SendSentenceToAllInstruments(OCPN_DBP_STC_WCC, RodeCounterValue, "m");
2270 mWCC_Watchdog = no_nav_watchdog_timeout_ticks;
2271 }
2272 }
2273}
2274
2275wxString talker_N2k = wxEmptyString;
2276void dashboard_pi::HandleN2K_129029(ObservedEvt ev) {
2277 NMEA2000Id id_129029(129029);
2278 std::vector<uint8_t> v = GetN2000Payload(id_129029, ev);
2279 // Get a uniqe ID, e.g, "127", to prioritize source(s)
2280 unsigned char source_id = v.at(7);
2281 char ss[4];
2282 sprintf(ss, "%d", source_id);
2283 std::string ident = std::string(ss);
2284 // Use the source ID prioritized by OCPN only
2285 std::string prio_ID(prioN2kPGNsat.substr(prioN2kPGNsat.find(':') + 1, 5));
2286 if (prio_ID.find(ident) == std::string::npos) return;
2287
2288 unsigned char SID;
2289 uint16_t DaysSince1970;
2290 double SecondsSinceMidnight;
2291 double Latitude, Longitude, Altitude;
2292 tN2kGNSStype GNSStype;
2293 tN2kGNSSmethod GNSSmethod;
2294 unsigned char nSatellites;
2295 double HDOP, PDOP, GeoidalSeparation;
2296 unsigned char nReferenceStations;
2297 tN2kGNSStype ReferenceStationType;
2298 uint16_t ReferenceSationID;
2299 double AgeOfCorrection;
2300
2301 // Get used satellite system
2302 if (ParseN2kPGN129029(v, SID, DaysSince1970, SecondsSinceMidnight, Latitude,
2303 Longitude, Altitude, GNSStype, GNSSmethod, nSatellites,
2304 HDOP, PDOP, GeoidalSeparation, nReferenceStations,
2305 ReferenceStationType, ReferenceSationID,
2306 AgeOfCorrection)) {
2307 switch (GNSStype) {
2308 case 0:
2309 talker_N2k = "GP";
2310 break; // GPS
2311 case 1:
2312 talker_N2k = "GL";
2313 break; // GLONASS
2314 case 2:
2315 talker_N2k = "GPSGLONAS";
2316 break;
2317 case 3:
2318 talker_N2k = "GP";
2319 break;
2320 case 4:
2321 talker_N2k = "GPSGLONAS";
2322 break;
2323 case 5:
2324 talker_N2k = "Chayka";
2325 break;
2326 case 8:
2327 talker_N2k = "GA";
2328 break; // Galileo
2329 default:
2330 talker_N2k = wxEmptyString;
2331 }
2332 if (!N2kIsNA(Altitude)) {
2333 if (mPriAlt >= 1) {
2334 SendSentenceToAllInstruments(OCPN_DBP_STC_ALTI, Altitude, _T("m"));
2335 mPriAlt = 1;
2336 mALT_Watchdog = gps_watchdog_timeout_ticks;
2337 }
2338 }
2339 }
2340}
2341
2342void dashboard_pi::HandleN2K_129540(ObservedEvt ev) {
2343 NMEA2000Id id_129540(129540);
2344 std::vector<uint8_t> v = GetN2000Payload(id_129540, ev);
2345
2346 // Get a uniqe ID, e.g, "127", to prioritize source(s)
2347 unsigned char source_id = v.at(7);
2348 char ss[4];
2349 sprintf(ss, "%d", source_id);
2350 std::string ident = std::string(ss);
2351 // Use the source ID prioritized by OCPN only
2352 std::string prio_ID(prioN2kPGNsat.substr(prioN2kPGNsat.find(':') + 1, 5));
2353 if (prio_ID.find(ident) == std::string::npos) return;
2354
2355 unsigned char SID;
2356 tN2kRangeResidualMode Mode;
2357 uint8_t NumberOfSVs;
2358
2359 // Get the GNSS status data
2360 if (ParseN2kPGN129540(v, SID, Mode, NumberOfSVs)) {
2361 if (!N2kIsNA(NumberOfSVs) && mPriSatStatus == 1) {
2362 // Step through each satellite, one-by-one
2363 // Arrange to max three messages with up to 4 sats each like N0183 GSV
2364 SAT_INFO N2K_SatInfo[4];
2365 int iPRN = 0;
2366 int iSNR = 0;
2367 double dElevRad = 0;
2368 double dAzimRad = 0;
2369 int idx = 0;
2370 uint8_t index = 0;
2371 for (int iMesNum = 0; iMesNum < 3; iMesNum++) {
2372 for (idx = 0; idx < 4; idx++) {
2373 tSatelliteInfo SatelliteInfo;
2374 index = idx + 4 * iMesNum;
2375 if (index >= NumberOfSVs - 1) break;
2376 if (ParseN2kPGN129540(v, index, SatelliteInfo)) {
2377 iPRN = (int)SatelliteInfo.PRN;
2378 dElevRad = SatelliteInfo.Elevation;
2379 dAzimRad = SatelliteInfo.Azimuth;
2380 iSNR = N2kIsNA(SatelliteInfo.SNR) ? 0 : (int)SatelliteInfo.SNR;
2381
2382 N2K_SatInfo[idx].SatNumber = iPRN;
2383 N2K_SatInfo[idx].ElevationDegrees = GEODESIC_RAD2DEG(dElevRad);
2384 N2K_SatInfo[idx].AzimuthDegreesTrue = GEODESIC_RAD2DEG(dAzimRad);
2385 N2K_SatInfo[idx].SignalToNoiseRatio = iSNR;
2386 }
2387 }
2388 // Send to GPS.cpp
2389 if (idx > 0) {
2390 SendSatInfoToAllInstruments(NumberOfSVs, iMesNum + 1, talker_N2k,
2391 N2K_SatInfo);
2392 // mPriSatStatus = 1;
2393 mSatStatus_Wdog = gps_watchdog_timeout_ticks;
2394 }
2395 }
2396 }
2397 }
2398}
2399
2400// Wind PGN 130306
2401void dashboard_pi::HandleN2K_130306(ObservedEvt ev) {
2402 NMEA2000Id id_130306(130306);
2403 std::vector<uint8_t> v = GetN2000Payload(id_130306, ev);
2404
2405 // No source prioritization for 130306 because there are
2406 // multiple variables that can come from different sources.
2407
2408 unsigned char SID;
2409 double WindSpeed, WindAngle;
2410 tN2kWindReference WindReference;
2411
2412 // Get wind data
2413 if (ParseN2kPGN130306(v, SID, WindSpeed, WindAngle, WindReference)) {
2414 if (!N2kIsNA(WindSpeed) && !N2kIsNA(WindAngle)) {
2415 double wind_angle_degr = GEODESIC_RAD2DEG(WindAngle);
2416 double wind_speed_kn = MS2KNOTS(WindSpeed);
2417 bool sendTWA = false, sendTWS = false;
2418
2419 switch (WindReference) {
2420 case 0: // N2kWind direction True North
2421 if (mPriWDN >= 1) {
2422 SendSentenceToAllInstruments(OCPN_DBP_STC_TWD, wind_angle_degr,
2423 _T("\u00B0"));
2424 mPriWDN = 1;
2425 sendTWS = true;
2426 mWDN_Watchdog = no_nav_watchdog_timeout_ticks;
2427 }
2428 break;
2429 case 1: // N2kWind direction Magnetic North
2430 if (mPriWDN >= 1) {
2431 // Make it true if variation is available
2432 if (!std::isnan(mVar)) {
2433 wind_angle_degr += mVar;
2434 if (wind_angle_degr > 360.) {
2435 wind_angle_degr -= 360;
2436 } else if (wind_angle_degr < 0.) {
2437 wind_angle_degr += 360;
2438 }
2439 }
2440 SendSentenceToAllInstruments(OCPN_DBP_STC_TWD, wind_angle_degr,
2441 _T("\u00B0"));
2442 mPriWDN = 1;
2443 sendTWS = true;
2444 mWDN_Watchdog = no_nav_watchdog_timeout_ticks;
2445 }
2446 break;
2447 case 2: // N2kWind_Apparent_centerline
2448 if (mPriAWA >= 1) {
2449 double calc_angle = wind_angle_degr;
2450 // Angle equals 0-360 degr
2451 wxString m_awaunit = _T("\u00B0R");
2452 // Should be unit "L" and 0-180 to port
2453 if (wind_angle_degr > 180.0) {
2454 wind_angle_degr = 360.0 - wind_angle_degr;
2455 m_awaunit = _T("\u00B0L");
2456 }
2457 SendSentenceToAllInstruments(OCPN_DBP_STC_AWA,
2458 mAWAFilter.filter(wind_angle_degr),
2459 m_awaunit);
2460 // Speed
2461 SendSentenceToAllInstruments(
2462 OCPN_DBP_STC_AWS,
2463 toUsrSpeed_Plugin(mAWSFilter.filter(wind_speed_kn),
2464 g_iDashWindSpeedUnit),
2465 getUsrSpeedUnit_Plugin(g_iDashWindSpeedUnit));
2466
2467 mPriAWA = 1;
2468 mMWVA_Watchdog = gps_watchdog_timeout_ticks;
2469
2470 // If not N2K true wind data are recently received calculate it.
2471 if (mPriTWA != 1) {
2472 // Wants -+ angle instead of "L"/"R"
2473 if (calc_angle > 180) calc_angle -= 360.0;
2474 CalculateAndUpdateTWDS(wind_speed_kn, calc_angle);
2475 mPriTWA = 2;
2476 mPriWDN = 2;
2477 mMWVT_Watchdog = gps_watchdog_timeout_ticks;
2478 mWDN_Watchdog = no_nav_watchdog_timeout_ticks;
2479 }
2480 }
2481 break;
2482 case 3: // N2kWind_True_centerline_boat(ground)
2483 if (mPriTWA >= 1 && g_bDBtrueWindGround) {
2484 sendTWA = true;
2485 sendTWS = true;
2486 mPriTWA = 1;
2487 }
2488 break;
2489 case 4: // N2kWind_True_Centerline__water
2490 if (mPriTWA >= 1 && !g_bDBtrueWindGround) {
2491 sendTWA = true;
2492 sendTWS = true;
2493 mPriTWA = 1;
2494 }
2495 break;
2496 case 6: // N2kWind_Error
2497 break;
2498 case 7: // N2kWind_Unavailable
2499 break;
2500 default:
2501 break;
2502 }
2503
2504 if (sendTWA) {
2505 // Wind angle is 0-360 degr
2506 wxString m_twaunit = _T("\u00B0R");
2507 // Should be unit "L" and 0-180 to port
2508 if (wind_angle_degr > 180.0) {
2509 wind_angle_degr = 360.0 - wind_angle_degr;
2510 m_twaunit = _T("\u00B0L");
2511 }
2512 SendSentenceToAllInstruments(OCPN_DBP_STC_TWA, wind_angle_degr,
2513 m_twaunit);
2514 }
2515
2516 // Wind speed
2517 if (sendTWS) {
2518 SendSentenceToAllInstruments(
2519 OCPN_DBP_STC_TWS,
2520 toUsrSpeed_Plugin(wind_speed_kn, g_iDashWindSpeedUnit),
2521 getUsrSpeedUnit_Plugin(g_iDashWindSpeedUnit));
2522 SendSentenceToAllInstruments(
2523 OCPN_DBP_STC_TWS2,
2524 toUsrSpeed_Plugin(wind_speed_kn, g_iDashWindSpeedUnit),
2525 getUsrSpeedUnit_Plugin(g_iDashWindSpeedUnit));
2526 mMWVT_Watchdog = gps_watchdog_timeout_ticks;
2527 }
2528 }
2529 }
2530}
2531
2532void dashboard_pi::HandleN2K_130310(ObservedEvt ev) {
2533 NMEA2000Id id_130310(130310);
2534 std::vector<uint8_t> v = GetN2000Payload(id_130310, ev);
2535 unsigned char SID;
2536 double WaterTemperature, OutsideAmbientAirTemperature, AtmosphericPressure;
2537
2538 // Outside Environmental parameters
2539 if (ParseN2kPGN130310(v, SID, WaterTemperature, OutsideAmbientAirTemperature,
2540 AtmosphericPressure)) {
2541 if (mPriWTP >= 1) {
2542 if (!N2kIsNA(WaterTemperature)) {
2543 double m_wtemp KELVIN2C(WaterTemperature);
2544 SendSentenceToAllInstruments(OCPN_DBP_STC_TMP,
2545 toUsrTemp_Plugin(m_wtemp, g_iDashTempUnit),
2546 getUsrTempUnit_Plugin(g_iDashTempUnit));
2547 mPriWTP = 1;
2548 mWTP_Watchdog = no_nav_watchdog_timeout_ticks;
2549 }
2550 }
2551
2552 if (mPriATMP >= 1) {
2553 if (!N2kIsNA(OutsideAmbientAirTemperature)) {
2554 double m_airtemp = KELVIN2C(OutsideAmbientAirTemperature);
2555 if (m_airtemp > -60 && m_airtemp < 100) {
2556 SendSentenceToAllInstruments(
2557 OCPN_DBP_STC_ATMP, toUsrTemp_Plugin(m_airtemp, g_iDashTempUnit),
2558 getUsrTempUnit_Plugin(g_iDashTempUnit));
2559 mPriATMP = 1;
2560 mATMP_Watchdog = no_nav_watchdog_timeout_ticks;
2561 }
2562 }
2563 }
2564
2565 if (!N2kIsNA(AtmosphericPressure) && mPriMDA >= 1) {
2566 double m_press = PA2HPA(AtmosphericPressure);
2567 SendSentenceToAllInstruments(OCPN_DBP_STC_MDA, m_press, _T("hPa"));
2568 mPriMDA = 1;
2569 mMDA_Watchdog = no_nav_watchdog_timeout_ticks;
2570 }
2571 }
2572}
2573// Humidity (Rel %)
2574void dashboard_pi::HandleN2K_130313(ObservedEvt ev) {
2575 NMEA2000Id id_130313(130313);
2576 std::vector<uint8_t> v = GetN2000Payload(id_130313, ev);
2577 unsigned char SID, HumidityInstance;
2578 tN2kHumiditySource HumiditySource;
2579 double ActualHumidity, SetHumidity;
2580
2581 if (ParseN2kPGN130313(v, SID, HumidityInstance, HumiditySource,
2582 ActualHumidity, SetHumidity)) {
2583 if (mPriHUM >= 1) {
2584 if (!N2kIsNA(ActualHumidity)) {
2585 SendSentenceToAllInstruments(OCPN_DBP_STC_HUM, ActualHumidity, "%");
2586 mPriHUM = 1;
2587 mHUM_Watchdog = no_nav_watchdog_timeout_ticks;
2588 }
2589 }
2590 }
2591}
2592
2593/****** Signal K *******/
2594void dashboard_pi::ParseSignalK(wxString &msg) {
2595 wxJSONValue root;
2596 wxJSONReader jsonReader;
2597
2598 int errors = jsonReader.Parse(msg, &root);
2599
2600 // wxString dmsg( _T("Dashboard:SignalK Event received: ") );
2601 // dmsg.append(msg);
2602 // wxLogMessage(dmsg);
2603 // printf("%s\n", dmsg.ToUTF8().data());
2604
2605 if (root.HasMember("self")) {
2606 if (root["self"].AsString().StartsWith(_T("vessels.")))
2607 m_self = (root["self"].AsString()); // for java server, and OpenPlotter
2608 // node.js server 1.20
2609 else if (root["self"].AsString().Length())
2610 m_self =
2611 _T("vessels.") + (root["self"].AsString()); // for Node.js server
2612 }
2613
2614 if (root.HasMember("context") && root["context"].IsString()) {
2615 auto context = root["context"].AsString();
2616 if (context != m_self) {
2617 return;
2618 }
2619 }
2620
2621 if (root.HasMember("updates") && root["updates"].IsArray()) {
2622 wxJSONValue &updates = root["updates"];
2623 for (int i = 0; i < updates.Size(); ++i) {
2624 handleSKUpdate(updates[i]);
2625 }
2626 }
2627}
2628
2629void dashboard_pi::handleSKUpdate(wxJSONValue &update) {
2630 wxString sfixtime = "";
2631
2632 if (update.HasMember("timestamp")) {
2633 sfixtime = update["timestamp"].AsString();
2634 }
2635 if (update.HasMember("values") && update["values"].IsArray()) {
2636 wxString talker = wxEmptyString;
2637 if (update.HasMember("source")) {
2638 if (update["source"].HasMember("talker")) {
2639 if (update["source"]["talker"].IsString()) {
2640 talker = update["source"]["talker"].AsString();
2641 }
2642 }
2643 }
2644 for (int j = 0; j < update["values"].Size(); ++j) {
2645 wxJSONValue &item = update["values"][j];
2646 updateSKItem(item, talker, sfixtime);
2647 }
2648 }
2649}
2650
2651void dashboard_pi::updateSKItem(wxJSONValue &item, wxString &talker,
2652 wxString &sfixtime) {
2653 if (item.HasMember("path") && item.HasMember("value")) {
2654 const wxString &update_path = item["path"].AsString();
2655 wxJSONValue &value = item["value"];
2656
2657 // Container for last received sat-system info from SK-N2k
2658 static wxString talkerID = wxEmptyString;
2659 // Container for last received AWA that may be needed for TWS calculation
2660 static double skAWA;
2661
2662 if (update_path == _T("navigation.position")) {
2663 if (mPriPosition >= 2) {
2664 if (value["latitude"].IsDouble() && value["longitude"].IsDouble()) {
2665 double lat = value["latitude"].AsDouble();
2666 double lon = value["longitude"].AsDouble();
2667 SendSentenceToAllInstruments(OCPN_DBP_STC_LAT, lat, _T("SDMM"));
2668 SendSentenceToAllInstruments(OCPN_DBP_STC_LON, lon, _T("SDMM"));
2669 mPriPosition = 2;
2670 }
2671 }
2672 } else if (update_path == _T("navigation.speedOverGround") &&
2673 2 == mPriPosition) {
2674 double sog_knot = GetJsonDouble(value);
2675 if (std::isnan(sog_knot)) return;
2676
2677 SendSentenceToAllInstruments(
2678 OCPN_DBP_STC_SOG,
2679 toUsrSpeed_Plugin(mSOGFilter.filter(sog_knot), g_iDashSpeedUnit),
2680 getUsrSpeedUnit_Plugin(g_iDashSpeedUnit));
2681 } else if (update_path == _T("navigation.courseOverGroundTrue") &&
2682 2 == mPriPosition) {
2683 double cog_rad = GetJsonDouble(value);
2684 if (std::isnan(cog_rad)) return;
2685
2686 double cog_deg = GEODESIC_RAD2DEG(cog_rad);
2687 SendSentenceToAllInstruments(OCPN_DBP_STC_COG, mCOGFilter.filter(cog_deg),
2688 _T("\u00B0"));
2689 } else if (update_path == _T("navigation.headingTrue")) {
2690 if (mPriHeadingT >= 2) {
2691 double hdt = GetJsonDouble(value);
2692 if (std::isnan(hdt)) return;
2693
2694 hdt = GEODESIC_RAD2DEG(hdt);
2695 SendSentenceToAllInstruments(OCPN_DBP_STC_HDT, hdt, _T("\u00B0T"));
2696 mPriHeadingT = 2;
2697 mHDT_Watchdog = gps_watchdog_timeout_ticks;
2698 }
2699 } else if (update_path == _T("navigation.headingMagnetic")) {
2700 if (mPriHeadingM >= 2) {
2701 double hdm = GetJsonDouble(value);
2702 if (std::isnan(hdm)) return;
2703
2704 hdm = GEODESIC_RAD2DEG(hdm);
2705 SendSentenceToAllInstruments(OCPN_DBP_STC_HDM, hdm, _T("\u00B0M"));
2706 mPriHeadingM = 2;
2707 mHDx_Watchdog = gps_watchdog_timeout_ticks;
2708
2709 // If no higher priority HDT, calculate it here.
2710 if (mPriHeadingT >= 6 && (!std::isnan(mVar))) {
2711 double heading = hdm + mVar;
2712 if (heading < 0)
2713 heading += 360;
2714 else if (heading >= 360.0)
2715 heading -= 360;
2716 SendSentenceToAllInstruments(OCPN_DBP_STC_HDT, heading, _T("\u00B0"));
2717 mPriHeadingT = 6;
2718 mHDT_Watchdog = gps_watchdog_timeout_ticks;
2719 }
2720 }
2721 } else if (update_path == _T("navigation.speedThroughWater")) {
2722 if (mPriSTW >= 2) {
2723 double stw_knots = GetJsonDouble(value);
2724 if (std::isnan(stw_knots)) return;
2725
2726 stw_knots = MS2KNOTS(stw_knots);
2727 SendSentenceToAllInstruments(
2728 OCPN_DBP_STC_STW, toUsrSpeed_Plugin(stw_knots, g_iDashSpeedUnit),
2729 getUsrSpeedUnit_Plugin(g_iDashSpeedUnit));
2730 mPriSTW = 2;
2731 mSTW_Watchdog = gps_watchdog_timeout_ticks;
2732 }
2733 } else if (update_path == _T("navigation.magneticVariation")) {
2734 if (mPriVar >= 2) {
2735 double dvar = GetJsonDouble(value);
2736 if (std::isnan(dvar)) return;
2737
2738 dvar = GEODESIC_RAD2DEG(dvar);
2739 if (0.0 != dvar) { // Let WMM do the job instead
2740 SendSentenceToAllInstruments(OCPN_DBP_STC_HMV, dvar, _T("\u00B0"));
2741 mPriVar = 2;
2742 mVar_Watchdog = gps_watchdog_timeout_ticks;
2743 }
2744 }
2745 } else if (update_path == _T("environment.wind.angleApparent")) {
2746 if (mPriAWA >= 2) {
2747 double m_awaangle = GetJsonDouble(value);
2748 if (std::isnan(m_awaangle)) return;
2749
2750 m_awaangle = GEODESIC_RAD2DEG(m_awaangle); // negative to port
2751 skAWA = m_awaangle;
2752 wxString m_awaunit = _T("\u00B0R");
2753 if (m_awaangle < 0) {
2754 m_awaunit = _T("\u00B0L");
2755 m_awaangle *= -1;
2756 }
2757 SendSentenceToAllInstruments(OCPN_DBP_STC_AWA,
2758 mAWAFilter.filter(m_awaangle), m_awaunit);
2759 mPriAWA = 2; // Set prio only here. No need to catch speed if no angle.
2760 mMWVA_Watchdog = gps_watchdog_timeout_ticks;
2761 }
2762 } else if (update_path == _T("environment.wind.speedApparent")) {
2763 if (mPriAWA >= 2) {
2764 double m_awaspeed_kn = GetJsonDouble(value);
2765 if (std::isnan(m_awaspeed_kn)) return;
2766
2767 m_awaspeed_kn = MS2KNOTS(m_awaspeed_kn);
2768 SendSentenceToAllInstruments(
2769 OCPN_DBP_STC_AWS,
2770 toUsrSpeed_Plugin(mAWSFilter.filter(m_awaspeed_kn),
2771 g_iDashWindSpeedUnit),
2772 getUsrSpeedUnit_Plugin(g_iDashWindSpeedUnit));
2773
2774 // If no TWA from SK try to use AWS/AWA to calculate it
2775 if (mPriTWA >= 6 && !std::isnan(skAWA)) {
2776 CalculateAndUpdateTWDS(m_awaspeed_kn, skAWA);
2777 mPriTWA = 6;
2778 mMWVT_Watchdog = gps_watchdog_timeout_ticks;
2779 mWDN_Watchdog = no_nav_watchdog_timeout_ticks;
2780 }
2781 }
2782 } else if ((update_path == _T("environment.wind.angleTrueWater") &&
2783 !g_bDBtrueWindGround) ||
2784 (update_path == _T("environment.wind.angleTrueGround") &&
2785 g_bDBtrueWindGround)) {
2786 if (mPriTWA >= 3) {
2787 double m_twaangle = GetJsonDouble(value);
2788 if (std::isnan(m_twaangle)) return;
2789
2790 m_twaangle = GEODESIC_RAD2DEG(m_twaangle);
2791 double m_twaangle_raw = m_twaangle; // for wind history
2792 wxString m_twaunit = _T("\u00B0R");
2793 if (m_twaangle < 0) {
2794 m_twaunit = _T("\u00B0L");
2795 m_twaangle *= -1;
2796 }
2797 SendSentenceToAllInstruments(OCPN_DBP_STC_TWA, m_twaangle, m_twaunit);
2798 mPriTWA = 3; // Set prio only here. No need to catch speed if no angle.
2799 mMWVT_Watchdog = gps_watchdog_timeout_ticks;
2800
2801 if (mPriWDN >= 5) {
2802 // m_twaangle_raw has wind angle relative to the bow.
2803 // Wind history use angle relative to north.
2804 // If no TWD with higher priority is present and
2805 // true heading is available calculate it.
2806 if (g_dHDT < 361. && g_dHDT >= 0.0) {
2807 double g_dCalWdir = (m_twaangle_raw) + g_dHDT;
2808 if (g_dCalWdir > 360.) {
2809 g_dCalWdir -= 360;
2810 } else if (g_dCalWdir < 0.) {
2811 g_dCalWdir += 360;
2812 }
2813 SendSentenceToAllInstruments(OCPN_DBP_STC_TWD, g_dCalWdir,
2814 _T("\u00B0"));
2815 mPriWDN = 5;
2816 mWDN_Watchdog = no_nav_watchdog_timeout_ticks;
2817 }
2818 }
2819 }
2820 } else if ((update_path == _T("environment.wind.speedTrue") &&
2821 !g_bDBtrueWindGround) ||
2822 (update_path == _T("environment.wind.speedOverGround") &&
2823 g_bDBtrueWindGround)) {
2824 if (mPriTWA >= 3) {
2825 double m_twaspeed_kn = GetJsonDouble(value);
2826 if (std::isnan(m_twaspeed_kn)) return;
2827
2828 m_twaspeed_kn = MS2KNOTS(m_twaspeed_kn);
2829 SendSentenceToAllInstruments(
2830 OCPN_DBP_STC_TWS,
2831 toUsrSpeed_Plugin(m_twaspeed_kn, g_iDashWindSpeedUnit),
2832 getUsrSpeedUnit_Plugin(g_iDashWindSpeedUnit));
2833 SendSentenceToAllInstruments(
2834 OCPN_DBP_STC_TWS2,
2835 toUsrSpeed_Plugin(m_twaspeed_kn, g_iDashWindSpeedUnit),
2836 getUsrSpeedUnit_Plugin(g_iDashWindSpeedUnit));
2837 }
2838 } else if (update_path == _T("environment.depth.belowSurface")) {
2839 if (mPriDepth >= 3) {
2840 double depth = GetJsonDouble(value);
2841 if (std::isnan(depth)) return;
2842
2843 mPriDepth = 3;
2844 depth += g_dDashDBTOffset;
2845 depth /= 1852.0;
2846 SendSentenceToAllInstruments(
2847 OCPN_DBP_STC_DPT, toUsrDistance_Plugin(depth, g_iDashDepthUnit),
2848 getUsrDistanceUnit_Plugin(g_iDashDepthUnit));
2849 mDPT_DBT_Watchdog = gps_watchdog_timeout_ticks;
2850 }
2851 } else if (update_path == _T("environment.depth.belowTransducer")) {
2852 if (mPriDepth >= 3) {
2853 double depth = GetJsonDouble(value);
2854 if (std::isnan(depth)) return;
2855
2856 mPriDepth = 3;
2857 depth += g_dDashDBTOffset;
2858 depth /= 1852.0;
2859 SendSentenceToAllInstruments(
2860 OCPN_DBP_STC_DPT, toUsrDistance_Plugin(depth, g_iDashDepthUnit),
2861 getUsrDistanceUnit_Plugin(g_iDashDepthUnit));
2862 mDPT_DBT_Watchdog = gps_watchdog_timeout_ticks;
2863 }
2864 } else if (update_path == _T("environment.water.temperature")) {
2865 if (mPriWTP >= 2) {
2866 double m_wtemp = GetJsonDouble(value);
2867 if (std::isnan(m_wtemp)) return;
2868
2869 m_wtemp = KELVIN2C(m_wtemp);
2870 if (m_wtemp > -60 && m_wtemp < 200 && !std::isnan(m_wtemp)) {
2871 SendSentenceToAllInstruments(
2872 OCPN_DBP_STC_TMP, toUsrTemp_Plugin(m_wtemp, g_iDashTempUnit),
2873 getUsrTempUnit_Plugin(g_iDashTempUnit));
2874 mPriWTP = 2;
2875 mWTP_Watchdog = no_nav_watchdog_timeout_ticks;
2876 }
2877 }
2878 } else if (update_path ==
2879 _T("navigation.courseRhumbline.nextPoint.velocityMadeGood")) {
2880 double m_vmg_kn = GetJsonDouble(value);
2881 if (std::isnan(m_vmg_kn)) return;
2882
2883 m_vmg_kn = MS2KNOTS(m_vmg_kn);
2884 SendSentenceToAllInstruments(
2885 OCPN_DBP_STC_VMG, toUsrSpeed_Plugin(m_vmg_kn, g_iDashSpeedUnit),
2886 getUsrSpeedUnit_Plugin(g_iDashSpeedUnit));
2887 mVMG_Watchdog = gps_watchdog_timeout_ticks;
2888 }
2889
2890 else if (update_path == _T("performance.velocityMadeGood")) {
2891 double m_vmgw_kn = GetJsonDouble(value);
2892 if (std::isnan(m_vmgw_kn)) return;
2893
2894 m_vmgw_kn = MS2KNOTS(m_vmgw_kn);
2895 SendSentenceToAllInstruments(
2896 OCPN_DBP_STC_VMGW, toUsrSpeed_Plugin(m_vmgw_kn, g_iDashSpeedUnit),
2897 getUsrSpeedUnit_Plugin(g_iDashSpeedUnit));
2898 mVMGW_Watchdog = gps_watchdog_timeout_ticks;
2899 }
2900
2901 else if (update_path == _T("steering.rudderAngle")) { // ->port
2902 if (mPriRSA >= 2) {
2903 double m_rudangle = GetJsonDouble(value);
2904 if (std::isnan(m_rudangle)) return;
2905
2906 m_rudangle = GEODESIC_RAD2DEG(m_rudangle);
2907 SendSentenceToAllInstruments(OCPN_DBP_STC_RSA, m_rudangle,
2908 _T("\u00B0"));
2909 mRSA_Watchdog = gps_watchdog_timeout_ticks;
2910 mPriRSA = 2;
2911 }
2912 } else if (update_path ==
2913 _T("navigation.gnss.satellites")) { // GNSS satellites in use
2914 if (mPriSatUsed >= 2) {
2915 int usedSats = (value).AsInt();
2916 if (usedSats < 1) return;
2917 SendSentenceToAllInstruments(OCPN_DBP_STC_SAT, usedSats, _T (""));
2918 mPriSatUsed = 2;
2919 mSatsUsed_Wdog = gps_watchdog_timeout_ticks;
2920 }
2921 } else if (update_path == _T("navigation.gnss.type")) {
2922 if (value.IsString() && value.AsString() != wxEmptyString) {
2923 talkerID = (value.AsString()); // Like "Combined GPS/GLONASS"
2924 talkerID.MakeUpper();
2925 m_PriN2kTalker = gps_watchdog_timeout_ticks;
2926 if ((talkerID.Contains(_T("GPS"))) &&
2927 (talkerID.Contains(_T("GLONASS"))))
2928 talkerID = _T("GPSGLONAS");
2929 else if (talkerID.Contains(_T("GPS")))
2930 talkerID = _T("GP");
2931 else if (talkerID.Contains(_T("GLONASS")))
2932 talkerID = _T("GL");
2933 else if (talkerID.Contains(_T("GALILEO")))
2934 talkerID = _T("GA");
2935 else if (talkerID.Contains(_T("BEIDOU")))
2936 talkerID = _T("GI");
2937 }
2938 } else if (update_path ==
2939 _T("navigation.gnss.satellitesInView")) { // GNSS satellites in
2940 // view
2941 if (mPriSatUsed >= 4) {
2942 if (value.HasMember("count") && value["count"].IsInt()) {
2943 double m_SK_SatsInView = (value["count"].AsInt());
2944 SendSentenceToAllInstruments(OCPN_DBP_STC_SAT, m_SK_SatsInView,
2945 _T (""));
2946 mPriSatUsed = 4;
2947 mSatsUsed_Wdog = gps_watchdog_timeout_ticks;
2948 }
2949 }
2950 if (mPriSatStatus == 2) {
2951 if (value.HasMember("satellites") && value["satellites"].IsArray()) {
2952 // Update satellites data.
2953 int iNumSats;
2954 if (value.HasMember("count") && value["count"].IsInt()) {
2955 iNumSats = (value["count"].AsInt());
2956 } else
2957 iNumSats = value[_T ("satellites")].Size();
2958
2959 SAT_INFO SK_SatInfo[4];
2960 for (int idx = 0; idx < 4; idx++) {
2961 SK_SatInfo[idx].SatNumber = 0;
2962 SK_SatInfo[idx].ElevationDegrees = 0;
2963 SK_SatInfo[idx].AzimuthDegreesTrue = 0;
2964 SK_SatInfo[idx].SignalToNoiseRatio = 0;
2965 }
2966
2967 if (iNumSats) {
2968 // Arrange SK's array[12] to max three messages like NMEA GSV
2969 int iID = 0;
2970 int iSNR = 0;
2971 double dElevRad = 0;
2972 double dAzimRad = 0;
2973 int idx = 0;
2974 int arr = 0;
2975 for (int iMesNum = 0; iMesNum < 3; iMesNum++) {
2976 for (idx = 0; idx < 4; idx++) {
2977 arr = idx + 4 * iMesNum;
2978 if (value["satellites"][arr]["id"].IsInt())
2979 iID = value["satellites"][arr]["id"].AsInt();
2980 if (value["satellites"][arr]["elevation"].IsDouble())
2981 dElevRad = value["satellites"][arr]["elevation"].AsDouble();
2982 if (value["satellites"][arr]["azimuth"].IsDouble())
2983 dAzimRad = value["satellites"][arr]["azimuth"].AsDouble();
2984 if (value["satellites"][arr]["SNR"].IsInt())
2985 iSNR = value["satellites"][arr]["SNR"].AsInt();
2986
2987 if (iID < 1) break;
2988 SK_SatInfo[idx].SatNumber = iID;
2989 SK_SatInfo[idx].ElevationDegrees = GEODESIC_RAD2DEG(dElevRad);
2990 SK_SatInfo[idx].AzimuthDegreesTrue = GEODESIC_RAD2DEG(dAzimRad);
2991 SK_SatInfo[idx].SignalToNoiseRatio = iSNR;
2992 }
2993 if (idx > 0) {
2994 if (m_PriN2kTalker <= 0 && talker != wxEmptyString &&
2995 (talker.StartsWith(_T("G")) ||
2996 talker.StartsWith(_T("BD")))) {
2997 talkerID = talker; // Origin NMEA0183
2998 }
2999 SendSatInfoToAllInstruments(iNumSats, iMesNum + 1, talkerID,
3000 SK_SatInfo);
3001 // mPriSatStatus = 2;
3002 mSatStatus_Wdog = gps_watchdog_timeout_ticks;
3003 }
3004
3005 if (iID < 1) break;
3006 }
3007 }
3008 }
3009 }
3010 } else if (update_path == _T("navigation.gnss.antennaAltitude")) {
3011 if (mPriAlt >= 2) {
3012 double m_alt = GetJsonDouble(value);
3013 if (std::isnan(m_alt)) return;
3014
3015 SendSentenceToAllInstruments(OCPN_DBP_STC_ALTI, m_alt, _T("m"));
3016 mPriAlt = 2;
3017 mALT_Watchdog = gps_watchdog_timeout_ticks;
3018 }
3019
3020 } else if (update_path == _T("navigation.datetime")) {
3021 if (mPriDateTime >= 1) {
3022 mPriDateTime = 1;
3023 wxString s_dt = (value.AsString()); //"2019-12-28T09:26:58.000Z"
3024 s_dt.Replace('-', wxEmptyString);
3025 s_dt.Replace(':', wxEmptyString);
3026 wxString utc_dt = s_dt.BeforeFirst('T'); // Date
3027 utc_dt.Append(s_dt.AfterFirst('T').Left(6)); // time
3028 mUTCDateTime.ParseFormat(utc_dt.c_str(), _T("%Y%m%d%H%M%S"));
3029 mUTC_Watchdog = gps_watchdog_timeout_ticks;
3030 }
3031 } else if (update_path == _T("environment.outside.temperature")) {
3032 if (mPriATMP >= 2) {
3033 double m_airtemp = GetJsonDouble(value);
3034 if (std::isnan(m_airtemp)) return;
3035
3036 m_airtemp = KELVIN2C(m_airtemp);
3037 if (m_airtemp > -60 && m_airtemp < 100) {
3038 SendSentenceToAllInstruments(
3039 OCPN_DBP_STC_ATMP, toUsrTemp_Plugin(m_airtemp, g_iDashTempUnit),
3040 getUsrTempUnit_Plugin(g_iDashTempUnit));
3041 mPriATMP = 2;
3042 mATMP_Watchdog = no_nav_watchdog_timeout_ticks;
3043 }
3044 }
3045 } else if (update_path == _T("environment.outside.humidity") ||
3046 update_path == _T("environment.outside.relativeHumidity")) {
3047 if (mPriHUM >= 2) {
3048 double m_hum = GetJsonDouble(value) * 100; // ratio2%
3049 if (std::isnan(m_hum)) return;
3050 SendSentenceToAllInstruments(OCPN_DBP_STC_HUM, m_hum, "%");
3051 mPriHUM = 2;
3052 mHUM_Watchdog = no_nav_watchdog_timeout_ticks;
3053 }
3054 } else if (update_path ==
3055 _T("environment.wind.directionTrue")) { // relative true north
3056 if (mPriWDN >= 3) {
3057 double m_twdT = GetJsonDouble(value);
3058 if (std::isnan(m_twdT)) return;
3059
3060 m_twdT = GEODESIC_RAD2DEG(m_twdT);
3061 SendSentenceToAllInstruments(OCPN_DBP_STC_TWD, m_twdT, _T("\u00B0"));
3062 mPriWDN = 3;
3063 mWDN_Watchdog = no_nav_watchdog_timeout_ticks;
3064 }
3065 } else if (update_path == _T("environment.wind.directionMagnetic")) {
3066 // relative magn north
3067 if (mPriWDN >= 4) {
3068 double m_twdM = GetJsonDouble(value);
3069 if (std::isnan(m_twdM)) return;
3070 m_twdM = GEODESIC_RAD2DEG(m_twdM);
3071 // Make it true if variation is available
3072 if (!std::isnan(mVar)) {
3073 m_twdM = (m_twdM) + mVar;
3074 if (m_twdM > 360.) {
3075 m_twdM -= 360;
3076 } else if (m_twdM < 0.) {
3077 m_twdM += 360;
3078 }
3079 }
3080 SendSentenceToAllInstruments(OCPN_DBP_STC_TWD, m_twdM, _T("\u00B0"));
3081 mPriWDN = 4;
3082 mWDN_Watchdog = no_nav_watchdog_timeout_ticks;
3083 }
3084 } else if (update_path == _T("navigation.trip.log")) { // m
3085 double m_tlog = GetJsonDouble(value);
3086 if (std::isnan(m_tlog)) return;
3087
3088 m_tlog = METERS2NM(m_tlog);
3089 SendSentenceToAllInstruments(
3090 OCPN_DBP_STC_VLW1, toUsrDistance_Plugin(m_tlog, g_iDashDistanceUnit),
3091 getUsrDistanceUnit_Plugin(g_iDashDistanceUnit));
3092 mTrLOG_Watchdog = no_nav_watchdog_timeout_ticks;
3093 } else if (update_path == _T("navigation.log")) { // m
3094 double m_slog = GetJsonDouble(value);
3095 if (std::isnan(m_slog)) return;
3096
3097 m_slog = METERS2NM(m_slog);
3098 SendSentenceToAllInstruments(
3099 OCPN_DBP_STC_VLW2, toUsrDistance_Plugin(m_slog, g_iDashDistanceUnit),
3100 getUsrDistanceUnit_Plugin(g_iDashDistanceUnit));
3101 mLOG_Watchdog = no_nav_watchdog_timeout_ticks;
3102 } else if (update_path == _T("environment.outside.pressure") &&
3103 mPriMDA >= 2) { // Pa
3104 double m_press = GetJsonDouble(value);
3105 if (std::isnan(m_press)) return;
3106
3107 m_press = PA2HPA(m_press);
3108 SendSentenceToAllInstruments(OCPN_DBP_STC_MDA, m_press, _T("hPa"));
3109 mPriMDA = 2;
3110 mMDA_Watchdog = no_nav_watchdog_timeout_ticks;
3111 } else if (update_path == _T("navigation.attitude")) { // rad
3112 if (mPriPitchRoll >= 2) {
3113 if (value["roll"].AsString() != "0") {
3114 double m_heel = GEODESIC_RAD2DEG(value["roll"].AsDouble());
3115 wxString h_unit = _T("\u00B0\u003E") + _("Stbd");
3116 if (m_heel < 0) {
3117 h_unit = _T("\u00B0\u003C") + _("Port");
3118 m_heel *= -1;
3119 }
3120 SendSentenceToAllInstruments(OCPN_DBP_STC_HEEL, m_heel, h_unit);
3121 mHEEL_Watchdog = gps_watchdog_timeout_ticks;
3122 mPriPitchRoll = 2;
3123 }
3124 if (value["pitch"].AsString() != "0") {
3125 double m_pitch = GEODESIC_RAD2DEG(value["pitch"].AsDouble());
3126 wxString p_unit = _T("\u00B0\u2191") + _("Up");
3127 if (m_pitch < 0) {
3128 p_unit = _T("\u00B0\u2193") + _("Down");
3129 m_pitch *= -1;
3130 }
3131 SendSentenceToAllInstruments(OCPN_DBP_STC_PITCH, m_pitch, p_unit);
3132 mPITCH_Watchdog = gps_watchdog_timeout_ticks;
3133 mPriPitchRoll = 2;
3134 }
3135 }
3136 }
3137 }
3138}
3139
3140/*******Nav data from OCPN core *******/
3142 if (mPriPosition >= 1) {
3143 mPriPosition = 1;
3144 SendSentenceToAllInstruments(OCPN_DBP_STC_LAT, pfix.Lat, _T("SDMM"));
3145 SendSentenceToAllInstruments(OCPN_DBP_STC_LON, pfix.Lon, _T("SDMM"));
3146 }
3147 if (mPriCOGSOG >= 1) {
3148 double dMagneticCOG;
3149 mPriCOGSOG = 1;
3150 SendSentenceToAllInstruments(
3151 OCPN_DBP_STC_SOG,
3152 toUsrSpeed_Plugin(mSOGFilter.filter(pfix.Sog), g_iDashSpeedUnit),
3153 getUsrSpeedUnit_Plugin(g_iDashSpeedUnit));
3154 SendSentenceToAllInstruments(OCPN_DBP_STC_COG, mCOGFilter.filter(pfix.Cog),
3155 _T("\u00B0"));
3156
3157 // Collect distance to update SumLog every second.
3158 double logSOG = mSOGFilter.filter(pfix.Sog);
3159 if (logSOG > 0.4) {
3160 d_tripNM += logSOG / 3600;
3161 // Update SumLog instrument every minute when we are under way.
3162 if (++logCount > 60) {
3163 UpdateSumLog(true);
3164 d_tripNM = 0.0;
3165 logCount = 0;
3166 }
3167 }
3168
3169 dMagneticCOG = mCOGFilter.get() - pfix.Var;
3170 if (dMagneticCOG < 0.0) dMagneticCOG = 360.0 + dMagneticCOG;
3171 if (dMagneticCOG > 360.0) dMagneticCOG = dMagneticCOG - 360.0;
3172 SendSentenceToAllInstruments(OCPN_DBP_STC_MCOG, dMagneticCOG,
3173 _T("\u00B0M"));
3174 }
3175 if (mPriVar >= 1) {
3176 if (!std::isnan(pfix.Var)) {
3177 mPriVar = 1;
3178 mVar = pfix.Var;
3179 mVar_Watchdog = gps_watchdog_timeout_ticks;
3180
3181 SendSentenceToAllInstruments(OCPN_DBP_STC_HMV, pfix.Var, _T("\u00B0"));
3182 }
3183 }
3184 if (mPriDateTime >= 6) { // We prefer the GPS datetime
3185 // If GNSS data is available, the value of pfix.FixTime is the GNSS time in
3186 // UTC. If GNSS data is not available, the special value 0 is used to
3187 // indicate that the time is not valid.
3188 if (pfix.FixTime > 0)
3189 mUTCDateTime.Set(pfix.FixTime);
3190 else
3191 mUTCDateTime = wxInvalidDateTime;
3192 if (mUTCDateTime.IsValid()) {
3193 mPriDateTime = 6;
3194 mUTCDateTime = mUTCDateTime.ToUTC();
3195 mUTC_Watchdog = gps_watchdog_timeout_ticks;
3196 }
3197 }
3198 if (mPriSatUsed >= 1) {
3199 mSatsInUse = pfix.nSats;
3200 if (mSatsInUse > 0) {
3201 SendSentenceToAllInstruments(OCPN_DBP_STC_SAT, mSatsInUse, _T(""));
3202 mPriSatUsed = 1;
3203 mSatsUsed_Wdog = gps_watchdog_timeout_ticks;
3204 }
3205 }
3206 if (mPriHeadingT >= 1) {
3207 double hdt = pfix.Hdt;
3208 if (std::isnan(hdt)) return;
3209 SendSentenceToAllInstruments(OCPN_DBP_STC_HDT, hdt, _T("\u00B0T"));
3210 mPriHeadingT = 1;
3211 mHDT_Watchdog = gps_watchdog_timeout_ticks;
3212 }
3213 if (mPriHeadingM >= 1) {
3214 double hdm = pfix.Hdm;
3215 if (std::isnan(hdm) && !std::isnan(pfix.Hdt) && !std::isnan(pfix.Var)) {
3216 hdm = pfix.Hdt - pfix.Var;
3217 if (hdm < 0)
3218 hdm += 360;
3219 else if (hdm >= 360.0)
3220 hdm -= 360;
3221 }
3222 if (std::isnan(hdm)) return;
3223 SendSentenceToAllInstruments(OCPN_DBP_STC_HDM, hdm, _T("\u00B0M"));
3224 mPriHeadingM = 1;
3225 mHDx_Watchdog = gps_watchdog_timeout_ticks;
3226 }
3227}
3228
3229void dashboard_pi::SetCursorLatLon(double lat, double lon) {
3230 SendSentenceToAllInstruments(OCPN_DBP_STC_PLA, lat, _T("SDMM"));
3231 SendSentenceToAllInstruments(OCPN_DBP_STC_PLO, lon, _T("SDMM"));
3232}
3233
3234void dashboard_pi::SetPluginMessage(wxString &message_id,
3235 wxString &message_body) {
3236 if (message_id == _T("WMM_VARIATION_BOAT")) {
3237 // construct the JSON root object
3238 wxJSONValue root;
3239 // construct a JSON parser
3240 wxJSONReader reader;
3241
3242 // now read the JSON text and store it in the 'root' structure
3243 // check for errors before retreiving values...
3244 int numErrors = reader.Parse(message_body, &root);
3245 if (numErrors > 0) {
3246 // const wxArrayString& errors = reader.GetErrors();
3247 return;
3248 }
3249
3250 // get the DECL value from the JSON message
3251 wxString decl = root[_T("Decl")].AsString();
3252 double decl_val;
3253 decl.ToDouble(&decl_val);
3254
3255 if (mPriVar >= 5) {
3256 mPriVar = 5;
3257 mVar = decl_val;
3258 mVar_Watchdog = gps_watchdog_timeout_ticks;
3259 SendSentenceToAllInstruments(OCPN_DBP_STC_HMV, mVar, _T("\u00B0"));
3260 }
3261 } else if (message_id == _T("OCPN_CORE_SIGNALK")) {
3262 ParseSignalK(message_body);
3263 }
3264}
3265
3267
3270 parent, wxID_ANY, m_ArrayOfDashboardWindow);
3271
3272 dialog->RecalculateSize();
3273
3274#ifdef __OCPN__ANDROID__
3275 dialog->GetHandle()->setStyleSheet(qtStyleSheet);
3276#endif
3277
3278#ifdef __OCPN__ANDROID__
3279 wxWindow *ccwin = GetOCPNCanvasWindow();
3280
3281 if (ccwin) {
3282 int xmax = ccwin->GetSize().GetWidth();
3283 int ymax = ccwin->GetParent()
3284 ->GetSize()
3285 .GetHeight(); // This would be the Frame itself
3286 dialog->SetSize(xmax, ymax);
3287 dialog->Layout();
3288
3289 dialog->Move(0, 0);
3290 }
3291#endif
3292
3293 if (dialog->ShowModal() == wxID_OK) {
3294 double scaler = 1.0;
3295 if (OCPN_GetWinDIPScaleFactor() < 1.0)
3296 scaler = 1.0 + OCPN_GetWinDIPScaleFactor() / 4;
3297 scaler = wxMax(1.0, scaler);
3298
3299 g_USFontTitle = *(dialog->m_pFontPickerTitle->GetFontData());
3300 g_FontTitle = *g_pUSFontTitle;
3301 g_FontTitle.SetChosenFont(g_pUSFontTitle->GetChosenFont().Scaled(scaler));
3302 g_FontTitle.SetColour(g_pUSFontTitle->GetColour());
3303 g_USFontTitle = *g_pUSFontTitle;
3304
3305 g_USFontData = *(dialog->m_pFontPickerData->GetFontData());
3306 g_FontData = *g_pUSFontData;
3307 g_FontData.SetChosenFont(g_pUSFontData->GetChosenFont().Scaled(scaler));
3308 g_FontData.SetColour(g_pUSFontData->GetColour());
3309 g_USFontData = *g_pUSFontData;
3310
3311 g_USFontLabel = *(dialog->m_pFontPickerLabel->GetFontData());
3312 g_FontLabel = *g_pUSFontLabel;
3313 g_FontLabel.SetChosenFont(g_pUSFontLabel->GetChosenFont().Scaled(scaler));
3314 g_FontLabel.SetColour(g_pUSFontLabel->GetColour());
3315 g_USFontLabel = *g_pUSFontLabel;
3316
3317 g_USFontSmall = *(dialog->m_pFontPickerSmall->GetFontData());
3318 g_FontSmall = *g_pUSFontSmall;
3319 g_FontSmall.SetChosenFont(g_pUSFontSmall->GetChosenFont().Scaled(scaler));
3320 g_FontSmall.SetColour(g_pUSFontSmall->GetColour());
3321 g_USFontSmall = *g_pUSFontSmall;
3322
3323 // OnClose should handle that for us normally but it doesn't seems to do so
3324 // We must save changes first
3325 g_dashPrefWidth = dialog->GetSize().x;
3326 g_dashPrefHeight = dialog->GetSize().y;
3327
3328 dialog->SaveDashboardConfig();
3329 m_ArrayOfDashboardWindow.Clear();
3330 m_ArrayOfDashboardWindow = dialog->m_Config;
3331
3332 ApplyConfig();
3333 SaveConfig();
3334 SetToolbarItemState(m_toolbar_item_id, GetDashboardWindowShownCount() != 0);
3335 }
3336 dialog->Destroy();
3337}
3338
3340 aktuellColorScheme = cs;
3341 for (size_t i = 0; i < m_ArrayOfDashboardWindow.GetCount(); i++) {
3342 DashboardWindow *dashboard_window =
3343 m_ArrayOfDashboardWindow.Item(i)->m_pDashboardWindow;
3344 if (dashboard_window) dashboard_window->SetColorScheme(cs);
3345 }
3346}
3347
3348int dashboard_pi::GetDashboardWindowShownCount() {
3349 int cnt = 0;
3350
3351 for (size_t i = 0; i < m_ArrayOfDashboardWindow.GetCount(); i++) {
3352 DashboardWindow *dashboard_window =
3353 m_ArrayOfDashboardWindow.Item(i)->m_pDashboardWindow;
3354 if (dashboard_window) {
3355 wxAuiPaneInfo &pane = m_pauimgr->GetPane(dashboard_window);
3356 if (pane.IsOk() && pane.IsShown()) cnt++;
3357 }
3358 }
3359 return cnt;
3360}
3361
3362void dashboard_pi::OnPaneClose(wxAuiManagerEvent &event) {
3363 // if name is unique, we should use it
3364 DashboardWindow *dashboard_window = (DashboardWindow *)event.pane->window;
3365 int cnt = 0;
3366 for (size_t i = 0; i < m_ArrayOfDashboardWindow.GetCount(); i++) {
3367 DashboardWindowContainer *cont = m_ArrayOfDashboardWindow.Item(i);
3368 DashboardWindow *d_w = cont->m_pDashboardWindow;
3369 if (d_w) {
3370 // we must not count this one because it is being closed
3371 if (dashboard_window != d_w) {
3372 wxAuiPaneInfo &pane = m_pauimgr->GetPane(d_w);
3373 if (pane.IsOk() && pane.IsShown()) cnt++;
3374 } else {
3375 cont->m_bIsVisible = false;
3376 }
3377 }
3378 }
3379 SetToolbarItemState(m_toolbar_item_id, cnt != 0);
3380
3381 event.Skip();
3382}
3383
3385 int cnt = GetDashboardWindowShownCount();
3386
3387 bool b_anyviz = false;
3388 for (size_t i = 0; i < m_ArrayOfDashboardWindow.GetCount(); i++) {
3389 DashboardWindowContainer *cont = m_ArrayOfDashboardWindow.Item(i);
3390 if (cont->m_bIsVisible) {
3391 b_anyviz = true;
3392 break;
3393 }
3394 }
3395
3396 for (size_t i = 0; i < m_ArrayOfDashboardWindow.GetCount(); i++) {
3397 DashboardWindowContainer *cont = m_ArrayOfDashboardWindow.Item(i);
3398 DashboardWindow *dashboard_window = cont->m_pDashboardWindow;
3399 if (dashboard_window) {
3400 wxAuiPaneInfo &pane = m_pauimgr->GetPane(dashboard_window);
3401 if (pane.IsOk()) {
3402 bool b_reset_pos = false;
3403
3404#ifdef __WXMSW__
3405 // Support MultiMonitor setups which an allow negative window
3406 // positions. If the requested window title bar does not intersect any
3407 // installed monitor, then default to simple primary monitor
3408 // positioning.
3409 RECT frame_title_rect;
3410 frame_title_rect.left = pane.floating_pos.x;
3411 frame_title_rect.top = pane.floating_pos.y;
3412 frame_title_rect.right = pane.floating_pos.x + pane.floating_size.x;
3413 frame_title_rect.bottom = pane.floating_pos.y + 30;
3414
3415 if (NULL == MonitorFromRect(&frame_title_rect, MONITOR_DEFAULTTONULL))
3416 b_reset_pos = true;
3417#else
3418
3419 // Make sure drag bar (title bar) of window intersects wxClient Area
3420 // of screen, with a little slop...
3421 wxRect window_title_rect; // conservative estimate
3422 window_title_rect.x = pane.floating_pos.x;
3423 window_title_rect.y = pane.floating_pos.y;
3424 window_title_rect.width = pane.floating_size.x;
3425 window_title_rect.height = 30;
3426
3427 wxRect ClientRect = wxGetClientDisplayRect();
3428 ClientRect.Deflate(
3429 60, 60); // Prevent the new window from being too close to the edge
3430 if (!ClientRect.Intersects(window_title_rect)) b_reset_pos = true;
3431
3432#endif
3433
3434 if (b_reset_pos) pane.FloatingPosition(50, 50);
3435
3436 if (cnt == 0)
3437 if (b_anyviz)
3438 pane.Show(cont->m_bIsVisible);
3439 else {
3440 cont->m_bIsVisible = cont->m_bPersVisible;
3441 pane.Show(cont->m_bIsVisible);
3442 }
3443 else
3444 pane.Show(false);
3445 }
3446
3447 // Restore size of docked pane
3448 if (pane.IsShown() && pane.IsDocked()) {
3449 pane.BestSize(cont->m_best_size);
3450 m_pauimgr->Update();
3451 }
3452
3453 // This patch fixes a bug in wxAUIManager
3454 // FS#548
3455 // Dropping a DashBoard Window right on top on the (supposedly fixed)
3456 // chart bar window causes a resize of the chart bar, and the Dashboard
3457 // window assumes some of its properties The Dashboard window is no longer
3458 // grabbable... Workaround: detect this case, and force the pane to be on
3459 // a different Row. so that the display is corrected by toggling the
3460 // dashboard off and back on.
3461 if ((pane.dock_direction == wxAUI_DOCK_BOTTOM) && pane.IsDocked())
3462 pane.Row(2);
3463 }
3464 }
3465 // Toggle is handled by the toolbar but we must keep plugin manager b_toggle
3466 // updated to actual status to ensure right status upon toolbar rebuild
3467 SetToolbarItemState(m_toolbar_item_id,
3468 GetDashboardWindowShownCount() != 0 /*cnt==0*/);
3469 m_pauimgr->Update();
3470}
3471
3473 // This method is called after the PlugIn is initialized
3474 // and the frame has done its initial layout, possibly from a saved
3475 // wxAuiManager "Perspective" It is a chance for the PlugIn to syncronize
3476 // itself internally with the state of any Panes that were added to the
3477 // frame in the PlugIn ctor.
3478
3479 for (size_t i = 0; i < m_ArrayOfDashboardWindow.GetCount(); i++) {
3480 DashboardWindowContainer *cont = m_ArrayOfDashboardWindow.Item(i);
3481 wxAuiPaneInfo &pane = m_pauimgr->GetPane(cont->m_pDashboardWindow);
3482 // Initialize visible state as perspective is loaded now
3483 cont->m_bIsVisible = (pane.IsOk() && pane.IsShown());
3484
3485 // Correct for incomplete AUIManager perspective when docked dashboard is
3486 // not visible at app close.
3487 if (pane.IsDocked()) {
3488 if ((cont->m_persist_size.x > 50) && (cont->m_persist_size.y > 50))
3489 cont->m_pDashboardWindow->SetSize(cont->m_persist_size);
3490 }
3491
3492#ifdef __WXQT__
3493 if (pane.IsShown()) {
3494 pane.Show(false);
3495 m_pauimgr->Update();
3496 pane.Show(true);
3497 m_pauimgr->Update();
3498 }
3499#endif
3500 }
3501 m_pauimgr->Update();
3502
3503 // We use this callback here to keep the context menu selection in sync
3504 // with the window state
3505
3506 SetToolbarItemState(m_toolbar_item_id, GetDashboardWindowShownCount() != 0);
3507}
3508
3509bool dashboard_pi::LoadConfig(void) {
3510 wxFileConfig *pConf = (wxFileConfig *)m_pconfig;
3511
3512 if (pConf) {
3513 pConf->SetPath(_T("/PlugIns/Dashboard"));
3514
3515 wxString version;
3516 pConf->Read(_T("Version"), &version, wxEmptyString);
3517 wxString config;
3518
3519 // Set some sensible defaults
3520 wxString TitleFont;
3521 wxString DataFont;
3522 wxString LabelFont;
3523 wxString SmallFont;
3524
3525#ifdef __OCPN__ANDROID__
3526 TitleFont = _T("Roboto,16,-1,5,50,0,0,0,0,0");
3527 DataFont = _T("Roboto,16,-1,5,50,0,0,0,0,0");
3528 LabelFont = _T("Roboto,16,-1,5,50,0,0,0,0,0");
3529 SmallFont = _T("Roboto,14,-1,5,50,0,0,0,0,0");
3530#else
3531 TitleFont = g_pFontTitle->GetChosenFont().GetNativeFontInfoDesc();
3532 DataFont = g_pFontData->GetChosenFont().GetNativeFontInfoDesc();
3533 LabelFont = g_pFontLabel->GetChosenFont().GetNativeFontInfoDesc();
3534 SmallFont = g_pFontSmall->GetChosenFont().GetNativeFontInfoDesc();
3535#endif
3536
3537 double scaler = 1.0;
3538 wxFont DummyFont;
3539 wxFont *pDF = &DummyFont;
3540
3541 if (OCPN_GetWinDIPScaleFactor() < 1.0)
3542 scaler = 1.0 + OCPN_GetWinDIPScaleFactor() / 4;
3543 scaler = wxMax(1.0, scaler);
3544
3545 g_pFontTitle = &g_FontTitle;
3546 pConf->Read(_T("FontTitle"), &config, TitleFont);
3547 LoadFont(&pDF, config);
3548 wxFont DummyFontTitle = *pDF;
3549 pConf->Read(_T("ColorTitle"), &config, "#000000");
3550 wxColour DummyColor(config);
3551 g_pUSFontTitle->SetChosenFont(DummyFontTitle);
3552 g_pUSFontTitle->SetColour(DummyColor);
3553
3554 g_FontTitle = *g_pUSFontTitle;
3555 g_FontTitle.SetChosenFont(g_pUSFontTitle->GetChosenFont().Scaled(scaler));
3556 g_USFontTitle = *g_pUSFontTitle;
3557
3558 g_pFontData = &g_FontData;
3559 pConf->Read(_T("FontData"), &config, DataFont);
3560 LoadFont(&pDF, config);
3561 wxFont DummyFontData = *pDF;
3562 pConf->Read(_T("ColorData"), &config, "#000000");
3563 DummyColor.Set(config);
3564 g_pUSFontData->SetChosenFont(DummyFontData);
3565 g_pUSFontData->SetColour(DummyColor);
3566 g_FontData = *g_pUSFontData;
3567 g_FontData.SetChosenFont(g_pUSFontData->GetChosenFont().Scaled(scaler));
3568 g_USFontData = *g_pUSFontData;
3569
3570 pConf->Read(_T("ForceBackgroundColor"), &g_ForceBackgroundColor, 0);
3571 pConf->Read(_T("BackgroundColor"), &config, "DASHL");
3572 g_BackgroundColor.Set(config);
3573
3574 int alignment;
3575 pConf->Read(_T("TitleAlignment"), &alignment, (int)wxALIGN_LEFT);
3576 g_TitleAlignment = (wxAlignment)alignment;
3577 if (g_TitleAlignment == wxALIGN_INVALID) g_TitleAlignment = wxALIGN_LEFT;
3578 pConf->Read(_T("TitleMargin"), &g_iTitleMargin, 5);
3579 pConf->Read(_T("DataShowUnit"), &g_bShowUnit, true);
3580 pConf->Read(_T("DataAlignment"), &alignment, (int)wxALIGN_LEFT);
3581 g_DataAlignment = (wxAlignment)alignment;
3582 if (g_DataAlignment == wxALIGN_INVALID) g_DataAlignment = wxALIGN_LEFT;
3583 pConf->Read(_T("DataMargin"), &g_iDataMargin, 10);
3584 pConf->Read(_T("InstrumentSpacing"), &g_iInstrumentSpacing, 0);
3585 pConf->Read(_T("TitleVerticalOffset"), &g_TitleVerticalOffset, 0.0);
3586
3587 g_pFontLabel = &g_FontLabel;
3588 pConf->Read(_T("FontLabel"), &config, LabelFont);
3589 LoadFont(&pDF, config);
3590 wxFont DummyFontLabel = *pDF;
3591 pConf->Read(_T("ColorLabel"), &config, "#000000");
3592 DummyColor.Set(config);
3593 g_pUSFontLabel->SetChosenFont(DummyFontLabel);
3594 g_pUSFontLabel->SetColour(DummyColor);
3595 g_FontLabel = *g_pUSFontLabel;
3596 g_FontLabel.SetChosenFont(g_pUSFontLabel->GetChosenFont().Scaled(scaler));
3597 g_USFontLabel = *g_pUSFontLabel;
3598
3599 g_pFontSmall = &g_FontSmall;
3600 pConf->Read(_T("FontSmall"), &config, SmallFont);
3601 LoadFont(&pDF, config);
3602 wxFont DummyFontSmall = *pDF;
3603 pConf->Read(_T("ColorSmall"), &config, "#000000");
3604 DummyColor.Set(config);
3605 g_pUSFontSmall->SetChosenFont(DummyFontSmall);
3606 g_pUSFontSmall->SetColour(DummyColor);
3607 g_FontSmall = *g_pUSFontSmall;
3608 g_FontSmall.SetChosenFont(g_pUSFontSmall->GetChosenFont().Scaled(scaler));
3609 g_USFontSmall = *g_pUSFontSmall;
3610
3611 pConf->Read(_T("SpeedometerMax"), &g_iDashSpeedMax, 12);
3612 pConf->Read(_T("COGDamp"), &g_iDashCOGDamp, 0);
3613 pConf->Read(_T("SpeedUnit"), &g_iDashSpeedUnit, 0);
3614 pConf->Read(_T("SOGDamp"), &g_iDashSOGDamp, 0);
3615 pConf->Read(_T("DepthUnit"), &g_iDashDepthUnit, 3);
3616 g_iDashDepthUnit = wxMax(g_iDashDepthUnit, 3);
3617 pConf->Read(_T("AWADamp"), &g_iDashAWADamp, 0);
3618 pConf->Read(_T("AWSDamp"), &g_iDashAWSDamp, 0);
3619
3620 pConf->Read(_T("DepthOffset"), &g_dDashDBTOffset, 0);
3621
3622 pConf->Read(_T("DistanceUnit"), &g_iDashDistanceUnit, 0);
3623 pConf->Read(_T("WindSpeedUnit"), &g_iDashWindSpeedUnit, 0);
3624 pConf->Read(_T("UseSignKtruewind"), &g_bDBtrueWindGround, 0);
3625 pConf->Read("UseInternSumlog", &g_bUseInternSumLog, 0);
3626 pConf->Read("SumLogNM", &g_dSumLogNM, 0.0);
3627 pConf->Read(_T("TemperatureUnit"), &g_iDashTempUnit, 0);
3628
3629 pConf->Read(_T("UTCOffset"), &g_iUTCOffset, 0);
3630
3631 pConf->Read(_T("PrefWidth"), &g_dashPrefWidth, 0);
3632 pConf->Read(_T("PrefHeight"), &g_dashPrefHeight, 0);
3633
3634 int d_cnt;
3635 pConf->Read(_T("DashboardCount"), &d_cnt, -1);
3636 // TODO: Memory leak? We should destroy everything first
3637 m_ArrayOfDashboardWindow.Clear();
3638 if (version.IsEmpty() && d_cnt == -1) {
3639 m_config_version = 1;
3640 // Let's load version 1 or default settings.
3641 int i_cnt;
3642 pConf->Read(_T("InstrumentCount"), &i_cnt, -1);
3643 wxArrayInt ar;
3644 wxArrayOfInstrumentProperties Property;
3645 if (i_cnt != -1) {
3646 for (int i = 0; i < i_cnt; i++) {
3647 int id;
3648 pConf->Read(wxString::Format(_T("Instrument%d"), i + 1), &id, -1);
3649 if (id != -1) ar.Add(id);
3650 }
3651 } else {
3652 // This is the default instrument list
3653#ifndef __OCPN__ANDROID__
3654 ar.Add(ID_DBP_I_POS);
3655 ar.Add(ID_DBP_D_COG);
3656 ar.Add(ID_DBP_D_GPS);
3657#else
3658 ar.Add(ID_DBP_I_POS);
3659 ar.Add(ID_DBP_D_COG);
3660 ar.Add(ID_DBP_I_SOG);
3661
3662#endif
3663 }
3664
3666 NULL, MakeName(), _("Dashboard"), _T("V"), ar, Property);
3667 cont->m_bPersVisible = true;
3668 m_ArrayOfDashboardWindow.Add(cont);
3669
3670 } else {
3671 // Version 2
3672 m_config_version = 2;
3673 bool b_onePersisted = false;
3674 wxSize best_size;
3675 wxSize persist_size;
3676 for (int k = 0; k < d_cnt; k++) {
3677 pConf->SetPath(
3678 wxString::Format(_T("/PlugIns/Dashboard/Dashboard%d"), k + 1));
3679 wxString name;
3680 pConf->Read(_T("Name"), &name, MakeName());
3681 wxString caption;
3682 pConf->Read(_T("Caption"), &caption, _("Dashboard"));
3683 wxString orient;
3684 pConf->Read(_T("Orientation"), &orient, _T("V"));
3685 int i_cnt;
3686 pConf->Read(_T("InstrumentCount"), &i_cnt, -1);
3687 bool b_persist;
3688 pConf->Read(_T("Persistence"), &b_persist, 1);
3689 int val;
3690 pConf->Read(_T("BestSizeX"), &val, DefaultWidth);
3691 best_size.x = val;
3692 pConf->Read(_T("BestSizeY"), &val, DefaultWidth);
3693 best_size.y = val;
3694 pConf->Read(_T("PersistSizeX"), &val, DefaultWidth);
3695 persist_size.x = val;
3696 pConf->Read(_T("PersistSizeY"), &val, DefaultWidth);
3697 persist_size.y = val;
3698
3699 wxArrayInt ar;
3700 wxArrayOfInstrumentProperties Property;
3701 for (int i = 0; i < i_cnt; i++) {
3702 int id;
3703 pConf->Read(wxString::Format(_T("Instrument%d"), i + 1), &id, -1);
3704 if (id != -1) {
3705 ar.Add(id);
3706 InstrumentProperties *instp;
3707 if (pConf->Exists(wxString::Format(_T("InstTitleFont%d"), i + 1))) {
3708 instp = new InstrumentProperties(id, i);
3709
3710 pConf->Read(wxString::Format(_T("InstTitleFont%d"), i + 1),
3711 &config, TitleFont);
3712 LoadFont(&pDF, config);
3713 wxFont DummyFontTitleA = *pDF;
3714 pConf->Read(wxString::Format(_T("InstTitleColor%d"), i + 1),
3715 &config, "#000000");
3716 DummyColor.Set(config);
3717 instp->m_USTitleFont.SetChosenFont(DummyFontTitleA);
3718 instp->m_USTitleFont.SetColour(DummyColor);
3719 instp->m_TitleFont = instp->m_USTitleFont;
3720 instp->m_TitleFont.SetChosenFont(
3721 instp->m_USTitleFont.GetChosenFont().Scaled(scaler));
3722
3723 pConf->Read(wxString::Format(_T("InstDataShowUnit%d"), i + 1),
3724 &instp->m_ShowUnit, -1);
3725 pConf->Read(wxString::Format(_T("InstDataMargin%d"), i + 1),
3726 &instp->m_DataMargin, -1);
3727 pConf->Read(wxString::Format(_T("InstDataAlignment%d"), i + 1),
3728 &alignment, (int)wxALIGN_INVALID);
3729 instp->m_DataAlignment = (wxAlignment)alignment;
3730 pConf->Read(
3731 wxString::Format(_T("InstInstrumentSpacing%d"), i + 1),
3732 &instp->m_InstrumentSpacing, -1);
3733 pConf->Read(wxString::Format(_T("InstDataFormat%d"), i + 1),
3734 &instp->m_Format, "");
3735 pConf->Read(wxString::Format(_T("InstTitle%d"), i + 1),
3736 &instp->m_Title, "");
3737
3738 pConf->Read(wxString::Format(_T("InstDataFont%d"), i + 1),
3739 &config, DataFont);
3740 LoadFont(&pDF, config);
3741 wxFont DummyFontDataA = *pDF;
3742 pConf->Read(wxString::Format(_T("InstDataColor%d"), i + 1),
3743 &config, "#000000");
3744 DummyColor.Set(config);
3745 instp->m_USDataFont.SetChosenFont(DummyFontDataA);
3746 instp->m_USDataFont.SetColour(DummyColor);
3747 instp->m_DataFont = instp->m_USDataFont;
3748 instp->m_DataFont.SetChosenFont(
3749 instp->m_USDataFont.GetChosenFont().Scaled(scaler));
3750
3751 pConf->Read(wxString::Format(_T("InstLabelFont%d"), i + 1),
3752 &config, LabelFont);
3753 LoadFont(&pDF, config);
3754 wxFont DummyFontLabelA = *pDF;
3755 pConf->Read(wxString::Format(_T("InstLabelColor%d"), i + 1),
3756 &config, "#000000");
3757 DummyColor.Set(config);
3758 instp->m_USLabelFont.SetChosenFont(DummyFontLabelA);
3759 instp->m_USLabelFont.SetColour(DummyColor);
3760 instp->m_LabelFont = instp->m_USLabelFont;
3761 instp->m_LabelFont.SetChosenFont(
3762 instp->m_USLabelFont.GetChosenFont().Scaled(scaler));
3763
3764 pConf->Read(wxString::Format(_T("InstSmallFont%d"), i + 1),
3765 &config, SmallFont);
3766 LoadFont(&pDF, config);
3767 wxFont DummyFontSmallA = *pDF;
3768 pConf->Read(wxString::Format(_T("InstSmallColor%d"), i + 1),
3769 &config, "#000000");
3770 DummyColor.Set(config);
3771 instp->m_USSmallFont.SetChosenFont(DummyFontSmallA);
3772 instp->m_USSmallFont.SetColour(DummyColor);
3773 instp->m_SmallFont = instp->m_USSmallFont;
3774 instp->m_SmallFont.SetChosenFont(
3775 instp->m_USSmallFont.GetChosenFont().Scaled(scaler));
3776
3777 pConf->Read(wxString::Format(_T("TitleBackColor%d"), i + 1),
3778 &config, "DASHL");
3779 instp->m_TitleBackgroundColour.Set(config);
3780
3781 pConf->Read(wxString::Format(_T("DataBackColor%d"), i + 1),
3782 &config, "DASHB");
3783 instp->m_DataBackgroundColour.Set(config);
3784
3785 pConf->Read(wxString::Format(_T("ArrowFirst%d"), i + 1), &config,
3786 "DASHN");
3787 instp->m_Arrow_First_Colour.Set(config);
3788
3789 pConf->Read(wxString::Format(_T("ArrowSecond%d"), i + 1), &config,
3790 "BLUE3");
3791 instp->m_Arrow_Second_Colour.Set(config);
3792
3793 Property.Add(instp);
3794 }
3795 }
3796 }
3797 // TODO: Do not add if GetCount == 0
3798
3800 NULL, name, caption, orient, ar, Property);
3801 cont->m_bPersVisible = b_persist;
3802 cont->m_conf_best_size = best_size;
3803 cont->m_persist_size = persist_size;
3804
3805 if (b_persist) b_onePersisted = true;
3806
3807 m_ArrayOfDashboardWindow.Add(cont);
3808 }
3809
3810 // Make sure at least one dashboard is scheduled to be visible
3811 if (m_ArrayOfDashboardWindow.Count() && !b_onePersisted) {
3812 DashboardWindowContainer *cont = m_ArrayOfDashboardWindow.Item(0);
3813 if (cont) cont->m_bPersVisible = true;
3814 }
3815 }
3816
3817 return true;
3818 } else
3819 return false;
3820}
3821
3822void dashboard_pi::LoadFont(wxFont **target, wxString native_info) {
3823 if (!native_info.IsEmpty()) {
3824#ifdef __OCPN__ANDROID__
3825 wxFont *nf = new wxFont(native_info);
3826 *target = nf;
3827#else
3828 (*target)->SetNativeFontInfo(native_info);
3829#endif
3830 }
3831}
3832
3833bool dashboard_pi::SaveConfig(void) {
3834 wxFileConfig *pConf = (wxFileConfig *)m_pconfig;
3835
3836 if (pConf) {
3837 pConf->SetPath(_T("/PlugIns/Dashboard"));
3838 pConf->Write(_T("Version"), _T("2"));
3839 pConf->Write(_T("FontTitle"),
3840 g_pUSFontTitle->GetChosenFont().GetNativeFontInfoDesc());
3841 pConf->Write(_T("ColorTitle"),
3842 g_pUSFontTitle->GetColour().GetAsString(wxC2S_HTML_SYNTAX));
3843 pConf->Write(_T("FontData"),
3844 g_pUSFontData->GetChosenFont().GetNativeFontInfoDesc());
3845 pConf->Write(_T("ColorData"),
3846 g_pUSFontData->GetColour().GetAsString(wxC2S_HTML_SYNTAX));
3847 pConf->Write(_T("FontLabel"),
3848 g_pUSFontLabel->GetChosenFont().GetNativeFontInfoDesc());
3849 pConf->Write(_T("ColorLabel"),
3850 g_pUSFontLabel->GetColour().GetAsString(wxC2S_HTML_SYNTAX));
3851 pConf->Write(_T("FontSmall"),
3852 g_pUSFontSmall->GetChosenFont().GetNativeFontInfoDesc());
3853 pConf->Write(_T("ColorSmall"),
3854 g_pUSFontSmall->GetColour().GetAsString(wxC2S_HTML_SYNTAX));
3855 pConf->Write(_T("SpeedometerMax"), g_iDashSpeedMax);
3856 pConf->Write(_T("COGDamp"), g_iDashCOGDamp);
3857 pConf->Write(_T("SpeedUnit"), g_iDashSpeedUnit);
3858 pConf->Write(_T("SOGDamp"), g_iDashSOGDamp);
3859 pConf->Write(_T("AWSDamp"), g_iDashAWSDamp);
3860 pConf->Write(_T("AWADamp"), g_iDashAWADamp);
3861 pConf->Write(_T("DepthUnit"), g_iDashDepthUnit);
3862 pConf->Write(_T("DepthOffset"), g_dDashDBTOffset);
3863 pConf->Write(_T("DistanceUnit"), g_iDashDistanceUnit);
3864 pConf->Write(_T("WindSpeedUnit"), g_iDashWindSpeedUnit);
3865 pConf->Write("UseInternSumlog", g_bUseInternSumLog);
3866 pConf->Write("SumLogNM", g_dSumLogNM);
3867 pConf->Write(_T("UTCOffset"), g_iUTCOffset);
3868 pConf->Write(_T("UseSignKtruewind"), g_bDBtrueWindGround);
3869 pConf->Write(_T("TemperatureUnit"), g_iDashTempUnit);
3870 pConf->Write(_T("PrefWidth"), g_dashPrefWidth);
3871 pConf->Write(_T("PrefHeight"), g_dashPrefHeight);
3872
3873 pConf->Write(_T("DashboardCount" ),
3874 (int)m_ArrayOfDashboardWindow.GetCount());
3875 // Delete old Dashborads
3876 for (size_t i = m_ArrayOfDashboardWindow.GetCount(); i < 20; i++) {
3877 if (pConf->Exists(
3878 wxString::Format(_T("/PlugIns/Dashboard/Dashboard%zu"), i + 1))) {
3879 pConf->DeleteGroup(
3880 wxString::Format(_T("/PlugIns/Dashboard/Dashboard%zu"), i + 1));
3881 }
3882 }
3883 for (size_t i = 0; i < m_ArrayOfDashboardWindow.GetCount(); i++) {
3884 DashboardWindowContainer *cont = m_ArrayOfDashboardWindow.Item(i);
3885 pConf->SetPath(
3886 wxString::Format(_T("/PlugIns/Dashboard/Dashboard%zu"), i + 1));
3887 pConf->Write(_T("Name"), cont->m_sName);
3888 pConf->Write(_T("Caption"), cont->m_sCaption);
3889 pConf->Write(_T("Orientation"), cont->m_sOrientation);
3890 pConf->Write(_T("Persistence"), cont->m_bPersVisible);
3891 pConf->Write(_T("InstrumentCount"),
3892 (int)cont->m_aInstrumentList.GetCount());
3893 pConf->Write(_T("BestSizeX"), cont->m_best_size.x);
3894 pConf->Write(_T("BestSizeY"), cont->m_best_size.y);
3895 pConf->Write(_T("PersistSizeX"), cont->m_pDashboardWindow->GetSize().x);
3896 pConf->Write(_T("PersistSizeY"), cont->m_pDashboardWindow->GetSize().y);
3897
3898 // Delete old Instruments
3899 for (size_t i = cont->m_aInstrumentList.GetCount(); i < 40; i++) {
3900 if (pConf->Exists(wxString::Format(_T("Instrument%zu"), i + 1))) {
3901 pConf->DeleteEntry(wxString::Format(_T("Instrument%zu"), i + 1));
3902 if (pConf->Exists(wxString::Format(_T("InstTitleFont%zu"), i + 1))) {
3903 pConf->DeleteEntry(wxString::Format(_T("InstTitleFont%zu"), i + 1));
3904 pConf->DeleteEntry(
3905 wxString::Format(_T("InstTitleColor%zu"), i + 1));
3906 pConf->DeleteEntry(wxString::Format(_T("InstTitle%zu"), i + 1));
3907 pConf->DeleteEntry(
3908 wxString::Format(_T("InstDataShowUnit%zu"), i + 1));
3909 pConf->DeleteEntry(
3910 wxString::Format(_T("InstDataMargin%zu"), i + 1));
3911 pConf->DeleteEntry(
3912 wxString::Format(_T("InstDataAlignment%zu"), i + 1));
3913 pConf->DeleteEntry(
3914 wxString::Format(_T("InstDataFormat%zu"), i + 1));
3915 pConf->DeleteEntry(wxString::Format(_T("InstDataFont%zu"), i + 1));
3916 pConf->DeleteEntry(wxString::Format(_T("InstDataColor%zu"), i + 1));
3917 pConf->DeleteEntry(wxString::Format(_T("InstLabelFont%zu"), i + 1));
3918 pConf->DeleteEntry(
3919 wxString::Format(_T("InstLabelColor%zu"), i + 1));
3920 pConf->DeleteEntry(wxString::Format(_T("InstSmallFont%zu"), i + 1));
3921 pConf->DeleteEntry(
3922 wxString::Format(_T("InstSmallColor%zu"), i + 1));
3923 pConf->DeleteEntry(
3924 wxString::Format(_T("TitleBackColor%zu"), i + 1));
3925 pConf->DeleteEntry(wxString::Format(_T("DataBackColor%zu"), i + 1));
3926 pConf->DeleteEntry(wxString::Format(_T("ArrowFirst%zu"), i + 1));
3927 pConf->DeleteEntry(wxString::Format(_T("ArrowSecond%zu"), i + 1));
3928 }
3929 }
3930 }
3931 for (size_t j = 0; j < cont->m_aInstrumentList.GetCount(); j++) {
3932 pConf->Write(wxString::Format(_T("Instrument%zu"), j + 1),
3933 cont->m_aInstrumentList.Item(j));
3934 InstrumentProperties *Inst = NULL;
3935 // First delete
3936 if (pConf->Exists(wxString::Format(_T("InstTitleFont%zu"), j + 1))) {
3937 bool Delete = true;
3938 for (size_t i = 0; i < cont->m_aInstrumentPropertyList.GetCount();
3939 i++) {
3940 Inst = cont->m_aInstrumentPropertyList.Item(i);
3941 if (Inst->m_Listplace == (int)j) {
3942 Delete = false;
3943 break;
3944 }
3945 }
3946 if (Delete) {
3947 pConf->DeleteEntry(wxString::Format(_T("InstTitleFont%zu"), j + 1));
3948 pConf->DeleteEntry(
3949 wxString::Format(_T("InstTitleColor%zu"), j + 1));
3950 pConf->DeleteEntry(wxString::Format(_T("InstTitle%zu"), j + 1));
3951 pConf->DeleteEntry(
3952 wxString::Format(_T("InstDataShowUnit%zu"), i + 1));
3953 pConf->DeleteEntry(
3954 wxString::Format(_T("InstDataMargin%zu"), i + 1));
3955 pConf->DeleteEntry(
3956 wxString::Format(_T("InstDataAlignment%zu"), i + 1));
3957 pConf->DeleteEntry(
3958 wxString::Format(_T("InstDataFormat%zu"), i + 1));
3959 pConf->DeleteEntry(wxString::Format(_T("InstDataFont%zu"), j + 1));
3960 pConf->DeleteEntry(wxString::Format(_T("InstDataColor%zu"), j + 1));
3961 pConf->DeleteEntry(wxString::Format(_T("InstLabelFont%zu"), j + 1));
3962 pConf->DeleteEntry(
3963 wxString::Format(_T("InstLabelColor%zu"), j + 1));
3964 pConf->DeleteEntry(wxString::Format(_T("InstSmallFont%zu"), j + 1));
3965 pConf->DeleteEntry(
3966 wxString::Format(_T("InstSmallColor%zu"), j + 1));
3967 pConf->DeleteEntry(
3968 wxString::Format(_T("TitleBackColor%zu"), i + 1));
3969 pConf->DeleteEntry(wxString::Format(_T("DataBackColor%zu"), i + 1));
3970 pConf->DeleteEntry(wxString::Format(_T("ArrowFirst%zu"), i + 1));
3971 pConf->DeleteEntry(wxString::Format(_T("ArrowSecond%zu"), i + 1));
3972 }
3973 }
3974 Inst = NULL;
3975 for (size_t i = 0; i < (cont->m_aInstrumentPropertyList.GetCount());
3976 i++) {
3977 Inst = cont->m_aInstrumentPropertyList.Item(i);
3978 if (Inst->m_Listplace == (int)j) {
3979 pConf->Write(
3980 wxString::Format(_T("InstTitleFont%zu"), j + 1),
3981 Inst->m_USTitleFont.GetChosenFont().GetNativeFontInfoDesc());
3982 pConf->Write(
3983 wxString::Format(_T("InstTitleColor%zu"), j + 1),
3984 Inst->m_USTitleFont.GetColour().GetAsString(wxC2S_HTML_SYNTAX));
3985 pConf->Write(
3986 wxString::Format(_T("InstDataFont%zu"), j + 1),
3987 Inst->m_USDataFont.GetChosenFont().GetNativeFontInfoDesc());
3988 pConf->Write(
3989 wxString::Format(_T("InstDataColor%zu"), j + 1),
3990 Inst->m_USDataFont.GetColour().GetAsString(wxC2S_HTML_SYNTAX));
3991 pConf->Write(
3992 wxString::Format(_T("InstLabelFont%zu"), j + 1),
3993 Inst->m_USLabelFont.GetChosenFont().GetNativeFontInfoDesc());
3994 pConf->Write(
3995 wxString::Format(_T("InstLabelColor%zu"), j + 1),
3996 Inst->m_USLabelFont.GetColour().GetAsString(wxC2S_HTML_SYNTAX));
3997 pConf->Write(
3998 wxString::Format(_T("InstSmallFont%zu"), j + 1),
3999 Inst->m_USSmallFont.GetChosenFont().GetNativeFontInfoDesc());
4000 pConf->Write(
4001 wxString::Format(_T("InstSmallColor%zu"), j + 1),
4002 Inst->m_USSmallFont.GetColour().GetAsString(wxC2S_HTML_SYNTAX));
4003 pConf->Write(
4004 wxString::Format(_T("TitleBackColor%zu"), j + 1),
4005 Inst->m_TitleBackgroundColour.GetAsString(wxC2S_HTML_SYNTAX));
4006 pConf->Write(
4007 wxString::Format(_T("DataBackColor%zu"), j + 1),
4008 Inst->m_DataBackgroundColour.GetAsString(wxC2S_HTML_SYNTAX));
4009 pConf->Write(
4010 wxString::Format(_T("ArrowFirst%zu"), j + 1),
4011 Inst->m_Arrow_First_Colour.GetAsString(wxC2S_HTML_SYNTAX));
4012 pConf->Write(
4013 wxString::Format(_T("ArrowSecond%zu"), j + 1),
4014 Inst->m_Arrow_Second_Colour.GetAsString(wxC2S_HTML_SYNTAX));
4015 break;
4016 }
4017 }
4018 }
4019 }
4020 pConf->Flush();
4021 return true;
4022 } else
4023 return false;
4024}
4025
4026void dashboard_pi::ApplyConfig(void) {
4027 // Reverse order to handle deletes
4028 for (size_t i = m_ArrayOfDashboardWindow.GetCount(); i > 0; i--) {
4029 DashboardWindowContainer *cont = m_ArrayOfDashboardWindow.Item(i - 1);
4030 int orient = (cont->m_sOrientation == _T("V") ? wxVERTICAL : wxHORIZONTAL);
4031 if (cont->m_bIsDeleted) {
4032 if (cont->m_pDashboardWindow) {
4033 m_pauimgr->DetachPane(cont->m_pDashboardWindow);
4034 cont->m_pDashboardWindow->Close();
4035 cont->m_pDashboardWindow->Destroy();
4036 cont->m_pDashboardWindow = NULL;
4037 }
4038 m_ArrayOfDashboardWindow.Remove(cont);
4039 delete cont;
4040
4041 } else if (!cont->m_pDashboardWindow) {
4042 // A new dashboard is created
4043 cont->m_pDashboardWindow = new DashboardWindow(
4044 GetOCPNCanvasWindow(), wxID_ANY, m_pauimgr, this, orient, cont);
4045 cont->m_pDashboardWindow->SetInstrumentList(
4046 cont->m_aInstrumentList, &(cont->m_aInstrumentPropertyList));
4047 bool vertical = orient == wxVERTICAL;
4048 wxSize sz = cont->m_pDashboardWindow->GetMinSize();
4049 wxSize best = cont->m_conf_best_size;
4050 if (best.x < 100) best = sz;
4051
4052// Mac has a little trouble with initial Layout() sizing...
4053#ifdef __WXOSX__
4054 if (sz.x == 0) sz.IncTo(wxSize(160, 388));
4055#endif
4056 wxAuiPaneInfo p = wxAuiPaneInfo()
4057 .Name(cont->m_sName)
4058 .Caption(cont->m_sCaption)
4059 .CaptionVisible(false)
4060 .TopDockable(!vertical)
4061 .BottomDockable(!vertical)
4062 .LeftDockable(vertical)
4063 .RightDockable(vertical)
4064 .MinSize(sz)
4065 .BestSize(best)
4066 .FloatingSize(sz)
4067 .FloatingPosition(100, 100)
4068 .Float()
4069 .Show(cont->m_bIsVisible)
4070 .Gripper(false);
4071
4072 m_pauimgr->AddPane(cont->m_pDashboardWindow, p);
4073 // wxAuiPaneInfo().Name( cont->m_sName ).Caption( cont->m_sCaption
4074 // ).CaptionVisible( false ).TopDockable(
4075 // !vertical ).BottomDockable( !vertical ).LeftDockable( vertical
4076 // ).RightDockable( vertical ).MinSize( sz ).BestSize( sz ).FloatingSize(
4077 // sz ).FloatingPosition( 100, 100 ).Float().Show( cont->m_bIsVisible ) );
4078
4079#ifdef __OCPN__ANDROID__
4080 wxAuiPaneInfo &pane = m_pauimgr->GetPane(cont->m_pDashboardWindow);
4081 pane.Dockable(false);
4082
4083#endif
4084
4085 } else {
4086 wxAuiPaneInfo &pane = m_pauimgr->GetPane(cont->m_pDashboardWindow);
4087 pane.Caption(cont->m_sCaption).Show(cont->m_bIsVisible);
4088 if (!cont->m_pDashboardWindow->isInstrumentListEqual(
4089 cont->m_aInstrumentList)) {
4090 cont->m_pDashboardWindow->SetInstrumentList(
4091 cont->m_aInstrumentList, &(cont->m_aInstrumentPropertyList));
4092 wxSize sz = cont->m_pDashboardWindow->GetMinSize();
4093 pane.MinSize(sz).BestSize(sz).FloatingSize(sz);
4094 }
4095 if (cont->m_pDashboardWindow->GetSizerOrientation() != orient) {
4096 cont->m_pDashboardWindow->ChangePaneOrientation(orient, false);
4097 }
4098 }
4099 }
4100 m_pauimgr->Update();
4101
4102 // Initialize the IIR Filter Co-efficients
4103 double sogFC = g_iDashSOGDamp ? 1.0 / (2.0 * g_iDashSOGDamp) : 0.0;
4104 double cogFC = g_iDashCOGDamp ? 1.0 / (2.0 * g_iDashCOGDamp) : 0.0;
4105 double awaFC = g_iDashAWADamp ? 1.0 / (2.0 * g_iDashAWADamp) : 0.0;
4106 double awsFC = g_iDashAWSDamp ? 1.0 / (2.0 * g_iDashAWSDamp) : 0.0;
4107
4108 if (abs(sogFC - mSOGFilter.getFc()) > 1e-6) {
4109 mSOGFilter.setFC(sogFC);
4110 }
4111 if (abs(cogFC - mCOGFilter.getFc()) > 1e-6) {
4112 mCOGFilter.setFC(cogFC);
4113 }
4114 if (abs(awaFC - mAWAFilter.getFc()) > 1e-6) {
4115 mAWAFilter.setFC(awaFC);
4116 }
4117 if (abs(awsFC - mAWSFilter.getFc()) > 1e-6) {
4118 mAWSFilter.setFC(awsFC);
4119 }
4120}
4121
4122void dashboard_pi::PopulateContextMenu(wxMenu *menu) {
4123 int nvis = 0;
4124 wxMenuItem *visItem = 0;
4125 for (size_t i = 0; i < m_ArrayOfDashboardWindow.GetCount(); i++) {
4126 DashboardWindowContainer *cont = m_ArrayOfDashboardWindow.Item(i);
4127 wxMenuItem *item = menu->AppendCheckItem(i + 1, cont->m_sCaption);
4128 item->Check(cont->m_bIsVisible);
4129 if (cont->m_bIsVisible) {
4130 nvis++;
4131 visItem = item;
4132 }
4133 }
4134 if (nvis == 1 && visItem) visItem->Enable(false);
4135}
4136
4137void dashboard_pi::ShowDashboard(size_t id, bool visible) {
4138 if (id < m_ArrayOfDashboardWindow.GetCount()) {
4139 DashboardWindowContainer *cont = m_ArrayOfDashboardWindow.Item(id);
4140 m_pauimgr->GetPane(cont->m_pDashboardWindow).Show(visible);
4141 cont->m_bIsVisible = visible;
4142 cont->m_bPersVisible = visible;
4143 m_pauimgr->Update();
4144 }
4145}
4146
4147/* DashboardPreferencesDialog
4148 *
4149 */
4150
4151DashboardPreferencesDialog::DashboardPreferencesDialog(
4152 wxWindow *parent, wxWindowID id, wxArrayOfDashboard config)
4153 : wxDialog(parent, id, _("Dashboard preferences"), wxDefaultPosition,
4154 wxDefaultSize, wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER) {
4155#ifdef __WXQT__
4156 wxFont *pF = OCPNGetFont(_("Dialog"));
4157 SetFont(*pF);
4158#endif
4159
4160 int display_width, display_height;
4161 wxDisplaySize(&display_width, &display_height);
4162
4163 wxString shareLocn = *GetpSharedDataLocation() + _T("plugins") +
4164 wxFileName::GetPathSeparator() + _T("dashboard_pi") +
4165 wxFileName::GetPathSeparator() + _T("data") +
4166 wxFileName::GetPathSeparator();
4167
4168 Connect(wxEVT_CLOSE_WINDOW,
4169 wxCloseEventHandler(DashboardPreferencesDialog::OnCloseDialog), NULL,
4170 this);
4171
4172 // Copy original config
4173 m_Config = wxArrayOfDashboard(config);
4174 // Build Dashboard Page for Toolbox
4175 int border_size = 2;
4176
4177 wxBoxSizer *itemBoxSizerMainPanel = new wxBoxSizer(wxVERTICAL);
4178 SetSizer(itemBoxSizerMainPanel);
4179
4180 wxWindow *dparent = this;
4181 wxScrolledWindow *scrollWin = new wxScrolledWindow(
4182 this, wxID_ANY, wxDefaultPosition, wxSize(-1, -1), wxVSCROLL | wxHSCROLL);
4183
4184 scrollWin->SetScrollRate(1, 1);
4185 itemBoxSizerMainPanel->Add(scrollWin, 1, wxEXPAND | wxALL, 0);
4186
4187 dparent = scrollWin;
4188
4189 wxBoxSizer *itemBoxSizer2 = new wxBoxSizer(wxVERTICAL);
4190 scrollWin->SetSizer(itemBoxSizer2);
4191
4192 auto *DialogButtonSizer = new wxStdDialogButtonSizer();
4193 DialogButtonSizer->AddButton(new wxButton(this, wxID_OK));
4194 DialogButtonSizer->AddButton(new wxButton(this, wxID_CANCEL));
4195 wxButton *help_btn = new wxButton(this, wxID_HELP);
4196 help_btn->Bind(wxEVT_COMMAND_BUTTON_CLICKED, [&](wxCommandEvent) {
4197 wxString datadir = GetPluginDataDir("manual_pi");
4198 Manual(this, datadir.ToStdString()).Launch("Dashboard");
4199 });
4200 DialogButtonSizer->AddButton(help_btn);
4201 DialogButtonSizer->Realize();
4202
4203 itemBoxSizerMainPanel->Add(DialogButtonSizer, 0, wxALIGN_RIGHT | wxALL, 5);
4204
4205 wxNotebook *itemNotebook = new wxNotebook(
4206 dparent, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxNB_TOP);
4207 itemBoxSizer2->Add(itemNotebook, 0, wxALL | wxEXPAND, border_size);
4208
4209 wxPanel *itemPanelNotebook01 =
4210 new wxPanel(itemNotebook, wxID_ANY, wxDefaultPosition, wxDefaultSize,
4211 wxTAB_TRAVERSAL);
4212 wxFlexGridSizer *itemFlexGridSizer01 = new wxFlexGridSizer(2);
4213 itemFlexGridSizer01->AddGrowableCol(1);
4214 itemPanelNotebook01->SetSizer(itemFlexGridSizer01);
4215 itemNotebook->AddPage(itemPanelNotebook01, _("Dashboard"));
4216
4217 wxBoxSizer *itemBoxSizer01 = new wxBoxSizer(wxVERTICAL);
4218 itemFlexGridSizer01->Add(itemBoxSizer01, 1, wxEXPAND | wxTOP | wxLEFT,
4219 border_size);
4220
4221 // Scale the images in the dashboard list control
4222 int imageRefSize = GetCharWidth() * 4;
4223
4224 wxImageList *imglist1 = new wxImageList(imageRefSize, imageRefSize, true, 1);
4225
4226 wxBitmap bmDashBoard;
4227#ifdef ocpnUSE_SVG
4228 wxString filename = shareLocn + _T("Dashboard.svg");
4229 bmDashBoard = GetBitmapFromSVGFile(filename, imageRefSize, imageRefSize);
4230#else
4231 wxImage dash1 = wxBitmap(*_img_dashboard_pi).ConvertToImage();
4232 wxImage dash1s =
4233 dash1.Scale(imageRefSize, imageRefSize, wxIMAGE_QUALITY_HIGH);
4234 bmDashBoard = wxBitmap(dash1s);
4235#endif
4236
4237 imglist1->Add(bmDashBoard);
4238
4239 m_pListCtrlDashboards =
4240 new wxListCtrl(itemPanelNotebook01, wxID_ANY, wxDefaultPosition,
4241 wxSize(imageRefSize * 3 / 2, 200),
4242 wxLC_REPORT | wxLC_NO_HEADER | wxLC_SINGLE_SEL);
4243
4244 m_pListCtrlDashboards->AssignImageList(imglist1, wxIMAGE_LIST_SMALL);
4245 m_pListCtrlDashboards->InsertColumn(0, _T(""));
4246 m_pListCtrlDashboards->Connect(
4247 wxEVT_COMMAND_LIST_ITEM_SELECTED,
4248 wxListEventHandler(DashboardPreferencesDialog::OnDashboardSelected), NULL,
4249 this);
4250 m_pListCtrlDashboards->Connect(
4251 wxEVT_COMMAND_LIST_ITEM_DESELECTED,
4252 wxListEventHandler(DashboardPreferencesDialog::OnDashboardSelected), NULL,
4253 this);
4254 itemBoxSizer01->Add(m_pListCtrlDashboards, 1, wxEXPAND, 0);
4255
4256 wxBoxSizer *itemBoxSizer02 = new wxBoxSizer(wxHORIZONTAL);
4257 itemBoxSizer01->Add(itemBoxSizer02);
4258
4259 wxBitmap bmPlus, bmMinus;
4260 int bmSize = imageRefSize * 100 / 275;
4261
4262#ifdef __OCPN__ANDROID__
4263 bmSize = imageRefSize / 2;
4264#endif
4265
4266#ifdef ocpnUSE_SVG
4267 bmPlus = GetBitmapFromSVGFile(shareLocn + _T("plus.svg"), bmSize, bmSize);
4268 bmMinus = GetBitmapFromSVGFile(shareLocn + _T("minus.svg"), bmSize, bmSize);
4269#else
4270 wxImage plus1 = wxBitmap(*_img_plus).ConvertToImage();
4271 wxImage plus1s = plus1.Scale(bmSize, bmSize, wxIMAGE_QUALITY_HIGH);
4272 bmPlus = wxBitmap(plus1s);
4273
4274 wxImage minus1 = wxBitmap(*_img_minus).ConvertToImage();
4275 wxImage minus1s = minus1.Scale(bmSize, bmSize, wxIMAGE_QUALITY_HIGH);
4276 bmMinus = wxBitmap(minus1s);
4277#endif
4278
4279 m_pButtonAddDashboard = new wxBitmapButton(
4280 itemPanelNotebook01, wxID_ANY, bmPlus, wxDefaultPosition, wxDefaultSize);
4281 itemBoxSizer02->Add(m_pButtonAddDashboard, 0, wxALIGN_CENTER, 2);
4282 m_pButtonAddDashboard->Connect(
4283 wxEVT_COMMAND_BUTTON_CLICKED,
4284 wxCommandEventHandler(DashboardPreferencesDialog::OnDashboardAdd), NULL,
4285 this);
4286
4287 m_pButtonDeleteDashboard = new wxBitmapButton(
4288 itemPanelNotebook01, wxID_ANY, bmMinus, wxDefaultPosition, wxDefaultSize);
4289 itemBoxSizer02->Add(m_pButtonDeleteDashboard, 0, wxALIGN_CENTER, 2);
4290 m_pButtonDeleteDashboard->Connect(
4291 wxEVT_COMMAND_BUTTON_CLICKED,
4292 wxCommandEventHandler(DashboardPreferencesDialog::OnDashboardDelete),
4293 NULL, this);
4294
4295 m_pPanelDashboard =
4296 new wxPanel(itemPanelNotebook01, wxID_ANY, wxDefaultPosition,
4297 wxDefaultSize, wxBORDER_SUNKEN);
4298 itemFlexGridSizer01->Add(m_pPanelDashboard, 1, wxEXPAND | wxTOP | wxRIGHT,
4299 border_size);
4300
4301 wxBoxSizer *itemBoxSizer03 = new wxBoxSizer(wxVERTICAL);
4302 m_pPanelDashboard->SetSizer(itemBoxSizer03);
4303
4304 wxStaticBox *itemStaticBox02 =
4305 new wxStaticBox(m_pPanelDashboard, wxID_ANY, _("Dashboard"));
4306 wxStaticBoxSizer *itemStaticBoxSizer02 =
4307 new wxStaticBoxSizer(itemStaticBox02, wxHORIZONTAL);
4308 itemBoxSizer03->Add(itemStaticBoxSizer02, 0, wxEXPAND | wxALL, border_size);
4309 wxFlexGridSizer *itemFlexGridSizer = new wxFlexGridSizer(2);
4310 itemFlexGridSizer->AddGrowableCol(1);
4311 itemStaticBoxSizer02->Add(itemFlexGridSizer, 1, wxEXPAND | wxALL, 0);
4312
4313 m_pCheckBoxIsVisible =
4314 new wxCheckBox(m_pPanelDashboard, wxID_ANY, _("show this dashboard"),
4315 wxDefaultPosition, wxDefaultSize, 0);
4316 m_pCheckBoxIsVisible->SetMinSize(wxSize(25 * GetCharWidth(), -1));
4317
4318 itemFlexGridSizer->Add(m_pCheckBoxIsVisible, 0, wxEXPAND | wxALL,
4319 border_size);
4320 wxStaticText *itemDummy01 =
4321 new wxStaticText(m_pPanelDashboard, wxID_ANY, _T(""));
4322 itemFlexGridSizer->Add(itemDummy01, 0, wxEXPAND | wxALL, border_size);
4323
4324 wxStaticText *itemStaticText01 =
4325 new wxStaticText(m_pPanelDashboard, wxID_ANY, _("Caption:"),
4326 wxDefaultPosition, wxDefaultSize, 0);
4327 itemFlexGridSizer->Add(itemStaticText01, 0, wxEXPAND | wxALL, border_size);
4328 m_pTextCtrlCaption = new wxTextCtrl(m_pPanelDashboard, wxID_ANY, _T(""),
4329 wxDefaultPosition, wxDefaultSize);
4330 m_pCheckBoxIsVisible->SetMinSize(wxSize(30 * GetCharWidth(), -1));
4331 itemFlexGridSizer->Add(m_pTextCtrlCaption, 0, wxALIGN_RIGHT | wxALL,
4332 border_size);
4333
4334 wxStaticText *itemStaticText02 =
4335 new wxStaticText(m_pPanelDashboard, wxID_ANY, _("Orientation:"),
4336 wxDefaultPosition, wxDefaultSize, 0);
4337 itemFlexGridSizer->Add(itemStaticText02, 0, wxEXPAND | wxALL, border_size);
4338 m_pChoiceOrientation = new wxChoice(m_pPanelDashboard, wxID_ANY,
4339 wxDefaultPosition, wxDefaultSize);
4340 m_pChoiceOrientation->SetMinSize(wxSize(15 * GetCharWidth(), -1));
4341 m_pChoiceOrientation->Append(_("Vertical"));
4342 m_pChoiceOrientation->Append(_("Horizontal"));
4343 itemFlexGridSizer->Add(m_pChoiceOrientation, 0, wxALIGN_RIGHT | wxALL,
4344 border_size);
4345
4346 int instImageRefSize = 20 * GetOCPNGUIToolScaleFactor_PlugIn();
4347
4348 wxImageList *imglist =
4349 new wxImageList(instImageRefSize, instImageRefSize, true, 2);
4350
4351 wxBitmap bmDial, bmInst;
4352#ifdef ocpnUSE_SVG
4353 bmDial = GetBitmapFromSVGFile(shareLocn + _T("dial.svg"), instImageRefSize,
4354 instImageRefSize);
4355 bmInst = GetBitmapFromSVGFile(shareLocn + _T("instrument.svg"),
4356 instImageRefSize, instImageRefSize);
4357#else
4358 wxImage dial1 = wxBitmap(*_img_dial).ConvertToImage();
4359 wxImage dial1s =
4360 dial1.Scale(instImageRefSize, instImageRefSize, wxIMAGE_QUALITY_HIGH);
4361 bmDial = wxBitmap(dial1);
4362
4363 wxImage inst1 = wxBitmap(*_img_instrument).ConvertToImage();
4364 wxImage inst1s =
4365 inst1.Scale(instImageRefSize, instImageRefSize, wxIMAGE_QUALITY_HIGH);
4366 bmInst = wxBitmap(inst1s);
4367#endif
4368
4369 imglist->Add(bmInst);
4370 imglist->Add(bmDial);
4371
4372 wxStaticBox *itemStaticBox03 =
4373 new wxStaticBox(m_pPanelDashboard, wxID_ANY, _("Instruments"));
4374 wxStaticBoxSizer *itemStaticBoxSizer03 =
4375 new wxStaticBoxSizer(itemStaticBox03, wxHORIZONTAL);
4376 itemBoxSizer03->Add(itemStaticBoxSizer03, 1, wxEXPAND | wxALL, border_size);
4377
4378 wxSize dsize = GetOCPNCanvasWindow()->GetClientSize();
4379 wxSize list_size = wxSize(-1, dsize.y * 35 / 100);
4380
4381#ifdef __ANDROID__
4382 int xsize = GetCharWidth() * 30;
4383 list_size = wxSize(xsize, dsize.y * 50 / 100);
4384#endif
4385
4386 m_pListCtrlInstruments =
4387 new wxListCtrl(m_pPanelDashboard, wxID_ANY, wxDefaultPosition, list_size,
4388 wxLC_REPORT | wxLC_NO_HEADER | wxLC_SINGLE_SEL);
4389
4390 itemStaticBoxSizer03->Add(m_pListCtrlInstruments, 1, wxEXPAND | wxALL,
4391 border_size);
4392 m_pListCtrlInstruments->AssignImageList(imglist, wxIMAGE_LIST_SMALL);
4393 m_pListCtrlInstruments->InsertColumn(0, _("Instruments"));
4394 m_pListCtrlInstruments->Connect(
4395 wxEVT_COMMAND_LIST_ITEM_SELECTED,
4396 wxListEventHandler(DashboardPreferencesDialog::OnInstrumentSelected),
4397 NULL, this);
4398 m_pListCtrlInstruments->Connect(
4399 wxEVT_COMMAND_LIST_ITEM_DESELECTED,
4400 wxListEventHandler(DashboardPreferencesDialog::OnInstrumentSelected),
4401 NULL, this);
4402
4403 wxBoxSizer *itemBoxSizer04 = new wxBoxSizer(wxVERTICAL);
4404 itemStaticBoxSizer03->Add(itemBoxSizer04, 0, wxALIGN_TOP | wxALL,
4405 border_size);
4406 m_pButtonAdd = new wxButton(m_pPanelDashboard, wxID_ANY, _("Add"),
4407 wxDefaultPosition, wxSize(-1, -1));
4408 itemBoxSizer04->Add(m_pButtonAdd, 0, wxEXPAND | wxALL, border_size);
4409 m_pButtonAdd->Connect(
4410 wxEVT_COMMAND_BUTTON_CLICKED,
4411 wxCommandEventHandler(DashboardPreferencesDialog::OnInstrumentAdd), NULL,
4412 this);
4413
4414 // TODO Instrument Properties ... done by Bernd Cirotzki
4415 m_pButtonEdit = new wxButton(m_pPanelDashboard, wxID_ANY, _("Edit"),
4416 wxDefaultPosition, wxDefaultSize);
4417 itemBoxSizer04->Add(m_pButtonEdit, 0, wxEXPAND | wxALL, border_size);
4418 m_pButtonEdit->Connect(
4419 wxEVT_COMMAND_BUTTON_CLICKED,
4420 wxCommandEventHandler(DashboardPreferencesDialog::OnInstrumentEdit), NULL,
4421 this);
4422
4423 // m_pFontPickerTitle =
4424 // new wxFontPickerCtrl(m_pPanelDashboard, wxID_ANY, g_USFontTitle,
4425 // wxDefaultPosition, wxSize(-1, -1), 0,
4426 // wxDefaultValidator, "Bernd");
4427 // itemBoxSizer04->Add(m_pFontPickerTitle, 0, wxALIGN_RIGHT | wxALL, 0);
4428 // wxColor Farbe = m_pFontPickerTitle->GetSelectedColour();
4429 // m_pFontPickerTitle->SetBackgroundColour(Farbe);
4430
4431 m_pButtonDelete = new wxButton(m_pPanelDashboard, wxID_ANY, _("Delete"),
4432 wxDefaultPosition, wxSize(-1, -1));
4433 itemBoxSizer04->Add(m_pButtonDelete, 0, wxEXPAND | wxALL, border_size);
4434 m_pButtonDelete->Connect(
4435 wxEVT_COMMAND_BUTTON_CLICKED,
4436 wxCommandEventHandler(DashboardPreferencesDialog::OnInstrumentDelete),
4437 NULL, this);
4438 itemBoxSizer04->AddSpacer(10);
4439 m_pButtonUp = new wxButton(m_pPanelDashboard, wxID_ANY, _("Up"),
4440 wxDefaultPosition, wxDefaultSize);
4441 itemBoxSizer04->Add(m_pButtonUp, 0, wxEXPAND | wxALL, border_size);
4442 m_pButtonUp->Connect(
4443 wxEVT_COMMAND_BUTTON_CLICKED,
4444 wxCommandEventHandler(DashboardPreferencesDialog::OnInstrumentUp), NULL,
4445 this);
4446 m_pButtonDown = new wxButton(m_pPanelDashboard, wxID_ANY, _("Down"),
4447 wxDefaultPosition, wxDefaultSize);
4448 itemBoxSizer04->Add(m_pButtonDown, 0, wxEXPAND | wxALL, border_size);
4449 m_pButtonDown->Connect(
4450 wxEVT_COMMAND_BUTTON_CLICKED,
4451 wxCommandEventHandler(DashboardPreferencesDialog::OnInstrumentDown), NULL,
4452 this);
4453
4454 wxPanel *itemPanelNotebook02 =
4455 new wxPanel(itemNotebook, wxID_ANY, wxDefaultPosition, wxDefaultSize,
4456 wxTAB_TRAVERSAL);
4457 wxBoxSizer *itemBoxSizer05 = new wxBoxSizer(wxVERTICAL);
4458 itemPanelNotebook02->SetSizer(itemBoxSizer05);
4459 itemNotebook->AddPage(itemPanelNotebook02, _("Appearance"));
4460
4461 wxStaticBox *itemStaticBox01 =
4462 new wxStaticBox(itemPanelNotebook02, wxID_ANY, _("Fonts"));
4463 wxStaticBoxSizer *itemStaticBoxSizer01 =
4464 new wxStaticBoxSizer(itemStaticBox01, wxHORIZONTAL);
4465 itemBoxSizer05->Add(itemStaticBoxSizer01, 0, wxEXPAND | wxALL, border_size);
4466 wxFlexGridSizer *itemFlexGridSizer03 = new wxFlexGridSizer(2);
4467 itemFlexGridSizer03->AddGrowableCol(1);
4468 itemStaticBoxSizer01->Add(itemFlexGridSizer03, 1, wxEXPAND | wxALL, 0);
4469
4470 wxStaticText *itemStaticText04 =
4471 new wxStaticText(itemPanelNotebook02, wxID_ANY, _("Title:"),
4472 wxDefaultPosition, wxDefaultSize, 0);
4473 itemFlexGridSizer03->Add(itemStaticText04, 0, wxEXPAND | wxALL, border_size);
4474
4475 m_pFontPickerTitle =
4476 new wxFontPickerCtrl(itemPanelNotebook02, wxID_ANY, g_USFontTitle,
4477 wxDefaultPosition, wxDefaultSize);
4478 itemFlexGridSizer03->Add(m_pFontPickerTitle, 0, wxALIGN_RIGHT | wxALL, 0);
4479
4480 wxStaticText *itemStaticText05 =
4481 new wxStaticText(itemPanelNotebook02, wxID_ANY, _("Data:"),
4482 wxDefaultPosition, wxDefaultSize, 0);
4483 itemFlexGridSizer03->Add(itemStaticText05, 0, wxEXPAND | wxALL, border_size);
4484 m_pFontPickerData =
4485 new wxFontPickerCtrl(itemPanelNotebook02, wxID_ANY, g_USFontData,
4486 wxDefaultPosition, wxDefaultSize);
4487 itemFlexGridSizer03->Add(m_pFontPickerData, 0, wxALIGN_RIGHT | wxALL, 0);
4488
4489 wxStaticText *itemStaticText06 =
4490 new wxStaticText(itemPanelNotebook02, wxID_ANY, _("Label:"),
4491 wxDefaultPosition, wxDefaultSize, 0);
4492 itemFlexGridSizer03->Add(itemStaticText06, 0, wxEXPAND | wxALL, border_size);
4493 m_pFontPickerLabel =
4494 new wxFontPickerCtrl(itemPanelNotebook02, wxID_ANY, g_USFontLabel,
4495 wxDefaultPosition, wxDefaultSize);
4496 itemFlexGridSizer03->Add(m_pFontPickerLabel, 0, wxALIGN_RIGHT | wxALL, 0);
4497
4498 wxStaticText *itemStaticText07 =
4499 new wxStaticText(itemPanelNotebook02, wxID_ANY, _("Small:"),
4500 wxDefaultPosition, wxDefaultSize, 0);
4501 itemFlexGridSizer03->Add(itemStaticText07, 0, wxEXPAND | wxALL, border_size);
4502 m_pFontPickerSmall =
4503 new wxFontPickerCtrl(itemPanelNotebook02, wxID_ANY, g_USFontSmall,
4504 wxDefaultPosition, wxDefaultSize);
4505 itemFlexGridSizer03->Add(m_pFontPickerSmall, 0, wxALIGN_RIGHT | wxALL, 0);
4506 // wxColourPickerCtrl
4507
4508 wxStaticText *itemStaticText80 =
4509 new wxStaticText(itemPanelNotebook02, wxID_ANY, _("Reset:"),
4510 wxDefaultPosition, wxDefaultSize, 0);
4511 itemFlexGridSizer03->Add(itemStaticText80, 0, wxEXPAND | wxALL, border_size);
4512
4513 m_pButtondefaultFont = new wxButton(itemPanelNotebook02, wxID_ANY,
4514 _("Set dashboard default fonts"),
4515 wxDefaultPosition, wxSize(-1, -1));
4516 itemFlexGridSizer03->Add(m_pButtondefaultFont, 0, wxALIGN_RIGHT | wxALL, 0);
4517 m_pButtondefaultFont->Connect(
4518 wxEVT_COMMAND_BUTTON_CLICKED,
4519 wxCommandEventHandler(DashboardPreferencesDialog::OnDashboarddefaultFont),
4520 NULL, this);
4521
4522 wxStaticBox *itemStaticBox04 = new wxStaticBox(itemPanelNotebook02, wxID_ANY,
4523 _("Units, Ranges, Formats"));
4524 wxStaticBoxSizer *itemStaticBoxSizer04 =
4525 new wxStaticBoxSizer(itemStaticBox04, wxHORIZONTAL);
4526 itemBoxSizer05->Add(itemStaticBoxSizer04, 0, wxEXPAND | wxALL, border_size);
4527 wxFlexGridSizer *itemFlexGridSizer04 = new wxFlexGridSizer(2);
4528 itemFlexGridSizer04->AddGrowableCol(1);
4529 itemStaticBoxSizer04->Add(itemFlexGridSizer04, 1, wxEXPAND | wxALL, 0);
4530 wxStaticText *itemStaticText08 = new wxStaticText(
4531 itemPanelNotebook02, wxID_ANY, _("Speedometer max value:"),
4532 wxDefaultPosition, wxDefaultSize, 0);
4533 itemFlexGridSizer04->Add(itemStaticText08, 0, wxEXPAND | wxALL, border_size);
4534 m_pSpinSpeedMax = new wxSpinCtrl(itemPanelNotebook02, wxID_ANY, wxEmptyString,
4535 wxDefaultPosition, wxDefaultSize,
4536 wxSP_ARROW_KEYS, 10, 100, g_iDashSpeedMax);
4537 itemFlexGridSizer04->Add(m_pSpinSpeedMax, 0, wxALIGN_RIGHT | wxALL, 0);
4538
4539 wxStaticText *itemStaticText10 = new wxStaticText(
4540 itemPanelNotebook02, wxID_ANY, _("Speed Over Ground Damping Factor:"),
4541 wxDefaultPosition, wxDefaultSize, 0);
4542 itemFlexGridSizer04->Add(itemStaticText10, 0, wxEXPAND | wxALL, border_size);
4543 m_pSpinSOGDamp = new wxSpinCtrl(itemPanelNotebook02, wxID_ANY, wxEmptyString,
4544 wxDefaultPosition, wxDefaultSize,
4545 wxSP_ARROW_KEYS, 0, 100, g_iDashSOGDamp);
4546 itemFlexGridSizer04->Add(m_pSpinSOGDamp, 0, wxALIGN_RIGHT | wxALL, 0);
4547
4548 wxStaticText *itemStaticText11 =
4549 new wxStaticText(itemPanelNotebook02, wxID_ANY, _("COG Damping Factor:"),
4550 wxDefaultPosition, wxDefaultSize, 0);
4551 itemFlexGridSizer04->Add(itemStaticText11, 0, wxEXPAND | wxALL, border_size);
4552 m_pSpinCOGDamp = new wxSpinCtrl(itemPanelNotebook02, wxID_ANY, wxEmptyString,
4553 wxDefaultPosition, wxDefaultSize,
4554 wxSP_ARROW_KEYS, 0, 100, g_iDashCOGDamp);
4555 itemFlexGridSizer04->Add(m_pSpinCOGDamp, 0, wxALIGN_RIGHT | wxALL, 0);
4556
4557 wxStaticText *itemStaticText13 = new wxStaticText(
4558 itemPanelNotebook02, wxID_ANY, _("Wind speed Damping Factor:"),
4559 wxDefaultPosition, wxDefaultSize, 0);
4560 itemFlexGridSizer04->Add(itemStaticText13, 0, wxEXPAND | wxALL, border_size);
4561 m_pSpinAWSDamp = new wxSpinCtrl(itemPanelNotebook02, wxID_ANY, wxEmptyString,
4562 wxDefaultPosition, wxDefaultSize,
4563 wxSP_ARROW_KEYS, 0, 100, g_iDashAWSDamp);
4564 itemFlexGridSizer04->Add(m_pSpinAWSDamp, 0, wxALIGN_RIGHT | wxALL, 0);
4565
4566 wxStaticText *itemStaticText14 = new wxStaticText(
4567 itemPanelNotebook02, wxID_ANY, _("Wind angle Damping Factor:"),
4568 wxDefaultPosition, wxDefaultSize, 0);
4569 itemFlexGridSizer04->Add(itemStaticText14, 0, wxEXPAND | wxALL, border_size);
4570 m_pSpinAWADamp = new wxSpinCtrl(itemPanelNotebook02, wxID_ANY, wxEmptyString,
4571 wxDefaultPosition, wxDefaultSize,
4572 wxSP_ARROW_KEYS, 0, 100, g_iDashAWADamp);
4573 itemFlexGridSizer04->Add(m_pSpinAWADamp, 0, wxALIGN_RIGHT | wxALL, 0);
4574
4575 wxStaticText *itemStaticText12 = new wxStaticText(
4576 itemPanelNotebook02, wxID_ANY, _("Local Time Offset From UTC:"),
4577 wxDefaultPosition, wxDefaultSize, 0);
4578 itemFlexGridSizer04->Add(itemStaticText12, 0, wxEXPAND | wxALL, border_size);
4579 wxString m_UTCOffsetChoices[] = {
4580 _T( "-12:00" ), _T( "-11:30" ), _T( "-11:00" ), _T( "-10:30" ),
4581 _T( "-10:00" ), _T( "-09:30" ), _T( "-09:00" ), _T( "-08:30" ),
4582 _T( "-08:00" ), _T( "-07:30" ), _T( "-07:00" ), _T( "-06:30" ),
4583 _T( "-06:00" ), _T( "-05:30" ), _T( "-05:00" ), _T( "-04:30" ),
4584 _T( "-04:00" ), _T( "-03:30" ), _T( "-03:00" ), _T( "-02:30" ),
4585 _T( "-02:00" ), _T( "-01:30" ), _T( "-01:00" ), _T( "-00:30" ),
4586 _T( " 00:00" ), _T( " 00:30" ), _T( " 01:00" ), _T( " 01:30" ),
4587 _T( " 02:00" ), _T( " 02:30" ), _T( " 03:00" ), _T( " 03:30" ),
4588 _T( " 04:00" ), _T( " 04:30" ), _T( " 05:00" ), _T( " 05:30" ),
4589 _T( " 06:00" ), _T( " 06:30" ), _T( " 07:00" ), _T( " 07:30" ),
4590 _T( " 08:00" ), _T( " 08:30" ), _T( " 09:00" ), _T( " 09:30" ),
4591 _T( " 10:00" ), _T( " 10:30" ), _T( " 11:00" ), _T( " 11:30" ),
4592 _T( " 12:00" )};
4593 int m_UTCOffsetNChoices = sizeof(m_UTCOffsetChoices) / sizeof(wxString);
4594 m_pChoiceUTCOffset =
4595 new wxChoice(itemPanelNotebook02, wxID_ANY, wxDefaultPosition,
4596 wxDefaultSize, m_UTCOffsetNChoices, m_UTCOffsetChoices, 0);
4597 m_pChoiceUTCOffset->SetSelection(g_iUTCOffset + 24);
4598 itemFlexGridSizer04->Add(m_pChoiceUTCOffset, 0, wxALIGN_RIGHT | wxALL, 0);
4599
4600 wxStaticText *itemStaticText09 =
4601 new wxStaticText(itemPanelNotebook02, wxID_ANY, _("Boat speed units:"),
4602 wxDefaultPosition, wxDefaultSize, 0);
4603 itemFlexGridSizer04->Add(itemStaticText09, 0, wxEXPAND | wxALL, border_size);
4604 wxString m_SpeedUnitChoices[] = {_("Honor OpenCPN settings"), _("Kts"),
4605 _("mph"), _("km/h"), _("m/s")};
4606 int m_SpeedUnitNChoices = sizeof(m_SpeedUnitChoices) / sizeof(wxString);
4607 wxSize szSpeedUnit = wxDefaultSize;
4608 m_pChoiceSpeedUnit =
4609 new wxChoice(itemPanelNotebook02, wxID_ANY, wxDefaultPosition,
4610 szSpeedUnit, m_SpeedUnitNChoices, m_SpeedUnitChoices, 0);
4611 for (auto const &iUnit : m_SpeedUnitChoices) {
4612 szSpeedUnit.IncTo(m_pChoiceSpeedUnit->GetTextExtent(iUnit));
4613 }
4614 m_pChoiceSpeedUnit->SetSize(szSpeedUnit);
4615 m_pChoiceSpeedUnit->SetSelection(g_iDashSpeedUnit + 1);
4616 itemFlexGridSizer04->Add(m_pChoiceSpeedUnit, 0, wxALIGN_RIGHT | wxALL, 0);
4617
4618 wxStaticText *itemStaticTextDepthU =
4619 new wxStaticText(itemPanelNotebook02, wxID_ANY, _("Depth units:"),
4620 wxDefaultPosition, wxDefaultSize, 0);
4621 itemFlexGridSizer04->Add(itemStaticTextDepthU, 0, wxEXPAND | wxALL,
4622 border_size);
4623 wxString m_DepthUnitChoices[] = {_("Meters"), _("Feet"), _("Fathoms"),
4624 _("Inches"), _("Centimeters")};
4625 int m_DepthUnitNChoices = sizeof(m_DepthUnitChoices) / sizeof(wxString);
4626 wxSize szDepthUnit = wxDefaultSize;
4627 m_pChoiceDepthUnit =
4628 new wxChoice(itemPanelNotebook02, wxID_ANY, wxDefaultPosition,
4629 szDepthUnit, m_DepthUnitNChoices, m_DepthUnitChoices, 0);
4630 for (auto const &iUnit : m_DepthUnitChoices) {
4631 szDepthUnit.IncTo(m_pChoiceDepthUnit->GetTextExtent(iUnit));
4632 }
4633 m_pChoiceDepthUnit->SetSize(szDepthUnit);
4634 m_pChoiceDepthUnit->SetSelection(g_iDashDepthUnit - 3);
4635 itemFlexGridSizer04->Add(m_pChoiceDepthUnit, 0, wxALIGN_RIGHT | wxALL, 0);
4636 wxString dMess = wxString::Format(_("Depth Offset (%s):"),
4637 m_DepthUnitChoices[g_iDashDepthUnit - 3]);
4638 wxStaticText *itemStaticDepthO =
4639 new wxStaticText(itemPanelNotebook02, wxID_ANY, dMess, wxDefaultPosition,
4640 wxDefaultSize, 0);
4641 double DepthOffset;
4642 switch (g_iDashDepthUnit - 3) {
4643 case 1:
4644 DepthOffset = g_dDashDBTOffset * 3.2808399;
4645 break;
4646 case 2:
4647 DepthOffset = g_dDashDBTOffset * 0.54680665;
4648 break;
4649 case 3:
4650 DepthOffset = g_dDashDBTOffset * 39.3700787;
4651 break;
4652 case 4:
4653 DepthOffset = g_dDashDBTOffset * 100;
4654 break;
4655 default:
4656 DepthOffset = g_dDashDBTOffset;
4657 }
4658 itemFlexGridSizer04->Add(itemStaticDepthO, 0, wxEXPAND | wxALL, border_size);
4659 m_pSpinDBTOffset = new wxSpinCtrlDouble(
4660 itemPanelNotebook02, wxID_ANY, wxEmptyString, wxDefaultPosition,
4661 wxDefaultSize, wxSP_ARROW_KEYS, -100, 100, DepthOffset, 0.1);
4662 itemFlexGridSizer04->Add(m_pSpinDBTOffset, 0, wxALIGN_RIGHT | wxALL, 0);
4663
4664 wxStaticText *itemStaticText0b =
4665 new wxStaticText(itemPanelNotebook02, wxID_ANY, _("Distance units:"),
4666 wxDefaultPosition, wxDefaultSize, 0);
4667 itemFlexGridSizer04->Add(itemStaticText0b, 0, wxEXPAND | wxALL, border_size);
4668 wxString m_DistanceUnitChoices[] = {_("Honor OpenCPN settings"),
4669 _("Nautical miles"), _("Statute miles"),
4670 _("Kilometers"), _("Meters")};
4671 wxSize szDistanceUnit = wxDefaultSize;
4672 int m_DistanceUnitNChoices = sizeof(m_DistanceUnitChoices) / sizeof(wxString);
4673 m_pChoiceDistanceUnit = new wxChoice(
4674 itemPanelNotebook02, wxID_ANY, wxDefaultPosition, szDistanceUnit,
4675 m_DistanceUnitNChoices, m_DistanceUnitChoices, 0);
4676 for (auto const &iUnit : m_DistanceUnitChoices) {
4677 szDistanceUnit.IncTo(m_pChoiceDistanceUnit->GetTextExtent(iUnit));
4678 }
4679 m_pChoiceDistanceUnit->SetSize(szDistanceUnit);
4680 m_pChoiceDistanceUnit->SetSelection(g_iDashDistanceUnit + 1);
4681 itemFlexGridSizer04->Add(m_pChoiceDistanceUnit, 0, wxALIGN_RIGHT | wxALL, 0);
4682 m_pChoiceDistanceUnit->Connect(
4683 wxEVT_COMMAND_CHOICE_SELECTED,
4684 wxCommandEventHandler(DashboardPreferencesDialog::OnDistanceUnitSelect),
4685 NULL, this);
4686
4687 wxStaticText *itemStaticText0a =
4688 new wxStaticText(itemPanelNotebook02, wxID_ANY, _("Wind speed units:"),
4689 wxDefaultPosition, wxDefaultSize, 0);
4690 itemFlexGridSizer04->Add(itemStaticText0a, 0, wxEXPAND | wxALL, border_size);
4691 wxString m_WSpeedUnitChoices[] = {_("Kts"), _("mph"), _("km/h"), _("m/s")};
4692 int m_WSpeedUnitNChoices = sizeof(m_WSpeedUnitChoices) / sizeof(wxString);
4693 wxSize szWSpeedUnit = wxDefaultSize;
4694 m_pChoiceWindSpeedUnit =
4695 new wxChoice(itemPanelNotebook02, wxID_ANY, wxDefaultPosition,
4696 szWSpeedUnit, m_WSpeedUnitNChoices, m_WSpeedUnitChoices, 0);
4697 for (auto const &iUnit : m_WSpeedUnitChoices) {
4698 szWSpeedUnit.IncTo(m_pChoiceWindSpeedUnit->GetTextExtent(iUnit));
4699 }
4700 m_pChoiceWindSpeedUnit->SetSize(szWSpeedUnit);
4701 m_pChoiceWindSpeedUnit->SetSelection(g_iDashWindSpeedUnit);
4702 itemFlexGridSizer04->Add(m_pChoiceWindSpeedUnit, 0, wxALIGN_RIGHT | wxALL, 0);
4703
4704 wxStaticText *itemStaticText0c =
4705 new wxStaticText(itemPanelNotebook02, wxID_ANY, _("Temperature units:"),
4706 wxDefaultPosition, wxDefaultSize, 0);
4707 itemFlexGridSizer04->Add(itemStaticText0c, 0, wxEXPAND | wxALL, border_size);
4708 wxString m_TempUnitChoices[] = {_("Celsius"), _("Fahrenheit"), _("Kelvin")};
4709 int m_TempUnitNChoices = sizeof(m_TempUnitChoices) / sizeof(wxString);
4710 wxSize szTempUnit = wxDefaultSize;
4711 m_pChoiceTempUnit =
4712 new wxChoice(itemPanelNotebook02, wxID_ANY, wxDefaultPosition, szTempUnit,
4713 m_TempUnitNChoices, m_TempUnitChoices, 0);
4714 for (auto const &iUnit : m_TempUnitChoices) {
4715 szTempUnit.IncTo(m_pChoiceTempUnit->GetTextExtent(iUnit));
4716 }
4717 m_pChoiceTempUnit->SetSize(szTempUnit);
4718 m_pChoiceTempUnit->SetSelection(g_iDashTempUnit);
4719 itemFlexGridSizer04->Add(m_pChoiceTempUnit, 0, wxALIGN_RIGHT | wxALL, 0);
4720
4721 // New static box to include a 3 column flexgrid sizer
4722#ifndef __ANDROID__
4723 wxStaticBox *itemStaticBox05 =
4724 new wxStaticBox(itemPanelNotebook02, wxID_ANY, _("Other selections"));
4725 wxStaticBoxSizer *itemStaticBoxSizer05 =
4726 new wxStaticBoxSizer(itemStaticBox05, wxHORIZONTAL);
4727 itemBoxSizer05->Add(itemStaticBoxSizer05, 0, wxEXPAND | wxALL, border_size);
4728
4729 wxFlexGridSizer *itemFlexGridSizer05 = new wxFlexGridSizer(3);
4730 itemFlexGridSizer05->AddGrowableCol(1);
4731 itemStaticBoxSizer05->Add(itemFlexGridSizer05, 1, wxEXPAND | wxALL, 0);
4732
4733 m_pUseInternSumLog = new wxCheckBox(itemPanelNotebook02, wxID_ANY,
4734 _("Use internal Sumlog.") + " " +
4735 _("Enter new value if desired"));
4736 itemFlexGridSizer05->Add(m_pUseInternSumLog, 1, wxALIGN_LEFT, border_size);
4737 m_pUseInternSumLog->SetValue(g_bUseInternSumLog);
4738
4739 m_SumLogUnit =
4740 new wxStaticText(itemPanelNotebook02, wxID_ANY,
4741 getUsrDistanceUnit_Plugin(g_iDashDistanceUnit) + ": ",
4742 wxDefaultPosition, wxDefaultSize, 0);
4743 itemFlexGridSizer05->Add(m_SumLogUnit, 1, wxALIGN_RIGHT, 0);
4744
4745 m_pSumLogValue = new wxTextCtrl(itemPanelNotebook02, wxID_ANY, "");
4746 itemFlexGridSizer05->Add(m_pSumLogValue, 1, wxALIGN_LEFT, 1);
4747 m_pSumLogValue->SetValue(wxString::Format(
4748 "%.1f", toUsrDistance_Plugin(g_dSumLogNM, g_iDashDistanceUnit)));
4749
4750 m_pUseTrueWinddata = new wxCheckBox(itemPanelNotebook02, wxID_ANY,
4751 _("Use N2K & SignalK true wind data over "
4752 "ground.\n(Instead of through water)"));
4753 itemFlexGridSizer05->Add(m_pUseTrueWinddata, 1, wxALIGN_LEFT, border_size);
4754 m_pUseTrueWinddata->SetValue(g_bDBtrueWindGround);
4755
4756#else
4757
4758 wxStaticBox *itemStaticBox05 =
4759 new wxStaticBox(itemPanelNotebook02, wxID_ANY, _("Other selections"));
4760 wxStaticBoxSizer *itemStaticBoxSizer05 =
4761 new wxStaticBoxSizer(itemStaticBox05, wxHORIZONTAL);
4762 itemBoxSizer05->Add(itemStaticBoxSizer05, 0, wxEXPAND | wxALL, border_size);
4763
4764 wxFlexGridSizer *itemFlexGridSizer05 = new wxFlexGridSizer(2);
4765 itemFlexGridSizer05->AddGrowableCol(1);
4766 itemStaticBoxSizer05->Add(itemFlexGridSizer05, 1, wxEXPAND | wxALL, 0);
4767
4768 m_pUseInternSumLog = new wxCheckBox(itemPanelNotebook02, wxID_ANY,
4769 _("Use internal Sumlog.") + " ");
4770 itemFlexGridSizer05->Add(m_pUseInternSumLog, 0, wxALIGN_LEFT, border_size);
4771 m_pUseInternSumLog->SetValue(g_bUseInternSumLog);
4772 itemFlexGridSizer05->AddSpacer(0);
4773
4774 auto xtext =
4775 new wxStaticText(itemPanelNotebook02, wxID_ANY,
4776 " " + _("Enter new value if desired") + " ",
4777 wxDefaultPosition, wxDefaultSize, 0);
4778 itemFlexGridSizer05->Add(xtext, 1, wxALIGN_LEFT, 0);
4779 itemFlexGridSizer05->AddSpacer(0);
4780
4781 m_SumLogUnit = new wxStaticText(
4782 itemPanelNotebook02, wxID_ANY,
4783 getUsrDistanceUnit_Plugin(g_iDashDistanceUnit) + ": ",
4784 wxDefaultPosition, wxDefaultSize, 0);
4785 itemFlexGridSizer05->Add(m_SumLogUnit, 1, wxALIGN_RIGHT, 0);
4786
4787 m_pSumLogValue = new wxTextCtrl(itemPanelNotebook02, wxID_ANY, "");
4788 itemFlexGridSizer05->Add(m_pSumLogValue, 1, wxALIGN_LEFT, 1);
4789 m_pSumLogValue->SetValue(wxString::Format(
4790 "%.1f", toUsrDistance_Plugin(g_dSumLogNM, g_iDashDistanceUnit)));
4791
4792 m_pUseTrueWinddata = new wxCheckBox(itemPanelNotebook02, wxID_ANY,
4793 _("Use N2K & SignalK true wind data over "
4794 "ground.\n(Instead of through water)"));
4795 itemFlexGridSizer05->Add(m_pUseTrueWinddata, 1, wxALIGN_LEFT, border_size);
4796 m_pUseTrueWinddata->SetValue(g_bDBtrueWindGround);
4797 itemFlexGridSizer05->AddSpacer(0);
4798
4799 auto xtext1 = new wxStaticText(itemPanelNotebook02, wxID_ANY, " ",
4800 wxDefaultPosition, wxDefaultSize, 0);
4801 itemFlexGridSizer05->Add(xtext1, 1, wxALIGN_LEFT, 0);
4802 itemFlexGridSizer05->AddSpacer(0);
4803
4804 auto xtext2 = new wxStaticText(itemPanelNotebook02, wxID_ANY, " ",
4805 wxDefaultPosition, wxDefaultSize, 0);
4806 itemFlexGridSizer05->Add(xtext2, 1, wxALIGN_LEFT, 0);
4807 itemFlexGridSizer05->AddSpacer(0);
4808
4809#endif
4810
4811 curSel = -1;
4812 for (size_t i = 0; i < m_Config.GetCount(); i++) {
4813 m_pListCtrlDashboards->InsertItem(i, 0);
4814 // Using data to store m_Config index for managing deletes
4815 m_pListCtrlDashboards->SetItemData(i, i);
4816 }
4817 m_pListCtrlDashboards->SetColumnWidth(0, wxLIST_AUTOSIZE);
4818
4819 m_pListCtrlDashboards->SetItemState(0, wxLIST_STATE_SELECTED,
4820 wxLIST_STATE_SELECTED);
4821 curSel = 0;
4822
4823 UpdateDashboardButtonsState();
4824 UpdateButtonsState();
4825
4826 // SetMinSize(wxSize(400, -1));
4827
4828 Fit();
4829
4830 // Constrain size on small displays
4831 SetMaxSize(wxSize(display_width, display_height));
4832
4833 wxSize canvas_size = GetOCPNCanvasWindow()->GetSize();
4834 if (display_height < 600) {
4835 if (g_dashPrefWidth > 0 && g_dashPrefHeight > 0)
4836 SetSize(wxSize(g_dashPrefWidth, g_dashPrefHeight));
4837 else
4838 SetSize(wxSize(canvas_size.x * 8 / 10, canvas_size.y * 8 / 10));
4839 } else {
4840 if (g_dashPrefWidth > 0 && g_dashPrefHeight > 0)
4841 SetSize(wxSize(g_dashPrefWidth, g_dashPrefHeight));
4842 else
4843 SetSize(wxSize(canvas_size.x * 3 / 4, canvas_size.y * 8 / 10));
4844 }
4845
4846 Layout();
4847 CentreOnScreen();
4848}
4849
4850void DashboardPreferencesDialog::RecalculateSize(void) {
4851#ifdef __OCPN__ANDROID__
4852 wxSize esize;
4853 esize.x = GetCharWidth() * 110;
4854 esize.y = GetCharHeight() * 40;
4855
4856 wxSize dsize = GetOCPNCanvasWindow()->GetClientSize();
4857 esize.y = wxMin(esize.y, dsize.y - (3 * GetCharHeight()));
4858 esize.x = wxMin(esize.x, dsize.x - (3 * GetCharHeight()));
4859 SetSize(esize);
4860
4861 CentreOnScreen();
4862#endif
4863}
4864
4865void DashboardPreferencesDialog::OnCloseDialog(wxCloseEvent &event) {
4866 g_dashPrefWidth = GetSize().x;
4867 g_dashPrefHeight = GetSize().y;
4868 SaveDashboardConfig();
4869 event.Skip();
4870}
4871
4872void DashboardPreferencesDialog::SaveDashboardConfig() {
4873 g_iDashSpeedMax = m_pSpinSpeedMax->GetValue();
4874 g_iDashCOGDamp = m_pSpinCOGDamp->GetValue();
4875 g_iDashSOGDamp = m_pSpinSOGDamp->GetValue();
4876 g_iDashAWADamp = m_pSpinAWADamp->GetValue();
4877 g_iDashAWSDamp = m_pSpinAWSDamp->GetValue();
4878
4879 g_bUseInternSumLog = m_pUseInternSumLog->IsChecked();
4880 double ursDist;
4881 m_pSumLogValue->GetValue().ToDouble(&ursDist);
4882 g_dSumLogNM = fromUsrDistance_Plugin(ursDist, g_iDashDistanceUnit);
4883
4884 g_iUTCOffset = m_pChoiceUTCOffset->GetSelection() - 24;
4885 g_iDashSpeedUnit = m_pChoiceSpeedUnit->GetSelection() - 1;
4886 double DashDBTOffset = m_pSpinDBTOffset->GetValue();
4887 switch (g_iDashDepthUnit - 3) {
4888 case 1:
4889 g_dDashDBTOffset = DashDBTOffset / 3.2808399;
4890 break;
4891 case 2:
4892 g_dDashDBTOffset = DashDBTOffset / 0.54680665;
4893 break;
4894 case 3:
4895 g_dDashDBTOffset = DashDBTOffset / 39.3700787;
4896 break;
4897 case 4:
4898 g_dDashDBTOffset = DashDBTOffset / 100;
4899 break;
4900 default:
4901 g_dDashDBTOffset = DashDBTOffset;
4902 }
4903 g_iDashDepthUnit = m_pChoiceDepthUnit->GetSelection() + 3;
4904 g_iDashDistanceUnit = m_pChoiceDistanceUnit->GetSelection() - 1;
4905 g_iDashWindSpeedUnit = m_pChoiceWindSpeedUnit->GetSelection();
4906 g_bDBtrueWindGround = m_pUseTrueWinddata->GetValue();
4907 g_iDashTempUnit = m_pChoiceTempUnit->GetSelection();
4908 if (curSel != -1) {
4909 DashboardWindowContainer *cont = m_Config.Item(curSel);
4910 cont->m_bIsVisible = m_pCheckBoxIsVisible->IsChecked();
4911 cont->m_sCaption = m_pTextCtrlCaption->GetValue();
4912 cont->m_sOrientation =
4913 m_pChoiceOrientation->GetSelection() == 0 ? _T("V") : _T("H");
4914 cont->m_aInstrumentList.Clear();
4915 for (int i = 0; i < m_pListCtrlInstruments->GetItemCount(); i++)
4916 cont->m_aInstrumentList.Add((int)m_pListCtrlInstruments->GetItemData(i));
4917 }
4918}
4919
4920void DashboardPreferencesDialog::OnDashboardSelected(wxListEvent &event) {
4921 // save changes
4922 SaveDashboardConfig();
4923 UpdateDashboardButtonsState();
4924}
4925
4926void DashboardPreferencesDialog::UpdateDashboardButtonsState() {
4927 long item = -1;
4928 item = m_pListCtrlDashboards->GetNextItem(item, wxLIST_NEXT_ALL,
4929 wxLIST_STATE_SELECTED);
4930 bool enable = (item != -1);
4931
4932 // Disable the Dashboard Delete button if the parent(Dashboard) of this
4933 // dialog is selected.
4934 bool delete_enable = enable;
4935 if (item != -1) {
4936 int sel = m_pListCtrlDashboards->GetItemData(item);
4937 DashboardWindowContainer *cont = m_Config.Item(sel);
4938 DashboardWindow *dash_sel = cont->m_pDashboardWindow;
4939 if (dash_sel == GetParent()) delete_enable = false;
4940 }
4941 m_pButtonDeleteDashboard->Enable(delete_enable);
4942
4943 m_pPanelDashboard->Enable(enable);
4944
4945 if (item != -1) {
4946 curSel = m_pListCtrlDashboards->GetItemData(item);
4947 DashboardWindowContainer *cont = m_Config.Item(curSel);
4948 m_pCheckBoxIsVisible->SetValue(cont->m_bIsVisible);
4949 m_pTextCtrlCaption->SetValue(cont->m_sCaption);
4950 m_pChoiceOrientation->SetSelection(cont->m_sOrientation == _T("V") ? 0 : 1);
4951 m_pListCtrlInstruments->DeleteAllItems();
4952 for (size_t i = 0; i < cont->m_aInstrumentList.GetCount(); i++) {
4953 wxListItem item;
4954 getListItemForInstrument(item, cont->m_aInstrumentList.Item(i));
4955 item.SetId(m_pListCtrlInstruments->GetItemCount());
4956 m_pListCtrlInstruments->InsertItem(item);
4957 }
4958
4959 m_pListCtrlInstruments->SetColumnWidth(0, wxLIST_AUTOSIZE);
4960 } else {
4961 curSel = -1;
4962 m_pCheckBoxIsVisible->SetValue(false);
4963 m_pTextCtrlCaption->SetValue(_T(""));
4964 m_pChoiceOrientation->SetSelection(0);
4965 m_pListCtrlInstruments->DeleteAllItems();
4966 }
4967 // UpdateButtonsState();
4968}
4969
4970void DashboardPreferencesDialog::OnDashboarddefaultFont(wxCommandEvent &event) {
4971 m_pFontPickerTitle->SetSelectedFont(
4972 wxFont(10, wxFONTFAMILY_SWISS, wxFONTSTYLE_ITALIC, wxFONTWEIGHT_NORMAL));
4973 m_pFontPickerTitle->SetSelectedColour(wxColour(0, 0, 0));
4974 m_pFontPickerData->SetSelectedFont(
4975 wxFont(14, wxFONTFAMILY_SWISS, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_NORMAL));
4976 m_pFontPickerData->SetSelectedColour(wxColour(0, 0, 0));
4977 m_pFontPickerLabel->SetSelectedFont(
4978 wxFont(8, wxFONTFAMILY_SWISS, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_NORMAL));
4979 m_pFontPickerLabel->SetSelectedColour(wxColour(0, 0, 0));
4980 m_pFontPickerSmall->SetSelectedFont(
4981 wxFont(8, wxFONTFAMILY_SWISS, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_NORMAL));
4982 m_pFontPickerSmall->SetSelectedColour(wxColour(0, 0, 0));
4983 double scaler = 1.0;
4984 if (OCPN_GetWinDIPScaleFactor() < 1.0)
4985 scaler = 1.0 + OCPN_GetWinDIPScaleFactor() / 4;
4986 scaler = wxMax(1.0, scaler);
4987
4988 g_USFontTitle = *(m_pFontPickerTitle->GetFontData());
4989 g_FontTitle = *g_pUSFontTitle;
4990 g_FontTitle.SetChosenFont(g_pUSFontTitle->GetChosenFont().Scaled(scaler));
4991 g_FontTitle.SetColour(g_pUSFontTitle->GetColour());
4992 g_USFontTitle = *g_pUSFontTitle;
4993
4994 g_USFontData = *(m_pFontPickerData->GetFontData());
4995 g_FontData = *g_pUSFontData;
4996 g_FontData.SetChosenFont(g_pUSFontData->GetChosenFont().Scaled(scaler));
4997 g_FontData.SetColour(g_pUSFontData->GetColour());
4998 g_USFontData = *g_pUSFontData;
4999
5000 g_USFontLabel = *(m_pFontPickerLabel->GetFontData());
5001 g_FontLabel = *g_pUSFontLabel;
5002 g_FontLabel.SetChosenFont(g_pUSFontLabel->GetChosenFont().Scaled(scaler));
5003 g_FontLabel.SetColour(g_pUSFontLabel->GetColour());
5004 g_USFontLabel = *g_pUSFontLabel;
5005
5006 g_USFontSmall = *(m_pFontPickerSmall->GetFontData());
5007 g_FontSmall = *g_pUSFontSmall;
5008 g_FontSmall.SetChosenFont(g_pUSFontSmall->GetChosenFont().Scaled(scaler));
5009 g_FontSmall.SetColour(g_pUSFontSmall->GetColour());
5010 g_USFontSmall = *g_pUSFontSmall;
5011}
5012
5013void DashboardPreferencesDialog::OnDashboardAdd(wxCommandEvent &event) {
5014 int idx = m_pListCtrlDashboards->GetItemCount();
5015 m_pListCtrlDashboards->InsertItem(idx, 0);
5016 // Data is index in m_Config
5017 m_pListCtrlDashboards->SetItemData(idx, m_Config.GetCount());
5018 wxArrayInt ar;
5019 wxArrayOfInstrumentProperties Property;
5021 NULL, MakeName(), _("Dashboard"), _T("V"), ar, Property);
5022 dwc->m_bIsVisible = true;
5023 m_Config.Add(dwc);
5024}
5025
5026void DashboardPreferencesDialog::OnDashboardDelete(wxCommandEvent &event) {
5027 long itemID = -1;
5028 itemID = m_pListCtrlDashboards->GetNextItem(itemID, wxLIST_NEXT_ALL,
5029 wxLIST_STATE_SELECTED);
5030
5031 int idx = m_pListCtrlDashboards->GetItemData(itemID);
5032 m_pListCtrlDashboards->DeleteItem(itemID);
5033 m_Config.Item(idx)->m_bIsDeleted = true;
5034 UpdateDashboardButtonsState();
5035}
5036
5037void DashboardPreferencesDialog::OnInstrumentSelected(wxListEvent &event) {
5038 UpdateButtonsState();
5039}
5040
5041void DashboardPreferencesDialog::UpdateButtonsState() {
5042 long item = -1;
5043 item = m_pListCtrlInstruments->GetNextItem(item, wxLIST_NEXT_ALL,
5044 wxLIST_STATE_SELECTED);
5045 bool enable = (item != -1);
5046
5047 m_pButtonDelete->Enable(enable);
5048 m_pButtonEdit->Enable(enable); // TODO: Properties ... done Bernd Cirotzki
5049 m_pButtonUp->Enable(item > 0);
5050 m_pButtonDown->Enable(item != -1 &&
5051 item < m_pListCtrlInstruments->GetItemCount() - 1);
5052}
5053
5054void DashboardPreferencesDialog::OnInstrumentAdd(wxCommandEvent &event) {
5055 AddInstrumentDlg pdlg((wxWindow *)event.GetEventObject(), wxID_ANY);
5056
5057#ifdef __OCPN__ANDROID__
5058 wxFont *pF = OCPNGetFont(_("Dialog"));
5059 pdlg.SetFont(*pF);
5060
5061 wxSize esize;
5062 esize.x = GetCharWidth() * 110;
5063 esize.y = GetCharHeight() * 40;
5064
5065 wxSize dsize = GetOCPNCanvasWindow()->GetClientSize();
5066 esize.y = wxMin(esize.y, dsize.y - (3 * GetCharHeight()));
5067 esize.x = wxMin(esize.x, dsize.x - (3 * GetCharHeight()));
5068 pdlg.SetSize(esize);
5069
5070 pdlg.CentreOnScreen();
5071#endif
5072 pdlg.ShowModal();
5073 if (pdlg.GetReturnCode() == wxID_OK) {
5074 wxListItem item;
5075 getListItemForInstrument(item, pdlg.GetInstrumentAdded());
5076 item.SetId(m_pListCtrlInstruments->GetItemCount());
5077 m_pListCtrlInstruments->InsertItem(item);
5078 m_pListCtrlInstruments->SetColumnWidth(0, wxLIST_AUTOSIZE);
5079 UpdateButtonsState();
5080 }
5081}
5082
5083void DashboardPreferencesDialog::OnInstrumentDelete(wxCommandEvent &event) {
5084 long itemIDWindow = -1;
5085 itemIDWindow = m_pListCtrlDashboards->GetNextItem(
5086 itemIDWindow, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED);
5087 long itemID = -1;
5088 itemID = m_pListCtrlInstruments->GetNextItem(itemID, wxLIST_NEXT_ALL,
5089 wxLIST_STATE_SELECTED);
5091 m_Config.Item(m_pListCtrlDashboards->GetItemData(itemIDWindow));
5092 InstrumentProperties *InstDel = NULL;
5093 if (cont) {
5094 InstrumentProperties *Inst = NULL;
5095 for (unsigned int i = 0; i < (cont->m_aInstrumentPropertyList.GetCount());
5096 i++) {
5097 Inst = cont->m_aInstrumentPropertyList.Item(i);
5098 if (Inst->m_aInstrument ==
5099 (int)m_pListCtrlInstruments->GetItemData(itemID) &&
5100 Inst->m_Listplace == itemID) {
5101 cont->m_aInstrumentPropertyList.Remove(Inst);
5102 InstDel = Inst;
5103 break;
5104 } else {
5105 if (Inst->m_Listplace > itemID) Inst->m_Listplace--;
5106 }
5107 }
5108 }
5109 m_pListCtrlInstruments->DeleteItem(itemID);
5110 if (InstDel) {
5111 cont->m_pDashboardWindow->SetInstrumentList(
5112 cont->m_aInstrumentList, &(cont->m_aInstrumentPropertyList));
5113 delete InstDel;
5114 }
5115 UpdateButtonsState();
5116}
5117
5118inline void GetFontData(OCPNFontButton *FontButton, wxFontData &UnScaledFont,
5119 wxFontData &ScaledFont, double scaler) {
5120 UnScaledFont = *(FontButton->GetFontData());
5121 ScaledFont = UnScaledFont;
5122 ScaledFont.SetChosenFont(UnScaledFont.GetChosenFont().Scaled(scaler));
5123}
5124
5125void DashboardPreferencesDialog::OnInstrumentEdit(wxCommandEvent &event) {
5126 // TODO: Instument options
5127 // m_Config = Arrayofdashboardwindows.
5128 long itemIDWindow = -1;
5129 itemIDWindow = m_pListCtrlDashboards->GetNextItem(
5130 itemIDWindow, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED);
5131 long itemID = -1;
5132 itemID = m_pListCtrlInstruments->GetNextItem(itemID, wxLIST_NEXT_ALL,
5133 wxLIST_STATE_SELECTED);
5134 // m_Config.
5135 // curSel = m_pListCtrlDashboards->GetItemData(itemWindow);
5136 // DashboardWindowContainer *cont = m_Config.Item(curSel);
5137 // if (cont) ....
5139 m_Config.Item(m_pListCtrlDashboards->GetItemData(itemIDWindow));
5140 if (!cont) return;
5141 InstrumentProperties *Inst = NULL;
5142 for (unsigned int i = 0; i < (cont->m_aInstrumentPropertyList.GetCount());
5143 i++) {
5144 Inst = cont->m_aInstrumentPropertyList.Item(
5145 i); // m_pListCtrlInstruments->GetItemData(itemID)
5146 if (Inst->m_aInstrument == (int)m_pListCtrlInstruments->GetItemData(
5147 itemID)) // Is for right Instrumenttype.
5148 {
5149 if (Inst->m_Listplace == itemID) break;
5150 }
5151 Inst = NULL;
5152 }
5153 if (!Inst) {
5154 Inst = new InstrumentProperties(m_pListCtrlInstruments->GetItemData(itemID),
5155 itemID);
5156 cont->m_aInstrumentPropertyList.Add(Inst);
5157 }
5158 EditDialog *Edit = new EditDialog(this, *Inst, wxID_ANY);
5159 Edit->Fit();
5160 bool DefaultFont = false;
5161 if (Edit->ShowModal() == wxID_OK) {
5162 DefaultFont = true;
5163 double scaler = 1.0;
5164 if (OCPN_GetWinDIPScaleFactor() < 1.0)
5165 scaler = 1.0 + OCPN_GetWinDIPScaleFactor() / 4;
5166 scaler = wxMax(1.0, scaler);
5167 if (Edit->m_fontPicker2->GetFont().Scaled(scaler) !=
5168 g_FontTitle.GetChosenFont() ||
5169 Edit->m_fontPicker2->GetSelectedColour() != g_FontTitle.GetColour())
5170 DefaultFont = false;
5171 if (Edit->m_fontPicker4->GetFont().Scaled(scaler) !=
5172 g_FontData.GetChosenFont() ||
5173 Edit->m_fontPicker4->GetSelectedColour() != g_FontData.GetColour())
5174 DefaultFont = false;
5175 if (Edit->m_fontPicker5->GetFont().Scaled(scaler) !=
5176 g_FontLabel.GetChosenFont() ||
5177 Edit->m_fontPicker5->GetSelectedColour() != g_FontLabel.GetColour())
5178 DefaultFont = false;
5179 if (Edit->m_fontPicker6->GetFont().Scaled(scaler) !=
5180 g_FontSmall.GetChosenFont() ||
5181 Edit->m_fontPicker6->GetSelectedColour() != g_FontSmall.GetColour())
5182 DefaultFont = false;
5183 wxColour dummy;
5184 GetGlobalColor(_T("DASHL"), &dummy);
5185 if (Edit->m_colourPicker1->GetColour() != dummy) DefaultFont = false;
5186 GetGlobalColor(_T("DASHB"), &dummy);
5187 if (Edit->m_colourPicker2->GetColour() != dummy) DefaultFont = false;
5188 GetGlobalColor(_T("DASHN"), &dummy);
5189 if (Edit->m_colourPicker3->GetColour() != dummy) DefaultFont = false;
5190 GetGlobalColor(_T("BLUE3"), &dummy);
5191 if (Edit->m_colourPicker4->GetColour() != dummy) DefaultFont = false;
5192 if (DefaultFont)
5193 cont->m_aInstrumentPropertyList.Remove(Inst);
5194 else {
5195 GetFontData(Edit->m_fontPicker2, Inst->m_USTitleFont, Inst->m_TitleFont,
5196 scaler);
5197 GetFontData(Edit->m_fontPicker4, Inst->m_USDataFont, Inst->m_DataFont,
5198 scaler);
5199 GetFontData(Edit->m_fontPicker5, Inst->m_USLabelFont, Inst->m_LabelFont,
5200 scaler);
5201 GetFontData(Edit->m_fontPicker6, Inst->m_USSmallFont, Inst->m_SmallFont,
5202 scaler);
5203 Inst->m_DataBackgroundColour = Edit->m_colourPicker2->GetColour();
5204 Inst->m_TitleBackgroundColour = Edit->m_colourPicker1->GetColour();
5205 Inst->m_Arrow_First_Colour = Edit->m_colourPicker3->GetColour();
5206 Inst->m_Arrow_Second_Colour = Edit->m_colourPicker4->GetColour();
5207 }
5208 }
5209 delete Edit;
5210 if (cont->m_pDashboardWindow) {
5211 cont->m_pDashboardWindow->SetInstrumentList(
5212 cont->m_aInstrumentList, &(cont->m_aInstrumentPropertyList));
5213 }
5214 if (DefaultFont) delete Inst;
5215}
5216
5217void DashboardPreferencesDialog::OnInstrumentUp(wxCommandEvent &event) {
5218 long itemIDWindow = -1;
5219 itemIDWindow = m_pListCtrlDashboards->GetNextItem(
5220 itemIDWindow, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED);
5221 long itemID = -1;
5222 itemID = m_pListCtrlInstruments->GetNextItem(itemID, wxLIST_NEXT_ALL,
5223 wxLIST_STATE_SELECTED);
5224 wxListItem item;
5225 item.SetId(itemID);
5226 item.SetMask(wxLIST_MASK_TEXT | wxLIST_MASK_IMAGE | wxLIST_MASK_DATA);
5227 m_pListCtrlInstruments->GetItem(item);
5228 item.SetId(itemID - 1);
5229 // item.SetImage(0); // image 0, by default
5230 // Now see if the Old itemId has an own Fontdata
5232 m_Config.Item(m_pListCtrlDashboards->GetItemData(itemIDWindow));
5233 if (cont) {
5234 InstrumentProperties *Inst = NULL;
5235 for (unsigned int i = 0; i < (cont->m_aInstrumentPropertyList.GetCount());
5236 i++) {
5237 Inst = cont->m_aInstrumentPropertyList.Item(i);
5238 if (Inst->m_Listplace == (itemID - 1)) Inst->m_Listplace = itemID;
5239 if (Inst->m_aInstrument ==
5240 (int)m_pListCtrlInstruments->GetItemData(itemID) &&
5241 Inst->m_Listplace == itemID) {
5242 cont->m_aInstrumentPropertyList.Item(i)->m_Listplace = itemID - 1;
5243 }
5244 }
5245 }
5246 m_pListCtrlInstruments->DeleteItem(itemID);
5247 m_pListCtrlInstruments->InsertItem(item);
5248 for (int i = 0; i < m_pListCtrlInstruments->GetItemCount(); i++)
5249 m_pListCtrlInstruments->SetItemState(i, 0, wxLIST_STATE_SELECTED);
5250
5251 m_pListCtrlInstruments->SetItemState(itemID - 1, wxLIST_STATE_SELECTED,
5252 wxLIST_STATE_SELECTED);
5253
5254 UpdateButtonsState();
5255}
5256
5257void DashboardPreferencesDialog::OnInstrumentDown(wxCommandEvent &event) {
5258 long itemIDWindow = -1;
5259 itemIDWindow = m_pListCtrlDashboards->GetNextItem(
5260 itemIDWindow, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED);
5261 long itemID = -1;
5262 itemID = m_pListCtrlInstruments->GetNextItem(itemID, wxLIST_NEXT_ALL,
5263 wxLIST_STATE_SELECTED);
5264
5265 wxListItem item;
5266 item.SetId(itemID);
5267 item.SetMask(wxLIST_MASK_TEXT | wxLIST_MASK_IMAGE | wxLIST_MASK_DATA);
5268 m_pListCtrlInstruments->GetItem(item);
5269 item.SetId(itemID + 1);
5270 // item.SetImage(0); // image 0, by default
5271 // Now see if the Old itemId has an own Fontdata
5273 m_Config.Item(m_pListCtrlDashboards->GetItemData(itemIDWindow));
5274 if (cont) {
5275 InstrumentProperties *Inst = NULL;
5276 for (unsigned int i = 0; i < (cont->m_aInstrumentPropertyList.GetCount());
5277 i++) {
5278 Inst = cont->m_aInstrumentPropertyList.Item(i);
5279 if (Inst->m_Listplace == (itemID + 1) &&
5280 Inst->m_aInstrument !=
5281 (int)m_pListCtrlInstruments->GetItemData(itemID))
5282 Inst->m_Listplace = itemID;
5283 if (Inst->m_aInstrument ==
5284 (int)m_pListCtrlInstruments->GetItemData(itemID) &&
5285 Inst->m_Listplace == itemID) {
5286 cont->m_aInstrumentPropertyList.Item(i)->m_Listplace = itemID + 1;
5287 break;
5288 }
5289 }
5290 }
5291 m_pListCtrlInstruments->DeleteItem(itemID);
5292 m_pListCtrlInstruments->InsertItem(item);
5293 for (int i = 0; i < m_pListCtrlInstruments->GetItemCount(); i++)
5294 m_pListCtrlInstruments->SetItemState(i, 0, wxLIST_STATE_SELECTED);
5295
5296 m_pListCtrlInstruments->SetItemState(itemID + 1, wxLIST_STATE_SELECTED,
5297 wxLIST_STATE_SELECTED);
5298
5299 UpdateButtonsState();
5300}
5301
5302void DashboardPreferencesDialog::OnDistanceUnitSelect(wxCommandEvent &event) {
5303 // Distance unit is changed so we need to update the SunLog value control.
5304 g_iDashDistanceUnit = m_pChoiceDistanceUnit->GetSelection() - 1;
5305 m_SumLogUnit->SetLabel(getUsrDistanceUnit_Plugin(g_iDashDistanceUnit) + ": ");
5306 m_pSumLogValue->SetValue(wxString::Format(
5307 "%.1f", toUsrDistance_Plugin(g_dSumLogNM, g_iDashDistanceUnit)));
5308}
5309
5310//----------------------------------------------------------------
5311//
5312// Add Instrument Dialog Implementation
5313//
5314//----------------------------------------------------------------
5315
5316AddInstrumentDlg::AddInstrumentDlg(wxWindow *pparent, wxWindowID id)
5317 : wxDialog(pparent, id, _("Add instrument"), wxDefaultPosition,
5318 wxDefaultSize, wxDEFAULT_DIALOG_STYLE) {
5319 wxBoxSizer *itemBoxSizer01 = new wxBoxSizer(wxVERTICAL);
5320 SetSizer(itemBoxSizer01);
5321 wxStaticText *itemStaticText01 =
5322 new wxStaticText(this, wxID_ANY, _("Select instrument to add:"),
5323 wxDefaultPosition, wxDefaultSize, 0);
5324 itemBoxSizer01->Add(itemStaticText01, 0, wxEXPAND | wxALL, 5);
5325
5326 int instImageRefSize = 20 * GetOCPNGUIToolScaleFactor_PlugIn();
5327
5328 wxImageList *imglist =
5329 new wxImageList(instImageRefSize, instImageRefSize, true, 2);
5330
5331 wxImage inst1 = wxBitmap(*_img_instrument).ConvertToImage();
5332 wxImage inst1s =
5333 inst1.Scale(instImageRefSize, instImageRefSize, wxIMAGE_QUALITY_HIGH);
5334 imglist->Add(wxBitmap(inst1s));
5335
5336 wxImage dial1 = wxBitmap(*_img_dial).ConvertToImage();
5337 wxImage dial1s =
5338 dial1.Scale(instImageRefSize, instImageRefSize, wxIMAGE_QUALITY_HIGH);
5339 imglist->Add(wxBitmap(dial1s));
5340
5341 wxSize dsize = GetOCPNCanvasWindow()->GetClientSize();
5342 int vsize = dsize.y * 50 / 100;
5343
5344#ifdef __OCPN__ANDROID__
5345 int dw, dh;
5346 wxDisplaySize(&dw, &dh);
5347 vsize = dh * 50 / 100;
5348#endif
5349
5350 m_pListCtrlInstruments = new wxListCtrl(
5351 this, wxID_ANY, wxDefaultPosition, wxSize(-1, vsize /*250, 180 */),
5352 wxLC_REPORT | wxLC_NO_HEADER | wxLC_SINGLE_SEL | wxLC_SORT_ASCENDING);
5353 itemBoxSizer01->Add(m_pListCtrlInstruments, 0, wxEXPAND | wxALL, 5);
5354 m_pListCtrlInstruments->AssignImageList(imglist, wxIMAGE_LIST_SMALL);
5355 m_pListCtrlInstruments->InsertColumn(0, _("Instruments"));
5356
5357 wxFont *pF = OCPNGetFont(_("Dialog"));
5358 m_pListCtrlInstruments->SetFont(*pF);
5359
5360#ifdef __OCPN__ANDROID__
5361 m_pListCtrlInstruments->GetHandle()->setStyleSheet(qtStyleSheet);
5363#endif
5364
5365 wxStdDialogButtonSizer *DialogButtonSizer =
5366 CreateStdDialogButtonSizer(wxOK | wxCANCEL);
5367 itemBoxSizer01->Add(DialogButtonSizer, 0, wxALIGN_RIGHT | wxALL, 5);
5368
5369 long ident = 0;
5370 for (unsigned int i = ID_DBP_I_POS; i < ID_DBP_LAST_ENTRY;
5371 i++) { // do not reference an instrument, but the last dummy entry in
5372 // the list
5373 wxListItem item;
5374 if (IsObsolete(i)) continue;
5375 getListItemForInstrument(item, i);
5376 item.SetId(ident);
5377 m_pListCtrlInstruments->InsertItem(item);
5378 id++;
5379 }
5380
5381 m_pListCtrlInstruments->SetColumnWidth(0, wxLIST_AUTOSIZE);
5382 m_pListCtrlInstruments->SetItemState(0, wxLIST_STATE_SELECTED,
5383 wxLIST_STATE_SELECTED);
5384 Fit();
5385}
5386
5387unsigned int AddInstrumentDlg::GetInstrumentAdded() {
5388 long itemID = -1;
5389 itemID = m_pListCtrlInstruments->GetNextItem(itemID, wxLIST_NEXT_ALL,
5390 wxLIST_STATE_SELECTED);
5391
5392 return (int)m_pListCtrlInstruments->GetItemData(itemID);
5393}
5394
5395//----------------------------------------------------------------
5396//
5397// Dashboard Window Implementation
5398//
5399//----------------------------------------------------------------
5400
5401// wxWS_EX_VALIDATE_RECURSIVELY required to push events to parents
5402DashboardWindow::DashboardWindow(wxWindow *pparent, wxWindowID id,
5403 wxAuiManager *auimgr, dashboard_pi *plugin,
5404 int orient, DashboardWindowContainer *mycont)
5405 : wxWindow(pparent, id, wxDefaultPosition, wxDefaultSize, 0) {
5406 // wxDialog::Create(pparent, id, _("tileMine"), wxDefaultPosition,
5407 // wxDefaultSize, wxDEFAULT_DIALOG_STYLE, _T("Dashboard"));
5408
5409 m_pauimgr = auimgr;
5410 m_plugin = plugin;
5411 m_Container = mycont;
5412
5413 // wx2.9 itemBoxSizer = new wxWrapSizer( orient );
5414 itemBoxSizer = new wxBoxSizer(orient);
5415 SetSizer(itemBoxSizer);
5416 Connect(wxEVT_SIZE, wxSizeEventHandler(DashboardWindow::OnSize), NULL, this);
5417 Connect(wxEVT_CONTEXT_MENU,
5418 wxContextMenuEventHandler(DashboardWindow::OnContextMenu), NULL,
5419 this);
5420 Connect(wxEVT_COMMAND_MENU_SELECTED,
5421 wxCommandEventHandler(DashboardWindow::OnContextMenuSelect), NULL,
5422 this);
5423
5424#ifdef __OCPN__ANDROID__
5425 Connect(wxEVT_LEFT_DOWN, wxMouseEventHandler(DashboardWindow::OnMouseEvent));
5426 Connect(wxEVT_LEFT_UP, wxMouseEventHandler(DashboardWindow::OnMouseEvent));
5427 Connect(wxEVT_MOTION, wxMouseEventHandler(DashboardWindow::OnMouseEvent));
5428
5429 GetHandle()->setAttribute(Qt::WA_AcceptTouchEvents);
5430 GetHandle()->grabGesture(Qt::PinchGesture);
5431 GetHandle()->grabGesture(Qt::PanGesture);
5432
5433 Connect(wxEVT_QT_PINCHGESTURE,
5434 (wxObjectEventFunction)(wxEventFunction)&DashboardWindow::
5435 OnEvtPinchGesture,
5436 NULL, this);
5437 Connect(
5438 wxEVT_QT_PANGESTURE,
5439 (wxObjectEventFunction)(wxEventFunction)&DashboardWindow::OnEvtPanGesture,
5440 NULL, this);
5441#endif
5442
5443 Hide();
5444
5445 m_binResize = false;
5446 m_binPinch = false;
5447}
5448
5449DashboardWindow::~DashboardWindow() {
5450 for (size_t i = 0; i < m_ArrayOfInstrument.GetCount(); i++) {
5451 DashboardInstrumentContainer *pdic = m_ArrayOfInstrument.Item(i);
5452 delete pdic;
5453 }
5454}
5455
5456#ifdef __OCPN__ANDROID__
5457void DashboardWindow::OnEvtPinchGesture(wxQT_PinchGestureEvent &event) {
5458 float zoom_gain = 0.3;
5459 float zoom_val;
5460 float total_zoom_val;
5461
5462 if (event.GetScaleFactor() > 1)
5463 zoom_val = ((event.GetScaleFactor() - 1.0) * zoom_gain) + 1.0;
5464 else
5465 zoom_val = 1.0 - ((1.0 - event.GetScaleFactor()) * zoom_gain);
5466
5467 if (event.GetTotalScaleFactor() > 1)
5468 total_zoom_val = ((event.GetTotalScaleFactor() - 1.0) * zoom_gain) + 1.0;
5469 else
5470 total_zoom_val = 1.0 - ((1.0 - event.GetTotalScaleFactor()) * zoom_gain);
5471
5472 wxAuiPaneInfo &pane = m_pauimgr->GetPane(this);
5473
5474 wxSize currentSize = wxSize(pane.floating_size.x, pane.floating_size.y);
5475 double aRatio = (double)currentSize.y / (double)currentSize.x;
5476
5477 wxSize par_size = GetOCPNCanvasWindow()->GetClientSize();
5478 wxPoint par_pos = wxPoint(pane.floating_pos.x, pane.floating_pos.y);
5479
5480 switch (event.GetState()) {
5481 case GestureStarted:
5482 m_binPinch = true;
5483 break;
5484
5485 case GestureUpdated:
5486 currentSize.y *= zoom_val;
5487 currentSize.x *= zoom_val;
5488
5489 if ((par_pos.y + currentSize.y) > par_size.y)
5490 currentSize.y = par_size.y - par_pos.y;
5491
5492 if ((par_pos.x + currentSize.x) > par_size.x)
5493 currentSize.x = par_size.x - par_pos.x;
5494
5496 currentSize.x = currentSize.y / aRatio;
5497
5498 currentSize.x = wxMax(currentSize.x, 150);
5499 currentSize.y = wxMax(currentSize.y, 150);
5500
5501 pane.FloatingSize(currentSize);
5502 m_pauimgr->Update();
5503
5504 break;
5505
5506 case GestureFinished: {
5507 if (itemBoxSizer->GetOrientation() == wxVERTICAL) {
5508 currentSize.y *= total_zoom_val;
5509 currentSize.x = currentSize.y / aRatio;
5510 } else {
5511 currentSize.x *= total_zoom_val;
5512 currentSize.y = currentSize.x * aRatio;
5513 }
5514
5515 // Bound the resulting size
5516 if ((par_pos.y + currentSize.y) > par_size.y)
5517 currentSize.y = par_size.y - par_pos.y;
5518
5519 if ((par_pos.x + currentSize.x) > par_size.x)
5520 currentSize.x = par_size.x - par_pos.x;
5521
5522 // not too small
5523 currentSize.x = wxMax(currentSize.x, 150);
5524 currentSize.y = wxMax(currentSize.y, 150);
5525
5526 // Try a manual layout of the window, to estimate a good primary size..
5527
5528 // vertical
5529 if (itemBoxSizer->GetOrientation() == wxVERTICAL) {
5530 int total_y = 0;
5531 for (unsigned int i = 0; i < m_ArrayOfInstrument.size(); i++) {
5532 DashboardInstrument *inst =
5533 m_ArrayOfInstrument.Item(i)->m_pInstrument;
5534 wxSize is =
5535 inst->GetSize(itemBoxSizer->GetOrientation(), currentSize);
5536 total_y += is.y;
5537 }
5538
5539 currentSize.y = total_y;
5540 }
5541
5542 pane.FloatingSize(currentSize);
5543
5544 // Reshow the window
5545 for (unsigned int i = 0; i < m_ArrayOfInstrument.size(); i++) {
5546 DashboardInstrument *inst = m_ArrayOfInstrument.Item(i)->m_pInstrument;
5547 inst->Show();
5548 }
5549
5550 m_pauimgr->Update();
5551
5552 m_binPinch = false;
5553 m_binResize = false;
5554
5555 break;
5556 }
5557
5558 case GestureCanceled:
5559 m_binPinch = false;
5560 m_binResize = false;
5561 break;
5562
5563 default:
5564 break;
5565 }
5566}
5567
5568void DashboardWindow::OnEvtPanGesture(wxQT_PanGestureEvent &event) {
5569 if (m_binPinch) return;
5570
5571 if (m_binResize) return;
5572
5573 int x = event.GetOffset().x;
5574 int y = event.GetOffset().y;
5575
5576 int lx = event.GetLastOffset().x;
5577 int ly = event.GetLastOffset().y;
5578
5579 int dx = x - lx;
5580 int dy = y - ly;
5581
5582 switch (event.GetState()) {
5583 case GestureStarted:
5584 if (m_binPan) break;
5585
5586 m_binPan = true;
5587 break;
5588
5589 case GestureUpdated:
5590 if (m_binPan) {
5591 wxSize par_size = GetOCPNCanvasWindow()->GetClientSize();
5592 wxPoint par_pos_old = ClientToScreen(wxPoint(0, 0)); // GetPosition();
5593
5594 wxPoint par_pos = par_pos_old;
5595 par_pos.x += dx;
5596 par_pos.y += dy;
5597
5598 par_pos.x = wxMax(par_pos.x, 0);
5599 par_pos.y = wxMax(par_pos.y, 0);
5600
5601 wxSize mySize = GetSize();
5602
5603 if ((par_pos.y + mySize.y) > par_size.y)
5604 par_pos.y = par_size.y - mySize.y;
5605
5606 if ((par_pos.x + mySize.x) > par_size.x)
5607 par_pos.x = par_size.x - mySize.x;
5608
5609 wxAuiPaneInfo &pane = m_pauimgr->GetPane(this);
5610 pane.FloatingPosition(par_pos).Float();
5611 m_pauimgr->Update();
5612 }
5613 break;
5614
5615 case GestureFinished:
5616 if (m_binPan) {
5617 }
5618 m_binPan = false;
5619
5620 break;
5621
5622 case GestureCanceled:
5623 m_binPan = false;
5624 break;
5625
5626 default:
5627 break;
5628 }
5629}
5630
5631void DashboardWindow::OnMouseEvent(wxMouseEvent &event) {
5632 if (m_binPinch) return;
5633
5634 if (m_binResize) {
5635 wxAuiPaneInfo &pane = m_pauimgr->GetPane(this);
5636 wxSize currentSize = wxSize(pane.floating_size.x, pane.floating_size.y);
5637 double aRatio = (double)currentSize.y / (double)currentSize.x;
5638
5639 wxSize par_size = GetOCPNCanvasWindow()->GetClientSize();
5640 wxPoint par_pos = wxPoint(pane.floating_pos.x, pane.floating_pos.y);
5641
5642 if (event.LeftDown()) {
5643 m_resizeStartPoint = event.GetPosition();
5644 m_resizeStartSize = currentSize;
5645 m_binResize2 = true;
5646 }
5647
5648 if (m_binResize2) {
5649 if (event.Dragging()) {
5650 wxPoint p = event.GetPosition();
5651
5652 wxSize dragSize = m_resizeStartSize;
5653
5654 dragSize.y += p.y - m_resizeStartPoint.y;
5655 dragSize.x += p.x - m_resizeStartPoint.x;
5656 ;
5657
5658 if ((par_pos.y + dragSize.y) > par_size.y)
5659 dragSize.y = par_size.y - par_pos.y;
5660
5661 if ((par_pos.x + dragSize.x) > par_size.x)
5662 dragSize.x = par_size.x - par_pos.x;
5663
5665 // dragSize.x = dragSize.y / aRatio;
5666
5667 // not too small
5668 dragSize.x = wxMax(dragSize.x, 150);
5669 dragSize.y = wxMax(dragSize.y, 150);
5670
5671 pane.FloatingSize(dragSize);
5672 m_pauimgr->Update();
5673 }
5674
5675 if (event.LeftUp()) {
5676 wxPoint p = event.GetPosition();
5677
5678 wxSize dragSize = m_resizeStartSize;
5679
5680 dragSize.y += p.y - m_resizeStartPoint.y;
5681 dragSize.x += p.x - m_resizeStartPoint.x;
5682 ;
5683
5684 if ((par_pos.y + dragSize.y) > par_size.y)
5685 dragSize.y = par_size.y - par_pos.y;
5686
5687 if ((par_pos.x + dragSize.x) > par_size.x)
5688 dragSize.x = par_size.x - par_pos.x;
5689
5690 // not too small
5691 dragSize.x = wxMax(dragSize.x, 150);
5692 dragSize.y = wxMax(dragSize.y, 150);
5693 /*
5694 for( unsigned int i=0; i<m_ArrayOfInstrument.size(); i++
5695 ) { DashboardInstrument* inst =
5696 m_ArrayOfInstrument.Item(i)->m_pInstrument; inst->Show();
5697 }
5698 */
5699 pane.FloatingSize(dragSize);
5700 m_pauimgr->Update();
5701
5702 m_binResize = false;
5703 m_binResize2 = false;
5704 }
5705 }
5706 }
5707}
5708#endif
5709
5710void DashboardWindow::OnSize(wxSizeEvent &event) {
5711 event.Skip();
5712 for (unsigned int i = 0; i < m_ArrayOfInstrument.size(); i++) {
5713 DashboardInstrument *inst = m_ArrayOfInstrument.Item(i)->m_pInstrument;
5714 inst->SetMinSize(
5715 inst->GetSize(itemBoxSizer->GetOrientation(), GetClientSize()));
5716 }
5717 Layout();
5718 Refresh();
5719 // Capture the user adjusted docked Dashboard size
5720 this->m_Container->m_best_size = event.GetSize();
5721}
5722
5723void DashboardWindow::OnContextMenu(wxContextMenuEvent &event) {
5724 wxMenu *contextMenu = new wxMenu();
5725
5726#ifdef __WXQT__
5727 wxFont *pf = OCPNGetFont(_("Menu"));
5728
5729 // add stuff
5730 wxMenuItem *item1 =
5731 new wxMenuItem(contextMenu, ID_DASH_PREFS, _("Preferences..."));
5732 item1->SetFont(*pf);
5733 contextMenu->Append(item1);
5734
5735 wxMenuItem *item2 =
5736 new wxMenuItem(contextMenu, ID_DASH_RESIZE, _("Resize..."));
5737 item2->SetFont(*pf);
5738 contextMenu->Append(item2);
5739
5740#else
5741
5742 wxAuiPaneInfo &pane = m_pauimgr->GetPane(this);
5743 if (pane.IsOk() && pane.IsDocked()) {
5744 contextMenu->Append(ID_DASH_UNDOCK, _("Undock"));
5745 }
5746 wxMenuItem *btnVertical =
5747 contextMenu->AppendRadioItem(ID_DASH_VERTICAL, _("Vertical"));
5748 btnVertical->Check(itemBoxSizer->GetOrientation() == wxVERTICAL);
5749 wxMenuItem *btnHorizontal =
5750 contextMenu->AppendRadioItem(ID_DASH_HORIZONTAL, _("Horizontal"));
5751 btnHorizontal->Check(itemBoxSizer->GetOrientation() == wxHORIZONTAL);
5752 contextMenu->AppendSeparator();
5753
5754 m_plugin->PopulateContextMenu(contextMenu);
5755
5756 contextMenu->AppendSeparator();
5757 contextMenu->Append(ID_DASH_PREFS, _("Preferences..."));
5758
5759#endif
5760
5761 PopupMenu(contextMenu);
5762 delete contextMenu;
5763}
5764
5765void DashboardWindow::OnContextMenuSelect(wxCommandEvent &event) {
5766 if (event.GetId() < ID_DASH_PREFS) { // Toggle dashboard visibility
5767 if (m_plugin->GetDashboardWindowShownCount() > 1 || event.IsChecked())
5768 m_plugin->ShowDashboard(event.GetId() - 1, event.IsChecked());
5769 else
5770 m_plugin->ShowDashboard(event.GetId() - 1, true);
5771
5772 SetToolbarItemState(m_plugin->GetToolbarItemId(),
5773 m_plugin->GetDashboardWindowShownCount() != 0);
5774 }
5775
5776 switch (event.GetId()) {
5777 case ID_DASH_PREFS: {
5778 wxAuiPaneInfo &pane = m_pauimgr->GetPane(this);
5779 // Capture the dashboard's floating_pos before update.
5780 wxPoint fp = pane.floating_pos;
5781 m_plugin->ShowPreferencesDialog(this);
5782
5783 /*
5784 This function sets the correct size of the edited dashboard with the
5785 previous floating position instead of the default that is otherwise used.
5786 If the panel is docked the size is instead specified by the docking logic
5787 */
5788 if (!pane.IsDocked())
5789 ChangePaneOrientation(GetSizerOrientation(), true, fp.x, fp.y);
5790
5791 if (g_bUseInternSumLog) m_plugin->UpdateSumLog(false);
5792 return; // Does it's own save.
5793 }
5794 case ID_DASH_RESIZE: {
5795 /*
5796 for( unsigned int i=0; i<m_ArrayOfInstrument.size(); i++ ) {
5797 DashboardInstrument* inst =
5798 m_ArrayOfInstrument.Item(i)->m_pInstrument; inst->Hide();
5799 }
5800 */
5801 m_binResize = true;
5802
5803 return;
5804 }
5805 case ID_DASH_VERTICAL: {
5806 ChangePaneOrientation(wxVERTICAL, true);
5807 m_Container->m_sOrientation = _T("V");
5808 break;
5809 }
5810 case ID_DASH_HORIZONTAL: {
5811 ChangePaneOrientation(wxHORIZONTAL, true);
5812 m_Container->m_sOrientation = _T("H");
5813 break;
5814 }
5815 case ID_DASH_UNDOCK: {
5816 ChangePaneOrientation(GetSizerOrientation(), true);
5817 return; // Nothing changed so nothing need be saved
5818 }
5819 }
5820 m_plugin->SaveConfig();
5821}
5822
5823void DashboardWindow::SetColorScheme(PI_ColorScheme cs) {
5824 DimeWindow(this);
5825
5826 // Improve appearance, especially in DUSK or NIGHT palette
5827 wxColour col = g_BackgroundColor;
5828
5829 if (!g_ForceBackgroundColor) GetGlobalColor(_T("DASHL"), &col);
5830 SetBackgroundColour(col);
5831
5832 Refresh(false);
5833}
5834
5835void DashboardWindow::ChangePaneOrientation(int orient, bool updateAUImgr,
5836 int fpx, int fpy) {
5837 m_pauimgr->DetachPane(this);
5838 SetSizerOrientation(orient);
5839 bool vertical = orient == wxVERTICAL;
5840 // wxSize sz = GetSize( orient, wxDefaultSize );
5841 wxSize sz = GetMinSize();
5842 // We must change Name to reset AUI perpective
5843 m_Container->m_sName = MakeName();
5844 m_pauimgr->AddPane(this, wxAuiPaneInfo()
5845 .Name(m_Container->m_sName)
5846 .Caption(m_Container->m_sCaption)
5847 .CaptionVisible(true)
5848 .TopDockable(!vertical)
5849 .BottomDockable(!vertical)
5850 .LeftDockable(vertical)
5851 .RightDockable(vertical)
5852 .MinSize(sz)
5853 .BestSize(sz)
5854 .FloatingSize(sz)
5855 .FloatingPosition(fpx, fpy)
5856 .Float()
5857 .Show(m_Container->m_bIsVisible));
5858
5859#ifdef __OCPN__ANDROID__
5860 wxAuiPaneInfo &pane = m_pauimgr->GetPane(this);
5861 pane.Dockable(false);
5862#endif
5863
5864 if (updateAUImgr) m_pauimgr->Update();
5865}
5866
5867void DashboardWindow::SetSizerOrientation(int orient) {
5868 itemBoxSizer->SetOrientation(orient);
5869 /* We must reset all MinSize to ensure we start with new default */
5870 wxWindowListNode *node = GetChildren().GetFirst();
5871 while (node) {
5872 node->GetData()->SetMinSize(wxDefaultSize);
5873 node = node->GetNext();
5874 }
5875 SetMinSize(wxDefaultSize);
5876 Fit();
5877 SetMinSize(itemBoxSizer->GetMinSize());
5878}
5879
5880int DashboardWindow::GetSizerOrientation() {
5881 return itemBoxSizer->GetOrientation();
5882}
5883
5884bool isArrayIntEqual(const wxArrayInt &l1, const wxArrayOfInstrument &l2) {
5885 if (l1.GetCount() != l2.GetCount()) return false;
5886
5887 for (size_t i = 0; i < l1.GetCount(); i++)
5888 if (l1.Item(i) != l2.Item(i)->m_ID) return false;
5889
5890 return true;
5891}
5892
5893bool DashboardWindow::isInstrumentListEqual(const wxArrayInt &list) {
5894 return isArrayIntEqual(list, m_ArrayOfInstrument);
5895}
5896
5897void DashboardWindow::SetInstrumentList(
5898 wxArrayInt list, wxArrayOfInstrumentProperties *InstrumentPropertyList) {
5899 /* options
5900 ID_DBP_D_SOG: config max value, show STW optional
5901 ID_DBP_D_COG: +SOG +HDG? +BRG?
5902 ID_DBP_D_AWS: config max value. Two arrows for AWS+TWS?
5903 ID_DBP_D_VMG: config max value
5904 ID_DBP_I_DPT: config unit (meter, feet, fathoms)
5905 ID_DBP_D_DPT: show temp optional
5906 // compass: use COG or HDG
5907 // velocity range
5908 // rudder range
5909
5910 */
5911 InstrumentProperties *Properties;
5912 m_ArrayOfInstrument.Clear();
5913 itemBoxSizer->Clear(true);
5914 for (size_t i = 0; i < list.GetCount(); i++) {
5915 int id = list.Item(i);
5916 Properties = NULL;
5917 for (size_t j = 0; j < InstrumentPropertyList->GetCount(); j++) {
5918 if (InstrumentPropertyList->Item(j)->m_aInstrument == id &&
5919 InstrumentPropertyList->Item(j)->m_Listplace == (int)i) {
5920 Properties = InstrumentPropertyList->Item(j);
5921 break;
5922 }
5923 }
5924 DashboardInstrument *instrument = NULL;
5925 switch (id) {
5926 case ID_DBP_I_POS:
5927 instrument = new DashboardInstrument_Position(
5928 this, wxID_ANY, getInstrumentCaption(id), Properties);
5929 break;
5930 case ID_DBP_I_SOG:
5931 instrument = new DashboardInstrument_Single(
5932 this, wxID_ANY, getInstrumentCaption(id), Properties,
5933 OCPN_DBP_STC_SOG, _T("%5.1f"));
5934 break;
5935 case ID_DBP_D_SOG:
5936 instrument = new DashboardInstrument_Speedometer(
5937 this, wxID_ANY, getInstrumentCaption(id), Properties,
5938 OCPN_DBP_STC_SOG, 0, g_iDashSpeedMax);
5939 ((DashboardInstrument_Dial *)instrument)
5940 ->SetOptionLabel(g_iDashSpeedMax / 20 + 1, DIAL_LABEL_HORIZONTAL);
5941 //(DashboardInstrument_Dial *)instrument->SetOptionMarker(0.1,
5942 // DIAL_MARKER_SIMPLE, 5);
5943 ((DashboardInstrument_Dial *)instrument)
5944 ->SetOptionMarker(0.5, DIAL_MARKER_SIMPLE, 2);
5945 ((DashboardInstrument_Dial *)instrument)
5946 ->SetOptionExtraValue(OCPN_DBP_STC_STW, "STW %.1f",
5947 DIAL_POSITION_BOTTOMMIDDLE);
5948 break;
5949 case ID_DBP_D_STW:
5950 instrument = new DashboardInstrument_Speedometer(
5951 this, wxID_ANY, getInstrumentCaption(id), Properties,
5952 OCPN_DBP_STC_STW, 0, g_iDashSpeedMax);
5953 ((DashboardInstrument_Dial *)instrument)
5954 ->SetOptionLabel(g_iDashSpeedMax / 20 + 1, DIAL_LABEL_HORIZONTAL);
5955 //(DashboardInstrument_Dial *)instrument->SetOptionMarker(0.1,
5956 // DIAL_MARKER_SIMPLE, 5);
5957 ((DashboardInstrument_Dial *)instrument)
5958 ->SetOptionMarker(0.5, DIAL_MARKER_SIMPLE, 2);
5959 ((DashboardInstrument_Dial *)instrument)
5960 ->SetOptionExtraValue(OCPN_DBP_STC_SOG, "SOG %.1f",
5961 DIAL_POSITION_BOTTOMMIDDLE);
5962 break;
5963 case ID_DBP_I_COG:
5964 instrument = new DashboardInstrument_Single(
5965 this, wxID_ANY, getInstrumentCaption(id), Properties,
5966 OCPN_DBP_STC_COG, _T("%03.0f"));
5967 break;
5968 case ID_DBP_M_COG:
5969 instrument = new DashboardInstrument_Single(
5970 this, wxID_ANY, getInstrumentCaption(id), Properties,
5971 OCPN_DBP_STC_MCOG, _T("%03.0f"));
5972 break;
5973 case ID_DBP_D_COG:
5974 instrument = new DashboardInstrument_Compass(
5975 this, wxID_ANY, getInstrumentCaption(id), Properties,
5976 OCPN_DBP_STC_COG);
5977 ((DashboardInstrument_Dial *)instrument)
5978 ->SetOptionMarker(5, DIAL_MARKER_SIMPLE, 2);
5979 ((DashboardInstrument_Dial *)instrument)
5980 ->SetOptionLabel(30, DIAL_LABEL_ROTATED);
5981 ((DashboardInstrument_Dial *)instrument)
5982 ->SetOptionExtraValue(OCPN_DBP_STC_SOG, _T("SOG\n%.2f"),
5983 DIAL_POSITION_BOTTOMLEFT);
5984 break;
5985 case ID_DBP_D_HDT:
5986 instrument = new DashboardInstrument_Compass(
5987 this, wxID_ANY, getInstrumentCaption(id), Properties,
5988 OCPN_DBP_STC_HDT);
5989 ((DashboardInstrument_Dial *)instrument)
5990 ->SetOptionMarker(5, DIAL_MARKER_SIMPLE, 2);
5991 ((DashboardInstrument_Dial *)instrument)
5992 ->SetOptionLabel(30, DIAL_LABEL_ROTATED);
5993 ((DashboardInstrument_Dial *)instrument)
5994 ->SetOptionExtraValue(OCPN_DBP_STC_STW, _T("STW\n%.1f"),
5995 DIAL_POSITION_BOTTOMLEFT);
5996 break;
5997 case ID_DBP_I_STW:
5998 instrument = new DashboardInstrument_Single(
5999 this, wxID_ANY, getInstrumentCaption(id), Properties,
6000 OCPN_DBP_STC_STW, _T("%.1f"));
6001 break;
6002 case ID_DBP_I_HDT: // true heading
6003 // TODO: Option True or Magnetic
6004 instrument = new DashboardInstrument_Single(
6005 this, wxID_ANY, getInstrumentCaption(id), Properties,
6006 OCPN_DBP_STC_HDT, _T("%03.0f"));
6007 break;
6008 case ID_DBP_I_HDM: // magnetic heading
6009 instrument = new DashboardInstrument_Single(
6010 this, wxID_ANY, getInstrumentCaption(id), Properties,
6011 OCPN_DBP_STC_HDM, _T("%03.0f"));
6012 break;
6013 case ID_DBP_D_AW:
6014 case ID_DBP_D_AWA:
6015 instrument = new DashboardInstrument_Wind(this, wxID_ANY,
6016 getInstrumentCaption(id),
6017 Properties, OCPN_DBP_STC_AWA);
6018 ((DashboardInstrument_Dial *)instrument)
6019 ->SetOptionMainValue(_T("%.0f"), DIAL_POSITION_BOTTOMLEFT);
6020 ((DashboardInstrument_Dial *)instrument)
6021 ->SetOptionExtraValue(OCPN_DBP_STC_AWS, _T("%.1f"),
6022 DIAL_POSITION_INSIDE);
6023 break;
6024 case ID_DBP_I_AWS:
6025 instrument = new DashboardInstrument_Single(
6026 this, wxID_ANY, getInstrumentCaption(id), Properties,
6027 OCPN_DBP_STC_AWS, _T("%.1f"));
6028 break;
6029 case ID_DBP_D_AWS:
6030 instrument = new DashboardInstrument_Speedometer(
6031 this, wxID_ANY, getInstrumentCaption(id), Properties,
6032 OCPN_DBP_STC_AWS, 0, 45);
6033 ((DashboardInstrument_Dial *)instrument)
6034 ->SetOptionLabel(5, DIAL_LABEL_HORIZONTAL);
6035 ((DashboardInstrument_Dial *)instrument)
6036 ->SetOptionMarker(1, DIAL_MARKER_SIMPLE, 5);
6037 ((DashboardInstrument_Dial *)instrument)
6038 ->SetOptionMainValue(_T("A %.1f"), DIAL_POSITION_BOTTOMLEFT);
6039 ((DashboardInstrument_Dial *)instrument)
6040 ->SetOptionExtraValue(OCPN_DBP_STC_TWS, _T("T %.1f"),
6041 DIAL_POSITION_BOTTOMRIGHT);
6042 break;
6043 case ID_DBP_D_TW: // True Wind angle +-180 degr on boat axis
6044 instrument = new DashboardInstrument_TrueWindAngle(
6045 this, wxID_ANY, getInstrumentCaption(id), Properties,
6046 OCPN_DBP_STC_TWA);
6047 ((DashboardInstrument_Dial *)instrument)
6048 ->SetOptionMainValue(_T("%.0f"), DIAL_POSITION_BOTTOMLEFT);
6049 ((DashboardInstrument_Dial *)instrument)
6050 ->SetOptionExtraValue(OCPN_DBP_STC_TWS, _T("%.1f"),
6051 DIAL_POSITION_INSIDE);
6052 break;
6053 case ID_DBP_D_AWA_TWA: // App/True Wind angle +-180 degr on boat axis
6054 instrument = new DashboardInstrument_AppTrueWindAngle(
6055 this, wxID_ANY, getInstrumentCaption(id), Properties,
6056 OCPN_DBP_STC_AWA);
6057 ((DashboardInstrument_Dial *)instrument)->SetCapFlag(OCPN_DBP_STC_TWA);
6058 ((DashboardInstrument_Dial *)instrument)
6059 ->SetOptionMainValue(_T("%.0f"), DIAL_POSITION_NONE);
6060 ((DashboardInstrument_Dial *)instrument)
6061 ->SetOptionExtraValue(OCPN_DBP_STC_TWS, _T("%.1f"),
6062 DIAL_POSITION_NONE);
6063 ((DashboardInstrument_Dial *)instrument)
6064 ->SetOptionExtraValue(OCPN_DBP_STC_AWS, _T("%.1f"),
6065 DIAL_POSITION_NONE);
6066 break;
6067 case ID_DBP_D_TWD: // True Wind direction
6068 instrument = new DashboardInstrument_WindCompass(
6069 this, wxID_ANY, getInstrumentCaption(id), Properties,
6070 OCPN_DBP_STC_TWD);
6071 ((DashboardInstrument_Dial *)instrument)
6072 ->SetOptionMainValue(_T("%.0f"), DIAL_POSITION_BOTTOMLEFT);
6073 ((DashboardInstrument_Dial *)instrument)
6074 ->SetOptionExtraValue(OCPN_DBP_STC_TWS2, _T("%.1f"),
6075 DIAL_POSITION_INSIDE);
6076 break;
6077 case ID_DBP_I_ALTI:
6078 instrument = new DashboardInstrument_Single(
6079 this, wxID_ANY, getInstrumentCaption(id), Properties,
6080 OCPN_DBP_STC_ALTI, _T("%6.1f"));
6081 break;
6082 case ID_DBP_D_ALTI:
6083 instrument = new DashboardInstrument_Altitude(
6084 this, wxID_ANY, getInstrumentCaption(id), Properties);
6085 break;
6086 case ID_DBP_I_DPT:
6087 instrument = new DashboardInstrument_Single(
6088 this, wxID_ANY, getInstrumentCaption(id), Properties,
6089 OCPN_DBP_STC_DPT, _T("%5.2f"));
6090 break;
6091 case ID_DBP_D_DPT:
6092 instrument = new DashboardInstrument_Depth(
6093 this, wxID_ANY, getInstrumentCaption(id), Properties);
6094 break;
6095 case ID_DBP_I_TMP: // water temperature
6096 instrument = new DashboardInstrument_Single(
6097 this, wxID_ANY, getInstrumentCaption(id), Properties,
6098 OCPN_DBP_STC_TMP, _T("%2.1f"));
6099 break;
6100 case ID_DBP_I_MDA: // barometric pressure
6101 instrument = new DashboardInstrument_Single(
6102 this, wxID_ANY, getInstrumentCaption(id), Properties,
6103 OCPN_DBP_STC_MDA, _T("%5.1f"));
6104 break;
6105 case ID_DBP_D_MDA: // barometric pressure
6106 instrument = new DashboardInstrument_Speedometer(
6107 this, wxID_ANY, getInstrumentCaption(id), Properties,
6108 OCPN_DBP_STC_MDA, 938, 1088);
6109 ((DashboardInstrument_Dial *)instrument)
6110 ->SetOptionLabel(15, DIAL_LABEL_HORIZONTAL);
6111 ((DashboardInstrument_Dial *)instrument)
6112 ->SetOptionMarker(7.5, DIAL_MARKER_SIMPLE, 1);
6113 ((DashboardInstrument_Dial *)instrument)
6114 ->SetOptionMainValue(_T("%5.1f"), DIAL_POSITION_INSIDE);
6115 break;
6116 case ID_DBP_I_ATMP: // air temperature
6117 instrument = new DashboardInstrument_Single(
6118 this, wxID_ANY, getInstrumentCaption(id), Properties,
6119 OCPN_DBP_STC_ATMP, _T("%2.1f"));
6120 break;
6121 case ID_DBP_I_VLW1: // Trip Log
6122 instrument = new DashboardInstrument_Single(
6123 this, wxID_ANY, getInstrumentCaption(id), Properties,
6124 OCPN_DBP_STC_VLW1, _T("%2.1f"));
6125 break;
6126
6127 case ID_DBP_I_VLW2: // Sum Log
6128 instrument = new DashboardInstrument_Single(
6129 this, wxID_ANY, getInstrumentCaption(id), Properties,
6130 OCPN_DBP_STC_VLW2, _T("%2.1f"));
6131 break;
6132
6133 case ID_DBP_I_TWA: // true wind angle
6134 instrument = new DashboardInstrument_Single(
6135 this, wxID_ANY, getInstrumentCaption(id), Properties,
6136 OCPN_DBP_STC_TWA, _T("%5.0f"));
6137 break;
6138 case ID_DBP_I_TWD: // true wind direction
6139 instrument = new DashboardInstrument_Single(
6140 this, wxID_ANY, getInstrumentCaption(id), Properties,
6141 OCPN_DBP_STC_TWD, _T("%3.0f"));
6142 break;
6143 case ID_DBP_I_TWS: // true wind speed
6144 instrument = new DashboardInstrument_Single(
6145 this, wxID_ANY, getInstrumentCaption(id), Properties,
6146 OCPN_DBP_STC_TWS, _T("%2.1f"));
6147 break;
6148 case ID_DBP_I_AWA: // apparent wind angle
6149 instrument = new DashboardInstrument_Single(
6150 this, wxID_ANY, getInstrumentCaption(id), Properties,
6151 OCPN_DBP_STC_AWA, _T("%3.0f"));
6152 break;
6153 case ID_DBP_I_VMGW: // VMG based on wind and STW
6154 instrument = new DashboardInstrument_Single(
6155 this, wxID_ANY, getInstrumentCaption(id), Properties,
6156 OCPN_DBP_STC_VMGW, _T("%2.1f"));
6157 break;
6158 case ID_DBP_I_VMG:
6159 instrument = new DashboardInstrument_Single(
6160 this, wxID_ANY, getInstrumentCaption(id), Properties,
6161 OCPN_DBP_STC_VMG, _T("%5.1f"));
6162 break;
6163 case ID_DBP_D_VMG:
6164 instrument = new DashboardInstrument_Speedometer(
6165 this, wxID_ANY, getInstrumentCaption(id), Properties,
6166 OCPN_DBP_STC_VMG, 0, g_iDashSpeedMax);
6167 ((DashboardInstrument_Dial *)instrument)
6168 ->SetOptionLabel(1, DIAL_LABEL_HORIZONTAL);
6169 ((DashboardInstrument_Dial *)instrument)
6170 ->SetOptionMarker(0.5, DIAL_MARKER_SIMPLE, 2);
6171 ((DashboardInstrument_Dial *)instrument)
6172 ->SetOptionExtraValue(OCPN_DBP_STC_SOG, _T("SOG\n%.1f"),
6173 DIAL_POSITION_BOTTOMLEFT);
6174 break;
6175 case ID_DBP_I_RSA:
6176 instrument = new DashboardInstrument_Single(
6177 this, wxID_ANY, getInstrumentCaption(id), Properties,
6178 OCPN_DBP_STC_RSA, _T("%5.0f"));
6179 break;
6180 case ID_DBP_D_RSA:
6181 instrument = new DashboardInstrument_RudderAngle(
6182 this, wxID_ANY, getInstrumentCaption(id), Properties);
6183 break;
6184 case ID_DBP_I_SAT:
6185 instrument = new DashboardInstrument_Single(
6186 this, wxID_ANY, getInstrumentCaption(id), Properties,
6187 OCPN_DBP_STC_SAT, _T("%5.0f"));
6188 break;
6189 case ID_DBP_D_GPS:
6190 instrument = new DashboardInstrument_GPS(
6191 this, wxID_ANY, getInstrumentCaption(id), Properties);
6192 break;
6193 case ID_DBP_I_PTR:
6194 instrument = new DashboardInstrument_Position(
6195 this, wxID_ANY, getInstrumentCaption(id), Properties,
6196 OCPN_DBP_STC_PLA, OCPN_DBP_STC_PLO);
6197 break;
6198 case ID_DBP_I_GPSUTC:
6199 instrument = new DashboardInstrument_Clock(
6200 this, wxID_ANY, getInstrumentCaption(id), Properties);
6201 break;
6202 case ID_DBP_I_SUN:
6203 instrument = new DashboardInstrument_Sun(
6204 this, wxID_ANY, getInstrumentCaption(id), Properties);
6205 break;
6206 case ID_DBP_D_MON:
6207 instrument = new DashboardInstrument_Moon(
6208 this, wxID_ANY, getInstrumentCaption(id), Properties);
6209 break;
6210 case ID_DBP_D_WDH:
6211 instrument = new DashboardInstrument_WindDirHistory(
6212 this, wxID_ANY, getInstrumentCaption(id), Properties);
6213 break;
6214 case ID_DBP_D_BPH:
6215 instrument = new DashboardInstrument_BaroHistory(
6216 this, wxID_ANY, getInstrumentCaption(id), Properties);
6217 break;
6218 case ID_DBP_I_FOS:
6219 instrument = new DashboardInstrument_FromOwnship(
6220 this, wxID_ANY, getInstrumentCaption(id), Properties);
6221 break;
6222 case ID_DBP_I_PITCH:
6223 instrument = new DashboardInstrument_Single(
6224 this, wxID_ANY, getInstrumentCaption(id), Properties,
6225 OCPN_DBP_STC_PITCH, _T("%2.1f"));
6226 break;
6227 case ID_DBP_I_HEEL:
6228 instrument = new DashboardInstrument_Single(
6229 this, wxID_ANY, getInstrumentCaption(id), Properties,
6230 OCPN_DBP_STC_HEEL, _T("%2.1f"));
6231 break;
6232 // any clock display with "LCL" in the format string is converted from
6233 // UTC to local TZ
6234 case ID_DBP_I_SUNLCL:
6235 instrument = new DashboardInstrument_Sun(
6236 this, wxID_ANY, getInstrumentCaption(id), Properties,
6237 _T( "%02i:%02i:%02i LCL" ));
6238 break;
6239 case ID_DBP_I_GPSLCL:
6240 instrument = new DashboardInstrument_Clock(
6241 this, wxID_ANY, getInstrumentCaption(id), Properties,
6242 OCPN_DBP_STC_CLK, _T( "%02i:%02i:%02i LCL" ));
6243 break;
6244 case ID_DBP_I_CPULCL:
6245 instrument = new DashboardInstrument_CPUClock(
6246 this, wxID_ANY, getInstrumentCaption(id), Properties,
6247 _T( "%02i:%02i:%02i LCL" ));
6248 break;
6249 case ID_DBP_I_HUM:
6250 instrument = new DashboardInstrument_Single(
6251 this, wxID_ANY, getInstrumentCaption(id), Properties,
6252 OCPN_DBP_STC_HUM, "%3.0f");
6253 break;
6254 case ID_DBP_I_WCC:
6255 instrument = new DashboardInstrument_Single(
6256 this, wxID_ANY, getInstrumentCaption(id), Properties,
6257 OCPN_DBP_STC_WCC, _T("%5.1f"));
6258 break;
6259 }
6260 if (instrument) {
6261 instrument->instrumentTypeId = id;
6262 m_ArrayOfInstrument.Add(new DashboardInstrumentContainer(
6263 id, instrument, instrument->GetCapacity()));
6264 itemBoxSizer->Add(instrument, 0, wxEXPAND, 0);
6265 if (itemBoxSizer->GetOrientation() == wxHORIZONTAL) {
6266 itemBoxSizer->AddSpacer(5);
6267 }
6268 }
6269 }
6270
6271 // In the absense of any other hints, build the default instrument sizes by
6272 // taking the calculated with of the first (and succeeding) instruments as
6273 // hints for the next. So, best in default loads to start with an instrument
6274 // that accurately calculates its minimum width. e.g.
6275 // DashboardInstrument_Position
6276
6277 wxSize Hint = wxSize(DefaultWidth, DefaultWidth);
6278
6279 for (unsigned int i = 0; i < m_ArrayOfInstrument.size(); i++) {
6280 DashboardInstrument *inst = m_ArrayOfInstrument.Item(i)->m_pInstrument;
6281 inst->SetMinSize(inst->GetSize(itemBoxSizer->GetOrientation(), Hint));
6282 Hint = inst->GetMinSize();
6283 }
6284
6285 Fit();
6286 Layout();
6287 SetMinSize(itemBoxSizer->GetMinSize());
6288}
6289
6290void DashboardWindow::SendSentenceToAllInstruments(DASH_CAP st, double value,
6291 wxString unit) {
6292 for (size_t i = 0; i < m_ArrayOfInstrument.GetCount(); i++) {
6293 if (m_ArrayOfInstrument.Item(i)->m_cap_flag.test(st))
6294 m_ArrayOfInstrument.Item(i)->m_pInstrument->SetData(st, value, unit);
6295 }
6296}
6297
6298void DashboardWindow::SendSatInfoToAllInstruments(int cnt, int seq,
6299 wxString talk,
6300 SAT_INFO sats[4]) {
6301 for (size_t i = 0; i < m_ArrayOfInstrument.GetCount(); i++) {
6302 if ((m_ArrayOfInstrument.Item(i)->m_cap_flag.test(OCPN_DBP_STC_GPS)) &&
6303 dynamic_cast<DashboardInstrument_GPS *>(
6304 m_ArrayOfInstrument.Item(i)->m_pInstrument))
6305 ((DashboardInstrument_GPS *)m_ArrayOfInstrument.Item(i)->m_pInstrument)
6306 ->SetSatInfo(cnt, seq, talk, sats);
6307 }
6308}
6309
6310void DashboardWindow::SendUtcTimeToAllInstruments(wxDateTime value) {
6311 for (size_t i = 0; i < m_ArrayOfInstrument.GetCount(); i++) {
6312 if ((m_ArrayOfInstrument.Item(i)->m_cap_flag.test(OCPN_DBP_STC_CLK)) &&
6313 dynamic_cast<DashboardInstrument_Clock *>(
6314 m_ArrayOfInstrument.Item(i)->m_pInstrument))
6315 // || m_ArrayOfInstrument.Item( i
6316 // )->m_pInstrument->IsKindOf( CLASSINFO(
6317 // DashboardInstrument_Sun ) )
6318 // || m_ArrayOfInstrument.Item( i
6319 // )->m_pInstrument->IsKindOf( CLASSINFO(
6320 // DashboardInstrument_Moon ) ) ) )
6321 ((DashboardInstrument_Clock *)m_ArrayOfInstrument.Item(i)->m_pInstrument)
6322 ->SetUtcTime(value);
6323 }
6324}
6325
6326// #include "wx/fontpicker.h"
6327
6328// #include "wx/fontdlg.h"
6329
6330// ============================================================================
6331// implementation
6332// ============================================================================
6333
6334// ----------------------------------------------------------------------------
6335// OCPNFontButton
6336// ----------------------------------------------------------------------------
6337
6338bool OCPNFontButton::Create(wxWindow *parent, wxWindowID id,
6339 const wxFontData &initial, const wxPoint &pos,
6340 const wxSize &size, long style,
6341 const wxValidator &validator,
6342 const wxString &name) {
6343 wxString label = (style & wxFNTP_FONTDESC_AS_LABEL)
6344 ? wxString()
6345 : // label will be updated by UpdateFont
6346 _("Choose font");
6347 label = name;
6348 // create this button
6349 if (!wxButton::Create(parent, id, label, pos, size, style, validator, name)) {
6350 wxFAIL_MSG(wxT("OCPNFontButton creation failed"));
6351 return false;
6352 }
6353
6354 // and handle user clicks on it
6355 Connect(GetId(), wxEVT_BUTTON,
6356 wxCommandEventHandler(OCPNFontButton::OnButtonClick), NULL, this);
6357
6358 m_data = initial;
6359 m_selectedFont =
6360 initial.GetChosenFont().IsOk() ? initial.GetChosenFont() : *wxNORMAL_FONT;
6361 UpdateFont();
6362
6363 return true;
6364}
6365
6366void OCPNFontButton::OnButtonClick(wxCommandEvent &WXUNUSED(ev)) {
6367 // update the wxFontData to be shown in the dialog
6368 m_data.SetInitialFont(m_selectedFont);
6369 wxFont *pF = OCPNGetFont(_("Dialog"));
6370
6371#ifdef __WXGTK__
6372 // Use a smaller font picker dialog (ocpnGenericFontDialog) for small displays
6373 int display_height = wxGetDisplaySize().y;
6374 if (display_height < 800) {
6375 // create the font dialog and display it
6376 ocpnGenericFontDialog dlg(this, m_data);
6377 dlg.SetFont(*pF);
6378 if (dlg.ShowModal() == wxID_OK) {
6379 m_data = dlg.GetFontData();
6380 m_selectedFont = m_data.GetChosenFont();
6381 // fire an event
6382 wxFontPickerEvent event(this, GetId(), m_selectedFont);
6383 GetEventHandler()->ProcessEvent(event);
6384 UpdateFont();
6385 }
6386 } else {
6387 // create the font dialog and display it
6388 wxFontDialog dlg(this, m_data);
6389 dlg.SetFont(*pF);
6390 if (dlg.ShowModal() == wxID_OK) {
6391 m_data = dlg.GetFontData();
6392 m_selectedFont = m_data.GetChosenFont();
6393 // fire an event
6394 wxFontPickerEvent event(this, GetId(), m_selectedFont);
6395 GetEventHandler()->ProcessEvent(event);
6396 UpdateFont();
6397 }
6398 }
6399
6400#else // Not __GTK__
6401 // create the font dialog and display it
6402 wxFontDialog dlg(this, m_data);
6403 dlg.SetFont(*pF);
6404
6405#ifdef __WXQT__
6406 // Make sure that font dialog will fit on the screen without scrolling
6407 // We do this by setting the dialog font size "small enough" to show "n" lines
6408 wxSize proposed_size = GetParent()->GetSize();
6409 float n_lines = 30;
6410 float font_size = pF->GetPointSize();
6411
6412 if ((proposed_size.y / font_size) < n_lines) {
6413 float new_font_size = proposed_size.y / n_lines;
6414 wxFont *smallFont = new wxFont(*pF);
6415 smallFont->SetPointSize(new_font_size);
6416 dlg.SetFont(*smallFont);
6417 }
6418
6419 dlg.SetSize(GetParent()->GetSize());
6420 dlg.Centre();
6421#endif
6422
6423 if (dlg.ShowModal() == wxID_OK) {
6424 m_data = dlg.GetFontData();
6425 m_selectedFont = m_data.GetChosenFont();
6426
6427 // fire an event
6428 wxFontPickerEvent event(this, GetId(), m_selectedFont);
6429 GetEventHandler()->ProcessEvent(event);
6430
6431 UpdateFont();
6432 }
6433#endif
6434}
6435
6436void OCPNFontButton::UpdateFont() {
6437 if (!m_selectedFont.IsOk()) return;
6438
6439 // Leave black, until Instruments are modified to accept color fonts
6440 // SetForegroundColour(m_data.GetColour());
6441
6442 if (HasFlag(wxFNTP_USEFONT_FOR_LABEL)) {
6443 // use currently selected font for the label...
6444 wxButton::SetFont(m_selectedFont);
6445 wxButton::SetForegroundColour(GetSelectedColour());
6446 }
6447
6448 wxString label =
6449 wxString::Format(wxT("%s, %d"), m_selectedFont.GetFaceName().c_str(),
6450 m_selectedFont.GetPointSize());
6451
6452 if (HasFlag(wxFNTP_FONTDESC_AS_LABEL)) {
6453 SetLabel(label);
6454 }
6455
6456 auto minsize = GetTextExtent(label);
6457 SetSize(minsize);
6458
6459 GetParent()->Layout();
6460 GetParent()->Fit();
6461}
6462
6463// Edit Dialog
6464
6465EditDialog::EditDialog(wxWindow *parent, InstrumentProperties &Properties,
6466 wxWindowID id, const wxString &title, const wxPoint &pos,
6467 const wxSize &size, long style)
6468 : wxDialog(parent, id, title, pos, size, style) {
6469 this->SetSizeHints(wxDefaultSize, wxDefaultSize);
6470
6471 wxBoxSizer *bSizer5;
6472 bSizer5 = new wxBoxSizer(wxVERTICAL);
6473
6474 wxFlexGridSizer *fgSizer2;
6475 fgSizer2 = new wxFlexGridSizer(0, 2, 0, 0);
6476 fgSizer2->SetFlexibleDirection(wxBOTH);
6477 fgSizer2->SetNonFlexibleGrowMode(wxFLEX_GROWMODE_SPECIFIED);
6478
6479 m_staticText1 = new wxStaticText(this, wxID_ANY, _("Title:"),
6480 wxDefaultPosition, wxDefaultSize, 0);
6481 m_staticText1->Wrap(-1);
6482 fgSizer2->Add(m_staticText1, 0, wxALIGN_CENTER_VERTICAL | wxALL, 5);
6483
6484 m_fontPicker2 = new wxFontPickerCtrl(this, wxID_ANY, Properties.m_USTitleFont,
6485 wxDefaultPosition, wxDefaultSize);
6486 fgSizer2->Add(m_fontPicker2, 0, wxALL, 5);
6487
6488 m_staticText5 = new wxStaticText(this, wxID_ANY, _("Title background color:"),
6489 wxDefaultPosition, wxDefaultSize, 0);
6490 m_staticText5->Wrap(-1);
6491 fgSizer2->Add(m_staticText5, 0, wxALIGN_CENTER_VERTICAL | wxALL, 5);
6492
6493 m_colourPicker1 = new wxColourPickerCtrl(
6494 this, wxID_ANY, Properties.m_TitleBackgroundColour, wxDefaultPosition,
6495 wxDefaultSize, wxCLRP_DEFAULT_STYLE);
6496 fgSizer2->Add(m_colourPicker1, 0, wxALL, 5);
6497
6498 m_staticText2 = new wxStaticText(this, wxID_ANY, _("Data:"),
6499 wxDefaultPosition, wxDefaultSize, 0);
6500 m_staticText2->Wrap(-1);
6501 fgSizer2->Add(m_staticText2, 0, wxALIGN_CENTER_VERTICAL | wxALL, 5);
6502
6503 m_fontPicker4 = new wxFontPickerCtrl(this, wxID_ANY, Properties.m_USDataFont,
6504 wxDefaultPosition, wxDefaultSize);
6505 fgSizer2->Add(m_fontPicker4, 0, wxALL, 5);
6506
6507 m_staticText6 = new wxStaticText(this, wxID_ANY, _("Data background color:"),
6508 wxDefaultPosition, wxDefaultSize, 0);
6509 m_staticText6->Wrap(-1);
6510 fgSizer2->Add(m_staticText6, 0, wxALL, 5);
6511
6512 m_colourPicker2 = new wxColourPickerCtrl(
6513 this, wxID_ANY, Properties.m_DataBackgroundColour, wxDefaultPosition,
6514 wxDefaultSize, wxCLRP_DEFAULT_STYLE);
6515 fgSizer2->Add(m_colourPicker2, 0, wxALL, 5);
6516
6517 m_staticText3 = new wxStaticText(this, wxID_ANY, _("Label:"),
6518 wxDefaultPosition, wxDefaultSize, 0);
6519 m_staticText3->Wrap(-1);
6520 fgSizer2->Add(m_staticText3, 0, wxALIGN_CENTER_VERTICAL | wxALL, 5);
6521
6522 m_fontPicker5 = new wxFontPickerCtrl(this, wxID_ANY, Properties.m_USLabelFont,
6523 wxDefaultPosition, wxDefaultSize);
6524 fgSizer2->Add(m_fontPicker5, 0, wxALL, 5);
6525
6526 m_staticText4 = new wxStaticText(this, wxID_ANY, _("Small:"),
6527 wxDefaultPosition, wxDefaultSize, 0);
6528 m_staticText4->Wrap(-1);
6529 fgSizer2->Add(m_staticText4, 0, wxALIGN_CENTER_VERTICAL | wxALL, 5);
6530
6531 m_fontPicker6 = new wxFontPickerCtrl(this, wxID_ANY, Properties.m_USSmallFont,
6532 wxDefaultPosition, wxDefaultSize);
6533 fgSizer2->Add(m_fontPicker6, 0, wxALL, 5);
6534
6535 m_staticText9 = new wxStaticText(this, wxID_ANY, _("Arrow 1 Color :"),
6536 wxDefaultPosition, wxDefaultSize, 0);
6537 m_staticText9->Wrap(-1);
6538 fgSizer2->Add(m_staticText9, 0, wxALIGN_CENTER_VERTICAL | wxALL, 5);
6539
6540 m_colourPicker3 = new wxColourPickerCtrl(
6541 this, wxID_ANY, Properties.m_Arrow_First_Colour, wxDefaultPosition,
6542 wxDefaultSize, wxCLRP_DEFAULT_STYLE);
6543 fgSizer2->Add(m_colourPicker3, 0, wxALL, 5);
6544
6545 m_staticText10 = new wxStaticText(this, wxID_ANY, _("Arrow 2 Color :"),
6546 wxDefaultPosition, wxDefaultSize, 0);
6547 m_staticText10->Wrap(-1);
6548 fgSizer2->Add(m_staticText10, 0, wxALIGN_CENTER_VERTICAL | wxALL, 5);
6549
6550 m_colourPicker4 = new wxColourPickerCtrl(
6551 this, wxID_ANY, Properties.m_Arrow_Second_Colour, wxDefaultPosition,
6552 wxDefaultSize, wxCLRP_DEFAULT_STYLE);
6553 fgSizer2->Add(m_colourPicker4, 0, wxALL, 5);
6554
6555 m_staticline1 = new wxStaticLine(this, wxID_ANY, wxDefaultPosition,
6556 wxDefaultSize, wxLI_HORIZONTAL);
6557 fgSizer2->Add(m_staticline1, 0, wxEXPAND | wxALL, 5);
6558
6559 m_staticline2 = new wxStaticLine(this, wxID_ANY, wxDefaultPosition,
6560 wxDefaultSize, wxLI_HORIZONTAL);
6561 fgSizer2->Add(m_staticline2, 0, wxEXPAND | wxALL, 5);
6562
6563 fgSizer2->Add(0, 5, 1, wxEXPAND, 5);
6564
6565 fgSizer2->Add(0, 0, 1, wxEXPAND, 5);
6566
6567 m_staticText7 = new wxStaticText(this, wxID_ANY, wxEmptyString,
6568 wxDefaultPosition, wxDefaultSize, 0);
6569 m_staticText7->Wrap(-1);
6570 fgSizer2->Add(m_staticText7, 0, wxALL, 5);
6571
6572 m_button1 = new wxButton(this, wxID_ANY, _("Set default"), wxDefaultPosition,
6573 wxDefaultSize, 0);
6574 fgSizer2->Add(m_button1, 0, wxALL, 5);
6575
6576 fgSizer2->Add(0, 5, 1, wxEXPAND, 5);
6577
6578 fgSizer2->Add(5, 0, 1, wxEXPAND, 5);
6579
6580 bSizer5->Add(fgSizer2, 1, wxALL | wxEXPAND, 5);
6581
6582 m_sdbSizer3 = new wxStdDialogButtonSizer();
6583 m_sdbSizer3OK = new wxButton(this, wxID_OK);
6584 m_sdbSizer3->AddButton(m_sdbSizer3OK);
6585 m_sdbSizer3Cancel = new wxButton(this, wxID_CANCEL);
6586 m_sdbSizer3->AddButton(m_sdbSizer3Cancel);
6587 m_sdbSizer3->Realize();
6588
6589 bSizer5->Add(m_sdbSizer3, 0, 0, 1);
6590
6591 bSizer5->Add(0, 10, 0, wxEXPAND, 5);
6592
6593 this->SetSizer(bSizer5);
6594 this->Layout();
6595 bSizer5->Fit(this);
6596
6597 this->Centre(wxBOTH);
6598
6599 // Connect Events
6600 m_button1->Connect(wxEVT_COMMAND_BUTTON_CLICKED,
6601 wxCommandEventHandler(EditDialog::OnSetdefault), NULL,
6602 this);
6603}
6604
6605EditDialog::~EditDialog() {
6606 // Disconnect Events
6607 m_button1->Disconnect(wxEVT_COMMAND_BUTTON_CLICKED,
6608 wxCommandEventHandler(EditDialog::OnSetdefault), NULL,
6609 this);
6610}
6611
6612void EditDialog::OnSetdefault(wxCommandEvent &event) {
6613 m_fontPicker2->SetSelectedFont(g_USFontTitle.GetChosenFont());
6614 m_fontPicker2->SetSelectedColour(g_USFontTitle.GetColour());
6615 m_fontPicker4->SetSelectedFont(g_USFontData.GetChosenFont());
6616 m_fontPicker4->SetSelectedColour(g_USFontData.GetColour());
6617 m_fontPicker5->SetSelectedFont(g_USFontLabel.GetChosenFont());
6618 m_fontPicker5->SetSelectedColour(g_USFontLabel.GetColour());
6619 m_fontPicker6->SetSelectedFont(g_USFontSmall.GetChosenFont());
6620 m_fontPicker6->SetSelectedColour(g_USFontSmall.GetColour());
6621 wxColour dummy;
6622 GetGlobalColor(_T("DASHL"), &dummy);
6623 m_colourPicker1->SetColour(dummy);
6624 GetGlobalColor(_T("DASHB"), &dummy);
6625 m_colourPicker2->SetColour(dummy);
6626 GetGlobalColor(_T("DASHN"), &dummy);
6627 m_colourPicker3->SetColour(dummy);
6628 GetGlobalColor(_T("BLUE3"), &dummy);
6629 m_colourPicker4->SetColour(dummy);
6630 Update();
6631}
6632
6633// Read or write SumLog data to config and update instrument if approropriate
6634// The sumlog value will be updated every minute when we are under way.
6635void dashboard_pi::UpdateSumLog(bool write) {
6636 if (write) {
6637 g_dSumLogNM += d_tripNM;
6638 // Write to config every 10 minutes to ensure that the sumlog value is
6639 // reasonably up to date even if O or the system stops wo SaveConfig().
6640 if (++confprint > 10) {
6641 wxFileConfig *logConf = (wxFileConfig *)m_pconfig;
6642 if (logConf) {
6643 logConf->SetPath("/PlugIns/Dashboard");
6644 logConf->Write("SumLogNM", g_dSumLogNM);
6645 logConf->Flush();
6646 }
6647 confprint = 0;
6648 }
6649 // If internal sumlog is used, update the instrument.
6650 if (g_bUseInternSumLog) {
6651 SendSentenceToAllInstruments(
6652 OCPN_DBP_STC_VLW2,
6653 toUsrDistance_Plugin(g_dSumLogNM, g_iDashDistanceUnit),
6654 getUsrDistanceUnit_Plugin(g_iDashDistanceUnit));
6655 mLOG_Watchdog = 70; // We update sumLog every minute
6656 }
6657 } else {
6658 // Update sumlog instrument from config.
6659 // This will only run if we use internal sumlog.
6660 SendSentenceToAllInstruments(
6661 OCPN_DBP_STC_VLW2,
6662 toUsrDistance_Plugin(g_dSumLogNM, g_iDashDistanceUnit),
6663 getUsrDistanceUnit_Plugin(g_iDashDistanceUnit));
6664 mLOG_Watchdog = no_nav_watchdog_timeout_ticks;
6665 }
6666}
wxString GetPluginDataDir(const char *plugin_name)
Returns an installed plugin's data directory given a plugin name.
A dashboard instrument that displays the current computer time.
Definition clock.h:130
A dashboard instrument that displays the GNSS clock time, if available.
Definition clock.h:54
A dashboard instrument that displays current moon phase information.
Definition clock.h:82
A dashboard instrument that displays sunrise and sunset times.
Definition clock.h:103
Custom event class for OpenCPN's notification system.
Extended position fix information.
int nSats
Number of satellites used in the fix.
double Var
Magnetic variation in degrees, typically from RMC message.
double Cog
Course over ground in degrees [0-360).
double Lat
Latitude in decimal degrees.
double Hdm
Heading magnetic in degrees [0-360).
time_t FixTime
UTC time of fix.
double Lon
Longitude in decimal degrees.
double Sog
Speed over ground in knots.
double Hdt
Heading true in degrees [0-360).
void SetCursorLatLon(double lat, double lon)
Receives cursor lat/lon position updates.
void ShowPreferencesDialog(wxWindow *parent)
Shows the plugin preferences dialog.
void OnToolbarToolCallback(int id)
Handles toolbar tool clicks.
void SetColorScheme(PI_ColorScheme cs)
Updates plugin color scheme.
void SetPositionFixEx(PlugIn_Position_Fix_Ex &pfix)
Updates plugin with extended position fix data at regular intervals.
int GetToolbarToolCount(void)
Returns the number of toolbar tools this plugin provides.
wxString GetCommonName()
Get the plugin's common (short) name.
wxString GetShortDescription()
Get a brief description of the plugin.
void UpdateAuiStatus(void)
Updates AUI manager status.
int GetAPIVersionMinor()
Returns the minor version number of the plugin API that this plugin supports.
wxBitmap * GetPlugInBitmap()
Get the plugin's icon bitmap.
void SetNMEASentence(wxString &sentence)
Receive all NMEA 0183 sentences from OpenCPN.
int GetAPIVersionMajor()
Returns the major version number of the plugin API that this plugin supports.
wxString GetLongDescription()
Get detailed plugin information.
int GetPlugInVersionMinor()
Returns the minor version number of the plugin itself.
bool DeInit(void)
Clean up plugin resources.
int Init(void)
Initialize the plugin and declare its capabilities.
int GetPlugInVersionMajor()
Returns the major version number of the plugin itself.
Base class for OpenCPN plugins.
The JSON parser.
Definition jsonreader.h:50
int Parse(const wxString &doc, wxJSONValue *val)
Parse the JSON document.
The JSON value class implementation.
Definition jsonval.h:84
bool IsArray() const
Return TRUE if the type of the value stored is an array type.
Definition jsonval.cpp:746
int Size() const
Return the size of the array or map stored in this value.
Definition jsonval.cpp:1332
bool HasMember(unsigned index) const
Return TRUE if the object contains an element at the specified index.
Definition jsonval.cpp:1298
double AsDouble() const
Return the stored value as a double.
Definition jsonval.cpp:827
bool IsString() const
Return TRUE if the type of the value stored is a wxString object.
Definition jsonval.cpp:717
wxString AsString() const
Return the stored value as a wxWidget's string.
Definition jsonval.cpp:872
#define WANTS_NMEA_EVENTS
Receive decoded NMEA events with parsed data.
PI_ColorScheme
Color schemes for different lighting conditions.
#define USES_AUI_MANAGER
Plugin uses wxAuiManager for window management.
#define WANTS_NMEA_SENTENCES
Receive raw NMEA 0183 sentences from all active ports.
#define WANTS_PREFERENCES
Plugin will add page(s) to global preferences dialog.
#define WANTS_CONFIG
Plugin requires persistent configuration storage.
#define WANTS_PLUGIN_MESSAGING
Enable message passing between plugins.
#define INSTALLS_TOOLBAR_TOOL
Plugin will add one or more toolbar buttons.
#define WANTS_CURSOR_LATLON
Receive updates when cursor moves over chart.
Definition ocpn_plugin.h:94
#define WANTS_TOOLBAR_CALLBACK
Receive notification when user left-clicks plugin's toolbar buttons.
double toUsrTemp_Plugin(double cel_temp, int unit)
Converts Celsius to user's preferred temperature unit.
wxWindow * GetOCPNCanvasWindow()
Gets OpenCPN's main canvas window.
wxFont * OCPNGetFont(wxString TextElement, int default_size)
Gets a font for UI elements.
wxString getUsrDistanceUnit_Plugin(int unit)
Gets display string for user's preferred distance unit.
wxFileConfig * GetOCPNConfigObject()
Gets OpenCPN's configuration object.
wxBitmap GetBitmapFromSVGFile(wxString filename, unsigned int width, unsigned int height)
Creates bitmap from SVG file.
double toUsrDistance_Plugin(double nm_distance, int unit)
Converts nautical miles to user's preferred distance unit.
int InsertPlugInToolSVG(wxString label, wxString SVGfile, wxString SVGfileRollover, wxString SVGfileToggled, wxItemKind kind, wxString shortHelp, wxString longHelp, wxObject *clientData, int position, int tool_sel, opencpn_plugin *pplugin)
Adds a tool using SVG graphics.
wxString getUsrSpeedUnit_Plugin(int unit)
Gets display string for user's preferred speed unit.
wxString * GetpSharedDataLocation()
Gets shared application data location.
void DimeWindow(wxWindow *win)
Applies system color scheme to window.
double GetOCPNGUIToolScaleFactor_PlugIn()
Gets current global GUI scaling factor.
wxString getUsrTempUnit_Plugin(int unit)
Gets display string for user's preferred temperature unit.
wxString GetActiveStyleName()
Gets name of currently active style sheet.
bool DecodeSingleVDOMessage(const wxString &str, PlugIn_Position_Fix_Ex *pos, wxString *accumulator)
Decodes a single VDO (Own Ship AIS) message.
wxAuiManager * GetFrameAuiManager()
Gets main frame AUI manager.
double toUsrSpeed_Plugin(double kts_speed, int unit)
Converts knots to user's preferred speed unit.
std::vector< std::string > GetActivePriorityIdentifiers()
Gets list of active priority identifiers.
void SetToolbarItemState(int item, bool toggle)
Sets toolbar item toggle state.
double OCPN_GetWinDIPScaleFactor()
Gets Windows-specific DPI scaling factor.
bool AddLocaleCatalog(wxString catalog)
Adds a locale catalog for translations.
double fromUsrDistance_Plugin(double usr_distance, int unit)
Converts from user's preferred distance unit to nautical miles.
shared_ptr< ObservableListener > GetListener(NMEA2000Id id, wxEventType et, wxEvtHandler *eh)
Gets listener for NMEA 2000 messages.
std::string GetN2000Source(NMEA2000Id id, ObservedEvt ev)
Return source identifier (iface) of a received n2000 message of type id in ev.
vector< uint8_t > GetN2000Payload(NMEA2000Id id, ObservedEvt ev)
Return N2K payload for a received n2000 message of type id in ev.
Identifier for NMEA 2000 message types.