#!/usr/bin/perl -w
###############################################################################
# THIS SOFTWARE IS FREE SOFTWARE! IT IS LICENCED UNDER THE GNU PUBLIC LICENCE
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; version 2 of the License
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
###############################################################################
# (c) Hartmut Schimmel, 07.10.2007
# 07.10.2007 sml first release
# 08.10.2007 sml resume for scrambled data added
# 12.10.2007 sml support for southern hemisphere and western lon added
# 12.10.2007 sml support for gpx format added
# 13.10.2007 sml time selection and daily split added
# 14.10.2007 sml time zone conversation added
# 23.10.2007 SH add GiSTEQ PhotoTrackr support
# 29.10.2007 sml removed tag as scramble criteria
# added GiSTEQ = iTrackU-SIRFIII support
# 29.10.2007 SH add altitude to GPX
# 02.01.2008 sml never give up on scrambled data, write out what was readed
# added timezone conversation acceleration by Module Date::Calc
# added Backup for existing outfiles
# added option -o for output file basename
# added speed in GPX
# added 00:00:00 time assumtion if HHMMSS is omitted in -b and
# -e
# 04.01.2008 sml fixed fallback to date -d if Date::Calc is not available
# 28.01.2008 sml added tag support for gpx, thanks to Steffen Moldaner
# added short arguments for option -i
# added memory dump read from STDIN
# removed 0.000000 in gpx because it is optional
# 03.12.2008 sml added Postgres SQL as export format (thanks to B.W.H. van
# Beest)
# 10.12.2008 sml applied Denis N. Antonioli's patch, which added tag-based
# waypoints in kml and fixed some typos. Thanks, Denis.
# TODO:
# - detect SIRFIII- and Nemerix data automatically
use Getopt::Std;
use strict;
$| = 1;
###############################################################################
# Usage
###############################################################################
( my $prg = $0 ) =~ s/^.*\/([^\/]+)$/$1/g;
sub usage {
die <
Use - as filename for the memory dump to read from STDIN.
Options: -d split outfiles into daily pieces
-b begin at YYYYMMDD[HHMMSS] (ignore trackpoints
before this date), combined with option -z the
local time is affected
-e end at YYYYMMDD[HHMMSS] (ignore trackpoints
after this date), combined with option -z the
local time is affected
-z add value to the GMT timestamps from the logger
to convert loggers GMT to local time, value can
be negative too.
-i select format of the sr-file:
n or iTrackU-Nemerix (defaut)
s or iTrackU-SIRFIII
p or PhotoTrackr
-o Basename of the output file without extension
instead of the default sr-file basename. This
option is ignored if option -d is given.
You must give this option if you read the
input file fron STDIN
Currently the following output formats are supported:
print - values on the screen, no file writing
csv - comma separated value for Excel
(exact as the original csv for applications which rely on this)
ecsv - comma separated value for Excel, extension will be .csv too
(fit better than csv into _e_nglish Excel settings)
dcsv - semicolon separated values for Excel, extension will be .csv too
(fit better than csv into german (_d_eutsche) Excel settings)
track - Trackmaker format, extension will be .txt
kml - GoogleEarth format
gpx - GPX format
sql - SQL format
Example: $prg csv iTu4l_20070410204511.sr
$prg dcsv SortDB.sr
$prg -d kml iTu4l_20070410204511.sr
$prg -z 2 gpx iTu4l_20070410204511.sr
$prg -b 20071012000000 -e 20071012090000 print 09-13102007.sr
$prg -b 20080102 -e 20080102 print 09-13102007.sr
$prg -i PhotoTrackr dcsv SortDB.sr
$prg -d -i PhotoTrackr sql SortDB.sr
This software is published under the GPL V2.0
EOF
}
my %o;
usage unless getopts('db:e:z:i:o:', \%o);
usage if ( $#ARGV != 1 );
$o{'z'} = 0 unless defined $o{'z'};
$o{'b'} = '00000000000000' unless defined $o{'b'};
$o{'e'} = '99999999999999' unless defined $o{'e'};
$o{'b'} .= "000000" if $o{'b'} =~ /^\d{8}$/;
$o{'e'} .= "000000" if $o{'e'} =~ /^\d{8}$/;
$o{'b'} =~ /^\d{14}$/ or die "Date format wrong, use YYYYMMDD[HHMMSS]\n";
$o{'e'} =~ /^\d{14}$/ or die "Date format wrong, use YYYYMMDD[HHMMSS]\n";
$o{'i'} = "iTrackU-Nemerix" unless defined $o{'i'};
if ( defined( $o{'d'} ) and defined( $o{'o'}) ) {
warn "Ignoring option -o because option -d is given\n";
}
###############################################################################
# Declarations and definitions
###############################################################################
my $format = shift;
my $srfile = shift;
my $d; # main data structure Hash
my $last_lat;
my $last_lon;
use constant PI => 4 * atan2(1, 1);
sub distance($$$$);
sub backup_existing_outfile($);
sub print_to_terminal($$);
sub write_csv($$$);
sub write_ecsv($$$);
sub write_dcsv($$$);
sub write_sql($$$);
sub write_kml($$$);
sub write_gpx($$$);
###############################################################################
# Check for Date::Calc Perl Module
###############################################################################
my $have_Date_Calc = 0;
if ( $o{'z'} != 0 ) {
die "Arument to Option -z has to be an integer value, not $o{'z'} - Abort.\n"
if ( $o{'z'} != int( $o{'z'}));
eval "use Date::Calc";
if ( $@ ) {
warn <$srfile" or die $!;
@_ = ;
print SRFILE @_;
close SRFILE;
$delete_srfile = 1;
}
( $o{'o'} = $srfile ) =~ s/\.\w+$//g unless defined $o{'o'};
###############################################################################
# Read sr-File
###############################################################################
open SRFILE, "$srfile" or die $!;
# each record is 16 byte long
my $record_no;
my $scrambled_data = 0;
my $skipped_bytes = 0;
while( sysread( SRFILE, $_, 16) != 15 ) {
# count the records readed
$record_no++;
my ($lon, $lat, $year, $month, $day, $hour, $minute, $second,
$speed, $tag, $date, $altitude );
$altitude = 0;
#############################################################################
# parse XAIOX iTrackU with Nemerix chipset data
#############################################################################
if (( lc( $o{'i'} ) eq lc( 'iTrackU-Nemerix' ) ) or
( lc( $o{'i'} ) eq 'n' ) ) {
# parse the record, this trick took me one whole day...
($lon, $lat, $year, $month, $day, $hour, $minute, $second, $speed, $tag) =
unpack( " V V C C C C C C C C", $_ );
# _Longitude_ _Latitude__ YY MM DD HH MM SS SpdTag
# ef f3 b7 00 d1 df 02 03 07 09 1d 06 01 2a 00 ff
}
#############################################################################
# parse GiSTEQ PhotoTrackr data
# thanks to Sven Haberer for GiSTEQ PhotoTrackr support
# format is the same like XAiOX iTrackU with SIRFIII chipset
# thanks to Erhard Schmidt for iTrackU-SIRFIII support
#############################################################################
elsif (( lc( $o{'i'} ) eq lc( 'PhotoTrackr' ) ) or
( lc( $o{'i'} ) eq 'p' ) or
( lc( $o{'i'} ) eq lc( 'iTrackU-SIRFIII' ) ) or
( lc( $o{'i'} ) eq 's' ) ) {
($lon, $lat, $date, $altitude, $speed, $tag) =
unpack( " V V V s C C", $_ );
# _Longitude_ _Latitude__ ___Date____ _Alt_ SpdTag
# bd 49 90 00 09 0c 1d 03 b4 eb a8 1e 3b 00 02 63
$second = $date & 63;
$minute = ($date >> 6) & 63;
$hour = ($date >> 12) & 31;
$day = ($date >> 17) & 31;
$month = ($date >> 22) & 15;
$year = ($date >> 26) & 63;
}
else {
die "undefined input format";
}
# the windows tool see end of data if lon is 0xFFFFFFFF
if ( $lon == 0xFFFFFFFF ) {
printf "End of data found at byte 0x%s (record %d)\n",
join( ":", unpack( "A4 A4", uc sprintf( "%08x", $record_no * 16 - 1))),
$record_no;
$record_no--;
last;
}
# west lon starts from 0x80000000
# Nemerix specific ???
if ( $lon >= 0x80000000 ) {
$lon -= 0x80000000;
$lon *= -1;
}
# south lat starts from 0x80000000
# Nemerix specific ???
if ( $lat >= 0x80000000 ) {
$lat -= 0x80000000;
$lat *= -1;
}
# detect and skip scrambled data records
# TODO: detection not yet perfect, there could go scrambled records thru
# but not yet seen so in practice
if (( abs($lon) > 180000000 ) or
( abs($lat) > 90000000 ) or
( $year > 30 ) or
( $month > 12 ) or
( $day > 31 ) or
( $hour > 23 ) or
( $minute > 59 ) or
( $second > 59 ) ) {
if ( $skipped_bytes == 0 ) {
printf "Data scrambled at byte 0x%s (record %d), trying to resume\n .",
join( ":", unpack( "A4 A4", uc sprintf( "%08x", ($record_no - 1) * 16 ))),
$record_no;
}
else {
print ".";
}
# count new scrambling events only once
$scrambled_data++ if $skipped_bytes == 0;
# count skipped bytes;
$skipped_bytes++;
# go back one record, skip one byte and try again
sysseek SRFILE, -15, 1;
$record_no--;
next;
}
print " Success after $skipped_bytes skipped bytes\n" if $skipped_bytes > 0;
$skipped_bytes = 0;
# conversations
$year += 2000;
$speed = sprintf "%3d", $speed * 1.852; # convert speed from kt to kph
# convert from degree.minutes to decimal degree
$lon = sprintf("%.6f", int($lon/1000000)
+ ($lon/1000000 - int($lon/1000000)) * 100 / 60);
$lat = sprintf("%.6f", int($lat/1000000)
+ ($lat/1000000 - int($lat/1000000)) * 100 / 60);
# distance beween this and the last previus point
my $distance = 0;
if (( defined $last_lon ) and ( defined $last_lat )) {
$distance = distance( $lat, $lon, $last_lat, $last_lon);
}
# convert timestamps to localtime, if required
if ( $o{'z'} != 0 ) {
if ( $have_Date_Calc ) {
($year,$month,$day, $hour,$minute,$second) =
Date::Calc::Add_Delta_DHMS($year,$month,$day, $hour,$minute,$second,0,$o{'z'},0,0);
}
else {
($second,$minute,$hour,$day,$month,$year,$_,$_,$_) =
localtime( `date -d "$year/$month/$day $hour:$minute:$second" +%s`
+ $o{'z'} * 3600);
$year += 1900; $month++;
}
}
# Save data structure, master key is YYYYMMDDHHMMSS
my $time = sprintf "%04d%02d%02d%02d%02d%02d",
$year, $month, $day, $hour, $minute, $second;
$d->{$time}->{lon} = $lon;
$d->{$time}->{lat} = $lat;
$d->{$time}->{spd} = $speed;
$d->{$time}->{dst} = $distance;
$d->{$time}->{altitude} = $altitude;
$d->{$time}->{tag} = $tag;
# remember this position for next distance calculation
$last_lon = $lon;
$last_lat = $lat;
}
close SRFILE;
unlink $srfile if $delete_srfile;
warn < 0;
WARNING:
Data was $scrambled_data times scrambled. Try to reread the tracks from the
logger. Sometimes then they are not scrambled anymore. However,
you can still use this data because the scrambled trackpoints where
deleted.
EOF
###############################################################################
# find the times wanted
###############################################################################
my @time_to_output;
foreach ( sort keys %$d ) {
next if $_ < $o{'b'};
last if $_ > $o{'e'};
push @time_to_output, $_;
}
###############################################################################
# write outfile(s)
###############################################################################
my %outfile;
# one file for each day
if ( defined $o{'d'} ) {
# generate an HoA with all date_times for each day
# HoA{"10"}[0...n] = ("20071009174949", "20071009174954", "20071009175000" ...)
foreach ( @time_to_output ){ push @{$outfile{ substr( $_, 0, 8)}}, $_; };
}
# all trackpoints into one file
else {
@{$outfile{$o{'o'}}} = @time_to_output;
}
foreach my $day ( sort keys %outfile ) {
my @time = @{$outfile{$day}};
my $outfile = $day . "." . $format;
if ( $format eq "csv" ) {
write_csv(\@time, $d, $outfile);
}
elsif ( $format eq "ecsv" ) {
write_ecsv(\@time, $d, $outfile);
}
elsif ( $format eq "dcsv" ) {
write_dcsv(\@time, $d, $outfile);
}
elsif ( $format eq "sql" ) {
write_sql(\@time, $d, $outfile);
}
elsif ( $format eq "kml" ) {
write_kml(\@time, $d, $outfile);
}
elsif ( $format eq "gpx" ) {
write_gpx(\@time, $d, $outfile);
}
elsif ( $format eq "print" ) {
print_to_terminal(\@time, $d);
}
elsif ( $format eq "track" ) {
$outfile =~ s/\.track$/.txt/g;
die "Format $format not __yet__ supported.\n";
}
elsif ( $format eq "txt" ) {
die "Format $format not supported, did you mean \"track\" ?\n";
}
else {
die "Format $format not supported.\n";
}
}
# all done
exit 0;
###############################################################################
###############################################################################
###############################################################################
# Subroutines
###############################################################################
###############################################################################
###############################################################################
sub asin { atan2($_[0], sqrt(1 - $_[0] * $_[0])) }
###############################################################################
# Write out csv
###############################################################################
sub write_csv($$$) {
my $time = shift;
my $d = shift;
my $outfile = shift;
my $total_distance = 0;
stat $outfile and backup_existing_outfile( $outfile);
open OUTFILE, ">$outfile" or die $!;
print OUTFILE "Longitude,Latitude,Speed(kilometer),DD/MM/YY,HH:MM:SS," .
"Distance(meter)\r\n";
for ( my $i = 0; $i <= $#$time; $i++ ) {
my ( $year, $month, $day, $hour, $minute, $second ) =
unpack "A4A2A2A2A2A2", $$time[$i];
my $record = $d->{$$time[$i]};
printf OUTFILE "%.6f,%.6f,%d,%d/%d/%d,%d:%d:%d,%d,\r\n",
$record->{lon}, $record->{lat}, $record->{spd},
$day, $month, $year, $hour, $minute, $second, $record->{dst};
$total_distance += $record->{dst};
}
printf OUTFILE "Total distance : %d Meter", $total_distance;
print OUTFILE chr(0);
close OUTFILE;
print "Wrote " . $#$time . " records to $outfile\n";
}
###############################################################################
# Write out ecsv
###############################################################################
sub write_ecsv($$$) {
my $time = shift;
my $d = shift;
my $outfile = shift;
my $total_distance = 0;
$outfile =~ s/\.ecsv$/.csv/g;
stat $outfile and backup_existing_outfile( $outfile);
open OUTFILE, ">$outfile" or die $!;
print OUTFILE "Longitude,Latitude,Speed(kph),YYYY/MM/DD,HH:MM:SS," .
"Distance(meter),Altitude(meter)\r\n";
for ( my $i = 0; $i <= $#$time; $i++ ) {
my ( $year, $month, $day, $hour, $minute, $second ) =
unpack "A4A2A2A2A2A2", $$time[$i];
my $record = $d->{$$time[$i]};
printf OUTFILE "%.6f,%.6f,%d,%04d/%02d/%02d,%02d:%02d:%02d,%d,%d\r\n",
$record->{lon}, $record->{lat}, $record->{spd},
$year, $month, $day, $hour, $minute, $second,
$record->{dst}, $record->{altitude};
$total_distance += $record->{dst};
}
printf OUTFILE "Total distance : %d Meter\r\n", $total_distance;
close OUTFILE;
print "Wrote " . $#$time . " records to $outfile\n";
}
###############################################################################
# Write out dcsv
###############################################################################
sub write_dcsv($$$) {
my $time = shift;
my $d = shift;
my $outfile = shift;
my $total_distance = 0;
$outfile =~ s/\.dcsv$/.csv/g;
stat $outfile and backup_existing_outfile( $outfile);
open OUTFILE, ">$outfile" or die $!;
print OUTFILE "Longitude;Latitude;Geschwindigkeit(kmh);DD/MM/YYYY;HH:MM:SS;" .
"Strecke(Meter),Hoehe(Meter);\r\n";
for ( my $i = 0; $i <= $#$time; $i++ ) {
my ( $year, $month, $day, $hour, $minute, $second ) =
unpack "A4A2A2A2A2A2", $$time[$i];
my $record = $d->{$$time[$i]};
( my $lon = $record->{lon} ) =~ s/\./,/g;
( my $lat = $record->{lat} ) =~ s/\./,/g;
printf OUTFILE "%s;%s;%d;%02d.%02d.%04d;%02d:%02d:%02d;%d,%d;\r\n",
$lon, $lat, $record->{spd},
$day, $month, $year, $hour, $minute, $second,
$record->{dst}, $record->{altitude};
$total_distance += $record->{dst};
}
printf OUTFILE "Gesamtstrecke : %d Meter\r\n", $total_distance;
close OUTFILE;
print "Wrote " . $#$time . " records to $outfile\n";
}
###############################################################################
# Write out sql (Postgres format)
###############################################################################
sub write_sql($$$)
{
my $time = shift;
my $d = shift;
my $outfile = shift;
my $tracktbl;
if ( defined $o{'d'} )
{
# The trackpoints will be stored in table "trck" .
($tracktbl = "trck" . $outfile) =~ s/.sql//g;
}
else
{
$tracktbl = "trackpoints";
}
stat $outfile and backup_existing_outfile( $outfile);
open OUTFILE, ">$outfile" or die $!;
# Write the SQL CREATE command
print OUTFILE "CREATE TABLE $tracktbl ( "
. "time TIMESTAMP PRIMARY KEY, longitude FLOAT, latitude FLOAT, altitude FLOAT"
. ", distance FLOAT, speed FLOAT );\n";
for ( my $i = 0; $i <= $#$time; $i++ )
{
my ( $year, $month, $day, $hour, $minute, $second ) =
unpack "A4A2A2A2A2A2", $$time[$i];
my $record = $d->{$$time[$i]};
printf OUTFILE "INSERT INTO $tracktbl VALUES ( '%4d-%02d-%02d %02d:%02d:%02d', %10s, %10s, %4d, %4d, %4d);\n",
$year, $month, $day, $hour, $minute, $second,
$record->{lon}, $record->{lat}, $record->{altitude}, $record->{dst}, $record->{spd};
}
close OUTFILE;
print "Wrote " . $#$time . " records to $outfile\n";
}
###############################################################################
# Write out kml
###############################################################################
sub write_kml($$$) {
my $time = shift;
my $d = shift;
my $outfile = shift;
my $total_distance = 0;
stat $outfile and backup_existing_outfile( $outfile);
open OUTFILE, ">$outfile" or die $!;
# Generate the name of this track as date from - to
my ( $year, $month, $day, $hour, $minute, $second ) =
unpack "A4A2A2A2A2A2", $$time[1];
my $track_name = sprintf "%02d.%02d.%04d %02d:%02d:%02d-",
$day, $month, $year, $hour, $minute, $second;
( $year, $month, $day, $hour, $minute, $second ) =
unpack "A4A2A2A2A2A2", $$time[-1];
$track_name = sprintf "$track_name%02d.%02d.%04d %02d:%02d:%02d",
$day, $month, $year, $hour, $minute, $second;
print OUTFILE <$ENV{USER}'s GPS Logger XAiOX iTrackU1Tracklogs1$track_name
EOF
my @tags;
for ( my $i = 0; $i <= $#$time; $i++ ) {
printf OUTFILE " %f,%f,%d\n",
$d->{$$time[$i]}->{lon}, $d->{$$time[$i]}->{lat},
$d->{$$time[$i]}->{altitude};
$total_distance += $d->{$$time[$i]}->{dst};
push @tags, $i unless $d->{$$time[$i]}->{tag} == 255;
}
print OUTFILE <
EOF
for my $i (@tags) {
my ( $year, $month, $day, $hour, $minute, $second ) =
unpack "A4A2A2A2A2A2", $$time[$i];
my $coord = sprintf("%f,%f,%d",
$d->{$$time[$i]}->{lon}, $d->{$$time[$i]}->{lat},
$d->{$$time[$i]}->{altitude});
print OUTFILE <$year/$month/$day $hour:$minute:$second$coord
EOF
}
print OUTFILE <
EOF
close OUTFILE;
print "Wrote " . $#$time . " records to $outfile\n";
}
###############################################################################
# Write out gpx
###############################################################################
sub write_gpx($$$) {
my $time = shift;
my $d = shift;
my $outfile = shift;
my $total_distance = 0;
stat $outfile and backup_existing_outfile( $outfile);
open OUTFILE, ">$outfile" or die $!;
# Generate the name of this track as date from - to
my ( $year, $month, $day, $hour, $minute, $second ) =
unpack "A4A2A2A2A2A2", $$time[1];
my $track_name = sprintf "%02d.%02d.%04d %02d:%02d:%02d-",
$day, $month, $year, $hour, $minute, $second;
( $year, $month, $day, $hour, $minute, $second ) =
unpack "A4A2A2A2A2A2", $$time[-1];
$track_name = sprintf "$track_name%02d.%02d.%04d %02d:%02d:%02d",
$day, $month, $year, $hour, $minute, $second;
print OUTFILE <
EOF
# Generate date in gpx header as date of first trackpoint
( $year, $month, $day, $hour, $minute, $second ) =
unpack "A4A2A2A2A2A2", $$time[1];
printf OUTFILE " \n",
$year, $month, $day, $hour, $minute, $second;
# trigger new tag for overall first trackpoint because the 8-bit tag can
# never be greater than 0xFF
my $previousTag = 0xFF + 1;
my $trkNo = 1;
my $defaultTrkNo = 1;
# for each trackpoint we have
for ( my $i = 0; $i <= $#$time; $i++ ) {
# new tag ?
if ($previousTag != $d->{$$time[$i]}->{tag} ) {
# close last track if this is not the overall first trackpoint
unless ( $i == 0 ) {
print OUTFILE " \n";
print OUTFILE " \n";
}
# start new track
print OUTFILE " \n";
if ( $d->{$$time[$i]}->{tag} == 0xFF ) {
printf OUTFILE " Default %.1d\n", $defaultTrkNo++;
}
else {
printf OUTFILE " Tag %.1d\n", $d->{$$time[$i]}->{tag};
}
printf OUTFILE " %.1d\n", $trkNo++;
print OUTFILE " \n";
}
# remember this tag
$previousTag = $d->{$$time[$i]}->{tag};
# print out the trackpount data
my ( $year, $month, $day, $hour, $minute, $second ) =
unpack "A4A2A2A2A2A2", $$time[$i];
printf OUTFILE " \n",
$d->{$$time[$i]}->{lat}, $d->{$$time[$i]}->{lon};
printf OUTFILE " %.1f\n",
$d->{$$time[$i]}->{altitude};
printf OUTFILE " \n",
$year, $month, $day, $hour, $minute, $second;
printf OUTFILE " %.2f\n",
$d->{$$time[$i]}->{spd} / 3.6;
print OUTFILE <
EOF
$total_distance += $d->{$$time[$i]}->{dst};
}
print OUTFILE <
EOF
close OUTFILE;
print "Wrote " . $#$time . " records to $outfile\n";
}
###############################################################################
# Print to terminal
###############################################################################
sub print_to_terminal($$) {
my $time = shift;
my $d = shift;
my $total_distance = 0;
print <{$$time[$i]};
printf "%6d %11.6f %11.6f %4d %02d/%02d/%04d %02d:%02d:%02d %10d %6d",
$i+1, $record->{lon}, $record->{lat}, $record->{spd},
$day, $month, $year, $hour, $minute, $second,
$record->{dst}, $record->{altitude};
if ( $record->{tag} != 255 ) {
print " $record->{tag}\n";
}
else {
print "\n";
}
$total_distance += $record->{dst};
}
printf "Total distance : %.2f km\r\n", $total_distance/1000;
}
###############################################################################
# Backup existing outfiles
###############################################################################
sub backup_existing_outfile($) {
my $outfile = shift;
my $backupfile = "${outfile}.bak";
warn "$outfile already exists and was moved to $backupfile\n";
stat $backupfile and backup_existing_outfile( $backupfile);
rename $outfile, $backupfile or die "Error: $!\n";
}
###############################################################################
# distance between two decimal coordinates
# http://williams.best.vwh.net/avform.htm
###############################################################################
sub distance($$$$) {
my $lat1 = shift;
my $lon1 = shift;
my $lat2 = shift;
my $lon2 = shift;
# convert decimal degree ==> radians
$lat1 *= PI / 180;
$lon1 *= PI / 180;
$lat2 *= PI / 180;
$lon2 *= PI / 180;
# calulate the distance using radiants
my $distance = 2 * asin( sqrt( ( sin(($lat1-$lat2)/2) )**2 +
cos($lat1)*cos($lat2)*( sin(($lon1-$lon2)/2) )**2 ));
# conversation distance_radians ==> nautical miles
$distance *= 180 * 60 / PI;
# conversation nautical miles ==> meter
$distance *= 1852;
return $distance;
}