OpenCPN Partial API docs
Loading...
Searching...
No Matches
altitude.cpp
1/******************************************************************************
2 * $Id: altitude.cpp, v0.3 $
3 *
4 * Project: OpenCPN
5 * Purpose: Dashboard Plugin, display altitude trace
6 * Author: derived from Jean-Eudes Onfray's depth.cpp by Andreas Merz
7 *
8 * Comment: since not every vessel is always on sea level, I found it
9 * sometimes interesting to observe the GPS altitude information.
10 * It can be extracted from the GGA nmea message.
11 *
12 * Besides showing a rolling plot, the grid will auto-rescale to
13 * keep the plot within the visible range.
14 * The top line shows mean altitude and its standard deviation.
15 *
16 ***************************************************************************
17 * Copyright (C) 2010 by David S. Register *
18 * *
19 * This program is free software; you can redistribute it and/or modify *
20 * it under the terms of the GNU General Public License as published by *
21 * the Free Software Foundation; either version 2 of the License, or *
22 * (at your option) any later version. *
23 * *
24 * This program is distributed in the hope that it will be useful, *
25 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
26 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
27 * GNU General Public License for more details. *
28 * *
29 * You should have received a copy of the GNU General Public License *
30 * along with this program; if not, write to the *
31 * Free Software Foundation, Inc., *
32 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
33 ***************************************************************************
34 */
35
36#include <wx/wxprec.h>
37
38#ifndef WX_PRECOMP
39#include <wx/wx.h>
40#endif // precompiled headers
41
42#include "altitude.h"
43extern int g_iDashDepthUnit; // use same unit for altitude
44
45#ifdef __BORLANDC__
46#pragma hdrstop
47#endif
48
49int m_aDataHeight;
50int x_alabel, y_alabel, a_plotdown, a_plotup, a_plotheight;
51
52DashboardInstrument_Altitude::DashboardInstrument_Altitude(
53 wxWindow* parent, wxWindowID id, wxString title,
54 InstrumentProperties* Properties)
55 : DashboardInstrument(parent, id, title, OCPN_DBP_STC_ALTI, Properties) {
56 m_cap_flag.set(OCPN_DBP_STC_TMP);
57 m_MaxAltitude = 0;
58 m_Altitude = 0;
59 m_AltitudeUnit = getUsrDistanceUnit_Plugin(g_iDashDepthUnit);
60 m_Temp = _T("--");
61 for (int idx = 0; idx < ALTITUDE_RECORD_COUNT; idx++) {
62 m_ArrayAltitude[idx] = 0.0;
63 }
64}
65
66wxSize DashboardInstrument_Altitude::GetSize(int orient, wxSize hint) {
67 wxClientDC dc(this);
68 int w;
69 wxFont f;
70 if (m_Properties) {
71 f = m_Properties->m_TitleFont.GetChosenFont();
72 dc.GetTextExtent(m_title, &w, &m_TitleHeight, 0, 0, &f);
73 f = m_Properties->m_DataFont.GetChosenFont();
74 dc.GetTextExtent("15.7 Feet", &w, &m_aDataHeight, 0, 0, &f);
75 f = m_Properties->m_LabelFont.GetChosenFont();
76 dc.GetTextExtent("20.8 C", &x_alabel, &y_alabel, 0, 0, &f);
77 } else {
78 f = g_pFontTitle->GetChosenFont();
79 dc.GetTextExtent(m_title, &w, &m_TitleHeight, 0, 0, &f);
80 f = g_pFontData->GetChosenFont();
81 dc.GetTextExtent("15.7 Feet", &w, &m_aDataHeight, 0, 0, &f);
82 // Space for bottom(temp)text later.
83 f = g_pFontLabel->GetChosenFont();
84 dc.GetTextExtent("20.8 C", &x_alabel, &y_alabel, 0, 0, &f);
85 }
86 int y_total =
87 // Title Alt. data plot area air-temp
88 m_TitleHeight + m_aDataHeight + 4 * m_aDataHeight + y_alabel;
89 if (orient == wxHORIZONTAL) {
90 return wxSize(DefaultWidth, wxMax(y_total, hint.y));
91 } else {
92 return wxSize(wxMax(hint.x, DefaultWidth), y_total);
93 }
94}
95
96void DashboardInstrument_Altitude::SetData(DASH_CAP st, double data,
97 wxString unit) {
98 if (st == OCPN_DBP_STC_ALTI) {
99 if (std::isnan(data)) {
100 m_cntValid = 0;
101 m_Altitude = 0.0;
102 } else {
103 m_Altitude = data;
104 // m_Altitude = m_Altitude*10.0 +1000; // inject fake testdata
105 if (m_cntValid < ALTITUDE_RECORD_COUNT && data != 0.0) m_cntValid++;
106 }
107 // printf("Altitude = %3.3f # %2d ", m_Altitude, m_cntValid); // debug
108
109 if (m_cntValid == 1) {
110 for (int idx = 1; idx < ALTITUDE_RECORD_COUNT; idx++) {
111 m_ArrayAltitude[idx - 1] = m_Altitude; // init FIFO with 1st valid data
112 }
113 } else {
114 for (int idx = 1; idx < ALTITUDE_RECORD_COUNT; idx++) {
115 m_ArrayAltitude[idx - 1] = m_ArrayAltitude[idx]; // shift FIFO
116 }
117 }
118 m_ArrayAltitude[ALTITUDE_RECORD_COUNT - 1] = m_Altitude;
119 m_AltitudeUnit = unit;
120 } else if (st == OCPN_DBP_STC_ATMP) { // Air Temperature
121 if (!std::isnan(data)) {
122 m_Temp = wxString::Format(_T("%.1f"), data) + DEGREE_SIGN + unit;
123 } else {
124 m_Temp = "---";
125 }
126 }
127}
128
129void DashboardInstrument_Altitude::setAttenuation(int steps) {
130 // fast int stuff
131 // increase the attenuation in 1 2 5 10 20 50 steps
132 if (steps > 0)
133 while (steps--) {
134 switch (m_Attenuation) {
135 case 1:
136 m_Attenuation = 2;
137 break;
138 case 2:
139 m_Attenuation = 5;
140 break;
141 default:
142 m_Attenuation = 1;
143 m_Decade *= 10;
144 }
145 }
146 // decrease the attenuation in 1 2 5 10 20 50 steps
147 else if (steps < 0)
148 while (steps++) {
149 switch (m_Attenuation) {
150 case 5:
151 m_Attenuation = 2;
152 break;
153 case 2:
154 m_Attenuation = 1;
155 break;
156 default:
157 m_Attenuation = 5;
158 m_Decade /= 10;
159 }
160 }
161 // bottom limit: unity
162 if (m_Decade < 1) {
163 m_Decade = 1;
164 m_Attenuation = 1;
165 }
166}
167
168int DashboardInstrument_Altitude::getAttenuation() {
169 return m_Attenuation * m_Decade;
170}
171
172void DashboardInstrument_Altitude::Draw(wxGCDC* dc) {
173 DrawBackground(dc);
174 DrawForeground(dc);
175}
176
177void DashboardInstrument_Altitude::DrawBackground(wxGCDC* dc) {
178 wxSize size = GetClientSize();
179 wxColour cl;
180 if (m_Properties) {
181 dc->SetTextForeground(
182 GetColourSchemeFont(m_Properties->m_LabelFont.GetColour()));
183 } else {
184 if (GetColourSchemeFont(g_pFontSmall->GetColour()) ==
185 GetColourSchemeFont(g_pFontLabel->GetColour())) {
186 GetGlobalColor(_T("DASHL"), &cl);
187 dc->SetTextForeground(cl);
188 } else
189 dc->SetTextForeground(GetColourSchemeFont(g_pFontLabel->GetColour()));
190 }
191
192 wxPen pen;
193 pen.SetStyle(wxPENSTYLE_SOLID);
194 if (m_Properties)
195 cl = GetColourSchemeFont(m_Properties->m_SmallFont.GetColour());
196 else
197 cl = GetColourSchemeFont(g_pFontSmall->GetColour());
198 // GetGlobalColor(_T("DASHF"), &cl);
199 pen.SetColour(cl);
200 pen.SetWidth(1);
201 dc->SetPen(pen);
202
203 a_plotup = m_TitleHeight + m_aDataHeight;
204 a_plotdown = size.y - y_alabel;
205 a_plotheight = a_plotdown - a_plotup;
206
207 // dc->DrawLine(3, 44, size.x - 3, 44);
208 dc->DrawLine(3, a_plotdown, size.x - 3, a_plotdown);
209
210#ifdef __WXMSW__
211 pen.SetStyle(wxPENSTYLE_SHORT_DASH);
212#else
213 pen.SetStyle(wxPENSTYLE_DOT);
214 pen.SetWidth(1);
215#endif
216
217 // Grid lines
218 dc->SetPen(pen);
219 dc->DrawLine(3, a_plotup, size.x - 3, a_plotup);
220 dc->DrawLine(3, a_plotup + a_plotheight / 4, size.x - 3,
221 a_plotup + a_plotheight / 4);
222 dc->DrawLine(3, a_plotup + a_plotheight * 2 / 4, size.x - 3,
223 a_plotup + a_plotheight * 2 / 4);
224 dc->DrawLine(3, a_plotup + a_plotheight * 3 / 4, size.x - 3,
225 a_plotup + a_plotheight * 3 / 4);
226 if (m_Properties) {
227 dc->SetFont(m_Properties->m_SmallFont.GetChosenFont());
228 dc->SetTextForeground(
229 GetColourSchemeFont(m_Properties->m_SmallFont.GetColour()));
230 } else {
231 dc->SetFont(g_pFontSmall->GetChosenFont());
232 dc->SetTextForeground(GetColourSchemeFont(g_pFontSmall->GetColour()));
233 }
234
235 // evaluate buffered data to determine its range, mean, variance
236 m_meanAltitude = m_ArrayAltitude[0]; // moving average
237 double MaxAltitude = m_ArrayAltitude[0];
238 double MinAltitude = m_ArrayAltitude[0];
239 double sum2Altitude = m_ArrayAltitude[0] * m_ArrayAltitude[0]; // sum^2
240 for (int idx = 1; idx < ALTITUDE_RECORD_COUNT; idx++) {
241 MaxAltitude = std::max(MaxAltitude, m_ArrayAltitude[idx]);
242 MinAltitude = std::min(MinAltitude, m_ArrayAltitude[idx]);
243 m_meanAltitude += m_ArrayAltitude[idx];
244 sum2Altitude += m_ArrayAltitude[idx] * m_ArrayAltitude[idx];
245 }
246 m_meanAltitude /= ALTITUDE_RECORD_COUNT;
247
248 // calculate 2nd Moment
249 double varAltitude = sum2Altitude / ALTITUDE_RECORD_COUNT;
250 varAltitude -= m_meanAltitude * m_meanAltitude;
251 // estimator bias correction
252 varAltitude *= (ALTITUDE_RECORD_COUNT / (ALTITUDE_RECORD_COUNT - 1.0));
253 // printf("varAltitude = %5.3f\n", varAltitude); // debug output
254 if (varAltitude < 0.0) varAltitude = 0.0; // avoid nan when calling sqrt().
255
256 // do AGC to adjust scaling
257 double range = MaxAltitude - MinAltitude;
258 if (range > 1.1 * m_Range) setAttenuation(+1);
259 if (range < 0.3 * m_Range) setAttenuation(-1); // some hysteresis
260 double grid = getAttenuation();
261 m_Range = grid * c_GridLines;
262
263 // only update axes on major corridor changes
264 if ((MaxAltitude - m_MaxAltitude) / grid > 0.25 ||
265 (MaxAltitude - m_MaxAltitude) / grid < -0.75 * c_GridLines) {
266 m_MaxAltitude = (round(MaxAltitude / grid) + 1) * grid;
267 m_MinAltitude = m_MaxAltitude - m_Range;
268 }
269 if ((MinAltitude - m_MinAltitude) / grid < -0.25 ||
270 (MinAltitude - m_MinAltitude) / grid > 0.75 * c_GridLines) {
271 m_MinAltitude = (round(MinAltitude / grid) - 1) * grid;
272 m_MaxAltitude = m_MinAltitude + m_Range;
273 }
274 // debug output
275 // printf("m_MinAltitude=%7.1f m_MaxAltitude=%7.1f m_Range = %5.1f "
276 // " range = %5.1f att=%d , mean=%7.2f, std=%5.2f\n",
277 // m_MinAltitude, m_MaxAltitude, m_Range,
278 // range, getAttenuation(), m_meanAltitude, sqrt(varAltitude));
279
280 wxString label;
281 label.Printf(_T("+/-%.1f %8.0f ") + m_AltitudeUnit, sqrt(varAltitude),
282 m_MaxAltitude);
283 int width, height;
284 wxFont f;
285 if (m_Properties)
286 f = m_Properties->m_SmallFont.GetChosenFont();
287 else
288 f = g_pFontSmall->GetChosenFont();
289 dc->GetTextExtent(label, &width, &height, 0, 0, &f);
290 dc->DrawText(label, size.x - width - 1, a_plotup - height);
291
292 label.Printf(_T("%.1f/ %8.0f ") + m_AltitudeUnit, m_Range / c_GridLines,
293 m_MinAltitude);
294 if (m_Properties)
295 f = m_Properties->m_SmallFont.GetChosenFont();
296 else
297 f = g_pFontSmall->GetChosenFont();
298 dc->GetTextExtent(label, &width, &height, 0, 0, &f);
299 dc->DrawText(label, size.x - width - 1, a_plotdown);
300}
301
302void DashboardInstrument_Altitude::DrawForeground(wxGCDC* dc) {
303 wxSize size = GetClientSize();
304 wxColour cl;
305 if (m_Properties) {
306 cl = GetColourSchemeFont(m_Properties->m_LabelFont.GetColour());
307 } else {
308 if (GetColourSchemeFont(g_pFontSmall->GetColour()) ==
309 GetColourSchemeFont(g_pFontLabel->GetColour()))
310 GetGlobalColor(_T("DASH1"), &cl);
311 else
312 cl = GetColourSchemeFont(g_pFontLabel->GetColour());
313 }
314 // GetGlobalColor(_T("DASH1"), &cl);
315 wxBrush brush;
316 brush.SetStyle(wxBRUSHSTYLE_SOLID);
317 brush.SetColour(cl);
318 dc->SetBrush(brush);
319 dc->SetPen(*wxTRANSPARENT_PEN);
320
321 double ratioH = double(a_plotheight) / m_Range;
322 double ratioW = double(size.x - 6) / (ALTITUDE_RECORD_COUNT - 1);
323 wxPoint points[ALTITUDE_RECORD_COUNT + 2];
324#ifdef __OCPN__ANDROID__
325 int px = 3;
326 points[0].x = px;
327 points[0].y = a_plotdown;
328
329 for (int idx = 0; idx < ALTITUDE_RECORD_COUNT - 1; idx++) {
330 points[1].x = points[0].x;
331 if (m_ArrayAltitude[idx])
332 points[1].y =
333 a_plotdown - (m_ArrayAltitude[idx] - m_MinAltitude) * ratioH;
334 else
335 points[1].y = a_plotdown;
336
337 points[2].x = points[1].x + ratioW;
338 if (m_ArrayAltitude[idx + 1])
339 points[2].y =
340 a_plotdown - (m_ArrayAltitude[idx + 1] - m_MinAltitude) * ratioH;
341 else
342 points[2].y = a_plotdown;
343
344 points[3].x = points[2].x;
345 points[3].y = a_plotdown;
346 dc->DrawPolygon(4, points);
347
348 points[0].x = points[2].x;
349 points[0].y = a_plotdown;
350 }
351
352#else
353 for (int idx = 0; idx < ALTITUDE_RECORD_COUNT; idx++) {
354 points[idx].x = idx * ratioW + 3;
355 points[idx].y =
356 a_plotdown - (m_ArrayAltitude[idx] - m_MinAltitude) * ratioH;
357 }
358 points[ALTITUDE_RECORD_COUNT].x = size.x - 3;
359 points[ALTITUDE_RECORD_COUNT].y = a_plotdown;
360 points[ALTITUDE_RECORD_COUNT + 1].x = 3;
361 points[ALTITUDE_RECORD_COUNT + 1].y = a_plotdown;
362 dc->DrawPolygon(ALTITUDE_RECORD_COUNT + 2, points);
363#endif
364 if (m_Properties) {
365 dc->SetFont(m_Properties->m_DataFont.GetChosenFont());
366 dc->SetTextForeground(
367 GetColourSchemeFont(m_Properties->m_DataFont.GetColour()));
368 } else {
369 // GetGlobalColor(_T("DASHF"), &cl);
370 dc->SetTextForeground(GetColourSchemeFont(g_pFontData->GetColour()));
371 dc->SetFont(g_pFontData->GetChosenFont());
372 }
373 if (m_AltitudeUnit != _T("-")) { // Watchdog
374 wxString s_alti = wxString::Format(_T("%.1f"), m_meanAltitude);
375 dc->DrawText(s_alti + _T(" ") + m_AltitudeUnit, 10, m_TitleHeight);
376 } else
377 dc->DrawText(_T("---"), 10, m_TitleHeight);
378
379 // TODO: test display air temperature ID_DBP_I_ATMP
380 if (m_Properties)
381 dc->SetFont(m_Properties->m_LabelFont.GetChosenFont());
382 else
383 dc->SetFont(g_pFontLabel->GetChosenFont());
384 int width, height;
385 wxFont f;
386 if (m_Properties) {
387 f = m_Properties->m_LabelFont.GetChosenFont();
388 dc->GetTextExtent(m_Temp, &width, &height, 0, 0, &f);
389 } else {
390 f = g_pFontLabel->GetChosenFont();
391 dc->GetTextExtent(m_Temp, &width, &height, 0, 0, &f);
392 }
393 dc->DrawText(m_Temp, 3, a_plotdown);
394}
wxString getUsrDistanceUnit_Plugin(int unit)
Gets display string for user's preferred distance unit.