FGCom-sa: force console output
[fg:hoorays-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   simgear::requestConsole();
279   sglog().setLogLevels( SG_ALL, SG_ALERT );
280
281   prog = strdup( base_name(argv[0]) );
282   
283   /* program header */
284   SG_LOG( SG_GENERAL, SG_ALERT, prog << " - a communication radio based on VoIP with IAX/Asterisk" );
285   SG_LOG( SG_GENERAL, SG_ALERT, "Original (c) 2007-2011 by H. Wirtz <wirtz@dfn.de>" );
286   SG_LOG( SG_GENERAL, SG_ALERT, "OSX and Windows ports 2012-2013 by Yves Sablonier and Geoff R. McLane, resp." );
287   SG_LOG( SG_GENERAL, SG_ALERT, "Version " << FGCOM_VERSION << " compiled " << __DATE__ << ", at " << __TIME__ );
288   SG_LOG( SG_GENERAL, SG_ALERT, "Using iaxclient library Version " << iaxc_version (pkt_buf) );
289   SG_LOG( SG_GENERAL, SG_ALERT, "" );
290
291   /* init values */
292   voipserver = DEFAULT_VOIP_SERVER;
293   fgserver = DEFAULT_FG_SERVER;
294   port = DEFAULT_FG_PORT;
295   username = DEFAULT_USER;
296   password = DEFAULT_PASSWORD;
297   codec_option = DEFAULT_CODEC;
298   mode = 0; // 0 = ATC mode, 1 = FG mode
299   positions_file = (char *) DEFAULT_POSITIONS_FILE;
300   frequency_file = (char *) SPECIAL_FREQUENCIES_FILE;
301
302 #ifndef _WIN32
303   /* catch signals */
304   signal (SIGINT, quit);
305   signal (SIGQUIT, quit);
306   signal (SIGTERM, quit);
307 #endif
308
309   /* setup iax */
310 #ifdef _MSC_VER
311   /* MSVC only - In certain circumstances the addresses placed in iaxc_sendto and iaxc_recvfrom 
312      can be an offset to a jump table, making a compare of the current address to the address of 
313      the actual imported function fail. So here ensure they are the same. */
314   iaxc_set_networking( (iaxc_sendto_t)sendto, (iaxc_recvfrom_t)recvfrom );
315 #endif // _MSC_VER
316
317   if (iaxc_initialize (DEFAULT_MAX_CALLS))
318     fatal_error ("cannot initialize iaxclient!\nHINT: Have you checked the mic and speakers?");
319
320   initialized = 1;
321
322   // option parser
323   fgcomInitOptions (fgcomOptionArray, argc, argv);
324
325   if( debug )
326     sglog().setLogLevels( SG_ALL, SG_DEBUG );
327
328   // codec
329   if (codec_option)
330     {
331       switch (codec_option)
332         {
333         case 'u':
334           codec = IAXC_FORMAT_ULAW;
335           break;
336         case 'a':
337           codec = IAXC_FORMAT_ALAW;
338           break;
339         case 'g':
340           codec = IAXC_FORMAT_GSM;
341           break;
342         case '7':
343           codec = IAXC_FORMAT_G726;
344           break;
345         case 's':
346           codec = IAXC_FORMAT_SPEEX;
347           break;
348         }
349     }
350
351   // airport
352   if (airport_option)
353     {
354       strtoupper (airport_option, airport, sizeof (airport));
355     }
356
357   // input level
358   if (level_in > 1.0)
359     {
360       level_in = 1.0;
361     }
362   if (level_in < 0.0)
363     {
364       level_in = 0.0;
365     }
366
367   // output level
368   if (level_out > 1.0)
369     {
370       level_out = 1.0;
371     }
372   if (level_out < 0.0)
373     {
374       level_out = 0.0;
375     }
376
377   // microphone boost
378   if (mic_boost)
379     {
380       iaxc_mic_boost_set (1);
381     }
382
383   if (list_audio)
384     {
385       SG_LOG( SG_GENERAL, SG_ALERT, "Input audio devices:" );
386       SG_LOG( SG_GENERAL, SG_ALERT, report_devices(IAXC_AD_INPUT) );
387
388       SG_LOG( SG_GENERAL, SG_ALERT, "Output audio devices:" );
389       SG_LOG( SG_GENERAL, SG_ALERT, report_devices(IAXC_AD_OUTPUT) );
390
391       iaxc_shutdown ();
392       exit (1);
393     }
394
395
396   if (audio_in)
397     {
398       set_device (audio_in, 0);
399     }
400
401   if (audio_out)
402     {
403       set_device (audio_out, 1);
404     }
405
406   /* checking consistency of arguments */
407   if (frequency > 0.0 && frequency < 1000.0)
408     {
409       if (strlen (airport) == 0 || strlen (airport) > 4)
410         {
411           strcpy (airport, "ZZZZ");
412         }
413       /* airport and frequency are given => ATC mode */
414       mode = 0;
415     }
416   else
417     {
418       /* no airport => FG mode */
419       mode = 1;
420     }
421
422         /* Read special frequencies file (if exists).
423          * If no file $(INSTALL_DIR)/special_frequencies.txt exists, then default frequencies
424          * are used and are hard coded.
425          */
426
427     if (fix_input_files()) { /* adjust default input per OS */
428         fatal_error ("cannot adjust default input files per OS!\nHINT: Maybe recompile with larger buffer.");
429     }
430
431         if((special_frequencies = read_special_frequencies(frequency_file)) == 0) {
432         SG_LOG( SG_GENERAL, SG_ALERT, "Failed to load file [" << frequency_file << "] !" );
433         SG_LOG( SG_GENERAL, SG_ALERT, "Using internal defaults" );
434         special_frequencies = special_frq;
435     } else {
436         SG_LOG( SG_GENERAL, SG_ALERT, "Loaded file [" << frequency_file << "]." );
437     }
438
439         /* read airport frequencies and positions */
440         airportlist = read_airports (positions_file);   /* never returns if fail! */
441
442   /* preconfigure iax */
443   SG_LOG( SG_GENERAL, SG_ALERT, "Initializing IAX client as " << username << ":" << "xxxxxxxxxxx@" << voipserver );
444
445   std::string app = "FGCOM-";
446   app += FGCOM_VERSION;
447
448   if( !callsign )
449     callsign = DEFAULT_USER;
450
451   iaxc_set_callerid ( callsign, app.c_str() );
452   iaxc_set_formats (IAXC_FORMAT_SPEEX, IAXC_FORMAT_ULAW|IAXC_FORMAT_SPEEX);
453   iaxc_set_speex_settings(1, 5, 0, 1, 0, 3);
454   iaxc_set_filters(IAXC_FILTER_AGC | IAXC_FILTER_DENOISE);
455   iaxc_set_silence_threshold(-20.0);
456   iaxc_set_event_callback (iaxc_callback);
457
458   iaxc_start_processing_thread ();
459
460   if (username && password && voipserver)
461     {
462       reg_id =
463         iaxc_register (const_cast < char *>(username),
464                        const_cast < char *>(password),
465                        const_cast < char *>(voipserver));
466       SG_LOG( SG_GENERAL, SG_DEBUG, "Registered as '" << username << "' at '" << voipserver );
467     }
468   else
469     {
470       SG_LOG( SG_GENERAL, SG_ALERT, "Failed iaxc_register !" );
471       SG_LOG( SG_GENERAL, SG_ALERT, "HINT: Check username, passwordd and address of server" );
472       exitcode = 130;
473       quit (0);
474     }
475
476   iaxc_millisleep (DEFAULT_MILLISLEEP);
477
478   /* main loop */
479   if (mode == 1)
480     {
481       SG_LOG( SG_GENERAL, SG_DEBUG, "Entering main loop in mode FGFS" );
482       /* only in FG mode */
483       simgear::Socket::initSockets();
484       sgSocket.open (false);
485       sgSocket.bind (fgserver, port);
486
487       /* mute mic, speaker on */
488       iaxc_input_level_set (0);
489       iaxc_output_level_set (level_out);
490
491       SGTimeStamp sg_clock;
492       sg_clock.stamp();
493
494       double sg_next_update = sg_clock.toSecs() + DEFAULT_ALARM_TIMER;
495       /* get data from flightgear */
496       while (1)
497         {
498           sg_clock.stamp();
499           double sg_wait = sg_next_update - sg_clock.toSecs();
500           if (sg_wait > 0.001)
501             {
502              simgear::Socket *readSockets[2] = { &sgSocket, 0 };
503              if (sgSocket.select (readSockets, readSockets + 1,
504                                    (int) (sg_wait * 1000)) == 1)
505                 {
506                   simgear::IPAddress their_addr;
507                   numbytes = sgSocket.recvfrom(pkt_buf, MAXBUFLEN - 1, 0, &their_addr);
508                   if (numbytes == -1)
509                     {
510                       perror ("recvfrom");
511                       exit (1);
512                     }
513                   pkt_buf[numbytes] = '\0';
514                   SG_LOG( SG_GENERAL, SG_DEBUG, "Got packet from " << their_addr.getHost () << ":" << their_addr.getPort () );
515                   SG_LOG( SG_GENERAL, SG_DEBUG, "Packet is " << numbytes << " bytes long" );
516                   SG_LOG( SG_GENERAL, SG_DEBUG, "Packet contains \"" << pkt_buf << "\"" );
517                   process_packet (pkt_buf);
518                 }
519             }
520           else
521             {
522               alarm_handler (0);
523               sg_clock.stamp();
524               sg_next_update = sg_clock.toSecs() + DEFAULT_ALARM_TIMER;
525             }
526         }
527     }
528   else
529     {
530       SG_LOG( SG_GENERAL, SG_DEBUG, "Entering main loop in mode ATC" );
531       /* mic on, speaker on */
532       iaxc_input_level_set (1.0);
533       iaxc_output_level_set (1.0);
534
535       icao2atisnumber (airport, frequency, num_buf);
536       SG_LOG( SG_GENERAL, SG_DEBUG, "Dialing " << airport << " " << frequency << " MHz: " << num_buf );
537       do_iaxc_call (username, password, voipserver, num_buf);
538
539       while (1)
540         {
541           /* sleep endless */
542           SGTimeStamp::sleepForMSec(3600000);
543         }
544     }
545
546   /* should never be reached */
547   exitcode = 999;
548   quit (0);
549 }
550
551 void
552 quit (int signal)
553 {
554   SG_LOG( SG_GENERAL, SG_ALERT, "Stopping service" );
555
556   if (initialized)
557     iaxc_shutdown ();
558   if (reg_id)
559     iaxc_unregister (reg_id);
560
561   exit (exitcode);
562 }
563
564 void
565 alarm_handler (int signal)
566 {
567   /* Check every DEFAULT_ALARM_TIMER seconds if position related things should happen */
568   if (check_special_frq (selected_frequency))
569     {
570       strcpy (icao, "ZZZZ");
571     }
572   else
573     {
574       strcpy (icao,
575               icaobypos (airportlist, selected_frequency, data.LAT, data.LON,
576                          DEFAULT_RANGE));
577     }
578
579   /* Check if we are out of range */
580   if (strlen (icao) == 0 && connected == 1)
581     {
582       /* Yes, we are out of range so hangup */
583       iaxc_dump_call ();
584       iaxc_millisleep (5 * DEFAULT_MILLISLEEP);
585       connected = 0;
586     }
587
588   /* Check if we are now in range */
589   else if (strlen (icao) != 0 && connected == 0)
590     {
591       icao2number (icao, selected_frequency, num_buf);
592       SG_LOG( SG_GENERAL, SG_DEBUG, "Dialing " << icao << " " << selected_frequency << " MHz: " << num_buf );
593       do_iaxc_call (username, password, voipserver, num_buf);
594       connected = 1;
595     }
596 }
597
598 void
599 strtoupper (const char *str, char *buf, size_t len)
600 {
601   unsigned int i;
602   for (i = 0; str[i] && i < len - 1; i++)
603     {
604       buf[i] = toupper (str[i]);
605     }
606
607   buf[i++] = '\0';
608 }
609
610 void
611 fatal_error (const char *err)
612 {
613   SG_LOG( SG_GENERAL, SG_ALERT, "FATAL ERROR: " << err );
614   if (initialized)
615     iaxc_shutdown ();
616   exit (1);
617 }
618
619 int
620 iaxc_callback (iaxc_event e)
621 {
622   switch (e.type)
623     {
624     case IAXC_EVENT_LEVELS:
625       event_level (e.ev.levels.input, e.ev.levels.output);
626       break;
627     case IAXC_EVENT_TEXT:
628       event_text (e.ev.text.type, e.ev.text.message);
629       break;
630     case IAXC_EVENT_STATE:
631       event_state (e.ev.call.state, e.ev.call.remote, e.ev.call.remote_name,
632                    e.ev.call.local, e.ev.call.local_context);
633       break;
634     case IAXC_EVENT_NETSTAT:
635       event_netstats (e.ev.netstats);
636     case IAXC_EVENT_REGISTRATION:
637       event_register (e.ev.reg.id, e.ev.reg.reply, e.ev.reg.msgcount);
638       break;
639     default:
640       event_unknown (e.type);
641       break;
642     }
643   return 1;
644 }
645
646 void
647 event_state (int state, char *remote, char *remote_name,
648              char *local, char *local_context)
649 {
650   last_state = state;
651   /* This is needed for auto-reconnect */
652   if (state == 0)
653     {
654       connected = 0;
655       /* FIXME: we should wake up the main thread somehow */
656       /* in fg mode the next incoming packet will do that anyway */
657     }
658
659   snprintf (rep_buf, MX_REPORT_BUF,
660             "S%c0x%x%c%s%c%.50s%c%.50s%c%.50s%c%.50s", delim, state,
661             delim, map_state (state), delim, remote, delim, remote_name,
662             delim, local, delim, local_context);
663   rep_buf[MX_REPORT_BUF] = 0; /* ensure null termination */
664   report (rep_buf);
665 }
666
667 void
668 event_text (int type, char *message)
669 {
670   snprintf (rep_buf, MX_REPORT_BUF, "T%c%d%c%.200s", delim, type, delim, message);
671   SG_LOG( SG_GENERAL, SG_ALERT, message );
672   rep_buf[MX_REPORT_BUF] = 0; /* ensure null termination */
673   report (rep_buf);
674 }
675
676 void
677 event_register (int id, int reply, int count)
678 {
679   const char *reason;
680   switch (reply)
681     {
682     case IAXC_REGISTRATION_REPLY_ACK:
683       reason = "accepted";
684       break;
685     case IAXC_REGISTRATION_REPLY_REJ:
686       reason = "denied";
687       if (strcmp (username, "guest") != 0)
688         {
689           SG_LOG( SG_GENERAL, SG_ALERT, "Registering denied" );
690         }
691       break;
692     case IAXC_REGISTRATION_REPLY_TIMEOUT:
693       reason = "timeout";
694       break;
695     default:
696       reason = "unknown";
697     }
698   snprintf (rep_buf, MX_REPORT_BUF, "R%c%d%c%s%c%d", delim, id, delim,
699             reason, delim, count);
700   rep_buf[MX_REPORT_BUF] = 0; /* ensure null termination */
701   report (rep_buf);
702 }
703
704 void
705 event_netstats (struct iaxc_ev_netstats stat)
706 {
707   struct iaxc_netstat local = stat.local;
708   struct iaxc_netstat remote = stat.remote;
709   snprintf (rep_buf, MX_REPORT_BUF,
710             "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",
711             delim, stat.callNo, delim, stat.rtt,
712             delim, local.jitter, delim, local.losspct, delim,
713             local.losscnt, delim, local.packets, delim, local.delay,
714             delim, local.dropped, delim, local.ooo, delim,
715             remote.jitter, delim, remote.losspct, delim, remote.losscnt,
716             delim, remote.packets, delim, remote.delay, delim,
717             remote.dropped, delim, remote.ooo);
718   rep_buf[MX_REPORT_BUF] = 0; /* ensure null termination */
719   report (rep_buf);
720 }
721
722 void
723 event_level (double in, double out)
724 {
725   snprintf (rep_buf, MX_REPORT_BUF, "L%c%.1f%c%.1f", delim, in, delim, out);
726   rep_buf[MX_REPORT_BUF] = 0; /* ensure null termination */
727   report (rep_buf);
728 }
729
730 const char *
731 map_state (int state)
732 {
733   int i, j;
734   int next = 0;
735   *states = '\0';
736   if (state == 0)
737     {
738       return "free";
739     }
740   for (i = 0, j = 1; map[i] != NULL; i++, j <<= 1)
741     {
742       if (state & j)
743         {
744           if (next)
745             strcat (states, ",");
746           strcat (states, map[i]);
747           next = 1;
748         }
749     }
750   return states;
751 }
752
753 void
754 event_unknown (int type)
755 {
756   snprintf (rep_buf, MX_REPORT_BUF, "U%c%d", delim, type);
757   rep_buf[MX_REPORT_BUF] = 0; /* ensure null termination */
758   report (rep_buf);
759 }
760
761 void
762 report (char *text)
763 {
764   if (debug)
765     {
766       SG_LOG( SG_GENERAL, SG_ALERT, text );
767       fflush (stdout);
768     }
769 }
770
771 void
772 ptt (int mode)
773 {
774   if (mode == 1)
775     {
776       /* mic is muted so unmute and mute speaker */
777       iaxc_input_level_set (level_in);
778       iaxc_output_level_set (0.0);
779       SG_LOG( SG_GENERAL, SG_ALERT, "[SPEAK] unmute mic, mute speaker" );
780     }
781   else
782     {
783       /* mic is unmuted so mute and unmute speaker */
784       iaxc_input_level_set (0.0);
785       iaxc_output_level_set (level_out);
786       SG_LOG( SG_GENERAL, SG_ALERT, "[LISTEN] mute mic, unmute speaker" );
787     }
788 }
789
790 int
791 split (char *string, char *fields[], int nfields, const char *sep)
792 {
793   register char *p = string;
794   register char c;              /* latest character */
795   register char sepc = sep[0];
796   register char sepc2;
797   register int fn;
798   register char **fp = fields;
799   register const char *sepp;
800   register int trimtrail;
801   /* white space */
802   if (sepc == '\0')
803     {
804       while ((c = *p++) == ' ' || c == '\t')
805         continue;
806       p--;
807       trimtrail = 1;
808       sep = " \t";              /* note, code below knows this is 2 long */
809       sepc = ' ';
810     }
811   else
812     trimtrail = 0;
813   sepc2 = sep[1];               /* now we can safely pick this up */
814   /* catch empties */
815   if (*p == '\0')
816     return (0);
817   /* single separator */
818   if (sepc2 == '\0')
819     {
820       fn = nfields;
821       for (;;)
822         {
823           *fp++ = p;
824           fn--;
825           if (fn == 0)
826             break;
827           while ((c = *p++) != sepc)
828             if (c == '\0')
829               return (nfields - fn);
830           *(p - 1) = '\0';
831         }
832       /* we have overflowed the fields vector -- just count them */
833       fn = nfields;
834       for (;;)
835         {
836           while ((c = *p++) != sepc)
837             if (c == '\0')
838               return (fn);
839           fn++;
840         }
841       /* not reached */
842     }
843
844   /* two separators */
845   if (sep[2] == '\0')
846     {
847       fn = nfields;
848       for (;;)
849         {
850           *fp++ = p;
851           fn--;
852           while ((c = *p++) != sepc && c != sepc2)
853             if (c == '\0')
854               {
855                 if (trimtrail && **(fp - 1) == '\0')
856                   fn++;
857                 return (nfields - fn);
858               }
859           if (fn == 0)
860             break;
861           *(p - 1) = '\0';
862           while ((c = *p++) == sepc || c == sepc2)
863             continue;
864           p--;
865         }
866       /* we have overflowed the fields vector -- just count them */
867       fn = nfields;
868       while (c != '\0')
869         {
870           while ((c = *p++) == sepc || c == sepc2)
871             continue;
872           p--;
873           fn++;
874           while ((c = *p++) != '\0' && c != sepc && c != sepc2)
875             continue;
876         }
877       /* might have to trim trailing white space */
878       if (trimtrail)
879         {
880           p--;
881           while ((c = *--p) == sepc || c == sepc2)
882             continue;
883           p++;
884           if (*p != '\0')
885             {
886               if (fn == nfields + 1)
887                 *p = '\0';
888               fn--;
889             }
890         }
891       return (fn);
892     }
893
894   /* n separators */
895   fn = 0;
896   for (;;)
897     {
898       if (fn < nfields)
899         *fp++ = p;
900       fn++;
901       for (;;)
902         {
903           c = *p++;
904           if (c == '\0')
905             return (fn);
906           sepp = sep;
907           while ((sepc = *sepp++) != '\0' && sepc != c)
908             continue;
909           if (sepc != '\0')     /* it was a separator */
910             break;
911         }
912       if (fn < nfields)
913         *(p - 1) = '\0';
914       for (;;)
915         {
916           c = *p++;
917           sepp = sep;
918           while ((sepc = *sepp++) != '\0' && sepc != c)
919             continue;
920           if (sepc == '\0')     /* it wasn't a separator */
921             break;
922         }
923       p--;
924     }
925
926   /* not reached */
927 }
928
929 /**
930  *
931  * \fn double *read_special_frequencies(const char *file)
932  *
933  * \brief Reads the file "special_frequencies.txt" if it exists.
934  * If no file exists, then no special frequencies are useable.
935  *
936  * \param file  pointer on the filename.
937  *
938  * \return Returns the pointer on an array containing doubles if file
939  * has been successfully opened and read, otherwise returns NULL.
940  *
941  */
942 double *read_special_frequencies(const char *file)
943 {
944         double *l_pfrq = NULL;
945         double l_value;
946         int l_count;
947         int l_allocated = 0;
948         int l_new_size;
949
950         if((l_pfrq = (double *)malloc(ALLOC_CHUNK_SIZE * sizeof(double))) != NULL)
951         {
952                 l_allocated += ALLOC_CHUNK_SIZE;
953
954                 if(FGC_SUCCESS(parser_init(file)))
955                 {
956                         l_count = 0;
957
958                         while(FGC_SUCCESS(parser_get_next_value(&l_value)))
959                         {
960                                 if(l_count >= l_allocated)
961                                 {
962                                         l_new_size = ALLOC_CHUNK_SIZE * (l_count / ALLOC_CHUNK_SIZE + 1);
963                                         l_pfrq = (double *)realloc(l_pfrq, l_new_size * sizeof(double));
964                                         l_allocated += ALLOC_CHUNK_SIZE;
965                                 }
966
967                                 l_pfrq[l_count] = l_value;
968
969                                 l_count++;
970                         }
971
972                         /* Last value of the array must be -1.0 which is the terminator. */
973                         if(l_count == l_allocated)
974                                 l_pfrq = (double *)realloc(l_pfrq, (l_count + 1) * sizeof(double));
975                         l_pfrq[l_count] = -1.0;
976                 } else {
977             // failed to open file
978             parser_exit();
979             free(l_pfrq);
980             return 0;
981         }
982         }
983
984         parser_exit();
985
986         return(l_pfrq);
987 }
988
989
990 struct airport *
991 read_airports (const char *file)
992 {
993   FILE *fp;
994   int ret;
995   struct airport airport_tmp;
996   struct airport *first = NULL;
997   struct airport *my_airport = NULL;
998   struct airport *previous_airport = NULL;
999   size_t counter = 0;
1000
1001   SG_LOG( SG_GENERAL, SG_ALERT, "Reading airports [" << file << "]" );
1002
1003   if ((fp = fopen (file, "rt")) == NULL)
1004     {
1005       SG_LOG( SG_GENERAL, SG_ALERT, "ERROR: open failed!" );
1006       perror ("fopen");
1007       exitcode = 120;
1008       quit (0);
1009     }
1010
1011   airport_tmp.next = NULL;
1012   while ((ret = fscanf (fp, " %4[^,],%f,%lf,%lf,%128[^,],%128[^\r\n]",
1013                         airport_tmp.icao, &airport_tmp.frequency,
1014                         &airport_tmp.lat, &airport_tmp.lon,
1015                         airport_tmp.type, airport_tmp.text)) == 6)
1016     {
1017         counter++;
1018       if ((my_airport =
1019            (struct airport *) malloc (sizeof (struct airport))) == NULL)
1020         {
1021           SG_LOG( SG_GENERAL, SG_ALERT, "Error allocating memory for airport data" );
1022           exitcode = 900;
1023           quit (0);
1024         }
1025
1026       if (first == NULL)
1027         first = my_airport;
1028       memcpy (my_airport, &airport_tmp, sizeof (airport_tmp));
1029       if (previous_airport != NULL)
1030         {
1031           previous_airport->next = my_airport;
1032         }
1033       previous_airport = my_airport;
1034     }
1035
1036   fclose (fp);
1037   if (ret != EOF)
1038     {
1039       SG_LOG( SG_GENERAL, SG_ALERT, "ERROR during reading airports!" );
1040       exitcode = 900;
1041       quit (0);
1042     }
1043
1044   SG_LOG( SG_GENERAL, SG_ALERT, "loaded " << counter << " entries" );
1045   return (first);
1046 }
1047
1048 char *
1049 report_devices (int in)
1050 {
1051   struct iaxc_audio_device *devs;  //audio devices
1052   int ndevs;                       //audio dedvice count
1053   int input, output, ring;         //audio device id's
1054   int current, i;
1055   int flag = in ? IAXC_AD_INPUT : IAXC_AD_OUTPUT;
1056   iaxc_audio_devices_get (&devs, &ndevs, &input, &output, &ring);
1057   current = in ? input : output;
1058   snprintf (rep_buf, MX_REPORT_BUF, "%s\n", devs[current].name);
1059   for (i = 0; i < ndevs; i++)
1060     {
1061       if (devs[i].capabilities & flag && i != current)
1062         {
1063           snprintf (rep_buf + strlen (rep_buf), MX_REPORT_BUF - strlen (rep_buf), "%s\n",
1064                     devs[i].name);
1065         }
1066         rep_buf[MX_REPORT_BUF] = 0; /* ensure null termination */
1067         if (strlen(rep_buf) >= MX_REPORT_BUF)
1068             break;
1069     }
1070   rep_buf[MX_REPORT_BUF] = 0; /* ensure null termination */
1071   return rep_buf;
1072 }
1073
1074 int
1075 set_device (const char *name, int out)
1076 {
1077   struct iaxc_audio_device *devs;       /* audio devices */
1078   int ndevs;                    /* audio dedvice count */
1079   int input, output, ring;      /* audio device id's */
1080   int i;
1081   iaxc_audio_devices_get (&devs, &ndevs, &input, &output, &ring);
1082   for (i = 0; i < ndevs; i++)
1083     {
1084       if (devs[i].capabilities & (out ? IAXC_AD_OUTPUT : IAXC_AD_INPUT) &&
1085           strcmp (name, devs[i].name) == 0)
1086         {
1087           if (out)
1088             {
1089               output = devs[i].devID;
1090             }
1091           else
1092             {
1093               input = devs[i].devID;
1094             }
1095           fprintf (stderr, "device %s = %s (%d)\n", out ? "out" : "in", name,
1096                    devs[i].devID);
1097           iaxc_audio_devices_set (input, output, ring);
1098           return 1;
1099         }
1100     }
1101   return 0;
1102 }
1103
1104 void
1105 parse_fgdata (struct fgdata *data, char *buf)
1106 {
1107   char *data_pair = NULL;
1108   char *fields[2];
1109   fields[0] = '\0';
1110   fields[1] = '\0';
1111   SG_LOG( SG_GENERAL, SG_DEBUG, "Parsing data: [" << buf << "]" );
1112   /* Parse data from FG */
1113   data_pair = strtok (buf, ",");
1114   while (data_pair != NULL)
1115     {
1116       split (data_pair, fields, 2, "=");
1117       if (strcmp (fields[0], "COM1_FRQ") == 0)
1118         {
1119           data->COM1_FRQ = atof (fields[1]);
1120           SG_LOG( SG_GENERAL, SG_DEBUG, "COM1_FRQ=" << data->COM1_FRQ );
1121         }
1122       else if (strcmp (fields[0], "COM2_FRQ") == 0)
1123         {
1124           data->COM2_FRQ = atof (fields[1]);
1125           SG_LOG( SG_GENERAL, SG_DEBUG, "COM2_FRQ=" << data->COM2_FRQ );
1126         }
1127       else if (strcmp (fields[0], "NAV1_FRQ") == 0)
1128         {
1129           data->NAV1_FRQ = atof (fields[1]);
1130           SG_LOG( SG_GENERAL, SG_DEBUG, "NAV1_FRQ=" << data->NAV1_FRQ );
1131         }
1132       else if (strcmp (fields[0], "NAV2_FRQ") == 0)
1133         {
1134           data->NAV2_FRQ = atof (fields[1]);
1135           SG_LOG( SG_GENERAL, SG_DEBUG, "NAV2_FRQ=" << data->NAV2_FRQ );
1136         }
1137       else if (strcmp (fields[0], "PTT") == 0)
1138         {
1139           data->PTT = atoi (fields[1]);
1140           SG_LOG( SG_GENERAL, SG_DEBUG, "PTT=" << data->PTT );
1141         }
1142       else if (strcmp (fields[0], "TRANSPONDER") == 0)
1143         {
1144           data->TRANSPONDER = atoi (fields[1]);
1145           SG_LOG( SG_GENERAL, SG_DEBUG, "TRANSPONDER=" << data->TRANSPONDER );
1146         }
1147       else if (strcmp (fields[0], "IAS") == 0)
1148         {
1149           data->IAS = atof (fields[1]);
1150           SG_LOG( SG_GENERAL, SG_DEBUG, "IAS=" << data->IAS );
1151         }
1152       else if (strcmp (fields[0], "GS") == 0)
1153         {
1154           data->GS = atof (fields[1]);
1155           SG_LOG( SG_GENERAL, SG_DEBUG, "GS=" << data->GS );
1156         }
1157       else if (strcmp (fields[0], "LON") == 0)
1158         {
1159           data->LON = atof (fields[1]);
1160           SG_LOG( SG_GENERAL, SG_DEBUG, "LON=" << data->LON );
1161         }
1162       else if (strcmp (fields[0], "LAT") == 0)
1163         {
1164           data->LAT = atof (fields[1]);
1165           SG_LOG( SG_GENERAL, SG_DEBUG, "LAT=" << data->LAT );
1166         }
1167       else if (strcmp (fields[0], "ALT") == 0)
1168         {
1169           data->ALT = atoi (fields[1]);
1170           SG_LOG( SG_GENERAL, SG_DEBUG, "ALT=" << data->ALT );
1171         }
1172       else if (strcmp (fields[0], "HEAD") == 0)
1173         {
1174           data->HEAD = atof (fields[1]);
1175           SG_LOG( SG_GENERAL, SG_DEBUG, "HEAD=" << data->HEAD );
1176         }
1177       else if (strcmp (fields[0], "COM1_SRV") == 0)
1178         {
1179           data->COM1_SRV = atoi (fields[1]);
1180           SG_LOG( SG_GENERAL, SG_DEBUG, "COM1_SRV" << data->COM1_SRV );
1181         }
1182       else if (strcmp (fields[0], "COM2_SRV") == 0)
1183         {
1184           data->COM2_SRV = atoi (fields[1]);
1185           SG_LOG( SG_GENERAL, SG_DEBUG, "COM2_SRV=" << data->COM2_SRV );
1186         }
1187       else if (strcmp (fields[0], "NAV1_SRV") == 0)
1188         {
1189           data->NAV1_SRV = atoi (fields[1]);
1190           SG_LOG( SG_GENERAL, SG_DEBUG, "NAV1_SRV=" << data->NAV1_SRV );
1191         }
1192       else if (strcmp (fields[0], "NAV2_SRV") == 0)
1193         {
1194           data->NAV2_SRV = atoi (fields[1]);
1195           SG_LOG( SG_GENERAL, SG_DEBUG, "NAV2_SRV=" << data->NAV2_SRV );
1196         }
1197       else if (strcmp (fields[0], "OUTPUT_VOL") == 0)
1198         {
1199           data->OUTPUT_VOL = atof (fields[1]);
1200           SG_LOG( SG_GENERAL, SG_DEBUG, "OUTPUT_VOL=" << data->OUTPUT_VOL );
1201         }
1202       else if (strcmp (fields[0], "CALLSIGN") == 0)
1203         {
1204           data->CALLSIGN = fields[1];
1205           SG_LOG( SG_GENERAL, SG_DEBUG, "CALLSIGN=" << data->CALLSIGN );
1206         }
1207       else
1208         {
1209           SG_LOG( SG_GENERAL, SG_DEBUG, "Unknown field " << fields[0] << "=" << fields[1] );
1210         }
1211
1212       data_pair = strtok (NULL, ",");
1213     }
1214   SG_LOG( SG_GENERAL, SG_DEBUG, "" );
1215 }
1216
1217 /**
1218  *
1219  * \fn int check_special_frq (double frq)
1220  *
1221  * \brief Check to see if specified frequency is a special frequency.
1222  *
1223  * \param frq   frequency to check against special frequencies
1224  *
1225  * \return Returns 1 if successful, otherwise returns 0.
1226  *
1227  */
1228 int check_special_frq (double frq)
1229 {
1230         int i = 0;
1231     frq = ceilf(frq*1000.0)/1000.0; // 20130602: By Clement de l'Hamaide, to 'Make 123.450Mhz usable'
1232         while (special_frequencies[i] >= 0.0)
1233         {
1234                 if (frq == special_frequencies[i])
1235                 {
1236                         SG_LOG( SG_GENERAL, SG_ALERT, "Special frequency: " << frq );
1237                         return (1);
1238                 }
1239                 i++;
1240         }
1241         return (0);
1242 }
1243
1244 void
1245 do_iaxc_call (const char *username, const char *password,
1246               const char *voipserver, char *number)
1247 {
1248   char dest[256];
1249   size_t len = strlen(number);
1250
1251   if( strcmp(voipserver, "delta384.server4you.de") == 0 ) {
1252     if( number[len-1] == '5' ) {
1253       number[len-1] = '0';
1254     }
1255   }
1256
1257   if( strcmp(number, "9990909090910000") == 0)
1258     number = (char *)"0190909090910000";
1259
1260   snprintf (dest, sizeof (dest), "%s:%s@%s/%s", username, password,
1261             voipserver, number);
1262   iaxc_call (dest);
1263   iaxc_millisleep (DEFAULT_MILLISLEEP);
1264 }
1265
1266 /* eof - fgcom.cpp */