1 // dclgps.hxx - a class to extend the operation of FG's current GPS
2 // code, and provide support for a KLN89-specific instrument. It
3 // is envisioned that eventually this file and class will be split
4 // up between current FG code and new KLN89-specific code and removed.
6 // Written by David Luff, started 2005.
8 // Copyright (C) 2005 - David C Luff - david.luff@nottingham.ac.uk
10 // This program is free software; you can redistribute it and/or
11 // modify it under the terms of the GNU General Public License as
12 // published by the Free Software Foundation; either version 2 of the
13 // License, or (at your option) any later version.
15 // This program is distributed in the hope that it will be useful, but
16 // WITHOUT ANY WARRANTY; without even the implied warranty of
17 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
18 // General Public License for more details.
20 // You should have received a copy of the GNU General Public License
21 // along with this program; if not, write to the Free Software
22 // Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
29 #include "render_area_2d.hxx"
35 #include <Cockpit/panel.hxx>
37 #include <Navaids/navrecord.hxx>
38 #include <Navaids/navlist.hxx>
39 #include <Navaids/fixlist.hxx>
40 #include <Airports/simple.hxx>
41 #include <simgear/structure/subsystem_mgr.hxx>
45 enum GPSDistanceUnits {
46 GPS_DIST_UNITS_NM = 0,
55 enum GPSAltitudeUnits {
60 enum GPSPressureUnits {
61 GPS_PRES_UNITS_IN = 1,
66 // --------------------- Waypoint / Flightplan stuff -----------------------------
67 // This should be merged with other similar stuff in FG at some point.
69 // NOTE - ORDERING IS IMPORTANT HERE - it matches the Bendix-King page ordering!
76 GPS_WP_VIRT // Used for virtual waypoints, such as the start of DTO operation.
80 GPS_IAF, // Initial approach fix
81 GPS_IAP, // Waypoint on approach sequence that isn't any of the others.
82 GPS_FAF, // Final approach fix
83 GPS_MAP, // Missed approach point
84 GPS_MAHP, // Initial missed approach holding point.
85 GPS_HDR, // A virtual 'waypoint' to represent the approach header in the fpl page
86 GPS_FENCE, // A virtual 'waypoint' to represent the NO WPT SEQ fence.
87 GPS_APP_NONE // Not part of the approach sequence - the default.
90 ostream& operator << (ostream& os, GPSAppWpType type);
95 string GetAprId(); // Returns the id with i, f, m or h added if appropriate. (Initial approach fix, final approach fix, etc)
100 GPSAppWpType appType; // only used for waypoints that are part of an approach sequence
103 typedef vector < GPSWaypoint* > gps_waypoint_array;
104 typedef gps_waypoint_array::iterator gps_waypoint_array_iterator;
105 typedef map < string, gps_waypoint_array > gps_waypoint_map;
106 typedef gps_waypoint_map::iterator gps_waypoint_map_iterator;
107 typedef gps_waypoint_map::const_iterator gps_waypoint_map_const_iterator;
109 class GPSFlightPlan {
111 vector<GPSWaypoint*> waypoints;
112 inline bool IsEmpty() { return(waypoints.size() == 0); }
115 // TODO - probably de-public the internals of the next 2 classes and add some methods!
116 // Instrument approach procedure base class
120 virtual ~FGIAP() = 0;
123 string _id; // The ID of the airport this approach is for
124 string _name; // The approach name, eg "VOR/DME OR GPS-B"
125 string _abbrev; // The abbreviation the GPS unit uses - eg "VOR/D" in this instance. Possibly GPS model specific.
126 string _rwyStr; // The string used to specify the rwy - eg "B" in this instance.
127 bool _precision; // True for precision approach, false for non-precision.
130 // Non-precision instrument approach procedure
131 class FGNPIAP : public FGIAP {
137 vector<GPSWaypoint*> _IAF; // The initial approach fix(es)
138 vector<GPSWaypoint*> _IAP; // The compulsory waypoints of the approach procedure (may duplicate one of the above).
139 // _IAP includes the FAF and MAF.
140 vector<GPSWaypoint*> _MAP; // The missed approach procedure (doesn't include the MAF).
143 typedef vector < FGIAP* > iap_list_type;
144 typedef map < string, iap_list_type > iap_map_type;
145 typedef iap_map_type::iterator iap_map_iterator;
147 // ------------------------------------------------------------------------------
154 GPSPage(DCLGPS* parent);
155 virtual ~GPSPage() = 0;
156 virtual void Update(double dt);
157 virtual void Knob1Left1();
158 virtual void Knob1Right1();
159 virtual void Knob2Left1();
160 virtual void Knob2Right1();
161 virtual void CrsrPressed();
162 virtual void EntPressed();
163 virtual void ClrPressed();
164 virtual void DtoPressed();
165 virtual void NrstPressed();
166 virtual void AltPressed();
167 virtual void OBSPressed();
168 virtual void MsgPressed();
170 // Sometimes a page needs to maintain state for some return paths,
171 // but change it for others. The CleanUp function can be used for
172 // changing state for non-ENT return paths in conjunction with
174 virtual void CleanUp();
176 // The LooseFocus function is called when a page or subpage looses focus
177 // and allow pages to clean up state that is maintained whilst focus is
178 // retained, but lost on return.
179 virtual void LooseFocus();
181 // Allows pages that display info for a given ID to have it set/get if they implement these functions.
182 virtual void SetId(const string& s);
183 virtual const string& GetId()=0;
185 inline int GetSubPage() { return(_subPage); }
187 inline int GetNSubPages() { return(_nSubPages); }
189 inline const string& GetName() { return(_name); }
193 string _name; // eg. "APT", "NAV" etc
195 // _subpage is zero based
196 int _subPage; // The subpage gets remembered when other pages are displayed
197 string GPSitoa(int n);
200 /*-----------------------------------------------------------------------*/
202 typedef vector<GPSPage*> gps_page_list_type;
203 typedef gps_page_list_type::iterator gps_page_list_itr;
205 // TODO - merge generic GPS functions instead and split out KLN specific stuff.
206 class DCLGPS : public SGSubsystem, public FGPanelInstrument {
208 friend class GPSPage;
211 DCLGPS(RenderArea2D* instrument);
212 virtual ~DCLGPS() = 0;
218 virtual void unbind();
219 virtual void update(double dt);
221 // Render string s in display field field at position x, y
222 // WHERE POSITION IS IN CHARACTER UNITS!
224 virtual void DrawText(const string& s, int field, int px, int py, bool bold = false);
226 // Render a char at a given position as above
227 virtual void DrawChar(char c, int field, int px, int py, bool bold = false);
229 virtual void Knob1Right1();
230 virtual void Knob1Left1();
231 virtual void Knob2Right1();
232 virtual void Knob2Left1();
233 virtual void CrsrPressed();
234 virtual void EntPressed();
235 virtual void ClrPressed();
236 virtual void DtoPressed();
237 virtual void NrstPressed();
238 virtual void AltPressed();
239 virtual void OBSPressed();
240 virtual void MsgPressed();
242 // Set the number of fields
243 inline void SetNumFields(int n) { _nFields = (n > _maxFields ? _maxFields : (n < 1 ? 1 : n)); }
246 // m if true, ft if false
247 inline void SetAltUnitsSI(bool b) { _altUnits = (b ? GPS_ALT_UNITS_M : GPS_ALT_UNITS_FT); }
248 // Returns true if alt units are SI (m), false if ft
249 inline bool GetAltUnitsSI() { return(_altUnits == GPS_ALT_UNITS_M ? true : false); }
250 // km and k/h if true, nm and kt if false
251 inline void SetDistVelUnitsSI(bool b) { _distUnits = (b ? GPS_DIST_UNITS_KM : GPS_DIST_UNITS_NM); _velUnits = (b ? GPS_VEL_UNITS_KPH : GPS_VEL_UNITS_KT); }
252 // Returns true if dist/vel units are SI
253 inline bool GetDistVelUnitsSI() { return(_distUnits == GPS_DIST_UNITS_KM && _velUnits == GPS_VEL_UNITS_KPH ? true : false); }
254 // Set baro units - 1 = in, 2 = mB, 3 = hP Wrapping if for the convienience of the GPS setter.
255 void SetBaroUnits(int n, bool wrap = false);
256 // Get baro units: 1 = in, 2 = mB, 3 = hP
257 inline int GetBaroUnits() { return((int)_baroUnits); }
259 // It is expected that specific GPS units will override these functions.
260 // Increase the CDI full-scale deflection (ie. increase the nm per dot) one (GPS unit dependent) increment. Wraps if necessary (GPS unit dependent).
261 virtual void CDIFSDIncrease();
262 // Ditto for decrease the distance per dot
263 virtual void CDIFSDDecrease();
266 ////inline void SetOverlays(Overlays* overlays) { _overlays = overlays; }
268 virtual void CreateDefaultFlightPlans();
270 void SetOBSFromWaypoint();
272 inline GPSWaypoint* GetActiveWaypoint() { return &_activeWaypoint; }
273 // Get the (zero-based) position of the active waypoint in the active flightplan
274 // Returns -1 if no active waypoint.
275 int GetActiveWaypointIndex();
276 // Ditto for an arbitrary waypoint id
277 int GetWaypointIndex(const string& id);
280 inline float GetDistToActiveWaypoint() { return _dist2Act; }
281 // Returns degrees (magnetic)
282 float GetHeadingToActiveWaypoint();
283 // Returns degrees (magnetic)
284 float GetHeadingFromActiveWaypoint();
285 // Get the time to the active waypoint in seconds.
286 // Returns -1 if groundspeed < 30 kts
287 double GetTimeToActiveWaypoint();
288 // Get the time to the final waypoint in seconds.
289 // Returns -1 if groundspeed < 30 kts
291 // Get the time to a given waypoint (spec'd by ID) in seconds.
292 // returns -1 if groundspeed is less than 30kts.
293 // If the waypoint is an unreached part of the active flight plan the time will be via each leg.
294 // otherwise it will be a direct-to time.
295 double GetTimeToWaypoint(const string& id);
297 // Return true if waypoint alerting is occuring
298 inline bool GetWaypointAlert() const { return(_waypointAlert); }
299 // Return true if in OBS mode
300 inline bool GetOBSMode() const { return(_obsMode); }
301 // Return true if in Leg mode
302 inline bool GetLegMode() const { return(!_obsMode); }
304 // Clear a flightplan
305 void ClearFlightPlan(int n);
306 void ClearFlightPlan(GPSFlightPlan* fp);
308 // Returns true if an approach is loaded/armed/active in the active flight plan
309 inline bool ApproachLoaded() const { return(_approachLoaded); }
310 inline bool GetApproachArm() const { return(_approachArm); }
311 inline bool GetApproachActive() const { return(_approachActive); }
312 double GetCDIDeflection() const;
313 inline bool GetToFlag() const { return(_headingBugTo); }
315 // Initiate Direct To operation to the supplied ID.
316 void DtoInitiate(const string& id);
317 // Cancel Direct To operation
321 // Maximum number of display fields for this device
323 // Current number of on-screen fields
329 // Lower (y) border per field
330 int _yFieldBorder[4];
331 // Left (x) border per field
332 int _xFieldBorder[4];
333 // Field start in x dir (border is part of field since it is the normal char border - sometimes map mode etc draws in it)
335 // Field start in y dir (for completeness - KLN89 only has vertical divider.
338 // The number of pages on the cyclic knob control
339 unsigned int _nPages;
340 // The current page we're on (Not sure how this ties in with extra pages such as direct or nearest).
341 unsigned int _curPage;
344 RenderArea2D* _instrument;
347 gps_page_list_type _pages;
349 // The currently active page
350 GPSPage* _activePage;
351 // And a facility to save the immediately preceeding active page
352 GPSPage* _lastActivePage;
355 GPSSpeedUnits _velUnits;
356 GPSDistanceUnits _distUnits;
357 GPSPressureUnits _baroUnits;
358 GPSAltitudeUnits _altUnits;
360 // CDI full-scale deflection, specified either as an index into a vector of values (standard values) or as a double precision float (intermediate values).
361 // This will influence how an externally driven CDI will display as well as the NAV1 page.
362 // Hence the variables are located here, not in the nav page class.
363 vector<float> _cdiScales;
364 unsigned int _currentCdiScaleIndex;
365 bool _cdiScaleTransition; // Set true when the floating CDI value is used during transitions
366 double _currentCdiScale; // The floating value to use.
367 unsigned int _targetCdiScaleIndex; // The target indexed value to attain during a transition.
368 unsigned int _sourceCdiScaleIndex; // The source indexed value during a transition - so we know which way we're heading!
369 // Timers to handle the transitions - not sure if we need these.
370 double _apprArmTimer;
371 double _apprActvTimer;
372 double _cdiTransitionTime; // Time for transition to occur in - normally 30sec but may be quicker if time to FAF < 30sec?
375 // Data and lookup functions
376 // All waypoints mapped by id.
377 gps_waypoint_map _waypoints;
379 // Worker function for the below.
380 const GPSWaypoint* ActualFindFirstById(const string& id, bool exact = false);
382 // Find first of any type of waypoint by id. (TODO - Possibly we should return multiple waypoints here).
383 const GPSWaypoint* FindFirstById(const string& id, bool &multi, bool exact = false);
384 FGNavRecord* FindFirstVorById(const string& id, bool &multi, bool exact = false);
385 FGNavRecord* FindFirstNDBById(const string& id, bool &multi, bool exact = false);
386 const FGAirport* FindFirstAptById(const string& id, bool &multi, bool exact = false);
387 const FGFix* FindFirstIntById(const string& id, bool &multi, bool exact = false);
388 // Find the closest VOR to a position in RADIANS.
389 FGNavRecord* FindClosestVor(double lat_rad, double lon_rad);
391 // Position, orientation and velocity.
392 // These should be read from FG's built-in GPS logic if possible.
393 // Use the property node pointers below to do this.
394 SGPropertyNode_ptr _lon_node;
395 SGPropertyNode_ptr _lat_node;
396 SGPropertyNode_ptr _alt_node;
397 SGPropertyNode_ptr _grnd_speed_node;
398 SGPropertyNode_ptr _true_track_node;
399 SGPropertyNode_ptr _mag_track_node;
400 // Present position. (Radians)
402 // Present altitude (ft). (Yuk! but it saves converting ft->m->ft every update).
404 // Reported position as measured by GPS. For now this is the same
405 // as present position, but in the future we might want to model
406 // GPS lat and lon errors.
407 // Note - we can depriciate _gpsLat and _gpsLon if we implement error handling in FG
408 // gps code and not our own.
409 double _gpsLat, _gpsLon; //(Radians)
410 // Hack - it seems that the GPS gets initialised before FG's initial position is properly set.
411 // By checking for abnormal slew in the position we can force a re-initialisation of active flight
412 // plan leg and anything else that might be affected.
413 // TODO - sort FlightGear's initialisation order properly!!!
414 double _checkLat, _checkLon; // (Radians)
415 double _groundSpeed_ms; // filtered groundspeed (m/s)
416 double _groundSpeed_kts; // ditto in knots
417 double _track; // filtered true track (degrees)
418 double _magTrackDeg; // magnetic track in degrees calculated from true track above
420 // _navFlagged is set true when GPS navigation is either not possible or not logical.
421 // This includes not receiving adequate signals, and not having an active flightplan entered.
424 // Positional functions copied from ATCutils that might get replaced
425 // INPUT in RADIANS, returns DEGREES!
427 double GetMagHeadingFromTo(double latA, double lonA, double latB, double lonB);
429 //double GetHeadingFromTo(double latA, double lonA, double latB, double lonB);
431 // Given two positions (lat & lon in RADIANS), get the HORIZONTAL separation (in meters)
432 //double GetHorizontalSeparation(double lat1, double lon1, double lat2, double lon2);
434 // Proper great circle positional functions from The Aviation Formulary
435 // Returns distance in Nm, input in RADIANS.
436 double GetGreatCircleDistance(double lat1, double lon1, double lat2, double lon2) const;
438 // Input in RADIANS, output in DEGREES.
440 double GetGreatCircleCourse(double lat1, double lon1, double lat2, double lon2) const;
442 // Return a position on a radial from wp1 given distance d (nm) and magnetic heading h (degrees)
443 // Note that d should be less that 1/4 Earth diameter!
444 GPSWaypoint GetPositionOnMagRadial(const GPSWaypoint& wp1, double d, double h);
446 // Return a position on a radial from wp1 given distance d (nm) and TRUE heading h (degrees)
447 // Note that d should be less that 1/4 Earth diameter!
448 GPSWaypoint GetPositionOnRadial(const GPSWaypoint& wp1, double d, double h);
450 // Calculate the current cross-track deviation in nm.
451 // Returns zero if a sensible value cannot be calculated.
452 double CalcCrossTrackDeviation() const;
454 // Calculate the cross-track deviation between 2 arbitrary waypoints in nm.
455 // Returns zero if a sensible value cannot be calculated.
456 double CalcCrossTrackDeviation(const GPSWaypoint& wp1, const GPSWaypoint& wp2) const;
459 // GPS can have up to _maxFlightPlans flightplans stored, PLUS an active FP which may or my not be one of the stored ones.
460 // This is from KLN89, but is probably not far off the mark for most if not all GPS.
461 vector<GPSFlightPlan*> _flightPlans;
462 unsigned int _maxFlightPlans;
463 GPSFlightPlan* _activeFP;
465 // Modes of operation.
466 // This is currently somewhat Bendix-King specific, but probably applies fundamentally to other units as well
467 // Mode defaults to leg, but is OBS if _obsMode is true.
469 // _dto is set true for DTO operation
471 // In leg mode, we need to know if we are displaying a from and to waypoint, or just the to waypoint (eg. when OBS mode is cancelled).
473 // In OBS mode we need to know the set OBS heading
476 // Operational variables
477 GPSWaypoint _activeWaypoint;
478 GPSWaypoint _fromWaypoint;
480 float _crosstrackDist; // UNITS ??????????
481 double _eta; // ETA in SECONDS to active waypoint.
482 // Desired track for active leg, true and magnetic, in degrees
483 double _dtkTrue, _dtkMag;
484 bool _headingBugTo; // Set true when the heading bug is TO, false when FROM.
485 bool _waypointAlert; // Set true when waypoint alerting is happening. (This is a variable NOT a user-setting).
486 bool _departed; // Set when groundspeed first exceeds 30kts.
487 string _departureTimeString; // Ditto.
488 double _elapsedTime; // Elapsed time in seconds since departure
490 // Configuration that affects flightplan operation
491 bool _turnAnticipationEnabled;
493 // Configuration that affects general operation
494 bool _suaAlertEnabled; // Alert user to potential SUA entry
495 bool _altAlertEnabled; // Alert user to min safe alt violation
497 // Magvar stuff. Might get some of this stuff (such as time) from FG in future.
500 list<string> _messageStack;
502 virtual void CreateFlightPlan(GPSFlightPlan* fp, vector<string> ids, vector<GPSWpType> wps);
504 // Orientate the GPS unit to a flightplan - ie. figure out from current position
505 // and possibly orientation which leg of the FP we are on.
506 virtual void OrientateToFlightPlan(GPSFlightPlan* fp);
508 // Ditto for active fp. Probably all we need really!
509 virtual void OrientateToActiveFlightPlan();
511 int _cleanUpPage; // -1 => no cleanup required.
514 iap_map_type _np_iap; // Non-precision approaches
515 iap_map_type _pr_iap; // Precision approaches
516 bool _approachLoaded; // Set true when an approach is loaded in the active flightplan
517 bool _approachArm; // Set true when in approach-arm mode
518 bool _approachReallyArmed; // Apparently, approach-arm mode can be set from an external GPS-APR switch outside 30nm from airport,
519 // but the CDI scale change doesn't happen until 30nm from airport. Bizarre that it can be armed without
520 // the scale change, but it's in the manual...
521 bool _approachActive; // Set true when in approach-active mode
522 GPSFlightPlan* _approachFP; // Current approach - not necessarily loaded.
523 string _approachID; // ID of the airport we have an approach loaded for - bit of a hack that can hopefully be removed in future.
524 // More hackery since we aren't actually storing an approach class... Doh!
525 string _approachAbbrev;
526 string _approachRwyStr;
529 #endif // _DCLGPS_HXX