]> git.mxchange.org Git - flightgear.git/blob - src/Instrumentation/KLN89/kln89_page_nav.cxx
Fix the crashes on mini-panel and panel-reload with the KLN89, by removing the multip...
[flightgear.git] / src / Instrumentation / KLN89 / kln89_page_nav.cxx
1 // kln89_page_*.[ch]xx - this file is one of the "pages" that
2 //                       are used in the KLN89 GPS unit simulation. 
3 //
4 // Written by David Luff, started 2005.
5 //
6 // Copyright (C) 2005 - David C Luff - david.luff@nottingham.ac.uk
7 //
8 // This program is free software; you can redistribute it and/or
9 // modify it under the terms of the GNU General Public License as
10 // published by the Free Software Foundation; either version 2 of the
11 // License, or (at your option) any later version.
12 //
13 // This program is distributed in the hope that it will be useful, but
14 // WITHOUT ANY WARRANTY; without even the implied warranty of
15 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16 // General Public License for more details.
17 //
18 // You should have received a copy of the GNU General Public License
19 // along with this program; if not, write to the Free Software
20 // Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
21 //
22 // $Id$
23
24 #include "kln89_page_nav.hxx"
25 #include <Main/fg_props.hxx>
26
27 KLN89NavPage::KLN89NavPage(KLN89* parent) 
28 : KLN89Page(parent) {
29         _nSubPages = 4;
30         _subPage = 0;
31         _name = "NAV";
32         _posFormat = 0;         // Check - should this default to ref from waypoint?
33         _vnv = 0;
34         _nav4DataSnippet = 0;
35         _cdiFormat = 0;
36         _menuActive = false;
37         _menuPos = 0;
38         _suspendAVS = false;
39 }
40
41 KLN89NavPage::~KLN89NavPage() {
42 }
43
44 void KLN89NavPage::Update(double dt) {
45         GPSFlightPlan* fp = ((KLN89*)_parent)->_activeFP;
46         GPSWaypoint* awp = _parent->GetActiveWaypoint();
47         bool crsr = (_kln89->_mode == KLN89_MODE_CRSR);
48         bool blink = _kln89->_blink;
49         double lat = _kln89->_gpsLat * SG_RADIANS_TO_DEGREES;
50         double lon = _kln89->_gpsLon * SG_RADIANS_TO_DEGREES;
51         
52         if(0 == _subPage) {
53                 if(_kln89->_navFlagged) {
54                         _kln89->DrawText(">    F L A G", 2, 0, 2);
55                         _kln89->DrawText("DTK ---  TK ---", 2, 0, 1);
56                         _kln89->DrawText(">--- To    --:--", 2, 0, 0);
57                         _kln89->DrawSpecialChar(0, 2, 7, 1);
58                         _kln89->DrawSpecialChar(0, 2, 15, 1);
59                         _kln89->DrawSpecialChar(0, 2, 4, 0);
60                         _kln89->DrawSpecialChar(1, 2, 3, 2);
61                         _kln89->DrawSpecialChar(1, 2, 4, 2);
62                         _kln89->DrawSpecialChar(1, 2, 6, 2);
63                         _kln89->DrawSpecialChar(1, 2, 10, 2);
64                         _kln89->DrawSpecialChar(1, 2, 12, 2);
65                         _kln89->DrawSpecialChar(1, 2, 13, 2);
66                 } else {
67                         if(_kln89->_dto) {
68                                 _kln89->DrawDTO(2, 7, 3);
69                         } else {
70                                 if(!(_kln89->_waypointAlert && _kln89->_blink)) {
71                                         _kln89->DrawSpecialChar(3, 2, 8, 3);
72                                 }
73                         }
74                         _kln89->DrawText(awp->id, 2, 10, 3);
75                         if(!_kln89->_dto && !_kln89->_obsMode && !_kln89->_fromWaypoint.id.empty()) {
76                                 _kln89->DrawText(_kln89->_fromWaypoint.id, 2, 1, 3);
77                         }
78                         if(!(crsr && blink && _uLinePos == 1)) {
79                                 if(_cdiFormat == 0) {
80                                         _kln89->DrawCDI();
81                                 } else if(_cdiFormat == 1) {
82                                         _kln89->DrawText("Fly", 2, 2, 2);
83                                         double x = _kln89->CalcCrossTrackDeviation();
84                                         // TODO - check the R/L from sign of x below - I *think* it holds but not sure!
85                                         // Note also that we're setting Fly R or L based on the aircraft
86                                         // position only, not the heading.  Not sure if this is correct or not.
87                                         _kln89->DrawText(x < 0.0 ? "R" : "L", 2, 6, 2);
88                                         char buf[6];
89                                         int n;
90                                         x = fabs(x * (_kln89->_distUnits == GPS_DIST_UNITS_NM ? 1.0 : SG_NM_TO_METER * 0.001));
91                                         if(x < 1.0) {
92                                                 n = snprintf(buf, 6, "%0.2f", x);
93                                         } else if(x < 100.0) {
94                                                 n = snprintf(buf, 6, "%0.1f", x);
95                                         } else {
96                                                 n = snprintf(buf, 6, "%i", (int)(x+0.5));
97                                         }
98                                         _kln89->DrawText((string)buf, 2, 13-n, 2);
99                                         _kln89->DrawText(_kln89->_distUnits == GPS_DIST_UNITS_NM ? "nm" : "km", 2, 13, 2);
100                                 } else {
101                                         _kln89->DrawText("CDI Scale:", 2, 1, 2);
102                                         double d = _kln89->_cdiScales[_kln89->_currentCdiScaleIndex] * (_kln89->_distUnits == GPS_DIST_UNITS_NM ? 1.0 : SG_NM_TO_METER * 0.001);
103                                         char buf[5];
104                                         string s;
105                                         if(d >= 1.0) {
106                                                 snprintf(buf, 4, "%2.1f", d);
107                                                 s = buf;
108                                         } else {
109                                                 snprintf(buf, 5, "%2.2f", d);
110                                                 // trim the leading zero
111                                                 s = buf;
112                                                 s = s.substr(1, s.size() - 1);
113                                         }
114                                         _kln89->DrawText(s, 2, 11, 2);
115                                         _kln89->DrawText(_kln89->_distUnits == GPS_DIST_UNITS_NM ? "nm" : "km", 2, 14, 2);
116                                 }
117                         }
118                         _kln89->DrawChar('>', 2, 0, 2);
119                         _kln89->DrawChar('>', 2, 0, 0);
120                         if(crsr) {
121                                 if(_uLinePos == 1) _kln89->Underline(2, 1, 2, 15);
122                                 else if(_uLinePos == 2) _kln89->Underline(2, 1, 0, 9);
123                         }
124                         // Desired and actual magnetic track
125                         if(!_kln89->_obsMode) {
126                                 _kln89->DrawText("DTK", 2, 0, 1);
127                                 _kln89->DrawHeading((int)_kln89->_dtkMag, 2, 7, 1);
128                         }
129                         _kln89->DrawText("TK", 2, 9, 1);
130                         if(_kln89->_groundSpeed_ms > 3) {       // about 6 knots, don't know exactly what value to disable track
131                                 // The trouble with relying on FG gps's track value is we don't know when it's valid.
132                                 _kln89->DrawHeading((int)_kln89->_magTrackDeg, 2, 15, 1);
133                         } else {
134                                 _kln89->DrawText("---", 2, 12, 1);
135                                 _kln89->DrawSpecialChar(0, 2, 15, 1);
136                         }
137                         
138                         // Radial to/from active waypoint.
139                         // TODO - Not sure if this either is or should be true or mag!!!!!!!
140                         if(!(crsr && blink && _uLinePos == 2)) {
141                                 if(0 == _vnv) {
142                                         _kln89->DrawHeading((int)_kln89->GetHeadingToActiveWaypoint(), 2, 4, 0);
143                                         _kln89->DrawText("To", 2, 5, 0);
144                                 } else if(1 == _vnv) {
145                                         _kln89->DrawHeading((int)_kln89->GetHeadingFromActiveWaypoint(), 2, 4, 0);
146                                         _kln89->DrawText("Fr", 2, 5, 0);
147                                 } else {
148                                         _kln89->DrawText("Vnv Off", 2, 1, 0);
149                                 }
150                         }
151                         // It seems that the floating point groundspeed must be at least 30kt
152                         // for an ETA to be calculated.  Note that this means that (integer) 30kt
153                         // can appear in the frame 1 display both with and without an ETA displayed.
154                         // TODO - need to switch off track (and heading bug change) based on instantaneous speed as well
155                         // since the long gps lag filter means we can still be displaying when stopped on ground.
156                         if(_kln89->_groundSpeed_kts > 30.0) {
157                                 // Assuming eta display is always hh:mm
158                                 // Does it ever switch to seconds when close?
159                                 if(_kln89->_eta / 3600.0 > 100.0) {
160                                         // More that 100 hours ! - Doesn't fit.
161                                         _kln89->DrawText("--:--", 2, 11, 0);
162                                 } else {
163                                         _kln89->DrawTime(_kln89->_eta, 2, 15, 0);
164                                 }
165                         } else {
166                                 _kln89->DrawText("--:--", 2, 11, 0);
167                         }
168                 }
169         } else if(1 == _subPage) {
170                 // Present position
171                 _kln89->DrawChar('>', 2, 1, 3);
172                 if(!(crsr && blink && _uLinePos == 1)) _kln89->DrawText("PRESENT POSN", 2, 2, 3);
173                 if(crsr && _uLinePos == 1) _kln89->Underline(2, 2, 3, 12);
174                 if(0 == _posFormat) {
175                         // Lat/lon
176                         _kln89->DrawLatitude(lat, 2, 3, 1);
177                         _kln89->DrawLongitude(lon, 2, 3, 0);
178                 } else {
179                         // Ref from wp - defaults to nearest vor (and resets to default when page left and re-entered).
180                 }
181         } else if(2 == _subPage) {
182                 _kln89->DrawText("Time", 2, 0, 3);
183                 // TODO - hardwired to UTC at the moment
184                 _kln89->DrawText("UTC", 2, 6, 3);
185                 string th = fgGetString("/instrumentation/clock/indicated-hour");
186                 string tm = fgGetString("/instrumentation/clock/indicated-min");
187                 if(th.size() == 1) th = "0" + th;
188                 if(tm.size() == 1) tm = "0" + tm;
189                 _kln89->DrawText(th + tm, 2, 11, 3);
190                 _kln89->DrawText("Depart", 2, 0, 2);
191                 _kln89->DrawText(_kln89->_departureTimeString, 2, 11, 2);
192                 _kln89->DrawText("ETA", 2, 0, 1);
193                 if(_kln89->_departed) {
194                         /* Rules of ETA waypoint are:
195                          If the active waypoint is part of the active flightplan, then display
196                          the ETA to the final (destination) waypoint of the active flightplan.
197                          If the active waypoint is not part of the active flightplan, then
198                          display the ETA to the active waypoint. */
199                         // TODO - implement the above properly - we haven't below!
200                         string wid = "";
201                         if(fp->waypoints.size()) {
202                                 wid = fp->waypoints[fp->waypoints.size() - 1]->id;
203                         } else if(awp) {
204                                 wid = awp->id;
205                         }
206                         if(!wid.empty()) {
207                                 _kln89->DrawText(wid, 2, 4, 1);
208                                 double tsec = _kln89->GetTimeToWaypoint(wid);
209                                 if(tsec < 0.0) {
210                                         _kln89->DrawText("----", 2, 11, 1);
211                                 } else {
212                                         int etah = (int)tsec / 3600;
213                                         int etam = ((int)tsec - etah * 3600) / 60;
214                                         etah += atoi(fgGetString("/instrumentation/clock/indicated-hour"));
215                                         etam += atoi(fgGetString("/instrumentation/clock/indicated-min"));
216                                         while(etam > 59) {
217                                                 etam -= 60;
218                                                 etah += 1;
219                                         }
220                                         while(etah > 23) {
221                                                 etah -= 24;
222                                         }
223                                         char buf[6];
224                                         int n = snprintf(buf, 6, "%02i%02i", etah, etam);
225                                         _kln89->DrawText((string)buf, 2, 15-n, 1);
226                                 }
227                         } else {
228                                 _kln89->DrawText("----", 2, 11, 1);
229                         }
230                 } else {
231                         _kln89->DrawText("----", 2, 11, 1);
232                 }
233                 _kln89->DrawText("Flight", 2, 0, 0);
234                 if(_kln89->_departed) {
235                         int eh = (int)_kln89->_elapsedTime / 3600;
236                         int em = ((int)_kln89->_elapsedTime - eh * 3600) / 60;
237                         char buf[6];
238                         int n = snprintf(buf, 6, "%i:%02i", eh, em);
239                         _kln89->DrawText((string)buf, 2, 15-n, 0);
240                 } else {
241                         _kln89->DrawText("-:--", 2, 11, 0);
242                 }
243         } else {
244                 // The moving map page the core KLN89 class draws this.
245                 if(_kln89->_mapOrientation == 2 && _kln89->_groundSpeed_kts < 2) {
246                         // Don't draw it if in track up mode and groundspeed < 2kts, as per real-life unit.
247                 } else {
248                         _kln89->DrawMap(!_suspendAVS);
249                 }
250                 // Now draw any annotation over it.
251                 int scale = KLN89MapScales[_kln89->_mapScaleUnits][_kln89->_mapScaleIndex];
252                 string scle_str = GPSitoa(scale);
253                 if(crsr) {
254                         if(_menuActive) {
255                                 // Draw a background quad to encompass on/off for the first three at 'off' length
256                                 _kln89->DrawMapQuad(28, 9, 48, 36, true);
257                                 _kln89->DrawMapText("SUA:", 1, 27, true);
258                                 if(!(_menuPos == 0 && _kln89->_blink)) _kln89->DrawMapText((_kln89->_drawSUA ? "on" : "off"), 29, 27, true);
259                                 if(_menuPos == 0) _kln89->DrawLine(28, 27, 48, 27);
260                                 _kln89->DrawMapText("VOR:", 1, 18, true);
261                                 if(!(_menuPos == 1 && _kln89->_blink)) _kln89->DrawMapText((_kln89->_drawVOR ? "on" : "off"), 29, 18, true);
262                                 if(_menuPos == 1) _kln89->DrawLine(28, 18, 48, 18);
263                                 _kln89->DrawMapText("APT:", 1, 9, true);
264                                 if(!(_menuPos == 2 && _kln89->_blink)) _kln89->DrawMapText((_kln89->_drawApt ? "on" : "off"), 29, 9, true);
265                                 if(_menuPos == 2) _kln89->DrawLine(28, 9, 48, 9);
266                                 _kln89->DrawMapQuad(0, 0, 27, 8, true);
267                                 if(!(_menuPos == 3 && _kln89->_blink)) {
268                                         if(_kln89->_mapOrientation == 0) {
269                                                 _kln89->DrawMapText("N", 1, 0, true);
270                                                 _kln89->DrawMapUpArrow(7, 1);
271                                         } else if(_kln89->_mapOrientation == 1) {
272                                                 _kln89->DrawMapText("DTK", 1, 0, true);
273                                                 _kln89->DrawMapUpArrow(21, 1);
274                                         } else {
275                                                 // Don't bother with heading up for now!
276                                                 _kln89->DrawMapText("TK", 1, 0, true);
277                                                 _kln89->DrawMapUpArrow(14, 1);
278                                         }
279                                 }
280                                 if(_menuPos == 3) _kln89->DrawLine(0, 0, 27, 0);
281                         } else {
282                                 if(_uLinePos == 2) {
283                                         if(!_kln89->_blink) {
284                                                 _kln89->DrawMapText("Menu?", 1, 9, true);
285                                                 _kln89->DrawEnt();
286                                                 _kln89->DrawLine(0, 9, 34, 9);
287                                         } else {
288                                                 _kln89->DrawMapQuad(0, 9, 34, 17, true);
289                                         }
290                                 } else {
291                                         _kln89->DrawMapText("Menu?", 1, 9, true);
292                                 }
293                                 // right-justify the scale when _uLinePos == 3
294                                 if(!(_uLinePos == 3 && _kln89->_blink)) _kln89->DrawMapText(scle_str, (_uLinePos == 3 ? 29 - (scle_str.size() * 7) : 1), 0, true);
295                                 if(_uLinePos == 3) _kln89->DrawLine(0, 0, 27, 0);
296                         }
297                 } else {
298                         // Just draw the scale
299                         _kln89->DrawMapText(scle_str, 1, 0, true);
300                 }
301                 // And do part of the field 1 update, since NAV 4 is a special case for the last line.
302                 _kln89->DrawChar('>', 1, 0, 0);
303                 if(crsr && _uLinePos == 1) _kln89->Underline(1, 1, 0, 5);
304                 if(!(crsr && _uLinePos == 1 && _kln89->_blink)) {
305                         if(_kln89->_obsMode && _nav4DataSnippet == 0) _nav4DataSnippet = 1;
306                         double tsec;
307                         switch(_nav4DataSnippet) {
308                         case 0:
309                                 // DTK
310                                 _kln89->DrawLabel("DTK", -39, 6); 
311                                 // TODO - check we have an active FP / dtk and draw dashes if not.
312                                 char buf0[4];
313                                 snprintf(buf0, 4, "%03i", (int)(_kln89->_dtkMag));
314                                 _kln89->DrawText((string)buf0, 1, 3, 0);
315                                 break;
316                         case 1:
317                                 // groundspeed
318                                 _kln89->DrawSpeed(_kln89->_groundSpeed_kts, 1, 5, 0);
319                                 break;
320                         case 2:
321                                 // ETE
322                                 tsec = _kln89->GetETE();
323                                 if(tsec < 0.0) {
324                                         _kln89->DrawText("--:--", 1, 1, 0);
325                                 } else {
326                                         int eteh = (int)tsec / 3600;
327                                         int etem = ((int)tsec - eteh * 3600) / 60;
328                                         char buf[6];
329                                         int n = snprintf(buf, 6, "%02i:%02i", eteh, etem);
330                                         _kln89->DrawText((string)buf, 1, 6-n, 0);
331                                 }
332                                 break;
333                         case 3:
334                                 // Cross-track correction
335                                 double x = _kln89->CalcCrossTrackDeviation();
336                                 if(x < 0.0) {
337                                         _kln89->DrawSpecialChar(3, 1, 5, 0);
338                                 } else {
339                                         _kln89->DrawSpecialChar(7, 1, 5, 0);
340                                 }
341                                 char buf3[6];
342                                 int n;
343                                 x = fabs(x * (_kln89->_distUnits == GPS_DIST_UNITS_NM ? 1.0 : SG_NM_TO_METER * 0.001));
344                                 if(x < 1.0) {
345                                         n = snprintf(buf3, 6, "%0.2f", x);
346                                 } else if(x < 100.0) {
347                                         n = snprintf(buf3, 6, "%0.1f", x);
348                                 } else {
349                                         n = snprintf(buf3, 6, "%i", (int)(x+0.5));
350                                 }
351                                 _kln89->DrawText((string)buf3, 1, 5-n, 0);
352                                 break;
353                         }
354                 }
355         }
356         
357         KLN89Page::Update(dt);
358 }
359
360 void KLN89NavPage::LooseFocus() {
361         _suspendAVS = false;
362 }
363
364 void KLN89NavPage::CrsrPressed() {
365         if(_kln89->_mode == KLN89_MODE_DISP) {
366                 // Crsr just switched off
367                 _menuActive = false;
368         } else {
369                 // Crsr just switched on
370                 if(_subPage < 3) {
371                         _uLinePos = 1;
372                 } else {
373                         _uLinePos = 3;
374                 }
375         }
376 }
377
378 void KLN89NavPage::EntPressed() {
379         if(_kln89->_mode == KLN89_MODE_CRSR) {
380                 if(_subPage == 3 && _uLinePos == 2 && !_menuActive) {
381                         _menuActive = true;
382                         _menuPos = 0;
383                         _suspendAVS = false;
384                 }
385         }
386 }
387
388 void KLN89NavPage::ClrPressed() {
389         if(_kln89->_mode == KLN89_MODE_CRSR) {
390                 if(_subPage == 0) {
391                         if(_uLinePos == 1) {
392                                 _cdiFormat++;
393                                 if(_cdiFormat > 2) _cdiFormat = 0;
394                         } else if(_uLinePos == 2) {
395                                 _vnv++;
396                                 if(_vnv > 2) _vnv = 0;
397                         }
398                 }
399                 if(_subPage == 3) {
400                         if(_uLinePos > 1) {
401                                 _suspendAVS = !_suspendAVS;
402                                 _menuActive = false;
403                         } else if(_uLinePos == 1) {
404                                 _nav4DataSnippet++;
405                                 if(_nav4DataSnippet > 3) _nav4DataSnippet = 0;
406                         }
407                 }
408         } else {
409                 if(_subPage == 3) {
410                         _suspendAVS = !_suspendAVS;
411                 }
412         }
413 }
414
415 void KLN89NavPage::Knob1Left1() {
416         if(_kln89->_mode == KLN89_MODE_CRSR) {
417                 if(!(_subPage == 3 && _menuActive)) {
418                         if(_uLinePos > 0) _uLinePos--;
419                 } else {
420                         if(_menuPos > 0) _menuPos--;
421                 }
422         }
423 }
424
425 void KLN89NavPage::Knob1Right1() {
426         if(_kln89->_mode == KLN89_MODE_CRSR) {
427                 if(_subPage < 2) {
428                         if(_uLinePos < 2) _uLinePos++;
429                 } else if(_subPage == 2) {
430                         _uLinePos = 1;
431                 } else {
432                         // NAV 4 - this is complicated by whether the menu is displayed or not.
433                         if(_menuActive) {
434                                 if(_menuPos < 3) _menuPos++;
435                         } else {
436                                 if(_uLinePos < 3) _uLinePos++;
437                         }
438                 }
439         }
440 }
441
442 void KLN89NavPage::Knob2Left1() {
443         if(_kln89->_mode != KLN89_MODE_CRSR || _uLinePos == 0) {
444                 KLN89Page::Knob2Left1();
445                 return;
446         }
447         if(_subPage == 0) {
448                 if(_uLinePos == 1 && _cdiFormat == 2) {
449                         _kln89->CDIFSDIncrease();
450                 }
451         } else if(_subPage == 3) {
452                 if(_menuActive) {
453                         if(_menuPos == 0) {
454                                 _kln89->_drawSUA = !_kln89->_drawSUA;
455                         } else if(_menuPos == 1) {
456                                 _kln89->_drawVOR = !_kln89->_drawVOR;
457                         } else if(_menuPos == 2) {
458                                 _kln89->_drawApt = !_kln89->_drawApt;
459                         } else {
460                                 if(_kln89->_mapOrientation == 0) {
461                                         // Don't allow heading up for now
462                                         _kln89->_mapOrientation = 2;
463                                 } else {
464                                         _kln89->_mapOrientation--;
465                                 }
466                                 _kln89->UpdateMapHeading();
467                         }
468                 } else if(_uLinePos == 3) {
469                         // TODO - add AUTO
470                         if(_kln89->_mapScaleIndex == 0) {
471                                 _kln89->_mapScaleIndex = 20;
472                         } else {
473                                 _kln89->_mapScaleIndex--;
474                         }
475                 }
476         }
477 }
478
479 void KLN89NavPage::Knob2Right1() {
480         if(_kln89->_mode != KLN89_MODE_CRSR || _uLinePos == 0) {
481                 KLN89Page::Knob2Right1();
482                 return;
483         }
484         if(_subPage == 0) {
485                 if(_uLinePos == 1 && _cdiFormat == 2) {
486                         _kln89->CDIFSDDecrease();
487                 }
488         } else if(_subPage == 3) {
489                 if(_menuActive) {
490                         if(_menuPos == 0) {
491                                 _kln89->_drawSUA = !_kln89->_drawSUA;
492                         } else if(_menuPos == 1) {
493                                 _kln89->_drawVOR = !_kln89->_drawVOR;
494                         } else if(_menuPos == 2) {
495                                 _kln89->_drawApt = !_kln89->_drawApt;
496                         } else {
497                                 if(_kln89->_mapOrientation >= 2) {
498                                         // Don't allow heading up for now
499                                         _kln89->_mapOrientation = 0;
500                                 } else {
501                                         _kln89->_mapOrientation++;
502                                 }
503                                 _kln89->UpdateMapHeading();
504                         }
505                 } else if(_uLinePos == 3) {
506                         // TODO - add AUTO
507                         if(_kln89->_mapScaleIndex == 20) {
508                                 _kln89->_mapScaleIndex = 0;
509                         } else {
510                                 _kln89->_mapScaleIndex++;
511                         }
512                 }
513         }
514 }