]> git.mxchange.org Git - flightgear.git/blob - src/Instrumentation/gps.cxx
08183fde95337d3a40424c91a7f8cd0d92ec8469
[flightgear.git] / src / Instrumentation / gps.cxx
1 // gps.cxx - distance-measuring equipment.
2 // Written by David Megginson, started 2003.
3 //
4 // This file is in the Public Domain and comes with no warranty.
5
6 #include <simgear/compiler.h>
7
8 #include <string.h>
9
10 #include STL_STRING
11
12 #include <Aircraft/aircraft.hxx>
13 #include <FDM/flight.hxx>
14 #include <Controls/controls.hxx>
15 #include <Scenery/scenery.hxx>
16
17 #include <simgear/constants.h>
18 #include <simgear/sg_inlines.h>
19 #include <simgear/debug/logstream.hxx>
20 #include <simgear/math/sg_geodesy.hxx>
21 #include <simgear/misc/sg_path.hxx>
22 #include <simgear/route/route.hxx>
23
24 #include <Airports/simple.hxx>
25 #include <GUI/gui.h>
26 #include <Main/fg_init.hxx>
27 #include <Main/globals.hxx>
28 #include <Main/fg_props.hxx>
29 #include <Main/util.hxx>
30 #include <Navaids/fixlist.hxx>
31 #include <Navaids/navlist.hxx>
32
33 #include "gps.hxx"
34
35 SG_USING_STD(string);
36
37
38 GPS::GPS ()
39     : _last_valid(false),
40       _last_longitude_deg(0),
41       _last_latitude_deg(0),
42       _last_altitude_m(0),
43       _last_speed_kts(0)
44 {
45 }
46
47 GPS::~GPS ()
48 {
49 }
50
51 void
52 GPS::init ()
53 {
54     _longitude_node = fgGetNode("/position/longitude-deg", true);
55     _latitude_node = fgGetNode("/position/latitude-deg", true);
56     _altitude_node = fgGetNode("/position/altitude-ft", true);
57     _magvar_node = fgGetNode("/environment/magnetic-variation-deg", true);
58     _serviceable_node = fgGetNode("/instrumentation/gps/serviceable", true);
59     _electrical_node = fgGetNode("/systems/electrical/outputs/gps", true);
60     _wp_longitude_node = 
61         fgGetNode("/instrumentation/gps/wp-longitude-deg", true);
62     _wp_latitude_node =
63         fgGetNode("/instrumentation/gps/wp-latitude-deg", true);
64     _wp_ID_node =
65         fgGetNode("/instrumentation/gps/wp-ID", true);
66     _wp_name_node =
67         fgGetNode("/instrumentation/gps/wp-name", true);
68     _wp_course_node = 
69         fgGetNode("/instrumentation/gps/course-deg", true);
70     _get_nearest_airport_node =
71       fgGetNode("/instrumentation/gps/get-nearest-airport", true);
72     _waypoint_type_node =
73       fgGetNode("/instrumentation/gps/waypoint-type", true);
74
75     _raim_node = fgGetNode("/instrumentation/gps/raim", true);
76     _indicated_longitude_node =
77         fgGetNode("/instrumentation/gps/longitude-deg", true);
78     _indicated_latitude_node =
79         fgGetNode("/instrumentation/gps/latitude-deg", true);
80     _indicated_altitude_node =
81         fgGetNode("/instrumentation/gps/altitude-ft", true);
82     _true_track_node =
83         fgGetNode("/instrumentation/gps/track-true-deg", true);
84     _magnetic_track_node =
85         fgGetNode("/instrumentation/gps/track-magnetic-deg", true);
86     _speed_node =
87         fgGetNode("/instrumentation/gps/ground-speed-kt", true);
88     _wp_distance_node =
89         fgGetNode("/instrumentation/gps/wp-distance-nm", true);
90     _wp_ttw_node =
91         fgGetNode("/instrumentation/gps/TTW",true);
92     _wp_bearing_node =
93         fgGetNode("/instrumentation/gps/wp-bearing-deg", true);
94     _wp_actual_radial_node =
95         fgGetNode("/instrumentation/gps/radials/actual-deg", true);
96     _wp_course_deviation_node =
97         fgGetNode("/instrumentation/gps/course-deviation-deg", true);
98     _wp_course_error_nm_node =
99         fgGetNode("/instrumentation/gps/course-error-nm", true);
100     _wp_to_flag_node =
101         fgGetNode("/instrumentation/gps/to-flag", true);
102     _odometer_node =
103         fgGetNode("/instrumentation/gps/odometer", true);
104     _trip_odometer_node =
105       fgGetNode("/instrumentation/gps/trip-odometer", true);
106 }
107
108 void
109 GPS::update (double delta_time_sec)
110 {
111                                 // If it's off, don't bother.
112     if (!_serviceable_node->getBoolValue() ||
113         !_electrical_node->getBoolValue()) {
114         _last_valid = false;
115         _last_longitude_deg = 0;
116         _last_latitude_deg = 0;
117         _last_altitude_m = 0;
118         _last_speed_kts = 0;
119         _raim_node->setDoubleValue(false);
120         _indicated_longitude_node->setDoubleValue(0);
121         _indicated_latitude_node->setDoubleValue(0);
122         _indicated_altitude_node->setDoubleValue(0);
123         _true_track_node->setDoubleValue(0);
124         _magnetic_track_node->setDoubleValue(0);
125         _speed_node->setDoubleValue(0);
126         _wp_distance_node->setDoubleValue(0);
127         _wp_bearing_node->setDoubleValue(0);
128         _wp_actual_radial_node->setDoubleValue(0);
129         _wp_longitude_node->setDoubleValue(0);
130         _wp_latitude_node->setDoubleValue(0);
131         _wp_course_node->setDoubleValue(0);
132         _odometer_node->setDoubleValue(0);
133         _trip_odometer_node->setDoubleValue(0);
134         return;
135     }
136
137                                 // Get the aircraft position
138     double longitude_deg = _longitude_node->getDoubleValue();
139     double latitude_deg = _latitude_node->getDoubleValue();
140     double altitude_m = _altitude_node->getDoubleValue() * SG_FEET_TO_METER;
141     double magvar_deg = _magvar_node->getDoubleValue();
142
143     double speed_kt;
144
145     _raim_node->setBoolValue(true);
146     _indicated_longitude_node->setDoubleValue(longitude_deg);
147     _indicated_latitude_node->setDoubleValue(latitude_deg);
148     _indicated_altitude_node->setDoubleValue(altitude_m * SG_METER_TO_FEET);
149
150     if (_last_valid) {
151         double track1_deg, track2_deg, distance_m, odometer;
152         geo_inverse_wgs_84(altitude_m,
153                            _last_latitude_deg, _last_longitude_deg,
154                            latitude_deg, longitude_deg,
155                            &track1_deg, &track2_deg, &distance_m);
156         speed_kt = ((distance_m * SG_METER_TO_NM) *
157                     ((1 / delta_time_sec) * 3600.0));
158         _true_track_node->setDoubleValue(track1_deg);
159         _magnetic_track_node->setDoubleValue(track1_deg - magvar_deg);
160         speed_kt = fgGetLowPass(_last_speed_kts, speed_kt, delta_time_sec/20.0);
161         _last_speed_kts = speed_kt;
162         _speed_node->setDoubleValue(speed_kt);
163
164         odometer = _odometer_node->getDoubleValue();
165         _odometer_node->setDoubleValue(odometer + distance_m * SG_METER_TO_NM);
166         odometer = _trip_odometer_node->getDoubleValue();
167         _trip_odometer_node->setDoubleValue(odometer + distance_m * SG_METER_TO_NM);
168
169         // Get waypoint position
170         double wp_longitude_deg = _wp_longitude_node->getDoubleValue();
171         double wp_latitude_deg = _wp_latitude_node->getDoubleValue();
172         double wp_course_deg = 
173           _wp_course_node->getDoubleValue();
174         double wp_distance, wp_bearing_deg, wp_actual_radial_deg,
175           wp_course_deviation_deg, wp_course_error_m, wp_TTW;
176         string wp_ID = _wp_ID_node->getStringValue();
177
178         // If the get-nearest-airport-node is true.
179         // Get the nearest airport, and set it as waypoint.
180         if (_get_nearest_airport_node->getBoolValue()) {
181           FGAirport a;
182           cout << "Airport found" << endl;
183           a = globals->get_airports()->search(longitude_deg, latitude_deg, false);
184           _wp_ID_node->setStringValue(a.id.c_str());
185           wp_longitude_deg = a.longitude;
186           wp_latitude_deg = a.latitude;
187           _wp_name_node->setStringValue(a.name.c_str());
188           _get_nearest_airport_node->setBoolValue(false);
189           _last_wp_ID = wp_ID = a.id.c_str();
190         }
191
192         // If the waypoint ID has changed, try to find the new ID
193         // in the airport-, fix-, nav-database.
194         if ( !(_last_wp_ID == wp_ID) ) {
195           string waypont_type =
196             _waypoint_type_node->getStringValue();
197           if (waypont_type == "airport") {
198             FGAirport a;
199             a = globals->get_airports()->search( wp_ID );
200             if ( a.id == wp_ID ) {
201               cout << "Airport found" << endl;
202               wp_longitude_deg = a.longitude;
203               wp_latitude_deg = a.latitude;
204               _wp_name_node->setStringValue(a.name.c_str());
205             }
206           }
207           else if (waypont_type == "nav") {
208             FGNav * n;
209             if ( (n = current_navlist->findByIdent(wp_ID.c_str(), 
210                                                       longitude_deg, 
211                                                       latitude_deg)) != NULL) {
212               cout << "Nav found" << endl;
213               wp_longitude_deg = n->get_lon();
214               wp_latitude_deg = n->get_lat();
215               _wp_name_node->setStringValue(n->get_name().c_str());
216             }
217           }
218           else if (waypont_type == "fix") {
219             FGFix f;
220             if ( current_fixlist->query(wp_ID, &f) ) {
221               cout << "Fix found" << endl;
222               wp_longitude_deg = f.get_lon();
223               wp_latitude_deg = f.get_lat();
224               _wp_name_node->setStringValue(wp_ID.c_str());
225             }
226           }
227           _last_wp_ID = wp_ID;
228         }
229
230         // Find the bearing and distance to the waypoint.
231         SGWayPoint wp(wp_longitude_deg, wp_latitude_deg, altitude_m);
232         wp.CourseAndDistance(longitude_deg, latitude_deg, altitude_m,
233                             &wp_bearing_deg, &wp_distance);
234         _wp_longitude_node->setDoubleValue(wp_longitude_deg);
235         _wp_latitude_node->setDoubleValue(wp_latitude_deg);
236         _wp_distance_node->setDoubleValue(wp_distance * SG_METER_TO_NM);
237         _wp_bearing_node->setDoubleValue(wp_bearing_deg);
238         
239         // Estimate time to waypoint.
240         // The estimation does not take track into consideration,
241         // so if you are going away from the waypoint the TTW will
242         // increase. Makes most sense when travelling directly towards
243         // the waypoint.
244         if (speed_kt > 0.0 && wp_distance > 0.0) {
245           wp_TTW = (wp_distance * SG_METER_TO_NM) / (speed_kt / 3600);
246         }
247         else {
248           wp_TTW = 0.0;
249         }
250         unsigned int wp_TTW_seconds = (int) (wp_TTW + 0.5);
251         if (wp_TTW_seconds < 356400) { // That's 99 hours
252           unsigned int wp_TTW_minutes = 0;
253           unsigned int wp_TTW_hours   = 0;
254           char wp_TTW_str[8];
255           while (wp_TTW_seconds >= 3600) {
256             wp_TTW_seconds -= 3600;
257             wp_TTW_hours++;
258           }
259           while (wp_TTW_seconds >= 60) {
260             wp_TTW_seconds -= 60;
261             wp_TTW_minutes++;
262           }
263           sprintf(wp_TTW_str, "%02d:%02d:%02d",
264             wp_TTW_hours, wp_TTW_minutes, wp_TTW_seconds);
265           _wp_ttw_node->setStringValue(wp_TTW_str);
266         }
267         else
268           _wp_ttw_node->setStringValue("--:--:--");
269
270         /*
271         // We are on the radial 180 degrees from the bearing to the waypoint.
272         wp_actual_radial_deg = wp_bearing - 180.0;
273         while (wp_actual_radial_deg < 0.0) {
274             wp_actual_radial_deg += 360.0; }
275         while (wp_actual_radial_deg >= 360.0) {
276             wp_actual_radial_deg -= 360.0; }
277         _wp_actual_radial_node->setDoubleValue(wp_actual_radial_deg);
278
279         // We want to be on the radial 180 degrees from the desired course.
280         wp_course_deg -= 180.0;
281         while (wp_course_deg < 0.0) {
282             wp_course_deg += 360.0; }
283         while (wp_course_deg >= 360.0) {
284             wp_course_deg -= 360.0;
285         }
286         */
287
288         // Course deviation is the diffenrence between the bearing
289         // and the course.
290         wp_course_deviation_deg = wp_bearing_deg -
291           wp_course_deg;
292         while (wp_course_deviation_deg < -180.0) {
293           wp_course_deviation_deg += 360.0; }
294         while (wp_course_deviation_deg > 180.0) {
295           wp_course_deviation_deg -= 360.0; }
296
297         // If the course deviation is less than 90 degrees to either side,
298         // our desired course is towards the waypoint.
299         // It does not matter if we are actually moving towards or from the waypoint.
300         if (fabs(wp_course_deviation_deg) < 90.0) {
301             _wp_to_flag_node->setBoolValue(true); }
302         // If it's more than 90 degrees the desired course is from the waypoint.
303         else if (fabs(wp_course_deviation_deg) > 90.0) {
304           _wp_to_flag_node->setBoolValue(false);
305           // When the course is away from the waypoint, 
306           // it makes sense to change the sign of the deviation.
307           wp_course_deviation_deg *= -1.0;
308           while (wp_course_deviation_deg < -90.0)
309             wp_course_deviation_deg += 180.0;
310           while (wp_course_deviation_deg >  90.0)
311             wp_course_deviation_deg -= 180.0; }
312
313         _wp_course_deviation_node->setDoubleValue(wp_course_deviation_deg);
314
315         // Cross track error.
316         wp_course_error_m = sin(wp_course_deviation_deg * SG_PI / 180.0) 
317           * (wp_distance);
318         _wp_course_error_nm_node->setDoubleValue(wp_course_error_m 
319                                                  * SG_METER_TO_NM);
320
321     } else {
322         _true_track_node->setDoubleValue(0.0);
323         _magnetic_track_node->setDoubleValue(0.0);
324         _speed_node->setDoubleValue(0.0);
325     }
326
327     _last_valid = true;
328     _last_longitude_deg = longitude_deg;
329     _last_latitude_deg = latitude_deg;
330     _last_altitude_m = altitude_m;
331 }
332
333 // end of gps.cxx