]> git.mxchange.org Git - flightgear.git/blob - src/ATC/approach.cxx
Mathias Fröhlich:
[flightgear.git] / src / ATC / approach.cxx
1 // FGApproach - a class to provide approach control at larger airports.
2 //
3 // Written by Alexander Kappes, started March 2002.
4 //
5 // Copyright (C) 2002  Alexander Kappes
6 //
7 // This program is free software; you can redistribute it and/or
8 // modify it under the terms of the GNU General Public License as
9 // published by the Free Software Foundation; either version 2 of the
10 // License, or (at your option) any later version.
11 //
12 // This program is distributed in the hope that it will be useful, but
13 // WITHOUT ANY WARRANTY; without even the implied warranty of
14 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15 // General Public License for more details.
16 //
17 // You should have received a copy of the GNU General Public License
18 // along with this program; if not, write to the Free Software
19 // Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
20
21 #include "approach.hxx"
22 #include "transmission.hxx"
23 #include "transmissionlist.hxx"
24 #include "ATCdisplay.hxx"
25 #include "ATCDialog.hxx"
26
27 #include <Airports/runways.hxx>
28 #include <simgear/math/polar3d.hxx>
29 #include <simgear/misc/sg_path.hxx>
30
31 #include <Environment/environment_mgr.hxx>
32 #include <Environment/environment.hxx>
33
34
35 #include <GUI/gui.h>
36
37 //Constructor
38 FGApproach::FGApproach(){
39   comm1_node = fgGetNode("/instrumentation/comm[0]/frequencies/selected-mhz", true);
40   comm2_node = fgGetNode("/instrumentation/comm[1]/frequencies/selected-mhz", true);
41   
42   _type = APPROACH;
43
44   num_planes = 0;
45   lon_node   = fgGetNode("/position/longitude-deg", true);
46   lat_node   = fgGetNode("/position/latitude-deg", true);
47   elev_node  = fgGetNode("/position/altitude-ft", true);
48   hdg_node   = fgGetNode("/orientation/heading-deg", true);
49   speed_node = fgGetNode("/velocities/airspeed-kt", true);
50   etime_node = fgGetNode("/sim/time/elapsed-sec", true);
51
52   first = true;
53   active_runway = "";
54   int i;
55   for ( i=0; i<max_planes; i++) {
56     planes[i].contact   = 0;
57     planes[i].wpn       = 0;
58     planes[i].dnwp      = -999.;
59     planes[i].on_crs    = true;
60     planes[i].turn_rate = 10.0;
61     planes[i].desc_rate = 1000.0;
62     planes[i].clmb_rate = 500.0;
63     planes[i].tlm       = 0.0;
64     planes[i].lmc.c1    = 0;
65     planes[i].lmc.c2    = 0;
66     planes[i].lmc.c3    = -1;
67     planes[i].wp_change = false;
68   }
69 }
70
71 //Destructor
72 FGApproach::~FGApproach(){
73 }
74
75 void FGApproach::Init() {
76 }
77
78
79
80 // ============================================================================
81 // the main update function
82 // ============================================================================
83 void FGApproach::Update(double dt) {
84         
85         const int max_trans = 20;
86         FGTransmission tmissions[max_trans];
87         int    wpn;
88         atc_type station = APPROACH;
89         TransCode code;
90         TransPar TPar;
91         int    i,j;
92         //double course, d, 
93         double adif, datp;
94         //char   buf[10];
95         string message;
96         //static string atcmsg1[10];
97         //static string atcmsg2[10];
98         string mentry;
99         string transm;
100         TransPar tpars;
101         //static bool TransDisplayed = false;
102         
103         update_plane_dat();
104         if ( active_runway == "" ) get_active_runway();
105         
106         double comm1_freq = comm1_node->getDoubleValue();
107         
108         //bool DisplayTransmissions = true;
109         
110         for (i=0; i<num_planes; i++) {
111                 if ( planes[i].ident == "Player") { 
112                         station = APPROACH;
113                         tpars.station = name;
114                         tpars.callsign = "Player";
115                         tpars.airport = ident;
116                         
117                         //cout << "ident = " << ident << " name = " << name << '\n';
118                         
119                         int num_trans = 0;
120                         // is the frequency of the station tuned in?
121                         if ( freq == (int)(comm1_freq*100.0 + 0.5) ) {
122                                 current_transmissionlist->query_station( station, tmissions, max_trans, num_trans );
123                                 // loop over all transmissions for station
124                                 for ( j=0; j<=num_trans-1; j++ ) {
125                                         code = tmissions[j].get_code();
126                                         //cout << "code is " << code.c1 << "  " << code.c2 << "  " << code.c3 << '\n';
127                                         // select proper transmissions
128                                         if(code.c3 != 2) {    // DCL - hack to prevent request crossing airspace being displayed since this isn't implemented yet.
129                                             if ( ( code.c2 == -1 && planes[i].lmc.c3 == 0 ) || 
130                                                     ( code.c1 == 0  && code.c2 == planes[i].lmc.c2 ) ) {
131                                                     mentry = current_transmissionlist->gen_text(station, code, tpars, false);
132                                                     transm = current_transmissionlist->gen_text(station, code, tpars, true);
133                                                     // is the transmission already registered?
134                                                     if (!current_atcdialog->trans_reg( ident, transm, APPROACH )) {
135                                                             current_atcdialog->add_entry( ident, transm, mentry, APPROACH, 0 );
136                                                     }
137                                             }
138                                         }
139                                 }
140                         }
141                 }
142         }
143         
144         for ( i=0; i<num_planes; i++ ) {
145                 //cout << "TPar.airport = " << TPar.airport << " TPar.station = " << TPar.station << " TPar.callsign = " << TPar.callsign << '\n';
146                 //if ( planes[i].ident == TPar.callsign && name == TPar.airport && TPar.station == "approach" ) {
147                         //if ( TPar.request && TPar.intention == "landing" && ident == TPar.intid) {
148                         if(planes[i].ident == "Player" && fgGetBool("/sim/atc/opt0")) {
149                                 //cout << "Landing requested\n";
150                                 fgSetBool("/sim/atc/opt0", false);
151                                 planes[i].wpn = 0; 
152                                 // ===========================
153                                 // === calculate waypoints ===
154                                 // ===========================
155                                 calc_wp( i );  
156                                 update_param( i );
157                                 wpn = planes[i].wpn-1;
158                                 planes[i].aalt = planes[i].wpts[wpn-1][2];
159                                 planes[i].ahdg = planes[i].wpts[wpn][4];
160                                 
161                                 // generate the message
162                                 code.c1 = 1;
163                                 code.c2 = 1;
164                                 code.c3 = 0;
165                                 adif = angle_diff_deg( planes[i].hdg, planes[i].ahdg );
166                                 tpars.station = name;
167                                 tpars.callsign = "Player";
168                                 if ( adif < 0 ) tpars.tdir = 1;
169                                 else            tpars.tdir = 2;
170                                 tpars.heading = planes[i].ahdg;
171                                 if      (planes[i].alt-planes[i].aalt > 100.0)  tpars.VDir = 1;
172                                 else if (planes[i].alt-planes[i].aalt < -100.0) tpars.VDir = 3;
173                                 else tpars.VDir = 2;
174                                 tpars.alt = planes[i].aalt;
175                                 message = current_transmissionlist->gen_text(station, code, tpars, true );
176                                 //cout << message << '\n';
177                                 globals->get_ATC_display()->RegisterSingleMessage( message, 0 );
178                                 planes[i].lmc = code;
179                                 planes[i].tlm = etime_node->getDoubleValue();
180                                 planes[i].on_crs = true;
181                                 planes[i].contact = 1;
182                         }
183                 //}
184                 
185                 //if(1) {
186                 if ( planes[i].contact == 1 ) {
187                         // =========================
188                         // === update parameters ===
189                         // =========================
190                         update_param( i );
191                         //cout << planes[i].brg << " " << planes[i].dist << " " << planes[i].wpts[wpn+1][0] 
192                         //<< " " << planes[i].wpts[wpn+1][1] << " " << planes[i].wpts[wpn+1][4] 
193                         //cout << wpn << " distance to current course = " << planes[i].dcc << endl;
194                         //cout << etime_node->getDoubleValue() << endl;
195                         
196                         // =========================
197                         // === reached waypoint? ===
198                         // =========================
199                         wpn = planes[i].wpn-2;
200                         adif = angle_diff_deg( planes[i].hdg, planes[i].wpts[wpn][4] ) 
201                         * SGD_DEGREES_TO_RADIANS;
202                         datp = 2*sin(fabs(adif)/2.0)*sin(fabs(adif)/2.0) *
203                                planes[i].spd/3600. * planes[i].turn_rate + 
204                                planes[i].spd/3600. * 3.0;
205                         //cout << adif/SGD_DEGREES_TO_RADIANS << " " 
206                         //     << datp << " " << planes[i].dnc << " " << planes[i].dcc <<endl;
207                         if ( fabs(planes[i].dnc) < datp ) {
208                         //if ( fabs(planes[i].dnc) < 0.3 && planes[i].dnwp < 1.0 ) {
209                                 //cout << "Reached next waypoint!\n";
210                                 planes[i].wpn -= 1;
211                                 wpn = planes[i].wpn-1;
212                                 planes[i].ahdg = planes[i].wpts[wpn][4];
213                                 planes[i].aalt = planes[i].wpts[wpn-1][2];
214                                 planes[i].wp_change = true;
215                                 
216                                 // generate the message
217                                 adif = angle_diff_deg( planes[i].hdg, planes[i].ahdg );
218                                 tpars.station = name;
219                                 tpars.callsign = "Player";
220                                 if ( adif < 0 ) tpars.tdir = 1;
221                                 else            tpars.tdir = 2;
222                                 tpars.heading = planes[i].ahdg;
223                                 
224                                 if ( wpn-1 != 0) { 
225                                         code.c1 = 1;
226                                         code.c2 = 1;
227                                         code.c3 = 0;
228                                         if      (planes[i].alt-planes[i].aalt > 100.0)  tpars.VDir = 1;
229                                         else if (planes[i].alt-planes[i].aalt < -100.0) tpars.VDir = 3;
230                                         else tpars.VDir = 2;
231                                         tpars.alt = planes[i].aalt;
232                                         message = current_transmissionlist->gen_text(station, code, tpars, true );
233                                         //cout << "Approach transmitting...\n";
234                                         //cout << message << endl;
235                                         globals->get_ATC_display()->RegisterSingleMessage( message, 0 );
236                                         
237                                 }
238                                 else {
239                                         code.c1 = 1;
240                                         code.c2 = 3;
241                                         code.c3 = 0;
242                                         tpars.runway = active_runway;
243                                         message = current_transmissionlist->gen_text(station, code, tpars, true);
244                                         //cout << "Approach transmitting 2 ...\n";
245                                         //cout << message << endl;
246                                         globals->get_ATC_display()->RegisterSingleMessage( message, 0 );
247                                 }
248                                 planes[i].lmc = code;
249                                 planes[i].tlm = etime_node->getDoubleValue();
250                                 planes[i].on_crs = true;
251                                 
252                                 update_param( i );
253                         }
254                         
255                         // =========================
256                         // === come off course ? ===
257                         // =========================
258                         if ( fabs(planes[i].dcc) > 1.0 && 
259                            ( !planes[i].wp_change || etime_node->getDoubleValue() - planes[i].tlm > tbm ) ) {
260                                 //cout << "Off course!\n";
261                                 if ( planes[i].on_crs ) {
262                                         if ( planes[i].dcc < 0) {
263                                                 planes[i].ahdg += 30.0;
264                                         }
265                                         else {
266                                                 planes[i].ahdg -= 30.0;
267                                         }
268                                         if (planes[i].ahdg > 360.0) planes[i].ahdg -= 360.0;
269                                         else if (planes[i].ahdg < 0.0) planes[i].ahdg += 360.0;
270                                 }
271                                 //cout << planes[i].on_crs << " " 
272                                 //     << angle_diff_deg( planes[i].hdg, planes[i].ahdg) << " "
273                                 //     << etime_node->getDoubleValue() << " "
274                                 //     << planes[i].tlm << endl;
275                                 // generate the message
276                                 if ( planes[i].on_crs || 
277                                    ( fabs(angle_diff_deg( planes[i].hdg, planes[i].ahdg )) >  30.0  && 
278                                     etime_node->getDoubleValue() - planes[i].tlm > tbm) ) {
279                                         // generate the message
280                                         code.c1 = 1;
281                                         code.c2 = 4;
282                                         code.c3 = 0;
283                                         adif = angle_diff_deg( planes[i].hdg, planes[i].ahdg );
284                                         tpars.station = name;
285                                         tpars.callsign = "Player";
286                                         tpars.miles   = fabs(planes[i].dcc);
287                                         if ( adif < 0 ) tpars.tdir = 1;
288                                         else            tpars.tdir = 2;
289                                         tpars.heading = planes[i].ahdg;
290                                         message = current_transmissionlist->gen_text(station, code, tpars, true);
291                                         //cout << "Approach transmitting 3 ...\n";
292                                         //cout << message << '\n';
293                                         globals->get_ATC_display()->RegisterSingleMessage( message, 0 );
294                                         planes[i].lmc = code;
295                                         planes[i].tlm = etime_node->getDoubleValue();
296                                 }
297                                 
298                                 planes[i].on_crs = false;
299                         }
300                         else if ( !planes[i].on_crs ) {
301                                 //cout << "Off course 2!\n";
302                                 wpn = planes[i].wpn-1;
303                                 adif = angle_diff_deg( planes[i].hdg, planes[i].wpts[wpn][4] ) 
304                                        * SGD_DEGREES_TO_RADIANS;
305                                 datp = 2*sin(fabs(adif)/2.0)*sin(fabs(adif)/2.0) *
306                                 planes[i].spd/3600. * planes[i].turn_rate + 
307                                 planes[i].spd/3600. * 3.0;
308                                 if ( fabs(planes[i].dcc) < datp ) { 
309                                         planes[i].ahdg = fabs(planes[i].wpts[wpn][4]);
310                                         
311                                         // generate the message
312                                         code.c1 = 1;
313                                         code.c2 = 2;
314                                         code.c3 = 0;
315                                         tpars.station = name;
316                                         tpars.callsign = "Player";
317                                         if ( adif < 0 ) tpars.tdir = 1;
318                                         else            tpars.tdir = 2;
319                                         tpars.heading = planes[i].ahdg;
320                                         message = current_transmissionlist->gen_text(station, code, tpars, true);
321                                         //cout << "Approach transmitting 4 ...\n";
322                                         //cout << message << '\n';
323                                         globals->get_ATC_display()->RegisterSingleMessage( message, 0 );
324                                         planes[i].lmc = code;
325                                         planes[i].tlm = etime_node->getDoubleValue();
326                                         
327                                         planes[i].on_crs = true;          
328                                 } 
329                         }
330                         else if ( planes[i].wp_change  ) {
331                                 planes[i].wp_change = false;
332                         }
333                         
334                         // ===================================================================
335                         // === Less than two minutes away from touchdown? -> Contact Tower ===
336                         // ===================================================================
337                         if ( planes[i].wpn == 2 && planes[i].dnwp < planes[i].spd/60.*2.0 ) {
338                                 
339                                 double freq = 121.95;   // Hardwired - FIXME
340                                 // generate message
341                                 code.c1 = 1;
342                                 code.c2 = 5;
343                                 code.c3 = 0;
344                                 tpars.station = name;
345                                 tpars.callsign = "Player";
346                                 tpars.freq    = freq;
347                                 message = current_transmissionlist->gen_text(station, code, tpars, true);
348                                 //cout << "Approach transmitting 5 ...\n";
349                                 //cout << message << '\n';
350                                 globals->get_ATC_display()->RegisterSingleMessage( message, 0 );
351                                 planes[i].lmc = code;
352                                 planes[i].tlm = etime_node->getDoubleValue();
353                                 
354                                 planes[i].contact = 2;
355                         }
356                 }
357         }
358 }
359
360
361 // ============================================================================
362 // update course parameters
363 // ============================================================================
364 void FGApproach::update_param( const int &i ) {
365   
366   double course, d;
367
368   int wpn = planes[i].wpn-1;            // this is the current waypoint
369
370   planes[i].dcc  = calc_psl_dist(planes[i].brg, planes[i].dist,
371                                  planes[i].wpts[wpn][0], planes[i].wpts[wpn][1],
372                                  planes[i].wpts[wpn][4]);
373   planes[i].dnc  = calc_psl_dist(planes[i].brg, planes[i].dist,
374                                  planes[i].wpts[wpn-1][0], planes[i].wpts[wpn-1][1],
375                                  planes[i].wpts[wpn-1][4]);
376   calc_hd_course_dist(planes[i].brg, planes[i].dist, 
377                       planes[i].wpts[wpn-1][0], planes[i].wpts[wpn-1][1],
378                       &course, &d);
379   planes[i].dnwp = d;
380
381 }
382
383 // ============================================================================
384 // smallest difference between two angles in degree
385 // difference is negative if a1 > a2 and positive if a2 > a1
386 // ===========================================================================
387 double FGApproach::angle_diff_deg( const double &a1, const double &a2) {
388   
389   double a3 = a2 - a1;
390   if (a3 < 180.0) a3 += 360.0;
391   if (a3 > 180.0) a3 -= 360.0;
392
393   return a3;
394 }
395
396 // ============================================================================
397 // calculate waypoints
398 // ============================================================================
399 void FGApproach::calc_wp( const int &i ) {
400         
401         int j;
402         double course, d, cd, a1;
403         
404         int wpn = planes[i].wpn;
405         // waypoint 0: Threshold of active runway
406         calc_gc_course_dist(Point3D(lon*SGD_DEGREES_TO_RADIANS, lat*SGD_DEGREES_TO_RADIANS, 0.0),
407                             Point3D(active_rw_lon*SGD_DEGREES_TO_RADIANS,active_rw_lat*SGD_DEGREES_TO_RADIANS, 0.0 ),
408                             &course, &d);
409         double d1 = active_rw_hdg+180.0;
410         if ( d1 > 360.0 ) d1 -=360.0;
411         calc_cd_head_dist(360.0-course*SGD_RADIANS_TO_DEGREES, d/SG_NM_TO_METER, 
412                           d1, active_rw_len/SG_NM_TO_METER/2.0, 
413                           &planes[i].wpts[wpn][0], &planes[i].wpts[wpn][1]);
414         planes[i].wpts[wpn][2] = elev;
415         planes[i].wpts[wpn][4] = 0.0;
416         planes[i].wpts[wpn][5] = 0.0;
417         wpn += 1;
418         
419         // ======================
420         // horizontal navigation
421         // ======================
422         // waypoint 1: point for turning onto final
423         calc_cd_head_dist(planes[i].wpts[wpn-1][0], planes[i].wpts[wpn-1][1], d1, lfl,
424                           &planes[i].wpts[wpn][0], &planes[i].wpts[wpn][1]);
425         calc_hd_course_dist(planes[i].wpts[wpn][0],   planes[i].wpts[wpn][1],
426                             planes[i].wpts[wpn-1][0], planes[i].wpts[wpn-1][1],
427                             &course, &d);
428         planes[i].wpts[wpn][4] = course;
429         planes[i].wpts[wpn][5] = d;
430         wpn += 1;
431         
432         // calculate course and distance from plane position to waypoint 1
433         calc_hd_course_dist(planes[i].brg, planes[i].dist, planes[i].wpts[1][0], 
434                             planes[i].wpts[1][1], &course, &d);
435         // check if airport is not between plane and waypoint 1 and
436         // DCA to airport on course to waypoint 1 is larger than 10 miles
437         double zero = 0.0;
438         if ( fabs(angle_diff_deg( planes[i].wpts[1][0], planes[i].brg  )) < 90.0 ||
439                 calc_psl_dist( zero, zero, planes[i].brg, planes[i].dist, course ) > 10.0 ) {
440                 // check if turning angle at waypoint 1 would be > max_ta
441                 if ( fabs(angle_diff_deg( planes[i].wpts[1][4], course )) > max_ta ) {
442                         cd = calc_psl_dist(planes[i].brg, planes[i].dist,
443                              planes[i].wpts[1][0], planes[i].wpts[1][1],
444                              planes[i].wpts[1][4]);
445                         a1 = atan2(cd,planes[i].wpts[1][1]);
446                         planes[i].wpts[wpn][0] = planes[i].wpts[1][0] - a1/SGD_DEGREES_TO_RADIANS;
447                         if ( planes[i].wpts[wpn][0] < 0.0)   planes[i].wpts[wpn][0] += 360.0;   
448                         if ( planes[i].wpts[wpn][0] > 360.0) planes[i].wpts[wpn][0] -= 360.0;   
449                         planes[i].wpts[wpn][1] = fabs(cd) / sin(fabs(a1));
450                         calc_hd_course_dist(planes[i].wpts[wpn][0],   planes[i].wpts[wpn][1],
451                                             planes[i].wpts[wpn-1][0], planes[i].wpts[wpn-1][1],
452                                             &course, &d);
453                         planes[i].wpts[wpn][4] = course;
454                         planes[i].wpts[wpn][5] = d;
455                         wpn += 1;
456                         
457                         calc_hd_course_dist(planes[i].brg, planes[i].dist, planes[i].wpts[wpn-1][0], 
458                                             planes[i].wpts[wpn-1][1], &course, &d);
459                 }
460         } else {
461                 double leg = 10.0;
462                 a1 = atan2(planes[i].wpts[1][1], leg );
463                 
464                 if ( angle_diff_deg(planes[i].brg,planes[i].wpts[1][0]) < 0 ) 
465                         planes[i].wpts[wpn][0] = planes[i].wpts[1][0] + a1/SGD_DEGREES_TO_RADIANS;
466                 else planes[i].wpts[wpn][0] = planes[i].wpts[1][0] - a1/SGD_DEGREES_TO_RADIANS;
467                 
468                 planes[i].wpts[wpn][1] = sqrt( planes[i].wpts[1][1]*planes[i].wpts[1][1] + leg*leg );
469                 calc_hd_course_dist(planes[i].wpts[wpn][0],   planes[i].wpts[wpn][1],
470                                     planes[i].wpts[wpn-1][0], planes[i].wpts[wpn-1][1],
471                                     &course, &d);
472                 planes[i].wpts[wpn][4] = course;
473                 planes[i].wpts[wpn][5] = d;
474                 wpn += 1;
475                 
476                 calc_hd_course_dist(planes[i].brg, planes[i].dist,
477                                     planes[i].wpts[wpn-1][0], planes[i].wpts[wpn-1][1],
478                                     &course, &d);
479         }
480         
481         planes[i].wpts[wpn][0] = planes[i].brg;
482         planes[i].wpts[wpn][1] = planes[i].dist;
483         planes[i].wpts[wpn][2] = planes[i].alt;
484         planes[i].wpts[wpn][4] = course;
485         planes[i].wpts[wpn][5] = d;
486         wpn += 1;
487         
488         planes[i].wpn = wpn;
489         
490         // Now check if legs are too short or if legs can be shortend
491         // legs must be at least 2 flight minutes long
492         double mdist = planes[i].spd / 60.0 * 2.0;
493         for ( j=2; j<wpn-1; ++j ) {
494                 if ( planes[i].wpts[j][1] < mdist) {
495                 }
496         }
497         
498         // ====================
499         // vertical navigation
500         // ====================
501         double alt = elev+3000.0;
502         planes[i].wpts[1][2] = round_alt( true, alt );
503         for ( j=2; j<wpn-1; ++j ) {
504                 double dalt = planes[i].alt - planes[i].wpts[j-1][2];
505                 if ( dalt > 0 ) {
506                         alt = planes[i].wpts[j-1][2] + 
507                               (planes[i].wpts[j][5] / planes[i].spd) * 60.0 * planes[i].desc_rate;
508                         planes[i].wpts[j][2] = round_alt( false, alt );
509                         if ( planes[i].wpts[j][2] > planes[i].alt ) 
510                                 planes[i].wpts[j][2] = round_alt( false, planes[i].alt );
511                 }
512                 else {
513                         planes[i].wpts[j][2] = planes[i].wpts[1][2];
514                 }
515         }
516         
517         cout << "Plane position: " << planes[i].brg << " " << planes[i].dist << endl;
518         for ( j=0; j<wpn; ++j ) {
519                 cout << "Waypoint " << j << endl;
520                 cout << "------------------" << endl;
521                 cout << planes[i].wpts[j][0] << "   " << planes[i].wpts[j][1]
522                      << "   " << planes[i].wpts[j][2] << "   " << planes[i].wpts[j][5]; 
523                 cout << endl << endl;
524         }
525         
526 }
527
528
529 // ============================================================================
530 // round altitude value to next highest/lowest 500 feet
531 // ============================================================================
532 double FGApproach::round_alt( const bool hl, double alt ) {
533
534   alt = alt/1000.0;
535   if ( hl ) {
536     if ( alt > (int)(alt)+0.5 ) alt = ((int)(alt)+1)*1000.0;
537     else alt = ((int)(alt)+0.5)*1000.0;
538   }
539   else {
540     if ( alt > (int)(alt)+0.5 ) alt = ((int)(alt)+0.5)*1000.0;
541     else alt = ((int)(alt))*1000.0;
542   }
543   
544   return alt;
545 }
546
547
548 // ============================================================================
549 // get active runway
550 // ============================================================================
551 void FGApproach::get_active_runway() {
552         //cout << "Entering FGApproach::get_active_runway()\n";
553
554   FGEnvironment stationweather =
555       ((FGEnvironmentMgr *)globals->get_subsystem("environment"))
556         ->getEnvironment(lat, lon, elev);
557
558   double hdg = stationweather.get_wind_from_heading_deg();
559   
560   FGRunway runway;
561   if ( globals->get_runways()->search( ident, int(hdg), &runway) ) {
562     active_runway = runway._rwy_no;
563     active_rw_hdg = runway._heading;
564     active_rw_lon = runway._lon;
565     active_rw_lat = runway._lat;
566     active_rw_len = runway._length;
567     //cout << "Active runway is: " << active_runway << "  heading = " 
568     // << active_rw_hdg 
569     // << " lon = " << active_rw_lon 
570     // << " lat = " << active_rw_lat <<endl;
571   }
572   else cout << "FGRunways search failed\n";
573
574 }
575
576 // ========================================================================
577 // update infos about plane
578 // ========================================================================
579 void FGApproach::update_plane_dat() {
580   
581   //cout << "Update Approach " << ident << "   " << num_planes << " registered" << endl;
582   // update plane positions
583   int i;
584   for (i=0; i<num_planes; i++) {
585     planes[i].lon = lon_node->getDoubleValue();
586     planes[i].lat = lat_node->getDoubleValue();
587     planes[i].alt = elev_node->getDoubleValue();
588     planes[i].hdg = hdg_node->getDoubleValue();
589     planes[i].spd = speed_node->getDoubleValue();
590
591     /*Point3D aircraft = sgGeodToCart( Point3D(planes[i].lon*SGD_DEGREES_TO_RADIANS, 
592                                              planes[i].lat*SGD_DEGREES_TO_RADIANS, 
593                                              planes[i].alt*SG_FEET_TO_METER) );*/
594     double course, distance;
595     calc_gc_course_dist(Point3D(lon*SGD_DEGREES_TO_RADIANS, lat*SGD_DEGREES_TO_RADIANS, 0.0),
596                         Point3D(planes[i].lon*SGD_DEGREES_TO_RADIANS,planes[i].lat*SGD_DEGREES_TO_RADIANS, 0.0 ),
597                         &course, &distance);
598     planes[i].dist = distance/SG_NM_TO_METER;
599     planes[i].brg  = 360.0-course*SGD_RADIANS_TO_DEGREES;
600
601     //cout << "Plane Id: " << planes[i].ident << "  Distance to " << ident 
602     // << " is " << planes[i].dist << " miles   " << "Bearing " << planes[i].brg << endl;
603     
604   }   
605 }
606
607 // =======================================================================
608 // Add plane to Approach list
609 // =======================================================================
610 void FGApproach::AddPlane(string pid) {
611
612   int i;
613   for ( i=0; i<num_planes; i++) {
614     if ( planes[i].ident == pid) {
615       //cout << "Plane already registered: " << planes[i].ident << ' ' << ident << ' ' << num_planes << endl;
616       return;
617     }
618   }
619   planes[num_planes].ident = pid;
620   ++num_planes;
621   //cout << "Plane added to list: " << ident << " " << num_planes << endl;
622   return;
623 }
624
625 // ================================================================================
626 // closest distance between a point (h1,d1) and a straigt line (h2,d2,h3) in 2 dim.
627 // ================================================================================
628 double FGApproach::calc_psl_dist(const double &h1, const double &d1,
629                                  const double &h2, const double &d2,
630                                  const double &h3)
631 {
632   double a1 = h1 * SGD_DEGREES_TO_RADIANS;
633   double a2 = h2 * SGD_DEGREES_TO_RADIANS;
634   double a3 = h3 * SGD_DEGREES_TO_RADIANS;
635   double x1 = cos(a1) * d1;
636   double y1 = sin(a1) * d1;
637   double x2 = cos(a2) * d2;
638   double y2 = sin(a2) * d2;
639   double x3 = cos(a3);
640   double y3 = sin(a3);
641   
642   // formula: dis = sqrt( (v1-v2)**2 - ((v1-v2)*v3)**2 ); vi = (xi,yi)
643   double val1   = (x1-x2)*(x1-x2) + (y1-y2)*(y1-y2);
644   double val2   = ((x1-x2)*x3 + (y1-y2)*y3) * ((x1-x2)*x3 + (y1-y2)*y3);
645   double dis    = val1 - val2;
646   // now get sign for offset 
647   //cout << x1 << " " << x2 << " " << y1 << " " << y2 << " " 
648   //     << x3 << " " << y3 << " " 
649   //     << val1 << " " << val2 << " " << dis << endl;
650   x3 *= sqrt(val2);
651   y3 *= sqrt(val2);
652   double da = fabs(atan2(y3,x3) - atan2(y1-y2,x1-x2));
653   if ( da > SGD_PI ) da -= 2*SGD_PI;
654   if ( fabs(da) > SGD_PI/2.) {
655     //if ( x3*(x1-x2) < 0.0 && y3*(y1-y2) < 0.0) {
656     x3 *= -1.0;
657     y3 *= -1.0;
658   }
659   //cout << x3 << " " << y3 << endl;
660   double dis1   = x1-x2-x3;
661   double dis2   = y1-y2-y3;
662   dis = sqrt(dis);
663   da = atan2(dis2,dis1);
664   if ( da < 0.0 ) da  += 2*SGD_PI;
665   if ( da < a3 )  dis *= -1.0;
666   //cout << dis1 << " " << dis2 << " " << da*SGD_RADIANS_TO_DEGREES << " " << h3
667   //     << " " << sqrt(dis1*dis1 + dis2*dis2) << " " << dis << endl;
668   //cout << atan2(dis2,dis1)*SGD_RADIANS_TO_DEGREES << " " << dis << endl;
669
670   return dis;
671 }
672
673
674 // ========================================================================
675 // Calculate new bear/dist given starting bear/dis, and offset radial,
676 // and distance.
677 // ========================================================================
678 void FGApproach::calc_cd_head_dist(const double &h1, const double &d1, 
679                                    const double &course, const double &dist,
680                                    double *h2, double *d2)
681 {
682   double a1 = h1 * SGD_DEGREES_TO_RADIANS;
683   double a2 = course * SGD_DEGREES_TO_RADIANS;
684   double x1 = cos(a1) * d1;
685   double y1 = sin(a1) * d1;
686   double x2 = cos(a2) * dist;
687   double y2 = sin(a2) * dist;
688     
689   *d2 = sqrt((x1+x2)*(x1+x2) + (y1+y2)*(y1+y2));
690   *h2 = atan2( (y1+y2), (x1+x2) ) * SGD_RADIANS_TO_DEGREES;
691   if ( *h2 < 0 ) *h2 = *h2+360;
692 }
693
694
695
696 // ========================================================================
697 // get heading and distance between two points; point1 ---> point2
698 // ========================================================================
699 void FGApproach::calc_hd_course_dist(const double &h1, const double &d1, 
700                                      const double &h2, const double &d2,
701                                      double *course, double *dist)
702 {
703   double a1 = h1 * SGD_DEGREES_TO_RADIANS;
704   double a2 = h2 * SGD_DEGREES_TO_RADIANS;
705   double x1 = cos(a1) * d1;
706   double y1 = sin(a1) * d1;
707   double x2 = cos(a2) * d2;
708   double y2 = sin(a2) * d2;
709            
710   *dist   = sqrt( (y2-y1)*(y2-y1) + (x2-x1)*(x2-x1) );
711   *course = atan2( (y2-y1), (x2-x1) ) * SGD_RADIANS_TO_DEGREES;
712   if ( *course < 0 ) *course = *course+360;
713   //cout << x1 << " " << y1 << " " << x2 << " " << y2 << " " << *dist << " " << *course << endl;
714 }
715
716
717
718 int FGApproach::RemovePlane() {
719
720   // first check if anything has to be done
721   bool rmplane = false;
722   int i;
723
724   for (i=0; i<num_planes; i++) {
725     if (planes[i].dist > range*SG_NM_TO_METER) {
726       rmplane = true;
727       break;
728     }
729   }
730   if (!rmplane) return num_planes;
731
732   // now make a copy of the plane list
733   PlaneApp tmp[max_planes];
734   for (i=0; i<num_planes; i++) {
735     tmp[i] = planes[i];
736   }
737   
738   int np = 0;
739   // now check which planes are still in range
740   for (i=0; i<num_planes; i++) {
741     if (tmp[i].dist <= range*SG_NM_TO_METER) {
742       planes[np] = tmp[i];
743       np += 1;
744     }
745   }
746   num_planes = np;
747
748   return num_planes;
749 }