]> git.mxchange.org Git - flightgear.git/blob - src/Instrumentation/KLN89/kln89.cxx
Merge branch 'jmt/reciprocal'
[flightgear.git] / src / Instrumentation / KLN89 / kln89.cxx
1 // kln89_page.cxx - a class to manage the simulation of a KLN89
2 //                  GPS unit.  Note that this is primarily the 
3 //                  simulation of the user interface and display
4 //                  - the core GPS calculations such as position
5 //                  and waypoint sequencing are done (or should 
6 //                  be done) by FG code. 
7 //
8 // Written by David Luff, started 2005.
9 //
10 // Copyright (C) 2005 - David C Luff - david.luff@nottingham.ac.uk
11 //
12 // This program is free software; you can redistribute it and/or
13 // modify it under the terms of the GNU General Public License as
14 // published by the Free Software Foundation; either version 2 of the
15 // License, or (at your option) any later version.
16 //
17 // This program is distributed in the hope that it will be useful, but
18 // WITHOUT ANY WARRANTY; without even the implied warranty of
19 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
20 // 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 Free Software
24 // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
25 //
26 // $Id$
27
28 #include "kln89.hxx"
29 #include "kln89_page.hxx"
30 #include "kln89_page_apt.hxx"
31 #include "kln89_page_vor.hxx"
32 #include "kln89_page_ndb.hxx"
33 #include "kln89_page_int.hxx"
34 #include "kln89_page_usr.hxx"
35 #include "kln89_page_act.hxx"
36 #include "kln89_page_nav.hxx"
37 #include "kln89_page_fpl.hxx"
38 #include "kln89_page_cal.hxx"
39 #include "kln89_page_set.hxx"
40 #include "kln89_page_oth.hxx"
41 #include "kln89_page_dir.hxx"
42 #include "kln89_page_nrst.hxx"
43 #include "kln89_symbols.hxx"
44 #include <iostream>
45
46 #include <ATCDCL/ATCProjection.hxx>
47 #include <Main/fg_props.hxx>
48 #include <simgear/math/SGMath.hxx>
49 #include <Airports/simple.hxx>
50
51 using std::cout;
52
53 KLN89::KLN89(RenderArea2D* instrument) 
54 : DCLGPS(instrument) {
55         _mode = KLN89_MODE_DISP;
56         _blink = false;
57         _cum_dt = 0.0;
58         _nFields = 2;
59         _maxFields = 2;
60         _xBorder = 0;
61         _yBorder = 4;
62         // ..Field..[0] => no fields in action
63         _xFieldBorder[0] = 0;
64         _xFieldBorder[1] = 0;
65         _yFieldBorder[0] = 0;
66         _yFieldBorder[1] = 0;
67         _xFieldBorder[2] = 2;
68         _yFieldBorder[2] = 0;
69         _xFieldStart[0] = 0;
70         _xFieldStart[1] = 0;
71         _xFieldStart[2] = 45;
72         _yFieldStart[0] = 0;
73         _yFieldStart[1] = 0;
74         _yFieldStart[2] = 0;
75         
76         //_pixelated = true;
77         _pixelated = false;
78
79         // Cyclic pages
80         GPSPage* apt_page = new KLN89AptPage(this);
81         _pages.push_back(apt_page);
82         GPSPage* vor_page = new KLN89VorPage(this);
83         _pages.push_back(vor_page);
84         GPSPage* ndb_page = new KLN89NDBPage(this);
85         _pages.push_back(ndb_page);
86         GPSPage* int_page = new KLN89IntPage(this);
87         _pages.push_back(int_page);
88         GPSPage* usr_page = new KLN89UsrPage(this);
89         _pages.push_back(usr_page);
90         GPSPage* act_page = new KLN89ActPage(this);
91         _pages.push_back(act_page);
92         GPSPage* nav_page = new KLN89NavPage(this);
93         _pages.push_back(nav_page);
94         GPSPage* fpl_page = new KLN89FplPage(this);
95         _pages.push_back(fpl_page);
96         GPSPage* cal_page = new KLN89CalPage(this);
97         _pages.push_back(cal_page);
98         GPSPage* set_page = new KLN89SetPage(this);
99         _pages.push_back(set_page);
100         GPSPage* oth_page = new KLN89OthPage(this);
101         _pages.push_back(oth_page);
102         _nPages = _pages.size();
103         _curPage = 0;
104         
105         // Other pages
106         _dir_page = new KLN89DirPage(this);
107         _nrst_page = new KLN89NrstPage(this);
108         
109         _activePage = apt_page;
110         _obsMode = false;
111         _dto = false;
112         _fullLegMode = true;
113         _obsHeading = 215;
114         
115         _maxFlightPlans = 26;
116         for(unsigned int i=0; i<_maxFlightPlans; ++i) {
117                 GPSFlightPlan* fp = new GPSFlightPlan;
118                 fp->waypoints.clear();
119                 _flightPlans.push_back(fp);
120         }
121         _activeFP = _flightPlans[0];
122         
123         // Hackish
124         _entJump = -1;
125         _entRestoreCrsr = false;
126         
127         _dispMsg = false;
128
129         // Moving map stuff
130         _mapOrientation = 0;
131         _mapHeading = 0.0;
132         _mapHeadingUpdateTimer = 0.0;
133         _drawSUA = false;
134         _drawVOR = false;
135         _drawApt = true;
136         //_mapScaleIndex = 20;
137         _mapScaleIndex = 7;     // I think that the above is more accurate for no-flightplan default, but this is more sane for initial testing!
138         _mapScaleAuto = true;
139         
140         // Mega-hack - hardwire airport town and state names for the FG base area since we don't have any data for these at the moment
141         // TODO - do this better one day!
142         _airportTowns["KSFO"] = "San Francisco";
143         _airportTowns["KSQL"] = "San Carlos";
144         _airportTowns["KPAO"] = "Palo Alto";
145         _airportTowns["KNUQ"] = "Mountain View";
146         _airportTowns["KSJC"] = "San Jose";
147         _airportTowns["KRHV"] = "San Jose";
148         _airportTowns["E16"] = "San Martin";
149         _airportTowns["KWVI"] = "Watsonville";
150         _airportTowns["KOAK"] = "Oakland";
151         _airportTowns["KHWD"] = "Hayward";
152         _airportTowns["KLVK"] = "Livermore";
153         _airportTowns["KCCR"] = "Concord";
154         _airportTowns["KTCY"] = "Tracy";
155         _airportTowns["KSCK"] = "Stockton";
156         _airportTowns["KHAF"] = "Half Moon Bay";
157         
158         _airportStates["KSFO"] = "CA";
159         _airportStates["KSQL"] = "CA";
160         _airportStates["KPAO"] = "CA";
161         _airportStates["KNUQ"] = "CA";
162         _airportStates["KSJC"] = "CA";
163         _airportStates["KRHV"] = "CA";
164         _airportStates["E16"] = "CA";
165         _airportStates["KWVI"] = "CA";
166         _airportStates["KOAK"] = "CA";
167         _airportStates["KHWD"] = "CA";
168         _airportStates["KLVK"] = "CA";
169         _airportStates["KCCR"] = "CA";
170         _airportStates["KTCY"] = "CA";
171         _airportStates["KSCK"] = "CA";
172         _airportStates["KHAF"] = "CA";
173 }
174
175 KLN89::~KLN89() {
176         for(unsigned int i=0; i<_pages.size(); ++i) {
177                 delete _pages[i];
178         }
179         
180         delete _dir_page;
181         delete _nrst_page;
182         
183         for(unsigned int i=0; i<_maxFlightPlans; ++i) {
184                 ClearFlightPlan(i);
185                 delete _flightPlans[i];
186         }
187 }
188
189 void KLN89::bind() {
190         fgTie("/instrumentation/gps/message-alert", this, &KLN89::GetMsgAlert);
191         DCLGPS::bind();
192 }
193
194 void KLN89::unbind() {
195         fgUntie("/instrumentation/gps/message-alert");
196         DCLGPS::unbind();
197 }
198
199 void KLN89::update(double dt) {
200         // Run any positional calc's required first
201         DCLGPS::update(dt);
202         
203         _cum_dt += dt;
204         if(_blink) {
205                 if(_cum_dt > 0.2) {
206                         _cum_dt = 0.0;
207                         _blink = false;
208                 }
209         } else {
210                 if(_cum_dt > 0.8) {
211                         _cum_dt = 0.0;
212                         _blink = true;
213                 }
214         }
215         
216         _mapHeadingUpdateTimer += dt;
217         if(_mapHeadingUpdateTimer > 1.0) {
218                 UpdateMapHeading();
219                 _mapHeadingUpdateTimer = 0.0;
220         }
221         
222         _instrument->Flush();
223         _instrument->DrawBackground();
224         
225         if(_dispMsg) {
226                 if(_messageStack.empty()) {
227                         DrawText("No Message", 0, 5, 2);
228                 } else {
229                         // TODO - parse the message string for special strings that indicate degrees signs etc!
230                         DrawText(*_messageStack.begin(), 0, 0, 3);
231                 }
232                 return;
233         } else {
234                 if(!_messageStack.empty()) {
235                         DrawMessageAlert();
236                 }
237         }
238         
239         if(_curPage == 6 && _activePage->GetSubPage() == 3) {
240                 // Don't draw the bar on the nav-4 page
241         } else if(_activePage == _nrst_page) {
242                 // Don't draw the bar on the nearest page
243         } else {
244                 DrawBar(_curPage);
245         }
246         
247         _activePage->Update(dt);
248 }
249
250 void KLN89::CreateDefaultFlightPlans() {
251         // TODO - read these in from preferences.xml or similar instead!!!!
252         // Create some hardwired default flightplans for testing.
253         vector<string> ids;
254         vector<GPSWpType> wps;
255         
256         ids.clear();
257         wps.clear();
258         ids.push_back("KLSN");
259         wps.push_back(GPS_WP_APT);
260         ids.push_back("VOLTA");
261         wps.push_back(GPS_WP_INT);
262         ids.push_back("C83");
263         wps.push_back(GPS_WP_APT);
264         CreateFlightPlan(_flightPlans[5], ids, wps);
265         
266         ids.clear();
267         wps.clear();
268         ids.push_back("KCCR");
269         wps.push_back(GPS_WP_APT);
270         ids.push_back("SUZYE");
271         wps.push_back(GPS_WP_INT);
272         ids.push_back("ALTAM");
273         wps.push_back(GPS_WP_INT);
274         ids.push_back("C83");
275         wps.push_back(GPS_WP_APT);
276         CreateFlightPlan(_flightPlans[4], ids, wps);
277         
278         ids.clear();
279         wps.clear();
280         ids.push_back("KLVK");
281         wps.push_back(GPS_WP_APT);
282         ids.push_back("OAK");
283         wps.push_back(GPS_WP_VOR);
284         ids.push_back("PORTE");
285         wps.push_back(GPS_WP_INT);
286         ids.push_back("KHAF");
287         wps.push_back(GPS_WP_APT);
288         CreateFlightPlan(_flightPlans[3], ids, wps);
289         
290         ids.clear();
291         wps.clear();
292         ids.push_back("KDPA");
293         wps.push_back(GPS_WP_APT);
294         ids.push_back("OBK");
295         wps.push_back(GPS_WP_VOR);
296         ids.push_back("ENW");
297         wps.push_back(GPS_WP_VOR);
298         ids.push_back("KRAC");
299         wps.push_back(GPS_WP_APT);
300         CreateFlightPlan(_flightPlans[2], ids, wps);
301         //cout << "Size of FP2 WP list is " << _flightPlans[2]->waypoints.size() << '\n';
302         
303         ids.clear();
304         wps.clear();
305         ids.push_back("KSFO");
306         ids.push_back("KOAK");
307         wps.push_back(GPS_WP_APT);
308         wps.push_back(GPS_WP_APT);
309         CreateFlightPlan(_flightPlans[1], ids, wps);
310         
311         ids.clear();
312         wps.clear();
313         //ids.push_back("KOSH");
314         ids.push_back("KSFO");
315         ids.push_back("KHAF");
316         ids.push_back("OSI");
317         ids.push_back("KSQL");
318         //ids.push_back("KPAO");
319         //ids.push_back("KHWD");
320         wps.push_back(GPS_WP_APT);
321         wps.push_back(GPS_WP_APT);
322         wps.push_back(GPS_WP_VOR);
323         wps.push_back(GPS_WP_APT);
324         //wps.push_back(GPS_WP_APT);
325         //wps.push_back(GPS_WP_APT);
326         CreateFlightPlan(_flightPlans[0], ids, wps);
327         
328         /*
329         ids.clear();
330         wps.clear();
331         ids.push_back("KLVK");
332         ids.push_back("KHWD");
333         wps.push_back(GPS_WP_APT);
334         wps.push_back(GPS_WP_APT);
335         CreateFlightPlan(_flightPlans[0], ids, wps);
336         */
337 }
338
339 void KLN89::Knob1Right1() {
340         if(_mode == KLN89_MODE_DISP) {
341                 _activePage->LooseFocus();
342                 if(_cleanUpPage >= 0) {
343                         _pages[(unsigned int)_cleanUpPage]->CleanUp();
344                         _cleanUpPage = -1;
345                 }
346                 _curPage++;
347                 if(_curPage >= _pages.size()) _curPage = 0;
348                 _activePage = _pages[_curPage];
349         } else {
350                 _activePage->Knob1Right1();
351         }
352         update(0.0);
353 }
354
355 void KLN89::Knob1Left1() {
356         if(_mode == KLN89_MODE_DISP) {
357                 _activePage->LooseFocus();
358                 if(_cleanUpPage >= 0) {
359                         _pages[(unsigned int)_cleanUpPage]->CleanUp();
360                         _cleanUpPage = -1;
361                 }
362                 if(_curPage == 0) {
363                         _curPage = _pages.size() - 1;
364                 } else {
365                         _curPage--;
366                 }
367                 _activePage = _pages[_curPage];
368         } else {
369                 _activePage->Knob1Left1();
370         }
371         update(0.0);
372 }
373
374 void KLN89::Knob2Left1() {
375         _activePage->Knob2Left1();
376 }
377
378 void KLN89::Knob2Right1() {
379         _activePage->Knob2Right1();
380 }
381
382 void KLN89::CrsrPressed() {
383         _dispMsg = false;
384         // CRSR cannot be switched off on nrst page.
385         if(_activePage == _nrst_page) { return; }
386         // CRSR is always off when inner-knob is out on nav4 page.
387         if(_curPage == 6 && _activePage->GetSubPage() == 3 && fgGetBool("/instrumentation/kln89/scan-pull")) { return; }
388         if(_cleanUpPage >= 0) {
389                 _pages[(unsigned int)_cleanUpPage]->CleanUp();
390                 _cleanUpPage = -1;
391         }
392         _entRestoreCrsr = false;
393         _entJump = -1;
394         ((KLN89Page*)_activePage)->SetEntInvert(false);
395         if(_mode == KLN89_MODE_DISP) {
396                 _mode = KLN89_MODE_CRSR;
397                 _activePage->CrsrPressed();
398         } else {
399                 _mode = KLN89_MODE_DISP;
400                 _activePage->CrsrPressed();
401         }
402         update(0.0);
403 }
404
405 void KLN89::EntPressed() {
406         if(_entJump >= 0) {
407                 if(_curPage < 5) {
408                         // one of the data pages.  Signal ent pressed to it here, and ent pressed to the call back page a few lines further down.
409                         // Ie. 2 ent pressed signals in this case is deliberate.
410                         _activePage->EntPressed();
411                 }
412                 _curPage = _entJump;
413                 _activePage = _pages[(unsigned int)_entJump];
414                 if(_entRestoreCrsr) _mode = KLN89_MODE_CRSR;
415                 _entJump = -1;
416         }
417         if(_activePage == _dir_page) {
418                 _dir_page->EntPressed();
419                 _mode = KLN89_MODE_DISP;
420                 _activePage = _pages[_curPage];
421         } else {
422                 _activePage->EntPressed();
423         }
424 }
425
426 void KLN89::ClrPressed() {
427         _activePage->ClrPressed();
428 }
429
430 void KLN89::DtoPressed() {
431         if(_activePage != _dir_page) {
432                 // Figure out which waypoint the dir page should display
433                 if(_curPage <= 5) {
434                         // Apt, Vor, Ndb, Int, Usr or Act
435                         if(!_activePage->GetId().empty()) {     // Guard against no user waypoints defined
436                                 _dir_page->SetId(_activePage->GetId());
437                         } else {
438                                 _dir_page->SetId(_activeWaypoint.id);
439                         }
440                 } else if(_curPage == 6 && _activePage->GetSubPage() == 3 && fgGetBool("/instrumentation/kln89/scan-pull") && _activeFP->waypoints.size()) {
441                         // NAV 4
442                         _dir_page->SetId(((KLN89NavPage*)_activePage)->GetNav4WpId());
443                 } else if(_curPage == 7 && _activePage->GetSubPage() == 0 && _mode == KLN89_MODE_CRSR) {
444                         //cout << "Checking the fpl page!\n";
445                         // FPL 0
446                         if(!_activePage->GetId().empty()) {
447                                 //cout << "Not empty!!!\n";
448                                 _dir_page->SetId(_activePage->GetId());
449                         } else {
450                                 //cout << "empty :-(\n";
451                                 _dir_page->SetId(_activeWaypoint.id);
452                         }
453                 } else {
454                         _dir_page->SetId(_activeWaypoint.id);
455                 }
456                 // This need to come after the bit before otherwise the FPL or NAV4 page clears their current ID when it looses focus.
457                 _activePage->LooseFocus();
458                 _activePage = _dir_page;
459                 _mode = KLN89_MODE_CRSR;
460         }
461 }
462
463 void KLN89::NrstPressed() {
464         if(_activePage != _nrst_page) {
465                 _activePage->LooseFocus();      // TODO - check whether we should call loose focus here
466                 _lastActivePage = _activePage;
467                 _activePage = _nrst_page;
468                 _lastMode = _mode;
469                 _mode = KLN89_MODE_CRSR;
470         } else {
471                 _activePage = _lastActivePage;
472                 _mode = _lastMode;
473         }
474 }
475         
476 void KLN89::AltPressed() {}
477
478 void KLN89::OBSPressed() {
479         DCLGPS::OBSPressed();
480         if(_obsMode) {
481                 // if(ORS 02)
482                 _mode = KLN89_MODE_CRSR;
483                 _activePage->OBSPressed();
484         }
485 }
486
487 void KLN89::MsgPressed() {
488         // TODO - handle persistent messages such as SUA alerting.
489         // (The message annunciation flashes before first view, but afterwards remains continuously lit with the message available
490         // until the potential conflict no longer pertains).
491         if(_dispMsg && _messageStack.size()) {
492                 _messageStack.pop_front();
493         }
494         _dispMsg = !_dispMsg;
495 }
496
497 void KLN89::DrawBar(int page) {
498         int px = 1 + (page * 15);
499         int py = 1;
500         for(int i=0; i<7; ++i) {
501                 // Ugh - this is crude and inefficient!
502                 _instrument->DrawPixel(px+i, py);
503                 _instrument->DrawPixel(px+i, py+1);
504         }
505 }
506
507 // Convert moving map to instrument co-ordinates
508 void KLN89::MapToInstrument(int &x, int &y) {
509         x += _xBorder + _xFieldBorder[2] + _xFieldStart[2];
510 }
511
512 // Draw a pixel specified in instrument co-ords, but clipped to the map region
513 //void KLN89::DrawInstrMapPixel(int x, int y) {
514
515 /*
516 // Clip, translate and draw a map pixel
517 // If we didn't need per-pixel clipping, it would be cheaper to translate object rather than pixel positions.
518 void KLN89::DrawMapPixel(int x, int y, bool invert) {
519         if(x < 0 || x > 111 || y < 0 || y > 39)  return;
520         x += _xBorder + _xFieldBorder[2] + _xFieldStart[2];
521         _instrument->DrawPixel(x, y, invert);
522 }
523 */
524
525 // HACK - use something FG provides
526 static double gps_min(const double &a, const double &b) {
527         return(a <= b ? a : b);
528 }
529
530 static double gps_max(const double &a, const double &b) {
531         return(a >= b ? a : b);
532 }
533
534 void KLN89::UpdateMapHeading() {
535         switch(_mapOrientation) {
536         case 0:         // North up
537                 _mapHeading = 0.0;
538                 break;
539         case 1:         // DTK up
540                 _mapHeading = _dtkTrue;
541                 break;
542         case 2:         // Track up
543                 _mapHeading = _track;
544                 break;
545         }
546 }               
547
548 // The screen area allocated to the moving map is 111 x 40 pixels.
549 // In North up mode, the user position marker is at 57, 20. (Map co-ords).
550 void KLN89::DrawMap(bool draw_avs) {
551         // Set the clipping region to the moving map part of the display
552         int xstart = _xBorder + _xFieldBorder[2] + _xFieldStart[2];
553         _instrument->SetClipRegion(xstart, 0, xstart + 110, 39);
554         
555         _mapScaleUnits = (int)_distUnits;
556         _mapScale = (double)(KLN89MapScales[_mapScaleUnits][_mapScaleIndex]);
557         
558         //cout << "Map scale = " << _mapScale << '\n';
559         
560         double mapScaleMeters = _mapScale * (_mapScaleUnits == 0 ? SG_NM_TO_METER : 1000);
561         
562         // TODO - use an aligned projection when either DTK or TK up!
563         FGATCAlignedProjection mapProj(SGGeod::fromRad(_gpsLon, _gpsLat), _mapHeading);
564         
565         double meter_per_pix = (_mapOrientation == 0 ? mapScaleMeters / 20.0f : mapScaleMeters / 29.0f);
566         
567         SGGeod bottomLeft = mapProj.ConvertFromLocal(SGVec3d(gps_max(-57.0 * meter_per_pix, -50000), gps_max((_mapOrientation == 0 ? -20.0 * meter_per_pix : -11.0 * meter_per_pix), -25000), 0.0));
568         SGGeod topRight = mapProj.ConvertFromLocal(SGVec3d(gps_min(54.0 * meter_per_pix, 50000), gps_min((_mapOrientation == 0 ? 20.0 * meter_per_pix : 29.0 * meter_per_pix), 25000), 0.0));
569         
570         // Draw Airport labels first (but not one's that are waypoints)
571         // Draw Airports first (but not one's that are waypoints)
572         // Ditto for VORs (not sure if SUA/VOR/Airport ordering is important or not).
573         // Ditto for SUA
574         // Then flighttrack
575         // Then waypoints
576         // Then waypoint labels (not sure if this should be before or after waypoints)
577         // Then user pos.
578         // Annotation then gets drawn by Nav page, NOT this function.
579
580         if(_drawApt && draw_avs) {
581                 /*
582                 bool have_apt = _overlays->FindArpByRegion(&apt, bottomLeft.lat(), bottomLeft.lon(), topRight.lat(), topRight.lon());
583                 //cout << "Vors enclosed are: ";
584                 // Draw all the labels first...
585                 for(unsigned int i=0; i<apt.size(); ++i) {
586                         //cout << nav[i]->id << ' ';
587                         Point3D p = mapProj.ConvertToLocal(Point3D(apt[i]->lon * SG_RADIANS_TO_DEGREES, apt[i]->lat * SG_RADIANS_TO_DEGREES, 0.0));
588                         //cout << p << " .... ";
589                         int mx = int(p.x() / meter_per_pix) + 56;
590                         int my = int(p.y() / meter_per_pix) + (_mapOrientation == 0 ? 19 : 10);
591                         //cout << "mx = " << mx << ", my = " << my << '\n';
592                         bool right_align = (p.x() < 0.0);
593                         DrawLabel(apt[i]->id, mx + (right_align ? -2 : 3), my + (p.y() < 0.0 ? -7 : 3), right_align);
594                         // I think that we probably should have -1 in the right_align case above to match the real life instrument.
595                 }
596                 // ...and then all the Apts.
597                 for(unsigned int i=0; i<apt.size(); ++i) {
598                         //cout << nav[i]->id << ' ';
599                         Point3D p = mapProj.ConvertToLocal(Point3D(apt[i]->lon * SG_RADIANS_TO_DEGREES, apt[i]->lat * SG_RADIANS_TO_DEGREES, 0.0));
600                         //cout << p << " .... ";
601                         int mx = int(p.x() / meter_per_pix) + 56;
602                         int my = int(p.y() / meter_per_pix) + (_mapOrientation == 0 ? 19 : 10);
603                         //cout << "mx = " << mx << ", my = " << my << '\n';
604                         DrawApt(mx, my);
605                 }
606                 //cout << '\n';
607                 */
608         }
609         /*
610         if(_drawVOR && draw_avs) {
611                 Overlays::nav_array_type nav;
612                 bool have_vor = _overlays->FindVorByRegion(&nav, bottomLeft.lat(), bottomLeft.lon(), topRight.lat(), topRight.lon());
613                 //cout << "Vors enclosed are: ";
614                 // Draw all the labels first...
615                 for(unsigned int i=0; i<nav.size(); ++i) {
616                         //cout << nav[i]->id << ' ';
617                         Point3D p = mapProj.ConvertToLocal(Point3D(nav[i]->lon * SG_RADIANS_TO_DEGREES, nav[i]->lat * SG_RADIANS_TO_DEGREES, 0.0));
618                         //cout << p << " .... ";
619                         int mx = int(p.x() / meter_per_pix) + 56;
620                         int my = int(p.y() / meter_per_pix) + (_mapOrientation == 0 ? 19 : 10);
621                         //cout << "mx = " << mx << ", my = " << my << '\n';
622                         bool right_align = (p.x() < 0.0);
623                         DrawLabel(nav[i]->id, mx + (right_align ? -2 : 3), my + (p.y() < 0.0 ? -7 : 3), right_align);
624                         // I think that we probably should have -1 in the right_align case above to match the real life instrument.
625                 }
626                 // ...and then all the VORs.
627                 for(unsigned int i=0; i<nav.size(); ++i) {
628                         //cout << nav[i]->id << ' ';
629                         Point3D p = mapProj.ConvertToLocal(Point3D(nav[i]->lon * SG_RADIANS_TO_DEGREES, nav[i]->lat * SG_RADIANS_TO_DEGREES, 0.0));
630                         //cout << p << " .... ";
631                         int mx = int(p.x() / meter_per_pix) + 56;
632                         int my = int(p.y() / meter_per_pix) + (_mapOrientation == 0 ? 19 : 10);
633                         //cout << "mx = " << mx << ", my = " << my << '\n';
634                         DrawVOR(mx, my);
635                 }
636                 //cout << '\n';
637         }
638         */
639         
640         // FlightTrack
641         if(_activeFP->waypoints.size() > 1) {
642                 vector<int> xvec, yvec, qvec;   // qvec stores the quadrant that each waypoint label should
643                                                                                 // be drawn in (relative to the waypoint). 
644                                                                                 // 1 = NE, 2 = SE, 3 = SW, 4 = NW.
645                 double save_h = 0.0; // Each pass, save a heading from the previous one for label quadrant determination.
646                 bool drawTrack = true;
647                 for(unsigned int i=1; i<_activeFP->waypoints.size(); ++i) {
648                         GPSWaypoint* wp0 = _activeFP->waypoints[i-1];
649                         GPSWaypoint* wp1 = _activeFP->waypoints[i];
650                         SGVec3d p0 = mapProj.ConvertToLocal(SGGeod::fromRad(wp0->lon, wp0->lat));
651                         SGVec3d p1 = mapProj.ConvertToLocal(SGGeod::fromRad(wp1->lon, wp1->lat));
652                         int mx0 = int(p0.x() / meter_per_pix) + 56;
653                         int my0 = int(p0.y() / meter_per_pix) + (_mapOrientation == 0 ? 19 : 10);
654                         int mx1 = int(p1.x() / meter_per_pix) + 56;
655                         int my1 = int(p1.y() / meter_per_pix) + (_mapOrientation == 0 ? 19 : 10);
656                         if(i == 1) {
657                                 xvec.push_back(mx0);
658                                 yvec.push_back(my0);
659                                 double h = GetGreatCircleCourse(wp0->lat, wp0->lon, wp1->lat, wp1->lon) * SG_RADIANS_TO_DEGREES;
660                                 // Adjust for map orientation
661                                 h -= _mapHeading;
662                                 qvec.push_back(GetLabelQuadrant(h));
663                                 //cout << "i = " << i << ", h = " << h << ", qvec[0] = " << qvec[0] << '\n';
664                         }
665                         xvec.push_back(mx1);
666                         yvec.push_back(my1);
667                         if(drawTrack) { DrawLine(mx0, my0, mx1, my1); }
668                         if(i != 1) {
669                                 double h = GetGreatCircleCourse(wp0->lat, wp0->lon, wp1->lat, wp1->lon) * SG_RADIANS_TO_DEGREES;
670                                 // Adjust for map orientation
671                                 h -= _mapHeading;
672                                 qvec.push_back(GetLabelQuadrant(save_h, h));
673                         }
674                         save_h = GetGreatCircleCourse(wp1->lat, wp1->lon, wp0->lat, wp0->lon) * SG_RADIANS_TO_DEGREES;
675                         // Adjust for map orientation
676                         save_h -= _mapHeading;
677                         if(i == _activeFP->waypoints.size() - 1) {
678                                 qvec.push_back(GetLabelQuadrant(save_h));
679                         }
680                         // Don't draw flight track beyond the missed approach point of an approach
681                         if(_approachLoaded) {
682                                 //cout << "Waypoints are " << wp0->id << " and " << wp1->id << '\n';
683                                 //cout << "Types are " << wp0->appType << " and " << wp1->appType << '\n';
684                                 if(wp1->appType == GPS_MAP) {
685                                         drawTrack = false;
686                                 }
687                         }
688                 }
689                 // ASSERT(xvec.size() == yvec.size() == qvec.size() == _activeFP->waypoints.size());
690                 for(unsigned int i=0; i<xvec.size(); ++i) {
691                         DrawWaypoint(xvec[i], yvec[i]);
692                         bool right_align = (qvec[i] > 2);
693                         bool top = (qvec[i] == 1 || qvec[i] == 4);
694                         // TODO - not sure if labels should be drawn in sequence with waypoints and flightpaths,
695                         // or all before or all afterwards.  Doesn't matter a huge deal though.
696                         DrawLabel(_activeFP->waypoints[i]->id, xvec[i] + (right_align ? -2 : 3), yvec[i] + (top ? 3 : -7), right_align);
697                 }
698         }
699         
700         // User pos
701         if(_mapOrientation == 0) {
702                 // North up
703                 DrawUser1(56, 19);
704         } else if(_mapOrientation == 1) {
705                 // DTK up
706                 DrawUser1(56, 10);
707         } else if(_mapOrientation == 2) {
708                 // TK up
709                 DrawUser2(56, 10);
710         } else {
711                 // Heading up
712                 // TODO - don't know what to do here!
713         }
714         
715         // And finally, reset the clip region to stop the rest of the code going pear-shaped!
716         _instrument->ResetClipRegion();
717 }
718
719 // Get the quadrant to draw the label of the start or end waypoint (i.e. one with only one track from it).
720 // Heading specified FROM the waypoint.
721 // 4 | 1
722 // -----
723 // 3 | 2
724 int KLN89::GetLabelQuadrant(double h) {
725         while(h < 0.0) h += 360.0;
726         while(h > 360.0) h -= 360.0;
727         if(h < 90.0) return(3);
728         if(h < 180.0) return(4);
729         if(h < 270.0) return(1);
730         return(2);
731 }
732
733 // Get the quadrant to draw the label of an en-route waypoint,
734 // with BOTH tracks specified as headings FROM the waypoint.
735 // 4 | 1
736 // -----
737 // 3 | 2
738 int KLN89::GetLabelQuadrant(double h1, double h2) {
739         while(h1 < 0.0) h1 += 360.0;
740         while(h1 > 360.0) h1 -= 360.0;
741         while(h2 < 0.0) h2 += 360.0;
742         while(h2 > 360.0) h2 -= 360.0;
743         double max_min_diff = 0.0;
744         int quad;
745         for(int i=0; i<4; ++i) {
746                 double h = 45 + (90 * i);
747                 double diff1 = fabs(h - h1);
748                 if(diff1 > 180) diff1 = 360 - diff1;
749                 double diff2 = fabs(h - h2);
750                 if(diff2 > 180) diff2 = 360 - diff2;
751                 double min_diff = gps_min(diff1, diff2);
752                 if(min_diff > max_min_diff) {
753                         max_min_diff = min_diff;
754                         quad = i + 1;
755                 }
756         }
757         //cout << "GetLabelQuadrant, h1 = " << h1 << ", h2 = " << h2 << ", quad = " << quad << '\n';
758         return(quad);
759 }
760
761 // Draw the diamond style of user pos
762 // 
763 //    o
764 //   oxo
765 //  oxxxo
766 // oxxxxxo
767 //  oxxxo
768 //   oxo
769 //    o
770 // 
771 void KLN89::DrawUser1(int x, int y) {
772         MapToInstrument(x, y);
773         int min_j = 0, max_j = 0;
774         for(int i=-3; i<=3; ++i) {
775                 for(int j=min_j; j<=max_j; ++j) {
776                         _instrument->DrawPixel(x+j, y+i, (j == min_j || j == max_j ? true : false));
777                 }
778                 if(i < 0) {
779                         min_j--;
780                         max_j++;
781                 } else {
782                         min_j++;
783                         max_j--;
784                 }
785         }
786 }
787
788 // Draw the airplane style of user pos
789 // Define the origin to be the midpoint of the *fuselage*
790 void KLN89::DrawUser2(int x, int y) {
791         MapToInstrument(x, y);
792         
793         // Draw the background as three black quads first
794         _instrument->DrawQuad(x-2, y-3, x+2, y-1, true);
795         _instrument->DrawQuad(x-3, y, x+3, y+2, true);
796         _instrument->DrawQuad(x-1, y+3, x+1, y+3, true);
797         
798         if(_pixelated) {
799                 for(int j=y-2; j<=y+2; ++j) {
800                         _instrument->DrawPixel(x, j);
801                 }
802                 for(int i=x-1; i<=x+1; ++i) {
803                         _instrument->DrawPixel(i, y-2);
804                 }
805                 for(int i=x-2; i<=x+2; ++i) {
806                         _instrument->DrawPixel(i, y+1);
807                 }
808         } else {
809                 _instrument->DrawQuad(x, y-2, x, y+2);
810                 _instrument->DrawQuad(x-1, y-2, x+1, y-2);
811                 _instrument->DrawQuad(x-2, y+1, x+2, y+1);
812         }
813 }
814
815 // Draw an airport symbol on the moving map
816 //
817 //  ooo
818 // ooxoo
819 // oxxxo
820 // ooxoo
821 //  ooo
822 //
823 void KLN89::DrawApt(int x, int y) {
824         MapToInstrument(x, y);
825         
826         int j = y-2;
827         int i;
828         for(i=x-1; i<=x+1; ++i) _instrument->DrawPixel(i, j, true);
829         ++j;
830         for(i=x-2; i<=x+2; ++i) _instrument->DrawPixel(i, j, (i != x ? true : false));
831         ++j;
832         for(i=x-2; i<=x+2; ++i) _instrument->DrawPixel(i, j, (abs(i - x) > 1 ? true : false));
833         ++j;
834         for(i=x-2; i<=x+2; ++i) _instrument->DrawPixel(i, j, (i != x ? true : false));
835         ++j;
836         for(i=x-1; i<=x+1; ++i) _instrument->DrawPixel(i, j, true);
837 }
838
839 // Draw a waypoint on the moving map
840 //
841 // ooooo
842 // oxxxo
843 // oxxxo
844 // oxxxo
845 // ooooo
846 //
847 void KLN89::DrawWaypoint(int x, int y) {
848         MapToInstrument(x, y);
849         _instrument->SetDebugging(true);
850         
851         // Draw black background
852         _instrument->DrawQuad(x-2, y-2, x+2, y+2, true);
853         
854         // Draw the coloured square
855         if(_pixelated) {
856                 for(int i=x-1; i<=x+1; ++i) {
857                         for(int j=y-1; j<=y+1; ++j) {
858                                 _instrument->DrawPixel(i, j);
859                         }
860                 }
861         } else {
862                 _instrument->DrawQuad(x-1, y-1, x+1, y+1);
863         }
864         _instrument->SetDebugging(false);
865 }
866
867 // Draw a VOR on the moving map
868 //
869 // ooooo
870 // oxxxo
871 // oxoxo
872 // oxxxo
873 // ooooo
874 //
875 void KLN89::DrawVOR(int x, int y) {
876         // Cheat - draw a waypoint and then a black pixel in the middle.
877         // Need to call Waypoint draw *before* translating co-ords.
878         DrawWaypoint(x, y);
879         MapToInstrument(x, y);
880         _instrument->DrawPixel(x, y, true);
881 }
882
883 // Draw a line on the moving map
884 void KLN89::DrawLine(int x1, int y1, int x2, int y2) {
885         MapToInstrument(x1, y1);
886         MapToInstrument(x2, y2);
887         _instrument->DrawLine(x1, y1, x2, y2);
888 }
889
890 void KLN89::DrawMapUpArrow(int x, int y) {
891         MapToInstrument(x, y);
892         if(_pixelated) {
893                 for(int j=0; j<7; ++j) {
894                         _instrument->DrawPixel(x + 2, y + j);
895                 }
896         } else {
897                 _instrument->DrawQuad(x+2, y, x+2, y+6);
898         }
899         _instrument->DrawPixel(x, y+4);
900         _instrument->DrawPixel(x+1, y+5);
901         _instrument->DrawPixel(x+3, y+5);
902         _instrument->DrawPixel(x+4, y+4);
903 }
904
905 // Draw a quad on the moving map
906 void KLN89::DrawMapQuad(int x1, int y1, int x2, int y2, bool invert) {
907         MapToInstrument(x1, y1);
908         MapToInstrument(x2, y2);
909         _instrument->DrawQuad(x1, y1, x2, y2, invert);
910 }
911
912 // Draw an airport or waypoint label on the moving map
913 // Specify position by the map pixel co-ordinate of the left or right, bottom, of the *visible* portion of the label.
914 // The black background quad will automatically overlap this by 1 pixel.
915 void KLN89::DrawLabel(const string& s, int x1, int y1, bool right_align) {
916         MapToInstrument(x1, y1);
917         if(!right_align) {
918                 for(unsigned int i=0; i<s.size(); ++i) {
919                         char c = s[i];
920                         x1 += DrawSmallChar(c, x1, y1);
921                         x1 ++;
922                 }
923         } else {
924                 for(int i=(int)(s.size()-1); i>=0; --i) {
925                         char c = s[i];
926                         x1 -= DrawSmallChar(c, x1, y1, right_align);
927                         x1--;
928                 }
929         }
930 }
931
932 void KLN89::DrawCDI() {
933         // Scale
934         for(int i=0; i<5; ++i) {
935                 DrawSpecialChar(2, 2, 3+i, 2);
936                 DrawSpecialChar(1, 2, 9+i, 2);
937         }
938         
939         int field = 2;
940         int px = 8 * 7 + _xBorder + _xFieldBorder[field] + _xFieldStart[field] + 2;
941         int py = 2 * 9 + _yBorder + _yFieldBorder[field] + _yFieldStart[field];
942         
943         // Deflection bar
944         // Every 7 pixels deflection left or right is one dot on the scale, and hence 1/5 FSD.
945         // Maximum deflection is 37 pixels left, or 38 pixels right !?!
946         double xtd = CalcCrossTrackDeviation();
947         int deflect;
948         if(_cdiScaleTransition) {
949                 double dots = (xtd / _currentCdiScale) * 5.0;
950                 deflect = (int)(dots * 7.0 * -1.0);
951                 // TODO - for all these I think I should add 0.5 before casting to int, and *then* multiply by -1.  Possibly!
952         } else {
953                 if(0 == _currentCdiScaleIndex) {        // 5.0nm FSD => 1 nm per dot => 7 pixels per nm.
954                         deflect = (int)(xtd * 7.0 * -1.0);      // The -1.0 is because we move the 'needle' indicating the course, not the plane.
955                 } else if(1 == _currentCdiScaleIndex) {
956                         deflect = (int)(xtd * 35.0 * -1.0);
957                 } else {        // 0.3 == _cdiScale
958                         deflect = (int)(xtd * 116.6666666667 * -1.0);
959                 }
960         }
961         if(deflect > 38) deflect = 38;
962         if(deflect < -37) deflect = -37;
963         if(_pixelated) {
964                 for(int j=0; j<9; ++j) {
965                         _instrument->DrawPixel(px + deflect, py+j);
966                         _instrument->DrawPixel(px + deflect + 1, py+j);
967                 }
968         } else {
969                 _instrument->DrawQuad(px + deflect, py, px + deflect + 1, py + 8);
970         }
971         
972         // To/From indicator
973         px-=4;
974         py+=2;
975         for(int j=4; j>=0; --j) {
976                 int k = 10 - (2*j);
977                 for(int i=0; i<k; ++i) {                
978                         _instrument->DrawPixel(px+j+i, (_headingBugTo ? py+j : py+4-j));
979                         // At the extremities, draw the outlining dark pixel
980                         if(i == 0 || i == k-1) {
981                                 _instrument->DrawPixel(px+j+i, (_headingBugTo ? py+j+1 : py+3-j), true);
982                         }
983                 }
984         }
985 }
986
987 void KLN89::DrawLegTail(int py) {
988         int px = 0 * 7 + _xBorder + _xFieldBorder[2] + _xFieldStart[2];
989         py = py * 9 + _yBorder + _yFieldBorder[2] + _yFieldStart[2];
990         
991         px++;
992         py+=3;
993         py++;   // Hack - not sure if this represents a border issue.
994         
995         for(int i=0; i<9; ++i) _instrument->DrawPixel(px, py+i);
996         for(int i2=0; i2<5; ++i2) _instrument->DrawPixel(px+i2, py+9);
997 }
998
999 void KLN89::DrawLongLegTail(int py) {
1000         int px = 0 * 7 + _xBorder + _xFieldBorder[2] + _xFieldStart[2];
1001         py = py * 9 + _yBorder + _yFieldBorder[2] + _yFieldStart[2];
1002         
1003         px++;
1004         py+=3;
1005         py++;   // Hack - not sure if this represents a border issue.
1006         
1007         for(int i=0; i<18; ++i) _instrument->DrawPixel(px, py+i);
1008         for(int i2=0; i2<5; ++i2) _instrument->DrawPixel(px+i2, py+18);
1009 }
1010
1011 void KLN89::DrawHalfLegTail(int py) {
1012 }
1013
1014 void KLN89::DrawDivider() {
1015         int px = _xFieldStart[2] - 1;
1016         int py = _yBorder;
1017         for(int i=0; i<36; ++i) {
1018                 _instrument->DrawPixel(px, py+i);
1019         }
1020 }
1021
1022 void KLN89::DrawEnt(int field, int px, int py) {
1023         px = px * 7 + _xBorder + _xFieldBorder[field] + _xFieldStart[field];
1024         py = py * 9 + _yBorder + _yFieldBorder[field] + _yFieldStart[field] + 1;
1025         
1026         px++;   // Not sure why we need px++, but it seems to work!
1027         py++;
1028         
1029         // E
1030         for(int i=0; i<5; ++i) _instrument->DrawPixel(px, py+i);
1031         _instrument->DrawPixel(px+1, py);
1032         _instrument->DrawPixel(px+2, py);
1033         _instrument->DrawPixel(px+1, py+2);
1034         _instrument->DrawPixel(px+1, py+4);
1035         _instrument->DrawPixel(px+2, py+4);
1036         
1037         px += 4;
1038         // N
1039         for(int i=0; i<4; ++i) _instrument->DrawPixel(px, py+i);
1040         _instrument->DrawPixel(px+1, py+2);
1041         _instrument->DrawPixel(px+2, py+1);
1042         for(int i=0; i<4; ++i) _instrument->DrawPixel(px+3, py+i);
1043         
1044         px += 5;
1045         // T
1046         _instrument->DrawPixel(px, py+3);
1047         for(int i=0; i<4; ++i) _instrument->DrawPixel(px+1, py+i);
1048         _instrument->DrawPixel(px+2, py+3);
1049 }
1050
1051 void KLN89::DrawMessageAlert() {
1052         // TODO - draw the proper message indicator
1053         if(!_blink) {
1054                 int px = _xBorder + _xFieldBorder[1] + _xFieldStart[1];
1055                 int py = 1 * 9 + _yBorder + _yFieldBorder[1] + _yFieldStart[1] + 1;
1056                 
1057                 px++;   // Not sure why we need px++, but it seems to work!
1058                 py++;
1059
1060                 DrawText("  ", 1, 0, 1, false, 99);
1061                 _instrument->DrawQuad(px+1, py-1, px+2, py+5, true);
1062                 _instrument->DrawQuad(px+3, py+3, px+3, py+5, true);
1063                 _instrument->DrawQuad(px+4, py+2, px+4, py+4, true);
1064                 _instrument->DrawQuad(px+5, py+1, px+6, py+3, true);
1065                 _instrument->DrawQuad(px+7, py+2, px+7, py+4, true);
1066                 _instrument->DrawQuad(px+8, py+3, px+8, py+5, true);
1067                 _instrument->DrawQuad(px+9, py-1, px+10, py+5, true);
1068         }
1069 }
1070
1071 void KLN89::Underline(int field, int px, int py, int len) {
1072         px = px * 7 + _xBorder + _xFieldBorder[field] + _xFieldStart[field];
1073         py = py * 9 + _yBorder + _yFieldBorder[field] + _yFieldStart[field];
1074         for(int i=0; i<(len*7); ++i) {
1075                 _instrument->DrawPixel(px, py);
1076                 ++px;
1077         }
1078 }
1079
1080 void KLN89::DrawKPH(int field, int cx, int cy) {
1081         // Add some border
1082         int px = cx * 7 + _xBorder + _xFieldBorder[field] + _xFieldStart[field];
1083         int py = cy * 9 + _yBorder + _yFieldBorder[field] + _yFieldStart[field];
1084         
1085         px++;
1086         py++;
1087         
1088         for(int j=0; j<=4; ++j) {
1089                 _instrument->DrawPixel(px, py + 2 +j);
1090                 _instrument->DrawPixel(px + 8, py + j);
1091                 if(j <= 1) {
1092                         _instrument->DrawPixel(px + 11, py + j);
1093                         _instrument->DrawPixel(px + 9 + j, py + 2);
1094                 }
1095         }
1096         
1097         for(int i=0; i<=6; ++i) {
1098                 if(i <= 2) {
1099                         _instrument->DrawPixel(px + 1 + i, py + 4 + i);
1100                         _instrument->DrawPixel(px + 1 + i, py + (4 - i));
1101                 }
1102                 _instrument->DrawPixel(px + 2 + i, py + i);
1103         }
1104 }
1105
1106 void KLN89::DrawDTO(int field, int cx, int cy) {
1107         DrawSpecialChar(6, field, cx, cy);
1108         if(!(_waypointAlert && _blink)) {
1109                 DrawSpecialChar(3, field, cx+1, cy);
1110         }
1111         
1112         int px = cx * 7 + _xBorder + _xFieldBorder[field] + _xFieldStart[field];
1113         int py = cy * 9 + _yBorder + _yFieldBorder[field] + _yFieldStart[field];
1114         
1115         px++;
1116         py++;
1117         
1118         // Fill in the gap between the 'D' and the arrow.
1119         _instrument->DrawPixel(px+5, py+3);
1120 }
1121
1122 // Takes character position
1123 void KLN89::DrawChar(char c, int field, int px, int py, bool bold, bool invert) {
1124         // Ignore field for now
1125         // Add some border
1126         px = px * 7 + _xBorder + _xFieldBorder[field] + _xFieldStart[field];
1127         py = py * 9 + _yBorder + _yFieldBorder[field] + _yFieldStart[field];
1128         
1129         // Draw an orange background for inverted characters
1130         if(invert) {
1131                 for(int i=0; i<7; ++i) {
1132                         for(int j=0; j<9; ++j) {
1133                                 _instrument->DrawPixel(px + i, py + j);
1134                         }
1135                 }
1136         }
1137                                 
1138         if(c < 33) return;  // space
1139         
1140         // Render normal decimal points in bold floats
1141         if(c == '.') bold = false;
1142         
1143         ++py;   // Shift the char up by one pixel
1144         for(int j=7; j>=0; --j) {
1145                 char c1 = (bold ? NumbersBold[c-48][j] : UpperAlpha[c-33][j]);
1146                 // Don't do the last column for now (ie. j = 1, not 0)
1147                 for(int i=5; i>=0; --i) {
1148                         if(c1 & (01 << i)) {
1149                                 _instrument->DrawPixel(px, py, invert);
1150                         }
1151                         ++px;
1152                 }
1153                 px -= 6;
1154                 ++py;
1155         }
1156 }
1157
1158 // Takes pixel position
1159 void KLN89::DrawFreeChar(char c, int x, int y, bool draw_background) {
1160         
1161         if(draw_background) {
1162                 _instrument->DrawQuad(x, y, x+6, y+8, true);
1163         }               
1164                                 
1165         if(c < 33) return;  // space
1166         
1167         ++y;    // Shift the char up by one pixel
1168         for(int j=7; j>=0; --j) {
1169                 char c1 = UpperAlpha[c-33][j];
1170                 // Don't do the last column for now (ie. j = 1, not 0)
1171                 for(int i=5; i>=0; --i) {
1172                         if(c1 & (01 << i)) {
1173                                 _instrument->DrawPixel(x, y);
1174                         }
1175                         ++x;
1176                 }
1177                 x -= 6;
1178                 ++y;
1179         }
1180 }
1181
1182 // Takes instrument pixel co-ordinates.
1183 // Position is specified by the bottom of the *visible* portion, by default the left position unless align_right is true.
1184 // The return value is the pixel width of the visible portion
1185 int KLN89::DrawSmallChar(char c, int x, int y, bool align_right) {
1186         // calculate the index into the SmallChar array
1187         int idx;
1188         if(c > 47 && c < 58) {
1189                 // number
1190                 idx = c - 48;
1191         } else if(c > 64 && c < 91) {
1192                 // Uppercase letter
1193                 idx = c - 55;
1194         } else {
1195                 return(0);
1196         }
1197         
1198         char n = SmallChar[idx][0];             // Width of visible portion
1199         if(align_right) x -= n;
1200         
1201         // background
1202         _instrument->DrawQuad(x - 1, y - 1, x + n, y + 5, true);
1203         
1204         for(int j=7; j>=3; --j) {
1205                 char c1 = SmallChar[idx][j];
1206                 for(int i=n-1; i>=0; --i) {
1207                         if(c1 & (01 << i)) {
1208                                 _instrument->DrawPixel(x, y);
1209                         }
1210                         ++x;
1211                 }
1212                 x -= n;
1213                 ++y;
1214         }
1215         
1216         return(n);
1217 }
1218
1219 // Takes character position
1220 void KLN89::DrawSpecialChar(char c, int field, int cx, int cy, bool bold) {
1221         if(c > 7) {
1222                 cout << "ERROR - requested special char outside array bounds!\n";
1223                 return;  // Increment this as SpecialChar grows
1224         }
1225         
1226         // Convert character to pixel position.
1227         // Ignore field for now
1228         // Add some border
1229         int px = cx * 7 + _xBorder + _xFieldBorder[field] + _xFieldStart[field];
1230         int py = cy * 9 + _yBorder + _yFieldBorder[field] + _yFieldStart[field];
1231         ++py;   // Total hack - the special chars were coming out 1 pixel too low!
1232         for(int i=7; i>=0; --i) {
1233                 char c1 = SpecialChar[(int)c][i];
1234                 // Don't do the last column for now (ie. j = 1, not 0)
1235                 for(int j=5; j>=0; --j) {
1236                         if(c1 & (01 << j)) {
1237                                 _instrument->DrawPixel(px, py);
1238                         }
1239                         ++px;
1240                 }
1241                 px -= 6;
1242                 ++py;
1243         }
1244 }
1245
1246 void KLN89::DrawText(const string& s, int field, int px, int py, bool bold, int invert) {
1247         for(int i = 0; i < (int)s.size(); ++i) {
1248                 DrawChar(s[(unsigned int)i], field, px+i, py, bold, (invert == i || invert == 99));
1249         }
1250 }
1251
1252 void KLN89::DrawMapText(const string& s, int x, int y, bool draw_background) {
1253         MapToInstrument(x, y);
1254         if(draw_background) {
1255                 //_instrument->DrawQuad(x, y, x + (7 * s.size()) - 1, y + 8, true);
1256                 _instrument->DrawQuad(x - 1, y, x + (7 * s.size()) - 2, y + 8, true);
1257                 // The minus 1 and minus 2 are an ugly hack to disguise the fact that I've lost track of exactly what's going on!
1258         }
1259         
1260         for(int i = 0; i < (int)s.size(); ++i) {
1261                 DrawFreeChar(s[(unsigned int)i], x+(i * 7)-1, y);
1262         }
1263 }
1264
1265 void KLN89::DrawLatitude(double d, int field, int px, int py) {
1266         DrawChar((d >= 0 ? 'N' : 'S'), field, px, py);
1267         d = fabs(d);
1268         px += 1;
1269         // TODO - sanity check input to ensure major lat field can only ever by 2 chars wide
1270         char buf[8];
1271         // Don't know whether to zero pad the below for single digits or not?
1272         //cout << d << ", " << (int)d << '\n';
1273         // 3 not 2 in size before for trailing \0
1274         int n = snprintf(buf, 3, "%i", (int)d);
1275         string s = buf;
1276         //cout << s << "... " << n << '\n';
1277         DrawText(s, field, px+(3-n), py);
1278         n = snprintf(buf, 7, "%05.2f'", ((double)(d - (int)d)) * 60.0f);
1279         s = buf;
1280         px += 3;
1281         DrawSpecialChar(0, field, px, py);      // Degrees symbol
1282         px++;
1283         DrawText(s, field, px, py);
1284 }
1285
1286 void KLN89::DrawLongitude(double d, int field, int px, int py) {
1287         DrawChar((d >= 0 ? 'E' : 'W'), field, px, py);
1288         d = fabs(d);
1289         px += 1;
1290         // TODO - sanity check input to ensure major lat field can only ever be 2 chars wide
1291         char buf[8];
1292         // Don't know whether to zero pad the below for single digits or not?
1293         //cout << d << ", " << (int)d << '\n';
1294         // 4 not 3 in size before for trailing \0
1295         int n = snprintf(buf, 4, "%i", (int)d);
1296         string s = buf;
1297         //cout << s << "... " << n << '\n';
1298         DrawText(s, field, px+(3-n), py);
1299         n = snprintf(buf, 7, "%05.2f'", ((double)(d - (int)d)) * 60.0f);
1300         s = buf;
1301         px += 3;
1302         DrawSpecialChar(0, field, px, py);      // Degrees symbol
1303         px++;
1304         DrawText(s, field, px, py);
1305 }
1306
1307 void KLN89::DrawFreq(double d, int field, int px, int py) {
1308         if(d >= 1000) d /= 100.0f;
1309         char buf[8];
1310         snprintf(buf, 7, "%6.2f", d);
1311         string s = buf;
1312         DrawText(s, field, px, py);
1313 }
1314
1315 void KLN89::DrawTime(double time, int field, int px, int py) {
1316         int hrs = (int)(time / 3600);
1317         int mins = (int)(ceil((time - (hrs * 3600)) / 60.0));
1318         char buf[10];
1319         int n;
1320         if(time >= 60.0) {
1321                 // Draw hr:min
1322                 n = snprintf(buf, 9, "%i:%02i", hrs, mins);
1323         } else {
1324                 // Draw :secs
1325                 n = snprintf(buf, 4, ":%02i", (int)time);
1326         }
1327         string s = buf;
1328         DrawText(s, field, px - n + 1, py);
1329 }
1330
1331 void KLN89::DrawHeading(int h, int field, int px, int py) {
1332         char buf[4];
1333         snprintf(buf, 4, "%i", h);
1334         string s = buf;
1335         DrawText(s, field, px - s.size(), py);
1336         DrawSpecialChar(0, field, px, py);      // Degrees symbol
1337 }
1338
1339 void KLN89::DrawDist(double d, int field, int px, int py) {
1340         d *= (_distUnits == GPS_DIST_UNITS_NM ? 1.0 : SG_NM_TO_METER * 0.001);
1341         char buf[10];
1342         snprintf(buf, 9, "%i", (int)(d + 0.5));
1343         string s = buf;
1344         s += (_distUnits == GPS_DIST_UNITS_NM ? "nm" : "Km");
1345         DrawText(s, field, px - s.size() + 1, py);
1346 }
1347
1348 void KLN89::DrawSpeed(double v, int field, int px, int py, int decimal) {
1349         // TODO - implement variable decimal places
1350         v *= (_velUnits == GPS_VEL_UNITS_KT ? 1.0 : 0.51444444444 * 0.001 * 3600.0);
1351         char buf[10];
1352         snprintf(buf, 9, "%i", (int)(v + 0.5));
1353         string s = buf;
1354         if(_velUnits == GPS_VEL_UNITS_KT) {
1355                 s += "kt";
1356                 DrawText(s, field, px - s.size() + 1, py);
1357         } else {
1358                 DrawText(s, field, px - s.size() - 1, py);
1359                 DrawKPH(field, px - 1, py);
1360         }
1361 }
1362
1363 void KLN89::DrawDirDistField(double lat, double lon, int field, int px, int py, bool to_flag, bool cursel) {
1364         DrawChar('>', field, px, py);
1365         char buf[8];
1366         double h;
1367         if(to_flag) {
1368                 h = GetMagHeadingFromTo(_gpsLat, _gpsLon, lat, lon);
1369         } else {
1370                 h = GetMagHeadingFromTo(lat, lon, _gpsLat, _gpsLon);
1371         }
1372         while(h < 0.0) h += 360.0;
1373         while(h > 360.0) h -= 360.0;
1374         snprintf(buf, 4, "%3i", (int)(h + 0.5));
1375         string s = buf;
1376         if(!(cursel && _blink)) { 
1377                 DrawText(s, field, px + 4 - s.size(), py);
1378                 DrawSpecialChar(0, field, px+4, py);
1379                 DrawText((to_flag ? "To" : "Fr"), field, px+5, py);
1380                 if(cursel) Underline(field, px + 1, py, 6);
1381         }
1382         //double d = GetHorizontalSeparation(_gpsLat, _gpsLon, lat, lon);
1383         //d *= (_distUnits == GPS_DIST_UNITS_NM ? SG_METER_TO_NM : 0.001);
1384         double d = GetGreatCircleDistance(_gpsLat, _gpsLon, lat, lon);
1385         d *= (_distUnits == GPS_DIST_UNITS_NM ? 1.0 : SG_NM_TO_METER * 0.001);
1386         if(d >= 100.0) {
1387                 snprintf(buf, 7, "%5i", (int)(d + 0.5));
1388         } else {
1389                 snprintf(buf, 7, "%4.1f", d);
1390         }
1391         s = buf;
1392         DrawText(s, field, px + 12 - s.size(), py);
1393         DrawText((_distUnits == GPS_DIST_UNITS_NM ? "nm" : "Km"), field, px + 12, py);
1394 }
1395
1396 char KLN89::IncChar(char c, bool gap, bool wrap) {
1397         if(c == '9') return(wrap ? (gap ? ' ' : 'A') : '9');
1398         if(c == 'Z') return('0');
1399         if(c == ' ') return('A');
1400         return(c + 1);
1401 }
1402
1403 char KLN89::DecChar(char c, bool gap, bool wrap) {
1404         if(c == 'A') return(wrap ? (gap ? ' ' : '9') : 'A');
1405         if(c == '0') return('Z');
1406         if(c == ' ') return('9');
1407         return(c - 1);
1408 }