]> git.mxchange.org Git - flightgear.git/blob - utils/fgcom/fgcom.cxx
FGCom-sa: remove commented lines
[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  * For more information read: http://squonk.abacab.org/dokuwiki/fgcom
9  *
10  * (c) H. Wirtz <dcoredump@gmail.com>
11  *
12  * This program is free software; you can redistribute it and/or
13  * modify it under the terms of the GNU General Public License as
14  * published by the Free Software Foundation; either version 2 of the
15  * License, or (at your option) any later version.
16  * 
17  * This program is distributed in the hope that it will be useful, but
18  * WITHOUT ANY WARRANTY; without even the implied warranty of
19  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
20  * General Public License for more details.
21  * 
22  * You should have received a copy of the GNU General Public License
23  * along with this program; if not, write to the Free Software
24  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
25  * MA  02110-1301, USA.
26  *
27  */
28
29 #ifdef HAVE_CONFIG_H
30 #include "config.h"
31 #endif
32
33 #include <simgear/debug/logstream.hxx>
34 #include <simgear/io/raw_socket.hxx>
35 #include <simgear/timing/timestamp.hxx>
36
37 #include "fgcom.hxx"
38 #include "fgcom_init.hxx"
39 #include "utils.hxx"
40 #include "version.h"
41
42 /* Global variables */
43 int exitcode = 0;
44 int initialized = 0;
45 int connected = 0;
46 int reg_id;
47 const char* dialstring;
48 char airport[5];
49 int codec = DEFAULT_IAX_CODEC;
50 simgear::Socket sgSocket;
51
52 /* Variables declared as static */
53 static double selected_frequency = 0.0;
54 static char mode = 0;
55 static char rep_buf[MX_REPORT_BUF+2]; /* report output buffer - used on iax callback thread - note +2 to ensure null termination */
56 static char num_buf[1024];            /* number generation buffer - used on main thread */
57 static char states[256];              /* buffer to hold ascii states */
58 static char delim = '\t';             /* output field delimiter */
59 static int last_state = 0;            /* previous state of the channel */
60
61 static const char *map[] = {
62   "unknown", "active", "outgoing", "ringing", "complete", "selected",
63   "busy", "transfer", NULL
64 };
65
66 static const char *radio_map[] = {"COM1", "COM2"};
67
68 char icao[5];
69 double special_frq[] = {
70     999.999,
71     911.000,
72     910.000,
73     123.450,
74     122.750,
75     123.500,
76     121.500,
77     732.340,
78      -1.0 };
79 double *special_frequencies;
80
81 double previous_com_frequency = 0.0;
82 int previous_ptt = 0;
83 float previous_vol = 0.0;
84 int com_select = 0;
85 int max_com_instruments = 2;
86 struct airport *airportlist;
87 struct fgdata data;
88 char *prog; //program name
89
90 /* configuration values */
91 static bool debug = false;
92 static int port;
93 static const char *voipserver;
94 static const char *fgserver;
95 static const char *airport_option;
96 static double frequency = -1.0;
97 static const char *audio_in;
98 static const char *audio_out;
99 static double level_in = 0.7;
100 static double level_out = 0.7;
101 static bool mic_boost;
102 static char codec_option;
103 static const char *callsign;
104 static const char *username;
105 static const char *password;
106 static bool list_audio;
107 static char *positions_file;
108 static char *frequency_file;
109
110 static const OptionEntry fgcomOptionArray[] = {
111   {"debug", 'd', false, OPTION_NONE, &debug, 0, "show debugging information",
112    0},
113   {"voipserver", 'S', true, OPTION_STRING, &voipserver, 0,
114    "voip server to connect to", &DEFAULT_VOIP_SERVER},
115   {"fgserver", 's', true, OPTION_STRING, &fgserver, 0, "fg to connect to ",
116    &DEFAULT_FG_SERVER},
117   {"port", 'p', true, OPTION_INT, &port, 0, "where we should listen to FG", &port},     // hm, good idea? don't think so...
118   {"airport", 'a', true, OPTION_STRING, &airport_option, 0,
119    "airport-id (ICAO) for ATC-mode", 0},
120   {"frequency", 'f', true, OPTION_DOUBLE, &frequency, 0,
121    "frequency for ATC-mode", 0},
122   {"callsign", 'C', true, OPTION_STRING, &callsign, 0,
123    "callsign to use", &DEFAULT_USER},
124   {"user", 'U', true, OPTION_STRING, &username, 0,
125    "username for VoIP account", &DEFAULT_USER},
126   {"password", 'P', true, OPTION_STRING, &password, 0,
127    "password for VoIP account", &DEFAULT_PASSWORD},
128   {"mic", 'i', true, OPTION_DOUBLE, &level_in, 0,
129    "mic input level (0.0 - 1.0)", 0},
130   {"speaker", 'o', true, OPTION_DOUBLE, &level_out, 0,
131    "speaker output level (0.0 - 1.0)", 0},
132   {"mic-boost", 'b', false, OPTION_BOOL, &mic_boost, 0, "enable mic boost",
133    0},
134   {"list-audio", 'l', false, OPTION_BOOL, &list_audio, 0,
135    "list audio devices", 0},
136   {"set-audio-in", 'r', true, OPTION_STRING, &audio_in, 0,
137    "use <devicename> as audio input", 0},
138   {"set-audio-out", 'k', true, OPTION_STRING, &audio_out, 0,
139    "use <devicename> as audio output", 0},
140   {"codec", 'c', true, OPTION_CHAR, &codec_option, 0,
141    "use codec <codec> as transfer codec", &codec_option},
142   {"positions", 'T', true, OPTION_STRING, &positions_file, 0,
143    "location positions file", &DEFAULT_POSITIONS_FILE},
144   {"special", 'Q', true, OPTION_STRING, &frequency_file, 0,
145    "location spl. frequency file (opt)", &SPECIAL_FREQUENCIES_FILE},
146   {NULL}
147 };
148
149 void
150 process_packet (char *buf)
151 {
152
153   /* cut off ending \n */
154   buf[strlen (buf) - 1] = '\0';
155
156   /* parse the data into a struct */
157   parse_fgdata (&data, buf);
158
159   /* get the selected frequency */
160   if (com_select == 0 && data.COM1_SRV == 1)
161     selected_frequency = data.COM1_FRQ;
162   else if (com_select == 1 && data.COM2_SRV == 1)
163     selected_frequency = data.COM2_FRQ;
164
165   /* Check for com frequency changes */
166   if (previous_com_frequency != selected_frequency)
167     {
168       SG_LOG( SG_GENERAL, SG_ALERT, "Selected frequency: " << selected_frequency );
169
170       /* remark the new frequency */
171       previous_com_frequency = selected_frequency;
172
173       if (connected == 1)
174         {
175           /* hangup call, if connected */
176           iaxc_dump_call ();
177           iaxc_millisleep (5 * DEFAULT_MILLISLEEP);
178           connected = 0;
179         }
180
181       strcpy (icao,
182               icaobypos (airportlist, selected_frequency, data.LAT,
183                          data.LON, DEFAULT_RANGE));
184       icao2number (icao, selected_frequency, num_buf);
185
186       SG_LOG( SG_GENERAL, SG_DEBUG, "Dialing " << icao << " " << selected_frequency << " MHz: " << num_buf );
187       do_iaxc_call (username, password, voipserver, num_buf);
188
189       connected = 1;
190     }
191
192   /* Check for pressed PTT key */
193   if (previous_ptt != data.PTT)
194     {
195       if (data.PTT == 2)
196         {
197           /* select the next com equipment */
198           com_select = (com_select + 1) % 2;
199           SG_LOG( SG_GENERAL, SG_ALERT, "Radio selected is " << radio_map[com_select] );
200         }
201       else if (connected == 1)
202         {
203           ptt (data.PTT);
204         }
205       previous_ptt = data.PTT;
206     }
207
208   /* Check for output volume change */
209   if (previous_vol != data.OUTPUT_VOL)
210     {
211       SG_LOG( SG_GENERAL, SG_ALERT, "Set speaker volume to " << data.OUTPUT_VOL );
212
213       iaxc_output_level_set( data.OUTPUT_VOL );
214       previous_vol = data.OUTPUT_VOL;
215     }
216
217   /* Check for callsign change */
218   if (strcmp(callsign, data.CALLSIGN) != 0)
219     {
220       iaxc_dump_call ();
221       callsign = data.CALLSIGN;
222       SG_LOG( SG_GENERAL, SG_ALERT, "FGCom will restart now with callsign " << callsign );
223       std::string app = "FGCOM-";
224       app += FGCOM_VERSION;
225       iaxc_set_callerid ( callsign, app.c_str() );
226     }
227 }
228
229 static char *base_name( char *name )
230 {
231     char *bn = name;
232     size_t len = strlen(name);
233     size_t i;
234     int c;
235     for ( i = 0; i < len; i++ ) {
236         c = name[i];
237         if (( c == '/' ) || ( c == '\\'))
238             bn = &name[i+1];
239     }
240     return bn;
241 }
242
243 // if default FAILS, for OSX and WIN try EXE path
244 int fix_input_files()
245 {
246     int iret = 0;
247     char *def_freq = (char *) SPECIAL_FREQUENCIES_FILE;
248     char *def_pos  = (char *) DEFAULT_POSITIONS_FILE;
249     char exepath[MX_PATH_SIZE+2];
250     exepath[0] = 0;
251     if (strcmp( frequency_file,def_freq) == 0) {
252         /* ok is default value - do some fixes */
253         if (is_file_or_directory( frequency_file ) != 1) {
254             exepath[0] = 0;
255             iret |= get_data_path_per_os( exepath, MX_PATH_SIZE );
256             strcat(exepath,def_freq);
257             frequency_file = strdup(exepath);
258         }
259     }
260     if (strcmp( positions_file,def_pos) == 0) {
261         // if default FAILS, for OSX and WIN try EXE path
262         if (is_file_or_directory( positions_file ) != 1) {
263             exepath[0] = 0;
264             iret |= get_data_path_per_os( exepath, MX_PATH_SIZE );
265             strcat(exepath,def_pos);
266             positions_file = strdup(exepath);
267         }
268     }
269     return iret;
270 }
271
272 int
273 main (int argc, char *argv[])
274 {
275   int numbytes;
276   static char pkt_buf[MAXBUFLEN+2];
277
278   sglog().setLogLevels( SG_ALL, SG_ALERT );
279
280   prog = strdup( base_name(argv[0]) );
281   
282   /* program header */
283   SG_LOG( SG_GENERAL, SG_ALERT, prog << " - a communication radio based on VoIP with IAX/Asterisk" );
284   SG_LOG( SG_GENERAL, SG_ALERT, "Original (c) 2007-2011 by H. Wirtz <wirtz@dfn.de>" );
285   SG_LOG( SG_GENERAL, SG_ALERT, "OSX and Windows ports 2012-2013 by Yves Sablonier and Geoff R. McLane, resp." );
286   SG_LOG( SG_GENERAL, SG_ALERT, "Version " << FGCOM_VERSION << " compiled " << __DATE__ << ", at " << __TIME__ );
287   SG_LOG( SG_GENERAL, SG_ALERT, "Using iaxclient library Version " << iaxc_version (pkt_buf) );
288   SG_LOG( SG_GENERAL, SG_ALERT, "" );
289
290   /* init values */
291   voipserver = DEFAULT_VOIP_SERVER;
292   fgserver = DEFAULT_FG_SERVER;
293   port = DEFAULT_FG_PORT;
294   username = DEFAULT_USER;
295   password = DEFAULT_PASSWORD;
296   codec_option = DEFAULT_CODEC;
297   mode = 0; // 0 = ATC mode, 1 = FG mode
298   positions_file = (char *) DEFAULT_POSITIONS_FILE;
299   frequency_file = (char *) SPECIAL_FREQUENCIES_FILE;
300
301 #ifndef _WIN32
302   /* catch signals */
303   signal (SIGINT, quit);
304   signal (SIGQUIT, quit);
305   signal (SIGTERM, quit);
306 #endif
307
308   /* setup iax */
309 #ifdef _MSC_VER
310   /* MSVC only - In certain circumstances the addresses placed in iaxc_sendto and iaxc_recvfrom 
311      can be an offset to a jump table, making a compare of the current address to the address of 
312      the actual imported function fail. So here ensure they are the same. */
313   iaxc_set_networking( (iaxc_sendto_t)sendto, (iaxc_recvfrom_t)recvfrom );
314 #endif // _MSC_VER
315
316   if (iaxc_initialize (DEFAULT_MAX_CALLS))
317     fatal_error ("cannot initialize iaxclient!\nHINT: Have you checked the mic and speakers?");
318
319   initialized = 1;
320
321   // option parser
322   fgcomInitOptions (fgcomOptionArray, argc, argv);
323
324   if( debug )
325     sglog().setLogLevels( SG_ALL, SG_DEBUG );
326
327   // codec
328   if (codec_option)
329     {
330       switch (codec_option)
331         {
332         case 'u':
333           codec = IAXC_FORMAT_ULAW;
334           break;
335         case 'a':
336           codec = IAXC_FORMAT_ALAW;
337           break;
338         case 'g':
339           codec = IAXC_FORMAT_GSM;
340           break;
341         case '7':
342           codec = IAXC_FORMAT_G726;
343           break;
344         case 's':
345           codec = IAXC_FORMAT_SPEEX;
346           break;
347         }
348     }
349
350   // airport
351   if (airport_option)
352     {
353       strtoupper (airport_option, airport, sizeof (airport));
354     }
355
356   // input level
357   if (level_in > 1.0)
358     {
359       level_in = 1.0;
360     }
361   if (level_in < 0.0)
362     {
363       level_in = 0.0;
364     }
365
366   // output level
367   if (level_out > 1.0)
368     {
369       level_out = 1.0;
370     }
371   if (level_out < 0.0)
372     {
373       level_out = 0.0;
374     }
375
376   // microphone boost
377   if (mic_boost)
378     {
379       iaxc_mic_boost_set (1);
380     }
381
382   if (list_audio)
383     {
384       SG_LOG( SG_GENERAL, SG_ALERT, "Input audio devices:" );
385       SG_LOG( SG_GENERAL, SG_ALERT, report_devices(IAXC_AD_INPUT) );
386
387       SG_LOG( SG_GENERAL, SG_ALERT, "Output audio devices:" );
388       SG_LOG( SG_GENERAL, SG_ALERT, report_devices(IAXC_AD_OUTPUT) );
389
390       iaxc_shutdown ();
391       exit (1);
392     }
393
394
395   if (audio_in)
396     {
397       set_device (audio_in, 0);
398     }
399
400   if (audio_out)
401     {
402       set_device (audio_out, 1);
403     }
404
405   /* checking consistency of arguments */
406   if (frequency > 0.0 && frequency < 1000.0)
407     {
408       if (strlen (airport) == 0 || strlen (airport) > 4)
409         {
410           strcpy (airport, "ZZZZ");
411         }
412       /* airport and frequency are given => ATC mode */
413       mode = 0;
414     }
415   else
416     {
417       /* no airport => FG mode */
418       mode = 1;
419     }
420
421         /* Read special frequencies file (if exists).
422          * If no file $(INSTALL_DIR)/special_frequencies.txt exists, then default frequencies
423          * are used and are hard coded.
424          */
425
426     if (fix_input_files()) { /* adjust default input per OS */
427         fatal_error ("cannot adjust default input files per OS!\nHINT: Maybe recompile with larger buffer.");
428     }
429
430         if((special_frequencies = read_special_frequencies(frequency_file)) == 0) {
431         SG_LOG( SG_GENERAL, SG_ALERT, "Failed to load file [" << frequency_file << "] !" );
432         SG_LOG( SG_GENERAL, SG_ALERT, "Using internal defaults" );
433         special_frequencies = special_frq;
434     } else {
435         SG_LOG( SG_GENERAL, SG_ALERT, "Loaded file [" << frequency_file << "]." );
436     }
437
438         /* read airport frequencies and positions */
439         airportlist = read_airports (positions_file);   /* never returns if fail! */
440
441   /* preconfigure iax */
442   SG_LOG( SG_GENERAL, SG_ALERT, "Initializing IAX client as " << username << ":" << "xxxxxxxxxxx@" << voipserver );
443
444   std::string app = "FGCOM-";
445   app += FGCOM_VERSION;
446
447   if( !callsign )
448     callsign = DEFAULT_USER;
449
450   iaxc_set_callerid ( callsign, app.c_str() );
451   iaxc_set_formats (IAXC_FORMAT_SPEEX, IAXC_FORMAT_SPEEX);
452   iaxc_set_speex_settings(1, 5, 0, 1, 0, 3);
453   iaxc_set_event_callback (iaxc_callback);
454
455   iaxc_start_processing_thread ();
456
457   if (username && password && voipserver)
458     {
459       reg_id =
460         iaxc_register (const_cast < char *>(username),
461                        const_cast < char *>(password),
462                        const_cast < char *>(voipserver));
463       SG_LOG( SG_GENERAL, SG_DEBUG, "Registered as '" << username << "' at '" << voipserver );
464     }
465   else
466     {
467       SG_LOG( SG_GENERAL, SG_ALERT, "Failed iaxc_register !" );
468       SG_LOG( SG_GENERAL, SG_ALERT, "HINT: Check username, passwordd and address of server" );
469       exitcode = 130;
470       quit (0);
471     }
472
473   iaxc_millisleep (DEFAULT_MILLISLEEP);
474
475   /* main loop */
476   if (mode == 1)
477     {
478       SG_LOG( SG_GENERAL, SG_DEBUG, "Entering main loop in mode FGFS" );
479       /* only in FG mode */
480       simgear::Socket::initSockets();
481       sgSocket.open (false);
482       sgSocket.bind (fgserver, port);
483
484       /* mute mic, speaker on */
485       iaxc_input_level_set (0);
486       iaxc_output_level_set (level_out);
487
488       SGTimeStamp sg_clock;
489       sg_clock.stamp();
490
491       double sg_next_update = sg_clock.toSecs() + DEFAULT_ALARM_TIMER;
492       /* get data from flightgear */
493       while (1)
494         {
495           sg_clock.stamp();
496           double sg_wait = sg_next_update - sg_clock.toSecs();
497           if (sg_wait > 0.001)
498             {
499              simgear::Socket *readSockets[2] = { &sgSocket, 0 };
500              if (sgSocket.select (readSockets, readSockets + 1,
501                                    (int) (sg_wait * 1000)) == 1)
502                 {
503                   simgear::IPAddress their_addr;
504                   numbytes = sgSocket.recvfrom(pkt_buf, MAXBUFLEN - 1, 0, &their_addr);
505                   if (numbytes == -1)
506                     {
507                       perror ("recvfrom");
508                       exit (1);
509                     }
510                   pkt_buf[numbytes] = '\0';
511                   SG_LOG( SG_GENERAL, SG_DEBUG, "Got packet from " << their_addr.getHost () << ":" << their_addr.getPort () );
512                   SG_LOG( SG_GENERAL, SG_DEBUG, "Packet is " << numbytes << " bytes long" );
513                   SG_LOG( SG_GENERAL, SG_DEBUG, "Packet contains \"" << pkt_buf << "\"" );
514                   process_packet (pkt_buf);
515                 }
516             }
517           else
518             {
519               alarm_handler (0);
520               sg_clock.stamp();
521               sg_next_update = sg_clock.toSecs() + DEFAULT_ALARM_TIMER;
522             }
523         }
524     }
525   else
526     {
527       SG_LOG( SG_GENERAL, SG_DEBUG, "Entering main loop in mode ATC" );
528       /* mic on, speaker on */
529       iaxc_input_level_set (1.0);
530       iaxc_output_level_set (1.0);
531
532       icao2atisnumber (airport, frequency, num_buf);
533       SG_LOG( SG_GENERAL, SG_DEBUG, "Dialing " << airport << " " << frequency << " MHz: " << num_buf );
534       do_iaxc_call (username, password, voipserver, num_buf);
535
536       while (1)
537         {
538           /* sleep endless */
539           SGTimeStamp::sleepForMSec(3600000);
540         }
541     }
542
543   /* should never be reached */
544   exitcode = 999;
545   quit (0);
546 }
547
548 void
549 quit (int signal)
550 {
551   SG_LOG( SG_GENERAL, SG_ALERT, "Stopping service" );
552
553   if (initialized)
554     iaxc_shutdown ();
555   if (reg_id)
556     iaxc_unregister (reg_id);
557
558   exit (exitcode);
559 }
560
561 void
562 alarm_handler (int signal)
563 {
564   /* Check every DEFAULT_ALARM_TIMER seconds if position related things should happen */
565   if (check_special_frq (selected_frequency))
566     {
567       strcpy (icao, "ZZZZ");
568     }
569   else
570     {
571       strcpy (icao,
572               icaobypos (airportlist, selected_frequency, data.LAT, data.LON,
573                          DEFAULT_RANGE));
574     }
575
576   /* Check if we are out of range */
577   if (strlen (icao) == 0 && connected == 1)
578     {
579       /* Yes, we are out of range so hangup */
580       iaxc_dump_call ();
581       iaxc_millisleep (5 * DEFAULT_MILLISLEEP);
582       connected = 0;
583     }
584
585   /* Check if we are now in range */
586   else if (strlen (icao) != 0 && connected == 0)
587     {
588       icao2number (icao, selected_frequency, num_buf);
589       SG_LOG( SG_GENERAL, SG_DEBUG, "Dialing " << icao << " " << selected_frequency << " MHz: " << num_buf );
590       do_iaxc_call (username, password, voipserver, num_buf);
591       connected = 1;
592     }
593 }
594
595 void
596 strtoupper (const char *str, char *buf, size_t len)
597 {
598   unsigned int i;
599   for (i = 0; str[i] && i < len - 1; i++)
600     {
601       buf[i] = toupper (str[i]);
602     }
603
604   buf[i++] = '\0';
605 }
606
607 void
608 fatal_error (const char *err)
609 {
610   SG_LOG( SG_GENERAL, SG_ALERT, "FATAL ERROR: " << err );
611   if (initialized)
612     iaxc_shutdown ();
613   exit (1);
614 }
615
616 int
617 iaxc_callback (iaxc_event e)
618 {
619   switch (e.type)
620     {
621     case IAXC_EVENT_LEVELS:
622       event_level (e.ev.levels.input, e.ev.levels.output);
623       break;
624     case IAXC_EVENT_TEXT:
625       event_text (e.ev.text.type, e.ev.text.message);
626       break;
627     case IAXC_EVENT_STATE:
628       event_state (e.ev.call.state, e.ev.call.remote, e.ev.call.remote_name,
629                    e.ev.call.local, e.ev.call.local_context);
630       break;
631     case IAXC_EVENT_NETSTAT:
632       event_netstats (e.ev.netstats);
633     case IAXC_EVENT_REGISTRATION:
634       event_register (e.ev.reg.id, e.ev.reg.reply, e.ev.reg.msgcount);
635       break;
636     default:
637       event_unknown (e.type);
638       break;
639     }
640   return 1;
641 }
642
643 void
644 event_state (int state, char *remote, char *remote_name,
645              char *local, char *local_context)
646 {
647   last_state = state;
648   /* This is needed for auto-reconnect */
649   if (state == 0)
650     {
651       connected = 0;
652       /* FIXME: we should wake up the main thread somehow */
653       /* in fg mode the next incoming packet will do that anyway */
654     }
655
656   snprintf (rep_buf, MX_REPORT_BUF,
657             "S%c0x%x%c%s%c%.50s%c%.50s%c%.50s%c%.50s", delim, state,
658             delim, map_state (state), delim, remote, delim, remote_name,
659             delim, local, delim, local_context);
660   rep_buf[MX_REPORT_BUF] = 0; /* ensure null termination */
661   report (rep_buf);
662 }
663
664 void
665 event_text (int type, char *message)
666 {
667   snprintf (rep_buf, MX_REPORT_BUF, "T%c%d%c%.200s", delim, type, delim, message);
668   SG_LOG( SG_GENERAL, SG_ALERT, message );
669   rep_buf[MX_REPORT_BUF] = 0; /* ensure null termination */
670   report (rep_buf);
671 }
672
673 void
674 event_register (int id, int reply, int count)
675 {
676   const char *reason;
677   switch (reply)
678     {
679     case IAXC_REGISTRATION_REPLY_ACK:
680       reason = "accepted";
681       break;
682     case IAXC_REGISTRATION_REPLY_REJ:
683       reason = "denied";
684       if (strcmp (username, "guest") != 0)
685         {
686           SG_LOG( SG_GENERAL, SG_ALERT, "Registering denied" );
687         }
688       break;
689     case IAXC_REGISTRATION_REPLY_TIMEOUT:
690       reason = "timeout";
691       break;
692     default:
693       reason = "unknown";
694     }
695   snprintf (rep_buf, MX_REPORT_BUF, "R%c%d%c%s%c%d", delim, id, delim,
696             reason, delim, count);
697   rep_buf[MX_REPORT_BUF] = 0; /* ensure null termination */
698   report (rep_buf);
699 }
700
701 void
702 event_netstats (struct iaxc_ev_netstats stat)
703 {
704   struct iaxc_netstat local = stat.local;
705   struct iaxc_netstat remote = stat.remote;
706   snprintf (rep_buf, MX_REPORT_BUF,
707             "N%c%d%c%d%c%d%c%d%c%d%c%d%c%d%c%d%c%d%c%d%c%d%c%d%c%d%c%d%c%d%c%d",
708             delim, stat.callNo, delim, stat.rtt,
709             delim, local.jitter, delim, local.losspct, delim,
710             local.losscnt, delim, local.packets, delim, local.delay,
711             delim, local.dropped, delim, local.ooo, delim,
712             remote.jitter, delim, remote.losspct, delim, remote.losscnt,
713             delim, remote.packets, delim, remote.delay, delim,
714             remote.dropped, delim, remote.ooo);
715   rep_buf[MX_REPORT_BUF] = 0; /* ensure null termination */
716   report (rep_buf);
717 }
718
719 void
720 event_level (double in, double out)
721 {
722   snprintf (rep_buf, MX_REPORT_BUF, "L%c%.1f%c%.1f", delim, in, delim, out);
723   rep_buf[MX_REPORT_BUF] = 0; /* ensure null termination */
724   report (rep_buf);
725 }
726
727 const char *
728 map_state (int state)
729 {
730   int i, j;
731   int next = 0;
732   *states = '\0';
733   if (state == 0)
734     {
735       return "free";
736     }
737   for (i = 0, j = 1; map[i] != NULL; i++, j <<= 1)
738     {
739       if (state & j)
740         {
741           if (next)
742             strcat (states, ",");
743           strcat (states, map[i]);
744           next = 1;
745         }
746     }
747   return states;
748 }
749
750 void
751 event_unknown (int type)
752 {
753   snprintf (rep_buf, MX_REPORT_BUF, "U%c%d", delim, type);
754   rep_buf[MX_REPORT_BUF] = 0; /* ensure null termination */
755   report (rep_buf);
756 }
757
758 void
759 report (char *text)
760 {
761   if (debug)
762     {
763       SG_LOG( SG_GENERAL, SG_ALERT, text );
764       fflush (stdout);
765     }
766 }
767
768 void
769 ptt (int mode)
770 {
771   if (mode == 1)
772     {
773       /* mic is muted so unmute and mute speaker */
774       iaxc_input_level_set (level_in);
775       iaxc_output_level_set (0.0);
776       SG_LOG( SG_GENERAL, SG_ALERT, "[SPEAK] unmute mic, mute speaker" );
777     }
778   else
779     {
780       /* mic is unmuted so mute and unmute speaker */
781       iaxc_input_level_set (0.0);
782       iaxc_output_level_set (level_out);
783       SG_LOG( SG_GENERAL, SG_ALERT, "[LISTEN] mute mic, unmute speaker" );
784     }
785 }
786
787 int
788 split (char *string, char *fields[], int nfields, const char *sep)
789 {
790   register char *p = string;
791   register char c;              /* latest character */
792   register char sepc = sep[0];
793   register char sepc2;
794   register int fn;
795   register char **fp = fields;
796   register const char *sepp;
797   register int trimtrail;
798   /* white space */
799   if (sepc == '\0')
800     {
801       while ((c = *p++) == ' ' || c == '\t')
802         continue;
803       p--;
804       trimtrail = 1;
805       sep = " \t";              /* note, code below knows this is 2 long */
806       sepc = ' ';
807     }
808   else
809     trimtrail = 0;
810   sepc2 = sep[1];               /* now we can safely pick this up */
811   /* catch empties */
812   if (*p == '\0')
813     return (0);
814   /* single separator */
815   if (sepc2 == '\0')
816     {
817       fn = nfields;
818       for (;;)
819         {
820           *fp++ = p;
821           fn--;
822           if (fn == 0)
823             break;
824           while ((c = *p++) != sepc)
825             if (c == '\0')
826               return (nfields - fn);
827           *(p - 1) = '\0';
828         }
829       /* we have overflowed the fields vector -- just count them */
830       fn = nfields;
831       for (;;)
832         {
833           while ((c = *p++) != sepc)
834             if (c == '\0')
835               return (fn);
836           fn++;
837         }
838       /* not reached */
839     }
840
841   /* two separators */
842   if (sep[2] == '\0')
843     {
844       fn = nfields;
845       for (;;)
846         {
847           *fp++ = p;
848           fn--;
849           while ((c = *p++) != sepc && c != sepc2)
850             if (c == '\0')
851               {
852                 if (trimtrail && **(fp - 1) == '\0')
853                   fn++;
854                 return (nfields - fn);
855               }
856           if (fn == 0)
857             break;
858           *(p - 1) = '\0';
859           while ((c = *p++) == sepc || c == sepc2)
860             continue;
861           p--;
862         }
863       /* we have overflowed the fields vector -- just count them */
864       fn = nfields;
865       while (c != '\0')
866         {
867           while ((c = *p++) == sepc || c == sepc2)
868             continue;
869           p--;
870           fn++;
871           while ((c = *p++) != '\0' && c != sepc && c != sepc2)
872             continue;
873         }
874       /* might have to trim trailing white space */
875       if (trimtrail)
876         {
877           p--;
878           while ((c = *--p) == sepc || c == sepc2)
879             continue;
880           p++;
881           if (*p != '\0')
882             {
883               if (fn == nfields + 1)
884                 *p = '\0';
885               fn--;
886             }
887         }
888       return (fn);
889     }
890
891   /* n separators */
892   fn = 0;
893   for (;;)
894     {
895       if (fn < nfields)
896         *fp++ = p;
897       fn++;
898       for (;;)
899         {
900           c = *p++;
901           if (c == '\0')
902             return (fn);
903           sepp = sep;
904           while ((sepc = *sepp++) != '\0' && sepc != c)
905             continue;
906           if (sepc != '\0')     /* it was a separator */
907             break;
908         }
909       if (fn < nfields)
910         *(p - 1) = '\0';
911       for (;;)
912         {
913           c = *p++;
914           sepp = sep;
915           while ((sepc = *sepp++) != '\0' && sepc != c)
916             continue;
917           if (sepc == '\0')     /* it wasn't a separator */
918             break;
919         }
920       p--;
921     }
922
923   /* not reached */
924 }
925
926 /**
927  *
928  * \fn double *read_special_frequencies(const char *file)
929  *
930  * \brief Reads the file "special_frequencies.txt" if it exists.
931  * If no file exists, then no special frequencies are useable.
932  *
933  * \param file  pointer on the filename.
934  *
935  * \return Returns the pointer on an array containing doubles if file
936  * has been successfully opened and read, otherwise returns NULL.
937  *
938  */
939 double *read_special_frequencies(const char *file)
940 {
941         double *l_pfrq = NULL;
942         double l_value;
943         int l_count;
944         int l_allocated = 0;
945         int l_new_size;
946
947         if((l_pfrq = (double *)malloc(ALLOC_CHUNK_SIZE * sizeof(double))) != NULL)
948         {
949                 l_allocated += ALLOC_CHUNK_SIZE;
950
951                 if(FGC_SUCCESS(parser_init(file)))
952                 {
953                         l_count = 0;
954
955                         while(FGC_SUCCESS(parser_get_next_value(&l_value)))
956                         {
957                                 if(l_count >= l_allocated)
958                                 {
959                                         l_new_size = ALLOC_CHUNK_SIZE * (l_count / ALLOC_CHUNK_SIZE + 1);
960                                         l_pfrq = (double *)realloc(l_pfrq, l_new_size * sizeof(double));
961                                         l_allocated += ALLOC_CHUNK_SIZE;
962                                 }
963
964                                 l_pfrq[l_count] = l_value;
965
966                                 l_count++;
967                         }
968
969                         /* Last value of the array must be -1.0 which is the terminator. */
970                         if(l_count == l_allocated)
971                                 l_pfrq = (double *)realloc(l_pfrq, (l_count + 1) * sizeof(double));
972                         l_pfrq[l_count] = -1.0;
973                 } else {
974             // failed to open file
975             parser_exit();
976             free(l_pfrq);
977             return 0;
978         }
979         }
980
981         parser_exit();
982
983         return(l_pfrq);
984 }
985
986
987 struct airport *
988 read_airports (const char *file)
989 {
990   FILE *fp;
991   int ret;
992   struct airport airport_tmp;
993   struct airport *first = NULL;
994   struct airport *my_airport = NULL;
995   struct airport *previous_airport = NULL;
996   size_t counter = 0;
997
998   SG_LOG( SG_GENERAL, SG_ALERT, "Reading airports [" << file << "]" );
999
1000   if ((fp = fopen (file, "rt")) == NULL)
1001     {
1002       SG_LOG( SG_GENERAL, SG_ALERT, "ERROR: open failed!" );
1003       perror ("fopen");
1004       exitcode = 120;
1005       quit (0);
1006     }
1007
1008   airport_tmp.next = NULL;
1009   while ((ret = fscanf (fp, " %4[^,],%f,%lf,%lf,%128[^,],%128[^\r\n]",
1010                         airport_tmp.icao, &airport_tmp.frequency,
1011                         &airport_tmp.lat, &airport_tmp.lon,
1012                         airport_tmp.type, airport_tmp.text)) == 6)
1013     {
1014         counter++;
1015       if ((my_airport =
1016            (struct airport *) malloc (sizeof (struct airport))) == NULL)
1017         {
1018           SG_LOG( SG_GENERAL, SG_ALERT, "Error allocating memory for airport data" );
1019           exitcode = 900;
1020           quit (0);
1021         }
1022
1023       if (first == NULL)
1024         first = my_airport;
1025       memcpy (my_airport, &airport_tmp, sizeof (airport_tmp));
1026       if (previous_airport != NULL)
1027         {
1028           previous_airport->next = my_airport;
1029         }
1030       previous_airport = my_airport;
1031     }
1032
1033   fclose (fp);
1034   if (ret != EOF)
1035     {
1036       SG_LOG( SG_GENERAL, SG_ALERT, "ERROR during reading airports!" );
1037       exitcode = 900;
1038       quit (0);
1039     }
1040
1041   SG_LOG( SG_GENERAL, SG_ALERT, "loaded " << counter << " entries" );
1042   return (first);
1043 }
1044
1045 char *
1046 report_devices (int in)
1047 {
1048   struct iaxc_audio_device *devs;  //audio devices
1049   int ndevs;                       //audio dedvice count
1050   int input, output, ring;         //audio device id's
1051   int current, i;
1052   int flag = in ? IAXC_AD_INPUT : IAXC_AD_OUTPUT;
1053   iaxc_audio_devices_get (&devs, &ndevs, &input, &output, &ring);
1054   current = in ? input : output;
1055   snprintf (rep_buf, MX_REPORT_BUF, "%s\n", devs[current].name);
1056   for (i = 0; i < ndevs; i++)
1057     {
1058       if (devs[i].capabilities & flag && i != current)
1059         {
1060           snprintf (rep_buf + strlen (rep_buf), MX_REPORT_BUF - strlen (rep_buf), "%s\n",
1061                     devs[i].name);
1062         }
1063         rep_buf[MX_REPORT_BUF] = 0; /* ensure null termination */
1064         if (strlen(rep_buf) >= MX_REPORT_BUF)
1065             break;
1066     }
1067   rep_buf[MX_REPORT_BUF] = 0; /* ensure null termination */
1068   return rep_buf;
1069 }
1070
1071 int
1072 set_device (const char *name, int out)
1073 {
1074   struct iaxc_audio_device *devs;       /* audio devices */
1075   int ndevs;                    /* audio dedvice count */
1076   int input, output, ring;      /* audio device id's */
1077   int i;
1078   iaxc_audio_devices_get (&devs, &ndevs, &input, &output, &ring);
1079   for (i = 0; i < ndevs; i++)
1080     {
1081       if (devs[i].capabilities & (out ? IAXC_AD_OUTPUT : IAXC_AD_INPUT) &&
1082           strcmp (name, devs[i].name) == 0)
1083         {
1084           if (out)
1085             {
1086               output = devs[i].devID;
1087             }
1088           else
1089             {
1090               input = devs[i].devID;
1091             }
1092           fprintf (stderr, "device %s = %s (%d)\n", out ? "out" : "in", name,
1093                    devs[i].devID);
1094           iaxc_audio_devices_set (input, output, ring);
1095           return 1;
1096         }
1097     }
1098   return 0;
1099 }
1100
1101 void
1102 parse_fgdata (struct fgdata *data, char *buf)
1103 {
1104   char *data_pair = NULL;
1105   char *fields[2];
1106   fields[0] = '\0';
1107   fields[1] = '\0';
1108   SG_LOG( SG_GENERAL, SG_DEBUG, "Parsing data: [" << buf << "]" );
1109   /* Parse data from FG */
1110   data_pair = strtok (buf, ",");
1111   while (data_pair != NULL)
1112     {
1113       split (data_pair, fields, 2, "=");
1114       if (strcmp (fields[0], "COM1_FRQ") == 0)
1115         {
1116           data->COM1_FRQ = atof (fields[1]);
1117           SG_LOG( SG_GENERAL, SG_DEBUG, "COM1_FRQ=" << data->COM1_FRQ );
1118         }
1119       else if (strcmp (fields[0], "COM2_FRQ") == 0)
1120         {
1121           data->COM2_FRQ = atof (fields[1]);
1122           SG_LOG( SG_GENERAL, SG_DEBUG, "COM2_FRQ=" << data->COM2_FRQ );
1123         }
1124       else if (strcmp (fields[0], "NAV1_FRQ") == 0)
1125         {
1126           data->NAV1_FRQ = atof (fields[1]);
1127           SG_LOG( SG_GENERAL, SG_DEBUG, "NAV1_FRQ=" << data->NAV1_FRQ );
1128         }
1129       else if (strcmp (fields[0], "NAV2_FRQ") == 0)
1130         {
1131           data->NAV2_FRQ = atof (fields[1]);
1132           SG_LOG( SG_GENERAL, SG_DEBUG, "NAV2_FRQ=" << data->NAV2_FRQ );
1133         }
1134       else if (strcmp (fields[0], "PTT") == 0)
1135         {
1136           data->PTT = atoi (fields[1]);
1137           SG_LOG( SG_GENERAL, SG_DEBUG, "PTT=" << data->PTT );
1138         }
1139       else if (strcmp (fields[0], "TRANSPONDER") == 0)
1140         {
1141           data->TRANSPONDER = atoi (fields[1]);
1142           SG_LOG( SG_GENERAL, SG_DEBUG, "TRANSPONDER=" << data->TRANSPONDER );
1143         }
1144       else if (strcmp (fields[0], "IAS") == 0)
1145         {
1146           data->IAS = atof (fields[1]);
1147           SG_LOG( SG_GENERAL, SG_DEBUG, "IAS=" << data->IAS );
1148         }
1149       else if (strcmp (fields[0], "GS") == 0)
1150         {
1151           data->GS = atof (fields[1]);
1152           SG_LOG( SG_GENERAL, SG_DEBUG, "GS=" << data->GS );
1153         }
1154       else if (strcmp (fields[0], "LON") == 0)
1155         {
1156           data->LON = atof (fields[1]);
1157           SG_LOG( SG_GENERAL, SG_DEBUG, "LON=" << data->LON );
1158         }
1159       else if (strcmp (fields[0], "LAT") == 0)
1160         {
1161           data->LAT = atof (fields[1]);
1162           SG_LOG( SG_GENERAL, SG_DEBUG, "LAT=" << data->LAT );
1163         }
1164       else if (strcmp (fields[0], "ALT") == 0)
1165         {
1166           data->ALT = atoi (fields[1]);
1167           SG_LOG( SG_GENERAL, SG_DEBUG, "ALT=" << data->ALT );
1168         }
1169       else if (strcmp (fields[0], "HEAD") == 0)
1170         {
1171           data->HEAD = atof (fields[1]);
1172           SG_LOG( SG_GENERAL, SG_DEBUG, "HEAD=" << data->HEAD );
1173         }
1174       else if (strcmp (fields[0], "COM1_SRV") == 0)
1175         {
1176           data->COM1_SRV = atoi (fields[1]);
1177           SG_LOG( SG_GENERAL, SG_DEBUG, "COM1_SRV" << data->COM1_SRV );
1178         }
1179       else if (strcmp (fields[0], "COM2_SRV") == 0)
1180         {
1181           data->COM2_SRV = atoi (fields[1]);
1182           SG_LOG( SG_GENERAL, SG_DEBUG, "COM2_SRV=" << data->COM2_SRV );
1183         }
1184       else if (strcmp (fields[0], "NAV1_SRV") == 0)
1185         {
1186           data->NAV1_SRV = atoi (fields[1]);
1187           SG_LOG( SG_GENERAL, SG_DEBUG, "NAV1_SRV=" << data->NAV1_SRV );
1188         }
1189       else if (strcmp (fields[0], "NAV2_SRV") == 0)
1190         {
1191           data->NAV2_SRV = atoi (fields[1]);
1192           SG_LOG( SG_GENERAL, SG_DEBUG, "NAV2_SRV=" << data->NAV2_SRV );
1193         }
1194       else if (strcmp (fields[0], "OUTPUT_VOL") == 0)
1195         {
1196           data->OUTPUT_VOL = atof (fields[1]);
1197           SG_LOG( SG_GENERAL, SG_DEBUG, "OUTPUT_VOL=" << data->OUTPUT_VOL );
1198         }
1199       else if (strcmp (fields[0], "CALLSIGN") == 0)
1200         {
1201           data->CALLSIGN = fields[1];
1202           SG_LOG( SG_GENERAL, SG_DEBUG, "CALLSIGN=" << data->CALLSIGN );
1203         }
1204       else
1205         {
1206           SG_LOG( SG_GENERAL, SG_DEBUG, "Unknown field " << fields[0] << "=" << fields[1] );
1207         }
1208
1209       data_pair = strtok (NULL, ",");
1210     }
1211   SG_LOG( SG_GENERAL, SG_DEBUG, "" );
1212 }
1213
1214 /**
1215  *
1216  * \fn int check_special_frq (double frq)
1217  *
1218  * \brief Check to see if specified frequency is a special frequency.
1219  *
1220  * \param frq   frequency to check against special frequencies
1221  *
1222  * \return Returns 1 if successful, otherwise returns 0.
1223  *
1224  */
1225 int check_special_frq (double frq)
1226 {
1227         int i = 0;
1228     frq = ceilf(frq*1000.0)/1000.0; // 20130602: By Clement de l'Hamaide, to 'Make 123.450Mhz usable'
1229         while (special_frequencies[i] >= 0.0)
1230         {
1231                 if (frq == special_frequencies[i])
1232                 {
1233                         SG_LOG( SG_GENERAL, SG_ALERT, "Special frequency: " << frq );
1234                         return (1);
1235                 }
1236                 i++;
1237         }
1238         return (0);
1239 }
1240
1241 void
1242 do_iaxc_call (const char *username, const char *password,
1243               const char *voipserver, char *number)
1244 {
1245   char dest[256];
1246   size_t len = strlen(number);
1247
1248   if( strcmp(voipserver, "delta384.server4you.de") == 0 ) {
1249     if( number[len-1] == '5' ) {
1250       number[len-1] = '0';
1251     }
1252   }
1253
1254   if( strcmp(number, "9990909090910000") == 0)
1255     number = (char *)"0190909090910000";
1256
1257   snprintf (dest, sizeof (dest), "%s:%s@%s/%s", username, password,
1258             voipserver, number);
1259   iaxc_call (dest);
1260   iaxc_millisleep (DEFAULT_MILLISLEEP);
1261 }
1262
1263 /* eof - fgcom.cpp */