]> git.mxchange.org Git - flightgear.git/blob - src/ATCDCL/ATCmgr.cxx
Merge branch 'tat/framework'
[flightgear.git] / src / ATCDCL / ATCmgr.cxx
1 // ATCmgr.cxx - Implementation of FGATCMgr - a global Flightgear ATC manager.
2 //
3 // Written by David Luff, started February 2002.
4 //
5 // Copyright (C) 2002  David C Luff - david.luff@nottingham.ac.uk
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., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
20
21 #ifdef HAVE_CONFIG_H
22 #  include <config.h>
23 #endif
24
25 #include <simgear/misc/sg_path.hxx>
26 #include <simgear/debug/logstream.hxx>
27
28 #include <Airports/simple.hxx>
29
30 #include "ATCmgr.hxx"
31 #include "commlist.hxx"
32 #include "ATCDialog.hxx"
33 #include "ATCutils.hxx"
34 #include "transmissionlist.hxx"
35 #include "ground.hxx"
36
37
38 /*
39 // periodic radio station search wrapper
40 static void fgATCSearch( void ) {
41     globals->get_ATC_mgr()->Search();
42 }
43 */ //This wouldn't compile - including Time/event.hxx breaks it :-(
44    // Is this still true?? -EMH-
45
46 AirportATC::AirportATC() :
47     atis_freq(0.0),
48     atis_active(false),
49     tower_freq(0.0),
50     tower_active(false),
51     ground_freq(0.0),
52     ground_active(false),
53     set_by_AI(false),
54     numAI(0)
55     //airport_atc_map.clear();
56 {
57 }
58
59 FGATCMgr::FGATCMgr() :
60     initDone(false),
61     atc_list(new atc_list_type),
62     last_in_range(false)
63 {
64 }
65
66 FGATCMgr::~FGATCMgr() {
67     delete v1;
68 }
69
70 void FGATCMgr::bind() {
71 }
72
73 void FGATCMgr::unbind() {
74 }
75
76 void FGATCMgr::init() {
77     //cout << "ATCMgr::init called..." << endl;
78     
79     lon_node = fgGetNode("/position/longitude-deg", true);
80     lat_node = fgGetNode("/position/latitude-deg", true);
81     elev_node = fgGetNode("/position/altitude-ft", true);
82     atc_list_itr = atc_list->begin();
83     
84     // Search for connected ATC stations once per 0.8 seconds or so
85     // globals->get_event_mgr()->add( "fgATCSearch()", fgATCSearch,
86         //                                 FGEvent::FG_EVENT_READY, 800);
87         //  
88     // For some reason the above doesn't compile - including Time/event.hxx stops compilation.
89         // Is this still true after the reorganization of the event managar??
90         // -EMH-
91     
92 #ifdef ENABLE_AUDIO_SUPPORT 
93     // Load all available voices.
94     // For now we'll do one hardwired one
95     
96     v1 = new FGATCVoice;
97     try {
98         voiceOK = v1->LoadVoice("default");
99         voice = true;
100     } catch ( sg_io_exception & e) {
101         voiceOK  = false;
102         SG_LOG(SG_ATC, SG_ALERT, "Unable to load default voice : " << e.getFormattedMessage().c_str());
103         voice = false;
104         delete v1;
105         v1 = 0;
106     }
107     
108     /* I've loaded the voice even if /sim/sound/pause is true
109     *  since I know no way of forcing load of the voice if the user
110     *  subsequently switches /sim/sound/audible to true.
111         *  (which is the right thing to do -- CLO) :-) */
112 #else
113     voice = false;
114 #endif
115
116     // Initialise the ATC Dialogs
117     //cout << "Initing Transmissions..." << endl;
118     SG_LOG(SG_ATC, SG_INFO, "  ATC Transmissions");
119     current_transmissionlist = new FGTransmissionList;
120     SGPath p_transmission( globals->get_fg_root() );
121     p_transmission.append( "ATC/default.transmissions" );
122     current_transmissionlist->init( p_transmission );
123     //cout << "Done Transmissions" << endl;
124
125     SG_LOG(SG_ATC, SG_INFO, "  ATC Dialog System");
126     current_atcdialog = new FGATCDialog;
127     current_atcdialog->Init();
128
129     initDone = true;
130     //cout << "ATCmgr::init done!" << endl;
131 }
132
133 void FGATCMgr::update(double dt) {
134     if(!initDone) {
135         init();
136         SG_LOG(SG_ATC, SG_WARN, "Warning - ATCMgr::update(...) called before ATCMgr::init()");
137     }
138     
139     current_atcdialog->Update(dt);
140     
141     //cout << "Entering update..." << endl;
142     //Traverse the list of active stations.
143     //Only update one class per update step to avoid the whole ATC system having to calculate between frames.
144     //Eventually we should only update every so many steps.
145     //cout << "In FGATCMgr::update - atc_list.size = " << atc_list->size() << endl;
146     if(atc_list->size()) {
147         if(atc_list_itr == atc_list->end()) {
148             atc_list_itr = atc_list->begin();
149         }
150         //cout << "Updating " << (*atc_list_itr)->get_ident() << ' ' << (*atc_list_itr)->GetType() << '\n';
151         //cout << "Freq = " << (*atc_list_itr)->get_freq() << '\n';
152         (*atc_list_itr).second->Update(dt * atc_list->size());
153         //cout << "Done ATC update..." << endl;
154         ++atc_list_itr;
155     }
156     
157 #ifdef ATC_TEST
158     cout << "ATC_LIST: " << atc_list->size() << ' ';
159     for(atc_list_iterator it = atc_list->begin(); it != atc_list->end(); it++) {
160         cout << (*it)->get_ident() << ' ';
161     }
162     cout << '\n';
163 #endif
164     
165     // Search the tuned frequencies every now and then - this should be done with the event scheduler
166     static int i = 0;   // Very ugly - but there should only ever be one instance of FGATCMgr.
167     /*** Area search is defeated.  Why?
168     if(i == 7) {
169         //cout << "About to AreaSearch()" << endl;
170         AreaSearch();
171     }
172     ***/
173     if(i == 15) {
174         //cout << "About to search navcomm1" << endl;
175         FreqSearch("comm", 0);
176         FreqSearch("nav", 0);
177     }
178     if(i == 30) {
179         //cout << "About to search navcomm2" << endl;
180         FreqSearch("comm", 1);
181         FreqSearch("nav", 1);
182         i = 0;
183     }
184     ++i;
185     
186     //cout << "comm1 type = " << comm_type[0] << '\n';
187     //cout << "Leaving update..." << endl;
188 }
189
190
191 // Returns frequency in KHz - should I alter this to return in MHz?
192 unsigned short int FGATCMgr::GetFrequency(const string& ident, const atc_type& tp) {
193     ATCData test;
194     bool ok = current_commlist->FindByCode(ident, test, tp);
195     return(ok ? test.freq : 0);
196 }   
197
198
199 // Register the fact that the AI system wants to activate an airport
200 // Might need more sophistication in this in the future - eg registration by aircraft call-sign.
201 bool FGATCMgr::AIRegisterAirport(const string& ident) {
202     SG_LOG(SG_ATC, SG_BULK, "AI registered airport " << ident << " with the ATC system");
203     //cout << "AI registered airport " << ident << " with the ATC system" << '\n';
204     if(airport_atc_map.find(ident) != airport_atc_map.end()) {
205         airport_atc_map[ident]->set_by_AI = true;
206         airport_atc_map[ident]->numAI++;
207         return(true);
208     } else {
209         const FGAirport *ap = fgFindAirportID(ident);
210         if (ap) {
211             //cout << "ident = " << ident << '\n';
212             AirportATC *a = new AirportATC;
213             // I'm not entirely sure that this AirportATC structure business is actually needed - it just duplicates what we can find out anyway!
214             a->geod = ap->geod();
215             a->atis_freq = GetFrequency(ident, ATIS)
216                     || GetFrequency(ident, AWOS);
217             //cout << "ATIS freq = " << a->atis_freq << '\n';
218             a->atis_active = false;
219             a->tower_freq = GetFrequency(ident, TOWER);
220             //cout << "Tower freq = " << a->tower_freq << '\n';
221             a->tower_active = false;
222             a->ground_freq = GetFrequency(ident, GROUND);
223             //cout << "Ground freq = " << a->ground_freq << '\n';
224             a->ground_active = false;
225             // TODO - some airports will have a tower/ground frequency but be inactive overnight.
226             a->set_by_AI = true;
227             a->numAI = 1;
228             airport_atc_map[ident] = a;
229             return(true);
230         } else {
231             SG_LOG(SG_ATC, SG_ALERT, "ERROR - can't find airport " << ident << " in AIRegisterAirport(...)");
232         }
233     }
234     return(false);
235 }
236
237 // Register the fact that the comm radio is tuned to an airport
238 // Channel is zero based
239 bool FGATCMgr::CommRegisterAirport(const string& ident, int chan, const atc_type& tp) {
240     SG_LOG(SG_ATC, SG_BULK, "Comm channel " << chan << " registered airport " << ident);
241     //cout << "Comm channel " << chan << " registered airport " << ident << ' ' << tp << '\n';
242     if(airport_atc_map.find(ident) != airport_atc_map.end()) {
243         //cout << "IN MAP - flagging set by comm..." << endl;
244 //xx        airport_atc_map[ident]->set_by_comm[chan][tp] = true;
245         if(tp == ATIS || tp == AWOS) {
246             airport_atc_map[ident]->atis_active = true;
247         } else if(tp == TOWER) {
248             airport_atc_map[ident]->tower_active = true;
249         } else if(tp == GROUND) {
250             airport_atc_map[ident]->ground_active = true;
251         } else if(tp == APPROACH) {
252             //a->approach_active = true;
253         }   // TODO - there *must* be a better way to do this!!!
254         return(true);
255     } else {
256         //cout << "NOT IN MAP - creating new..." << endl;
257         const FGAirport *ap = fgFindAirportID(ident);
258         if (ap) {
259             AirportATC *a = new AirportATC;
260             // I'm not entirely sure that this AirportATC structure business is actually needed - it just duplicates what we can find out anyway!
261             a->geod = ap->geod();
262             a->atis_freq = GetFrequency(ident, ATIS)
263                     || GetFrequency(ident, AWOS);
264             a->atis_active = false;
265             a->tower_freq = GetFrequency(ident, TOWER);
266             a->tower_active = false;
267             a->ground_freq = GetFrequency(ident, GROUND);
268             a->ground_active = false;
269             if(tp == ATIS || tp == AWOS) {
270                 a->atis_active = true;
271             } else if(tp == TOWER) {
272                 a->tower_active = true;
273             } else if(tp == GROUND) {
274                 a->ground_active = true;
275             } else if(tp == APPROACH) {
276                 //a->approach_active = true;
277             }   // TODO - there *must* be a better way to do this!!!
278             // TODO - some airports will have a tower/ground frequency but be inactive overnight.
279             a->set_by_AI = false;
280             a->numAI = 0;
281 //xx            a->set_by_comm[chan][tp] = true;
282             airport_atc_map[ident] = a;
283             return(true);
284         }
285     }
286     return(false);
287 }
288
289 typedef map<string,int> MSI;
290
291 void FGATCMgr::ZapOtherService(const string ncunit, const string svc_name){
292   for (atc_list_iterator svc = atc_list->begin(); svc != atc_list->end(); svc++) {
293     //cout << "Zapping " << navcomm 
294     //  << "[" << unit << "]"  << "  otherthan: " << svc_name << endl;
295     if (svc->first != svc_name) {
296       MSI &actv = svc->second->active_on;
297       // OK, we have found some OTHER service;
298       // see if it is (was) active on our unit:
299       if (!actv.count(ncunit)) continue;
300       // cout << "Eradicating '" << svc->first << "' from: " << ncunit << endl;
301       actv.erase(ncunit);
302       if (!actv.size()) {
303         //cout << "Eradicating service: '" << svc->first << "'" << endl;
304     svc->second->SetNoDisplay();
305     svc->second->Update(0);     // one last update
306     delete svc->second;
307     atc_list->erase(svc);
308 // ALL pointers into the ATC list are now invalid,
309 // so let's reset them:
310     atc_list_itr = atc_list->begin();
311       }
312       break;        // cannot be duplicates in the active list
313     }
314   }
315 }
316
317
318 // Find in list - return a currently active ATC pointer given ICAO code and type
319 // Return NULL if the given service is not in the list
320 // - *** THE CALLING FUNCTION MUST CHECK FOR THIS ***
321 FGATC* FGATCMgr::FindInList(const string& id, const atc_type& tp) {
322   string ndx = id + decimalNumeral(tp);
323   if (!atc_list->count(ndx)) return 0;
324   return (*atc_list)[ndx];
325 }
326
327 // Returns true if the airport is found in the map
328 bool FGATCMgr::GetAirportATCDetails(const string& icao, AirportATC* a) {
329     if(airport_atc_map.find(icao) != airport_atc_map.end()) {
330         *a = *airport_atc_map[icao];
331         return(true);
332     } else {
333         return(false);
334     }
335 }
336
337
338 // Return a pointer to a given sort of ATC at a given airport and activate if necessary
339 // Returns NULL if service doesn't exist - calling function should check for this.
340 // We really ought to make this private and call it from the CommRegisterAirport / AIRegisterAirport functions
341 // - at the moment all these GetATC... functions exposed are just too complicated.
342 FGATC* FGATCMgr::GetATCPointer(const string& icao, const atc_type& type) {
343     if(airport_atc_map.find(icao) == airport_atc_map.end()) {
344         //cout << "Unable to find " << icao << ' ' << type << " in the airport_atc_map" << endl;
345         return NULL;
346     }
347     //cout << "In GetATCPointer, found " << icao << ' ' << type << endl;
348     AirportATC *a = airport_atc_map[icao];
349     //cout << "a->lon = " << a->lon << '\n';
350     //cout << "a->elev = " << a->elev << '\n';
351     //cout << "a->tower_freq = " << a->tower_freq << '\n';
352     switch(type) {
353     case TOWER:
354         if(a->tower_active) {
355             // Get the pointer from the list
356             return(FindInList(icao, type));
357         } else {
358             ATCData data;
359             if(current_commlist->FindByFreq(a->geod, a->tower_freq, &data, TOWER)) {
360                 FGTower* t = new FGTower;
361                 t->SetData(&data);
362                 (*atc_list)[icao+decimalNumeral(type)] = t;
363                 a->tower_active = true;
364                 airport_atc_map[icao] = a;
365                 //cout << "Initing tower " << icao << " in GetATCPointer()\n";
366                 t->Init();
367                 return(t);
368             } else {
369                 SG_LOG(SG_ATC, SG_ALERT, "ERROR - tower that should exist in FGATCMgr::GetATCPointer for airport " << icao << " not found");
370             }
371         }
372         break;
373     case APPROACH:
374         break;
375     case ATIS: case AWOS:
376         SG_LOG(SG_ATC, SG_ALERT, "ERROR - ATIS station should not be requested from FGATCMgr::GetATCPointer");
377         break;
378     case GROUND:
379         //cout << "IN CASE GROUND" << endl;
380         if(a->ground_active) {
381             // Get the pointer from the list
382             return(FindInList(icao, type));
383         } else {
384             ATCData data;
385             if(current_commlist->FindByFreq(a->geod, a->ground_freq, &data, GROUND)) {
386                 FGGround* g = new FGGround;
387                 g->SetData(&data);
388                 (*atc_list)[icao+decimalNumeral(type)] = g;
389                 a->ground_active = true;
390                 airport_atc_map[icao] = a;
391                 g->Init();
392                 return(g);
393             } else {
394                 SG_LOG(SG_ATC, SG_ALERT, "ERROR - ground control that should exist in FGATCMgr::GetATCPointer for airport " << icao << " not found");
395             }
396         }
397         break;
398         case INVALID:
399         break;
400         case ENROUTE:
401         break;
402         case DEPARTURE:
403         break;
404     }
405     
406     SG_LOG(SG_ATC, SG_ALERT, "ERROR IN FGATCMgr - reached end of GetATCPointer");
407     //cout << "ERROR IN FGATCMgr - reached end of GetATCPointer" << endl;
408     
409     return(NULL);
410 }
411
412 // Return a pointer to an appropriate voice for a given type of ATC
413 // creating the voice if necessary - ie. make sure exactly one copy
414 // of every voice in use exists in memory.
415 //
416 // TODO - in the future this will get more complex and dole out country/airport
417 // specific voices, and possible make sure that the same voice doesn't get used
418 // at different airports in quick succession if a large enough selection are available.
419 FGATCVoice* FGATCMgr::GetVoicePointer(const atc_type& type) {
420     // TODO - implement me better - maintain a list of loaded voices and other voices!!
421     if(voice) {
422         switch(type) {
423         case ATIS: case AWOS:
424             if(voiceOK) {
425                 return(v1);
426             }
427         case TOWER:
428             return(NULL);
429         case APPROACH:
430             return(NULL);
431         case GROUND:
432             return(NULL);
433         default:
434             return(NULL);
435         }
436         return(NULL);
437     } else {
438         return(NULL);
439     }
440 }
441
442 // Search for ATC stations by frequency
443 void FGATCMgr::FreqSearch(const string navcomm, const int unit) {
444
445
446         string ncunit = navcomm + "[" + decimalNumeral(unit) + "]";
447     string commbase = "/instrumentation/" + ncunit;
448     string commfreq = commbase + "/frequencies/selected-mhz";
449         SGPropertyNode_ptr comm_node = fgGetNode(commfreq.c_str(), false);
450
451     //cout << "FreqSearch: " << ncunit
452     //  << "  node: " << comm_node << endl;
453     if (!comm_node) return; // no such radio unit
454
455     ATCData data;   
456     double freq = comm_node->getDoubleValue();
457     _aircraftPos = SGGeod::fromDegFt(lon_node->getDoubleValue(),
458       lat_node->getDoubleValue(), elev_node->getDoubleValue());
459     
460 // Query the data store and get the closest match if any
461     //cout << "Will FindByFreq: " << lat << " " << lon << " " << elev
462     //      << "  freq: " << freq << endl;
463     if(current_commlist->FindByFreq(_aircraftPos, freq, &data)) {
464       //cout << "FoundByFreq: " << freq 
465       //  << "  ident: " << data.ident 
466       //  << "  type: " << data.type << " ***" << endl;
467 // We are in range of something.
468
469
470 // Get rid of any *other* service that was on this radio unit:
471             string svc_name = data.ident+decimalNumeral(data.type);
472         ZapOtherService(ncunit, svc_name);
473 // See if the service already exists, possibly connected to
474 // some other radio unit:
475         if (atc_list->count(svc_name)) {
476           // make sure the service knows it's tuned on this radio:
477           FGATC* svc = (*atc_list)[svc_name];
478           svc->active_on[ncunit] = 1;
479           svc->SetDisplay();
480           if (data.type == APPROACH) svc->AddPlane("Player");
481           return;
482         }
483
484         CommRegisterAirport(data.ident, unit, data.type);
485
486 // This was a switch-case statement but the compiler didn't like 
487 // the new variable creation with it. 
488         if      (data.type == ATIS
489               || data.type == AWOS)     (*atc_list)[svc_name] = new FGATIS;
490         else if (data.type == TOWER)    (*atc_list)[svc_name] = new FGTower;
491         else if (data.type == GROUND)   (*atc_list)[svc_name] = new FGGround;
492         else if (data.type == APPROACH) (*atc_list)[svc_name] = new FGApproach;
493         FGATC* svc = (*atc_list)[svc_name];
494         svc->SetData(&data);
495         svc->active_on[ncunit] = 1;
496         svc->SetDisplay();
497         svc->Init();
498         if (data.type == APPROACH) svc->AddPlane("Player");
499     } else {
500       // No services in range.  Zap any service on this unit.
501       ZapOtherService(ncunit, "x x x");
502     } 
503 }
504
505 #ifdef AREA_SEARCH
506 /* I don't think AreaSearch ever gets called */
507 // Search ATC stations by area in order that we appear 'on the radar'
508 void FGATCMgr::AreaSearch() {
509   const string AREA("AREA");
510   // Search for Approach stations
511   comm_list_type approaches;
512   comm_list_iterator app_itr;
513
514   lon = lon_node->getDoubleValue();
515   lat = lat_node->getDoubleValue();
516   elev = elev_node->getDoubleValue() * SG_FEET_TO_METER;
517   for (atc_list_iterator svc = atc_list->begin(); svc != atc_list->end(); svc++) {
518     MSI &actv = svc->second->active_on;
519     if (actv.count(AREA)) actv[AREA] = 0;  // Mark all as maybe not in range
520   }
521
522   // search stations in range
523   int num_app = current_commlist->FindByPos(lon, lat, elev, 100.0, &approaches, APPROACH);
524   if (num_app != 0) {
525     //cout << num_app << " approaches found in area search !!!!" << endl;
526
527     for(app_itr = approaches.begin(); app_itr != approaches.end(); app_itr++) {
528       FGATC* app = FindInList(app_itr->ident, app_itr->type);
529       string svc_name = app_itr->ident+decimalNumeral(app_itr->type);
530       if(app != NULL) {
531     // The station is already in the ATC list
532     app->AddPlane("Player");
533       } else {
534     // Generate the station and put in the ATC list
535     FGApproach* a = new FGApproach;
536     a->SetData(&(*app_itr));
537     a->AddPlane("Player");
538     (*atc_list)[svc_name] = a;
539     //cout << "New area service: " << svc_name << endl;
540       }
541       FGATC* svc = (*atc_list)[svc_name];
542       svc->active_on[AREA] = 1;
543     }
544   }
545
546   for (atc_list_iterator svc = atc_list->begin(); svc != atc_list->end(); svc++) {
547     MSI &actv = svc->second->active_on;
548     if (!actv.count(AREA)) continue;
549     if (!actv[AREA]) actv.erase(AREA);
550     if (!actv.size()) {     // this service no longer active at all
551       cout << "Eradicating area service: " << svc->first << endl;
552       svc->second->SetNoDisplay();
553       svc->second->Update(0);
554       delete (svc->second);
555       atc_list->erase(svc);
556 // Reset the persistent iterator, since any erase() makes it invalid:
557       atc_list_itr = atc_list->begin(); 
558 // Hope we only move out of one approach-area;
559 // others will not be noticed until next update:
560       break;
561     }
562   }
563 }
564 #endif