#!/usr/bin/perl # NAME: findapX.pl # AIM: Read X-Plane (Robin Pell) apt.dat, and find an airport given the name, # 2020-02-28 - Review - Add search_per_country if no exact match found... # 21/08/2013 - Also output the airport frequency list # 09/04/2013 - Add -2fg to output the found airport in FG apt.dat 8.10 format # geoff mclane - http://geoffmclane.com/mperl/index.htm - 20100801 use strict; use warnings; use File::Copy; use Math::Trig; use File::Basename; # split path ($name,$dir,$ext) = fileparse($file [, qr/\.[^.]*/] ) use Time::HiRes qw( gettimeofday tv_interval ); my $os = $^O; my $perl_dir = '/home/geoff/bin'; my $PATH_SEP = '/'; my $temp_dir = '/tmp'; if ($os =~ /win/i) { $perl_dir = 'C:\GTools\perl'; $temp_dir = $perl_dir; $PATH_SEP = "\\"; } unshift(@INC, $perl_dir); require 'lib_utils.pl' or die "Unable to load 'lib_utils.pl' Check paths in \@INC...\n"; require 'fg_wsg84.pl' or die "Unable to load fg_wsg84.pl ...\n"; require "Bucket2.pm" or die "Unable to load Bucket2.pm ...\n"; #require "Bucket.pm" or die "Unable to load Bucket.pm ...\n"; my $DSCNROOT = 'D:\Scenery\terrascenery\data\Scenery'; # ============================================================================= # This NEEDS to be adjusted to YOUR particular default location of these files. my $FGROOT = "D:/FG/xplane/1000"; ###my $FGROOT = "D:/SAVES/xplane"; my $SCENEROOT = (exists $ENV{'FG_SCENERY'})? $ENV{'FG_SCENERY'} : $DSCNROOT; my $APT_FILE = "$FGROOT/apt.dat"; # the airports data file ###my $APT_FILE = "$FGROOT/apt4.dat"; # the airports data file my $NAV_FILE = "$FGROOT/earth_nav.dat"; # the NAV, NDB, etc. data file my $FIX_FILE = "$FGROOT/earth_fix.dat"; # the FIX data file my $LIC_FILE = "$FGROOT/AptNavGNULicence.txt"; # ============================================================================= # log file stuff our ($LF); my $pgmname = $0; if ($pgmname =~ /(\\|\/)/) { my @tmpsp = split(/(\\|\/)/,$pgmname); $pgmname = $tmpsp[-1]; } my $outfile = $temp_dir.$PATH_SEP."temp.$pgmname.txt"; open_log($outfile); my $t0 = [gettimeofday]; my $VERS = "20200228"; #my $out_path = 'C:\FG\17\fgx-globe\apt1000'; my $out_path = $temp_dir.$PATH_SEP."temp-apts3"; #my $out_path = $temp_dir.$PATH_SEP."temp-apts2"; #my $out_path = $temp_dir.$PATH_SEP."temp-apts"; # program variables - set during running # different searches -icao=LFPO, -latlon=1,2, or -name="airport name" # KSFO San Francisco Intl (37.6208607739872,-122.381074803838) my $aptdat = $APT_FILE; my $navdat = $NAV_FILE; my $licfil = $LIC_FILE; my $SRCHICAO = 0; # search using icao id ... takes precedence my $SRCHONLL = 0; # search using lat,lon my $SRCHNAME = 0; # search using name my $SHOWNAVS = 0; # show navaids around airport found my $aptname = "strasbourg"; my $apticao = 'KSFO'; my $in_lat = 37.6; my $in_lon = -122.4; my $maxlatd = 0.5; my $maxlond = 0.5; my $nmaxlatd = 0.1; my $nmaxlond = 0.1; my $max_cnt = 0; # maximum airport count - 0 = no limit my $max_range_km = 5; # range search using KILOMETERS my $out_file = $temp_dir.$PATH_SEP."tempapt"; my $out_base = 'apt1000'; # features and options my $tryharder = 0; # Expand the search for NAVAID, until at least 1 found my $usekmrange = 0; # search using KILOMETER range - see $max_range_km my $sortbyfreq = 1; # sort NAVAIDS by FREQUENCY my $load_log = 0; my $show_fg_form = 0; my $gen_threshold_xml = 0; my $write_output = 1; my $add_newline = 1; my $skip_done_files = 0; my $output_full_list = 0; my $do_nav_filter = 1; my $output_as_json = 0; # variables for range using distance calculation my $PI = 3.1415926535897932384626433832795029; my $D2R = $PI / 180; my $R2D = 180 / $PI; my $ERAD = 6378138.12; my $DIST_FACTOR = $ERAD; #/** Meters to Nautical Miles. 1 nm = 6076.11549 feet */ my $METER_TO_NM = 0.0005399568034557235; #/** Nautical Miles to Meters */ my $NM_TO_METER = 1852; #/** Feet to Meters */ my $FEET_TO_METER = 0.3048; #/** Meters to Feet */ my $METER_TO_FEET = 3.28083989501312335958; my ($file_version); my $av_apt_lat = 0; # later will be $tlat / $ac; my $av_apt_lon = 0; # later $tlon / $ac; # apt.dat CODES - see http://x-plane.org/home/robinp/Apt850.htm for DETAILS #my $aln = '1'; # airport line #my $rln = '10'; # runways/taxiways line 810 OLD CODE #my $sealn = '16'; # Seaplane base header data. #my $heliln = '17'; # Heliport header data. #my $rln = '100'; # land runways #my $water = '101'; # Water runway #my $heli = '102'; # Helipad # offsets into land runway array #my $of_lat1 = 9; #my $of_lon1 = 10; #my $of_lat2 = 18; #my $of_lon2 = 19; my $twrln = '14'; # Tower view location. my $rampln = '15'; # Ramp startup position(s) my $bcnln = '18'; # Airport light beacons my $wsln = '19'; # windsock my $minatc = '50'; my $twrfrq = '54'; # like 12210 TWR my $appfrq = '55'; # like 11970 ROTTERDAM APP my $maxatc = '56'; my $lastln = '99'; # end of file # nav.dat.gz CODES my $navNDB = '2'; my $navVOR = '3'; my $navILS = '4'; my $navLOC = '5'; my $navGS = '6'; my $navOM = '7'; my $navMM = '8'; my $navIM = '9'; my $navVDME = '12'; my $navNDME = '13'; my @navset = ($navNDB, $navVOR, $navILS, $navLOC, $navGS, $navOM, $navMM, $navIM, $navVDME, $navNDME); my @navtypes = qw( NDB VOR ILS LOC GS OM NM IM VDME NDME ); my $maxnnlen = 4; my $actnav = ''; my $line = ''; my $apt = ''; my $alat = 0; my $alon = 0; my $glat = 0; my $glon = 0; my $rlat = 0; my $rlon = 0; my $dlat = 0; my $dlon = 0; my $diff = 0; my $rwycnt = 0; my $icao = ''; my $name = ''; my @aptlist = (); my @aptlist2 = (); my @navlist = (); my @navlist2 = (); my $totaptcnt = 0; my $acnt = 0; my @lines = (); my $cnt = 0; my $loadlog = 0; my $outcount = 0; my @tilelist = (); my $max_range_used = $max_range_km; # if expanded usign tryharder this is final value my @warnings = (); my $max_name_len = 4; # debug tests # =================== my $test_name = 0; # to TEST a NAME search my $def_name = "hong kong"; my $test_ll = 0; # to TEST a LAT,LON search my $def_lat = 37.6; my $def_lon = -122.4; my $test_icao = 0; # to TEST an ICAO search my $def_icao = 'LSTZ'; # 'VHHH'; ## 'KHAF'; ## LFPO'; ## 'KSFO'; # debug my $dbg1 = 0; # show airport during finding ... my $dbg2 = 0; # show navaid during finding ... my $dbg3 = 0; # show count after finding my $verb3 = 0; my $dbg10 = 0; # show EVERY airport my $dbg11 = 0; # prt( "$name $icao runways $rwycnt\n" ) if ($dbg11); # =================== ### program variables my $verbosity = 0; sub VERB1() { return $verbosity >= 1; } sub VERB2() { return $verbosity >= 2; } sub VERB5() { return $verbosity >= 5; } sub VERB9() { return $verbosity >= 9; } sub show_warnings($) { my ($val) = @_; if (@warnings) { prt( "\nGot ".scalar @warnings." WARNINGS...\n" ); foreach my $itm (@warnings) { prt("$itm\n"); } prt("\n"); } else { prt( "\nNo warnings issued.\n\n" ) if (VERB9()); } } sub pgm_exit($$) { my ($val,$msg) = @_; if (length($msg)) { $msg .= "\n" if (!($msg =~ /\n$/)); prt($msg); } show_warnings($val); close_log($outfile,$load_log); exit($val); } sub prtw($) { my ($tx) = shift; $tx =~ s/\n$//; prt("$tx\n"); push(@warnings,$tx); } ######################################################################## ### SUBS sub trimall($) { # version 20061127 my ($ln) = shift; chomp $ln; # remove CR (\n) $ln =~ s/\r$//; # remove LF (\r) $ln =~ s/\t/ /g; # TAB(s) to a SPACE $ln =~ s/\s\s/ /g while ($ln =~ /\s\s/); # all double space to SINGLE $ln = substr($ln,1) while ($ln =~ /^\s/); # remove all LEADING space $ln = substr($ln,0, length($ln) - 1) while ($ln =~ /\s$/); # remove all TRAILING space return $ln; } #Surface Type Code Surface type of runways or taxiways #1 Asphalt #2 Concrete #3 Turf or grass #4 Dirt (brown) #5 Gravel (grey) #12 Dry lakebed (eg. At KEDW) Example: KEDW (Edwards AFB) #13 Water runways Nothing displayed #14 Snow or ice Poor friction. Runway markings cannot be added. #15 Transparent Hard surface, but no texture/markings (use in custom scenery) # offset 2 in runway array my %runway_surface = ( 1 => 'Asphalt', 2 => 'Concrete', 3 => 'Turf/grass', 4 => 'Dirt', 5 => 'Gravel', 6 => 'H-Asphalt', # helepad (big 'H' in the middle). 7 => 'H_Concrete', # helepad (big 'H' in the middle). 8 => 'H_Turf', # helepad (big 'H' in the middle). 9 => 'H_Dirt', # helepad (big 'H' in the middle). 10 => 'T_Asphalt', # taxiway - with yellow hold line across long axis (not available from WorldMaker). 11 => 'T_Concrete', # taxiway - with yellow hold line across long axis (not available from WorldMaker). 12 => 'Dry_Lakebed', # (eg. at KEDW Edwards AFB). 13 => 'Water', # runways (marked with bobbing buoys) for seaplane/floatplane bases (available in X-Plane 7.0 and later). 14 => 'Snow', 15 => 'Transparent' ); my %frequency_code = ( 50 => "ATIS", # 50 Recorded AWOS, ASOS or ATIS 51 => "CTAF", # 51 Unicom Unicom (US), CTAF (US), Radio (UK) 52 => "CLD", # 52 CLD Clearance Delivery 53 => "GND", # 53 GND Ground 54 => "TWR", # 54 TWR Tower 55 => "APP", # 55 APP Approach 56 => "DEP" # 56 DEP Departure ); sub get_current_threshold($$) { my ($icao,$rt) = @_; return 0 if (! -d $SCENEROOT); return 0 if (length($icao) < 3); my $dir = $SCENEROOT.$PATH_SEP."Airports"; return 0 if (! -d $dir); $dir .= $PATH_SEP.substr($icao,0,1); $dir .= $PATH_SEP.substr($icao,1,1); $dir .= $PATH_SEP.substr($icao,2,1); $dir .= $PATH_SEP.$icao.".threshold.xml"; return 0 if (! -f $dir); ${$rt} = $dir; return 1; } sub show_airports_found { my ($mx) = shift; # limit the AIRPORT OUTPUT my $scnt = $acnt; my $tile = ''; my ($off,$rra,$rwa,$rha,$rfa,$i); my ($j,$rcnt,$rrwy); my ($type,$wid,$surf,$code,$smth,$ctln,$elit); my ($rwy1,$lat1,$lon1,$rwy2,$lat2,$lon2,$rlen); my ($dist,$az1,$az2,$racnt,$hdg1,$hdg2); my ($displ1,$displ2,$stopw1,$stopw2,$xml,$file); my ($tmp); if ($mx && ($mx < $scnt)) { $scnt = $mx; prt( "Listing $scnt of $acnt aiports " ); } else { prt( "Listing $scnt aiport(s) " ); } if ($SRCHICAO) { prt( "with ICAO [$apticao] ...\n" ); } elsif ($SRCHONLL) { prt( "around lat,lon [$in_lat,$in_lon], using diff [$maxlatd,$maxlond] ...\n" ); } else { # $SRCHNAME prt( "matching [$aptname] ...\n" ); } for ($i = 0; $i < $scnt; $i++) { $xml = "\n\n"; # 0 1 2 3 4 5 # push(@aptlist2, [$diff, $icao, $name, $alat, $alon, $off]); $diff = $aptlist2[$i][0]; $icao = $aptlist2[$i][1]; $name = $aptlist2[$i][2]; $alat = $aptlist2[$i][3]; $alon = $aptlist2[$i][4]; $off = $aptlist2[$i][5]; $tile = get_bucket_info( $alon, $alat ); $icao .= ' ' while (length($icao) < 4); $line = $diff; $line = ' '.$line while (length($line) < 6); #$line .= ' '.$icao.' '.$name.' ('.$alat.','.$alon.") tile=$tile"; $line .= ' '.$icao.' '.$name.' ('.$alat.','.$alon.") tile=".get_tile($alon,$alat); prt("$line\n"); # 0 1 2 3 4 5 6 7 8 #push(@aptlist, [$diff, $icao, $name, $alat, $alon, \@r, \@w, \@h, \@f]); $rra = $aptlist[$off][5]; # runway ref array $rwa = $aptlist[$off][6]; # waterways ref array $rha = $aptlist[$off][7]; # helipad ref array $rfa = $aptlist[$off][8]; # freq ref array $rcnt = scalar @{$rra}; if ($rcnt > 0) { if ($output_as_json) { $line = '{"runway_count":'.$rcnt.',"runways":['."\n"; } else { $line = 'rwys='.$rcnt.":\n"; } for ($j = 0; $j < $rcnt; $j++) { $rrwy = ${$rra}[$j]; $racnt = scalar @{$rrwy}; if ($racnt < 20) { prtw("WARNING: icao=$icao: Array count $racnt! SKIPPING\n"); prt(join(",",@{$rrwy})."\n"); next; } $type = ${$rrwy}[0]; #0 - 100 for land runways $wid = ${$rrwy}[1]; #1 - 29.87 - Width of runway in metres Two decimal places recommended. Must be >= 1.00 $surf = ${$rrwy}[2]; #2 - 3 - Code defining the surface type (concrete, asphalt, etc) Integer value for a Surface Type Code $code = ${$rrwy}[3]; #3 - 0 - Code defining a runway shoulder surface type 0=no shoulder, 1=asphalt shoulder, 2=concrete shoulder $smth = ${$rrwy}[4]; #4 - 0.15 - Runway smoothness (not used by X-Plane yet) 0.00 (smooth) to 1.00 (very rough). Default is 0.25 $ctln = ${$rrwy}[5]; #5 - 0 - Runway centre-line lights 0=no centerline lights, 1=centre line lights $elit = ${$rrwy}[6]; #6 - 0 - Runway edge lighting (also implies threshold lights) 0=no edge lights, 2=medium intensity edge lights # #7 - 1 - Auto-generate distance-remaining signs (turn off if created manually) 0=no auto signs, 1=auto-generate signs # #The following fields are repeated for each end of the runway $rwy1 = ${$rrwy}[8]; #8 - 13L - Runway number (eg. 31R, 02). Leading zeros are required. Two to three characters. Valid suffixes: L, R or C (or blank) $lat1 = sprintf("%.8f",${$rrwy}[9]); #9 - 47.53801700 - Latitude of runway threshold (on runway centerline) in decimal degrees Eight decimal places supported $lon1 = sprintf("%.8f",${$rrwy}[10]); #10 - -122.30746100 - Longitude of runway threshold (on runway centerline) in decimal degrees Eight decimal places supported $displ1 = ${$rrwy}[11]; # #11 - 73.15 - Length of displaced threshold in metres (this is included in implied runway length) Two decimal places (metres). Default is 0.00 $stopw1 = ${$rrwy}[12]; # #12 - 0.00 - Length of overrun/blast-pad in metres (not included in implied runway length) Two decimal places (metres). Default is 0.00 # #13 - 2 - Code for runway markings (Visual, non-precision, precision) Integer value for Runway Marking Code # #14 - 0 - Code for approach lighting for this runway end Integer value for Approach Lighting Code # #15 - 0 - Flag for runway touchdown zone (TDZ) lighting 0=no TDZ lighting, 1=TDZ lighting # #16 - 1 - Code for Runway End Identifier Lights (REIL) 0=no REIL, 1=omni-directional REIL, 2=unidirectional REIL $rwy2 = ${$rrwy}[17]; #17 - 31R $lat2 = sprintf("%.8f",${$rrwy}[18]); #18 - 47.52919200 $lon2 = sprintf("%.8f",${$rrwy}[19]); #19 - -122.30000000 $displ2 = ${$rrwy}[20]; # #20 - 110.95 $stopw2 = ${$rrwy}[21]; # #21 - 0.00 # #22 - 2 # #23 - 0 # #24 - 0 # #25 - 1 $rlen = fg_geo_inverse_wgs_84($lat1, $lon1, $lat2, $lon2, \$az1, \$az2, \$dist); $hdg1 = $az1; $hdg2 = $az2; if ($output_as_json) { $line .= '{'; $line .= '"len_m":'.int($dist); $line .= ',"wid_m":'.$wid; $line .= ',"hdg":'.int($hdg1); $line .= ',"surf":"'; if (defined $runway_surface{$surf}) { $line .= $runway_surface{$surf}; } else { $line .= $surf; } $line .= '"'; $line .= ',"rwy1":"'.$rwy1.'"'; $line .= ',"lat1":'.$lat1; $line .= ',"lon1":'.$lon1; $line .= ',"displ1":'.$displ1; $line .= ',"stopw1":'.$stopw1; $line .= ',"rwy2":"'.$rwy2.'"'; $line .= ',"lat2":'.$lat2; $line .= ',"lon2":'.$lon2; $line .= "}"; if (($j + 1) < $rcnt) { $line .= ','; } else { $line .= ']'; } $line .= "\n"; } else { $line .= int($dist * $METER_TO_FEET)."x".int($wid*$METER_TO_FEET)." ft, hdg=".int($az1).","; if (defined $runway_surface{$surf}) { $line .= $runway_surface{$surf}; } else { $line .= $surf; } my $dsp1 = int($displ1*$METER_TO_FEET).','.int($stopw1*$METER_TO_FEET); my $dsp2 = int($displ2*$METER_TO_FEET).','.int($stopw2*$METER_TO_FEET); $line .= " $rwy1 $lat1,$lon1 ($dsp1) $rwy2 $lat2,$lon2 ($dsp2)\n"; } if ($gen_threshold_xml) { my $lato1 = $lat1; my $lono1 = $lon2; my $lato2 = $lat2; my $lono2 = $lon2; my ($s); if (($displ1 + $stopw1) > 0) { $s = ($displ1 + $stopw1) * $FEET_TO_METER; fg_geo_direct_wgs_84( $lat1, $lon1, $hdg1, $s, \$lato1, \$lono1, \$az2 ); } $xml .= " \n"; $xml .= " \n"; $xml .= " $rwy1\n"; $xml .= " $lato1\n"; $xml .= " $lono1\n"; $xml .= " $hdg1\n"; $xml .= " "; $xml .= $displ1 * $FEET_TO_METER; $xml .= "\n"; $xml .= " "; $xml .= $stopw1 * $FEET_TO_METER; $xml .= "\n"; $xml .= " \n"; if (($displ2 + $stopw2) > 0) { $s = ($displ2 + $stopw2) * $FEET_TO_METER; fg_geo_direct_wgs_84( $lat2, $lon2, $hdg2, $s, \$lato2, \$lono2, \$az2 ); } $xml .= " \n"; $xml .= " $rwy2\n"; $xml .= " $lato2\n"; $xml .= " $lono2\n"; $xml .= " $hdg2\n"; $xml .= " "; $xml .= $displ2 * $FEET_TO_METER; $xml .= "\n"; $xml .= " "; $xml .= $stopw2 * $FEET_TO_METER; $xml .= "\n"; $xml .= " \n"; $xml .= " \n"; } } prt($line); } $outcount++; add_2_tiles($tile); if ($gen_threshold_xml) { $icao = $aptlist2[$i][1]; # get ICAO back $xml .= "\n"; $file = $temp_dir.$PATH_SEP."temp.".$icao.".threshold.xml"; rename_2_old_bak($file); # rename any previous write2file($xml,$file); prt("Threshold XML written to [$file]\n"); if (get_current_threshold($icao,\$tmp)) { prt("Compare this with [$tmp]\n"); } } # 20130821 - show frequencies # $rfa = $aptlist[$off][8]; # freq ref array # array like # 50 12082 LFMV ATIS # 54 12260 AVIGNON TWR # 55 12087 AVIGNON APP # my %frequency_code = ( # 50 => "ATIS", # 50 Recorded AWOS, ASOS or ATIS # 51 => "CTAF", # 51 Unicom Unicom (US), CTAF (US), Radio (UK) # 52 => "CLD", # 52 CLD Clearance Delivery # 53 => "GND", # 53 GND Ground # 54 => "TWR", # 54 TWR Tower # 55 => "APP", # 55 APP Approach # 56 => "DEP" # 56 DEP Departure # ); my ($freq); $rcnt = scalar @{$rfa}; prt("Freq:$rcnt: "); for ($j = 0; $j < $rcnt; $j++) { $rrwy = ${$rfa}[$j]; $type = ${$rrwy}[0]; $freq = ${$rrwy}[1] / 100; if (defined $frequency_code{$type}) { $type = $frequency_code{$type}; } prt("$type $freq "); } prt("\n"); } # for EACH airport ### prt( "Done $scnt list ...\n" ); } sub get_tile { # $alon, $alat my ($lon, $lat) = @_; my $tile = 'e'; if ($lon < 0) { $tile = 'w'; $lon = -$lon; } my $ilon = int($lon / 10) * 10; if ($ilon < 10) { $tile .= "00$ilon"; } elsif ($ilon < 100) { $tile .= "0$ilon"; } else { $tile .= "$ilon" } if ($lat < 0) { $tile .= 's'; $lat = -$lat; } else { $tile .= 'n'; } my $ilat = int($lat / 10) * 10; if ($ilat < 10) { $tile .= "0$ilat"; } elsif ($ilon < 100) { $tile .= "$ilat"; } else { $tile .= "$ilat" } return $tile; } sub add_2_tiles { # $tile my ($tl) = shift; if (@tilelist) { foreach my $t (@tilelist) { if ($t eq $tl) { return 0; } } } push(@tilelist, $tl); return 1; } sub is_valid_nav { my ($tx) = shift; if ($tx && length($tx)) { my $txt = "$tx"; my $cnt = 0; foreach my $n (@navset) { $cnt++; if ($n eq $txt) { $actnav = $navtypes[$cnt]; return $cnt; } } } return 0; } sub set_average_apt_latlon { my $ac = scalar @aptlist2; my $tlat = 0; my $tlon = 0; if ($ac) { for (my $i = 0; $i < $ac; $i++ ) { $alat = $aptlist2[$i][3]; $alon = $aptlist2[$i][4]; $tlat += $alat; $tlon += $alon; } $av_apt_lat = $tlat / $ac; $av_apt_lon = $tlon / $ac; } } # push(@aptlist2, [$diff, $icao, $name, $alat, $alon]); # my $nmaxlatd = 1.5; # my $nmaxlond = 1.5; sub near_an_airport { my ($lt, $ln, $dist, $az) = @_; my ($az1, $az2, $s, $ret); my $ac = scalar @aptlist2; my ($x,$y,$z) = fg_ll2xyz($ln,$lt); # get cart x,y,z my $d2 = $max_range_km * 1000; # get meters for (my $i = 0; $i < $ac; $i++ ) { $diff = $aptlist2[$i][0]; $icao = $aptlist2[$i][1]; $name = $aptlist2[$i][2]; $alat = $aptlist2[$i][3]; $alon = $aptlist2[$i][4]; if ($usekmrange) { my ($xb, $yb, $yz) = fg_ll2xyz($alon, $alat); my $dst = sqrt( fg_coord_dist_sq( $x, $y, $z, $xb, $yb, $yz ) ) * $DIST_FACTOR; if ($dst < $d2) { $s = -1; $az1 = -1; $ret = fg_geo_inverse_wgs_84($alat, $alon, $lt, $ln, \$az1, \$az2, \$s); $$dist = $s; $$az = $az1; return ($i + 1); } } else { my $td = abs($lt - $alat); my $nd = abs($ln - $alon); if (($td < $nmaxlatd)&&($nd < $nmaxlond)) { $s = -1; $az1 = -1; $ret = fg_geo_inverse_wgs_84($alat, $alon, $lt, $ln, \$az1, \$az2, \$s); $$dist = $s; $$az = $az1; return ($i + 1); } } } return 0; } sub show_navaids_found { my ($ic, $in, $line, $lcnt, $dnone); my ($diff, $icao, $alat, $alon); my ($typ, $nlat, $nlon, $nalt, $nfrq, $nrng, $nfrq2, $nid, $name, $off); my ($dist, $az, $adkm, $ahdg); my $hdr = "Type Latitude Logitude Alt. Freq. Range Frequency2 ID Name"; #prt( "$actnav, $typ, $nlat, $nlon, $nalt, $nfrq, $nrng, $nfrq2, $nid, $name ($off)\n"); #push(@navlist2, [$typ, $nlat, $nlon, $nalt, $nfrq, $nrng, $nfrq2, $nid, $name, $off]); my $ac = scalar @aptlist2; @navlist2 = sort mycmp_ascend_n4 @navlist2 if ($sortbyfreq); my $nc = scalar @navlist2; my %freqs = (); my @offs = (); for ( $in = 0; $in < $nc; $in++ ) { $nfrq = $navlist2[$in][4]; if (!defined $freqs{$nfrq}) { push(@offs,$in); $freqs{$nfrq} = 1 } } $nc = scalar @offs; prt( "For $ac ap, found $nc NAVAID freqs, " ); if ($usekmrange) { $ahdg = sprintf("%.1f",$max_range_used); prt( "within [$ahdg] Km ...\n" ); } else { prt( "within [$nmaxlatd,$nmaxlond] degrees ...\n" ); } $lcnt = 0; for ($ic = 0; $ic < $ac; $ic++) { $diff = $aptlist2[$ic][0]; $icao = $aptlist2[$ic][1]; $name = $aptlist2[$ic][2]; $alat = $aptlist2[$ic][3]; $alon = $aptlist2[$ic][4]; $icao .= ' ' while (length($icao) < 4); $line = $diff; $line = ' '.$line while (length($line) < 6); $line .= ' '.$icao.' '.$name.' ('.$alat.','.$alon.')'; prt("\n") if ($ic); prt("$line\n"); $outcount++; $dnone = 0; ### for ( $in = 0; $in < $nc; $in++ ) { foreach $in (@offs) { $typ = $navlist2[$in][0]; $nlat = $navlist2[$in][1]; $nlon = $navlist2[$in][2]; $nalt = $navlist2[$in][3]; $nfrq = $navlist2[$in][4]; $nrng = $navlist2[$in][5]; $nfrq2 = $navlist2[$in][6]; $nid = $navlist2[$in][7]; $name = $navlist2[$in][8]; $off = $navlist2[$in][9]; $dist = $navlist2[$in][10]; $az = $navlist2[$in][11]; if ($off == ($ic + 1)) { # it is FOR this airport is_valid_nav($typ); # NDB 50.049000, 008.328667, 490, 399, 25, 0.000, WBD, Wiesbaden NDB (ap=2 nnnKm on 270.1) # Type Latitude Logitude Alt. Freq. Range Frequency2 ID Name # VOR 37.61948300, -122.37389200, 13, 11580, 40, 17.0, SFO, SAN FRANCISCO VOR-DME (ap=1 nnnKm on 1.1) #prt( "$actnav, $typ, $nlat, $nlon, $nalt, $nfrq, $nrng, $nfrq2, $nid, $name ($off)\n"); $line = $actnav; $line .= ' ' while (length($line) < $maxnnlen); $line .= ' '; $nalt = ' '.$nalt while (length($nalt) < 5); $nfrq = ' '.$nfrq while (length($nfrq) < 5); $nrng = ' '.$nrng while (length($nrng) < 5); $nfrq2 = ' '.$nfrq2 while (length($nfrq2) < 10); $nid = ' '.$nid while (length($nid) < 4); $nlat = ' '.$nlat while (length($nlat) < 12); $nlon = ' '.$nlon while (length($nlon) < 13); $adkm = sprintf( "%0.2f", ($dist / 1000.0)); # get kilometers $ahdg = sprintf( "%0.1f", $az ); # and azimuth $line .= "$nlat, $nlon, $nalt, $nfrq, $nrng, $nfrq2, $nid, $name (ap=$off ".$adkm."Km on $ahdg)"; if ($dnone == 0) { #prt( "Type Latitude Logitude Alt. Freq. Range Frequency2 ID Name\n" ); prt( "$hdr\n" ); $dnone = 1; } prt( "$line\n" ); $outcount++; $lcnt++; add_2_tiles( get_bucket_info( $nlon, $nlat ) ); } } prt( "$hdr\n" ) if ($dnone && ($outcount > 10)); } prt( "Listed $lcnt NAVAIDS ...\n" ); } sub set_apt_version($$) { my ($ra,$cnt) = @_; if ($cnt < 5) { prt("ERROR: Insufficient lines to be an apt.dat file!\n"); exit(1); } my $line = trimall(${$ra}[0]); if ($line ne 'I') { prt("ERROR: File does NOT begin with an 'I'!\n"); exit(1); } $line = trimall(${$ra}[1]); if ($line =~ /^(\d+)\s+Version\s+/i) { $file_version = $1; prt("Dealing with file version [$file_version]\n"); } else { prt("ERROR: File does NOT begin with Version info!\n"); exit(1); } } # sort by type sub mycmp_ascend_n0 { return -1 if (${$a}[0] < ${$b}[0]); return 1 if (${$a}[0] > ${$b}[0]); return 0; } # sort by ICAO text sub mycmp_ascend_n1 { if (${$a}[1] lt ${$b}[1]) { return -1; } if (${$a}[1] gt ${$b}[1]) { return 1; } return 0; } # put least first sub mycmp_ascend_n4 { if (${$a}[4] < ${$b}[4]) { return -1; } if (${$a}[4] > ${$b}[4]) { return 1; } return 0; } sub mycmp_ascend_n5 { if (${$a}[5] < ${$b}[5]) { return -1; } if (${$a}[5] > ${$b}[5]) { return 1; } return 0; } sub mycmp_ascend_n6 { if (${$a}[6] < ${$b}[6]) { return -1; } if (${$a}[6] > ${$b}[6]) { return 1; } return 0; } # put least first sub mycmp_ascend { if (${$a}[0] < ${$b}[0]) { prt( "-[".${$a}[0]."] < [".${$b}[0]."]\n" ) if $verb3; return -1; } if (${$a}[0] > ${$b}[0]) { prt( "+[".${$a}[0]."] < [".${$b}[0]."]\n" ) if $verb3; return 1; } prt( "=[".${$a}[0]."] == [".${$b}[0]."]\n" ) if $verb3; return 0; } sub mycmp_decend { if (${$a}[0] < ${$b}[0]) { prt( "+[".${$a}[0]."] < [".${$b}[0]."]\n" ) if $verb3; return 1; } if (${$a}[0] > ${$b}[0]) { prt( "-[".${$a}[0]."] < [".${$b}[0]."]\n" ) if $verb3; return -1; } prt( "=[".${$a}[0]."] == [".${$b}[0]."]\n" ) if $verb3; return 0; } sub mycmp_ascend_a1 { return -1 if (${$a}[1] lt ${$b}[1]); return 1 if (${$a}[1] gt ${$b}[1]); return 0; } sub fix_airport_name($) { my $name = shift; my @arr = split(/\s/,$name); my $nname = ''; my $len; foreach $name (@arr) { $nname .= ' ' if (length($nname)); $nname .= uc(substr($name,0,1)); $len = length($name); if ($len > 1) { $nname .= lc(substr($name,1)); } } return $nname; } sub in_world_range($$) { my ($lat,$lon) = @_; if (($lat <= 90)&& ($lat >= -90)&& ($lon <= 180)&& ($lon >= -180)) { return 1; } return 0; } sub find_ils_for_apt($) { my $find = shift; # 0 1 2 3 4 5 6 7 8 9 10 # push(@navlist,[$type,$nlat,$nlon,$feet,$freq,$rng,$bear,$id,$icao,$rwy,$name]); my $max = scalar @navlist; my ($i,$ra,$icao,$cnt,$type); $cnt = 0; for ($i = 0; $i < $max; $i++) { $cnt++; $ra = $navlist[$i]; $type = ${$ra}[0]; $icao = ${$ra}[8]; next if ($type != 6); return $cnt if ($find eq $icao); } return 0; } my @aptlist3 = (); sub search_per_country($) { my $ficao = shift; my $len = length($ficao); ## 0 1 2 3 4 5 6 7 8 #push(@aptlist, [$diff, $icao, $name, $alat, $alon, \@ra, \@wa, \@ha, \@fa ]); my $max = scalar @aptlist; my @arr = sort mycmp_ascend_a1 @aptlist; my $ic = substr($ficao,0,2); my ($ra,$icao,$ic2,$diff,$name,$alat,$alon,$rra,$rwa,$rha,$rfa,$off); my $rap = \@aptlist3; #foreach $ra (@aptlist) $off = 0; foreach $ra (@arr) { $icao = ${$ra}[1]; $ic2 = substr($icao,0,2); if ($ic eq $ic2) { $diff = ${$ra}[0]; $name = ${$ra}[2]; $alat = ${$ra}[3]; $alon = ${$ra}[4]; $rra = ${$ra}[5]; $rwa = ${$ra}[6]; $rha = ${$ra}[7]; $rfa = ${$ra}[8]; push(@{$rap}, [ $diff, $icao, $name, $alat, $alon, $rra, $rwa, $rha, $rfa ]); # print out $icao .= ' ' while (length($icao) < 4); $name .= ' ' while (length($name) < $max_name_len); prt("Found $icao, $name, $alat, $alon... Off $off, $diff\n"); #pgm_exit(1,"TEMP EXIT\n"); } $off++; } } # 1000 Version - data cycle 2012.08, build 20121293 sub load_apt_data { prt("Loading $aptdat file ...\n"); mydie("ERROR: Can NOT locate $aptdat ...$!...\n") if ( !( -f $aptdat) ); ###open IF, "<$aptdat" or mydie("OOPS, failed to open [$aptdat] ... check name and location ...\n"); open IF, "<$aptdat" or mydie( "ERROR: CAN NOT OPEN $aptdat...$!...\n" ); @lines = ; close IF; $cnt = scalar @lines; prt( "Processing $cnt lines ... airports, runways, and taxiways ...\n" ) if (VERB9()); set_apt_version( \@lines, $cnt ); if ($SRCHICAO) { prt( "Searching for ICAO [$apticao] ...\n" ); } elsif ($SRCHONLL) { prt( "Searching for LAT,LON [$in_lat,$in_lon], using max. diff. [$maxlatd,$maxlond] ...\n" ); } else { # $SRCHNAME (default) prt( "Searching for NAME [$aptname] ...\n" ); } my ($rlat1,$rlat2,$rlon1,$rlon2,$type,$len,$lnn,$add,$off); my ($nlen); my @runways = (); my $wwcnt = 0; my @waterways = (); my $helicnt = 0; my @heliways = (); my $trwycnt = 0; my @freqs = (); foreach $line (@lines) { $lnn++; $line = trimall($line); $len = length($line); next if ($lnn < 3); # skip first two lines next if ($len == 0); ###prt("$line\n"); my @arr = split(/\s+/,$line); $type = $arr[0]; if ($line =~ /^99\s?/) { # 99, followed by space, count 0 or more ... prt( "Reached END OF FILE (99)... \n" ) if ($dbg1); last; } elsif (($line =~ /^1\s+/)||($line =~ /^16\s+/)||($line =~ /^17\s+/)) { # start with '1' $trwycnt = $rwycnt; $trwycnt += $wwcnt; $trwycnt += $helicnt; if (length($apt) && ($trwycnt > 0)) { $alat = $glat / $trwycnt; $alon = $glon / $trwycnt; $dlat = abs( $in_lat - $alat ); $dlon = abs( $in_lon - $alon ); $diff = int( ($dlat * 10) + ($dlon * 10) ); # 0 1 2 3 4 5+ # like: 1 28 1 0 VHHH Hong Kong Intl my @arr2 = split(/\s+/,$apt); $icao = $arr2[4]; $name = join(' ', splice(@arr2,5)); $nlen = length($name); $max_name_len = $nlen if ($nlen > $max_name_len); prt( "[$name] $icao $alat $alon rwys=$rwycnt\n" ) if ($dbg11); ##prt("$diff [$apt] (with $rwycnt runways at [$alat, $alon]) ...\n"); ##prt("$diff [$icao] [$name] ...\n"); $off = scalar @aptlist; my @ra = @runways; my @wa = @waterways; my @ha = @heliways; my @fa = @freqs; # 0 1 2 3 4 5 6 7 8 push(@aptlist, [$diff, $icao, $name, $alat, $alon, \@ra, \@wa, \@ha, \@fa ]); $add = 0; if ($SRCHICAO) { $add = 1 if ($icao =~ /$apticao/); } else { if ($SRCHONLL) { $add = 1 if (($dlat < $maxlatd) && ($dlon < $maxlond)); } else { # $SRCHNAME (default) $add = 1 if ($name =~ /$aptname/i); } } if ($add) { prt("$icao, $name, $alat, $alon \n") if ($dbg1); push(@aptlist2, [$diff, $icao, $name, $alat, $alon, $off]); } } $apt = $line; # set the AIRPORT line $rwycnt = 0; # restart RUNWAY counter @runways = (); $glat = 0; $glon = 0; $wwcnt = 0; @waterways = (); $helicnt = 0; @heliways = (); @freqs = (); $totaptcnt++; # count another AIRPORT prt("$apt\n") if ($dbg10); } elsif ($line =~ /^100\s+/) { $rlat1 = $arr[9]; # $of_lat1 $rlon1 = $arr[10]; # $of_lon1 $rlat2 = $arr[18]; # $of_lat2 $rlon2 = $arr[19]; # $of_lon2 $rlat = ($rlat1 + $rlat2) / 2; $rlon = ($rlon1 + $rlon2) / 2; ###prt( "$line [$rlat, $rlon]\n" ); $glat += $rlat; $glon += $rlon; my @a2 = @arr; push(@runways, \@a2); $rwycnt++; } elsif ($type == 101) { # Water runways # 0 1 2 3 4 5 6 7 8 # 101 243.84 0 16 29.27763293 -089.35826258 34 29.26458929 -089.35340410 # 101 22.86 0 07 29.12988952 -089.39561501 25 29.13389936 -089.38060001 # prt("$.: $line\n"); $rlat1 = $arr[4]; $rlon1 = $arr[5]; $rlat2 = $arr[7]; $rlon2 = $arr[8]; $rlat = sprintf("%.8f",(($rlat1 + $rlat2) / 2)); $rlon = sprintf("%.8f",(($rlon1 + $rlon2) / 2)); if (!in_world_range($rlat,$rlon)) { prtw( "WARNING: $.: $line [$rlat, $rlon] NOT IN WORLD\n" ); next; } $glat += $rlat; $glon += $rlon; my @a2 = @arr; push(@waterways, \@a2); $wwcnt++; } elsif ($type == 102) { # Heliport # my $heli = '102'; # Helipad # 0 1 2 3 4 5 6 7 8 9 10 11 # 102 H2 52.48160046 013.39580674 355.00 18.90 18.90 2 0 0 0.00 0 # 102 H3 52.48071507 013.39937648 2.64 13.11 13.11 1 0 0 0.00 0 # prt("$.: $line\n"); $rlat = sprintf("%.8f",$arr[2]); $rlon = sprintf("%.8f",$arr[3]); if (!in_world_range($rlat,$rlon)) { prtw( "WARNING: $.: $line [$rlat, $rlon] NOT IN WORLD\n" ); next; } $glat += $rlat; $glon += $rlon; my @a2 = @arr; push(@heliways, \@a2); $helicnt++; } elsif (($type >= 50)&&($type <= 56)) { # 50–56 Communication frequencies Zero, one or many for each airport # 50 12082 LFMV ATIS my @tfa = @arr; push(@freqs, \@tfa); # save the freq array } } # do any LAST entry $trwycnt = $rwycnt; $trwycnt += $wwcnt; $trwycnt += $helicnt; if ($trwycnt > 0) { $alat = $glat / $trwycnt; $alon = $glon / $trwycnt; $dlat = abs( $in_lat - $alat ); $dlon = abs( $in_lon - $alon ); $diff = int( ($dlat * 10) + ($dlon * 10) ); # 0 1 2 3 4 5+ # like: 1 28 1 0 VHHH Hong Kong Intl my @arr2 = split(/\s/,$apt); $icao = $arr2[4]; $name = join(' ', splice(@arr2,5)); prt( "[$name] $icao $alat $alon rwys=$rwycnt\n" ) if ($dbg11); ##prt("$diff [$apt] (with $rwycnt runways at [$alat, $alon]) ...\n"); ##prt("$diff [$icao] [$name] ...\n"); $off = scalar @aptlist; my @r = @runways; my @w = @waterways; my @h = @heliways; my @f = @freqs; push(@aptlist, [$diff, $icao, $name, $alat, $alon, \@r, \@w, \@h, \@f]); $add = 0; if ($SRCHICAO) { $add = 1 if ($icao =~ /$apticao/); } else { if ($SRCHONLL) { $add = 1 if (($dlat < $maxlatd) && ($dlon < $maxlond)); } else { # $SRCHNAME (default) $add = 1 if ($name =~ /$aptname/i); } } if ($add) { prt("$icao, $name, $alat, $alon \n") if ($dbg1); push(@aptlist2, [$diff, $icao, $name, $alat, $alon, $off]); } $totaptcnt++; # count another AIRPORT } $diff = scalar @aptlist2; prt("Loaded $totaptcnt airports... found $diff matching...\n"); if ($diff == 0) { if ($SRCHICAO) { search_per_country($apticao); } } } sub load_nav_file { prt("\nLoading $navdat file ...\n"); mydie("ERROR: Can NOT locate [$navdat]!\n") if ( !( -f $navdat) ); open NIF, "<$navdat" or mydie( "ERROR: CAN NOT OPEN $navdat...$!...\n" ); my @nav_lines = ; close NIF; return @nav_lines; } sub search_nav { my ($typ, $nlat, $nlon, $nalt, $nfrq, $nrng, $nfrq2, $nid, $name, $off); my ($alat, $alon); my ($dist, $az,$tmp); my @nav_lines = load_nav_file(); my $nav_cnt = scalar @nav_lines; my $ac = scalar @aptlist2; prt("Processing $nav_cnt lines ...\n"); $max_range_used = $max_range_km; $tmp = sprintf("%.1f",$max_range_used); if ($ac == 1) { $alat = $aptlist2[0][3]; $alon = $aptlist2[0][4]; if ($usekmrange) { prt("Using distance of [$tmp] Km from $ac airport at $alat,$alon.\n" ); } else { prt("Using deviation of [$nmaxlatd,$nmaxlond] from $ac airport at $alat,$alon.\n" ); } } else { if ($usekmrange) { prt("Using maximum distance of [$tmp] from $ac airports.\n" ); } else { prt("Using maximum lat,lon deviation of [$nmaxlatd,$nmaxlond] from $ac airports.\n" ); } } my $vcnt = 0; my $navs_found = 0; my (@arr, $nc); foreach $line (@nav_lines) { $line = trimall($line); ###prt("$line\n"); @arr = split(/ /,$line); # 0 1 (lat) 2 (lon) 3 4 5 6 7 8++ # 2 38.087769 -077.324919 284 396 25 0.000 APH A P Hill NDB # 3 57.103719 009.995578 57 11670 100 1.000 AAL Aalborg VORTAC # 4 39.980911 -075.877814 660 10850 18 281.662 IMQS 40N 29 ILS-cat-I # 4 -09.458922 147.231225 128 11010 18 148.650 IWG AYPY 14L ILS-cat-I # 5 40.034606 -079.023281 2272 10870 18 236.086 ISOZ 2G9 24 LOC # 5 67.018506 -050.682072 165 10955 18 61.600 ISF BGSF 10 LOC # 6 39.977294 -075.860275 655 10850 10 300281.205 --- 40N 29 GS # 6 -09.432703 147.216444 128 11010 10 302148.785 --- AYPY 14L GS # 7 39.960719 -075.750778 660 0 0 281.205 --- 40N 29 OM # 7 -09.376150 147.176867 146 0 0 148.785 JSN AYPY 14L OM # 8 -09.421875 147.208331 91 0 0 148.785 MM AYPY 14L MM # 8 -09.461050 147.232544 146 0 0 328.777 PY AYPY 32R MM # 9 65.609444 -018.052222 32 0 0 22.093 --- BIAR 01 IM # 9 08.425319 004.475597 1126 0 0 49.252 IL DNIL 05 IM # 12 -09.432703 147.216444 11 11010 18 0.000 IWG AYPY 14L DME-ILS # 12 -09.449222 147.226589 11 10950 18 0.000 IBB AYPY 32R DME-ILS $nc = scalar @arr; $typ = $arr[0]; # Check for type number in @navset, and set $actnav to name, like VOR, NDB, etc if ( is_valid_nav($typ) ) { $vcnt++; $nlat = $arr[1]; $nlon = $arr[2]; $nalt = $arr[3]; $nfrq = $arr[4]; $nrng = $arr[5]; $nfrq2 = $arr[6]; $nid = $arr[7]; $name = ''; for (my $i = 8; $i < $nc; $i++) { $name .= ' ' if length($name); $name .= $arr[$i]; } push(@navlist, [$typ, $nlat, $nlon, $nalt, $nfrq, $nrng, $nfrq2, $nid, $name]); # Using $nmaxlatd, $nmaxlond, check airports in @aptlist2; $off = near_an_airport( $nlat, $nlon, \$dist, \$az ); if ($off) { prt( "$actnav, $typ, $nlat, $nlon, $nalt, $nfrq, $nrng, $nfrq2, $nid, $name ($off)\n") if ($dbg2); push(@navlist2, [$typ, $nlat, $nlon, $nalt, $nfrq, $nrng, $nfrq2, $nid, $name, $off, $dist, $az]); } ### } elsif ($line =~ /^\d+\s+Version\s+-\s+DAFIF\s+/) { } elsif ($line =~ /^\d+\s+Version\s+-\s+/) { my $ind = index($line,','); prt( "NAVAID: ".substr($line, 0, (($ind > 0) ? $ind : 50) )."\n" ); # 810 Version - data cycle 2012.08, ... } elsif (($line eq 'I')||($line eq '99')) { # ignore these } elsif (length($line)) { prtw("WARNING: What is this line? [$line]???\n"); } } $navs_found = scalar @navlist2; if (($navs_found == 0) && $tryharder) { my $def_latd = $nmaxlatd; my $def_lond = $nmaxlond; my $def_dist = $max_range_km; while ($navs_found == 0) { $nmaxlatd += 0.1; $nmaxlond += 0.1; $max_range_km += 0.1; if ($usekmrange) { $tmp = sprintf("%.1f",$max_range_km); prt("Expanded to [$tmp] Km from $ac airport(s)...\n" ) if (VERB9()); } else { prt("Expanded to [$nmaxlatd,$nmaxlond] from $ac airport(s)...\n" ) if (VERB9()); } foreach $line (@nav_lines) { $line = trimall($line); ###prt("$line\n"); @arr = split(/ /,$line); $nc = scalar @arr; $typ = $arr[0]; # Check for type number in @navset, and set $actnav to name, like VOR, NDB, etc if ( is_valid_nav($typ) ) { $nlat = $arr[1]; $nlon = $arr[2]; $nalt = $arr[3]; $nfrq = $arr[4]; $nrng = $arr[5]; $nfrq2 = $arr[6]; $nid = $arr[7]; $name = ''; for (my $i = 8; $i < $nc; $i++) { $name .= ' ' if length($name); $name .= $arr[$i]; } # Using $nmaxlatd, $nmaxlond, check airports in @aptlist2; $off = near_an_airport( $nlat, $nlon, \$dist, \$az ); if ($off) { prt( "$actnav, $typ, $nlat, $nlon, $nalt, $nfrq, $nrng, $nfrq2, $nid, $name ($off)\n") if ($dbg2); push(@navlist2, [$typ, $nlat, $nlon, $nalt, $nfrq, $nrng, $nfrq2, $nid, $name, $off, $dist, $az]); } } } $navs_found = scalar @navlist2; } prt("Found $navs_found nearby NAVAIDS, "); if ($usekmrange) { $tmp = sprintf("%.1f",$max_range_km); prt("using distance $tmp Km...\n" ); } else { prt("using difference $nmaxlatd, $nmaxlond...\n" ); } $max_range_used = $max_range_km; $nmaxlatd = $def_latd; $nmaxlond = $def_lond; $max_range_km = $def_dist; } prt("Done - Found $navs_found nearby NAVAIDS, of $vcnt searched...\n" ); } ############## ### functions # 12/12/2008 - Additional distance calculations # from 'signs' perl script # Melchior FRANZ # $Id: signs,v 1.37 2005/06/01 15:53:00 m Exp $ # sub ll2xyz($$) { sub ll2xyz { my $lon = (shift) * $D2R; my $lat = (shift) * $D2R; my $cosphi = cos $lat; my $di = $cosphi * cos $lon; my $dj = $cosphi * sin $lon; my $dk = sin $lat; return ($di, $dj, $dk); } # sub xyz2ll($$$) { sub xyz2ll { my ($di, $dj, $dk) = @_; my $aux = $di * $di + $dj * $dj; my $lat = atan2($dk, sqrt $aux) * $R2D; my $lon = atan2($dj, $di) * $R2D; return ($lon, $lat); } # sub coord_dist_sq($$$$$$) { sub coord_dist_sq { my ($xa, $ya, $za, $xb, $yb, $zb) = @_; my $x = $xb - $xa; my $y = $yb - $ya; my $z = $zb - $za; return $x * $x + $y * $y + $z * $z; } # Ensure argument exists, or die. sub require_arg { my ($arg, @arglist) = @_; mydie( "ERROR: no argument given for option '$arg' ...\n" ) if ! @arglist; } sub get_bucket_info { my ($lon,$lat) = @_; my $b = Bucket2->new(); $b->set_bucket($lon,$lat); return $b->bucket_info(); } sub look_like_icao($) { my $icao = shift; my $up = uc($icao); my $len = length($icao); if (($len == 4) && ($up eq $icao)) { return 1; } return 0; } # How can I tell if a string is a number? # The simplest method is: # if ($string == "$string") { # # It is a number # } # Note the use of the == operator to compare the string to its numeric value. # However, this approach is dangerous because the $string might contain arbitrary # code such as @{[system "rm -rf /"]} which would be executed as a result of the # interpolation process. For safety, use this regular expression: # if ($var =~ /(?=.)M{0,3}(C[MD]|D?C{0,3})(X[CL]|L?X{0,3})(I[XV]|V?I{0,3})/) { # print "$var contains a number.\b"; # } # contains digits,commas and 1 period AND # does not contain alpha's, more than 1 period # commas or periods at the beggining and ends of # each line AND # is not null sub IsANumber($) { my $var = shift; if ( ( $var =~ /(^[0-9]{1,}?$)|(\,*?)|(\.{1})/ ) && !( $var =~ /([a-zA-Z])|(^[\.\,]|[\.\,]$)/ ) && ($var ne '') ) { return 1; } return 0; } sub looks_like_rwy($) { my $rwy = shift; if (length($rwy) > 0) { my $ch = substr($rwy,0,1); if (IsANumber($ch)) { # or perhaps if ($ch == "$ch") return 1; } } return 0; } sub parse_nav_lines($) { my $rnava = shift; my $max = scalar @{$rnava}; # add to my @navlist = (); my ($i,$line,$lnn,@arr,$acnt,$type,$len,$vnav); my ($nlat,$nlon,$feet,$freq,$rng,$bear,$id,$icao,$name,$rwy); $lnn = 0; prt("Processing $max line of NAV data...\n") if (VERB9()); for ($i = 0; $i < $max; $i++) { $line = ${$rnava}[$i]; chomp $line; $line = trimall($line); $len = length($line); $lnn++; next if ($len == 0); if ($lnn < 3) { if ($lnn == 2) { prt(substr($line,0,50)."\n"); } next; } @arr = split(/\s+/,$line); $acnt = scalar @arr; $type = $arr[0]; if ($type == 99) { prt("$lnn: Reached EOF (99)\n"); last; } #0 1 2 3 4 5 6 7 8 9 10 #CD LAT LON ELEV FREQ RNG BEARING ID NAME RWY NAME # FT NM. GS Ang ICAO #2 47.63252778 -122.38952778 0 362 50 0.0 BF NOLLA NDB #3 47.43538889 -122.30961111 354 11680 130 19.0 SEA SEATTLE VORTAC #4 47.42939200 -122.30805600 338 11030 18 180.343 ISNQ KSEA 16L ILS-cat-I #6 47.46081700 -122.30939400 425 11030 10 300180.343 ISNQ KSEA 16L GS if ($acnt < 9) { prt("Split only yielded $acnt!\n"); prt("$lnn: [$line]\n"); pgm_exit(1,""); } $nlat = $arr[1]; $nlon = $arr[2]; $feet = $arr[3]; $freq = $arr[4]; $rng = $arr[5]; $bear = $arr[6]; $id = $arr[7]; $icao = $arr[8]; $name = $icao; $rwy = ''; if ($type == 2) { # 2 NDB - (Non-Directional Beacon) Includes NDB component of Locator Outer Markers (LOM) # 2 47.63252778 -122.38952778 0 362 50 0.0 BF NOLLA NDB $icao = ''; $name = join(' ', splice(@arr,8)); } elsif ($type == 3) { # 3 VOR - (including VOR-DME and VORTACs) Includes VORs, VOR-DMEs and VORTACs # 3 47.43538889 -122.30961111 354 11680 130 19.0 SEA SEATTLE VORTAC $icao = ''; $name = join(' ', splice(@arr,8)); } elsif ($type == 4) { # 4 ILS - LOC Localiser component of an ILS (Instrument Landing System) # 0 1 2 3 4 5 6 7 8 9 10 # 4 47.42939200 -122.30805600 338 11030 18 180.343 ISNQ KSEA 16L ILS-cat-I $rwy = $arr[9]; $name = $arr[10]; } elsif ($type == 5) { # 5 LOC - Localiser component of a localiser-only approach Includes for LDAs and SDFs # 5 40.03460600 -079.02328100 2272 10870 18 236.086 ISOZ 2G9 25 LOC # 5 67.01850600 -050.68207200 165 10955 18 61.600 ISF BGSF 10 LOC $rwy = $arr[9]; $name = $arr[10]; } elsif ($type == 6) { # 6 GS - Glideslope component of an ILS Frequency shown is paired frequency, not the DME channel # 6 47.46081700 -122.30939400 425 11030 10 300180.343 ISNQ KSEA 16L GS $rwy = $arr[9]; $name = $arr[10]; } elsif ($type == 7) { # 7 OM - Outer markers (OM) for an ILS Includes outer maker component of LOMs $rwy = $arr[9]; $name = $arr[10]; } elsif ($type == 8) { # 8 MM - Middle markers (MM) for an ILS # 8 47.47223300 -122.31102500 433 0 0 180.343 ---- KSEA 16L MM $rwy = $arr[9]; $name = $arr[10]; } elsif ($type == 9) { # 9 IM - Inner markers (IM) for an ILS $rwy = $arr[9]; $name = $arr[10]; } elsif ($type == 12) { # 12 DME - including the DME component of an ILS, VORTAC or VOR-DME Frequency display suppressed on X-Plane’s charts # 0 1 2 3 4 5 6 7 8 9 10 # 12 47.43433300 -122.30630000 369 11030 18 0.000 ISNQ KSEA 16L DME-ILS # 12 47.43538889 -122.30961111 354 11680 130 0.0 SEA SEATTLE VORTAC DME if (($acnt > 10) && look_like_icao($icao) && looks_like_rwy($arr[9])) { $rwy = $arr[9]; $name = $arr[10]; } else { ###prt("$lnn: Split $acnt! [$line]\n"); $icao = ''; # this is NOT an ICAO $name = join(' ', splice(@arr,8)); } } elsif ($type == 13) { # 13 Stand-alone DME, or the DME component of an NDB-DME Frequency will displayed on X-Plane’s charts # 0 1 2 3 4 5 6 7 8 # 13 57.10393300 009.99280800 57 11670 199 0.0 AAL AALBORG TACAN # 13 68.71941900 -052.79275300 172 10875 25 0.0 AS AASIAAT DME $icao = ''; $name = join(' ', splice(@arr,8)); } else { prt("$lnn: INVALID [$line]\n"); next; } # 0 1 2 3 4 5 6 7 8 9 10 push(@navlist,[$type,$nlat,$nlon,$feet,$freq,$rng,$bear,$id,$icao,$rwy,$name]); } my (@sorted,$ra,$diff,$ofile); @sorted = sort mycmp_ascend_n0 @navlist; $ofile = $out_file."-nav.csv"; $diff = scalar @sorted; prt("Loaded $diff navaids... writing to $ofile\n"); $line = "type,lat,lon,feet,freq,rng,bear,id,icao,rwy,name\n"; # 0 1 2 3 4 5 6 7 8 9 10 # push(@navlist,[$type,$nlat,$nlon,$feet,$freq,$rng,$bear,$id,$icao,$rwy,$name]); for ($i = 0; $i < $diff; $i++) { $ra = $sorted[$i]; $type = ${$ra}[0]; $nlat = ${$ra}[1]; $nlon = ${$ra}[2]; $feet = ${$ra}[3]; $freq = ${$ra}[4]; $rng = ${$ra}[5]; $bear = ${$ra}[6]; $id = ${$ra}[7]; $icao = ${$ra}[8]; $rwy = ${$ra}[9]; $name = ${$ra}[10]; $line .= "$type,$nlat,$nlon,$feet,$freq,$rng,$bear,$id,$icao,$rwy,$name\n"; } write2file($line,$ofile); prt("nav CSV written to [$ofile]\n"); } ############################################ ### MAIN ### ############################################ ###prt( "$pgmname ... Hello, World ... ".scalar localtime(time())."\n" ); parse_args(@ARGV); # collect command line arguments ... load_apt_data(); $acnt = scalar @aptlist2; prt( "Found $acnt, of $totaptcnt, airports ...\n" ) if ($dbg3); set_average_apt_latlon(); if ($SRCHICAO) { prt( "Found $acnt matching $apticao ...(av. $av_apt_lat,$av_apt_lon)\n" ) if ($dbg3); } elsif ($SRCHONLL) { prt( "Found $acnt matching $in_lat, $in_lon ...(av. $av_apt_lat,$av_apt_lon)\n" ) if ($dbg3); } else { # $SRCHNAME prt( "Found $acnt matching $aptname ... (av. $av_apt_lat,$av_apt_lon)\n" ) if ($dbg3); } #my @aptsort = sort mycmp_ascend @aptlist; show_airports_found($max_cnt); if ($acnt && $SHOWNAVS) { search_nav(); show_navaids_found(); show_airports_found($max_cnt); } $cnt = scalar @tilelist; if ($cnt) { prt( "Scenery Tile" ); if ($cnt > 1) { prt( "s" ); } prt( ": " ); foreach $name (@tilelist) { prt( "$name " ); } prt( "\n" ); } show_warnings(0); my $elapsed = tv_interval ( $t0, [gettimeofday]); prt( "Ran for $elapsed seconds ...\n" ); $loadlog = 1 if (($outcount > 30) || $dbg10 || $dbg11); close_log($outfile,$loadlog); ###unlink($outfile); exit(0); ############################################ ############################################ sub give_help { prt( "FLIGHTGEAR AIRPORT SEARCH UTILITY - $VERS\n" ); prt( "Usage: $pgmname options\n" ); prt( "Options: A ? anywhere for this help.\n" ); prt( " -icao=$apticao - Search using icao.\n" ); prt( " -latlon=$in_lat,$in_lon - Search using latitude, longitude.\n" ); prt( " -maxout=$max_cnt - Limit the airport output. A 0 for ALL.\n" ); prt( " -maxll=$maxlatd,$maxlond - Maximum difference, when searching ariports using lat,lon.\n" ); prt( " -name=\"$aptname\" - Search using airport name. (A -name=. would match all.)\n" ); prt( " -shownavs (or -s) - Show NAVAIDS around airport found, if any. " ); prt( "(Def=". ($SHOWNAVS ? "On" : "Off") . ")\n" ); prt( " -nmaxll=$nmaxlatd,$nmaxlond - Maximum difference, when searching NAVAID lat,lon.\n" ); prt( " -range=$max_range_km - Set Km range when checking for NAVAIDS.\n" ); prt( " -r - Use above range ($max_range_km Km) for searching.\n" ); prt( " -tryhard (or -t) - Expand search if no NAVAIDS found in range. " ); prt( "(Def=". ($tryharder ? "On" : "Off") . ")\n" ); prt( " -2fg - Output in FG 8.10 apt.dat format, if found.\n" ); prt( " -v[1-9] - Bump, or set verbosity. (def=$verbosity)\n"); prt( " -xml - Output threshold xml file. (def=$gen_threshold_xml)\n"); prt( "Data Files Used:\n"); prt( " -aptdata=$aptdat - Use a specific AIRPORT data file.\n" ); prt( " -navdata=$navdat - Use a specific NAVAID data file.\n" ); mydie( " Happy Searching.\n" ); } # set $SRCHICAO on/off # set $SRCHONLL on/off sub parse_args { my (@av) = @_; my (@arr,$sarg); while(@av) { my $arg = $av[0]; # shift @av; my $lcarg = lc($arg); $sarg = $arg; if ($sarg =~ /^-/) { $sarg = substr($sarg,1) while ($sarg =~ /^-/); if ($arg =~ /\?/) { give_help(); # BY ICAO } elsif ( $arg =~ /^-icao=(.+)$/i ) { $apticao = $1; $SRCHICAO = 1; $SRCHONLL = 0; $SRCHNAME = 0; prt("[v1] Search using ICAO of [$apticao] ...\n" ) if (VERB1()); } elsif ( $lcarg eq '-icao' ) { require_arg(@av); shift @av; $SRCHICAO = 1; $SRCHONLL = 0; $SRCHNAME = 0; $apticao = $av[0]; prt("[v1] Search using ICAO of [$apticao] ...\n" ) if (VERB1()); # BY LAT,LON } elsif ( $arg =~ /^-latlon=(.+)$/i ) { $SRCHICAO = 0; $SRCHONLL = 1; $SRCHNAME = 0; @arr = split(',', $1); if (scalar @arr == 2) { $in_lat = $arr[0]; $in_lon = $arr[1]; prt("[v1] Search using LAT,LON of [$in_lat,$in_lon] ...\n" ) if (VERB1()); } else { mydie( "ERROR: Failed to find lat,lon in [$arg]...\n" ); } } elsif ( $lcarg eq '-latlon' ) { require_arg(@av); shift @av; $SRCHICAO = 0; $SRCHONLL = 1; $SRCHNAME = 0; @arr = split(',', $av[0]); if (scalar @arr == 2) { $in_lat = $arr[0]; $in_lon = $arr[1]; prt("[v1] Search using LAT,LON of [$in_lat,$in_lon] ...\n" ) if (VERB1()); } else { mydie( "ERROR: Failed to find lat,lon in [$arg]...\n" ); } # By NAME } elsif ( $arg =~ /^-name=(.+)$/i ) { $aptname = $1; $SRCHICAO = 0; $SRCHONLL = 0; $SRCHNAME = 1; prt("[v1] Search using NAME of [$aptname] ...\n" ) if (VERB1()); } elsif ( $lcarg eq '-name' ) { require_arg(@av); shift @av; $SRCHICAO = 0; $SRCHONLL = 0; $SRCHNAME = 1; $aptname = $av[0]; prt("[v1] Search using NAME of [$aptname] ...\n" ) if (VERB1()); } elsif ( $arg =~ /^-loadlog$/i ) { $loadlog = 1; prt( "[v1] Load log into wordpad.\n" ) if (VERB1()); } elsif ( $arg =~ /^-shownavs$/i ) { $SHOWNAVS = 1; prt( "[v1] And show NAVAIDS around airport, if any.\n" ) if (VERB1()); } elsif ( $arg =~ /^-s$/i ) { $SHOWNAVS = 1; prt( "[v1] And show NAVAIDS around airport, if any.\n" ) if (VERB1()); } elsif ( $arg =~ /^-maxll=(.+)$/i ) { @arr = split(',', $1); if (scalar @arr == 2) { $maxlatd = $arr[0]; $maxlond = $arr[1]; prt("[v1] Search maximum difference LAT,LON of [$maxlatd,$maxlond] ...\n") if (VERB1()); } else { mydie( "ERROR: Failed to find maximum lat,lon difference in [$arg]...\n" ); } } elsif ( $lcarg eq '-maxll' ) { require_arg(@av); shift @av; @arr = split(',', $av[0]); if (scalar @arr == 2) { $maxlatd = $arr[0]; $maxlond = $arr[1]; prt("[v1] Search maximum difference LAT,LON of [$maxlatd,$maxlond] ...\n" ) if (VERB1()); } else { mydie( "ERROR: Failed to find maximum lat,lon difference in [$arg]...\n" ); } } elsif ( $arg =~ /^-nmaxll=(.+)$/i ) { @arr = split(',', $1); if (scalar @arr == 2) { $nmaxlatd = $arr[0]; $nmaxlond = $arr[1]; prt("[v1] Search maximum difference LAT,LON of [$nmaxlatd,$nmaxlond] ...\n" ) if (VERB1()); } else { mydie( "ERROR: Failed to find maximum lat,lon difference in [$arg]...\n" ); } } elsif ( $lcarg eq '-nmaxll' ) { require_arg(@av); shift @av; @arr = split(',', $av[0]); if (scalar @arr == 2) { $nmaxlatd = $arr[0]; $nmaxlond = $arr[1]; prt("[v1] Search maximum difference LAT,LON of [$nmaxlatd,$nmaxlond] ...\n" ) if (VERB1()); } else { mydie( "ERROR: Failed to find maximum lat,lon difference in [$arg]...\n" ); } } elsif ( $arg =~ /^-aptdata=(.+)$/i ) { $aptdat = $1; # the airports data file prt("[v1] Using AIRPORT data file [$aptdat] ...\n" ) if (VERB1()); } elsif ( $lcarg eq '-aptdata' ) { require_arg(@av); shift @av; $aptdat = $av[0]; # the airports data file prt("[v1] Using AIRPORT data file [$aptdat] ...\n" ) if (VERB1()); } elsif ( $arg =~ /^-navdata=(.+)$/i ) { $navdat = $1; prt("[v1] Using NAVAID data file [$navdat] ...\n" ) if (VERB1()); } elsif ( $lcarg eq '-navdata' ) { require_arg(@av); shift @av; $navdat = $av[0]; prt("[v1] Using NAVAID data file [$navdat] ...\n" ) if (VERB1()); } elsif ( $arg =~ /^-maxout=(.+)$/i ) { $max_cnt = $1; prt("[v1] Airport output limited to $max_cnt. A zero (0), for no limit\n" ) if (VERB1()); } elsif ( $lcarg eq '-maxout' ) { require_arg(@av); shift @av; $max_cnt = $av[0]; prt("[v1] Airport output limited to $max_cnt. A zero (0), for no limit\n" ) if (VERB1()); } elsif ( $arg =~ /^-range=(.+)$/i ) { $max_range_km = $1; $max_range_used = $max_range_km; prt("[v1] Navaid search using $max_range_km Km. A zero (0), for no limit\n" ) if (VERB1()); $usekmrange = 1; prt("[v1] Navaid search using $max_range_km Km.\n" ) if (VERB1()); } elsif ( $lcarg eq '-range' ) { require_arg(@av); shift @av; $max_range_km = $av[0]; prt("[v1] Navaid search using $max_range_km Km. A zero (0), for no limit\n" ) if (VERB1()); $usekmrange = 1; prt("[v1] Navaid search using $max_range_km Km.\n" ) if (VERB1()); $max_range_used = $max_range_km; } elsif ( $lcarg eq '-r' ) { $usekmrange = 1; prt("[v1] Navaid search using $max_range_km Km.\n" ) if (VERB1()); } elsif (( $lcarg eq '-tryhard' )||( $lcarg eq '-t' )) { $tryharder = 1; # Expand the search for NAVAID, until at least 1 found prt("[v1] Navaid search 'tryharder' set.\n" ) if (VERB1()); } elsif ($sarg =~ /^2/) { $show_fg_form = 1; prt("[v1] Set to show FG 8.10 form if found.\n") if (VERB1()); } elsif ($sarg =~ /^x/) { $gen_threshold_xml = 1; prt("[v1] Set to generate threshold xml, if found.\n") if (VERB1()); } elsif ($sarg =~ /^v/) { if ($sarg =~ /^v.*(\d+)$/) { $verbosity = $1; } else { while ($sarg =~ /^v/) { $verbosity++; $sarg = substr($sarg,1); } } prt("[v1] Set verbosity to $verbosity\n") if (VERB1()); } else { mydie( "ERROR: Unknown argument [$arg]. Try ? for HELP ...\n" ); } } else { # bare argument # ASSUME AN AIRPORT NAME, unless exactly 4 upper case if ((length($arg) == 4)&&(uc($arg) eq $arg)) { $SRCHICAO = 1; $SRCHONLL = 0; $SRCHNAME = 0; $apticao = $arg; prt("[v1] Search using ICAO of [$apticao]\n" ) if (VERB1()); } else { $SRCHICAO = 0; $SRCHONLL = 0; $SRCHNAME = 1; $aptname = $av[0]; prt("[v1] Search using NAME of [$aptname]\n" ) if (VERB1()); } } shift @av; } # *** ONLY FOR TESTING *** if ($test_name) { $SRCHICAO = 0; $SRCHONLL = 0; $SRCHNAME = 1; $SHOWNAVS = 1; $usekmrange = 1; $max_range_km = 5; $aptname = $def_name; prtw("WARNING: DEBUG test_name is ON, finding [$aptname]\n"); } elsif ($test_ll) { $in_lat = $def_lat; $in_lon = $def_lon; $maxlatd = 0.1; $maxlond = 0.1; $SRCHICAO = 0; $SRCHONLL = 1; $SRCHNAME = 0; prtw("WARNING: DEBUG test_ll is ON, finding [$in_lat,$in_lon], max $maxlatd,$maxlond\n"); } elsif ($test_icao) { $SRCHICAO = 1; $SRCHONLL = 0; $SRCHNAME = 0; $SHOWNAVS = 1; $apticao = $def_icao; # now have $tryharder to expand this, if NO NAVAIDS found $tryharder = 1; $usekmrange = 1; $show_fg_form = 1; prtw("WARNING: DEBUG test_icao is ON, finding [$apticao], with show navs, tryharder, usekmrange, show_fg_form.\n"); } $max_range_used = $max_range_km; if ( ($SRCHICAO == 0) && ($SRCHONLL == 0) && ($SRCHNAME == 0) ) { prt( "ERROR: No valid command action found, like -\n" ); prt( "By ICAO -icao=KSFO, by LAT/LON -latlon=21,-122, or NAME -name=something!\n" ); give_help(); } } # eof - findap02.pl # X-Plane apt.dat codes my $x_code = <= 1.00 2 - 3 - Code defining the surface type (concrete, asphalt, etc) Integer value for a Surface Type Code 3 - 0 - Code defining a runway shoulder surface type 0=no shoulder, 1=asphalt shoulder, 2=concrete shoulder 4 - 0.15 - Runway smoothness (not used by X-Plane yet) 0.00 (smooth) to 1.00 (very rough). Default is 0.25 5 - 0 - Runway centre-line lights 0=no centerline lights, 1=centre line lights 6 - 0 - Runway edge lighting (also implies threshold lights) 0=no edge lights, 2=medium intensity edge lights 7 - 1 - Auto-generate distance-remaining signs (turn off if created manually) 0=no auto signs, 1=auto-generate signs The following fields are repeated for each end of the runway 8 - 13L - Runway number (eg. 31R, 02). Leading zeros are required. Two to three characters. Valid suffixes: L, R or C (or blank) 9 - 47.53801700 - Latitude of runway threshold (on runway centerline) in decimal degrees Eight decimal places supported 10 - -122.30746100 - Longitude of runway threshold (on runway centerline) in decimal degrees Eight decimal places supported 11 - 73.15 - Length of displaced threshold in metres (this is included in implied runway length) Two decimal places (metres). Default is 0.00 12 - 0.00 - Length of overrun/blast-pad in metres (not included in implied runway length) Two decimal places (metres). Default is 0.00 13 - 2 - Code for runway markings (Visual, non-precision, precision) Integer value for Runway Marking Code 14 - 0 - Code for approach lighting for this runway end Integer value for Approach Lighting Code 15 - 0 - Flag for runway touchdown zone (TDZ) lighting 0=no TDZ lighting, 1=TDZ lighting 16 - 1 - Code for Runway End Identifier Lights (REIL) 0=no REIL, 1=omni-directional REIL, 2=unidirectional REIL 17 - 31R 18 - 47.52919200 19 - -122.30000000 20 - 110.95 21 - 0.00 22 - 2 23 - 0 24 - 0 25 - 1 Nav-aids: 810 version (latest - revised Oct 2011), 740 Version (expired) ======================================================================== from : http://data.x-plane.com/file_specs/XP%20NAV810%20Spec.pdf for earth_nav.dat 2 NDB - (Non-Directional Beacon) Includes NDB component of Locator Outer Markers (LOM) 3 VOR - (including VOR-DME and VORTACs) Includes VORs, VOR-DMEs and VORTACs 4 ILS - LOC Localiser component of an ILS (Instrument Landing System) 5 LOC - Localiser component of a localiser-only approach Includes for LDAs and SDFs 6 GS - Glideslope component of an ILS Frequency shown is paired frequency, not the DME channel 7 OM - Outer markers (OM) for an ILS Includes outer maker component of LOMs 8 MM - Middle markers (MM) for an ILS 9 IM - Inner markers (IM) for an ILS 12 DME - including the DME component of an ILS, VORTAC or VOR-DME Frequency display suppressed on X-Plane’s charts 13 Stand-alone DME, or the DME component of an NDB-DME Frequency will displayed on X-Plane’s charts Sample data 0 1 2 3 4 5 6 7 8 9 10 CD LAT LON ELEV FREQ RNG BEARING ID NAME RWY NAME FT NM. GS Ang ICAO 2 47.63252778 -122.38952778 0 362 50 0.0 BF NOLLA NDB 3 47.43538889 -122.30961111 354 11680 130 19.0 SEA SEATTLE VORTAC 4 47.42939200 -122.30805600 338 11030 18 180.343 ISNQ KSEA 16L ILS-cat-I 6 47.46081700 -122.30939400 425 11030 10 300180.343 ISNQ KSEA 16L GS 8 47.47223300 -122.31102500 433 0 0 180.343 ---- KSEA 16L MM 12 47.43433300 -122.30630000 369 11030 18 0.000 ISNQ KSEA 16L DME-ILS 12 47.43538889 -122.30961111 354 11680 130 0.0 SEA SEATTLE VORTAC DME 13 57.10393300 009.99280800 57 11670 199 0.0 AAL AALBORG TACAN 13 68.71941900 -052.79275300 172 10875 25 0.0 AS AASIAAT DME Fixes: 600 Version (latest - revised July 2009) =============================================== from : http://data.x-plane.com/file_specs/XP%20FIX600%20Spec.pdf for earth_fix.dat Sample 37.428522 -097.419194 ACESI Airways: 640 Version (latest) ============================== from : http://data.x-plane.com/file_specs/Awy640.htm sample ABCDE 32.283733 -106.898669 ABC 33.282503 -107.280542 2 180 450 J13 ABC 33.282503 -107.280542 DEF 35.043797 -106.816314 2 180 450 J13 DEF 35.043797 -106.816314 KLMNO 35.438056 -106.649536 2 180 450 J13-J14-J15 ABCDE Name of intersection or nav-aid at the beginning of this segment (the fix ABCDE in this example). 32.283733 Latitude of the beginning of this segment. -106.898669 Longitude of the beginning of this segment. ABC Name of intersection or nav-aid at the end of this segment (the nav-aid ABC in this example). 33.282503 Latitude of the end of this segment. -107.280542 Longitude of the end of this segment. 2 This is a "High" airway (1 = "low", 2 = "high"). If an airway segment is both High and Low, then it should be listed twice (once in each category). This determines if the airway is shown on X-Plane's "High Enroute" or "Low Enroute" charts. 180 Base of airway in hundreds of feet (18,000 feet in this example). 450 Top of airways in hundreds of feet (45,000 feet in this example). J13 Airway segment name. If multiple airways share this segment, then all names will be included separated by a hyphen (eg. "J13-J14-J15") Astronomical: 740 Version (latest) =================================== from : http://data.x-plane.com/file_specs/Astro740.htm sample 6.752569 -16.713143 -1.43 Sirius 19.846301 8.867385 0.77 Altair 2.529743 89.264138 1.97 Polaris 6.752569 Right Ascension in decimal hours. Always a positive number. -16.713143 Declination in decimal degrees. Positive declinations are north of the celestial equator (eg. the pole star, Polaris, is at a declination of 89.264138 degrees). -1.43 Visible magnitude of the star. This is a weird logarithmic scale (low numbers are brightest), and stars to a magnitude of +6.5 are considered visible to the naked eye (though this will vary hugely with your local seeing conditions, light pollution, altitude, etc.). Sirius (the brightest star in the night sky) has a negative magnitude (-1.43) because it is very, very bright. Sirius Star name (optional - not used by X-Plane). Any units of angular measure can be used for right ascension, but it is customarily measured in hours ( h ), minutes ( m ), and seconds ( s ), with 24h being equivalent to a full circle. ################################ ### ICAO.threshold.xml file -107.548332 53.3667557256793 16 180.00 0 0 -107.548331146226 53.3598982743207 34 0.00 0 0 EOF # ================================