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

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

Revision 1.0, Mon Jul 2 02:12:37 2007 UTC (16 years, 10 months ago) by philip
Branch: MAIN

Initial load

#! /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', -command => [\&load_igc]],
     [command    => 'Exit', -command => [\&exit]],
    ]);

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

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;

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

    my $igc = $mw->getOpenFile(-initialdir=>'/cygwin/home/philip',
			       -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 = ();
	$maxalt = 0;
	$minalt = 99999;
	$maxpres = 0;
	$minpres = 99999;
	$minlat = 90;
	$maxlat = -90;
	$minlon = 180;
	$maxlon = 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]).{1}(\d{5})(\d{5})/ ){
		  $time = $1;
		  my $lat = latitude($2);
		  my $lon = longitude($3);
		  my $alt = $4;
		  my $pres = $5;

		  # $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]));

	$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(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;
	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];
            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++;
  	    }
            if ( $launch == 3 ) {
            	$id = $bg->createLine($x,400,$x,150, -fill => "black");
                $id = $bg->createText($x,140, -text => "takeoff");
                $launch++;
            }
  	    if ( ($speed < 0.000100) and ($oalt == $alt) and ($launch > 0)){
    		$launch = 0;
    		$id = $bg->createLine($x,400,$x,150,-fill => "black");
                $id = $bg->createText($x,140, -text => "land");
  	    }
	    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=>'/',
		            -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=>'/',
		            -initialfile=>$igcfile);
           if ( defined $frag ){
            open TEMP, ">" . $frag or die "Couldn't open $frag: $!\n";
            print TEMP $logs[0], $logs[$i];
            close TEMP;
           }
        }
    }
}


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;