4
# Version 1.04 - 26 Sept 2013
5
# Version 1.03 - 04 Sept 2013
6
# Version 1.02 - 01 Sept 2013
8
# Modified by Clément de l'Hamaide - 20130926
9
# * Disable phonebook.txt generation
11
# Modified by Geoff R. McLane - 20130904
12
# * Added strict and warnings, fixing all variables to comply
13
# * Added command line interface and 'help', but kept same defaults
14
# * Removed numerous substr($txt,0,-1) which truncated some string
15
# * Change 'chop' to the safer 'chomp'
16
# * Added heliport(16) and seaport(17) under an option switch
17
# * Added output of some stats of what was collected, and skipped
18
# * Added $trim_to_20 option to align the phonebook if desired
19
# * Replaced all tabs with ' ' to better align the code for readability
20
# * Added a trim_all($txt) to removed some unwanted line endings
21
# * Is compatible with FG 810 AND XP 1000 apt.dat files
23
# Modified by Clement de l'Hamaide - 20130901
34
my $VERS = "1.04 - 26 Sept 2013";
36
my $FG_AIRPORTS = "./apt.dat.gz";
37
my $FG_NAVAIDS = "./nav.dat.gz";
38
my $out_fil1 = "fgcom.conf";
39
my $out_pos = "positions.txt";
40
#my $out_phon = "phonebook.txt";
44
my $add_heliports = 1;
47
my $phonebook_post="ZZZZ 910.000 0190909090910000 Echo-Box
48
ZZZZ 911.000 0190909090911000 Music-Box
49
ZZZZ 700.000 0190909090700000 Radio-Box
50
ZZZZ 123.450 0190909090123450 Air2Air
51
ZZZZ 122.750 0190909090122750 Air2Air
52
ZZZZ 121.500 0190909090121500 Air2Air
53
ZZZZ 123.500 0190909090123500 Air2Air
54
ZZZZ 121.000 0190909090121000 Emergency
55
ZZZZ 723.340 0190909090723340 French Air Patrol
58
my $extensions_pre="[globals]
59
ATIS_RECORDINGS=/var/fgcom-server/atis
60
RADIO_FILE=/var/fgcom-server/radio
61
;Morse tone, 1020Hz for VOR and ILS but 1350Hz for DME, in Hz
63
;Dit length for morse code, in ms
69
exten => s,n,MeetMe(\${MACRO_EXTEN},qd)
80
; Check if audio file exists
81
exten => s,n,TrySystem(ls \${ATIS_RECORDINGS}/99\${MACRO_EXTEN:2}*)
82
exten => s,n,Goto(\${SYSTEMSTATUS})
83
; If audio file exists, play it
84
exten => s,n(SUCCESS),While(\$[1])
85
exten => s,n,Playback(\${ATIS_RECORDINGS}/99\${MACRO_EXTEN:2})
89
; If audio doesn't exist or TrySystem failed (why?), go to festival macro
90
;exten => s,n,(APPERROR),Macro(festival, \${MACRO_EXTEN}) ; DISABLED FOR NOW
91
;exten => s,n,(FAILURE),Macro(festival, \${MACRO_EXTEN}) ; DISABLED FOR NOW
92
exten => s,n,(APPERROR),Hangup()
93
exten => s,n,(FAILURE),Hangup()
97
exten => s,n,SendText(Record begin in 3s)
99
exten => s,n,SendText(Record begin in 2s)
101
exten => s,n,SendText(Record begin in 1s)
103
exten => s,n,Record(\${ATIS_RECORDINGS}/\${MACRO_EXTEN}:gsm,,90,k)
105
exten => s,n,Playback(\${ATIS_RECORDINGS}/\${MACRO_EXTEN})
106
exten => s,n,Hangup()
109
exten => s,1,Set(metar=\"\${SHELL(getmetar \$[1]):0:-1}\");
110
exten => s,n,While(\$[1])
111
exten => s,n,Festival(\${metar:1:-1})
113
exten => s,n,EndWhile
116
exten => s,1,Answer()
117
exten => s,n,While(\$[1])
118
exten => s,n,Morsecode(\${ARG1})
120
exten => s,n,EndWhile
121
exten => s,n,Hangup()
124
exten => s,1,Answer()
125
exten => s,n,Playback(\${RADIO_FILE})
126
exten => s,n,Hangup()
130
exten => 0190909090910000,1,SendText(Echo Box - For testing FGCOM)
131
exten => 0190909090910000,n,Macro(echo)
133
exten => 0190909090911000,1,Answer
134
exten => 0190909090911000,n,SendText(Music On Hold Box - For testing FGCOM)
135
exten => 0190909090911000,n,MusicOnHold(default)
136
; Radio Station: 700.000 MHz
137
exten => 0190909090700000,1,SendText(Radio Station - For testing FGCOM)
138
exten => 0190909090700000,n,Macro(radio)
141
exten => 0190909090121500,1,SendText(121.500 Auto-information frequency)
142
exten => 0190909090121500,n,Macro(com)
144
exten => 0190909090123450,1,SendText(123.450 Auto-information frequency)
145
exten => 0190909090123450,n,Macro(com)
147
exten => 0190909090123500,1,SendText(123.500 Auto-information frequency)
148
exten => 0190909090123500,n,Macro(com)
150
exten => 0190909090122750,1,SendText(122.750 Auto-information frequency)
151
exten => 0190909090122750,n,Macro(com)
154
exten => 0190909090121000,1,SendText(121.000 Emergency frequency)
155
exten => 0190909090121000,n,Macro(com)
157
; 723.340 Franch Air Patrol
158
exten => 0190909090723340,1,SendText(723.340 French Air Patrol frequency)
159
exten => 0190909090723340,n,Macro(com)
163
my $dt = DateTime->now;
165
if ($pgmname =~ /(\\|\/)/) {
166
my @tmpsp = split(/(\\|\/)/,$pgmname);
167
$pgmname = $tmpsp[-1];
169
my $numberOfFrequenciesParsed = 0;
170
my $numberOfFrequenciesComputed = 0;
171
my $numberOfFrequenciesWritten = 0;
172
my $numberOfFrequenciesSkipped = 0;
176
my ($fh,$z,$icao,$lat,$lon,$latW,$lonW,$com,$code,$text);
177
my ($vor,$icao_number,$f,$positions,$extensions,$phonebook);
178
my ($i,$tmp,$airport,$nav,$freq,$ssf,$type);
183
my $no_latloncnt = 0;
185
my $airportcount = 0;
186
my $navlinecount = 0;
189
sub prt($) { print shift; }
194
$icao = " ".$icao while(length($icao) < 4);
196
for ($i = 0; $i< length($icao); $i++) {
197
$n = ord(substr($icao,$i,1));
198
$number .= sprintf("%02d",$n);
203
sub trim_tailing($) {
205
$ln = substr($ln,0, length($ln) - 1) while ($ln =~ /\s$/g); # remove all TRAILING space
209
sub trim_leading($) {
211
$ln = substr($ln,1) while ($ln =~ /^\s/); # remove all LEADING space
217
$ln = trim_tailing($ln); # remove all TRAINING space
218
$ln = trim_leading($ln); # remove all LEADING space
224
$ln =~ s/\n/ /gm; # replace CR (\n)
225
$ln =~ s/\r/ /gm; # replace LF (\r)
226
$ln =~ s/\t/ /g; # TAB(s) to a SPACE
227
$ln = trim_ends($ln);
228
$ln =~ s/\s{2}/ /g while ($ln =~ /\s{2}/); # all double space to SINGLE
232
##############################################################################
234
##############################################################################
236
# read airport data in hash
238
if ($fh->open($FG_AIRPORTS, "r")) {
240
printf("Parsing ".$FG_AIRPORTS." ...\n");
245
if (scalar(keys(%frq)) > 0) {
246
if (!$lat && !$lon) {
247
if ($latW && $lonW) {
251
print($icao." :: LAT/LON not found\n");
255
$APT{$icao}{'text'} = $text;
256
$APT{$icao}{'lat'} = $lat;
257
$APT{$icao}{'lon'} = $lon;
258
foreach $f (keys(%frq)) {
259
if (length($frq{$f}) <= 1) {
262
$APT{$icao}{'com'}{$f} = $frq{$f};
271
} elsif ($hadlines) {
272
$numberOfFrequenciesSkipped++;
284
elsif ($z=~/^1\s+-?\d+\s+[01]\s+[01]\s+([A-Z0-9]+)\s+(.+)\s*$/)
288
$text = trim_all($2);
291
elsif ($z=~/^16\s+\d+\s+[01]\s+[01]\s+([A-Z0-9]+)\s+(.+)\s*$/)
297
$text = trim_all($2);
300
elsif ($z=~/^17\s+\d+\s+[01]\s+[01]\s+([A-Z0-9]+)\s+(.+)\s*$/)
306
$text = trim_all($2);
309
elsif ($z=~/^14\s+(-?\d+\.\d+)\s+(-?\d+\.\d+)/)
315
elsif ($z=~/^19\s+(-?\d+\.\d+)\s+(-?\d+\.\d+)/)
321
elsif ($z=~/^5[0-6]\s+(\d{5})\d*\s+(.+)\s*$/)
325
$type = trim_all($2);
326
###prt("$freq $type\n");
327
$ssf = substr($freq, -1);
328
if ( $ssf == 2 || $ssf == 7) {
329
$com = sprintf("%3.2f5", $freq/100);
331
$com = sprintf("%3.3f", $freq/100);
340
die("Cannot open $FG_AIRPORTS :$!\n");
343
prt("Got $airportcount airports, $land_apcnt land(1), $heli_apcnt heliports(16), and $sea_apcnt seaports(17), skipped $no_latloncnt no lat/lon, $no_freqcnt no freqs\n");
345
# read nav data in hash
347
if($nav->open($FG_NAVAIDS, "r"))
350
printf("Parsing ".$FG_NAVAIDS." ...\n");
354
last if ($z =~ /^99/);
357
if ($z =~ /^3\s+(-?\d+\.\d+)\s+(-?\d+\.\d+)\s+\d+\s+(\d+)\s+\d+\s+-?\d+\.\d+\s+([A-Z]+)\s+(.*)\s*$/) {
361
$freq=sprintf("%3.3f",$3/100);
363
$text = trim_all($5);
365
$NAV{$code}{'lat'}=$lat;
366
$NAV{$code}{'lon'}=$lon;
367
$NAV{$code}{'frq'}=$freq;
368
$NAV{$code}{'text'}=$text;
375
die("Cannot open $FG_NAVAIDS :$!\n");
379
prt("Done $navlinecount nav.dat lines, adding $vordmeadded VOR/DME(3) records\n");
381
# get output files open
383
# open positions file
384
$positions = new IO::File;
385
$positions->open($out_pos, "w") || die("Cannot open $out_pos for writing: $!\n");
387
# open phonebook file
388
#$phonebook = new IO::File;
389
#$phonebook->open($out_phon, "w") || die("Cannot open $out_phon for writing: $!\n");
392
$extensions = new IO::File;
393
$extensions->open($out_fil1, "w") || die("Cannot open $out_fil1 for writing: $!\n");
396
#print $phonebook "File generated by $pgmname - ".join ' ', $dt->ymd, $dt->hms." UTC\n";
397
#print $phonebook keys(%APT)." airports and ".keys(%NAV)." navaids are present in this file (".$numberOfFrequenciesSkipped." freq skipped) \n\n";
398
#print $phonebook "ICAO Decription FRQ Phone no. Name\n";
399
#print $phonebook "-" x 79,"\n";
401
# read pre data for fgcom.conf;
402
print $extensions $extensions_pre;
404
printf("Writing airports data ...\n");
406
# Print all known airports
407
foreach $airport (sort(keys(%APT))) {
408
foreach $f (keys(%{$APT{$airport}{'com'}})) {
409
$ssf = substr($f, -2);
410
$type = $APT{$airport}{'com'}{$f};
411
if ($trim_to_20 && (length($type) > 20)) {
412
$type = substr($type,0,20);
414
if ($ssf == 30 || $ssf == 80) {
416
printf("Found a 8.33KHz freq !!! $airport\n");
419
$icao_number = icao2number($airport);
421
# write positions APT
422
print $positions Encode::encode( "utf8", $airport.",".$f.",".$APT{$airport}{'lat'}.",".$APT{$airport}{'lon'}.",".$APT{$airport}{'com'}{$f}.",".$APT{$airport}{'text'}."\n");
425
#$tmp = sprintf("%4s %-20s %3.3f %-.16s %-20s\n",$airport,$type,$f,"01".$icao_number.$f*1000,$APT{$airport}{'text'});
426
#print $phonebook Encode::encode( "utf8", $tmp);
428
# write extensions.conf
429
$tmp = "; $airport $type $f - ".$APT{$airport}{'text'}."\n";
431
print $extensions Encode::encode( "utf8", $tmp);
432
if ( $APT{$airport}{'com'}{$f} =~ /ATIS$/ ) {
433
# ATIS playback extension
434
print $extensions Encode::encode( "utf8", "exten => 01$icao_number".($f*1000).",1,SendText($airport ".$APT{$airport}{'text'}." $f ".$APT{$airport}{'com'}{$f}.")\n");
435
print $extensions Encode::encode( "utf8", "exten => 01$icao_number".($f*1000).",n,SendURL(http://www.the-airport-guide.com/search.php?by=icao&search=$airport)\n");
436
print $extensions Encode::encode( "utf8", "exten => 01$icao_number".($f*1000).",n,Macro(atis)\n");
438
print $positions Encode::encode( "utf8", $airport.",".($f+0.005).",".$APT{$airport}{'lat'}.",".$APT{$airport}{'lon'}.",".$APT{$airport}{'com'}{$f}.",".$APT{$airport}{'text'}."\n");
439
print $extensions Encode::encode( "utf8", "exten => 01$icao_number".($f*1000+5).",1,Dial(Local/01$icao_number".($f*1000).")\n");
440
} elsif ($ssf == 25) {
441
print $positions Encode::encode( "utf8", $airport.",".($f+0.005)."0,".$APT{$airport}{'lat'}.",".$APT{$airport}{'lon'}.",".$APT{$airport}{'com'}{$f}.",".$APT{$airport}{'text'}."\n");
442
print $positions Encode::encode( "utf8", $airport.",".($f-0.005)."0,".$APT{$airport}{'lat'}.",".$APT{$airport}{'lon'}.",".$APT{$airport}{'com'}{$f}.",".$APT{$airport}{'text'}."\n");
443
print $extensions Encode::encode( "utf8", "exten => 01$icao_number".($f*1000-5).",1,Dial(Local/01$icao_number".($f*1000).")\n");
444
print $extensions Encode::encode( "utf8", "exten => 01$icao_number".($f*1000+5).",1,Dial(Local/01$icao_number".($f*1000).")\n");
445
} elsif ($ssf == 50) {
446
print $positions Encode::encode( "utf8", $airport.",".($f+0.005).",".$APT{$airport}{'lat'}.",".$APT{$airport}{'lon'}.",".$APT{$airport}{'com'}{$f}.",".$APT{$airport}{'text'}."\n");
447
print $extensions Encode::encode( "utf8", "exten => 01$icao_number".($f*1000+5).",1,Dial(Local/01$icao_number".($f*1000).")\n");
448
} elsif($ssf == 75) {
449
print $positions Encode::encode( "utf8", $airport.",".($f+0.005)."0,".$APT{$airport}{'lat'}.",".$APT{$airport}{'lon'}.",".$APT{$airport}{'com'}{$f}.",".$APT{$airport}{'text'}."\n");
450
print $positions Encode::encode( "utf8", $airport.",".($f-0.005)."0,".$APT{$airport}{'lat'}.",".$APT{$airport}{'lon'}.",".$APT{$airport}{'com'}{$f}.",".$APT{$airport}{'text'}."\n");
451
print $extensions Encode::encode( "utf8", "exten => 01$icao_number".($f*1000-5).",1,Dial(Local/01$icao_number".($f*1000).")\n");
452
print $extensions Encode::encode( "utf8", "exten => 01$icao_number".($f*1000+5).",1,Dial(Local/01$icao_number".($f*1000).")\n");
455
# ATIS record extension
456
print $extensions Encode::encode( "utf8", "exten => 99$icao_number".($f*1000).",1,SendText($airport ".$APT{$airport}{'text'}." $f Record-".$APT{$airport}{'com'}{$f}.")\n");
457
print $extensions Encode::encode( "utf8", "exten => 99$icao_number".($f*1000).",n,Macro(record-atis)\n");
461
print $extensions Encode::encode( "utf8", "exten => 01$icao_number".($f*1000).",1,SendText($airport ".$APT{$airport}{'text'}." $f ".$APT{$airport}{'com'}{$f}.")\n");
462
print $extensions Encode::encode( "utf8", "exten => 01$icao_number".($f*1000).",n,SendURL(http://www.the-airport-guide.com/search.php?by=icao&search=$airport)\n");
463
print $extensions Encode::encode( "utf8", "exten => 01$icao_number".($f*1000).",n,Macro(com)\n");
465
print $positions Encode::encode( "utf8", $airport.",".($f+0.005).",".$APT{$airport}{'lat'}.",".$APT{$airport}{'lon'}.",".$APT{$airport}{'com'}{$f}.",".$APT{$airport}{'text'}."\n");
466
print $extensions Encode::encode( "utf8", "exten => 01$icao_number".($f*1000+5).",1,Dial(Local/01$icao_number".($f*1000).")\n");
467
} elsif ($ssf == 25) {
468
print $positions Encode::encode( "utf8", $airport.",".($f+0.005)."0,".$APT{$airport}{'lat'}.",".$APT{$airport}{'lon'}.",".$APT{$airport}{'com'}{$f}.",".$APT{$airport}{'text'}."\n");
469
print $positions Encode::encode( "utf8", $airport.",".($f-0.005)."0,".$APT{$airport}{'lat'}.",".$APT{$airport}{'lon'}.",".$APT{$airport}{'com'}{$f}.",".$APT{$airport}{'text'}."\n");
470
print $extensions Encode::encode( "utf8", "exten => 01$icao_number".($f*1000-5).",1,Dial(Local/01$icao_number".($f*1000).")\n");
471
print $extensions Encode::encode( "utf8", "exten => 01$icao_number".($f*1000+5).",1,Dial(Local/01$icao_number".($f*1000).")\n");
472
} elsif ($ssf == 50) {
473
print $positions Encode::encode( "utf8", $airport.",".($f+0.005).",".$APT{$airport}{'lat'}.",".$APT{$airport}{'lon'}.",".$APT{$airport}{'com'}{$f}.",".$APT{$airport}{'text'}."\n");
474
print $extensions Encode::encode( "utf8", "exten => 01$icao_number".($f*1000+5).",1,Dial(Local/01$icao_number".($f*1000).")\n");
475
} elsif ($ssf == 75) {
476
print $positions Encode::encode( "utf8", $airport.",".($f+0.005)."0,".$APT{$airport}{'lat'}.",".$APT{$airport}{'lon'}.",".$APT{$airport}{'com'}{$f}.",".$APT{$airport}{'text'}."\n");
477
print $positions Encode::encode( "utf8", $airport.",".($f-0.005)."0,".$APT{$airport}{'lat'}.",".$APT{$airport}{'lon'}.",".$APT{$airport}{'com'}{$f}.",".$APT{$airport}{'text'}."\n");
478
print $extensions Encode::encode( "utf8", "exten => 01$icao_number".($f*1000-5).",1,Dial(Local/01$icao_number".($f*1000).")\n");
479
print $extensions Encode::encode( "utf8", "exten => 01$icao_number".($f*1000+5).",1,Dial(Local/01$icao_number".($f*1000).")\n");
484
print $extensions ";\n";
489
printf("Writing navaids data ...\n");
490
# write VORs to files
491
foreach $vor (sort(keys(%NAV))) {
493
$icao_number = icao2number($vor);
495
# write positions NAV
496
print $positions Encode::encode( "utf8", $vor.",".$NAV{$vor}{'frq'}.",".$NAV{$vor}{'lat'}.",".$NAV{$vor}{'lon'}.",VOR,".$NAV{$vor}{'text'}."\n");
499
#$tmp = sprintf("%4s %-20s %3.3f %-.16s %-20s\n",$vor,"",$NAV{$vor}{'frq'},"01".$icao_number.$NAV{$vor}{'frq'}*1000,$NAV{$vor}{'text'});
500
#print $phonebook Encode::encode( "utf8", $tmp);
502
# write extensions.conf
503
print $extensions Encode::encode( "utf8", "; VOR $vor $NAV{$vor}{'frq'} - $NAV{$vor}{'text'}\n");
504
print $extensions Encode::encode( "utf8", "exten => 01$icao_number".($NAV{$vor}{'frq'}*1000).",1,SendText($vor ".$NAV{$vor}{'text'}." ".$NAV{$vor}{'frq'}.")\n");
505
print $extensions Encode::encode( "utf8", "exten => 01$icao_number".($NAV{$vor}{'frq'}*1000).",n,Macro(vor,$vor)\n");
515
#print $phonebook $phonebook_post;
518
prt("Done file outputs... $out_fil1 and $out_pos...\n");
521
###############################################
524
prt("$pgmname, version $VERS\n");
526
prt(" --help (-h, -?) = This help and exit(2)\n");
528
prt("Input files:\n");
529
prt(" --air <file> (-a) = Name of airports file. (def=$FG_AIRPORTS)\n");
530
prt(" --nav <file> (-n) = Name of navaids file. (def=$FG_NAVAIDS)\n");
532
prt("Output files:\n");
533
prt(" --conf <file> (-c) = Name of conf file. (def=$out_fil1)\n");
534
prt(" --pos <file> (-p) = Name of position file. (def=$out_pos)\n");
535
#prt(" --book <file> (-b) = Name of phonebook file. (def=$out_phon)\n");
538
prt("Read the input files, and output the extensions (conf),\n");
539
prt("used to configure the asterisk voip server, and output the positions file,\n");
540
prt("used by standalone fgcom to establish the location of the caller.\n");
547
die("ERROR: [$arg] must have a following argument!\n") if (!@av);
556
### prt("ARG [$arg]\n");
558
$sarg = substr($arg,1);
559
$sarg = substr($sarg,1) while ($sarg =~ /^-/);
564
$FG_AIRPORTS = $sarg;
565
} elsif ($sarg =~ /^n/) {
570
} elsif ($sarg =~ /^c/) {
575
} elsif ($sarg =~ /^p/) {
580
# } elsif ($sarg =~ /^b/) {
585
} elsif (($sarg =~ /^h/)||($sarg eq '?')) {
588
die("ERROR: Unknown argument [$arg]\n");
591
die("ERROR: Unknown argument [$arg]\n");
596
if (! -f $FG_AIRPORTS ) {
597
$arg .= "Error: Unable to locate [$FG_AIRPORTS]!\n";
600
if (! -f $FG_NAVAIDS ) {
601
$arg .= "Error: Unable to locate [$FG_NAVAIDS]!\n";
604
die("Invalid input file!\n$arg") if ($bad);