[BACK]Return to igc_tool.pl CVS log [TXT][DIR] Up to [tkperl]

File: [tkperl] / igc_tool.pl (download)

Revision 1.1, Mon Jul 2 02:20:55 2007 UTC (16 years, 9 months ago) by philip
Branch: MAIN
CVS Tags: HEAD

Cleaned up turnpoint/task

#! /usr/bin/perl -w
use Tk;
use Tk::widgets qw/Dialog ErrorDialog ROText/;
use Time::Local qw/timegm_nocheck timelocal/;
use strict;

my $mw = MainWindow->new;
$mw->CmdLine;
$mw->title("IGC log tool");
$mw->configure(-menu => my $menubar = $mw->Menu);

my $file = $menubar->cascade(qw/-label File -underline 0 -menuitems/ =>
    [
     [command    => 'Open IGC', -command => [\&load_igc]],
     [command    => 'Open DAT', -command => [\&load_dat]],
     [command    => 'Exit', -command => [\&exit]],
    ]);

my $igc = $menubar->cascade(qw/-label Log -underline 0 -menuitems/ =>
    [
     [command    => 'Open IGC', -command => [\&load_igc]],
     [command    => 'Barograph', -command => [\&baro]],
     [command    => 'Track map', -command => [\&map]],
     [command    => 'Generate Mapfile', -command => [\&generate_tab]],
     [command    => 'Split log', -command => [\&logsplit]],
    ]);

my $turnpoints = $menubar->cascade(qw/-label Turnpoints -underline 0 -menuitems/ =>
    [
     [command    => 'Open DAT', -command => [\&load_dat]],
     [command    => 'View list', -command => [\&tpdump]],
     [command    => 'View task', -command => [\&taskdump]],
     [command    => 'Turn/Task', -command => [\&tplist]],
    ]);

my $help = $menubar->cascade(qw/-label Help -underline 0 -menuitems/ =>
    [
      [command => 'About'],
      [command => 'Usage'],
      [command => 'Barograph'],
      [command => 'Track map'],
      [command => 'Split log'],
    ]);

my $DIALOG_ABOUT = $mw->Dialog(
    -title          => 'About IGC tool',
    -bitmap         => 'info',
    -default_button => 'OK',
    -buttons        => ['OK'],
    -text           => "IGC log tool\nWindows version\nPhilip Plane\n" .
    		       "6 March 2006\n\nPerl Version $]" .
                       "\nTk Version $Tk::VERSION",
);

$help->cget(-menu)->entryconfigure('About',
    -command => [$DIALOG_ABOUT => 'Show'],
);

my $DIALOG_USAGE = $mw->Dialog(
    -title          => 'Using IGC tool',
    -bitmap         => 'info',
    -default_button => 'OK',
    -buttons        => ['OK'],
    -text           => "To use the IGC tool, first open an IGC log file " .
    		       "from the File menu. The text of the log will " .
                       "be displayed in the main window. " .
                       "When a file has been loaded the actions in the Log " .
                       "menu can be applied to the log.",
);

$help->cget(-menu)->entryconfigure('Usage',
    -command => [$DIALOG_USAGE => 'Show'],
);

my $DIALOG_BARO = $mw->Dialog(
    -title          => 'Barograph',
    -bitmap         => 'info',
    -default_button => 'OK',
    -buttons        => ['OK'],
    -text           => "The barograph trace shows height in feet, " .
    		       "and various information from the log. " ,
);

$help->cget(-menu)->entryconfigure('Barograph',
    -command => [$DIALOG_BARO => 'Show'],
);

my $DIALOG_MAP = $mw->Dialog(
    -title          => 'Track map',
    -bitmap         => 'info',
    -default_button => 'OK',
    -buttons        => ['OK'],
    -text           => "The track map shows the track from the log file, " .
    		       "any turnpoints in the log file, " .
                       "and any task in the log file. " .
                       "Projection is straight xy." .
                       "Turnpoints are shown as 500 meter circles",
);

$help->cget(-menu)->entryconfigure('Track map',
    -command => [$DIALOG_MAP => 'Show'],
);

my $DIALOG_SPLIT = $mw->Dialog(
    -title          => 'Split log',
    -bitmap         => 'info',
    -default_button => 'OK',
    -buttons        => ['OK'],
    -text           => "Some IGC loggers and their software produce logs " .
    		       "that have more than one flight in the log. " .
                       "The log splitter scans through the log and attempts " .
                       "to seperate the flights into individual logs. " .
                       "If the number of logs it finds sounds reasonable, " .
                       "save the new logs to disk. The log files are named " .
                       "using the original file name with the eighth character " .
                       "replaced with a sequence number. ",
);

$help->cget(-menu)->entryconfigure('Split log',
    -command => [$DIALOG_SPLIT => 'Show'],
);

my $text = $mw->Scrolled("ROText")->pack();

# Globals from IGC file
my $maxalt;
my $minalt;
my $maxpres;
my $minpres;
my $minlat;
my $maxlat;
my $minlon;
my $maxlon;
my $mday;
my $month;
my $mon;
my $year;
my $time;
my $glider;
my $cid;
my $pilot;
my $recorder;
my $igcfile;
my @log;
my @tp;
my @task;
my @track;

my $mindiff;
my $maxdiff;

my $lbtask;
my $lbturn;
my @landings;

my $initialdir = "/";

sub load_igc {
    my $types = [
		 ['IGC Files','.igc'],
		 ['IGC Files','.IGC'],
		 ];

    my $igc = $mw->getOpenFile(-initialdir=>$initialdir,
			       -filetypes=>$types);
    if ( defined $igc ){
	my @igc = split /\//,$igc;
	$igcfile = pop @igc;
        open IGC, $igc or die "Can't open $igc : $!\n";
        my @log = <IGC>;
        close IGC;
        $text->delete("1.0",'end');
	@tp = ();
	@task = ();
	@track = ();
        @landings = ();
	$maxalt = 0;
	$minalt = 99999;
	$maxpres = 0;
	$minpres = 99999;
	$minlat = 90;
	$maxlat = -90;
	$minlon = 180;
	$maxlon = 0;
        $mindiff = 9999;
        $maxdiff = 0;
	my $oh = 0;
        while ( $_ = shift @log ){
	    s/\r//;
	  SWITCH: {
	      if ( /^HFDTE/ ){
		  my $date = substr($_,5);
		  $mday = substr($date,0,2);
		  $month = substr($date,2,2);
		  $year = substr($date,-2,2);
		  $mon = $month - 1;
		  last SWITCH;
	      }
	      if ( /^H.GID/ ){
		  $glider = substr($_,5);
		  last SWITCH;
	      }
	      if ( /^H.CID/ ){
		  $cid = substr($_,5);
		  last SWITCH;
	      }
	      if ( /^H.PLT/ ){
		  $pilot = substr($_,5);
		  last SWITCH;
	      }
	      if ( /^H.FTY/ ){
		  $recorder = substr($_,5);
		  chop $recorder;
		  last SWITCH;
	      }
	      if ( /^L\D{4}?(\d{5}?)(\d{7}?[NS])(\d{8}?[WE])(\d{6})(.{8}?)(.*)/ ){
		  # [ lat, lon, id, name, type ]
		  # turnpoints
		  my $lat = latitude($2);
		  my $lon = longitude($3);
		  push @tp, [ $lat, $lon, $1, $6, $5 ];
		  last SWITCH;
	      }
	      if ( /^C(\d{7}?[NS])(\d{8}?[WE])(.*)/ ){
		  # [ lat, lon, name ]
		  # task
		  my $lat = latitude($1);
		  my $lon = longitude($2);
		  if ( $lat == 0 ){
		    last SWITCH;
		  }
		  push @task, [ $lat, $lon, $3 ];
		  last SWITCH;
	      }
	      if ( /^B(\d{6})(\d{7}[NS])(\d{8}?[WE])A(\d{5})(\d{5})/ ){
                  $time = $1;
		  my $lat = latitude($2);
		  my $lon = longitude($3);
		  my $alt = $4;
		  my $pres = $5;
                  # keep track of the difference between GPS altitude
                  # and pressure altitude
                  my $diff = $pres - $alt;
                  if ( $diff < 0 ){
                    $diff = 0 - $diff;
                  }
                  if ( $diff > $maxdiff ){
                    $maxdiff = $diff;
                  }
                  if ( $diff < $mindiff ){
                    $mindiff = $diff;
                  }

		  # $time is HHMMSS
		  $time =~ /(\d{2})(\d{2})(\d{2})/;
		  my $hour = $1;
		  my $min = $2;
		  my $sec = $3;
		  if ( $hour < $oh ) {
		    $mday++;
		  }
		  $oh = $hour;
		  my $gt = timegm_nocheck($sec,$min,$hour,$mday,$mon,$year);

		  if ( $lat > $maxlat ) {
		      $maxlat = $lat;
		  }
		  if ( $lat < $minlat ) {
		      $minlat = $lat;
		  }
		  if ( $lon > $maxlon ) {
		      $maxlon = $lon;
		  }
		  if ( $lon < $minlon ) {
		      $minlon = $lon;
		  }

		  if ( $alt < $minalt ){
		      $minalt = $alt;
		  }
		  if ( $alt > $maxalt ){
		      $maxalt = $alt;
		  }
		  if ( $pres < $minpres ){
		      $minpres = $pres;
		  }
		  if ( $pres > $maxpres ){
		      $maxpres = $pres;
		  }

		  push @track, [ $gt, $lat, $lon, $alt, $pres ];
		  last SWITCH;
	      }
	  }
	    $text->insert('end',$_);
	}
    }
}

sub baro {
    my $maxrec = scalar @track;
    if ( $maxrec > 0 ) {
	my $bwin = $mw->Toplevel();
	$bwin->title($igcfile . " Barograph");
	my $bg = $bwin->Scrolled('Canvas', -width => 850, -height => 500, -scrollregion => [-10,-10,1000,1000])->pack();
	my $id;
	my $date = sprintf("%02d/%02d/20%02d",$mday,$month,$year);
	if ( not defined $glider ) {
	    $glider = $cid;
	}
        #clean up pilot name, remove extra whitespace
	if ( defined $pilot ){
           chop $pilot;
	   while ( $pilot =~ m/  / ){
	    $pilot =~ s/  / /;
	   }
        }
        if ( defined $glider ){
	 chop $glider;
        }
	my $gps = sprintf("GPS Low point: %5d ft High point: %5d ft",
           feet($minalt),feet($maxalt));
	my $pres = sprintf("Alt Low point: %5d ft High point: %5d ft",
           feet($minpres),feet($maxpres));

	my @t = localtime($track[0][0]);
	my $start = sprintf("Log started at %02d:%02d:%02d",$t[2],$t[1],$t[0]);
	my @f = localtime($track[$maxrec - 1][0]);
	my $finish = sprintf("Log ended at %02d:%02d:%02d",$f[2],$f[1],$f[0]);
	my $duration = sprintf("Log duration %s",
            sec2time($track[$maxrec - 1][0] - $track[0][0]));
        my $diff = sprintf("GPS/Pressure difference: max %4d ft / min %4d ft",
            feet($maxdiff),feet($mindiff));

	$id = $bg->createText(10,10, -text => $pilot, -anchor => "w");
	$id = $bg->createText(410,10, -text => $recorder, -anchor => "w");
	$id = $bg->createText(10,25, -text => $glider, -anchor => "w");
	$id = $bg->createText(10,40, -text => $pres,-fill => "red", -anchor => "w");
	$id = $bg->createText(10,55, -text => $gps, -fill => "blue", -anchor => "w");
	$id = $bg->createText(10,70, -text => $date, -anchor => "w");
        $id = $bg->createText(410,70, -text => $diff, -anchor => "w");
        $id = $bg->createText(10,85, -text => $start, -anchor => "w");
	$id = $bg->createText(10,100, -text => $finish, -anchor => "w");
	$id = $bg->createText(10,115, -text => $duration, -anchor => "w");

	my $width = 800;
	my $height = 450;

        my $hscale = ($track[$maxrec-1][0] - $track[0][0])/($width - 50);
	my $vscale = 25;
	if ($maxalt < 2000){
	    $vscale = 10;
	}
	elsif ($maxalt < 4000){
	    $vscale = 15;
	}

	my $m = $maxalt / 305;

        # Draw lines at 1000ft (305m)
	for(my $mil = 0;$mil < $m;$mil++){
	    my $ml = 400 - (($mil * 305)/$vscale);
	    $id = $bg->createLine(40,$ml,$width ,$ml, -dash => [6,4]);
	    $id = $bg->createText(35,$ml, -text => sprintf("%5d", 1000 * $mil), -anchor => "e");
	}

	my $or = 400 - ( $track[0][4] / $vscale);
	my $ob = 400 - ( $track[0][3] / $vscale);
	my $ox = 30;
        # munch through the track array [ $gt, $lat, $lon, $alt, $pres ]
        my $osec = $track[0][0];
        my $olat = $track[0][1];
        my $olon = $track[0][2];
        my $oalt = $track[0][3];
        my $opres = $track[0][4];
        my $launch = 0;
        my $land = 0;
	for (my $i = 0; $i < $maxrec; $i++) {
            my $t_sec = $track[$i][0];
            $t_sec = $t_sec - $track[0][0];
            my $x = 50 + int($t_sec / $hscale);
	    my $pres = $track[$i][4];
	    my $alt = $track[$i][3];
            # figure out the takeoff/landing
            my $dlat = abs($track[$i][1] - $olat);
  	    my $dlng = abs($track[$i][2] - $olon);
  	    my $dist = sqrt(($dlat*$dlat) + ($dlng*$dlng));
            my $speed = 0;
            my $dsec = $t_sec - $osec;
  	    $osec = $t_sec;
  	    if ( ($dsec > 0) and ($dist > 0)){
    	      $speed = $dist / $dsec;
  	    }
  	    if ( ($speed > 0.000100) and ($oalt < $alt)){
    		$launch++;
                $land = 0;
  	    }
            if ( $launch == 3 ) {
            	$id = $bg->createLine($x,400,$x,150, -fill => "black");
                $id = $bg->createText($x,140, -text => "takeoff");
                $launch++;
            }
  	    if ( ($speed < 0.0000001) and ($oalt == $alt) and ($launch > 0)){
                $land++;
            }
            if ( $land == 3 ){
            	$land = 0;
    		$launch = 0;
    		$id = $bg->createLine($x,400,$x,150,-fill => "black");
                $id = $bg->createText($x,140, -text => "land");
                push @landings,$track[$i][0];
  	    }

	    my $y = 400 - ( $pres/$vscale);
	    $id = $bg->createLine($x,$y,$ox,$or, -fill => "red");
	    $or = $y;
	    $y = 400 - ($alt/$vscale);
	    $id = $bg->createLine($x,$y,$ox,$ob, -fill => "blue");
	    $ob = $y;
	    $ox = $x;
            $olat = $track[$i][1];
            $olon = $track[$i][2];
            $opres = $pres;
            $oalt = $alt;
	}
        # add the timeticks
        my @start = localtime($track[0][0]);
        my @finish = localtime($track[$maxrec-1][0]);
        for ( my $h = $start[2]+1;
              $h <= $finish[2];
              $h++ ){
          @t = @start;
          $t[2] = $h;
          $t[0] = 0;
          $t[1] = 0;
          my $t = timelocal(@t);
          my $x = 50 + int(($t - $track[0][0]) / $hscale);
 	  $id = $bg->createText($x,410, -text => sprintf("%02d:00",$h));
          $id = $bg->createLine($x,400,$x,350, -dash => [3,2],
                                               -fill => "black");
        }
    }
}

sub map {
    my $maxrec = scalar @track;
    if ( $maxrec > 0 ) {
        my $zone;
	my $ox;
	my $oy;

	#use max vertical to set scale as most tracks are taller than wider.
        my $scale = $maxlat - $minlat;

	my $bwin = $mw->Toplevel();
	$bwin->title($igcfile . " Flight Track");
	my $bg = $bwin->Scrolled('Canvas', -width => 850, -height => 600, -scrollregion => [-500,-500,1500,1500])->pack();

	my $width = 1000;
	my $height = 1000;

	$scale = $height / $scale;


	# 500 meter radius circle
	my $r = .004 * $scale;

	#show the turnpoints
	my $maxtp = scalar @tp;
	for (my $t = 0; $t < $maxtp; $t++) {
            my $y = $height - (($tp[$t][0] - $minlat) * $scale);
            my $x = ($tp[$t][1] - $minlon) * $scale;
	    my $id = $bg->createText($x, $y + 10 + $r, -text => $tp[$t][3]);
	    $id = $bg->createOval($x - $r, $y - $r, $x + $r, $y + $r);
	}
	#show the task
	my $maxtask = scalar @task;
	if ( $maxtask > 0 ){
            $oy = $height - (($task[0][0] - $minlat) * $scale);
            $ox = ($task[0][1] - $minlon) * $scale;
	}
	for (my $t = 0; $t < $maxtask; $t++) {
            my $y = $height - (($task[$t][0] - $minlat) * $scale);
            my $x = ($task[$t][1] - $minlon) * $scale;
	    my $id = $bg->createText($x, $y + 10 + $r, -text => $task[$t][2]);
	    $id = $bg->createOval($x - $r, $y - $r, $x + $r, $y + $r, -outline => "green");
	    $id = $bg->createLine($x,$y,$ox,$oy, -dash => [6,4], -fill => "green");
	    $ox = $x;
	    $oy = $y;
	}

        #prime the pump
        $oy = $height - (($track[0][1] - $minlat) * $scale);
        $ox = ($track[0][2] - $minlon) * $scale;

	#show the track
	for (my $i = 0; $i < $maxrec; $i++) {
            my $y = $height - (($track[$i][1] - $minlat) * $scale);
            my $x = ($track[$i][2] - $minlon) * $scale;
	    my $id = $bg->createLine($ox,$oy,$x,$y, -fill => "blue");
	    $ox = $x;
	    $oy = $y;
	}
    }
}

sub generate_tab {
 my $header = "sTrackDescr\tiTrkID\tiColor\tsTimestamp\tfLat\tfLong\tfAlt\tfEasting\tfNorthing\n";
#IGClog	123		3:21	44.500000	170.200000	123.0
 my $tab = $mw->getSaveFile(-initialdir=>$initialdir,
		            -initialfile=>'tracklog.txt');
 if ( defined $tab ){
  open TXT, "> " . $tab or die "Couldn't open file $tab: $!\n";
  print TXT $header;
  my $maxrec = scalar @track;
  for (my $i = 0; $i < $maxrec; $i++) {
   my @now = localtime($track[$i][0]);
   print TXT "IGCLog\t001\t\t";
   printf TXT ("%02d:%02d:%02d\t",$now[2], $now[1], $now[0]);
   printf TXT ("%6f\t%6f\n",$track[$i][1], $track[$i][2]);
  }
  close TXT;
 }
}

sub logsplit {
    my $maxrec = scalar @log;
    my $osec = 0;
    my $igccore;
    my $igccount = 0;
    my @logs =();

    for (my $i = 0; $i < $maxrec; $i++) {
	if ( $log[$i] =~ /^B(\d{6})(\d{7}[NS])(\d{8}?[WE]).{1}(\d{5})(\d{5})/ ){
	    my $alt = $4;
	    my $time = $1;
	    $time =~ /(\d{2})(\d{2})(\d{2})/;
	    my $sec = ($1 *3600) + ($2 * 60) + $3;
	    # if the current second is less than the previous, time
	    # has gone backwards. This indicates you've crossed the
	    # boundry of a new day.
	    if ( $sec < $osec ) {
		$osec -= 86400;
	    }
	    # if the gap in the log is more than sixty seconds assume that
	    # it's a new flight
	    if (( $sec - $osec ) > 60){
                $logs[$igccount++] = $igccore;
		$igccore = "";
	    }
	    $osec = $sec;
	}
	$igccore .= $log[$i];
    }
    $logs[$igccount] = $igccore;
    my $split = $mw->Dialog(
    -title          => 'Split IGC log file',
    -bitmap         => 'question',
    -default_button => 'OK',
    -buttons        => ['OK','Cancel'],
    -text           => "Found $igccount logs.\nWant to create seperate log files for them?\n",
    )->Show();
    if ($split eq 'OK'){
        for (my $i = 1; $i <= $igccount; $i++) {
           substr($igcfile,7,1) = $i;
           my $frag = $mw->getSaveFile(-initialdir=>$initialdir,
		            -initialfile=>$igcfile);
           if ( defined $frag ){
            open TEMP, ">" . $frag or die "Couldn't open $frag: $!\n";
            print TEMP $logs[0], $logs[$i];
            close TEMP;
           }
        }
    }
}


sub load_dat {
    # @tp
    # [ lat, lon, id, name, type ]
    # Cambridge turnpoints file
    #1,44:29.080S,169:58.660E,1380F,TAH,01-Omarama  ,*EastEnd Rwa
    my $types = [
		 ['DAT Files','.dat'],
		 ['DAT Files','.DAT'],
		 ];

    my $dat = $mw->getOpenFile(-initialdir=>$initialdir,
			       -filetypes=>$types);
    if ( defined $dat ){
        my $count = 0;
        @tp = ();
	my @dat = split /\//,$dat;
	my $datfile = pop @dat;
        open DAT, $dat or die "Can't open $dat : $!\n";
        my @log = <DAT>;
        close DAT;
        while ( $_ = shift @log ){
            if ( $_ =~ /^\d/ ){
            	@dat = split(/\,/,$_);
                if ( $dat[4] =~ /T|S|F/ ){
                        if ( $dat[5] eq "" ) {
                          $dat[5] = $dat[0];
                        }
                        $tp[$count][0] = datlat($dat[1]);
                        $tp[$count][1] = datlng($dat[2]);
                        $tp[$count][2] = $dat[0];
                        $tp[$count][3] = $dat[5];
                        $tp[$count][4] = $dat[4];
                        $count++;
                }
            }
	}
    }
}

sub datlat{
  #44:29.080S
  my $lat = shift @_;
  $lat =~ /(\d{2})\:(\d{2}\.\d{3})([SN])/;
  my $degrees = $1;
  my $min = $2;
  my $hemi = $3;
  $min = $min * 0.0166666667;
  $degrees = $degrees + $min;
  if ( $hemi eq 'S' ){
    $degrees = 0 - $degrees;
  }
  return $degrees;
}

sub datlng{
  #169:58.660E
  my $lng = shift @_;
  $lng =~ /(\d{3})\:(\d{2}\.\d{3})([WE])/;
  my $degrees = $1;
  my $min = $2;
  my $hemi = $3;
  $min = $min * 0.0166666667;
  $degrees = $degrees + $min;
  if ( $hemi eq 'W' ){
    $degrees = 0 - $degrees;
  }
  return $degrees;
}

sub tpdump {
  #show the turnpoints
  my $maxtp = scalar @tp;
  $text->delete("1.0",'end');
  for (my $t = 0; $t < $maxtp; $t++) {
    my $tmp = sprintf("%6.2f %6.2f %s\n",$tp[$t][0], $tp[$t][1], $tp[$t][3]);
    $text->insert('end',$tmp);
  }
}

sub taskdump {
  #show task
  my $maxrec = scalar @task;
  $text->delete("1.0".'end');
  for (my $t = 0; $t < $maxrec; $t++) {
    my $tmp = sprintf("%6.2f %6.2f %s\n",$task[$t][0], $task[$t][1], $task[$t][2]);
    $text->insert('end',$tmp);
  }
}

sub tplist {
    my $maxrec = scalar @tp;
    if ( $maxrec > 0 ) {
	my $bwin = $mw->Toplevel();
        $bwin->title("Turnpoints");
        $bwin->minsize(175,50);
        $bwin->Label(-text=>"Turnpoints")->grid( "x", $bwin->Label(-text=>"Task"));

	$lbturn = $bwin->Scrolled("Listbox",
        	-scrollbars => "e",
                -selectmode => "single")->grid(-row=> 0,
                                               -column=> 0,
                                               -rowspan=> 2,
                                               -sticky=> "nsew");

	$bwin->Button(-text=>"Add ->",
	            -command=> \&add_to_task
                    )->grid(-row=> 0,
                            -column=> 1,
                            -sticky=> "sew");

       $lbtask = $bwin->Scrolled("Listbox",
        	-scrollbars => "e",
                -selectmode => "single")->grid(-row=> 0,
                                               -column=> 2,
                                               -rowspan=> 2,
                                               -sticky=> "nsew");

	$bwin->Button(-text=>"Del <-",
                    -command=> \&del_from_task
                    )->grid(-row=> 1,
                            -column=> 1,
                            -sticky=> "new");
        #populate the turnpoint list
        $lbturn->delete("1.0",'end');
  	for (my $t = 0; $t < $maxrec; $t++) {
    	  $lbturn->insert('end',$tp[$t][3]);
  	}
        # populate the task
        $maxrec = scalar @task;
        $lbtask->delete("1.0",'end');
  	for (my $t = 0; $t < $maxrec; $t++) {
    	  $lbtask->insert('end',$task[$t][2]);
  	}

    }
}

sub add_to_task {
	my @selection = $lbturn->curselection();
        foreach ( @selection ){
        	my $turnpoint = $lbturn->get($_);
                $lbtask->insert('end',$turnpoint);
        }
}

sub del_from_task {
	my @selection = $lbtask->curselection();
        foreach ( @selection ){
        	$lbtask->delete($_);
        }
}

sub feet{
    my $meters = shift @_;
    my $feet = $meters * 3.2808399;
    return int($feet);
}

sub duration{
    # expects 2 parameters, start time and finish time
    my $start = shift @_;
    my $end = shift @_;
    my $ss = seconds($start);
    my $se = seconds($end);
    # if we finished before we started, we crossed the day boundry
    if ( $se < $ss ) {
      $se += 60 * 60 * 24;
    }
    return $se - $ss;
}

sub seconds{
    my $time = shift @_;
    my $hours = substr($time,0,2);
    my $minutes = substr($time,2,2);
    my $seconds = substr($time,4,2);
    return $seconds + ($minutes * 60) + ($hours * 60 * 60);
}

sub sec2time{
    my $sec = shift @_;
    my $hours = int($sec / 3600);
    $sec = $sec % 3600;
    my $minutes = int($sec / 60);
    $sec = $sec % 60;
    return sprintf("%02d:%02d:%02d",$hours,$minutes,$sec);
}

sub latitude{
  my $lat = shift @_;
  my $degrees = substr($lat,0,2);
  my $min = '0.' . substr($lat,2,5);
  $min = $min * 1.66666667;
  $degrees = $degrees + $min;
  my $hemi = substr($lat,-1);
  if ( $hemi eq 'S' ){
    $degrees = 0 - $degrees;
  }
  return $degrees;
}

sub longitude{
  my $lon = shift @_;
  my $degrees = substr($lon,0,3);
  my $min = '0.' . substr($lon,3,5);
  $min = $min * 1.66666667;
  $degrees = $degrees + $min;
  my $hemi = substr($lon,-1);
  if ( $hemi eq 'W' ){
    $degrees = 0 - $degrees;
  }
  return $degrees;
}

MainLoop;