xastir/scripts/permutations.pl
Tom Russo cd2be5c7ba Remove "-W" and "-w" from shebang, then "use warnings"
Wheen I replaced the non-portable "#!/usr/bin/perl" with the portable
"#!/usr/bin/env perl" I neglected the fact that some scripts have a -w
or -W argument after the command.  This can't work with env, and the
correct approach is to put "use warnings;" in the script instead.
2019-04-18 17:16:53 -06:00

389 lines
11 KiB
Perl
Executable file

#!/usr/bin/env perl
use warnings;
#
# Written by Paul Lutt, KE7XT & Curt Mills, WE7U.
# Released to the public domain.
#
#
#
# Finds the different lat/long representations corresponding to the
# input numbers. A space is required between the degrees portion
# and the rest of the input. Writes out a log file containing APRS
# objects suitable for importing into Xastir, to graphically plot
# the locations of the objects. Now that Xastir has a server port
# we could directly inject them into the program via that route as
# well, but we currently don't do that.
#
# Converts between different lat/lon formats. Will also give UMS
# position if the lat/lon resides somewhere inside the Seattle area
# aeronautical map.
#
# UMS coordinates have been used in the past by King County, WA SAR.
# It can be useful for plotting positions on Green Trails maps and
# perhaps other maps. The maps must be 15' topo maps and marked in
# tenths of miles along the edge in order to make use of this
# coordinate system.
#
# Web pages which discuss UMS format:
# http://www.impulse.net/~mlynch/land_nav.html
# http://www.logicsouth.com/~lcoble/dir9/land_nav.htm
# http://www.aasar.org/training/academy/navigation.pdf
#
use lib "/usr/local/lib";
use Coordinate; # WE7U's Coordinate.pm module
# Get the username
$user = getlogin;
chomp $user;
$filename = "/var/tmp/PERMUTATIONS-$user.log";
sub convert {
# Snag the input
$_ = $_[0];
print "\n";
# If the first item has 2 digits and one character and there are
# three "words" in the input, we're starting with a UTM value.
if (/^\d\d[a-zA-Z]\s+\w+\s+\w+\s*$/) {
# printf("Found a UTM value\n");
# We'll convert it to the standard format first and then run
# through the rest of the code.
$zone = $_;
$easting = $_;
$northing = $_;
$zone =~ s/^(\d\d[a-zA-Z])\s+\w+\s+\w+\s*$/$1/;
$easting =~ s/^\d\d[a-zA-Z]\s+(\w+)\s+\w+\s*$/$1/;
$northing =~ s/^\d\d[a-zA-Z]\s+\w+\s+(\w+)\s*$/$1/;
if ($easting > 999999) {
printf("Easting value is too high!\n");
return;
}
$position->zone($zone);
$position->easting($easting);
$position->northing($northing);
# Convert to lat/lon values
$position->utm_to_lat_lon();
# printf("Calculated Lat, Long position(Lat, Long): %f %f\n",
# $position->latitude(),
# $position->longitude() );
$latitude = $position->latitude();
$longitude = $position->longitude();
$lat_dir = "N";
$long_dir = "E";
if ($latitude < 0.0) {
$latitude = abs($latitude);
$lat_dir = "S"
}
if ($longitude < 0.0) {
$longitude = abs($longitude);
$long_dir = "W";
}
# printf("%f%s %f%s\n", $latitude,$lat_dir,$longitude,$long_dir);
$_ = sprintf("%f%s %f%s",
$latitude,$lat_dir,$longitude,$long_dir);
}
# Look for lat/long value in the input
# Check for N/S/E/W characters in the input. Set the
# appropriate flags if found.
$lat_dir = "N";
$long_dir = "E";
if (/S/ || /s/) { $lat_dir = "S"; }
if (/W/ || /w/) { $long_dir = "W"; }
# Filter out these characters from the input
tr/nsewNSEW//d;
# Convert to DD MM SS format
($lat_deg, $lat_min, $lat_sec,
$long_deg, $long_min, $long_sec) = split(' ');
# Decimal Degrees?
if ($lat_deg =~ /\./) {
$long_deg = $lat_min; # Save long_degrees in proper place
$temp = $lat_deg;
$lat_deg = int $temp;
$lat_min = int ((abs($temp) * 60.0) % 60);
# Modulus converts to integers, so we bump up by 10 and then
# back down.
$lat_sec = (abs($temp) * 36000.0) % 600;
$lat_sec = $lat_sec / 10;
$temp = $long_deg;
$long_deg = int $temp;
$long_min = int ((abs($temp) * 60.0) % 60);
$long_sec = (abs($temp) * 36000.0) % 600;
$long_sec = $long_sec / 10;
}
# Decimal Minutes?
elsif ($lat_min =~ /\./) {
$long_min = $long_deg; # Save long_minutes in proper place
$long_deg = $lat_sec; # Save long_degrees in proper place
$temp = $lat_min;
$lat_min = int abs($temp);
$lat_sec = (abs($temp) * 60.0) % 60;
$lat_sec = $lat_sec / 10;
$temp = $long_min;
$long_min = int abs($temp);
$long_sec = (abs($temp) * 600.0) % 600;
$long_sec = $long_sec / 10;
}
# Decimal Seconds
else { # Already in DD MM SS format, don't convert
}
# Print out the three lat/long formats
printf(" Decimal Degrees: %8.5f%s %8.5f%s\n",
$lat_deg + ($lat_min/60.0) + ($lat_sec/3600.0), $lat_dir,
$long_deg + ($long_min/60.0) + ($long_sec/3600.0), $long_dir );
printf(" Degrees/Decimal Minutes: %02d %06.3f%s %02d %06.3f%s\n",
$lat_deg, $lat_min + ($lat_sec/60.0), $lat_dir,
$long_deg, $long_min + ($long_sec/60.0), $long_dir );
printf(" Degrees/Minutes/Dec. Seconds: %02d %02d %4.1f%s %02d %02d %4.1f%s\n",
$lat_deg, $lat_min, $lat_sec, $lat_dir,
$long_deg, $long_min, $long_sec, $long_dir);
# Dump out the coordinate in APRS Item format:
printf(FH "TEST>APRS:)%s!%02d%05.2f%s/%03d%05.2f%s/\n",
$_[1],
$lat_deg,
$lat_min + ($lat_sec/60.0),
$lat_dir,
$long_deg,
$long_min + ($long_sec/60.0),
$long_dir );
# Fill in the coordinate object with the current lat/lon.
# Assuming WGS84 datum
if ($lat_dir =~ /S/) {
$position->latitude( -( $lat_deg + ($lat_min/60.0) + ($lat_sec/3600.0) ) );
}
else {
$position->latitude( $lat_deg + ($lat_min/60.0) + ($lat_sec/3600.0) );
}
if ($long_dir =~ /W/) {
$position->longitude( -( $long_deg + ($long_min/60.0) + ($long_sec/3600.0) ) );
}
else {
$position->longitude( $long_deg + ($long_min/60.0) + ($long_sec/3600.0) );
}
#printf("%f %f\n",$position->latitude,$position->longitude);
$position->lat_lon_to_utm();
printf(" Universal Transverse Mercator: %s %07.0f %07.0f\n",
$position->zone(),
$position->easting(),
$position->northing() );
# Check whether the coordinates are within the SEA aeronautical
# map area
$lat_err = 0;
if ($lat_dir =~ /S/ ||
$lat_deg < 44 || $lat_deg > 49 ||
($lat_deg == 44 && ($lat_min < 30 || ($lat_min == 30 && $lat_sec == 0))) ||
($lat_deg == 49 && ($lat_min > 0 || $lat_sec > 0))) {
print " lat. out of range ";
$lat_err = 1;
}
$long_err = 0;
if ($long_dir =~ /E/ ||
$long_deg < 117 || $long_deg > 125 ||
($long_deg == 117 && ($long_min == 0 && $long_sec == 0)) ||
($long_deg == 125 && ($long_min > 0 || $long_sec > 0))) {
print " long. out of range";
$long_err = 1;
}
return if ( $lat_err || $long_err);
# Compute UMS coordinates
$y_sec = 3600 * ($lat_deg - 44) + 60 * $lat_min + $lat_sec;
$y_sec = 18000 - $y_sec;
$x_sec = 3600 * ($long_deg - 117) + 60 * $long_min + $long_sec;
$x_sec = 28800 - $x_sec;
$quad = 32 * int($y_sec / 900) + int($x_sec / 900) + 1;
# print "\tx_sec= $x_sec, y_sec= $y_sec, quad= $quad\n";
$y_subquad_offset = int($y_sec / 450);
$x_subquad_offset = int($x_sec / 450);
if (&even($x_subquad_offset) && &even($y_subquad_offset)) {
print " UMS (Green Trails Maps): SEA ${quad} A ";
printf "%02d", &s2m_x($x_sec - (450 * $x_subquad_offset));
printf "%02d\n", &s2m_y($y_sec - (450 * $y_subquad_offset));
} elsif (&odd($x_subquad_offset) && &even($y_subquad_offset)) {
print " UMS (Green Trails Maps): SEA ${quad} B ";
printf "%02d", &s2m_x(450 * ($x_subquad_offset + 1) - $x_sec);
printf "%02d\n", &s2m_y($y_sec - (450 * $y_subquad_offset));
} elsif (&even($x_subquad_offset) && &odd($y_subquad_offset)) {
print " UMS (Green Trails Maps): SEA ${quad} C ";
printf "%02d", &s2m_x($x_sec - (450 * $x_subquad_offset));
printf "%02d\n", &s2m_y(450 * ($y_subquad_offset + 1) - $y_sec);
} else {
print " UMS (Green Trails Maps): SEA ${quad} D ";
printf "%02d", &s2m_x(450 * ($x_subquad_offset + 1) - $x_sec);
printf "%02d\n", &s2m_y(450 * ($y_subquad_offset + 1) - $y_sec);
}
}
sub even {
return (($_[0] & 1) == 0);
}
sub odd {
return (($_[0] & 1) == 1);
}
sub s2m_y {
return (int((0.1917966 * $_[0]) + 0.5));
}
sub s2m_x {
return (int((cos(($lat_deg + ($lat_min / 60.0) + ($lat_sec / 3600.0)) / 57.29578) * (0.1917966 * $_[0])) + 0.5));
}
##############
# Main Program
##############
open (FH, ">$filename") || die "Couldn't open file for writing:$!\n";
# Create new Coordinate object
$position = Coordinate->new();
$position->datum("WGS 84"); # Datum
print "\n";
print "Examples: 48 07228N 122 07228W\n";
print " 48 08N 122 07W\n";
print " 10U 0565264 5330343\n";
print "\nAPRS Items will be written to: $filename\n";
print "Enter a Lat/Long value or UTM value:\n";
# Snag the input
$input = <>;
# Get rid of whitespace at the beginning
$input =~ s/\s*//;
# UTM value?
if ($input =~ /^\d\d[a-zA-Z]\s+\w+\s+\w+\s*$/) {
$input2 = $input;
&convert($input2, "UTM");
print "\n";
# Swap Easting/Northing values and convert again. Leave the
# zone in it's original spot.
$input2 = $input;
$input2 =~ s/^(\d\d[a-zA-Z])\s+(\w+)\s+(\w+)\s*$/$1 $3 $2\n/;
print $input2;
&convert($input2, "UTM2");
}
# else must be lat/lon value
else {
# Need to break up the input into several possible formats,
# possibly including swapping lat/long pieces and plotting N/S
# and E/W variants.
# I'm going to assume that the user knows his/her approximate
# lat/lon, so they can input it in roughly the proper format to
# begin with. All that's left then is to determine which of the
# three lat/lon formats it's in.
# 48 07228N 122 07228W
$input =~ s/^(\w+)\s+(\d)(\w)\s+(\w+)\s+(\d)(\w)\s*$/$1 0$2$3 $4 0$5$6\n/;
# DD.DDD
$input2 = $input;
$input2 =~ s/^(\w+)\s+(\w+)\s+(\w+)\s+(\w+)\s*$/$1.$2 $3.$4\n/;
print $input2;
&convert($input2, "DD.DDD");
print "\n";
# DD MM.MMM
$input2 = $input;
$input2 =~ s/^(\w+)\s+(\d\d)(\w+)\s+(\w+)\s+(\d\d)(\w+)\s*$/$1 $2.$3 $4 $5.$6\n/;
$input2 =~ s/(\.N)/.00N/;
$input2 =~ s/(\.S)/.00S/;
$input2 =~ s/(\.E)/.00E/;
$input2 =~ s/(\.W)/.00W/;
print $input2;
&convert($input2, "DD MM.MM");
print "\n";
# DD MM SS.S
$input2 = $input;
$input2 =~ s/^(\w+)\s+(\d\d)(\w)\s+(\w+)\s+(\d\d)(\w)\s*$/$1 $2 00.$3 $4 $5 00.$6\n/;
$input2 =~ s/^(\w+)\s+(\d\d)(\d)(\w)\s+(\w+)\s+(\d\d)(\d)(\w)\s*$/$1 $2 $3.$4 $5 $6 $7.$8\n/;
$input2 =~ s/^(\w+)\s+(\d\d)(\d\d)(\w+)\s+(\w+)\s+(\d\d)(\d\d)(\w+)\s*$/$1 $2 $3.$4 $5 $6 $7.$8\n/;
$input2 =~ s/(\.N)/.0N/;
$input2 =~ s/(\.S)/.0S/;
$input2 =~ s/(\.E)/.0E/;
$input2 =~ s/(\.W)/.0W/;
$input2 =~ s/\s(\d)(\.\d)/ 0$1$2/g;
print $input2;
&convert($input2, "DD MM SS");
}
close(FH);