]> git.mxchange.org Git - flightgear.git/blob - utils/fgcom/fgcom.cxx
ATC/Traffic doesn’t crash reset.
[flightgear.git] / utils / fgcom / fgcom.cxx
1 /*
2  * fgcom - VoIP-Client for the FlightGear-Radio-Infrastructure
3  *
4  * This program realizes the usage of the VoIP infractructure based
5  * on flight data which is send from FlightGear with an external
6  * protocol to this application.
7  *
8  * Clement de l'Hamaide - Jan 2014
9  * Re-writting of FGCom standalone
10  *
11  * This program is free software; you can redistribute it and/or
12  * modify it under the terms of the GNU General Public License as
13  * published by the Free Software Foundation; either version 2 of the
14  * License, or (at your option) any later version.
15  * 
16  * This program is distributed in the hope that it will be useful, but
17  * WITHOUT ANY WARRANTY; without even the implied warranty of
18  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
19  * General Public License for more details.
20  * 
21  * You should have received a copy of the GNU General Public License
22  * along with this program; if not, write to the Free Software
23  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
24  * MA  02110-1301, USA.
25  *
26  */
27
28 #ifdef HAVE_CONFIG_H
29 #include "config.h"
30 #endif
31
32 #include <simgear/sg_inlines.h>
33 #include <simgear/math/SGMath.hxx>
34 #include <simgear/io/raw_socket.hxx>
35 #include <simgear/misc/strutils.hxx>
36 #include <simgear/debug/logstream.hxx>
37 #include <simgear/timing/timestamp.hxx>
38
39 #include <3rdparty/iaxclient/lib/iaxclient.h>
40
41 #include "fgcom.hxx"
42 #include "positions.hxx" // provides _positionsData[];
43
44 int         _port           = 16661;
45 int         _callId         = -1;
46 int         _currentFreqKhz = -1;
47 int         _maxRange       = 100;
48 int         _minRange       = 10;
49 int         _registrationId = -1;
50 bool        _libInitialized = false;
51 bool        _running        = true;
52 bool        _debug          = false;
53 bool        _connected      = false;
54 double      _frequency      = -1;
55 double      _atis           = -1;
56 double      _silenceThd     = -35.0;
57 std::string _app            = "FGCOM-";
58 std::string _host           = "127.0.0.1";
59 std::string _server         = "fgcom.flightgear.org";
60 std::string _airport        = "ZZZZ";
61 std::string _callsign       = "guest";
62 std::string _username       = "guest";
63 std::string _password       = "guest";
64
65 SGGeod      _airportPos;
66 SGTimeStamp _p;
67 std::multimap<int, Airport> _airportsData;
68
69 const int special_freq[] = { // Define some freq who need to be used with icao = ZZZZ
70         910000,
71         911000,
72         700000,
73         123450,
74         122750,
75         121500,
76         123500 };
77
78 //
79 // Main loop
80 //
81
82 int main(int argc, char** argv)
83 {
84     signal(SIGINT,  quit);
85     signal(SIGTERM, quit);
86
87     simgear::requestConsole();
88     sglog().setLogLevels(SG_ALL, SG_INFO);
89     _app += FGCOM_VERSION;
90     Modes mode          = PILOT;
91     std::string num     = "";
92
93     for(int count = 1; count < argc; count++) {
94         std::string item = argv[count];
95         std::string option = item.substr(2, item.find("=")-2);
96         std::string value = item.substr(item.find("=")+1, item.size());
97         if(option == "server")             _server        = value;
98         if(option == "host")               _host          = value;
99         if(option == "port")               _port          = atoi(value.c_str());
100         if(option == "callsign")           _callsign      = value;
101         if(option == "frequency")          _frequency     = atof(value.c_str());
102         if(option == "atis")               _atis          = atof(value.c_str());
103         if(option == "airport")            _airport       = simgear::strutils::uppercase(value);
104         if(option == "username")           _username      = value;
105         if(option == "password")           _password      = value;
106         if(option == "silence-threshold")  _silenceThd    = atof(value.c_str());
107         if(option == "debug")               sglog().setLogLevels(SG_ALL, SG_DEBUG);
108         if(option == "help")                return usage();
109         if(option == "version")             return version();
110     }
111
112     if(_frequency == 910.000)
113         mode = TEST;
114     if(_frequency <= 136.975 && _frequency >= 118.000)
115         mode = OBS;
116     if(_atis <= 136.975 && _atis >= 118.000 && _airport != "ZZZZ")
117         mode = ATC;
118
119     SG_LOG(SG_GENERAL, SG_INFO, "FGCom " << FGCOM_VERSION << " compiled " << __DATE__
120                                 << ", at " << __TIME__ );
121     SG_LOG(SG_GENERAL, SG_INFO, "For help usage, use --help");
122     SG_LOG(SG_GENERAL, SG_INFO, "Starting FGCom session as " << _username << ":xxxxxxxxx@" << _server);
123     
124     if( !(_libInitialized = lib_init()) )
125         return EXIT_FAILURE;
126
127     if (_username != "guest" && _password != "guest")
128         _registrationId = lib_registration();
129   
130     if(mode == PILOT) {
131         SG_LOG( SG_GENERAL, SG_DEBUG, "Entering main loop in mode PILOT" );
132
133         simgear::Socket::initSockets();
134         simgear::Socket sgSocket;
135         sgSocket.open(false);
136         sgSocket.bind(_host.c_str(), _port);
137         sgSocket.setBlocking(false);
138         lib_setVolume(0.0, 1.0);
139         static char currentPacket[MAXBUFLEN+2], previousPacket[MAXBUFLEN+2];
140         struct Data currentData, previousData, previousPosData;
141         double currentFreq = -1, previousFreq = -1;
142         std::string currentIcao = "";
143         ActiveComm activeComm = COM1;
144
145         _airportsData = getAirportsData();
146         SG_LOG(SG_GENERAL, SG_INFO, "");
147
148         while(_running) {
149             int bytes = sgSocket.recv(currentPacket, sizeof(currentPacket)-1, 0);
150             if (bytes == -1) {
151                 SGTimeStamp::sleepForMSec(1);  // Prevent full CPU usage (loop)
152                 continue;
153             }
154
155             currentPacket[bytes] = '\0';
156             if( strcmp(currentPacket, previousPacket) != 0 ) {
157                 std::string packet(currentPacket);
158                 std::vector<std::string> properties = simgear::strutils::split(packet, ",");
159                 for(size_t i=0; i < properties.size(); i++) {
160                     std::vector<std::string> prop = simgear::strutils::split(properties[i], "=");
161                     if(prop[0] == "PTT")         currentData.ptt         = atoi(prop[1].c_str());
162                     if(prop[0] == "LAT")         currentData.lat         = atof(prop[1].c_str());
163                     if(prop[0] == "LON")         currentData.lon         = atof(prop[1].c_str());
164                     if(prop[0] == "ALT")         currentData.alt         = atof(prop[1].c_str());
165                     if(prop[0] == "COM1_FRQ")    currentData.com1        = atof(prop[1].c_str());
166                     if(prop[0] == "COM2_FRQ")    currentData.com2        = atof(prop[1].c_str());
167                     if(prop[0] == "OUTPUT_VOL")  currentData.outputVol   = atof(prop[1].c_str());
168                     if(prop[0] == "SILENCE_THD") currentData.silenceThd  = atof(prop[1].c_str());
169                     if(prop[0] == "CALLSIGN")    currentData.callsign    = prop[1];
170                 }
171
172                 if(currentData.ptt != previousData.ptt) {
173                     if(currentData.ptt == 2) {
174                         if(activeComm == COM1) {
175                             activeComm = COM2;
176                             currentFreq = currentData.com2;
177                         } else {
178                             activeComm = COM1;
179                             currentFreq = currentData.com1;
180                         }
181                         SG_LOG( SG_GENERAL, SG_INFO, "Select radio " << activeComm << " on " << currentFreq << " MHz" );
182                     } else if(currentData.ptt) {
183                         SG_LOG( SG_GENERAL, SG_INFO, "[SPEAK] unmute mic, mute speaker" );
184                         lib_setVolume(1.0, 0.0);
185                     } else {
186                         SG_LOG( SG_GENERAL, SG_INFO, "[LISTEN] mute mic, unmute speaker" );
187                         lib_setVolume(0.0, currentData.outputVol);
188                     }
189                 }
190
191                 if(currentData.outputVol != previousData.outputVol)
192                     lib_setVolume(0.0, currentData.outputVol);
193
194                 if(currentData.silenceThd != previousData.silenceThd)
195                     lib_setSilenceThreshold(currentData.silenceThd);
196
197                 if(currentData.callsign != previousData.callsign)
198                     lib_setCallerId(currentData.callsign);
199
200                 if(currentData.com1 != previousData.com1 && activeComm == COM1) {
201                     currentFreq = currentData.com1;
202                     SG_LOG( SG_GENERAL, SG_INFO, "Select frequency " << currentFreq << " MHz on radio " << activeComm );
203                 }
204
205                 if(currentData.com2 != previousData.com2 && activeComm == COM2) {
206                     currentFreq = currentData.com2;
207                     SG_LOG( SG_GENERAL, SG_INFO, "Select frequency " << currentFreq << " MHz on radio " << activeComm );
208                 }
209
210                 if(previousFreq != currentFreq || currentData.callsign != previousData.callsign) {
211                     _currentFreqKhz = 10 * static_cast<int>(currentFreq * 100 + 0.25);
212                     currentIcao = getClosestAirportForFreq(currentFreq, currentData.lat, currentData.lon, currentData.alt);
213
214                     if(isInRange(currentIcao, currentData.lat, currentData.lon, currentData.alt)) {
215                         _connected = lib_call(currentIcao, currentFreq);
216                         SG_LOG( SG_GENERAL, SG_INFO, "Connecting " << currentIcao << " on " << currentFreq << " MHz" );
217                     } else {
218                         if(_connected) {
219                             _connected = lib_hangup();
220                             SG_LOG( SG_GENERAL, SG_INFO, "Disconnecting " << currentIcao << " on " << currentFreq << " MHz (out of range)" );
221                         }
222                     }
223                 }
224
225                 if( currentData.lat <= previousPosData.lat - 0.05  ||
226                     currentData.lon <= previousPosData.lon - 0.05  ||
227                     currentData.alt <= previousPosData.alt - 50.0  ||
228                     currentData.lat >= previousPosData.lat + 0.05  ||
229                     currentData.lon >= previousPosData.lon + 0.05  ||
230                     currentData.alt >= previousPosData.alt + 50.0) {
231
232                     currentIcao = getClosestAirportForFreq(currentFreq, currentData.lat, currentData.lon, currentData.alt);
233                     if(_connected) {
234                         if(!isInRange(currentIcao, currentData.lat, currentData.lon, currentData.alt)) {
235                             _connected = lib_hangup();
236                             SG_LOG( SG_GENERAL, SG_INFO, "Disconnecting " << currentIcao << " on " << currentFreq << " MHz (out of range)" );
237                         }
238                     } else {
239                         if(isInRange(currentIcao, currentData.lat, currentData.lon, currentData.alt)) {
240                             _connected = lib_call(currentIcao, currentFreq);
241                             SG_LOG( SG_GENERAL, SG_INFO, "Connecting " << currentIcao << " on " << currentFreq << " MHz" );
242                         }
243                     }
244                     previousPosData = currentData;
245                 }
246                 previousFreq = currentFreq;
247                 previousData = currentData;
248             }
249             strcpy(previousPacket, currentPacket);
250         } // while()
251         sgSocket.close();
252     } else { // if(mode == PILOT)
253         int sessionDuration = 1000;
254         _p.stamp();
255         if(mode == OBS) {
256             SG_LOG( SG_GENERAL, SG_DEBUG, "Entering main loop in mode OBS (max duration: 6 hours)" );
257             sessionDuration *= 2160; // 6 hours for OBS mode
258             lib_setVolume(0.0, 1.0);
259             lib_setCallerId("::OBS::");
260             num = computePhoneNumber(_frequency, _airport);
261         } else {
262             lib_setVolume(1.0, 1.0);
263             if(mode == TEST) {
264                 sessionDuration *= 65; // 65 seconds for TEST mode
265                 SG_LOG( SG_GENERAL, SG_DEBUG, "Entering main loop in mode TEST (max duration: 65 seconds)" );
266                 _airport = "ZZZZ";
267                 num = computePhoneNumber(_frequency, _airport);
268             } else if(mode == ATC) {
269                 sessionDuration *= 45; // 45 seconds for ATC mode
270                 SG_LOG( SG_GENERAL, SG_DEBUG, "Entering main loop in mode ATC (max duration: 45 seconds)" );
271                 num = computePhoneNumber(_atis, _airport, true);
272             }
273         }
274         _connected = lib_directCall(_airport, _frequency, num);
275
276         while (_p.elapsedMSec() <= sessionDuration && _running){
277             SGTimeStamp::sleepForMSec(2000);
278         }
279     }
280
281     if(!lib_shutdown())
282         return EXIT_FAILURE;
283     return EXIT_SUCCESS;
284 }
285
286 // function: getAirportsData
287 // action: parse positionsData.hxx then build multimap
288
289 std::multimap<int, Airport> getAirportsData()
290 {
291     std::vector<std::string> lines;
292     std::multimap<int, Airport> aptData;
293     SG_LOG(SG_GENERAL, SG_INFO, "Loading airports information...");
294
295     for(size_t i=0; i < sizeof(_positionsData)/sizeof(*_positionsData); i++) { // _positionsData is provided by positions.hxx
296         std::vector<std::string> entries = simgear::strutils::split(_positionsData[i], ",");
297         if(entries.size() == 6) {
298             // [0]=ICAO, [1]=Frequency, [2]=Latitude, [3]=Longitude, [4]=ID/Type, [5]=Name
299             std::string entryIcao  = entries[0];
300             double      entryFreq  = atof(entries[1].c_str());
301             double      entryLat   = atof(entries[2].c_str());
302             double      entryLon   = atof(entries[3].c_str());
303             std::string entryType  = entries[4];
304             std::string entryName  = entries[5];
305
306             int aptFreqKhz = 10 * static_cast<int>(entryFreq * 100 + 0.25);
307             Airport apt;
308                     apt.icao       = entryIcao;
309                     apt.frequency  = entryFreq;
310                     apt.latitude   = entryLat;
311                     apt.longitude  = entryLon;
312                     apt.type       = entryType;
313                     apt.name       = entryName;
314             aptData.insert( std::pair<int, Airport>(aptFreqKhz, apt) );
315         }
316     }
317     return aptData;
318 }
319
320 // function: orderByDistanceNm
321 // action: sort airportsInRange vector by distanceNm ASC in getClosestAirportForFreq()
322
323 bool orderByDistanceNm(Airport a, Airport b)
324 {
325     return a.distanceNm < b.distanceNm;
326 }
327
328 // function: gestClosestAircraftForFreq
329 // action: return ICAO of closest airport with given frequency and define his position
330
331 std::string getClosestAirportForFreq(double freq, double acftLat, double acftLon, double acftAlt)
332 {
333     for(size_t i=0; i<sizeof(special_freq)/sizeof(special_freq[0]); i++) { // Check if it's a special freq
334       if( special_freq[i] == _currentFreqKhz )
335         return std::string("ZZZZ");
336     }
337
338     std::string icao = "";
339     double aptLon    = 0;
340     double aptLat    = 0;
341     int freqKhz      = 10 * static_cast<int>(freq * 100 + 0.25);
342     SGGeod acftPos   = SGGeod::fromDegFt(acftLon, acftLat, acftAlt);
343     std::vector<Airport> airportsInRange;
344
345     std::pair <std::multimap<int, Airport>::iterator, std::multimap<int, Airport>::iterator> ret;
346     ret = _airportsData.equal_range(freqKhz);
347     for (std::multimap<int, Airport>::iterator it=ret.first; it!=ret.second; ++it) {
348         SGGeod aptPos = SGGeod::fromDeg(it->second.longitude, it->second.latitude);
349         double distNm = SGGeodesy::distanceNm(aptPos, acftPos);
350         if(distNm <= _maxRange){
351             it->second.distanceNm = distNm;
352             airportsInRange.push_back(it->second);
353         }
354     }
355
356     if(!airportsInRange.size())
357         return icao;
358
359     std::sort(airportsInRange.begin(), airportsInRange.end(), orderByDistanceNm);
360
361     aptLon      = airportsInRange[0].longitude;
362     aptLat      = airportsInRange[0].latitude;
363     icao        = airportsInRange[0].icao;
364     _airportPos = SGGeod::fromDeg(aptLon, aptLat);
365
366     SG_LOG(SG_GENERAL, SG_INFO, "Airport " << airportsInRange[0].icao << " " << airportsInRange[0].name << " - "
367                                 << airportsInRange[0].type << " on " << airportsInRange[0].frequency
368                                 << " - is in range " << airportsInRange[0].distanceNm << "nm ("
369                                 << (SG_NM_TO_METER*airportsInRange[0].distanceNm)/1000 <<"km)");
370     return icao;
371 }
372
373 // function: isInRange
374 // action: return TRUE if airport/freq is in range, else return FALSE
375
376 bool isInRange(std::string icao, double acftLat, double acftLon, double acftAlt)
377 {
378     for(size_t i=0; i<sizeof(special_freq)/sizeof(special_freq[0]); i++) { // Check if it's a special freq
379       if( special_freq[i] == _currentFreqKhz )
380         return true;
381     }
382
383     if(icao.empty())
384         return false;
385
386     SGGeod acftPos = SGGeod::fromDegFt(acftLon, acftLat, acftAlt);
387     double distNm = SGGeodesy::distanceNm(_airportPos, acftPos);
388     double delta_elevation_ft = fabs(acftPos.getElevationFt() - _airportPos.getElevationFt());
389     double rangeNm = 1.23 * sqrt(delta_elevation_ft);
390
391     if (rangeNm > _maxRange) rangeNm = _maxRange;
392     if (rangeNm < _minRange) rangeNm = _minRange;
393     if( distNm > rangeNm )   return false;
394     return true;
395 }
396
397 // function: quit
398 // action: set _running flag to false
399
400 void quit(int state)
401 {
402     SG_LOG( SG_GENERAL, SG_INFO, "Exiting FGCom" );
403     _running = false;
404 #ifdef _WIN32
405     lib_shutdown();
406     SG_LOG(SG_GENERAL, SG_INFO, "You can close the terminal now");
407 #endif
408 }
409
410 // function: usage
411 // action: display FGCom usage then quit
412
413 int usage()
414 {
415     std::cout << "FGCom " << FGCOM_VERSION << " usage:" << std::endl;
416     std::cout << "        --server=fgcom.flightgear.org   -  Server to connect" << std::endl;
417     std::cout << "        --host=127.0.0.1                -  Host to listen i.e where FG is running" << std::endl;
418     std::cout << "        --port=16661                    -  Port to use" << std::endl;
419     std::cout << "        --callsign=guest                -  Callsign during session e.g F-ELYD" << std::endl;
420     std::cout << "        --frequency=xxx.xxx             -  Frequency e.g 120.500" << std::endl;
421     std::cout << "        --airport=YYYY                  -  ICAO of airport e.g KSFO" << std::endl;
422     std::cout << "        --username=guest                -  Username for registration" << std::endl;
423     std::cout << "        --password=guest                -  Password for registration" << std::endl;
424     std::cout << "        --silence-threshold=-35         -  Silence threshold in dB (-60 < range < 0 )" << std::endl;
425     std::cout << "        --debug                         -  Enable debug output" << std::endl;
426     std::cout << "        --help                          -  Show this message" << std::endl;
427     std::cout << "        --version                       -  Show version" << std::endl;
428     std::cout << "" << std::endl;
429     std::cout << "  None of these options are required, you can simply start FGCom without option at all: it works" << std::endl;
430     std::cout << "  For further information, please visit: http://wiki.flightgear.org/FGCom_3.0" << std::endl;
431     std::cout << "" << std::endl;
432     std::cout << "  About silence-threshold:" << std::endl;
433     std::cout << "    This is the limit, in dB, when FGCom consider no voice in your microphone." << std::endl;
434     std::cout << "    --silence-threshold=-60 is similar to micro always ON" << std::endl;
435     std::cout << "    --silence-threshold=0 is similar to micro always OFF" << std::endl;
436     std::cout << "    Default value is -35.0 dB" << std::endl;
437     std::cout << "" << std::endl;
438     std::cout << "  In order to make an echo-test, you have to start FGCom like:" << std::endl;
439     std::cout << "    fgcom --frequency=910" << std::endl;
440     std::cout << "" << std::endl;
441     std::cout << "  In order to listen a frequency, you have to start FGCom like:" << std::endl;
442     std::cout << "    fgcom --frequency=xxx.xxx --airport=YYYY" << std::endl;
443     std::cout << "    where xxx.xxx is the frequency of the ICAO airport YYYY that you want to listen to" << std::endl;
444     std::cout << "" << std::endl;
445     std::cout << "  In order to record an ATIS message, you have to start FGCom like:" << std::endl;
446     std::cout << "    fgcom --atis=xxx.xxx --airport=YYYY" << std::endl;
447     std::cout << "    where xxx.xxx is the ATIS frequency of the ICAO airport YYYY" << std::endl;
448     std::cout << "" << std::endl;
449     return EXIT_SUCCESS;
450 }
451
452 // function: version
453 // action: display FGCom version then quit
454
455 int version()
456 {
457     SG_LOG(SG_GENERAL, SG_INFO, "FGCom " << FGCOM_VERSION << " compiled " << __DATE__
458                                 << ", at " << __TIME__ );
459     std::cout << "" << std::endl;
460     return EXIT_SUCCESS;
461 }
462
463 // function: computePhoneNumber
464 // action: return phone number
465
466 std::string computePhoneNumber(double freq, std::string icao, bool atis)
467 {
468     if(icao.empty())
469         return std::string(); 
470
471     char phoneNumber[256];
472     char exten[32];
473     char tmp[5];
474     int  prefix = atis ? 99 : 01;
475
476     sprintf( tmp, "%4s", icao.c_str() );
477
478     sprintf( exten,
479              "%02d%02d%02d%02d%02d%06d",
480              prefix,
481              tmp[0],
482              tmp[1],
483              tmp[2],
484              tmp[3],
485              (int) (freq * 1000 + 0.5) );
486     exten[16] = '\0';
487
488     snprintf( phoneNumber,
489               sizeof(phoneNumber),
490               "%s:%s@%s/%s",
491               _username.c_str(),
492               _password.c_str(),
493               _server.c_str(),
494               exten);
495     return phoneNumber;
496 }
497
498 // function: lib_setVolume
499 // action: set input/output volume
500
501 void lib_setVolume(double input, double output)
502 {
503     SG_CLAMP_RANGE<double>(input, 0.0, 1.0);
504     SG_CLAMP_RANGE<double>(output, 0.0, 1.0);
505     SG_LOG(SG_GENERAL, SG_DEBUG, "Set volume input=" << input << " , output=" << output);
506     iaxc_input_level_set(input);
507     iaxc_output_level_set(output);
508 }
509
510 // function: lib_setSilenceThreshold
511 // action: set silence threshold
512
513 void lib_setSilenceThreshold(double thd)
514 {
515     SG_CLAMP_RANGE<double>(thd, -60, 0);
516     SG_LOG(SG_GENERAL, SG_DEBUG, "Set silence threshold=" << thd);
517     iaxc_set_silence_threshold(thd);
518 }
519
520 // function: lib_setCallerId
521 // action: set caller id for the session
522
523 void lib_setCallerId(std::string callsign)
524 {
525     SG_LOG(SG_GENERAL, SG_DEBUG, "Set caller ID=" << callsign);
526     iaxc_set_callerid (callsign.c_str(), _app.c_str());
527 }
528
529 // function: lib_init
530 // action: init the library
531
532 bool lib_init()
533 {
534     SG_LOG(SG_GENERAL, SG_DEBUG, "Initializing IAX library");
535 #ifdef _MSC_VER
536     iaxc_set_networking( (iaxc_sendto_t)sendto, (iaxc_recvfrom_t)recvfrom );
537 #endif
538     if (iaxc_initialize(4)) {
539         SG_LOG( SG_GENERAL, SG_ALERT, "Error: cannot initialize IAXClient !\nHINT: Have you checked the mic and speakers ?" );
540         return false;
541     }
542
543     iaxc_set_callerid( _callsign.c_str(), _app.c_str() );
544     iaxc_set_formats(IAXC_FORMAT_SPEEX, IAXC_FORMAT_ULAW|IAXC_FORMAT_SPEEX);
545     iaxc_set_speex_settings(1, 5, 0, 1, 0, 3);
546     iaxc_set_filters(IAXC_FILTER_AGC | IAXC_FILTER_DENOISE);
547     iaxc_set_event_callback(iaxc_callback);
548     iaxc_start_processing_thread ();
549     lib_setSilenceThreshold(_silenceThd);
550     return true;
551 }
552
553 // function: lib_shutdown
554 // action: stop the library
555
556 bool lib_shutdown()
557 {
558     SG_LOG(SG_GENERAL, SG_DEBUG, "Shutdown IAX library");
559     lib_hangup();
560     if(_registrationId != -1)
561         iaxc_unregister(_registrationId);
562     return true;
563 }
564
565 // function: lib_call
566 // action: register a user on remote server then return the registration ID
567
568 int lib_registration()
569 {
570     SG_LOG(SG_GENERAL, SG_DEBUG, "Request registration");
571     SG_LOG(SG_GENERAL, SG_DEBUG, "           username: " << _username);
572     SG_LOG(SG_GENERAL, SG_DEBUG, "           password: xxxxxxxx");
573     SG_LOG(SG_GENERAL, SG_DEBUG, "           server:   " << _server);
574     int regId = iaxc_register( _username.c_str(), _password.c_str(), _server.c_str());
575     if(regId == -1) {
576         SG_LOG( SG_GENERAL, SG_ALERT, "Warning: cannot register '" << _username << "' at '" << _server );
577     }
578     return regId;
579 }
580
581 // function: lib_call
582 // action: kill current call then do a new call
583
584 bool lib_call(std::string icao, double freq)
585 {
586     SG_LOG(SG_GENERAL, SG_DEBUG, "Request new call");
587     SG_LOG(SG_GENERAL, SG_DEBUG, "           icao: " << icao);
588     SG_LOG(SG_GENERAL, SG_DEBUG, "           freq: " << freq);
589     lib_hangup();
590     iaxc_millisleep(300);
591     std::string num = computePhoneNumber(freq, icao);
592     if(num.empty())
593         return false;
594     _callId = iaxc_call(num.c_str());
595     if(_callId == -1) {
596         SG_LOG( SG_GENERAL, SG_ALERT, "Warning: cannot call: " << num );
597         return false;
598     }
599     return true;
600 }
601
602 bool lib_directCall(std::string icao, double freq, std::string num)
603 {
604     SG_LOG(SG_GENERAL, SG_DEBUG, "Request new call");
605     SG_LOG(SG_GENERAL, SG_DEBUG, "           icao: " << icao);
606     SG_LOG(SG_GENERAL, SG_DEBUG, "           freq: " << freq);
607     lib_hangup();
608     iaxc_millisleep(300);
609     if(num.empty())
610         return false;
611     _callId = iaxc_call(num.c_str());
612     if(_callId == -1) {
613         SG_LOG( SG_GENERAL, SG_ALERT, "Warning: cannot call: " << num );
614         return false;
615     }
616     return true;
617 }
618
619 // function: lib_hangup
620 // action: kill current call
621
622 bool lib_hangup()
623 {
624     if(!_connected)
625         return false;
626     SG_LOG(SG_GENERAL, SG_DEBUG, "Request hangup");
627     iaxc_dump_all_calls();
628     _callId = -1;
629     return false;
630 }
631
632 // function: iaxc_callback
633 // action: parse IAX event then call event handler
634
635 int iaxc_callback(iaxc_event e)
636 {
637     switch (e.type) {
638         case IAXC_EVENT_TEXT:
639             if(e.ev.text.type == IAXC_TEXT_TYPE_STATUS ||
640                e.ev.text.type == IAXC_TEXT_TYPE_IAX)
641                    SG_LOG( SG_GENERAL, SG_INFO, "Message: " << e.ev.text.message );
642             break;
643     }
644     return 1;
645 }
646 // eof