1
// kln89_page_*.[ch]xx - this file is one of the "pages" that
2
// are used in the KLN89 GPS unit simulation.
4
// Written by David Luff, started 2005.
6
// Copyright (C) 2005 - David C Luff - david.luff@nottingham.ac.uk
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.
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.
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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
22
// $Id: kln89_page_nav.cxx,v 1.6 2006-02-21 01:19:03 mfranz Exp $
28
#include "kln89_page_nav.hxx"
29
#include <Main/fg_props.hxx>
31
KLN89NavPage::KLN89NavPage(KLN89* parent)
36
_posFormat = 0; // Check - should this default to ref from waypoint?
46
KLN89NavPage::~KLN89NavPage() {
49
void KLN89NavPage::Update(double dt) {
50
GPSFlightPlan* fp = ((KLN89*)_parent)->_activeFP;
51
GPSWaypoint* awp = _parent->GetActiveWaypoint();
52
// Scan-pull out on nav4 page switches off the cursor
53
if(3 == _subPage && fgGetBool("/instrumentation/kln89/scan-pull")) { _kln89->_mode = KLN89_MODE_DISP; }
54
bool crsr = (_kln89->_mode == KLN89_MODE_CRSR);
55
bool blink = _kln89->_blink;
56
double lat = _kln89->_gpsLat * SG_RADIANS_TO_DEGREES;
57
double lon = _kln89->_gpsLon * SG_RADIANS_TO_DEGREES;
59
if(_subPage != 3) { _scanWpSet = false; }
62
if(_kln89->_navFlagged) {
63
_kln89->DrawText("> F L A G", 2, 0, 2);
64
_kln89->DrawText("DTK --- TK ---", 2, 0, 1);
65
_kln89->DrawText(">--- To --:--", 2, 0, 0);
66
_kln89->DrawSpecialChar(0, 2, 7, 1);
67
_kln89->DrawSpecialChar(0, 2, 15, 1);
68
_kln89->DrawSpecialChar(0, 2, 4, 0);
69
_kln89->DrawSpecialChar(1, 2, 3, 2);
70
_kln89->DrawSpecialChar(1, 2, 4, 2);
71
_kln89->DrawSpecialChar(1, 2, 6, 2);
72
_kln89->DrawSpecialChar(1, 2, 10, 2);
73
_kln89->DrawSpecialChar(1, 2, 12, 2);
74
_kln89->DrawSpecialChar(1, 2, 13, 2);
77
_kln89->DrawDTO(2, 7, 3);
79
if(!(_kln89->_waypointAlert && _kln89->_blink)) {
80
_kln89->DrawSpecialChar(3, 2, 8, 3);
83
_kln89->DrawText(awp->id, 2, 10, 3);
84
if(!_kln89->_dto && !_kln89->_obsMode && !_kln89->_fromWaypoint.id.empty()) {
85
_kln89->DrawText(_kln89->_fromWaypoint.id, 2, 1, 3);
87
if(!(crsr && blink && _uLinePos == 1)) {
90
} else if(_cdiFormat == 1) {
91
_kln89->DrawText("Fly", 2, 2, 2);
92
double x = _kln89->CalcCrossTrackDeviation();
93
// TODO - check the R/L from sign of x below - I *think* it holds but not sure!
94
// Note also that we're setting Fly R or L based on the aircraft
95
// position only, not the heading. Not sure if this is correct or not.
96
_kln89->DrawText(x < 0.0 ? "R" : "L", 2, 6, 2);
99
x = fabs(x * (_kln89->_distUnits == GPS_DIST_UNITS_NM ? 1.0 : SG_NM_TO_METER * 0.001));
101
n = snprintf(buf, 6, "%0.2f", x);
102
} else if(x < 100.0) {
103
n = snprintf(buf, 6, "%0.1f", x);
105
n = snprintf(buf, 6, "%i", (int)(x+0.5));
107
_kln89->DrawText((string)buf, 2, 13-n, 2);
108
_kln89->DrawText(_kln89->_distUnits == GPS_DIST_UNITS_NM ? "nm" : "km", 2, 13, 2);
110
_kln89->DrawText("CDI Scale:", 2, 1, 2);
111
double d = _kln89->_cdiScales[_kln89->_currentCdiScaleIndex] * (_kln89->_distUnits == GPS_DIST_UNITS_NM ? 1.0 : SG_NM_TO_METER * 0.001);
115
snprintf(buf, 4, "%2.1f", d);
118
snprintf(buf, 5, "%2.2f", d);
119
// trim the leading zero
121
s = s.substr(1, s.size() - 1);
123
_kln89->DrawText(s, 2, 11, 2);
124
_kln89->DrawText(_kln89->_distUnits == GPS_DIST_UNITS_NM ? "nm" : "km", 2, 14, 2);
127
_kln89->DrawChar('>', 2, 0, 2);
128
_kln89->DrawChar('>', 2, 0, 0);
130
if(_uLinePos == 1) _kln89->Underline(2, 1, 2, 15);
131
else if(_uLinePos == 2) _kln89->Underline(2, 1, 0, 9);
133
// Desired and actual magnetic track
134
if(!_kln89->_obsMode) {
135
_kln89->DrawText("DTK", 2, 0, 1);
136
_kln89->DrawHeading((int)_kln89->_dtkMag, 2, 7, 1);
138
_kln89->DrawText("TK", 2, 9, 1);
139
if(_kln89->_groundSpeed_ms > 3) { // about 6 knots, don't know exactly what value to disable track
140
// The trouble with relying on FG gps's track value is we don't know when it's valid.
141
_kln89->DrawHeading((int)_kln89->_magTrackDeg, 2, 15, 1);
143
_kln89->DrawText("---", 2, 12, 1);
144
_kln89->DrawSpecialChar(0, 2, 15, 1);
147
// Radial to/from active waypoint.
148
// TODO - Not sure if this either is or should be true or mag!!!!!!!
149
if(!(crsr && blink && _uLinePos == 2)) {
151
_kln89->DrawHeading((int)_kln89->GetHeadingToActiveWaypoint(), 2, 4, 0);
152
_kln89->DrawText("To", 2, 5, 0);
153
} else if(1 == _vnv) {
154
_kln89->DrawHeading((int)_kln89->GetHeadingFromActiveWaypoint(), 2, 4, 0);
155
_kln89->DrawText("Fr", 2, 5, 0);
157
_kln89->DrawText("Vnv Off", 2, 1, 0);
160
// It seems that the floating point groundspeed must be at least 30kt
161
// for an ETA to be calculated. Note that this means that (integer) 30kt
162
// can appear in the frame 1 display both with and without an ETA displayed.
163
// TODO - need to switch off track (and heading bug change) based on instantaneous speed as well
164
// since the long gps lag filter means we can still be displaying when stopped on ground.
165
if(_kln89->_groundSpeed_kts > 30.0) {
166
// Assuming eta display is always hh:mm
167
// Does it ever switch to seconds when close?
168
if(_kln89->_eta / 3600.0 > 100.0) {
169
// More that 100 hours ! - Doesn't fit.
170
_kln89->DrawText("--:--", 2, 11, 0);
172
_kln89->DrawTime(_kln89->_eta, 2, 15, 0);
175
_kln89->DrawText("--:--", 2, 11, 0);
178
} else if(1 == _subPage) {
180
_kln89->DrawChar('>', 2, 1, 3);
181
if(!(crsr && blink && _uLinePos == 1)) _kln89->DrawText("PRESENT POSN", 2, 2, 3);
182
if(crsr && _uLinePos == 1) _kln89->Underline(2, 2, 3, 12);
183
if(0 == _posFormat) {
185
_kln89->DrawLatitude(lat, 2, 3, 1);
186
_kln89->DrawLongitude(lon, 2, 3, 0);
188
// Ref from wp - defaults to nearest vor (and resets to default when page left and re-entered).
190
} else if(2 == _subPage) {
191
_kln89->DrawText("Time", 2, 0, 3);
192
// TODO - hardwired to UTC at the moment
193
_kln89->DrawText("UTC", 2, 6, 3);
194
string th = fgGetString("/instrumentation/clock/indicated-hour");
195
string tm = fgGetString("/instrumentation/clock/indicated-min");
196
if(th.size() == 1) th = "0" + th;
197
if(tm.size() == 1) tm = "0" + tm;
198
_kln89->DrawText(th + tm, 2, 11, 3);
199
_kln89->DrawText("Depart", 2, 0, 2);
200
_kln89->DrawText(_kln89->_departureTimeString, 2, 11, 2);
201
_kln89->DrawText("ETA", 2, 0, 1);
202
if(_kln89->_departed) {
203
/* Rules of ETA waypoint are:
204
If the active waypoint is part of the active flightplan, then display
205
the ETA to the final (destination) waypoint of the active flightplan.
206
If the active waypoint is not part of the active flightplan, then
207
display the ETA to the active waypoint. */
208
// TODO - implement the above properly - we haven't below!
210
if(fp->waypoints.size()) {
211
wid = fp->waypoints[fp->waypoints.size() - 1]->id;
216
_kln89->DrawText(wid, 2, 4, 1);
217
double tsec = _kln89->GetTimeToWaypoint(wid);
219
_kln89->DrawText("----", 2, 11, 1);
221
int etah = (int)tsec / 3600;
222
int etam = ((int)tsec - etah * 3600) / 60;
223
etah += atoi(fgGetString("/instrumentation/clock/indicated-hour"));
224
etam += atoi(fgGetString("/instrumentation/clock/indicated-min"));
233
int n = snprintf(buf, 6, "%02i%02i", etah, etam);
234
_kln89->DrawText((string)buf, 2, 15-n, 1);
237
_kln89->DrawText("----", 2, 11, 1);
240
_kln89->DrawText("----", 2, 11, 1);
242
_kln89->DrawText("Flight", 2, 0, 0);
243
if(_kln89->_departed) {
244
int eh = (int)_kln89->_elapsedTime / 3600;
245
int em = ((int)_kln89->_elapsedTime - eh * 3600) / 60;
247
int n = snprintf(buf, 6, "%i:%02i", eh, em);
248
_kln89->DrawText((string)buf, 2, 15-n, 0);
250
_kln89->DrawText("-:--", 2, 11, 0);
252
} else { // if(3 == _subPage)
253
// Switch the cursor off if scan-pull is out on this page.
254
if(fgGetBool("/instrumentation/kln89/scan-pull")) { _kln89->_mode = KLN89_MODE_DISP; }
255
// The moving map page the core KLN89 class draws this.
256
if(_kln89->_mapOrientation == 2 && _kln89->_groundSpeed_kts < 2) {
257
// Don't draw it if in track up mode and groundspeed < 2kts, as per real-life unit.
259
_kln89->DrawMap(!_suspendAVS);
261
// Now draw any annotation over it.
262
int scale = KLN89MapScales[_kln89->_mapScaleUnits][_kln89->_mapScaleIndex];
263
string scle_str = GPSitoa(scale);
266
// Draw a background quad to encompass on/off for the first three at 'off' length
267
_kln89->DrawMapQuad(28, 9, 48, 36, true);
268
_kln89->DrawMapText("SUA:", 1, 27, true);
269
if(!(_menuPos == 0 && _kln89->_blink)) _kln89->DrawMapText((_kln89->_drawSUA ? "on" : "off"), 29, 27, true);
270
if(_menuPos == 0) _kln89->DrawLine(28, 27, 48, 27);
271
_kln89->DrawMapText("VOR:", 1, 18, true);
272
if(!(_menuPos == 1 && _kln89->_blink)) _kln89->DrawMapText((_kln89->_drawVOR ? "on" : "off"), 29, 18, true);
273
if(_menuPos == 1) _kln89->DrawLine(28, 18, 48, 18);
274
_kln89->DrawMapText("APT:", 1, 9, true);
275
if(!(_menuPos == 2 && _kln89->_blink)) _kln89->DrawMapText((_kln89->_drawApt ? "on" : "off"), 29, 9, true);
276
if(_menuPos == 2) _kln89->DrawLine(28, 9, 48, 9);
277
_kln89->DrawMapQuad(0, 0, 27, 8, true);
278
if(!(_menuPos == 3 && _kln89->_blink)) {
279
if(_kln89->_mapOrientation == 0) {
280
_kln89->DrawMapText("N", 1, 0, true);
281
_kln89->DrawMapUpArrow(7, 1);
282
} else if(_kln89->_mapOrientation == 1) {
283
_kln89->DrawMapText("DTK", 1, 0, true);
284
_kln89->DrawMapUpArrow(21, 1);
286
// Don't bother with heading up for now!
287
_kln89->DrawMapText("TK", 1, 0, true);
288
_kln89->DrawMapUpArrow(14, 1);
291
if(_menuPos == 3) _kln89->DrawLine(0, 0, 27, 0);
294
if(!_kln89->_blink) {
295
_kln89->DrawMapText("Menu?", 1, 9, true);
297
_kln89->DrawLine(0, 9, 34, 9);
299
_kln89->DrawMapQuad(0, 9, 34, 17, true);
302
_kln89->DrawMapText("Menu?", 1, 9, true);
304
// right-justify the scale when _uLinePos == 3
305
if(!(_uLinePos == 3 && _kln89->_blink)) _kln89->DrawMapText(scle_str, (_uLinePos == 3 ? 29 - (scle_str.size() * 7) : 1), 0, true);
306
if(_uLinePos == 3) _kln89->DrawLine(0, 0, 27, 0);
309
// Just draw the scale
310
_kln89->DrawMapText(scle_str, 1, 0, true);
312
// If the scan-pull knob is out, draw one of the waypoints (if applicable).
313
if(fgGetBool("/instrumentation/kln89/scan-pull")) {
314
if(_kln89->_activeFP->waypoints.size()) {
315
//cout << "Need to draw a waypoint!\n";
316
_kln89->DrawLine(70, 0, 111, 0);
317
if(!_kln89->_blink) {
318
//_kln89->DrawMapQuad(45, 0, 97, 8, true);
320
_scanWpIndex = _kln89->GetActiveWaypointIndex();
323
_kln89->DrawMapText(_kln89->_activeFP->waypoints[_scanWpIndex]->id, 71, 0, true);
327
// And do part of the field 1 update, since NAV 4 is a special case for the last line.
328
_kln89->DrawChar('>', 1, 0, 0);
329
if(crsr && _uLinePos == 1) _kln89->Underline(1, 1, 0, 5);
330
if(!(crsr && _uLinePos == 1 && _kln89->_blink)) {
331
if(_kln89->_obsMode && _nav4DataSnippet == 0) _nav4DataSnippet = 1;
333
switch(_nav4DataSnippet) {
336
_kln89->DrawLabel("DTK", -39, 6);
337
// TODO - check we have an active FP / dtk and draw dashes if not.
339
snprintf(buf0, 4, "%03i", (int)(_kln89->_dtkMag));
340
_kln89->DrawText((string)buf0, 1, 3, 0);
344
_kln89->DrawSpeed(_kln89->_groundSpeed_kts, 1, 5, 0);
348
tsec = _kln89->GetETE();
350
_kln89->DrawText("--:--", 1, 1, 0);
352
int eteh = (int)tsec / 3600;
353
int etem = ((int)tsec - eteh * 3600) / 60;
355
int n = snprintf(buf, 6, "%02i:%02i", eteh, etem);
356
_kln89->DrawText((string)buf, 1, 6-n, 0);
360
// Cross-track correction
361
double x = _kln89->CalcCrossTrackDeviation();
363
_kln89->DrawSpecialChar(3, 1, 5, 0);
365
_kln89->DrawSpecialChar(7, 1, 5, 0);
369
x = fabs(x * (_kln89->_distUnits == GPS_DIST_UNITS_NM ? 1.0 : SG_NM_TO_METER * 0.001));
371
n = snprintf(buf3, 6, "%0.2f", x);
372
} else if(x < 100.0) {
373
n = snprintf(buf3, 6, "%0.1f", x);
375
n = snprintf(buf3, 6, "%i", (int)(x+0.5));
377
_kln89->DrawText((string)buf3, 1, 5-n, 0);
383
KLN89Page::Update(dt);
386
// Returns the id string of the selected waypoint on NAV4 if valid, else returns an empty string.
387
string KLN89NavPage::GetNav4WpId() {
389
if(fgGetBool("/instrumentation/kln89/scan-pull")) {
390
if(_kln89->_activeFP->waypoints.size()) {
392
return(_kln89->_activeWaypoint.id);
394
return(_kln89->_activeFP->waypoints[_scanWpIndex]->id);
402
void KLN89NavPage::LooseFocus() {
407
void KLN89NavPage::CrsrPressed() {
408
if(_kln89->_mode == KLN89_MODE_DISP) {
409
// Crsr just switched off
412
// Crsr just switched on
421
void KLN89NavPage::EntPressed() {
422
if(_kln89->_mode == KLN89_MODE_CRSR) {
423
if(_subPage == 3 && _uLinePos == 2 && !_menuActive) {
431
void KLN89NavPage::ClrPressed() {
432
if(_kln89->_mode == KLN89_MODE_CRSR) {
436
if(_cdiFormat > 2) _cdiFormat = 0;
437
} else if(_uLinePos == 2) {
439
if(_vnv > 2) _vnv = 0;
444
_suspendAVS = !_suspendAVS;
446
} else if(_uLinePos == 1) {
448
if(_nav4DataSnippet > 3) _nav4DataSnippet = 0;
453
_suspendAVS = !_suspendAVS;
458
void KLN89NavPage::Knob1Left1() {
459
if(_kln89->_mode == KLN89_MODE_CRSR) {
460
if(!(_subPage == 3 && _menuActive)) {
461
if(_uLinePos > 0) _uLinePos--;
463
if(_menuPos > 0) _menuPos--;
468
void KLN89NavPage::Knob1Right1() {
469
if(_kln89->_mode == KLN89_MODE_CRSR) {
471
if(_uLinePos < 2) _uLinePos++;
472
} else if(_subPage == 2) {
475
// NAV 4 - this is complicated by whether the menu is displayed or not.
477
if(_menuPos < 3) _menuPos++;
479
if(_uLinePos < 3) _uLinePos++;
485
void KLN89NavPage::Knob2Left1() {
486
// If the inner-knob is out on the nav4 page, the only effect is to cycle the displayed waypoint.
487
if(3 == _subPage && fgGetBool("/instrumentation/kln89/scan-pull")) {
488
if(_kln89->_activeFP->waypoints.size()) { // TODO - find out what happens when scan-pull is on on nav4 without an active FP.
489
// It's unlikely that we could get here without _scanWpSet, but theoretically possible, so we need to cover it.
491
_scanWpIndex = _kln89->GetActiveWaypointIndex();
494
if(0 == _scanWpIndex) {
495
_scanWpIndex = _kln89->_activeFP->waypoints.size() - 1;
503
if(_kln89->_mode != KLN89_MODE_CRSR || _uLinePos == 0) {
504
KLN89Page::Knob2Left1();
508
if(_uLinePos == 1 && _cdiFormat == 2) {
509
_kln89->CDIFSDIncrease();
511
} else if(_subPage == 3) {
514
_kln89->_drawSUA = !_kln89->_drawSUA;
515
} else if(_menuPos == 1) {
516
_kln89->_drawVOR = !_kln89->_drawVOR;
517
} else if(_menuPos == 2) {
518
_kln89->_drawApt = !_kln89->_drawApt;
520
if(_kln89->_mapOrientation == 0) {
521
// Don't allow heading up for now
522
_kln89->_mapOrientation = 2;
524
_kln89->_mapOrientation--;
526
_kln89->UpdateMapHeading();
528
} else if(_uLinePos == 3) {
530
if(_kln89->_mapScaleIndex == 0) {
531
_kln89->_mapScaleIndex = 20;
533
_kln89->_mapScaleIndex--;
539
void KLN89NavPage::Knob2Right1() {
540
// If the inner-knob is out on the nav4 page, the only effect is to cycle the displayed waypoint.
541
if(3 == _subPage && fgGetBool("/instrumentation/kln89/scan-pull")) {
542
if(_kln89->_activeFP->waypoints.size()) { // TODO - find out what happens when scan-pull is on on nav4 without an active FP.
543
// It's unlikely that we could get here without _scanWpSet, but theoretically possible, so we need to cover it.
545
_scanWpIndex = _kln89->GetActiveWaypointIndex();
549
if(_scanWpIndex > _kln89->_activeFP->waypoints.size() - 1) {
556
if(_kln89->_mode != KLN89_MODE_CRSR || _uLinePos == 0) {
557
KLN89Page::Knob2Right1();
561
if(_uLinePos == 1 && _cdiFormat == 2) {
562
_kln89->CDIFSDDecrease();
564
} else if(_subPage == 3) {
567
_kln89->_drawSUA = !_kln89->_drawSUA;
568
} else if(_menuPos == 1) {
569
_kln89->_drawVOR = !_kln89->_drawVOR;
570
} else if(_menuPos == 2) {
571
_kln89->_drawApt = !_kln89->_drawApt;
573
if(_kln89->_mapOrientation >= 2) {
574
// Don't allow heading up for now
575
_kln89->_mapOrientation = 0;
577
_kln89->_mapOrientation++;
579
_kln89->UpdateMapHeading();
581
} else if(_uLinePos == 3) {
583
if(_kln89->_mapScaleIndex == 20) {
584
_kln89->_mapScaleIndex = 0;
586
_kln89->_mapScaleIndex++;