]> git.mxchange.org Git - flightgear.git/blob - src/ATCDCL/ATC.cxx
MapWidget: make use of the new POI system and display cities on the map.
[flightgear.git] / src / ATCDCL / ATC.cxx
1 // Implementation of FGATC - ATC subsystem base class.
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 "ATC.hxx"
26
27 #include <iostream>
28
29 #include <simgear/sound/soundmgr_openal.hxx>
30 #include <simgear/sound/sample_group.hxx>
31 #include <simgear/structure/exception.hxx>
32
33 #include <Main/globals.hxx>
34 #include <Main/fg_props.hxx>
35 #include <ATC/CommStation.hxx>
36 #include <Airports/airport.hxx>
37
38 FGATC::FGATC() :
39     freq(0),
40     _currentStation(NULL),
41     range(0),
42     _voice(true),
43     _playing(false),
44     _sgr(NULL),
45     _type(INVALID),
46     _display(false)
47 #ifdef OLD_ATC_MGR
48     ,freqClear(true),
49     receiving(false),
50     respond(false),
51     responseID(""),
52     runResponseCounter(false),
53     _runReleaseCounter(false),
54     responseReqd(false),
55     // Transmission timing stuff
56     pending_transmission(""),
57     _timeout(0),
58     _pending(false),
59     _transmit(false),
60     _transmitting(false),
61     _counter(0.0),
62     _max_count(5.0)
63 #endif
64 {
65     SGSoundMgr *smgr = globals->get_soundmgr();
66     _sgr = smgr->find("atc", true);
67     _sgr->tie_to_listener();
68
69     _masterVolume = fgGetNode("/sim/sound/atc/volume", true);
70     _enabled = fgGetNode("/sim/sound/atc/enabled", true);
71     _atc_external = fgGetNode("/sim/sound/atc/external-view", true);
72     _internal = fgGetNode("/sim/current-view/internal", true);
73 }
74
75 FGATC::~FGATC() {
76 }
77
78 #ifndef OLD_ATC_MGR
79 // Derived classes wishing to use the response counter should 
80 // call this from their own Update(...).
81 void FGATC::update(double dt) {
82
83     // TODO This doesn't really do anything specific to this instance.
84     // All FGATCs share the same "_sgr" sound group. So this really should
85     // only be done once for all FGATCs.
86 #ifdef ENABLE_AUDIO_SUPPORT
87     bool active = _atc_external->getBoolValue() ||
88               _internal->getBoolValue();
89
90     if ( active && _enabled->getBoolValue() ) {
91         _sgr->set_volume( _masterVolume->getFloatValue() );
92         _sgr->resume(); // no-op if already in resumed state
93     } else {
94         _sgr->suspend();
95     }
96 #endif
97 }
98 #endif
99
100 void FGATC::SetStation(flightgear::CommStation* sta) {
101     if (_currentStation == sta)
102         return;
103     _currentStation = sta;
104
105     if (sta)
106     {
107         switch (sta->type()) {
108             case FGPositioned::FREQ_ATIS:   _type = ATIS; break;
109             case FGPositioned::FREQ_AWOS:   _type = AWOS; break;
110             default:
111                 sta = NULL;
112                 break;
113         }
114     }
115
116     if (sta == NULL)
117     {
118         range = 0;
119         ident = "";
120         name = "";
121         freq = 0;
122
123         SetNoDisplay();
124         update(0);     // one last update
125     }
126     else
127     {
128         _geod = sta->geod();
129         _cart = sta->cart();
130
131         range = sta->rangeNm();
132         ident = sta->airport()->ident();
133         name = sta->airport()->name();
134         freq = sta->freqKHz();
135         SetDisplay();
136     }
137 }
138
139 // Render a transmission
140 // Outputs the transmission either on screen or as audio depending on user preference
141 // The refname is a string to identify this sample to the sound manager
142 // The repeating flag indicates whether the message should be repeated continuously or played once.
143 void FGATC::Render(std::string& msg, const float volume,
144                    const std::string& refname, const bool repeating) {
145     if ((!_display) ||(volume < 0.05))
146     {
147         NoRender(refname);
148         return;
149     }
150
151     if (repeating)
152         fgSetString("/sim/messages/atis", msg.c_str());
153     else
154         fgSetString("/sim/messages/atc", msg.c_str());
155
156 #ifdef ENABLE_AUDIO_SUPPORT
157     bool useVoice = _voice && fgGetBool("/sim/sound/voice") && fgGetBool("/sim/sound/atc/enabled");
158     SGSoundSample *simple = _sgr->find(refname);
159     if(useVoice) {
160         if (simple && (_currentMsg == msg))
161         {
162             simple->set_volume(volume);
163         }
164         else
165         {
166             _currentMsg = msg;
167             size_t len;
168             void* buf = NULL;
169             FGATCVoice* vPtr = GetVoicePointer();
170             if (vPtr)
171                 buf = vPtr->WriteMessage((char*)msg.c_str(), &len);
172             NoRender(refname);
173             if(buf) {
174                 try {
175 // >>> Beware: must pass a (new) object to the (add) method,
176 // >>> because the (remove) method is going to do a (delete)
177 // >>> whether that's what you want or not.
178                     simple = new SGSoundSample(&buf, len, 8000);
179                     simple->set_volume(volume);
180                     _sgr->add(simple, refname);
181                     _sgr->play(refname, repeating);
182                 } catch ( sg_io_exception &e ) {
183                     SG_LOG(SG_ATC, SG_ALERT, e.getFormattedMessage());
184                 }
185             }
186         }
187     }
188     else
189     if (simple)
190     {
191         NoRender(refname);
192     }
193 #else
194     bool useVoice = false;
195 #endif    // ENABLE_AUDIO_SUPPORT
196
197     if (!useVoice)
198     {
199         // first rip the underscores and the pause hints out of the string - these are for the convenience of the voice parser
200         for(unsigned int i = 0; i < msg.length(); ++i) {
201             if((msg.substr(i,1) == "_") || (msg.substr(i,1) == "/")) {
202                 msg[i] = ' ';
203             }
204         }
205     }
206     _playing = true;
207 }
208
209
210 // Cease rendering a transmission.
211 void FGATC::NoRender(const std::string& refname) {
212     if(_playing) {
213         if(_voice) {
214 #ifdef ENABLE_AUDIO_SUPPORT
215             _sgr->stop(refname);
216             _sgr->remove(refname);
217 #endif
218         }
219         _playing = false;
220     }
221 }
222
223 #ifdef OLD_ATC_MGR
224 // Derived classes wishing to use the response counter should
225 // call this from their own Update(...).
226 void FGATC::Update(double dt) {
227
228 #ifdef ENABLE_AUDIO_SUPPORT
229     bool active = _atc_external->getBoolValue() ||
230               _internal->getBoolValue();
231
232     if ( active && _enabled->getBoolValue() ) {
233         _sgr->set_volume( _masterVolume->getFloatValue() );
234         _sgr->resume(); // no-op if already in resumed state
235     } else {
236         _sgr->suspend();
237     }
238 #endif
239
240     if(runResponseCounter) {
241         //cout << responseCounter << '\t' << responseTime << '\n';
242         if(responseCounter >= responseTime) {
243             runResponseCounter = false;
244             respond = true;
245             //cout << "RESPOND\n";
246         } else {
247             responseCounter += dt;
248         }
249     }
250
251     if(_runReleaseCounter) {
252         if(_releaseCounter >= _releaseTime) {
253             freqClear = true;
254             _runReleaseCounter = false;
255         } else {
256             _releaseCounter += dt;
257         }
258     }
259
260     // Transmission stuff cribbed from AIPlane.cxx
261     if(_pending) {
262         if(GetFreqClear()) {
263             //cout << "TUNED STATION FREQ CLEAR\n";
264             SetFreqInUse();
265             _pending = false;
266             _transmit = true;
267             _transmitting = false;
268         } else {
269             if(_timeout > 0.0) {    // allows count down to be avoided by initially setting it to zero
270                 _timeout -= dt;
271                 if(_timeout <= 0.0) {
272                     _timeout = 0.0;
273                     _pending = false;
274                     // timed out - don't render.
275                 }
276             }
277         }
278     }
279
280     if(_transmit) {
281         _counter = 0.0;
282         _max_count = 5.0;        // FIXME - hardwired length of message - need to calculate it!
283
284         //cout << "Transmission = " << pending_transmission << '\n';
285         if(_display) {
286             //Render(pending_transmission, ident, false);
287             Render(pending_transmission);
288         }
289         _transmit = false;
290         _transmitting = true;
291     } else if(_transmitting) {
292         if(_counter >= _max_count) {
293             //NoRender(plane.callsign);  commented out since at the moment NoRender is designed just to stop repeating messages,
294             // and this will be primarily used on single messages.
295             _transmitting = false;
296             //if(tuned_station) tuned_station->NotifyTransmissionFinished(plane.callsign);
297             // TODO - need to let the plane the transmission is aimed at that it's finished.
298             // However, for now we'll just release the frequency since if we don't it all goes pear-shaped
299             _releaseCounter = 0.0;
300             _releaseTime = 0.9;
301             _runReleaseCounter = true;
302         }
303         _counter += dt;
304     }
305 }
306
307 void FGATC::ReceiveUserCallback(int code) {
308     SG_LOG(SG_ATC, SG_WARN, "WARNING - whichever ATC class was intended to receive callback code " << code << " didn't get it!!!");
309 }
310
311 void FGATC::SetResponseReqd(const string& rid) {
312     receiving = false;
313     responseReqd = true;
314     respond = false;    // TODO - this ignores the fact that more than one plane could call this before response
315                         // Shouldn't happen with AI only, but user could confuse things??
316     responseID = rid;
317     runResponseCounter = true;
318     responseCounter = 0.0;
319     responseTime = 1.8;        // TODO - randomize this slightly.
320 }
321
322 void FGATC::NotifyTransmissionFinished(const string& rid) {
323     //cout << "Transmission finished, callsign = " << rid << '\n';
324     receiving = false;
325     responseID = rid;
326     if(responseReqd) {
327         runResponseCounter = true;
328         responseCounter = 0.0;
329         responseTime = 1.2;    // TODO - randomize this slightly, and allow it to be dependent on the transmission and how busy the ATC is.
330         respond = false;    // TODO - this ignores the fact that more than one plane could call this before response
331                             // Shouldn't happen with AI only, but user could confuse things??
332     } else {
333         freqClear = true;
334     }
335 }
336
337 // Generate the text of a message from its parameters and the current context.
338 string FGATC::GenText(const string& m, int c) {
339     return("");
340 }
341
342 ostream& operator << (ostream& os, atc_type atc) {
343     switch(atc) {
344         case(AWOS):       return(os << "AWOS");
345         case(ATIS):       return(os << "ATIS");
346         case(GROUND):     return(os << "GROUND");
347         case(TOWER):      return(os << "TOWER");
348         case(APPROACH):   return(os << "APPROACH");
349         case(DEPARTURE):  return(os << "DEPARTURE");
350         case(ENROUTE):    return(os << "ENROUTE");
351         case(INVALID):    return(os << "INVALID");
352     }
353     return(os << "ERROR - Unknown switch in atc_type operator << ");
354 }
355
356 std::istream& operator >> ( std::istream& fin, ATCData& a )
357 {
358     double f;
359     char ch;
360     char tp;
361
362     fin >> tp;
363
364     switch(tp) {
365     case 'I':
366         a.type = ATIS;
367         break;
368     case 'T':
369         a.type = TOWER;
370         break;
371     case 'G':
372         a.type = GROUND;
373         break;
374     case 'A':
375         a.type = APPROACH;
376         break;
377     case '[':
378         a.type = INVALID;
379         return fin >> skipeol;
380     default:
381         SG_LOG(SG_ATC, SG_ALERT, "Warning - unknown type \'" << tp << "\' found whilst reading ATC frequency data!\n");
382         a.type = INVALID;
383         return fin >> skipeol;
384     }
385
386     double lat, lon, elev;
387
388     fin >> lat >> lon >> elev >> f >> a.range >> a.ident;
389     a.geod = SGGeod::fromDegM(lon, lat, elev);
390     a.name = "";
391     fin >> ch;
392     if(ch != '"') a.name += ch;
393     while(1) {
394         //in >> noskipws
395         fin.unsetf(std::ios::skipws);
396         fin >> ch;
397         if((ch == '"') || (ch == 0x0A)) {
398             break;
399         }   // we shouldn't need the 0x0A but it makes a nice safely in case someone leaves off the "
400         a.name += ch;
401     }
402     fin.setf(std::ios::skipws);
403     //cout << "Comm name = " << a.name << '\n';
404
405     a.freq = (int)(f*100.0 + 0.5);
406
407     // cout << a.ident << endl;
408
409     // generate cartesian coordinates
410     a.cart = SGVec3d::fromGeod(a.geod);
411     return fin >> skipeol;
412 }
413 #endif