]> git.mxchange.org Git - flightgear.git/blob - src/ATC/approach.cxx
Whoops - missed a file from the commlist changes yesterday
[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 "ATCdisplay.hxx"
23 #include <Airports/runways.hxx>
24
25 #include <simgear/misc/sg_path.hxx>
26
27 #ifdef FG_WEATHERCM
28 # include <WeatherCM/FGLocalWeatherDatabase.h>
29 #else
30 # include <Environment/environment_mgr.hxx>
31 # include <Environment/environment.hxx>
32 #endif
33
34
35 PlaneApp::PlaneApp()
36 :
37   ident(""),
38   lon(0.0),
39   lat(0.0),
40   alt(0.0),
41   hdg(0.0),
42   dist(0.0),
43   brg(0.0),
44   spd(0.0),
45   contact(0),
46   wpn(0),
47   dnwp(-999.),
48   dcc(0.0),
49   dnc(0.0),
50   aalt(0.0),
51   ahdg(0.0),
52   on_crs(true),
53   tlm(0.0)
54 {
55 }
56
57 //Constructor
58 FGApproach::FGApproach() :
59   bucket(0),
60   active_runway(""),
61   active_rw_hdg(0.0),
62   display(false),
63   displaying(false),
64   num_planes(0),
65   transmission(""),
66   first(true),
67   trans_ident(""),
68   approach_failed(false)
69 {
70   comm1_node = fgGetNode("/radios/comm[0]/frequencies/selected-mhz", true);
71   comm2_node = fgGetNode("/radios/comm[1]/frequencies/selected-mhz", true);
72
73   lon_node = fgGetNode("/position/longitude-deg", true);
74   lat_node = fgGetNode("/position/latitude-deg", true);
75   elev_node = fgGetNode("/position/altitude-ft", true);
76 }
77
78 //Destructor
79 FGApproach::~FGApproach(){
80 }
81
82 void FGApproach::Init() {
83   display    = false;
84 }
85
86 // ============================================================================
87 // the main update function
88 // ============================================================================
89 void FGApproach::Update() {
90
91   int wpn;
92   double course, d;
93
94   update_plane_dat();
95   if ( active_runway == "" ) get_active_runway();
96   
97   for ( int i=0; i<num_planes; i++ ) {
98     
99     if ( planes[i].contact == 0) {
100       double comm1_freq = comm1_node->getDoubleValue();
101       if ( (int)(comm1_freq*100.0 + 0.5) == freq ) planes[i].contact = 1;
102           //cout << "comm1 = " << (int)(comm1_freq*100.0 + 0.5) << " freq = " << freq << '\n';
103     }
104     else if ( planes[i].contact == 1 ) {
105       if ( planes[i].wpn == 0 ) {    // calculate initial waypoints
106         wpn = planes[i].wpn;
107         // airport
108         planes[i].wpts[wpn][0] = active_rw_hdg;
109         planes[i].wpts[wpn][1] = 0.0;
110         planes[i].wpts[wpn][2] = elev;
111         planes[i].wpts[wpn][4] = 0.0;
112         planes[i].wpts[wpn][5] = 0.0;
113         wpn += 1;
114
115         planes[i].wpts[wpn][0] = active_rw_hdg + 180.0;
116         if ( planes[i].wpts[wpn][0] > 360.0 ) planes[i].wpts[wpn][0] -= 360.0;
117         planes[i].wpts[wpn][1] = 5;
118         planes[i].wpts[wpn][2] = elev + 1000.0;
119         calc_hd_course_dist(planes[i].wpts[wpn][0],   planes[i].wpts[wpn][1],
120                             planes[i].wpts[wpn-1][0], planes[i].wpts[wpn-1][1],
121                             &course, &d);
122         planes[i].wpts[wpn][4] = course;
123         planes[i].wpts[wpn][5] = d;
124         wpn += 1;
125         
126         planes[i].wpts[wpn][0] = planes[i].brg;
127         planes[i].wpts[wpn][1] = planes[i].dist;
128         planes[i].wpts[wpn][2] = planes[i].alt;
129         calc_hd_course_dist(planes[i].wpts[wpn][0],   planes[i].wpts[wpn][1],
130                             planes[i].wpts[wpn-1][0], planes[i].wpts[wpn-1][1],
131                             &course, &d);
132         planes[i].wpts[wpn][4] = course;
133         planes[i].wpts[wpn][5] = d;
134         wpn += 1;
135
136         planes[i].wpn = wpn;
137
138         planes[i].ahdg = planes[i].wpts[wpn-1][4];
139         cout << endl;
140         cout << "Contact " << planes[i].wpn << endl;
141         cout << "Turn to heading   = " << (int)(planes[i].ahdg) << endl;
142         cout << endl;
143         planes[i].on_crs = true;
144       }
145
146       // reached waypoint?
147       if ( fabs(planes[i].dnc) < 0.3 && planes[i].dnwp < 1.0 ) {
148         planes[i].wpn -= 1;
149         wpn = planes[i].wpn-1;
150         planes[i].ahdg = planes[i].wpts[wpn][4];
151         cout << endl;
152         cout << "Next waypoint = " << planes[i].wpn << endl;
153         cout << "New heading   = " << planes[i].ahdg << endl;
154         cout << endl;
155         planes[i].on_crs = true;
156       }
157
158       // update assigned parameters
159       wpn = planes[i].wpn-1;            // this is the current waypoint
160
161       planes[i].dcc  = calc_psl_dist(planes[i].brg, planes[i].dist,
162                                      planes[i].wpts[wpn][0], planes[i].wpts[wpn][1],
163                                      planes[i].wpts[wpn][4]);
164       planes[i].dnc  = calc_psl_dist(planes[i].brg, planes[i].dist,
165                                      planes[i].wpts[wpn-1][0], planes[i].wpts[wpn-1][1],
166                                      planes[i].wpts[wpn-1][4]);
167       calc_hd_course_dist(planes[i].brg, planes[i].dist, 
168                           planes[i].wpts[wpn-1][0], planes[i].wpts[wpn-1][1],
169                           &course, &d);
170       planes[i].dnwp = d;
171
172       //cout << planes[i].brg << " " << planes[i].dist << " " << planes[i].wpts[wpn+1][0] 
173       //<< " " << planes[i].wpts[wpn+1][1] << " " << planes[i].wpts[wpn+1][4] 
174       //cout << " distance to current course = " << planes[i].dcc << endl;
175
176       // come off course ?
177       if ( fabs(planes[i].dcc) > 0.5 && planes[i].on_crs) {
178         wpn = wpn-1;
179         if ( planes[i].wpts[wpn][4] < 0) {
180           planes[i].ahdg += 30.0;
181         }
182         else {
183           planes[i].ahdg -= 30.0;
184         }
185         planes[i].on_crs = false;
186
187         cout << endl;
188         cout << "Your are " << planes[i].dcc << " miles off the asigned course: " << endl;
189         cout << "New heading = " << (int)(planes[i].ahdg) << endl;
190         cout << endl;
191       }
192       else if ( fabs(planes[i].dcc) < 0.1 && !planes[i].on_crs) {
193         planes[i].ahdg = fabs(planes[i].wpts[wpn][4]);
194         planes[i].on_crs = true;
195         
196         cout << endl;
197         cout << "New heading = " << (int)(planes[i].ahdg) << endl;
198         cout << endl;
199         }
200       
201       // In range of tower?
202       if ( planes[i].wpn == 2 && planes[i].dnwp < 3. ) {
203         cout << endl;
204         cout << "Contact Tower";
205         cout << endl;
206         planes[i].contact = 2;
207       }
208     }
209   }
210
211 }
212
213 // ============================================================================
214 // get active runway
215 // ============================================================================
216 void FGApproach::get_active_runway() {
217
218 #ifdef FG_WEATHERCM
219   sgVec3 position = { lat, lon, elev };
220   FGPhysicalProperty stationweather = WeatherDatabase->get(position);
221 #else
222   FGEnvironment stationweather =
223     globals->get_environment_mgr()->getEnvironment(lat, lon, elev);
224 #endif
225
226   SGPath path( globals->get_fg_root() );
227   path.append( "Airports" );
228   path.append( "runways.mk4" );
229   FGRunways runways( path.c_str() );
230   
231   //Set the heading to into the wind
232 #ifdef FG_WEATHERCM
233   double wind_x = stationweather.Wind[0];
234   double wind_y = stationweather.Wind[1];
235   
236   double speed = sqrt( wind_x*wind_x + wind_y*wind_y ) * SG_METER_TO_NM / (60.0*60.0);
237   double hdg;
238   
239   //If no wind use 270degrees
240   if(speed == 0) {
241     hdg = 270;
242   } else {
243     // //normalize the wind to get the direction
244     //wind_x /= speed; wind_y /= speed;
245     
246     hdg = - atan2 ( wind_x, wind_y ) * SG_RADIANS_TO_DEGREES ;
247     if (hdg < 0.0)
248       hdg += 360.0;
249   }
250 #else
251   double hdg = stationweather.get_wind_from_heading_deg();
252 #endif
253
254   FGRunway runway;
255   //if ( runways.search( "EGNX", int(hdg), &runway) ) {
256   if ( runways.search( ident, int(hdg), &runway) ) {
257     active_runway = runway.rwy_no;
258     active_rw_hdg = runway.heading;
259     //cout << "Active runway is: " << active_runway << "  heading = " 
260     // << active_rw_hdg << endl;
261   }
262   else cout << "FGRunways search failed" << endl;
263
264 }
265
266 // ========================================================================
267 // update infos about plane
268 // ========================================================================
269 void FGApproach::update_plane_dat() {
270   
271   //cout << "Update Approach " << ident << "   " << num_planes << " registered" << endl;
272   // update plane positions
273   for (int i=0; i<num_planes; i++) {
274     planes[i].lon = lon_node->getDoubleValue();
275     planes[i].lat = lat_node->getDoubleValue();
276     planes[i].alt = elev_node->getDoubleValue();
277 //    Point3D aircraft = sgGeodToCart( Point3D(planes[i].lon*SGD_DEGREES_TO_RADIANS, 
278 //                                           planes[i].lat*SGD_DEGREES_TO_RADIANS, 
279 //                                           planes[i].alt*SG_FEET_TO_METER) );
280     double course, distance;
281     calc_gc_course_dist(Point3D(lon*SGD_DEGREES_TO_RADIANS, lat*SGD_DEGREES_TO_RADIANS, 0.0),
282                         Point3D(planes[i].lon*SGD_DEGREES_TO_RADIANS,planes[i].lat*SGD_DEGREES_TO_RADIANS, 0.0 ),
283                         &course, &distance);
284     planes[i].dist = distance/SG_NM_TO_METER;
285     planes[i].brg  = 360.0-course*SGD_RADIANS_TO_DEGREES;
286
287     //cout << "Plane Id: " << planes[i].ident << "  Distance to " << ident 
288     //<< " is " << planes[i].dist << " m" << endl;
289     
290     //if (first) {
291     //transmission = ident;
292     //globals->get_ATC_display()->RegisterRepeatingMessage(transmission);
293     //first = false;
294     //}
295   }   
296 }
297
298 // =======================================================================
299 // Add plane to Approach list
300 // =======================================================================
301 void FGApproach::AddPlane(string pid) {
302   for ( int i=0; i<num_planes; i++) {
303     if ( planes[i].ident == pid) {
304       //cout << "Plane already registered: " << ident << " " << num_planes << endl;
305       return;
306     }
307   }
308   planes[num_planes].ident = pid;
309   ++num_planes;
310   //cout << "Plane added to list: " << ident << " " << num_planes << endl;
311   return;
312 }
313
314 // ========================================================================
315 // closest distance between a point and a straigt line in 2 dim.
316 // ========================================================================
317 double FGApproach::calc_psl_dist(const double &h1, const double &d1,
318                                  const double &h2, const double &d2,
319                                  const double &h3)
320 {
321   double a1 = h1 * SGD_DEGREES_TO_RADIANS;
322   double a2 = h2 * SGD_DEGREES_TO_RADIANS;
323   double a3 = h3 * SGD_DEGREES_TO_RADIANS;
324   double x1 = cos(a1) * d1;
325   double y1 = sin(a1) * d1;
326   double x2 = cos(a2) * d2;
327   double y2 = sin(a2) * d2;
328   double x3 = cos(a3);
329   double y3 = sin(a3);
330   
331   // formula: dis = sqrt( (v1-v2)**2 - ((v1-v2)*v3)**2 ); vi = (xi,yi)
332   double val1   = (x1-x2)*(x1-x2) + (y1-y2)*(y1-y2);
333   double val2   = ((x1-x2)*x3 + (y1-y2)*y3) * ((x1-x2)*x3 + (y1-y2)*y3);
334   double dis    = val1 - val2;
335   // now get sign for offset 
336   //cout << x1 << " " << x2 << " " << y1 << " " << y2 << " " 
337   //     << x3 << " " << y3 << " " 
338   //     << val1 << " " << val2 << " " << dis << endl;
339   x3 *= sqrt(val2);
340   y3 *= sqrt(val2);
341   if ( x3*(x1-x2) < 0.0 && y3*(y1-y2) < 0.0) {
342     x3 *= -1.0;
343     y3 *= -1.0;
344   }
345   //cout << x3 << " " << y3 << endl;
346   double dis1   = x1-x2-x3;
347   double dis2   = y1-y2-y3;
348   dis = sqrt(dis);
349   if (atan2(dis2,dis1) < a3) dis *= -1.0;
350   //cout << dis1 << " " << dis2 << " " << atan2(dis2,dis1)*SGD_RADIANS_TO_DEGREES << " " << h3
351   //     << " " << sqrt(dis1*dis1 + dis2*dis2) << " " << dis << endl;
352   //cout << atan2(dis2,dis1)*SGD_RADIANS_TO_DEGREES << " " << dis << endl;
353
354   return dis;
355 }
356
357 // ========================================================================
358 // get heading and distance between two points; point1 ---> point2
359 // ========================================================================
360 void FGApproach::calc_hd_course_dist(const double &h1, const double &d1, 
361                                      const double &h2, const double &d2,
362                                      double *course, double *dist)
363 {
364   double a1 = h1 * SGD_DEGREES_TO_RADIANS;
365   double a2 = h2 * SGD_DEGREES_TO_RADIANS;
366   double x1 = cos(a1) * d1;
367   double y1 = sin(a1) * d1;
368   double x2 = cos(a2) * d2;
369   double y2 = sin(a2) * d2;
370            
371   *dist   = sqrt( (y2-y1)*(y2-y1) + (x2-x1)*(x2-x1) );
372   *course = atan2( (y2-y1), (x2-x1) ) * SGD_RADIANS_TO_DEGREES;
373   if ( *course < 0 ) *course = *course+360;
374   //cout << x1 << " " << y1 << " " << x2 << " " << y2 << " " << *dist << " " << *course << endl;
375 }
376
377
378
379 int FGApproach::RemovePlane() {
380
381   // first check if anything has to be done
382   int i;
383   bool rmplane = false;
384   for (i=0; i<num_planes; i++) {
385     if (planes[i].dist > range*SG_NM_TO_METER) {
386       rmplane = true;
387       break;
388     }
389   }
390   if (!rmplane) return num_planes;
391
392   // now make a copy of the plane list
393   PlaneApp tmp[max_planes];
394   for (i=0; i<num_planes; i++) {
395     tmp[i] = planes[i];
396   }
397   
398   int np = 0;
399   // now check which planes are still in range
400   for (i=0; i<num_planes; i++) {
401     if (tmp[i].dist <= range*SG_NM_TO_METER) {
402       planes[np] = tmp[i];
403       np += 1;
404     }
405   }
406   num_planes = np;
407   return num_planes;
408 }