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