#!/usr/bin/perl -w # NAME: findnavs.pl # AIM: Given a lat,lon, search for navaids nearby... # 27/08/2020 - Review in DELL03 # 11/10/2014 - Add -b bounding box, and find/list all navs in that bbox # 14/02/2014 - Add -i input file - line separated command # 12/02/2014 - Also search by NAME # 13/01/2013 geoff mclane http://geoffair.net/mperl use strict; use warnings; use File::Basename; # split path ($name,$dir,$ext) = fileparse($file [, qr/\.[^.]*/] ) use Cwd; 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 'lib_fgio.pl' or die "Unable to load 'lib_fgio.pl' Check paths in \@INC...\n"; # 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"; my $xgfile = $temp_dir.$PATH_SEP."temptrack.xg"; open_log($outfile); my $VERS = "0.0.5 2020-08-27"; ###my $VERS = "0.0.4 2014-10-11"; ###my $VERS = "0.0.3 2014-02-14"; ###my $VERS = "0.0.2 2014-01-13"; # user variables my $load_log = 0; my $in_file = ''; my $verbosity = 0; my $out_file = ''; my $m_lat = -1; my $m_lon = -1; my $m_icao = ''; my $max_out = 20; my $use_xplane_dat = 0; my $nav_name = ''; my $nav_id = ''; my @nav_list = (); my @nav_ids = (); my $find_vor_only = 1; my $search_by_name = 0; my $filter_vor_pairs = 1; my $add_json_output = 0; my $got_only_one = 0; my ($u_min_lon,$u_min_lat,$u_max_lon,$u_max_lat); my $u_got_bbox = 0; my ($bgn_lon,$bgn_lat,$end_lon,$end_lat); my $got_track = 0; my $fudge = 0.01; # degrees our $HOST = "localhost"; our $PORT = 5556; our $TIMEOUT = 1; our $DELAY = 5; #============================================================================ # This NEEDS to be adjusted to YOUR particular default location of these files. my $FGROOT = 'd:\fg\next\fgdata'; # 20200827 - Dell03 ### my $FGROOT ="F:/fgdata"; # 20140110 - 2.99 my $APTFILE = "$FGROOT/Airports/apt.dat.gz"; # the airports data file my $NAVFILE = "$FGROOT/Navaids/nav.dat.gz"; # the NAV, NDB, etc. data file # add these files my $FIXFILE = "$FGROOT/Navaids/fix.dat.gz"; # the FIX data file my $AWYFILE = "$FGROOT/Navaids/awy.dat.gz"; # Airways data #============================================================================ # ============================================================================= # This NEEDS to be adjusted to YOUR particular default location of these files. ### my $XPROOT = "D:/FG/xplane/1000"; my $XPROOT = 'D:\X-Plane-11\Custom Scenery\Global Airports\Earth nav data'; # 20200827 - Dell03 my $APT_FILE = "$XPROOT/apt.dat"; # the airports data file my $NAV_FILE = "$XPROOT/earth_nav.dat"; # the NAV, NDB, etc. data file my $FIX_FILE = "$XPROOT/earth_fix.dat"; # the FIX data file # ============================================================================= my $g_aptdat = $APTFILE; my $g_navdat = $NAVFILE; my $g_fixfile = $FIXFILE; my $g_awyfile = $AWYFILE; my $x_aptdat = $APT_FILE; my $x_navdat = $NAV_FILE; # ### DEBUG ### my $debug_on = 0; my $def_file = 'MODESTO'; ### program variables my @warnings = (); my $cwd = cwd(); my @g_nav_lines = (); my $actnav = ''; my @navlist = (); my $actnavdat = ''; my @found_navs = (); my $in_input = 0; my $M2NM = 0.000539957; my $NM2KM = 1.852; # 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 MM IM VDME NDME ); 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); } sub is_valid_nav { my ($t) = shift; if ($t && length($t)) { my $txt = "$t"; my $cnt = 0; foreach my $n (@navset) { if ($n eq $txt) { $actnav = $navtypes[$cnt]; return 1; } $cnt++; } } return 0; } sub mycmp_decend_n9 { return -1 if (${$a}[9] < ${$b}[9]); return 1 if (${$a}[9] > ${$b}[9]); return 0; } sub load_nav_file() { prt("\n[v9] Loading $g_navdat file ...\n") if (VERB9()); mydie("ERROR: Can NOT locate [$g_navdat]!\n") if ( !( -f $g_navdat) ); open NIF, "gzip -d -c $g_navdat|" or mydie( "ERROR: CAN NOT OPEN $g_navdat...$!...\n" ); @g_nav_lines = ; close NIF; prt("[v9] Got ".scalar @g_nav_lines." lines to scan...\n") if (VERB9()); $actnavdat = $g_navdat; } sub load_xnav_file { prt("\nLoading $x_navdat file ...\n"); mydie("ERROR: Can NOT locate [$x_navdat]!\n") if ( !( -f $x_navdat) ); open NIF, "<$x_navdat" or mydie( "ERROR: CAN NOT OPEN $x_navdat...$!...\n" ); @g_nav_lines = ; close NIF; prt("[v9] Got ".scalar @g_nav_lines." lines to scan...\n") if (VERB9()); $actnavdat = $x_navdat; } my @fnd_list = (); my $act_find = ''; my $act_id = ''; sub is_in_nav_list($$) { my ($nid,$name) = @_; $act_find = $nav_name; $act_id = $nav_id; return 1 if (($nid eq $act_find)&&($name =~ /$act_find/i)); my ($i,$cnt,$tstid,$tstnm); $cnt = scalar @nav_list; for ($i = 0; $i < $cnt; $i++) { $tstnm = $nav_list[$i]; $tstid = $nav_ids[$i]; $act_find = $tstnm; $act_id = $tstid; return 1 if (($nid eq $tstid)&&($name =~ /$tstnm/i)); if ($got_only_one) { return 1 if (($nid eq $tstid)||($name =~ /$tstnm/i)); } } return 0; } sub show_missed_names() { my ($name,$tst,$fnd,$msg,$msg2,$id,$cnt,$i,$cmb); $msg = ''; $msg2 = ''; $cnt = scalar @nav_list; for ($i = 0; $i < $cnt; $i++) { $name = $nav_list[$i]; $id = $nav_ids[$i]; $cmb = "$name:$id"; # get user requested name $fnd = 0; foreach $tst (@fnd_list) { if ($cmb eq $tst) { $fnd = 1; last; } } if ($fnd) { # found this one $msg2 .= ' ' if (length($msg2)); $msg2 .= $cmb; } else { $msg .= ' ' if (length($msg)); $msg .= $cmb; } } if (length($msg)) { prt("Note: "); prt("Found $msg2, BUT ") if (length($msg2)); prt("did NOT find $msg\n"); } else { #if (length($msg2)) { # prt("Found $msg2\n"); #} my $cnt = scalar @nav_list; prt("Appear to have found all $cnt names...\n"); } } sub mycmp_decend_n0 { return -1 if (${$a}[0] < ${$b}[0]); return 1 if (${$a}[0] > ${$b}[0]); return 0; } sub order_by_distance($) { my $ra = shift; my $max = scalar @{$ra}; my @narr = (); my @ind = (); my ($i,$j,$dist,$az1,$az2,$line,@arr,$res); my ($nlat,$nlon); for ($i = 0; $i < $max; $i++) { $line = ${$ra}[$i]; @arr = split(/\s+/,$line); $nlat = $arr[1]; $nlon = $arr[2]; $res = fg_geo_inverse_wgs_84($bgn_lat,$bgn_lon,$nlat,$nlon,\$az1,\$az2,\$dist); push(@ind,[$dist,$i]); } @ind = sort mycmp_decend_n0 @ind; for ($j = 0; $j < $max; $j++) { $i = $ind[$j][1]; $line = ${$ra}[$i]; push(@narr,$line); } return @narr; } sub in_bounding_box($$) { my ($nlat,$nlon) = @_; return 0 if ($nlat < $u_min_lat); return 0 if ($nlat > $u_max_lat); return 0 if ($nlon < $u_min_lon); return 0 if ($nlon > $u_max_lon); return 1; } sub search_nav_bbox() { my $rnls = \@g_nav_lines; my $nav_cnt = scalar @{$rnls}; prt("Processing $nav_cnt navaid records, get those in box $u_min_lon,$u_min_lat,$u_max_lon,$u_max_lat, fudge=$fudge.\n"); my ($ln,$lnn,$line,@arr,$nc,$typ); my ($nlat,$nlon,$nalt,$nfrq,$nrng,$nfrq2,$nid,$name,$i,$len,$nmmx); my @navsinbox = (); if ($fudge > 0) { $u_min_lon -= $fudge; $u_min_lat -= $fudge; $u_max_lon += $fudge; $u_max_lat += $fudge; } $nmmx = 0; for ($ln = 0; $ln < $nav_cnt; $ln++) { $lnn = $ln + 1; $line = ${$rnls}[$ln]; $line = trim_all($line); $len = length($line); next if ($line =~ /\s+Version\s+/i); next if ($line =~ /^I/); next if ($len == 0); @arr = split(/\s+/,$line); $nc = scalar @arr; $typ = $arr[0]; last if ($typ == 99); if ($nc < 8) { prt("Type: [$typ] - Handle this line [$line] - count = $nc...\n"); pgm_exit(1,"ERROR: FIX ME FIRST!\n"); } if ( is_valid_nav($typ) ) { $nlat = $arr[1]; $nlon = $arr[2]; next if (!in_bounding_box($nlat,$nlon)); $name = ''; for ($i = 8; $i < $nc; $i++) { $name .= ' ' if length($name); $name .= $arr[$i]; } $len = length($name); $nmmx = $len if ($len > $nmmx); push(@navsinbox,$line); } else { pgm_exit(1,"ERROR: FIX ME! Unknown type [$line]\n"); } } if ($got_track) { @navsinbox = order_by_distance(\@navsinbox); } $nav_cnt = scalar @navsinbox; prt("Found $nav_cnt navaids in bbox...\n"); my ($nav,$res,$dist,$az1,$az2,$nm); my ($s,$lat1,$lon1,$lat2,$lon2); my $xg = ''; my $inc = 10; for ($ln = 0; $ln < $nav_cnt; $ln++) { $line = $navsinbox[$ln]; @arr = split(/\s+/,$line); $nc = scalar @arr; # prt("$line\n"); $typ = $arr[0]; $nlat = $arr[1]; $nlon = $arr[2]; $nalt = $arr[3]; $nfrq = $arr[4]; $nrng = $arr[5]; $nfrq2 = $arr[6]; $nid = $arr[7]; $name = ''; for ($i = 8; $i < $nc; $i++) { $name .= ' ' if length($name); $name .= $arr[$i]; } is_valid_nav($typ); $nav = $actnav; if (($typ == 2)||($typ == 3)||($typ == 12)) { $xg .= "anno $nlon $nlat $actnav $name\n"; if ($nrng > 0) { $s = $nrng * $NM2KM * 1000; $az1 = 0; $res = fg_geo_direct_wgs_84( $nlat, $nlon, $az1, $s, \$lat1, \$lon1, \$az2 ); $az1 = 180; $res = fg_geo_direct_wgs_84( $nlat, $nlon, $az1, $s, \$lat2, \$lon2, \$az2 ); $xg .= "$lon1 $lat1\n"; $xg .= "$lon2 $lat2\n"; $xg .= "NEXT\n"; $az1 = 90; $res = fg_geo_direct_wgs_84( $nlat, $nlon, $az1, $s, \$lat1, \$lon1, \$az2 ); $az1 = 270; $res = fg_geo_direct_wgs_84( $nlat, $nlon, $az1, $s, \$lat2, \$lon2, \$az2 ); $xg .= "$lon1 $lat1\n"; $xg .= "$lon2 $lat2\n"; $xg .= "NEXT\n"; for ($az1 = 0; $az1 <= 360; $az1 += $inc) { $res = fg_geo_direct_wgs_84( $nlat, $nlon, $az1, $s, \$lat1, \$lon1, \$az2 ); $xg .= "$lon1 $lat1\n"; } $xg .= "NEXT\n"; } } if ($got_track) { $res = fg_geo_inverse_wgs_84($bgn_lat,$bgn_lon,$nlat,$nlon,\$az1,\$az2,\$dist); } # for display $nav .= ' ' while (length($nav) < 4); $typ .= ' ' while (length($typ) < 2); $nalt = ' '.$nalt while (length($nalt) < 4); $nfrq = ' '.$nfrq while (length($nfrq) < 5); $nrng = ' '.$nrng while (length($nrng) < 3); $nid .= ' ' while (length($nid) < 4); $name .= ' ' while (length($name) < $nmmx); prt("$nav,$typ,$nlat,$nlon, $nalt, $nfrq, $nrng, $nid, $name "); if ($got_track) { $nm = int($M2NM * $dist * 10) / 10; $az1 = int($az1 + 0.5); $az2 = int($az2 + 0.5); if (! ($nm =~ /\./) ) { $nm .= ".0"; } $nm = ' '.$nm while (length($nm) < 6); prt("$nm nm, on $az1/$az2"); } prt("\n"); } if ($got_track) { prt("Listed $nav_cnt navaids in distance from begin lat/lon $bgn_lat,$bgn_lon...\n"); } if (length($xg) && length($xgfile)) { if (open XG, ">$xgfile") { print XG "# Navaids on route\n"; print XG "anno $bgn_lon $bgn_lat Begin of track\n"; print XG "color gray\n"; print XG $xg; print XG "anno $end_lon $end_lat End of track\n"; print XG "color red\n"; print XG "$bgn_lon $bgn_lat # Begin of track\n"; print XG "$end_lon $end_lat # End of track\n"; print XG "NEXT\n"; close XG; prt("Navaids XG written to $xgfile\n"); } } } sub search_nav_lines() { my $rnls = \@g_nav_lines; my $nav_cnt = scalar @{$rnls}; prt("Processing $nav_cnt navaid records, get distance to $m_lat,$m_lon...\n"); my ($ln,$line,$len,$lnn,@arr,$typ,$nc,$i,$dist,$az1,$az2,$res); my ($nlat,$nlon,$nalt,$nfrq,$nrng,$nfrq2,$nid,$name,$km,$az); my $by_name = $search_by_name; for ($ln = 0; $ln < $nav_cnt; $ln++) { $lnn = $ln + 1; $line = ${$rnls}[$ln]; $line = trim_all($line); $len = length($line); next if ($line =~ /\s+Version\s+/i); next if ($line =~ /^I/); next if ($len == 0); # 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 @arr = split(/\s+/,$line); $nc = scalar @arr; $typ = $arr[0]; last if ($typ == 99); if ($nc < 8) { prt("Type: [$typ] - Handle this line [$line] - count = $nc...\n"); pgm_exit(1,"ERROR: FIX ME FIRST!\n"); } 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 ($i = 8; $i < $nc; $i++) { $name .= ' ' if length($name); $name .= $arr[$i]; } $az1 = 400; $az2 = 400; if ($by_name) { $dist = -1; # this is by NAME, not location if (is_in_nav_list($nid,$name)) { # 0=typ,1=lat, 2=lon, 3=alt, 4=frq, 5-rng, 6-frq2, 7=nid, 8=name,9=find,10=az1,11=az2 push(@found_navs, [$typ, $nlat, $nlon, $nalt, $nfrq, $nrng, $nfrq2, $nid, $name, $act_find, $az1, $az2]); push(@fnd_list,"$act_find:$act_id"); # keep the fact we found one for this name } } else { $res = fg_geo_inverse_wgs_84 ($m_lat,$m_lon,$nlat,$nlon,\$az1,\$az2,\$dist); $km = $dist / 1000; $km = (int(($km + 0.05) * 10) / 10); $az = (int(($az1 + 0.05) * 10) / 10); } prt( "[v5] $actnav, $typ, $nlat, $nlon, $nalt, $nfrq, $nrng, $nfrq2, $nid, $name, $km, $az\n") if (VERB5()); # 0=typ,1=lat, 2=lon, 3=alt, 4=frq, 5-rng, 6-frq2, 7=nid,8=name,9=dist,10=az1,11=az2 push(@navlist, [$typ, $nlat, $nlon, $nalt, $nfrq, $nrng, $nfrq2, $nid, $name, $dist, $az1, $az2]); } else { pgm_exit(1,"ERROR: FIX ME! Unknown type [$line]\n"); } } $nc = scalar @navlist; prt("Loaded $nc navaids... from $actnavdat\n"); } sub is_vor_type($) { my $t = shift; return 1 if ($t == 3); return 2 if ($t == 12); return 3 if ($t == 13); return 0; } sub is_ndb_type($) { my $t = shift; return 1 if ($t == 2); return 0; } # VDME, 12, 37.61948333, -122.37389167, 7, 11580, 40, SFO , SAN FRANCISCO VOR-DME , 17.9, 229.4 my $hdr = 'type, # , latitude , longitude , elev, freq., rng, ID , Name , dist., brng'; my $done_hdr = 0; my $min_lat = 90; my $min_lon = 180; my $max_lat = -90; my $max_lon = -180; my $min_alt = 9999; my $max_alt = -9999; sub prtnav($$$$$$$$$$$) { my ($nav, $typ, $nlat, $nlon, $nalt, $nfrq, $nrng, $nid, $name, $km, $az) = @_; prt("$hdr\n") if (!$done_hdr); $done_hdr = 1; if ($nlat > $max_lat) { $max_lat = $nlat; } if ($nlat < $min_lat) { $min_lat = $nlat; } if ($nlon > $max_lon) { $max_lon = $nlon; } if ($nlon < $min_lon) { $min_lon = $nlon; } if ($nalt > $max_alt) { $max_alt = $nalt; } if ($nalt < $min_alt) { $min_alt = $nalt; } $nav .= ' ' while (length($nav) < 4); $typ .= ' ' while (length($typ) < 2); $nalt = ' '.$nalt while (length($nalt) < 4); $nfrq = ' '.$nfrq while (length($nfrq) < 5); $nrng = ' '.$nrng while (length($nrng) < 3); $nid .= ' ' while (length($nid) < 4); $name .= ' ' while (length($name) < 24); $km = ' '.$km while (length($km) < 5); my $msg = "$nav, $typ, $nlat, $nlon, $nalt, $nfrq, $nrng, $nid, $name, $km, $az\n"; prt($msg); return $msg; } sub prtnav2($$$$$$$$$) { my ($nav, $typ, $nlat, $nlon, $nalt, $nfrq, $nrng, $nid, $name) = @_; $nav .= ' ' while (length($nav) < 4); $typ .= ' ' while (length($typ) < 2); $nalt = ' '.$nalt while (length($nalt) < 4); $nfrq = ' '.$nfrq while (length($nfrq) < 5); $nrng = ' '.$nrng while (length($nrng) < 3); $nid .= ' ' while (length($nid) < 4); $name .= ' ' while (length($name) < 24); my $msg = "$nav, $typ, $nlat, $nlon, $nalt, $nfrq, $nrng, $nid, $name\n"; prt($msg); return $msg; } sub ind_in_array($$) { my ($ind,$ra) = @_; my ($i); foreach $i (@{$ra}) { return 1 if ($ind == $i); } return 0; } sub filter_vor_pair() { my ($i,$typ,$nlat,$nlon,$nalt,$nfrq,$nrng,$nfrq2,$nid,$name,$dist,$az1,$km,$az,$out,$msg,$clat,$clon,$cfrq); my $max = scalar @found_navs; if ($filter_vor_pairs) { my @navs = (); my ($ra,$ra2,$j,$j2,$typ2,$dist2,$fnd,$i2); my $max = scalar @found_navs; prt("Filter $max for pairs...\n") if (VERB5()); my @excu = (); for ($i = 0; $i < $max; $i++) { $i2 = $i + 1; $ra = $found_navs[$i]; $typ = ${$ra}[0]; if ($find_vor_only) { next if (is_ndb_type($typ)); } next if (ind_in_array($i,\@excu)); $dist = ${$ra}[9]; $fnd = 0; for ($j = 0; $j < $max; $j++) { next if ($i == $j); next if (ind_in_array($j,\@excu)); $j2 = $j + 1; $ra2 = $found_navs[$j]; $typ2 = ${$ra2}[0]; if ($find_vor_only) { next if (is_ndb_type($typ2)); } $dist2 = ${$ra2}[9]; next if (length($dist2) == 0); # prt("Comparing $dist with $dist2\n"); if ($dist eq $dist2) { # same 'name' - decide which prt("$i2:$j2: Equal names $dist type $typ and $typ2 ") if (VERB9()); if (($typ == 3)&&($typ2 == 12)) { push(@excu,$j); push(@navs,$ra2); $fnd = 1; prt("Stored $typ2\n") if (VERB9()); } elsif (($typ == 12)&&($typ2 == 3)) { push(@navs,$ra); push(@excu,$i); $fnd = 1; prt("Stored $typ\n") if (VERB9()); } elsif ((($typ == 13)&&($typ2 == 3))||($typ == 3)&&($typ2 == 13)|| (($typ == $typ2)&&(($typ == 3)||($typ ==13)))) { push(@navs,$ra); push(@excu,$i); $fnd = 1; prt("Stored $typ\n") if (VERB9()); } else { prtw("WARNING: Presently NO filter of types $typ and $typ2 - ** FIX ME **\n"); } } } if ($fnd == 0) { prt("$i2: Not found $dist twice, or more...\n") if (VERB5()); push(@navs,$ra); } } $max = scalar @navs; prt("Return $max after filtering.\n") if (VERB5()); return @navs; } else { prt("No filtering of $max found...\n") if (VERB5()); } return @found_navs; } sub filter_dupes() { my ($i,$ra,$max,$tst,$nfrq,$nid,$name); $max = scalar @found_navs; my @navs = (); my %dupes = (); for ($i = 0; $i < $max; $i++) { $ra = $found_navs[$i]; $nfrq = ${$ra}[4]; $nid = ${$ra}[7]; $name = trim_all(${$ra}[8]); $tst = $nfrq.$nid.$name; next if (defined $dupes{$tst}); $dupes{$tst} = 1; push(@navs,$ra); } return @navs; } sub show_found_navs() { my ($i,$typ,$nlat,$nlon,$nalt,$nfrq,$nrng,$nfrq2,$nid,$name,$dist,$az1,$km,$az,$out,$msg,$clat,$clon,$cfrq); @found_navs = filter_dupes(); $out = scalar @nav_list; my $max = scalar @found_navs; my @nav_list = filter_vor_pair(); $i = scalar @nav_list; prt("Found $max matching $out navaids"); if ($i < $max) { prt(", REDUCED to $i after filtering. Use -A to stop filtering "); $max = $i; } prt("\n"); my $rnl = \@nav_list; $out = 0; $msg = ''; if ($max) { # VOR , 3 , 37.62736111, -120.95786111, 93, 11460, 130, MOD , MODESTO VOR-DME prt("type,cod, latitude, longitude, alt, freq, rng, id, name\n"); } my $json = "\"navaids\":[\n"; for ($i = 0; $i < $max; $i++) { $typ = ${$rnl}[$i][0]; if ($find_vor_only) { next if (is_ndb_type($typ)); } $nlat = ${$rnl}[$i][1]; $nlon = ${$rnl}[$i][2]; $nalt = ${$rnl}[$i][3]; $nfrq = ${$rnl}[$i][4]; $nrng = ${$rnl}[$i][5]; $nfrq2 = ${$rnl}[$i][6]; $nid = ${$rnl}[$i][7]; $name = ${$rnl}[$i][8]; $dist = ${$rnl}[$i][9]; $az1 = ${$rnl}[$i][10]; is_valid_nav($typ); $msg .= prtnav2( $actnav, $typ, $nlat, $nlon, $nalt, $nfrq, $nrng, $nid, $name ); $clat = sprintf("%.8f",$nlat); $clon = sprintf("%.8f",$nlon); $cfrq = sprintf("%.2f", $nfrq / 100); $json .= ",\n" if ($out); #$json .= "{\"type\":\"$actnav\",\"lat\":$clat,\"lon\":$clon,\"alt\":$nalt,\"freq\":$cfrq,\"rng\":$nrng,\"id\":\"$nid\",\"name\":\"$name\"}"; $json .= "{\"N\":\"$dist\",\"id\":\"$nid\",\"type\":\"$actnav\",\"lat\":$clat,\"lon\":$clon,\"alt\":$nalt,\"freq\":$cfrq,\"rng\":$nrng,\"fn\":\"$name\"}"; $out++; } $json .= "\n]\n"; if ($out) { if (length($out_file)) { write2file($json,$out_file); prt("Written json to $out_file\n"); } elsif ($add_json_output) { prt($json); } } } sub vor_pair_filter($) { my $rnl = shift; my $max = $max_out; $max *= 2; $max += 4; my $tot = scalar @{$rnl}; if ($tot < $max) { $max = $tot; } my @navs = (); my ($i,$typ,$nfrq,$j,$typ2,$nfrq2,$rn,$rn2,$fnd,%inds); for ($i = 0; $i < $max; $i++) { $rn = ${$rnl}[$i]; $typ = ${$rn}[0]; if (!is_vor_type($typ)) { push(@navs,$rn); # not a VOR next; } $nfrq = ${$rn}[4]; $fnd = 0; %inds = (); for ($j = 0; $j < $max; $i++) { next if ($i == $j); $rn2 = ${$rnl}[$j]; $typ2 = ${$rn2}[0]; next if (!is_vor_type($typ2)); $nfrq2 = ${$rn2}[4]; if ($nfrq == $nfrq2) { # decide which of these to keep $fnd = 1; $inds{$i} = 1; $inds{$j} = 1; } } if ($fnd) { # decide which indexed item to keep - usually of 2, but could be more my @arr = keys %inds; my ($k,$icnt,$in1,$in2,$rnk); $icnt = scalar @arr; $rnk = $rn; for ($k = 0; $k < $icnt - 1; $k++) { $in1 = $arr[$k]; $in2 = $arr[$k+1]; $rn = ${$rnl}[$in1]; $rn2 = ${$rnl}[$in2]; $typ = ${$rn}[0]; $typ2 = ${$rn2}[0]; if ($typ == $typ2) { $rnk = $rn; } elsif (($typ == 3)&&($typ2 == 12)) { $rnk = $rn2; } elsif (($typ == 12)&&($typ2 == 3)) { $rnk = $rn; } } push(@navs,$rnk); } else { push(@navs,$rn); } } return @navs; } sub show_nearest_navs() { my ($i); my @navs = sort mycmp_decend_n9 @navlist; # TODO - #if ($filter_vor_pairs) { # @navs = vor_pair_filter(\@navs); #} my $rnl = \@navs; my ($typ,$nlat,$nlon,$nalt,$nfrq,$nrng,$nfrq2,$nid,$name,$dist,$az1,$km,$az,$out); $out = 0; my $msg = ''; my $max = $max_out; if (scalar @{$rnl} < $max) { $max = scalar @{$rnl}; } if ($max == 0) { pgm_exit(1,"Failed to find any navs suiting criteria!\n"); } if ($u_got_bbox) { prt("Listing $max in bbox $u_min_lon,$u_min_lat,$u_max_lon,$u_max_lon, VOR/DME first, then NDB, then ILS...\n"); } else { prt("Listing closest $max, VOR/DME first, then NDB, then ILS...\n"); } for ($i = 0; $i < $max; $i++) { $typ = ${$rnl}[$i][0]; next if (!is_vor_type($typ)); $nlat = ${$rnl}[$i][1]; $nlon = ${$rnl}[$i][2]; $nalt = ${$rnl}[$i][3]; $nfrq = ${$rnl}[$i][4]; $nrng = ${$rnl}[$i][5]; $nfrq2 = ${$rnl}[$i][6]; $nid = ${$rnl}[$i][7]; $name = ${$rnl}[$i][8]; $dist = ${$rnl}[$i][9]; $az1 = ${$rnl}[$i][10]; $km = $dist / 1000; $km = (int(($km + 0.05) * 10) / 10); $az = (int(($az1 + 0.05) * 10) / 10); is_valid_nav($typ); $msg .= prtnav( $actnav, $typ, $nlat, $nlon, $nalt, $nfrq, $nrng, $nid, $name, $km, $az ); $out++; } prt("Listed $out VOR\n") if ($out); $done_hdr = 0; $out = 0; for ($i = 0; $i < $max; $i++) { $typ = ${$rnl}[$i][0]; next if (!is_ndb_type($typ)); $nlat = ${$rnl}[$i][1]; $nlon = ${$rnl}[$i][2]; $nalt = ${$rnl}[$i][3]; $nfrq = ${$rnl}[$i][4]; $nrng = ${$rnl}[$i][5]; $nfrq2 = ${$rnl}[$i][6]; $nid = ${$rnl}[$i][7]; $name = ${$rnl}[$i][8]; $dist = ${$rnl}[$i][9]; $az1 = ${$rnl}[$i][10]; $km = $dist / 1000; $km = (int(($km + 0.05) * 10) / 10); $az = (int(($az1 + 0.05) * 10) / 10); is_valid_nav($typ); $msg .= prtnav( $actnav, $typ, $nlat, $nlon, $nalt, $nfrq, $nrng, $nid, $name, $km, $az ); $out++; } prt("Listed $out NDB\n") if ($out); $out = 0; $done_hdr = 0; for ($i = 0; $i < $max; $i++) { $typ = ${$rnl}[$i][0]; next if (is_vor_type($typ) || is_ndb_type($typ)); $nlat = ${$rnl}[$i][1]; $nlon = ${$rnl}[$i][2]; $nalt = ${$rnl}[$i][3]; $nfrq = ${$rnl}[$i][4]; $nrng = ${$rnl}[$i][5]; $nfrq2 = ${$rnl}[$i][6]; $nid = ${$rnl}[$i][7]; $name = ${$rnl}[$i][8]; $dist = ${$rnl}[$i][9]; $az1 = ${$rnl}[$i][10]; $km = $dist / 1000; $km = (int(($km + 0.05) * 10) / 10); $az = (int(($az1 + 0.05) * 10) / 10); is_valid_nav($typ); $msg .= prtnav( $actnav, $typ, $nlat, $nlon, $nalt, $nfrq, $nrng, $nid, $name, $km, $az ); $out++; } prt("Listed $out ILS and components\n") if ($out); $nlat = ($min_lat + $max_lat) / 2; $nlon = ($min_lon + $max_lon) / 2; $nfrq = $max_lat - $min_lat; $nfrq2 = $max_lon - $min_lon; prt("Range: lat,lon,alt min $min_lat,$min_lon,$min_alt max $max_lat,$max_lon,$max_alt\n"); prt("Center: $nlat,$nlon ranges lat/lon $nfrq/$nfrq2\n"); if (length($out_file)) { write2file($msg,$out_file); prt("List written to [$out_file]\n"); } } sub process_in_file($) { my ($inf) = @_; if (! open INF, "<$inf") { pgm_exit(1,"ERROR: Unable to open file [$inf]\n"); } my @lines = ; close INF; my $lncnt = scalar @lines; prt("Processing $lncnt lines, from [$inf]...\n"); my ($line,$inc,$lnn); $lnn = 0; foreach $line (@lines) { chomp $line; $lnn++; if ($line =~ /\s*#\s*include\s+(.+)$/) { $inc = $1; prt("$lnn: $inc\n"); } } } sub is_a_double($) { my $val = shift; return 1 if ($val =~ /^-?\d+\.?\d*$/); # { print "is a real number\n" } return 1 if ($val =~ /^\d+$/); # { print "is a whole number\n" } return 1 if ($val =~ /^-?\d+$/); # { print "is an integer\n" } return 1 if ($val =~ /^[+-]?\d+$/); # { print "is a +/- integer\n" } return 1 if ($val =~ /^-?(?:\d+(?:\.\d*)?|\.\d+)$/); # { print "is a decimal number\n" } return 1 if ($val =~ /^([+-]?)(?=\d|\.\d)\d*(\.\d*)?([Ee]([+-]?\d+))?$/); # { print "a C float\n" } return 0 if ($val =~ /\D/); # { print "has nondigits\n" } return 0; } sub show_pos($) { my $rc = shift; $m_lon = ${$rc}{'lon'}; $m_lat = ${$rc}{'lat'}; prt("Got aircraft position lat=$m_lat, lon=$m_lon\n"); } sub get_fg_pos() { set_lib_fgio_verb(0); if (fgfs_connect($HOST,$PORT,$TIMEOUT)) { prt("Connection established...\n") if (VERB5()); fgfs_send("data"); # switch exchange to data mode my $rc = fgfs_get_position(); show_pos($rc); fgfs_disconnect(); } else { prt("Connection FAILED!\n") if (VERB5()); } } ######################################### ### MAIN ### # get_fg_pos(); parse_args(@ARGV); if ($use_xplane_dat) { load_xnav_file(); } else { load_nav_file(); } if ($u_got_bbox) { search_nav_bbox(); } else { search_nav_lines(); if (length($nav_name) == 0) { show_nearest_navs(); } else { show_found_navs(); show_missed_names(); } } pgm_exit(0,""); ######################################## sub need_arg { my ($arg,@av) = @_; pgm_exit(1,"ERROR: [$arg] must have a following argument!\n") if (!@av); } sub load_input_file($) { my $file = shift; if (open INP, "<$file") { my @lines = ; close INP; my ($line); my @inputs = (); foreach $line (@lines) { chomp $line; $line = trim_all($line); next if (length($line) == 0); push(@inputs,$line); } if (@inputs) { parse_args(@inputs); } } else { pgm_exit(1,"Error: Unable to load input file $file\n"); } } sub in_world($$) { my ($lon,$lat) = @_; if (($lat < -90)|| ($lat > 90)|| ($lon < -180)|| ($lon > 180)) { return 0; } return 1; } sub get_bbox($) { my $bbox = shift; my @arr = split(",",$bbox); my $cnt = scalar @arr; if ($cnt != 4) { prt("bbox $bbox failed to split into 4 on comma!\n"); return 0; } $u_min_lon = $arr[0]; $u_min_lat = $arr[1]; if (!in_world($u_min_lon,$u_min_lat)) { prt("min-lon $u_min_lon or min-lat $u_min_lat NOT in world range!\n"); return 0; } $u_max_lon = $arr[2]; $u_max_lat = $arr[3]; if (!in_world($u_max_lon,$u_max_lat)) { prt("max-lon $u_max_lon or max-lat $u_max_lat NOT in world range!\n"); return 0; } if ($u_min_lon >= $u_max_lon) { prt("min-lon $u_min_lon GTE max-lon $u_max_lon!\n"); return 0; } if ($u_min_lat >= $u_max_lat) { prt("min-lat $u_min_lat GTE max-lat $u_max_lat!\n"); return 0; } $u_got_bbox = 1; return 1; } sub get_track($) { my $bbox = shift; my @arr = split(",",$bbox); my $cnt = scalar @arr; if ($cnt != 4) { prt("track $bbox failed to split into 4 on comma!\n"); return 0; } $bgn_lat = $arr[0]; $bgn_lon = $arr[1]; if (!in_world_range($bgn_lat,$bgn_lon)) { prt("bgn-lat $bgn_lat or bgn-lon $bgn_lon NOT in world range!\n"); return 0; } $end_lat = $arr[2]; $end_lon = $arr[3]; if (!in_world_range($end_lat,$end_lon)) { prt("end-lat $end_lat or end-lon $end_lon NOT in world range!\n"); return 0; } my ($minlat,$minlon,$maxlat,$maxlon); $minlat = $bgn_lat < $end_lat ? $bgn_lat : $end_lat; $minlon = $bgn_lon < $end_lon ? $bgn_lon : $end_lon; $maxlat = $bgn_lat > $end_lat ? $bgn_lat : $end_lat; $maxlon = $bgn_lon > $end_lon ? $bgn_lon : $end_lon; $bbox = "$minlon,$minlat,$maxlon,$maxlat"; if (!get_bbox($bbox)) { prt("get_bbox($bbox) failed!\n"); return 0; } $got_track = 1; return 1; } sub parse_args { my (@av) = @_; my ($arg,$sarg,@arr); my $verb = VERB2(); my %dupes = (); while (@av) { $arg = $av[0]; if ( ($arg =~ /^-/) && (!is_a_double($arg)) ) { $sarg = substr($arg,1); $sarg = substr($sarg,1) while ($sarg =~ /^-/); if (($sarg =~ /^h/i)||($sarg eq '?')) { give_help(); pgm_exit(0,"Help exit(0)"); } elsif ($sarg =~ /^a/) { $filter_vor_pairs = 0; prt("Set to NO filtering of navaids.\n") if ($verb); } elsif ($sarg =~ /^A/) { $find_vor_only = 0; prt("Set to find ALL matching navaids.\n") if ($verb); } elsif ($sarg =~ /^b/) { need_arg(@av); shift @av; $sarg = $av[0]; if (!get_bbox($sarg)) { pgm_exit(1,"Error: Failed to get min-lon,min-lat,max-lon,max_lat from '$sarg'!\n"); } } elsif ($sarg =~ /^f/) { need_arg(@av); shift @av; $sarg = $av[0]; $fudge = $sarg; } elsif ($sarg =~ /^t/) { need_arg(@av); shift @av; $sarg = $av[0]; if (!get_track($sarg)) { pgm_exit(1,"Error: Failed to get bgn-lat,bgn-lon,end-lat,end-lon from '$sarg'!\n"); } } elsif ($sarg =~ /^v/) { if ($sarg =~ /^v.*(\d+)$/) { $verbosity = $1; } else { while ($sarg =~ /^v/) { $verbosity++; $sarg = substr($sarg,1); } } $verb = VERB2(); prt("Verbosity = $verbosity\n") if ($verb); } elsif ($sarg =~ /^l/) { if ($sarg =~ /^ll/) { $load_log = 2; } else { $load_log = 1; } prt("Set to load log at end. ($load_log)\n") if ($verb); } elsif ($sarg =~ /^n/) { need_arg(@av); shift @av; $sarg = $av[0]; if (!defined $dupes{$sarg}) { $dupes{$sarg} = 1; @arr = split(":",$sarg); if (scalar @arr == 2) { $nav_name = $arr[0]; $nav_id = $arr[1]; push(@nav_list,$nav_name); push(@nav_ids,$nav_id); } else { #pgm_exit(1,"Need to give NAME:ID pair! Arg $sarg did NOT split in 2 in ':'!!\n"); $nav_name = $sarg; push(@nav_list,$nav_name); push(@nav_ids,$nav_name); $got_only_one = 1; } prt("Set nav name to [$nav_name], id to [$nav_id].\n") if ($verb); $search_by_name = 1; } } elsif ($sarg =~ /^o/) { need_arg(@av); shift @av; $sarg = $av[0]; $out_file = $sarg; prt("Set out file to [$out_file].\n") if ($verb); } elsif ($sarg =~ /^x/) { $use_xplane_dat = 1; prt("Set to use x-plane data [$x_navdat].\n") if ($verb); } elsif ($sarg =~ /^i/) { need_arg(@av); shift @av; $sarg = $av[0]; $in_input++; load_input_file($sarg); $in_input--; } elsif ($sarg =~ /^j/) { $add_json_output = 1; } elsif ($sarg =~ /^m/) { need_arg(@av); shift @av; $sarg = $av[0]; if ($sarg =~ /^\d+$/) { $max_out = $sarg; } else { pgm_exit(1,"Error: command $arg must be followed by an integer, not $sarg!\n"); } } else { pgm_exit(1,"ERROR: Invalid argument [$arg]! Try -?\n"); } } else { if ($arg =~ /,/) { @arr = split(",",$arg); if (scalar @arr == 2) { $m_lat = $arr[0]; $m_lon = $arr[1]; prt("Set lat [$m_lat], lon [$m_lon]\n") if ($verb); } else { pgm_exit(1,"Command [$arg] did not split into 2 lat,lon\n"); } } elsif ($arg =~ /:/) { @arr = split(":",$arg); if (scalar @arr == 2) { $nav_name = $arr[0]; $nav_id = $arr[1]; push(@nav_list,$nav_name); push(@nav_ids,$nav_id); } else { pgm_exit(1,"Need to give NAME:ID pair! Arg $arg did NOT split in 2 in ':'!!\n"); } prt("Set nav name to [$nav_name], id to [$nav_id].\n") if ($verb); $search_by_name = 1; } elsif ($m_lat == -1) { $m_lat = $arg; prt("Set lat to [$m_lat]\n") if ($verb); } elsif ($m_lon == -1) { $m_lon = $arg; prt("Set lon to [$m_lon]\n") if ($verb); } else { pgm_exit(1,"What is this? [$arg]! Already have lat $m_lat, lon $m_lon\n"); } } shift @av; } if (!$in_input) { if ($debug_on) { prtw("WARNING: DEBUG is ON!\n"); if (length($nav_name) == 0) { $nav_name = $def_file; prt("Set DEFAULT search name to [$nav_name]\n"); } } if ((($m_lat == -1) || ($m_lon == -1)) && (length($nav_name) == 0) && ($u_got_bbox == 0)) { pgm_exit(1,"ERROR: No lat,lon, navaid name (-n), nor bbox (-b) found in command!\n"); } } } sub give_help { prt("$pgmname: version $VERS\n"); prt("Usage: $pgmname [options] lat,lon\n"); prt("Options:\n"); prt(" --help (-h or -?) = This help, and exit 0.\n"); prt(" --verb[n] (-v) = Bump [or set] verbosity. def=$verbosity\n"); prt(" --load (-l) = Load LOG at end. ($outfile)\n"); prt(" --out (-o) = Write output to this file.\n"); prt(" --xplane (-x) = Use x-plane data (def=$x_navdat)\n"); prt(" --name (-n) = Search using a name. Give NAME:ID pair.\n"); prt(" --in (-i) = Use file line separated list of inputs.\n"); prt(" --json (-j) = Also output in json format.\n"); prt(" --all (-a) = No filtering of found navaids.\n"); prt(" --ALL (-A) = Include ALL navaids. Default is VOR(3) and VOR-DME(12) only.\n"); prt(" --max (-m) = Set the maximum output list. (def=$max_out)\n"); prt(" --bbox (-b) = Find all navs within the bounding box.\n"); prt(" --track (-t) = Find all navs along a track, bgn-lat,bgn-lon,end-lat,end-lon.\n"); prt(" --fudge (-f) = Expand bbox by this fudge factor. (def=$fudge)\n"); prt(" bbox = min-lon,min-lat,max-lon,max-lat\n"); prt(" Default nav data file used is [$g_navdat]\n"); prt(" Given a bear lat,lon pair search for all navaids, sorted by distance, and output closest $max_out,\n"); prt(" OR if given a -name name:id pair, search for all navaids with this name:id\n"); } # eof - findnavs.pl