Subversion Repositories Projects

Compare Revisions

Ignore whitespace Rev 532 → Rev 533

/MissionCockpit/branches/V0.2.3/mkcockpit.pl
0,0 → 1,2733
#!/usr/bin/perl
#!/usr/bin/perl -d:ptkdb
 
###############################################################################
#
# mkcockpit.pl - MK Mission Cockpit - GUI
#
# Copyright (C) 2009 Rainer Walther (rainerwalther-mail@web.de)
#
# Creative Commons Lizenz mit den Zusaetzen (by, nc, sa)
#
# Es ist Ihnen gestattet:
# * das Werk vervielfältigen, verbreiten und öffentlich zugänglich machen
# * Abwandlungen bzw. Bearbeitungen des Inhaltes anfertigen
#
# Zu den folgenden Bedingungen:
# * Namensnennung.
# Sie müssen den Namen des Autors/Rechteinhabers in der von ihm festgelegten Weise nennen.
# * Keine kommerzielle Nutzung.
# Dieses Werk darf nicht für kommerzielle Zwecke verwendet werden.
# * Weitergabe unter gleichen Bedingungen.
# Wenn Sie den lizenzierten Inhalt bearbeiten oder in anderer Weise umgestalten,
# verändern oder als Grundlage für einen anderen Inhalt verwenden,
# dürfen Sie den neu entstandenen Inhalt nur unter Verwendung von Lizenzbedingungen
# weitergeben, die mit denen dieses Lizenzvertrages identisch oder vergleichbar sind.
#
# Im Falle einer Verbreitung müssen Sie anderen die Lizenzbedingungen, unter welche dieses
# Werk fällt, mitteilen. Am Einfachsten ist es, einen Link auf diese Seite einzubinden.
#
# Jede der vorgenannten Bedingungen kann aufgehoben werden, sofern Sie die Einwilligung
# des Rechteinhabers dazu erhalten.
#
# Diese Lizenz lässt die Urheberpersönlichkeitsrechte unberührt.
#
# Weitere Details zur Lizenzbestimmung gibt es hier:
# Kurzform: http://creativecommons.org/licenses/by-nc-sa/3.0/de/
# Komplett: http://creativecommons.org/licenses/by-nc-sa/3.0/de/legalcode
#
###############################################################################
# 2009-02-20 0.0.1 rw created
# 2009-04-01 0.1.0 rw RC1
# 2009-04-16 0.1.1 rw Bugfix, ALT= average of airsensor and Sat
# 2009-05-14 0.2.0 rw Waypoint Player
# 2009-05-17 0.2.1 rw Cursor-Steuerung fuer WP-Player. Cmdline-Parameter "-geometry"
# 2009-07-18 0.2.2 rw DE/EN multinational
# Target-Balloon with Distance, Tolerance and Holdtime
# Fix footprint "Ausreiser"
# JPEG and PNG maps supported
# Player for KML Files
# 2009-07-26 0.2.3 rw System Messages Balloon
#
#
###############################################################################
 
$Version = "0.2.3 - 2009-07-26";
use threads; # http://search.cpan.org/~jdhedden/threads-1.72/threads.pm
# http://perldoc.perl.org/threads.html
use threads::shared; # http://search.cpan.org/~jdhedden/threads-shared-1.28/shared.pm
use Thread::Queue; # http://search.cpan.org/dist/Thread-Queue-2.11/lib/Thread/Queue.pm
use Tk;
use Tk::Balloon;
use Tk::Dialog;
use Tk::Notebook;
use Tk::JPEG; # http://search.cpan.org/~srezic/Tk-804.028/JPEG/JPEG.pm
use Tk::PNG; # http://search.cpan.org/~srezic/Tk-804.028/PNG/PNG.pm
use Math::Trig;
use Time::HiRes qw(usleep); # http://search.cpan.org/~jhi/Time-HiRes-1.9719/HiRes.pm
use XML::Simple; # http://search.cpan.org/dist/XML-Simple-2.18/lib/XML/Simple.pm
 
# change working directory to program path
my $Cwd = substr ($0, 0, rindex ($0, "mkcockpit.pl"));
chdir $Cwd;
 
# set path for local Perl libs
push @INC, $Cwd . "perl/lib", $Cwd . "perl/site/lib";
 
# Version setting
share (%Version);
$Version{'mkcockpit.pl'} = $Version;
 
# Read configuration
 
$XmlConfigFile = "mkcockpit.xml";
$Cfg = XMLin($XmlConfigFile);
 
require "track.pl"; # Tracking antenna
require "mkcomm.pl"; # MK communication
require "logging.pl"; # CSV and GPX Logging
require "geserver.pl"; # Google Earth Server
require "$Cfg->{'map'}->{'MapDir'}/map.pl"; # Landkarte
require "libmap.pl"; # map subs
require "translate.pl"; # Übersetzungstable
 
# Commandline parameter
my %CmdLine = @ARGV;
 
# Thread fuer Kommunikation mit MK starten
# Output: %MkOsd, %MkTarget, %MkNcDebug, %Mk
# Input: Thread-Queue: $MkSendQueue
$mk_thr = threads->create (\&MkCommLoop) -> detach();
 
# Start Logging Thread
$log_thr = threads->create (\&MkLogLoop) -> detach();
 
# Start GoogleEarth Thread
$ge_thr = threads->create (\&GeServer) -> detach();
 
# Aktuell gültige Karte
my %Map = %{$Maps{'Current'}};
# Hauptfenster
my $main = new MainWindow;
$main->title ("MK Mission Cockpit - Version $Version");
 
if ( $CmdLine{'-geometry'} ne "" )
{
$main->geometry( "$CmdLine{'-geometry'}" );
}
 
#-----------------------------------------------------------------
# Menu
#-----------------------------------------------------------------
 
# Menu bar
my $menu_bar = $main->Menu;
$main->optionAdd("*tearOff", "false");
$main->configure ('-menu' => $menu_bar);
 
my $menu_file = $menu_bar->cascade('-label' => $Translate{'File'});
$menu_file->command('-label' => $Translate{'Preferences'},
'-command' => [\&Configure],
);
$menu_file->separator;
$menu_file->command('-label' => $Translate{'Exit'},
'-command' => sub{exit(0)},
);
 
my $menu_debug = $menu_bar->cascade(-label => $Translate{'Debug'});
$menu_debug->command('-label' => $Translate{'NcOsdDataset'},
'-command' => [\&DisplayHash, \%MkOsd, $Translate{'NcOsdDataset'}, "Display Refresh Heartbeat"],
);
$menu_debug->command('-label' => $Translate{'NcTargetDataset'},
'-command' => [\&DisplayHash, \%MkTarget, $Translate{'NcTargetDataset'}, "Display Refresh Heartbeat"],
);
$menu_debug->command('-label' => $Translate{'NcDebugDataset'},
'-command' => [\&DisplayHash, \%MkNcDebug, $Translate{'NcDebugDataset'}, "Display Refresh Heartbeat"],
);
$menu_debug->command('-label' => $Translate{'NcOther'},
'-command' => [\&DisplayHash, \%Mk, $Translate{'NcOther'}, "Display Refresh Heartbeat"],
);
$menu_debug->separator;
$menu_debug->command('-label' => $Translate{'TrackingDebugDataset'},
'-command' => [\&DisplayHash, \%MkTrack, $Translate{'TrackingDebugDataset'}, "Display Refresh Heartbeat"],
);
 
my $menu_help = $menu_bar->cascade(-label => $Translate{'Help'});
$menu_help->command('-label' => 'Version',
'-command' => [\&DisplayHash, \%Version, $Translate{'Version'}, "Display"],
);
$menu_help->separator;
$menu_help->command('-label' => $Translate{'About'},
'-command' => sub
{
my $License = <<EOF;
Copyright (C) 2009 Rainer Walther (rainerwalther-mail\@web.de)
 
Creative Commons Lizenz mit den Zusaetzen (by, nc, sa)
 
See LICENSE.TXT
EOF
 
my $DlgAbout = $frame_map->Dialog('-title' => $Translate{'AboutMissionCockpit'},
'-text' => "$License",
'-buttons' => ['OK'],
'-bitmap' => 'info',
);
$DlgAbout->Show;
});
 
 
# Hauptfenster Statuszeile
$frame_status = $main->Frame( '-background' => 'lightgray',
) -> pack('-side' => 'bottom',
'-anchor' => 'w',
'-fill' => 'none',
'-expand' => 'y',
);
$status_line = $frame_status->Label ('-text' => $Translate{'StatusLine'},
) -> pack ('-side' => 'bottom',
);
 
#-----------------------------------------------------------------
# Frames
#-----------------------------------------------------------------
 
#
# Frame: Map
#
$frame_map = $main->Frame( '-background' => 'lightgray',
'-relief' => 'sunken',
'-borderwidth' => 5,
) -> pack('-side' => 'top',
'-fill' => 'x',
);
 
# Map Überschrift
$frame_map_top = $frame_map->Frame() -> pack( '-side' => 'top',
'-expand' => 'x',
'-anchor' => 'w',
);
 
$frame_map_top->Label ('-text' => "$Translate{'Map'}: $Map{'Name'} ($Map{'File'})",
'-background' => 'lightgray',
'-relief' => 'flat',
) -> pack( '-side' => 'left' );
 
# Map Statuszeile
$map_status = $frame_map->Frame( '-background' => 'lightgray',
) -> pack('-side' => 'bottom',
'-anchor' => 'w',
'-fill' => 'none',
'-expand' => 'y',
);
$map_status_line = $map_status->Label ( '-text' => $Translate{'StatusLine'},
'-background' => 'lightgray',
) -> pack ('-side' => 'bottom',);
 
# Map Canvas
 
# Canvas size
$MapSizeX = $Map{'Size_X'};
$MapSizeY = $Map{'Size_Y'};
 
$map_canvas = $frame_map->Canvas( '-width' => $MapSizeX,
'-height' => $MapSizeY,
'-cursor' => 'cross',
) -> pack();
 
# load Map photo
$map_canvas->Photo( 'Map',
'-file' => "$Cfg->{'map'}->{'MapDir'}/$Map{'File'}",
);
$map_canvas->createImage( 0, 0,
'-tags' => 'Map',
'-anchor' => 'nw',
'-image' => 'Map',
);
 
# border polygon
$map_canvas->createPolygon( @Map{'Border'},
'-tags' => 'Map-Border',
'-fill' => '',
'-outline' => $Cfg->{'mkcockpit'}->{'ColorAirfield'}, '-width' => 2,
);
# load Heartbeat icon
$map_canvas->Photo( 'HeartbeatSmall',
'-file' => "$Cfg->{'mkcockpit'}->{'IconHeartSmall'}",
);
$map_canvas->Photo( 'HeartbeatLarge',
'-file' => "$Cfg->{'mkcockpit'}->{'IconHeartLarge'}",
);
$map_canvas->createImage( $MapSizeX/4, 10,
'-tags' => 'Heartbeat',
'-anchor' => 'nw',
'-image' => 'HeartbeatSmall',
);
 
# load Satellite icon
$map_canvas->Photo( 'Satellite-Photo',
'-file' => "$Cfg->{'mkcockpit'}->{'IconSatellite'}",
);
$map_canvas->createImage($MapSizeX-290, -100, # hide photo
'-tags' => 'Satellite',
'-anchor' => 'nw',
'-image' => 'Satellite-Photo',
);
 
# load Waypoint icon
$map_canvas->Photo( 'Waypoint-Photo',
'-file' => "$Cfg->{'mkcockpit'}->{'IconWaypoint'}",
);
# load Target icon
$map_canvas->Photo( 'Target-Photo',
'-file' => "$Cfg->{'mkcockpit'}->{'IconTarget'}",
);
$map_canvas->createImage(0, -100, # hide photo
'-tags' => 'Target',
'-anchor' => 'nw',
'-image' => 'Target-Photo',
);
 
# load Fox icon
$map_canvas->Photo( 'Fox-Photo',
'-file' => "$Cfg->{'mkcockpit'}->{'IconFox'}",
);
$map_canvas->createImage($MapSizeX/2-100, $MapSizeY/2,
'-tags' => 'Fox',
'-anchor' => 'nw',
'-image' => 'Fox-Photo',
);
 
# load WP-Player icons
$map_canvas->Photo( 'WpPlay-Foto', '-file' => "$Cfg->{'waypoint'}->{'IconPlay'}");
$map_canvas->Photo( 'WpStop-Foto', '-file' => "$Cfg->{'waypoint'}->{'IconStop'}");
$map_canvas->Photo( 'WpPause-Foto', '-file' => "$Cfg->{'waypoint'}->{'IconPause'}");
$map_canvas->Photo( 'WpNext-Foto', '-file' => "$Cfg->{'waypoint'}->{'IconNext'}");
$map_canvas->Photo( 'WpPrev-Foto', '-file' => "$Cfg->{'waypoint'}->{'IconPrev'}");
$map_canvas->Photo( 'WpFirst-Foto', '-file' => "$Cfg->{'waypoint'}->{'IconFirst'}");
$map_canvas->Photo( 'WpLast-Foto', '-file' => "$Cfg->{'waypoint'}->{'IconLast'}");
$map_canvas->Photo( 'WpHome-Foto', '-file' => "$Cfg->{'waypoint'}->{'IconHome'}");
$map_canvas->Photo( 'WpWpt-Foto', '-file' => "$Cfg->{'waypoint'}->{'IconWpt'}");
$map_canvas->Photo( 'WpKml-Foto', '-file' => "$Cfg->{'waypoint'}->{'IconKml'}");
 
$map_canvas->createImage($MapSizeX/2+100, $MapSizeY-48,
'-tags' => 'Wp-PlayPause',
'-anchor' => 'nw',
'-image' => 'WpPlay-Foto',
);
$map_canvas->createImage($MapSizeX/2+150, $MapSizeY-48,
'-tags' => 'Wp-Stop',
'-anchor' => 'nw',
'-image' => 'WpStop-Foto',
);
$map_canvas->createImage($MapSizeX/2, $MapSizeY-48,
'-tags' => 'Wp-Next',
'-anchor' => 'nw',
'-image' => 'WpNext-Foto',
);
$map_canvas->createImage($MapSizeX/2-50, $MapSizeY-48,
'-tags' => 'Wp-Prev',
'-anchor' => 'nw',
'-image' => 'WpPrev-Foto',
);
$map_canvas->createImage($MapSizeX/2-100, $MapSizeY-48,
'-tags' => 'Wp-First',
'-anchor' => 'nw',
'-image' => 'WpFirst-Foto',
);
$map_canvas->createImage($MapSizeX/2+50, $MapSizeY-48,
'-tags' => 'Wp-Last',
'-anchor' => 'nw',
'-image' => 'WpLast-Foto',
);
$map_canvas->createImage($MapSizeX/2-150, $MapSizeY-48,
'-tags' => 'Wp-Home',
'-anchor' => 'nw',
'-image' => 'WpHome-Foto',
);
$map_canvas->createImage($MapSizeX/2-200, $MapSizeY-48,
'-tags' => 'Wp-WptKml',
'-anchor' => 'nw',
'-image' => 'WpWpt-Foto',
);
 
 
 
# Balloon attached to Canvas
$map_balloon = $frame_map->Balloon('-statusbar' => $status_line, );
$map_balloon->attach($map_canvas,
'-balloonposition' => 'mouse',
'-state' => 'balloon',
'-msg' => { 'MK-Arrow' => $Translate{'Balloon-MK-Arrow'},
'MK-Home-Line' => $Translate{'Balloon-MK-Home-Line'},
'MK-Home-Dist' => $Translate{'Balloon-MK-Home-Dist'},
'MK-Target-Line' => $Translate{'Balloon-MK-Target-Line' },
'MK-Target-Dist' => $Translate{'Balloon-MK-Target-Dist'},
'MK-Speed' => $Translate{'Balloon-MK-Speed'},
'Map-Variometer' => $Translate{'Balloon-Map-Variometer' },
'Map-Variometer-Pointer' => $Translate{'Balloon-Map-Variometer-Pointer'},
'Map-Variometer-Skala' => $Translate{'Balloon-Map-Variometer-Pointer'},
'Fox' => $Translate{'Balloon-Fox'},
'Heartbeat' => $Translate{'Balloon-Heartbeat'},
'Satellite' => $Translate{'Balloon-Satellite'},
'Waypoint' => $Translate{'Balloon-Waypoint'},
'Map-Border' => $Translate{'Balloon-Map-Border'},
'Waypoint-Connector' => $Translate{'Balloon-Waypoint-Connector'},
'Wp-PlayPause' => $Translate{'Balloon-Wp-PlayPause'},
'Wp-Stop' => $Translate{'Balloon-Wp-Stop'},
'Wp-First' => $Translate{'Balloon-Wp-First'},
'Wp-Last' => $Translate{'Balloon-Wp-Last'},
'Wp-Next' => $Translate{'Balloon-Wp-Next'},
'Wp-Prev' => $Translate{'Balloon-Wp-Prev'},
'Wp-Home' => $Translate{'Balloon-Wp-Home'},
'Wp-WptKml' => $Translate{'Balloon-Wp-WptKml'},
},
);
 
#
# Mouse buttons
#
# general Mouse button 1
$map_canvas->CanvasBind("<Button-1>", sub
{
# print coords in status line
my ($x, $y) = ($Tk::event->x, $Tk::event->y);
my ($Lat, $Lon) = &MapXY2Gps($x, $y);
$map_status_line->configure ('-text' => "Lat: $Lat Lon: $Lon x: $x y: $y");
});
# Mouse button 1 for Fox
my $FoxOldx = 0;
my $FoxOldy = 0;
 
# Pick Fox
$map_canvas->bind('Fox' => '<Button-1>' => sub
{
# prepare to move Fox
my ($x, $y) = ($Tk::event->x, $Tk::event->y);
$FoxOldx = $x;
$FoxOldy = $y;
$FoxTime = time;
});
 
# Move Fox
$map_canvas->bind('Fox' => '<Button1-Motion>' => sub
{
my ($x, $y) = ($Tk::event->x, $Tk::event->y);
my $id = $map_canvas->find('withtag', 'current');
 
$map_canvas->move($id => $x - $FoxOldx, $y - $FoxOldy);
$FoxOldx = $x;
$FoxOldy = $y;
if ( time > $FoxTime )
{
# wenn in Bewegung Koordinaten nur 1/s senden
my ($x0, $y0, $x1, $y1) = $map_canvas->bbox ($id);
$x = $x0 + ($x1 - $x0)/2;
$y = $y1;
 
my ($Lat, $Lon) = &MapXY2Gps($x, $y);
&MkFlyTo ( '-lat' => $Lat,
'-lon' => $Lon,
'-mode' => "Target",
);
$FoxTime = time;
 
$map_status_line->configure ('-text' => "$Translate{'TargetCoordSent'} -> Lat: $Lat Lon: $Lon x: $x y: $y");
}
});
 
# Release Fox
$map_canvas->bind('Fox' => '<Button1-ButtonRelease>' => sub
{
my ($x, $y) = ($Tk::event->x, $Tk::event->y);
my $id = $map_canvas->find('withtag', 'current');
 
my ($x0, $y0, $x1, $y1) = $map_canvas->bbox ($id);
$x = $x0 + ($x1 - $x0)/2;
$y = $y1;
my ($Lat, $Lon) = &MapXY2Gps($x, $y);
&MkFlyTo ( '-lat' => $Lat,
'-lon' => $Lon,
'-mode' => "Target",
);
 
# Show user that Waypoints in MK are cleared
$WaypointsModified = 1;
&WpRedrawLines();
 
$map_status_line->configure ('-text' => "$Translate{'TargetCoordSent'} -> Lat: $Lat Lon: $Lon x: $x y: $y");
});
 
# Pick Waypoint
$map_canvas->bind('Waypoint' => '<Button-1>' => sub
{
# prepare to move
my ($x, $y) = ($Tk::event->x, $Tk::event->y);
$WpOldx = $x;
$WpOldy = $y;
});
# Move Waypoint
$map_canvas->bind('Waypoint' => '<Button1-Motion>' => sub
{
my ($x, $y) = ($Tk::event->x, $Tk::event->y);
my $id = $map_canvas->find('withtag', 'current');
 
# move icon and Wp-Number
my $WpIndex = &WpGetIndexFromId($id);
if ( $WpIndex >= 0 )
{
my $Tag = $Waypoints[$WpIndex]{'Tag'};
$map_canvas->move($Tag => $x - $WpOldx, $y - $WpOldy);
}
 
$WpOldx = $x;
$WpOldy = $y;
});
 
# Release Wp
$map_canvas->bind('Waypoint' => '<Button1-ButtonRelease>' => sub
{
my ($x, $y) = ($Tk::event->x, $Tk::event->y);
my $id = $map_canvas->find('withtag', 'current');
 
# take coords from lower/middle icon position
my ($x0, $y0, $x1, $y1) = $map_canvas->bbox ($id);
$x = $x0 + ($x1 - $x0)/2;
$y = $y1;
 
# update Waypoint-Array
my $WpIndex = &WpGetIndexFromId($id);
if ( $WpIndex >= 0 )
{
# got it: set new coords
 
my ($Lat, $Lon) = &MapXY2Gps($x, $y);
my $Wp = $Waypoints[$WpIndex];
$Wp->{'MapX'} = $x;
$Wp->{'MapY'} = $y;
$Wp->{'Pos_Lat'} = $Lat;
$Wp->{'Pos_Lon'} = $Lon;
# redraw connector-lines
&WpRedrawLines();
 
# red connectors: Wp still have to be sent to MK
$map_canvas->itemconfigure('Waypoint-Connector',
'-fill' => $Cfg->{'mkcockpit'}->{'ColorWpResend'},
);
$WaypointsModified = 1;
my $WpNum = $WpIndex + 1;
$map_status_line->configure ('-text' => "$Translate{'WpMoved'}: $WpNum -> Lat: $Lat Lon: $Lon x: $x y: $y");
}
});
 
 
#
# Player:
# Waypoint-List: @Waypoints
# KML-Target-List: @KmlTargets
#
 
# Player state machine
$PlayerMode = 'Stop'; # Start, Stop, Pause, Home ...
$WpPlayerIndex = 0;
$WpPlayerHoldtime = -1;
$KmlPlayerIndex = 0;
$WptKmlMode = 'WPT'; # WPT, KML
 
# Mouse bindings
$map_canvas->bind('Wp-PlayPause' => '<Button-1>' => \&CbPlayerPlayPause );
$map_canvas->bind('Wp-Next' => '<Button-1>' => \&CbPlayerNext );
$map_canvas->bind('Wp-Prev' => '<Button-1>' => \&CbPlayerPrev );
$map_canvas->bind('Wp-First' => '<Button-1>' => \&CbPlayerFirst );
$map_canvas->bind('Wp-Last' => '<Button-1>' => \&CbPlayerLast );
$map_canvas->bind('Wp-Home' => '<Button-1>' => \&CbPlayerHome );
$map_canvas->bind('Wp-Stop' => '<Button-1>' => \&CbPlayerStop );
$map_canvas->bind('Wp-WptKml' => '<Button-1>' => \&CbPlayerWptKml );
 
 
# Focus Canvas, if any key pressed. Needed for the following key-bindings
$main->bind('<Any-Enter>' => sub { $map_canvas->Tk::focus });
 
# Disable default arrow-key bindings on canvas
$main->bind('Tk::Canvas',"<$_>",undef)for qw /Left Right Up Down/;
 
# keyboard bindings
$map_canvas->Tk::bind( '<Key-space>' , \&CbPlayerPlayPause );
$map_canvas->Tk::bind( '<Key-n>' , \&CbPlayerNext );
$map_canvas->Tk::bind( '<Key-p>' , \&CbPlayerPrev );
$map_canvas->Tk::bind( '<Key-f>' , \&CbPlayerFirst );
$map_canvas->Tk::bind( '<Key-l>' , \&CbPlayerLast );
$map_canvas->Tk::bind( '<Key-h>' , \&CbPlayerHome );
$map_canvas->Tk::bind( '<Key-s>' , \&CbPlayerStop );
$map_canvas->Tk::bind( '<Key-w>' , \&CbPlayerWptKml );
$map_canvas->Tk::bind( '<Key-k>' , \&CbPlayerWptKml );
$map_canvas->Tk::bind( '<Key-Left>' , [\&CbPlayerMove, -1, 0] );
$map_canvas->Tk::bind( '<Key-Right>' , [\&CbPlayerMove, 1, 0] );
$map_canvas->Tk::bind( '<Key-Up>' , [\&CbPlayerMove, 0, 1] );
$map_canvas->Tk::bind( '<Key-Down>' , [\&CbPlayerMove, 0, -1] );
$map_canvas->Tk::bind( '<Key-Escape>', sub { exit; } );
 
 
 
# Mouse button 3 context menu
my $map_menu = $map_canvas->Menu('-tearoff' => 0,
'-title' =>'None',
'-menuitems' =>
[
[Button => $Translate{'WpAddAndSend'}, -command => sub
{
 
# send Wp to MK
($Lat, $Lon) = &MapXY2Gps($MapCanvasX, $MapCanvasY);
&MkFlyTo ( '-lat' => $Lat,
'-lon' => $Lon,
'-mode' => "Waypoint"
);
# save Wp-Hash in Waypoint-Array
my $Wp = {};
my $Tag = sprintf "Waypoint-%d.%d", time, int (rand(9)) ; # kind of unique Tag for this Wp
$Wp->{'Tag'} = $Tag;
$Wp->{'MapX'} = $MapCanvasX;
$Wp->{'MapY'} = $MapCanvasY;
$Wp->{'Pos_Lat'} = $Lat;
$Wp->{'Pos_Lon'} = $Lon;
$Wp->{'Pos_Alt'} = $MkOsd{'CurPos_Alt'};
$Wp->{'Heading'} = $Cfg->{'waypoint'}->{'DefaultHeading'};
$Wp->{'ToleranceRadius'} = $Cfg->{'waypoint'}->{'DefaultToleranceRadius'};
$Wp->{'Holdtime'} = $Cfg->{'waypoint'}->{'DefaultHoldtime'};
$Wp->{'Event_Flag'} = $Cfg->{'waypoint'}->{'DefaultEventFlag'};
push @Waypoints, $Wp;
 
# switch player to Wp mode and redraw waypoints
&WptKmlSwitch ('WPT');
 
$map_status_line->configure ('-text' => "$Translate{'WpSavedAndSent'} -> Lat: $Lat Lon: $Lon");
}],
 
 
[Button => $Translate{'WpProperties'}, -command => sub
{
# find Wp-Hash for selected icon/tag
my $WpIndex = &WpGetIndexFromId($MapCanvasId);
if ( $WpIndex >= 0 )
{
my $Wp = $Waypoints[$WpIndex];
my $WpNum = $WpIndex + 1;
 
&DisplayHash ($Wp, "$Translate{'WpProperties'} $WpNum", "Edit Waypoint Refresh");
 
$map_status_line->configure ('-text' => "$Translate{'WpProperties'} $WpNum");
}
}],
[Button => $Translate{'WpResendAll'}, -command => sub
{
&WpSendAll();
$map_status_line->configure ('-text' => $Translate{'WpAllSent'});
}],
 
'', # Separator
[Button => $Translate{'WpLoadAndSend'}, -command => sub
{
$WpFile = $main->getOpenFile('-defaultextension' => ".xml",
'-filetypes' =>
[['Waypoints', '.xml' ],
['All Files', '*', ],
],
'-initialdir' => $Cfg->{'waypoint'}->{'WpDir'},
'-title' => $Translate{'WpLoad'},
);
if ( -f $WpFile )
{
# XML in Hash-Ref lesen
my $Wp = XMLin($WpFile, ForceArray => 1);
 
# XML Hash-Ref in Wp-Array umkopieren
undef @Waypoints;
foreach $key (sort keys %$Wp)
{
my $Point = $Wp->{$key}->[0];
 
# relative Pixelkoordinaten auf Bildgroesse umrechnen
if ( $Point->{'MapX'} <= 1 and $Point->{'MapY'} <= 1 )
{
$Point->{'MapX'} = int ( $Point->{'MapX'} * $MapSizeX + 0.5 );
$Point->{'MapY'} = int ( $Point->{'MapY'} * $MapSizeY + 0.5 );
}
 
# GPS Koordinaten für die aktuelle Karte neu aus Map x/y berechnen
my ($Lat, $Lon) = &MapXY2Gps($Point->{'MapX'}, $Point->{'MapY'});
$Point->{'Pos_Lat'} = $Lat;
$Point->{'Pos_Lon'} = $Lon;
 
push @Waypoints, $Point;
}
 
# send all Wp to MK
&WpSendAll();
 
# switch player to Wp mode and redraw waypoints
&WptKmlSwitch ('WPT');
$map_status_line->configure ('-text' => "$Translate{'WpLoadedAndSent'}: $WpFile");
}
}],
[Button => $Translate{'WpSave'}, -command => sub
{
$WpFile = $main->getSaveFile('-defaultextension' => ".xml",
'-filetypes' =>
[['Waypoints', '.xml' ],
['All Files', '*', ],
],
'-initialdir' => $Cfg->{'waypoint'}->{'WpDir'},
'-title' => $Translate{'WpSave'},
);
 
# Waypoint-Array in Hash umkopieren
my %Wp;
for $i ( 0 .. $#Waypoints )
{
 
my $key = sprintf ("WP-%04d", $i);
$Wp{$key} = $Waypoints[$i];
 
# Pixelkoordinaten relativ zur Bildgroesse speichern
$Wp{$key}{'MapX'} /= $MapSizeX;
$Wp{$key}{'MapY'} /= $MapSizeY;
}
 
# WP-Hash als XML speichern
&XMLout (\%Wp,
'OutputFile' => $WpFile,
'AttrIndent' => '1',
'RootName' => 'Waypoints',
);
$map_status_line->configure ('-text' => "$Translate{'WpSaved'}: $WpFile");
}],
'', # Separator
 
[Button => $Translate{'WpDelete'}, -command => sub
{
# find Wp-Hash for selected icon/tag
my $WpIndex = &WpGetIndexFromId($MapCanvasId);
if ( $WpIndex >= 0 )
{
my $Wp = $Waypoints[$WpIndex];
 
# remove icon and Wp-Number on canvas;
$map_canvas->delete($Wp->{'Tag'});
 
# delete Wp in Waypoint-Array
splice @Waypoints, $WpIndex, 1;
 
# redraw connector-lines
$WaypointsModified = 1;
&WpRedrawLines();
&WpRedrawIcons(); # wg. Wp-Nummern
 
$WpNum = $WpIndex + 1;
$map_status_line->configure ('-text' => "$Translate{'WpDeleted'}: $WpNum");
}
}],
 
[Button => $Translate{'WpAllDeleteAndSend'}, -command => sub
{
undef @Waypoints;
$WpPlayerIndex = 0;
$WpPlayerHoldtime = -1;
 
# remove all Wp-Icons and Wp-Number on canvas
&WpHide();
 
&WpSendAll();
 
$map_status_line->configure ('-text' => "$Translate{'WpAllDeleted'}: $WpIndex");
}],
 
'', # Separator
 
[Button => $Translate{'KmlLoadAndPlay'}, -command => sub
{
$KmlFile = $main->getOpenFile('-defaultextension' => ".kml",
'-filetypes' =>
[['KML', '.kml' ],
['All Files', '*', ],
],
'-initialdir' => $Cfg->{'waypoint'}->{'KmlDir'},
'-title' => $Translate{'KmlLoad'},
);
if ( -f $KmlFile )
{
# XML in Hash-Ref lesen
my $Kml = XMLin($KmlFile);
 
# init state maschine
undef @KmlTargets;
$KmlPlayerIndex = 0;
 
my $Coordinates = $Kml->{Document}->{Placemark}->{LineString}->{coordinates};
foreach $Line (split "\n", $Coordinates)
{
chomp $Line;
$Line =~ s/\s//g; # remove white space
if ( $Line ne "" )
{
my ($Lon, $Lat, $Alt) = split ",", $Line;
$Lon = sprintf ("%f", $Lon);
$Lat = sprintf ("%f", $Lat);
$Alt = sprintf ("%f", $Alt);
 
push @KmlTargets, {'Lat' => $Lat,
'Lon' => $Lon,
'Alt' => $Alt,
};
}
}
 
# switch player to KML mode and redraw track
&WptKmlSwitch ('KML');
 
$map_status_line->configure ('-text' => "$Translate{'KmlLoaded'}: $KmlFile" );
}
 
}],
 
'', # Separator
[Button => $Translate{'WpFlyImmediately'}, -command => sub
{
&MkFlyTo ( '-x' => $MapCanvasX,
'-y' => $MapCanvasY,
'-mode' => "Target"
);
 
# redraw connector-lines
$WaypointsModified = 1;
&WpRedrawLines();
 
$map_status_line->configure ('-text' => "$Translate{'TargetCoordSent'} -> Lat: $Lat Lon: $Lon x: $MapCanvasX y: $MapCanvasY");
}],
]
);
$map_canvas->CanvasBind("<Button-3>" => [ sub
{
$map_canvas->focus;
my($w, $x, $y) = @_;
($MapCanvasX, $MapCanvasY) = ($Tk::event->x, $Tk::event->y);
$MapCanvasId = $map_canvas->find('withtag', 'current');
$map_menu->post($x, $y);
}, Ev('X'), Ev('Y') ] );
 
 
#
# Objects on canvas
#
 
# Line from MK to Home
$map_canvas->createLine ( $MapSizeX/2, $MapSizeY/2, $MapSizeX/2, $MapSizeY/2,
'-tags' => 'MK-Home-Line',
'-arrow' => 'none',
'-fill' => $Cfg->{'mkcockpit'}->{'ColorHomeLine'},
'-width' => 3,
);
# Text Entfernung positioniert an der Home-Linie
$map_canvas->createText ( $MapSizeX/2 + 8, $MapSizeY/2 - 8,
'-tags' => 'MK-Home-Dist',
'-text' => '0 m',
'-anchor' => 'w',
'-font' => '-*-Arial-Bold-R-Normal--*-200-*',
'-fill' => $Cfg->{'mkcockpit'}->{'ColorHomeDist'},
);
 
# Line from MK to Target, draw invisible out of sight
$map_canvas->createLine ( 0, -100, 0, -100,
'-tags' => 'MK-Target-Line',
'-arrow' => 'none',
'-fill' => $Cfg->{'mkcockpit'}->{'ColorTargetLine'},
'-width' => 3,
);
# Text Entfernung positioniert an der Target-Linie
$map_canvas->createText ( 0, -100,
'-tags' => 'MK-Target-Dist',
'-text' => '0 m',
'-anchor' => 'w',
'-font' => '-*-Arial-Bold-R-Normal--*-200-*',
'-fill' => $Cfg->{'mkcockpit'}->{'ColorTargetDist'},
);
# MK Geschwindigkeits-Vektor
$MapMkSpeedLen = 60; # Länge Speed-Zeiger
my $x0 = $MapSizeX/2;
my $y0 = $MapSizeY/2;
my $x1 = $MapSizeX/2;
my $y1 = $MapSizeY/2 - $MapMkSpeedLen;
$map_canvas->createLine ( $x0, $y0, $x1, $y1,
'-tags' => 'MK-Speed',
'-arrow' => 'last',
'-arrowshape' => [10, 10, 3 ],
'-fill' => $Cfg->{'mkcockpit'}->{'ColorSpeedVector'},
'-width' => 4,
);
 
# MK als Pfeilspitze einer Linie darstellen
$MapMkLen = 25;
my $x0 = $MapSizeX/2;
my $y0 = $MapSizeY/2 + $MapMkLen/2;
my $x1 = $MapSizeX/2;
my $y1 = $MapSizeY/2 - $MapMkLen/2;
$map_canvas->createLine ( $x0, $y0, $x1, $y1,
'-tags' => 'MK-Arrow',
'-arrow' => 'last',
'-arrowshape' => [25, 30, 10 ],
'-fill' => $Cfg->{'mkcockpit'}->{'ColorMkSatNo'},
'-width' => 1
);
$MkPos_x = $MapSizeX/2;
$MkPos_y = $MapSizeY/2;
 
 
# OSD Daten auf Karte anzeigen
 
# Flugzeit
$map_canvas->createText ( $MapSizeX/2 - 40, 20,
'-tags' => 'MK-OSD-Tim-Label',
'-text' => 'TIM',
'-font' => '-*-Arial-Bold-R-Normal--*-150-*',
'-fill' => $Cfg->{'mkcockpit'}->{'ColorOsd'},
'-anchor' => 'w',
);
 
$map_canvas->createText ( $MapSizeX/2, 20,
'-tags' => 'MK-OSD-Tim-Value',
'-text' => $MkFlyingTime, # $MkOsd{'FlyingTime'},
'-font' => '-*-Arial-Bold-R-Normal--*-270-*',
'-fill' => $Cfg->{'mkcockpit'}->{'ColorOsd'},
'-anchor' => 'w',
);
 
# Batterie Spannung
$map_canvas->createText ( $MapSizeX/2 - 40, 50,
'-tags' => 'MK-OSD-Bat-Label',
'-text' => 'BAT',
'-font' => '-*-Arial-Bold-R-Normal--*-150-*',
'-fill' => $Cfg->{'mkcockpit'}->{'ColorOsd'},
'-anchor' => 'w',
);
 
$map_canvas->createText ( $MapSizeX/2, 50,
'-tags' => 'MK-OSD-Bat-Value',
'-text' => sprintf ("%3.1f V", $MkOsd{'UBat'}),
'-font' => '-*-Arial-Bold-R-Normal--*-270-*',
'-fill' => $Cfg->{'mkcockpit'}->{'ColorOsd'},
'-anchor' => 'w',
);
 
# Ground speed
$map_canvas->createText ( 10, 20,
'-tags' => 'MK-OSD-Spd-Label',
'-text' => 'SPD',
'-font' => '-*-Arial-Bold-R-Normal--*-150-*',
'-fill' => $Cfg->{'mkcockpit'}->{'ColorOsd'},
'-anchor' => 'w',
);
 
$map_canvas->createText ( 50, 20,
'-tags' => 'MK-OSD-Spd-Value',
'-text' => sprintf ("%3d km/h", $MkOsd{'GroundSpeed'} * 0.036),
'-font' => '-*-Arial-Bold-R-Normal--*-270-*',
'-fill' => $Cfg->{'mkcockpit'}->{'ColorOsd'},
'-anchor' => 'w',
);
 
# Hoehe (Luftdruck)
$map_canvas->createText ( 10, 50,
'-tags' => 'MK-OSD-Alt-Label',
'-text' => 'ALT',
'-font' => '-*-Arial-Bold-R-Normal--*-150-*',
'-fill' => $Cfg->{'mkcockpit'}->{'ColorOsd'},
'-anchor' => 'w',
);
 
$map_canvas->createText ( 50, 50,
'-tags' => 'MK-OSD-Alt-Value',
'-text' => sprintf ("%3d m", $MkOsd{'Altimeter'}/$Cfg->{'mkcockpit'}->{'AltFactor'}),
'-font' => '-*-Arial-Bold-R-Normal--*-270-*',
'-fill' => $Cfg->{'mkcockpit'}->{'ColorOsd'},
'-anchor' => 'w',
);
 
# Variometer
$map_canvas->createText ( 10, 80,
'-tags' => 'MK-OSD-Vsi-Label',
'-text' => 'VSI',
'-font' => '-*-Arial-Bold-R-Normal--*-150-*',
'-fill' => $Cfg->{'mkcockpit'}->{'ColorOsd'},
'-anchor' => 'w',
);
 
$map_canvas->createText ( 50, 80,
'-tags' => 'MK-OSD-Vsi-Value',
'-text' => sprintf ("%3d", $MkOsd{'Variometer'}),
'-font' => '-*-Arial-Bold-R-Normal--*-270-*',
'-fill' => $Cfg->{'mkcockpit'}->{'ColorOsd'},
'-anchor' => 'w',
);
 
# Anzahl Satelitten
$map_canvas->createText ( $MapSizeX - 220, 20,
'-tags' => 'MK-OSD-Sat-Label',
'-text' => 'SAT',
'-font' => '-*-Arial-Bold-R-Normal--*-150-*',
'-fill' => $Cfg->{'mkcockpit'}->{'ColorOsd'},
'-anchor' => 'w',
);
 
$map_canvas->createText ( $MapSizeX - 170, 20,
'-tags' => 'MK-OSD-Sat-Value',
'-text' => "$MkOsd{'SatsInUse'}",
'-font' => '-*-Arial-Bold-R-Normal--*-270-*',
'-fill' => $Cfg->{'mkcockpit'}->{'ColorOsd'},
'-anchor' => 'w',
);
 
# Wegpunkte
$map_canvas->createText ( $MapSizeX - 220, 50,
'-tags' => 'MK-OSD-Wp-Label',
'-text' => 'WPT',
'-font' => '-*-Arial-Bold-R-Normal--*-150-*',
'-fill' => $Cfg->{'mkcockpit'}->{'ColorOsd'},
'-anchor' => 'w',
);
 
$map_canvas->createText ( $MapSizeX - 170, 50,
'-tags' => 'MK-OSD-Wp-Value',
'-text' => $MkOsd{'WaypointIndex'} . "/" . $MkOsd{'WaypointNumber'} ,
'-font' => '-*-Arial-Bold-R-Normal--*-270-*',
'-fill' => $Cfg->{'mkcockpit'}->{'ColorOsd'},
'-anchor' => 'w',
);
# Navigation Mode
$map_canvas->createText ( $MapSizeX - 220, 80,
'-tags' => 'MK-OSD-Mode-Label',
'-text' => 'MOD',
'-font' => '-*-Arial-Bold-R-Normal--*-150-*',
'-fill' => $Cfg->{'mkcockpit'}->{'ColorOsd'},
'-anchor' => 'w',
);
 
$map_canvas->createText ( $MapSizeX - 170, 80,
'-tags' => 'MK-OSD-Mode-Value',
'-text' => '' ,
'-font' => '-*-Arial-Bold-R-Normal--*-270-*',
'-fill' => $Cfg->{'mkcockpit'}->{'ColorOsd'},
'-anchor' => 'w',
);
 
# Variometer on canvas
my @Polygon;
for ( $y = -100; $y <= 100; $y += 10)
{
my $Len = 5;
if ( ($y % 50) == 0 )
{
$Len = 10;
$map_canvas->createText ( $Len+5, $MapSizeY/2 + $y,
'-tags' => 'Map-Variometer-Skala',
'-text' => sprintf ("%3d", -$y / 10),
'-anchor' => 'w',
'-font' => '-*-Arial-Normal-R-Normal--*-150-*',
'-fill' => $Cfg->{'mkcockpit'}->{'ColorVariometer'},
);
}
push @Polygon, ( 0, $MapSizeY/2 + $y);
push @Polygon, ($Len, $MapSizeY/2 + $y);
push @Polygon, ( 0, $MapSizeY/2 + $y);
}
 
$map_canvas->createLine(@Polygon,
'-tags' => 'Map-Variometer',
'-fill' => $Cfg->{'mkcockpit'}->{'ColorVariometer'},
'-width' => 2,
'-arrow' => 'none',
);
# Vario Pointer
$map_canvas->createPolygon( 5, $MapSizeY/2, 20, $MapSizeY/2+10, 20, $MapSizeY/2-10,
'-tags' => 'Map-Variometer-Pointer',
'-fill' => $Cfg->{'mkcockpit'}->{'ColorVariometerPointer'},
'-outline' => 'black', '-width' => 1,
);
 
# Tracking Canvas
 
if ( $Cfg->{'track'}->{'Active'} =~ /y/i )
{
# Canvas size
$TrackSizeX = 125;
$TrackSizeY = 100;
$TrackOffY = $TrackSizeY - $MapSizeY + 20;
$TrackPtrLen = 50; # Länge Zeiger
 
# draw in map-canvas
$track_canvas = $map_canvas;
 
# Ziffernblatt
my $x0 = $TrackSizeX/2 - $TrackPtrLen;
my $y0 = $TrackSizeY + $TrackPtrLen - $TrackOffY;
my $x1 = $TrackSizeX/2 + $TrackPtrLen;
my $y1 = $TrackSizeY - $TrackPtrLen - $TrackOffY;
$track_canvas->createArc ( $x0, $y0, $x1, $y1,
'-extent' => '200',
'-start' => '-10',
'-style' => 'chord',
'-outline' => 'gray', '-width' => '1',
);
 
# Skala Ziffernblatt
for ($i=0; $i<=180; $i+=15)
{
my $pi = 3.14159265358979;
my $x0 = $TrackSizeX/2 - ($TrackPtrLen - 20) * cos($i / 180 * $pi);
my $y0 = $TrackSizeY - ($TrackPtrLen - 20) * sin($i / 180 * $pi) - $TrackOffY;
my $x1 = $TrackSizeX/2 - ($TrackPtrLen - 28) * cos($i / 180 * $pi);
my $y1 = $TrackSizeY - ($TrackPtrLen - 28) * sin($i / 180 * $pi) - $TrackOffY;
$track_canvas->createLine ( $x0, $y0, $x1, $y1,
'-fill' => 'white',
'-width' => 1,
);
}
 
# Skala Beschriftung Ziffernblatt
for ($i=0; $i<=180; $i+=45)
{
my $pi = 3.14159265358979;
my $x0 = $TrackSizeX/2 - ($TrackPtrLen - 12) * cos($i / 180 * $pi);
my $y0 = $TrackSizeY - ($TrackPtrLen - 12) * sin($i / 180 * $pi) - $TrackOffY;
$track_canvas->createText ( $x0, $y0,
'-text' => $i - 90,
'-fill' => 'white',
);
}
 
# Ziffernblatt Beschriftung Einheit
my $x0 = $TrackSizeX/2;
my $y0 = $MapSizeY -6;
$track_canvas->createText ( $x0, $y0,
'-text' => "Antenne Winkel",
'-justify' => 'center',
'-fill' => 'white',
);
# Zeiger
my $x0 = $TrackSizeX/2;
my $y0 = $TrackSizeY - 0 - $TrackOffY;
my $x1 = $TrackSizeX/2;
my $y1 = $TrackSizeY - ($TrackPtrLen - 22) - $TrackOffY;
$track_ptr_id= $track_canvas->createLine ( $x0, $y0, $x1, $y1,
'-tags' => 'Track-Ptr',
'-arrow' => 'last',
'-arrowshape' => [20, 30, 5 ],
'-fill' => 'red',
'-width' => 8,
);
# Zeiger Center
my $Dia = 7;
my $x0 = $TrackSizeX/2 - $Dia;
my $y0 = $TrackSizeY + $Dia - $TrackOffY;
my $x1 = $TrackSizeX/2 + $Dia;
my $y1 = $TrackSizeY - $Dia - $TrackOffY;
$track_canvas->createArc ( $x0, $y0, $x1, $y1,
'-extent' => '359',
'-outline' => 'gray', '-width' => 1,
'-fill' => 'gray',
);
}
#-----------------------------------------------------------------
# Timer
#-----------------------------------------------------------------
 
#
# Timer: 5s
#
$main->repeat (5000, sub
{
if ( ! $MkSendWp )
{
# Abfragefrequenz OSD und Debug regelmäßig neu einstellen, falls Übertragungsfehler
$MkSendQueue->enqueue( "o", "$AddrNC", pack ("C", 10) ); # Frequenz OSD Datensatz, * 10ms
$MkSendQueue->enqueue( "d", "$AddrNC", pack ("C", 10) ); # Frequenz MK Debug Datensatz, * 10ms
$MkSendQueue->enqueue( "v", "$AddrNC", ""); # Version
$MkSendQueue->enqueue( "e", "$AddrNC", ""); # Error Text Request
}
});
 
#
# Timer: 0.1s - Map Overlay aktualisieren
#
$frame_map_top->repeat (100, sub
{
 
# Clear old messages from this timer
&MkMessageInit ("Timer-MapOverlay");
 
lock (%MkOsd); # until end of block
 
# Aktuell gültige Karte
my %Map = %{$Maps{'Current'}};
 
if ( $MkOsd{'_Timestamp'} >= time-2 )
{
# Gueltige OSD Daten
my $SatsInUse = $MkOsd{'SatsInUse'};
if ( $SatsInUse > 0 and $MkOsd{'CurPos_Stat'} == 1 and $MkOsd{'HomePos_Stat'} == 1 )
{
# ausreichender GPS Empfang
 
# get x,y map coords of current position
my ($C_x, $C_y, $C_Angel) = &MapGps2XY($MkOsd{'CurPos_Lat'}, $MkOsd{'CurPos_Lon'}, $MkOsd{'CompassHeading'});
# rotate MK arrow
my $dy = sin (deg2rad $C_Angel) * ($MapMkLen/2);
my $dx = cos (deg2rad $C_Angel) * ($MapMkLen/2);
my $x0 = $C_x - $dx;
my $y0 = $C_y - $dy;
my $x1 = $C_x + $dx;
my $y1 = $C_y + $dy;
$map_canvas->coords ('MK-Arrow', $x0, $y0, $x1, $y1);
$MkPos_x = $C_x;
$MkPos_y = $C_y;
 
# Update speed vector
my $MapAngel = &MapAngel(); # Norh to Map-Horizont
my $GpsSpeedNorth = $MkNcDebug{'Analog_21'};
my $GpsSpeedEast = $MkNcDebug{'Analog_22'};
my $PhiGpsSpeed = rad2deg atan2 ( $GpsSpeedEast, $GpsSpeedNorth );
$PhiMapSpeed = $PhiGpsSpeed - $MapAngel;
 
# 555 cm/s ~ 20 km/h -> Zeigerlänge = $MkSpeedLen bei 20 km/h
my $dy = sin (deg2rad $PhiMapSpeed) * $MapMkSpeedLen * $MkOsd{'GroundSpeed'} / 555;
my $dx = cos (deg2rad $PhiMapSpeed) * $MapMkSpeedLen * $MkOsd{'GroundSpeed'} / 555;
my $x0 = $C_x;
my $y0 = $C_y;
my $x1 = $C_x + $dx;
my $y1 = $C_y + $dy;
$map_canvas->coords ('MK-Speed', $x0, $y0, $x1, $y1);
# Update Line between Home and MK
my ($H_x, $H_y) = &MapGps2XY($MkOsd{'HomePos_Lat'}, $MkOsd{'HomePos_Lon'});
$map_canvas->coords ('MK-Home-Line', $H_x, $H_y, $C_x, $C_y);
 
# Update Distance between Home and MK
my ($Dist, $Bearing) = MapGpsTo($MkOsd{'CurPos_Lat'}, $MkOsd{'CurPos_Lon'},
$MkOsd{'HomePos_Lat'}, $MkOsd{'HomePos_Lon'} );
my $x = ($C_x - $H_x) / 2 + $H_x + 8;
my $y = ($C_y - $H_y) / 2 + $H_y + 8;
$map_canvas->coords ('MK-Home-Dist', $x, $y);
$map_canvas->itemconfigure ('MK-Home-Dist',
'-text' => sprintf ("%4d m", int ($Dist + 0.5) ),
);
 
# Update OSD - Sat dependent values
$map_canvas->itemconfigure ('MK-OSD-Spd-Value', '-text' => sprintf ("%3d km/h", $MkOsd{'GroundSpeed'} * 0.036) );
 
# Alt = average Luftdruck und Sat
my $Alt = int ( ($MkOsd{'Altimeter'} / $Cfg->{'mkcockpit'}->{'AltFactor'} +
$MkOsd{'CurPos_Alt'} - $MkOsd{'HomePos_Alt'} + 0.5) / 2 );
$map_canvas->itemconfigure ('MK-OSD-Alt-Value', '-text' => sprintf ("%d m", $Alt) );
 
if ( $MkOsd{'TargetPos_Stat'} == 1 )
{
# Valid Target
# Update Line between Target and MK
my ($T_x, $T_y) = &MapGps2XY($MkOsd{'TargetPos_Lat'}, $MkOsd{'TargetPos_Lon'});
$map_canvas->coords ('MK-Target-Line', $C_x, $C_y, $T_x, $T_y);
 
# Update Distance between Target and MK
my ($Dist, $Bearing) = MapGpsTo($MkOsd{'CurPos_Lat'}, $MkOsd{'CurPos_Lon'},
$MkOsd{'TargetPos_Lat'}, $MkOsd{'TargetPos_Lon'} );
 
if ( $Dist >= 25 )
{
my $x = ($C_x - $T_x) / 2 + $T_x - 8;
my $y = ($C_y - $T_y) / 2 + $T_y + 8;
$map_canvas->coords ('MK-Target-Dist', $x, $y);
$map_canvas->itemconfigure ('MK-Target-Dist',
'-text' => sprintf ("%4d m", int ($Dist + 0.5) ),
);
}
else
{
# Don't show distance < 25m
$map_canvas->coords ('MK-Target-Dist', 0, -100);
}
 
# show target icon
my $IconHeight = 48;
my $IconWidth = 48;
$map_canvas->coords('Target', $T_x - $IconWidth/2, $T_y - $IconHeight );
}
else
{
# No valid Target, move target line out of sight/canvas
$map_canvas->coords ('MK-Target-Line', 0, -100, 0, -100);
$map_canvas->coords ('MK-Target-Dist', 0, -100);
 
# hide target icon
$map_canvas->coords('Target', 0, -100, );
}
}
else
{
# kein ausreichender Sat-Empfang
$map_canvas->itemconfigure ('MK-OSD-Spd-Value', '-text' => sprintf ("%3d km/h", 0 ) );
}
 
# Update OSD - non Sat dependent values
$map_canvas->itemconfigure ('MK-OSD-Bat-Value', '-text' => sprintf ("%3.1f V", $MkOsd{'UBat'}) );
$map_canvas->itemconfigure ('MK-OSD-Vsi-Value', '-text' => sprintf ("%3d", $MkOsd{'Variometer'}) );
$map_canvas->itemconfigure ('MK-OSD-Tim-Value', '-text' => sprintf ("%02d:%02d", $MkFlyingTime / 60, $MkFlyingTime % 60) );
$map_canvas->itemconfigure ('MK-OSD-Sat-Value', '-text' => $MkOsd{'SatsInUse'} );
 
# Waypoints abhaengig vom Modus NC/Player
my $WpValue = "--/--";
if ( $MkOsd{'WaypointNumber'} > 0)
{
$WpValue = sprintf ("%d / %d", $MkOsd{'WaypointIndex'} + 1, $MkOsd{'WaypointNumber'});
}
if ($PlayerMode ne "Stop" and $WptKmlMode eq "WPT" )
{
$WpValue = sprintf ("%d / %d", $WpPlayerIndex +1, scalar @Waypoints);
}
if ($PlayerMode ne "Stop" and $WptKmlMode eq "KML" )
{
my $KmlTimeBase = $Cfg->{'waypoint'}->{'KmlTimeBase'} || 1.0;
my $CurrTime = int ($KmlPlayerIndex * $KmlTimeBase + 0.5);
my $TotTime = int (scalar @KmlTargets * $KmlTimeBase + 0.5);
$WpValue = sprintf ("%02d:%02d / %02d:%02d", $CurrTime / 60, $CurrTime % 60, $TotTime / 60, $TotTime % 60);
}
$map_canvas->itemconfigure ('MK-OSD-Wp-Value', '-text' => "$WpValue");
 
# blink battery warning
$map_canvas->itemconfigure ('MK-OSD-Bat-Value', '-fill' => $Cfg->{'mkcockpit'}->{'ColorOsd'});
if ( $MkOsd{'UBat'} < $Cfg->{'mkcockpit'}->{'UBatWarning'} )
{
&MkMessage ($Translate{'MsgBatWarning'}, "Timer-MapOverlay");
 
if ( time %2 )
{
$map_canvas->itemconfigure ('MK-OSD-Bat-Value', '-fill' => 'red');
}
}
 
 
my $Mode = "";
my $Extension = "";
if ($MkOsd{'NCFlags'} & 0x04) { $Mode = "WPT"};
if ($PlayerMode eq "Play") { $Mode = "Play"};
if ($PlayerMode eq "Pause") { $Mode = "Paus" };
if ($PlayerMode eq "Home") { $Mode = "Home" };
if ($MkOsd{'NCFlags'} & 0x01) { $Mode = "Free"};
if ($MkOsd{'NCFlags'} & 0x02) { $Mode = "PH"};
if ($Mode eq "Play") { $Extension = $WptKmlMode};
$map_canvas->itemconfigure ('MK-OSD-Mode-Value', '-text' => "$Mode $Extension" );
 
 
# Farbe MK-Zeiger abhängig vom GPS Empfang
my $MkCol= $Cfg->{'mkcockpit'}->{'ColorMkSatNo'};
if ( $SatsInUse >= 1 ) { $MkCol = $Cfg->{'mkcockpit'}->{'ColorMkSatLow'} ; }
if ( $SatsInUse >= 6 ) { $MkCol = $Cfg->{'mkcockpit'}->{'ColorMkSatGood'}; }
$map_canvas->itemconfigure ('MK-Arrow', '-fill' => $MkCol);
 
# Show/Hide SatFix Icon
if ($SatsInUse >= 6 )
{
$map_canvas->coords('Satellite', $MapSizeX-290, 10, );
}
else
{
# move icon out of sight
$map_canvas->coords('Satellite', 0, -100, );
}
 
if ( $SatsInUse == 0 )
{
&MkMessage ($Translate{'MsgNoSatReception'}, "Timer-MapOverlay");
}
elsif ( $SatsInUse > 0 and $SatsInUse < 6 )
{
&MkMessage ($Translate{'MsgWeakSatReception'}, "Timer-MapOverlay");
}
 
# Variometer Pointer
my $dy = -$MkOsd{'Variometer'} * 10;
$map_canvas->coords('Map-Variometer-Pointer', 5, $MapSizeY/2+$dy, 20, $MapSizeY/2+10+$dy, 20, $MapSizeY/2-10+$dy);
 
#
# System checks
#
 
# RC range check
my $RcQuality = $MkOsd{'RC_Quality'};
if ( $RcQuality < 100 )
{
&MkMessage ($Translate{'MsgRcError'}, "Timer-MapOverlay");
}
elsif ( $RcQuality < 150 )
{
&MkMessage ($Translate{'MsgRcWarning'}, "Timer-MapOverlay");
}
 
if (($MkOsd{'MKFlags'} & 0x01) == 0) { &MkMessage ($Translate{'MsgMototOff'}, "Timer-MapOverlay"); }
if (($MkOsd{'MKFlags'} & 0x02) == 0) { &MkMessage ($Translate{'MsgNotFlying'}, "Timer-MapOverlay"); }
if ($MkOsd{'MKFlags'} & 0x04 ) { &MkMessage ($Translate{'MsgCalibrate'}, "Timer-MapOverlay"); }
if ($MkOsd{'MKFlags'} & 0x08 ) { &MkMessage ($Translate{'MsgStart'}, "Timer-MapOverlay") }
if ($MkOsd{'MKFlags'} & 0x10 ) { &MkMessage ($Translate{'MsgEmergencyLanding'}, "Timer-MapOverlay"); }
if ($MkOsd{'NCFlags'} & 0x08) { &MkMessage ($Translate{'MsgRangeLimit'}, "Timer-MapOverlay"); }
 
#
# Show Balloon, when aproaching Target
#
 
$map_canvas->delete('Target-Balloon'); # delete old Balloon
 
my ($T_x, $T_y) = &MapGps2XY($MkOsd{'TargetPos_Lat'}, $MkOsd{'TargetPos_Lon'});
 
if ( $Mode ne "Free" and $MkOsd{'TargetPos_Stat'} == 1 and $MkOsd{'TargetPosDev_Dist'} /10 < 25 )
{
 
my $BalloonLines = 1;
$ColorBalloon = "blue";
 
# Holdtime Wp-Player Mode
if ( $WpPlayerHoldtime >= 0 )
{
# Holdtime
$ColorBalloon = 'red';
my $HoldTime = sprintf ("%5s %3d s", "HLD:", int ($WpPlayerHoldtime / 2 + 0.5) );
$map_canvas->createText ( $T_x + 25, $T_y - 40,
'-tags' => ['Target-Balloon', 'Target-BalloonText'],
'-text' => $HoldTime,
'-font' => '-*-Arial-Bold-R-Normal--*-200-*',
'-fill' => $ColorBalloon,
'-anchor' => 'w',
);
$BalloonLines ++;
}
 
 
# Holdtime WPT-Mode
if ( $MkOsd{'NCFlags'} & 0x20 and $Mode eq "WPT" )
{
# Holdtime
$ColorBalloon = 'red';
my $HoldTime = sprintf ("%5s %3d s", "HLD:", int ($MkOsd{'TargetHoldTime'} + 0.5) );
$map_canvas->createText ( $T_x + 25, $T_y - 40,
'-tags' => ['Target-Balloon', 'Target-BalloonText'],
'-text' => $HoldTime,
'-font' => '-*-Arial-Bold-R-Normal--*-200-*',
'-fill' => $ColorBalloon,
'-anchor' => 'w',
);
$BalloonLines ++;
}
 
 
# Tolerance Radius Player Mode
if ( $MkOsd{'NCFlags'} & 0x04 and $Mode eq "Play" and $WptKmlMode eq "WPT" )
{
my $WpTolerance = sprintf ("%5s %3d m", "TOL:", $Waypoints[$WpPlayerIndex]{'ToleranceRadius'});
$map_canvas->createText ( $T_x + 25, $T_y - 60,
'-tags' => ['Target-Balloon', 'Target-BalloonText'],
'-text' => $WpTolerance,
'-font' => '-*-Arial-Bold-R-Normal--*-200-*',
'-fill' => $ColorBalloon,
'-anchor' => 'w',
);
$BalloonLines ++;
}
 
 
# Tolerance WPT-Mode
if ( $MkOsd{'NCFlags'} & 0x04 and $Mode eq "WPT" )
{
my $WpTolerance = sprintf ("%5s %3d m", "TOL:", $Waypoints[$MkOsd{'WaypointIndex'}]{'ToleranceRadius'} );
$map_canvas->createText ( $T_x + 25, $T_y - 60,
'-tags' => ['Target-Balloon', 'Target-BalloonText'],
'-text' => $WpTolerance,
'-font' => '-*-Arial-Bold-R-Normal--*-200-*',
'-fill' => $ColorBalloon,
'-anchor' => 'w',
);
$BalloonLines ++;
}
 
 
# Distance to Target
my $Dist = int ($MkOsd{'TargetPosDev_Dist'} /10 + 0.5);
$map_canvas->createText ( $T_x + 25, $T_y - 80,
'-tags' => ['Target-Balloon', 'Target-BalloonText'],
'-text' => sprintf ("%5s %3d m", "DST:", $Dist) ,
'-font' => '-*-Arial-Bold-R-Normal--*-200-*',
'-fill' => $ColorBalloon,
'-anchor' => 'w',
);
 
if ( $BalloonLines >= 1 )
{
# draw Balloon
my @TargetBalloon = ( $T_x , $T_y,
$T_x + 30, $T_y - (3 - $BalloonLines) * 20 -27,
$T_x + 150, $T_y - (3 - $BalloonLines) * 20 -27 ,
$T_x + 150, $T_y - 93,
$T_x + 20, $T_y - 93,
$T_x + 20, $T_y - (3 - $BalloonLines) * 20 -27,
$T_x, $T_y,
);
 
$map_canvas->createPolygon( @TargetBalloon,
'-tags' => ['Target-Balloon', 'Target-BalloonBubble'],
'-fill' => 'lightgray',
'-outline' => 'yellow',
'-width' => 1,
);
}
 
 
$map_canvas->lower('Target-Balloon', 'MK-Home-Line');
$map_canvas->lower('Target-BalloonBubble', 'Target-BalloonText');
}
 
}
else
{
# keine aktuellen OSD Daten vom MK verfügbar
&MkMessage ($Translate{'MsgNoData'}, "Timer-MapOverlay");
}
 
# Show System Messages
&MkMessageShow();
 
});
 
#
# Timer: 0.1s - Tracking Anzeige aktualisieren
#
if ( $Cfg->{'track'}->{'Active'} =~ /y/i )
{
$frame_map_top->repeat (100, sub
{
# Clear old messages from this timer
&MkMessageInit ("Timer-Tracking");
 
lock (%MkOsd); # until end of block
 
# Aktuell gültige Karte
my %Map = %{$Maps{'Current'}};
 
# Zeiger neu zeichnen
my $ServoPan = @ServoPos[$MkTrack{'ServoPan'}];
if ( $ServoPan ne "" )
{
my $x0 = $TrackSizeX/2;
my $y0 = $TrackSizeY - 0 - $TrackOffY;
my $x1 = $TrackSizeX/2 - ($TrackPtrLen-22) * cos( deg2rad $ServoPan);
my $y1 = $TrackSizeY - ($TrackPtrLen-22) * sin (deg2rad $ServoPan) - $TrackOffY;
$track_canvas->coords ('Track-Ptr', $x0, $y0, $x1, $y1);
}
 
# Farbe Zeiger abhängig vom GPS Empfang
my $SatsInUse = $MkOsd{'SatsInUse'};
my $TrackPtrCol= 'red';
if ( $SatsInUse >= 1 ) { $TrackPtrCol = 'orange'; }
if ( $SatsInUse >= 6 ) { $TrackPtrCol = 'green'; }
$track_canvas->itemconfigure ('Track-Ptr', '-fill' => $TrackPtrCol);
});
}
 
 
#
# Timer: 0.5s - Waypoint Player
#
$frame_map_top->repeat (500, sub
{
# Clear old messages from this timer
&MkMessageInit ("Timer-Player");
 
lock (%MkOsd); # until end of block
 
if ($MkOsd{'NCFlags'} & 0x04)
{
# NC is in WPT Mode
 
if ( $PlayerMode eq "Pause" )
{
if ( $PlayerPause_Lat ne "" and $PlayerPause_Lon ne "" )
{
# Gespeicherte Pausen-Pos senden
&MkFlyTo ( '-lat' => $PlayerPause_Lat,
'-lon' => $PlayerPause_Lon,
'-holdtime' => "60",
'-mode' => "Target",
);
}
}
 
if ( $PlayerMode eq "Home" )
{
if ( $MkOsd{'HomePos_Stat'} == 1 )
{
&MkFlyTo ( '-lat' => $MkOsd{'HomePos_Lat'},
'-lon' => $MkOsd{'HomePos_Lon'},
'-holdtime' => "60",
'-mode' => "Target",
);
}
}
 
 
if ( $WptKmlMode ne 'WPT' )
{
# not in Wp mode
return;
}
 
 
my $WpCnt = scalar @Waypoints;
if ( $PlayerMode eq "Play" and $WpCnt > 0 and $WpPlayerIndex < $WpCnt )
{
# Target WP-Pos senden
my $Wp_Lon = $Waypoints[$WpPlayerIndex]{'Pos_Lon'};
my $Wp_Lat = $Waypoints[$WpPlayerIndex]{'Pos_Lat'};
if ( $Wp_Lat ne "" and $Wp_Lon ne "" )
{
&MkFlyTo ( '-lat' => $Wp_Lat,
'-lon' => $Wp_Lon,
'-holdtime' => "60",
'-mode' => "Target",
);
}
}
if ( $PlayerMode eq "Play" )
{
# Ziel erreicht?
if ( &WpCheckTargetReached() )
{
&WpTargetNext();
}
}
}
 
# WP Player Holdtime count down
if ( $WpPlayerHoldtime > 0 )
{
$WpPlayerHoldtime --;
}
});
 
 
#
# Timer: variabel - KML Player
#
my $KmlTimeBase = $Cfg->{'waypoint'}->{'KmlTimeBase'} || 1.0;
$KmlTimeBase *= 1000;
 
$frame_map_top->repeat ($KmlTimeBase, sub
{
 
# Clear old messages from this timer
&MkMessageInit ("Timer-KMLPlayer");
 
if ( $WptKmlMode ne 'KML' )
{
# not in KML mode
return;
}
 
lock (%MkOsd); # until end of block
 
if ($MkOsd{'NCFlags'} & 0x04)
{
# NC is in WPT Mode
 
# Pause, Home is handled in WPT-Timer
 
my $KmlCnt = scalar @KmlTargets;
if ( $PlayerMode eq "Play" and $KmlCnt > 0 and $KmlPlayerIndex < $KmlCnt )
{
 
my $Lat = $KmlTargets[$KmlPlayerIndex]->{'Lat'};
my $Lon = $KmlTargets[$KmlPlayerIndex]->{'Lon'};
my $Alt = $KmlTargets[$KmlPlayerIndex]->{'Alt'};
 
&MkFlyTo ( '-lat' => $Lat,
'-lon' => $Lon,
'-alt' => $Alt,
'-holdtime' => "60",
'-mode' => "Target",
);
 
# next Target
$KmlPlayerIndex ++;
if ( $KmlPlayerIndex >= scalar @KmlTargets )
{
$KmlPlayerIndex = 0;
}
}
}
});
 
 
#
# Timer: 1s
#
$frame_map_top->repeat (1000, sub
{
# Clear old messages from this timer
&MkMessageInit ("Timer-Misc-1s");
 
lock (%MkOsd); # until end of block
 
# Aktuell gültige Karte
my %Map = %{$Maps{'Current'}};
 
if ( $MkOsd{'_Timestamp'} >= time -2 )
{
 
# Heartbeat MK Datenübertragung
if ( time %2 )
{
$map_canvas->itemconfigure('Heartbeat', '-image' => 'HeartbeatLarge', );
}
else
{
$map_canvas->itemconfigure('Heartbeat', '-image' => 'HeartbeatSmall', );
}
 
# Flugzeit aktualisieren
# Flugzeit selber mitzählen, da $MkOsd{'FlyingTime'} immer 0 (0.14b)
if ( $MkOsd{'MKFlags'} & 0x02 )
{
$MkFlyingTime += 1;
}
 
# Footprint
if ( $Cfg->{'mkcockpit'}->{'FootprintLength'} > 0 )
{
if ( $MkOsd{'SatsInUse'} > 0 and $MkOsd{'CurPos_Stat'} == 1 )
{
# neuen Footprint hinten anhaengen
my ($x, $y) = &MapGps2XY($MkOsd{'CurPos_Lat'}, $MkOsd{'CurPos_Lon'});
push @Footprint, $x, $y;
}
 
while ( $#Footprint / 2 > $Cfg->{'mkcockpit'}->{'FootprintLength'} )
{
# alte Footprints entfernen
splice @Footprint, 0, 2;
}
 
&FootprintRedraw();
}
 
# tracking antenne
if ( $MkOsd{'MKFlags'} & 0x01 and ! $MkTrack{'IsRunning'} and
$Cfg->{'track'}->{'Active'} =~ /y/i )
{
# start track at 1st motor start
$track_thr = threads->create (\&TrackAntennaGps)->detach();
$MkTrack{'IsRunning'} = "Running";
}
}
 
});
 
 
MainLoop(); # should never end
 
 
#-----------------------------------------------------------------
# Subroutines
#-----------------------------------------------------------------
 
# Get Wp Index from Canvas Id
sub WpGetIndexFromId()
{
my ($id) = @_;
 
my @Tags = $map_canvas->gettags($id);
my $WpTag = $Tags[1];
 
for $i (0 .. $#Waypoints)
{
my $Wp = $Waypoints[$i];
if ( $Wp->{'Tag'} eq $WpTag )
{
# got it
return $i;
}
}
return -1;
}
 
# Resend all Waypoints to MK
sub WpSendAll()
{
# OSD/Debug Abfragefrequenz verringern, sonst kommen nicht alle Wp im MK an
# Sicherheitshalber doppelt senden
$MkSendWp = 1; # verhindert ueberschreiben im Timer
$MkSendQueue->enqueue( "o", "$AddrNC", pack ("C", 1000) ); # Frequenz OSD Datensatz, * 10ms
$MkSendQueue->enqueue( "d", "$AddrNC", pack ("C", 1000) ); # Frequenz MK Debug Datensatz, * 10ms
usleep (200000);
$MkSendQueue->enqueue( "o", "$AddrNC", pack ("C", 1000) ); # Frequenz OSD Datensatz, * 10ms
$MkSendQueue->enqueue( "d", "$AddrNC", pack ("C", 1000) ); # Frequenz MK Debug Datensatz, * 10ms
usleep (200000);
 
# Alte WP-Liste im MK löschen
my $Wp = $Waypoints[0];
&MkFlyTo ( '-lat' => $Wp->{'Pos_Lat'},
'-lon' => Wp->{'Pos_Lon'},
'-mode' => "Waypoint Delete"
);
for $i (0 .. $#Waypoints)
{
my $Wp = $Waypoints[$i];
&MkFlyTo ( '-lat' => $Wp->{'Pos_Lat'},
'-lon' => $Wp->{'Pos_Lon'},
'-alt' => $Wp->{'Pos_Alt'},
'-heading' => $Wp->{'Heading'},
'-toleranceradius' => $Wp->{'ToleranceRadius'},
'-holdtime' => $Wp->{'Holdtime'},
'-eventflag' => $Wp->{'Event_Flag'},
'-mode' => "Waypoint"
);
usleep (150000) # NC Zeit zum Verarbeiten geben
}
 
$MkSendWp = 0; # normale OSD/Debug Abfragefrequenz wird automatisch im 5s Timer wieder eingestellt
 
# gray connectors: Wp are sent to MK
$map_canvas->itemconfigure('Waypoint-Connector',
'-fill' => $Cfg->{'mkcockpit'}->{'ColorWpConnector'},
);
 
# MK ist nun synchron mit @Waypoints
$WaypointsModified = 0;
}
 
# Redraw Waypoint Icons
sub WpRedrawIcons()
{
if ( $WptKmlMode =~ /WPT/i )
{
 
# delete old icons and Wp-Number from canvas
$map_canvas->delete('Waypoint');
$map_canvas->delete('WaypointNumber');
 
# create new icons
for $i (0 .. $#Waypoints)
{
my $Wp = $Waypoints[$i];
my $x = $Wp->{'MapX'};
my $y = $Wp->{'MapY'};
my $Tag = $Wp->{'Tag'};
 
# Waypoint Icon
my $IconHeight = 48;
my $IconWidth = 48;
$map_canvas->createImage($x-$IconWidth/2, $y-$IconHeight,
'-tags' => ['Waypoint', $Tag],
'-anchor' => 'nw',
'-image' => 'Waypoint-Photo',
);
# Waypoint Number
my $WpNumber = $i + 1;
$map_canvas->createText ( $x+3, $y-$IconHeight/2+12,
'-tags' => ['WaypointNumber', $Tag],
'-text' => $WpNumber,
'-font' => '-*-Arial-Bold-R-Normal--*-100-*',
'-fill' => $Cfg->{'mkcockpit'}->{'ColorWpNumber'},
'-anchor' => 'w',
);
 
}
$map_canvas->lower('Waypoint', 'Fox'); # waypoint below Fox
$map_canvas->lower('WaypointNumber', 'Waypoint'); # waypoint-number below waypoint
}
}
 
# Redraw Waypoint connectors
sub WpRedrawLines()
{
if ( $WptKmlMode =~ /WPT/i )
{
# delete old connectors from canvas
$map_canvas->delete('Waypoint-Connector');
 
my $Color = $Cfg->{'mkcockpit'}->{'ColorWpConnector'};
if ( $WaypointsModified )
{
$Color = $Cfg->{'mkcockpit'}->{'ColorWpResend'};
}
 
my $Wp = $Waypoints[0];
my $x_last = $Wp->{'MapX'};
my $y_last = $Wp->{'MapY'};
for $i (1 .. $#Waypoints)
{
my $Wp = $Waypoints[$i];
my $x = $Wp->{'MapX'};
my $y = $Wp->{'MapY'};
 
$map_canvas->createLine ( $x_last, $y_last, $x, $y,
'-tags' => 'Waypoint-Connector',
'-arrow' => 'last',
'-arrowshape' => [10, 10, 3 ],
'-fill' => $Color,
'-width' => 1,
);
$x_last = $x;
$y_last = $y;
}
$map_canvas->lower('Waypoint-Connector', 'Waypoint'); # connector below waypoint
}
}
 
 
# Hide Waypoints on Canvas
sub WpHide()
{
$map_canvas->delete('Waypoint');
$map_canvas->delete('WaypointNumber');
$map_canvas->delete('Waypoint-Connector');
}
 
 
# Redraw Footprint
sub FootprintRedraw()
{
# delete old Footprint from canvas
$map_canvas->delete('Footprint');
 
if ( scalar @Footprint >= 4 ) # at least 2 Koordinaten-Paare
{
$map_canvas->createLine ( @Footprint,
'-tags' => 'Footprint',
'-fill' => $Cfg->{'mkcockpit'}->{'ColorFootprint'},
'-width' => 1,
);
}
$map_canvas->lower('Footprint', 'Fox');
}
 
 
# Redraw KML track
sub KmlRedraw()
{
 
# delete old Track from canvas
$map_canvas->delete('KML-Track');
 
my @Track;
 
foreach $Target ( @KmlTargets )
{
my $Lat = $Target->{'Lat'};
my $Lon = $Target->{'Lon'};
my $Alt = $Target->{'Alt'};
my ($x, $y) = &MapGps2XY($Lat, $Lon);
push @Track, $x, $y;
}
 
if ( scalar @Track >= 4 ) # at least 2 Koordinaten-Paare
{
$map_canvas->createLine ( @Track,
'-tags' => 'KML-Track',
'-fill' => $Cfg->{'mkcockpit'}->{'ColorKmlTrack'},
'-width' => 1,
);
 
$map_canvas->lower('KML-Track', 'Target'); # Track below Target
}
}
 
 
# Hide Kml-Track on Canvas
sub KmlHide()
{
$map_canvas->delete('KML-Track');
}
 
 
# Switch player between WPT and KML
sub WptKmlSwitch()
{
my ($Mode) = @_;
 
# Wpt/Kml-Player-Icon loeschen und neu anzeigen
$map_canvas->delete('Wp-WptKml');
 
if ( $Mode =~ /KML/i )
{
$WptKmlMode = 'KML';
 
# set player button to KML
$map_canvas->createImage($MapSizeX/2-200, $MapSizeY-48,
'-tags' => 'Wp-WptKml',
'-anchor' => 'nw',
'-image' => 'WpKml-Foto',
);
 
# delete Waypoints from canvas
&WpHide();
 
# show KML Track
&KmlRedraw();
}
 
if ( $Mode =~ /WPT/i )
{
$WptKmlMode = 'WPT';
 
# set player button to WPT
$map_canvas->createImage($MapSizeX/2-200, $MapSizeY-48,
'-tags' => 'Wp-WptKml',
'-anchor' => 'nw',
'-image' => 'WpWpt-Foto',
);
 
# delete Kml-Track from canvas
&KmlHide();
 
# Show waypoints
&WpRedrawIcons()
&WpRedrawLines()
}
}
 
 
# Display or Modify Hash
sub DisplayHash()
{
my ($hrefData, $Titel, $Mode) = @_;
 
# $Mode: Display, Edit, Waypoint, Refresh
 
my %Id;
my $Label;
my $Value;
 
# Neues Fenster aufmachen
my $popup = $main->Toplevel();
$popup->title($Titel);
# Buttons
my $popup_button = $popup->Frame() -> pack('-side' => 'bottom',
'-expand' => 'y',
'-anchor' => 's',
'-padx' => 5,
'-pady' => 5,
);
$popup_button->Button('-text' => 'Schließen',
'-command' => sub
{
if ( $Mode =~ /edit/i and $Mode =~ /waypoint/i )
{
$WaypointsModified = 1;
&WpRedrawLines();
&WpRedrawIcons();
}
 
$popup->destroy()
})->pack;
 
# Frame mit den Labels
my $popup_label = $popup->Frame() -> pack('-side' => 'left',
'-expand' => 'y',
'-anchor' => 'w',
'-padx' => 10,
'-pady' => 10,
);
# Labels anzeigen
foreach $Label ( sort keys %{$hrefData})
{
if ( $Translate{$Label} ne "" )
{
$Label = $Translate{$Label};
}
$popup_label->Label ('-text' => $Label,
'-width' => 25,
'-anchor' => 'w',
) -> pack();
}
# Frame mit den Daten
my $popup_values = $popup->Frame() -> pack('-side' => 'left',
'-expand' => 'y',
'-anchor' => 'w',
'-padx' => 10,
'-pady' => 10,
);
# Daten anzeigen
foreach $Value ( sort keys %{$hrefData})
{
if ( $Mode =~ /display/i )
{
# Display
$Id{$Value} = $popup_values->Label ('-text' => ${$hrefData}{$Value},
'-width' => 20,
'-anchor' => 'e',
'-relief' => 'sunken',
) -> pack();
}
if ( $Mode =~ /edit/i )
{
# Edit
$Id{$Value} = $popup_values->Entry ('-textvariable' => \${$hrefData}{$Value},
'-exportselection' => '1',
'-width' => 20,
'-relief' => 'sunken',
) -> pack();
if ( $Mode =~ /waypoint/i )
{
# einige Waypoint-Felder nicht aenderbar einstellen
if ( "MapX MapY Pos_Lat Pos_Lon Tag" =~ /$Value/i )
{
$Id{$Value}->configure('-state' => 'disabled', );
}
}
}
}
 
if ( $Mode =~ /refresh/i )
{
# Timer: 0.1s
$popup_values->repeat (100, sub
{
# Datenfelder alle 100ms aktualisieren
 
my $BgColor = 'white';
if ( $Mode =~ /heartbeat/i )
{
$BgColor = 'red';
if ( $MkOsd{'_Timestamp'} >= time-2 )
{
# gültige daten vom MK
$BgColor = 'white';
}
}
foreach $Value ( sort keys %{$hrefData} )
{
# Eingebbare Waypoint-Felder nicht aktualisieren
if ( ! ($Mode =~ /waypoint/i and
"Event_Flag Heading ToleranceRadius HoldTime Pos_Alt" =~ /$Value/i) )
{
$Id{$Value}->configure('-text' => ${$hrefData}{$Value},
'-background' => "$BgColor",
);
}
}
});
}
 
return 0;
}
 
 
 
# Konfigurationsdatei mkcockpit.xml im Popup-Fenster editieren
sub Configure()
{
 
# Copy Cfg-Hash for editing
my $CfgEdit = {%{$Cfg}};
foreach $key (keys %{$Cfg})
{
if ( ref $Cfg->{$key} )
{
$CfgEdit->{$key} = {%{$Cfg->{$key}}};
}
}
 
# Neues Fenster aufmachen
my $popup = $main->Toplevel();
$popup->title("Einstellungen - $XmlConfigFile");
 
# jede Sektion in einem Tab anzeigen
my $book = $popup->NoteBook()->pack( -fill=>'both', -expand=>1 );
foreach $key (sort keys %{$CfgEdit})
{
if ( ! ref $CfgEdit->{$key} )
{
next;
}
 
my $TabLabel = "$key";
if ( $Translate{$key} ne "" )
{
$TabLabel = $Translate{$key};
}
 
my $Tab = $book->add( "$key", -label=>"$TabLabel", );
 
# Frame fuer Buttons
my $book_button = $Tab->Frame() -> pack('-side' => 'bottom',
'-expand' => 'y',
'-anchor' => 's',
'-padx' => 5,
'-pady' => 5,
);
 
$book_button->Button('-text' => 'OK',
'-width' => '10',
'-command' => sub
{
# Copy back CfgEdit-Hash
$Cfg = {%{$CfgEdit}};
foreach $key (keys %{$CfgEdit})
{
if ( ref $CfgEdit->{$key} )
{
$Cfg->{$key} = {%{$CfgEdit->{$key}}};
}
}
 
# set new timestamp
my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time);
my $TimeStamp = sprintf ("%04d%02d%02d-%02d%02d%02d", $year+1900, $mon+1, $mday, $hour, $min, $sec);
$Cfg->{'CreationDate'} = $TimeStamp;
 
# Cfg in mkcockpit.xml speichern
&XMLout ($Cfg,
'OutputFile' => $XmlConfigFile,
'AttrIndent' => '1',
'RootName' => 'mkcockpit-Config',
);
 
$popup->destroy();
} )->pack ('-side' => 'left',
'-expand' => 'y',
'-anchor' => 's',
'-padx' => 5,
'-pady' => 5,
);
$book_button->Button('-text' => $Translate{'Abort'},
'-width' => '10',
'-command' => sub { $popup->destroy() },
)->pack ('-side' => 'left',
'-expand' => 'y',
'-anchor' => 's',
'-padx' => 5,
'-pady' => 5,
);
$book_button->Label ('-text' => $Translate{'RestartRequired'},
'-anchor' => 'w',
'-foreground' => 'red',
) ->pack ('-side' => 'left',
'-expand' => 'y',
'-anchor' => 's',
'-padx' => 10,
'-pady' => 5,
);
 
# Frame mit den Labels
my $popup_label = $Tab->Frame() -> pack('-side' => 'left',
'-expand' => 'y',
'-anchor' => 'w',
'-padx' => 10,
'-pady' => 10,
);
# Labels anzeigen
foreach $Label ( sort keys %{$CfgEdit->{$key}})
{
if ( $Translate{$Label} ne "" )
{
$Label = $Translate{$Label};
}
$popup_label->Label ('-text' => $Label,
'-width' => 30,
'-anchor' => 'w',
) -> pack();
}
# Frame mit den Daten
my $popup_values = $Tab->Frame() -> pack('-side' => 'left',
'-expand' => 'y',
'-anchor' => 'w',
'-padx' => 10,
'-pady' => 10,
);
# Eingabefelder mit Daten anzeigen
foreach $Value ( sort keys %{$CfgEdit->{$key}})
{
$popup_values->Entry ('-textvariable' => \$CfgEdit->{$key}->{$Value},
'-exportselection' => '1',
'-width' => 30,
'-relief' => 'sunken',
) -> pack();
}
}
}
 
 
# Waypoint Player: Goto next Waypoint
sub WpTargetNext()
{
$WpPlayerIndex ++;
if ( $WpPlayerIndex > $#Waypoints )
{
# Restart with 1st Wp
$WpPlayerIndex = 0;
}
 
$WpPlayerHoldtime = -1;
}
 
# Waypoint Player: Goto previous Waypoint
sub WpTargetPrev()
{
$WpPlayerIndex --;
if ( $WpPlayerIndex < 0 )
{
# Restart with last Wp
$WpPlayerIndex = $#Waypoints;
}
 
$WpPlayerHoldtime = -1;
}
 
# Waypoint Player: Goto first Waypoint
sub WpTargetFirst()
{
$WpPlayerIndex = 0;
$WpPlayerHoldtime = -1;
}
 
# Waypoint Player: Goto last Waypoint
sub WpTargetLast()
{
$WpPlayerIndex = $#Waypoints;
$WpPlayerHoldtime = -1;
}
 
 
# Waypoint Player: Waypoint Target reached?
sub WpCheckTargetReached()
{
if ( $WpPlayerHoldtime == -1 )
{
lock (%MkOsd); # until end of block
 
if ( $MkOsd{'_Timestamp'} >= time-2 and # Gueltige OSD Daten
$MkOsd{'NCFlags'} & 0x04 and # WPT-Mode
$MkOsd{'SatsInUse'} >= 6 and $MkOsd{'CurPos_Stat'} == 1 and $MkOsd{'HomePos_Stat'} == 1)
{
# Gueltige SAT Daten
my $WpTarget_Lat = $Waypoints[$WpPlayerIndex]{'Pos_Lat'};
my $WpTarget_Lon = $Waypoints[$WpPlayerIndex]{'Pos_Lon'};
my $WpTolerance = $Waypoints[$WpPlayerIndex]{'ToleranceRadius'};
my $WpHoldtime = $Waypoints[$WpPlayerIndex]{'Holdtime'};
 
# Operation Radius pruefen
my ($HomeDist, $HomeBearing) = &MapGpsTo($MkOsd{'HomePos_Lat'}, $MkOsd{'HomePos_Lon'}, $WpTarget_Lat, $WpTarget_Lon );
if ( $HomeDist > $MkOsd{'OperatingRadius'} )
{
# Target entsprechend Operation Radius neu berechnen
$HomeDist = $MkOsd{'OperatingRadius'};
($WpTarget_Lat, $WpTarget_Lon) = &MapGpsAt($MkOsd{'HomePos_Lat'}, $MkOsd{'HomePos_Lon'}, $HomeDist, $HomeBearing);
}
 
# Abstand zum Ziel pruefen
my ($Dist, $Bearing) = &MapGpsTo($MkOsd{'CurPos_Lat'}, $MkOsd{'CurPos_Lon'}, $WpTarget_Lat, $WpTarget_Lon );
$Dist = int ($Dist + 0.5);
if ( $Dist <= $WpTolerance )
{
# Target reached - count down Holdtime
$WpPlayerHoldtime = 2 * $WpHoldtime; # 0..2n - decrement im 0.5s timer
}
}
}
 
if ( $WpPlayerHoldtime == 0 ) # wird im 0.5s timer runtergezaehlt
{
# Target reached - Holdtime is over
$WpPlayerHoldtime = -1;
return 1;
}
 
# Target NOT reached
return 0;
}
 
 
# KML Player: 10s forward
sub KmlTargetNext()
{
$KmlPlayerIndex += int (10 / $Cfg->{waypoint}->{'KmlTimeBase'} + 0.5);
if ( $KmlPlayerIndex > $#KmlTargets )
{
# Next loop
$KmlPlayerIndex -= $#KmlTargets;
}
}
 
# KML Player: 10s backward
sub KmlTargetPrev()
{
$KmlPlayerIndex -= int (10 / $Cfg->{waypoint}->{'KmlTimeBase'} + 0.5);
if ( $KmlPlayerIndex < 0 )
{
# Next loop
$KmlPlayerIndex += $#KmlTargets;
}
}
 
# KML Player: Goto first Target
sub KmlTargetFirst()
{
$KmlPlayerIndex = 0;
}
 
# KML Player: Goto last Target
sub KmlTargetLast()
{
$KmlPlayerIndex = $#KmlTargets;
}
 
 
#
# Call Back
#
 
# Player CallBack: Play/Pause button
sub CbPlayerPlayPause()
{
# Play/Pause-Icon loeschen und neu anzeigen
$map_canvas->delete('Wp-PlayPause');
 
if ( ($PlayerMode eq "Pause") or ($PlayerMode eq "Stop") or ($PlayerMode eq "Home") )
{
$PlayerMode = 'Play';
$WpPlayerHoldtime = -1;
$map_canvas->createImage($MapSizeX/2+100, $MapSizeY-48,
'-tags' => 'Wp-PlayPause',
'-anchor' => 'nw',
'-image' => 'WpPause-Foto',
);
}
else
{
$PlayerMode = 'Pause';
$WpPlayerHoldtime = -1;
$map_canvas->createImage($MapSizeX/2+100, $MapSizeY-48,
'-tags' => 'Wp-PlayPause',
'-anchor' => 'nw',
'-image' => 'WpPlay-Foto',
);
 
# momentane Position merken und im Player-Timer dauernd senden
$PlayerPause_Lon = "";
$PlayerPause_Lat = "";
 
lock (%MkOsd); # until end of block
if ( $MkOsd{'_Timestamp'} >= time-2 )
{
# Gueltige OSD Daten
if ( $MkOsd{'SatsInUse'} >= 6 and $MkOsd{'CurPos_Stat'} == 1 )
{
$PlayerPause_Lon = $MkOsd{'CurPos_Lon'};
$PlayerPause_Lat = $MkOsd{'CurPos_Lat'};
}
}
}
}
 
 
# Player CallBack: Next
sub CbPlayerNext()
{
if ( $PlayerMode ne 'Stop' )
{
 
if ( $WptKmlMode eq 'WPT' )
{
&WpTargetNext();
}
 
if ( $WptKmlMode eq 'KML' )
{
&KmlTargetNext();
}
 
}
}
 
 
# Player CallBack: Prev
sub CbPlayerPrev()
{
if ( $PlayerMode ne 'Stop' )
{
 
if ( $WptKmlMode eq 'WPT' )
{
&WpTargetPrev();
}
 
if ( $WptKmlMode eq 'KML' )
{
&KmlTargetPrev();
}
 
}
}
 
 
# Player CallBack: First
sub CbPlayerFirst()
{
if ( $PlayerMode ne 'Stop' )
{
 
if ( $WptKmlMode eq 'WPT' )
{
&WpTargetFirst();
}
 
if ( $WptKmlMode eq 'KML' )
{
&KmlTargetFirst();
}
 
}
}
 
# Player CallBack: Last
sub CbPlayerLast()
{
if ( $PlayerMode ne 'Stop' )
{
 
if ( $WptKmlMode eq 'WPT' )
{
&WpTargetLast();
}
 
if ( $WptKmlMode eq 'KML' )
{
&KmlTargetLast();
}
 
}
}
 
 
# Player CallBack: Home
sub CbPlayerHome()
{
if ( $PlayerMode ne 'Stop' )
{
$PlayerMode = 'Home';
&WpTargetFirst();
 
$map_canvas->delete('Wp-PlayPause');
$map_canvas->createImage($MapSizeX/2+100, $MapSizeY-48,
'-tags' => 'Wp-PlayPause',
'-anchor' => 'nw',
'-image' => 'WpPlay-Foto',
);
}
}
 
 
# Player CallBack: Stop
sub CbPlayerStop()
{
if ( $PlayerMode ne 'Stop' )
{
$PlayerMode = 'Stop';
&WpTargetFirst();
 
# set Play/Pause Icon to "Play
$map_canvas->delete('Wp-PlayPause');
$map_canvas->createImage($MapSizeX/2+100, $MapSizeY-48,
'-tags' => 'Wp-PlayPause',
'-anchor' => 'nw',
'-image' => 'WpPlay-Foto',
);
 
 
# WP resend required
$WaypointsModified = 1;
 
# switch player to Wp Mode
&WptKmlSwitch ('WPT');
}
}
 
 
# Player CallBack: Move MK in Pause-Mode
sub CbPlayerMove()
{
my ($Id, $DirX, $DirY) = @_;
 
if ( $PlayerMode eq 'Pause' and
$PlayerPause_Lat ne "" and $PlayerPause_Lon ne "" )
{
my $Dist = $Cfg->{'waypoint'}->{'PauseMoveDist'} || 1; # 1m default
 
my $BearingTop = &MapAngel() - 90.0;
my $BearingKey = rad2deg atan2($DirX, $DirY);
my $Bearing = $BearingTop + $BearingKey;
 
($PlayerPause_Lat, $PlayerPause_Lon) = &MapGpsAt($PlayerPause_Lat, $PlayerPause_Lon, $Dist, $Bearing)
}
}
 
 
# Player CallBack: Toggle WPT/KML button
sub CbPlayerWptKml()
{
 
if ( $WptKmlMode =~ /WPT/i )
{
# switch player to KML Mode
&WptKmlSwitch ('KML');
}
 
elsif ( $WptKmlMode =~ /KML/i )
{
# WP resend required
$WaypointsModified = 1;
 
# switch player to Wp Mode
&WptKmlSwitch ('WPT');
}
 
}
 
#
# System Messages
 
# Init Messages for a Subsystem/timer
sub MkMessageInit ()
{
my ($Id) = @_;
 
$MkMessages{$Id} = [];
}
 
 
# Register message
sub MkMessage ()
{
my ($Message, $Id) = @_;
 
push @{$MkMessages{$Id}}, $Message;
}
 
 
# show registered messages
sub MkMessageShow()
{
my @Messages;
my $MsgLines = 0;
my $MaxMsgLen = 0;
 
# Collect Messages of each category
foreach my $Id (keys %MkMessages)
{
foreach $i ( 0 .. $#{$MkMessages{$Id}} )
{
my $Msg = $MkMessages{$Id}[$i];
push @Messages, $Msg;
 
$MsgLines ++;
my $Len = length $Msg;
if ( $Len > $MaxMsgLen )
{
$MaxMsgLen = $Len;
}
}
}
 
$map_canvas->delete('Message-Balloon'); # delete old Balloon
 
if ( $MsgLines > 0 )
{
# draw Balloon
my @MsgBalloon = ( $MkPos_x , $MkPos_y,
$MkPos_x + 30 , $MkPos_y + 40,
$MkPos_x + 30 + $MaxMsgLen * 11, $MkPos_y + 40,
$MkPos_x + 30 + $MaxMsgLen * 11, $MkPos_y + 44 + $MsgLines * 20,
$MkPos_x + 20, $MkPos_y + 44 + $MsgLines * 20,
$MkPos_x + 20, $MkPos_y + 40,
$MkPos_x, $MkPos_y,
);
 
$map_canvas->createPolygon( @MsgBalloon,
'-tags' => ['Message-Balloon', 'Message-BalloonBubble'],
'-fill' => 'yellow',
'-outline' => 'yellow',
'-width' => 1,
);
# draw Messages
my $MsgLine = 1;
foreach my $Msg (@Messages)
{
$map_canvas->createText ( $MkPos_x + 25, $MkPos_y + 32 + $MsgLine * 20 ,
'-tags' => ['Message-Balloon', 'Message-BalloonText'],
'-text' => $Msg,
'-font' => '-*-Arial-Bold-R-Normal--*-200-*',
'-fill' => 'blue',
'-anchor' => 'w',
);
$MsgLine ++;
}
 
 
$map_canvas->lower('Message-Balloon', 'MK-Arrow');
}
 
}
 
 
1;
__END__
/MissionCockpit/branches/V0.2.3/translate.pl
0,0 → 1,524
#!/usr/bin/perl
#!/usr/bin/perl -d:ptkdb
 
###############################################################################
#
# translate.pl - Translation fuer MK Datensaetze
#
# Copyright (C) 2009 Rainer Walther (rainerwalther-mail@web.de)
#
# Creative Commons Lizenz mit den Zusaetzen (by, nc, sa)
#
# Es ist Ihnen gestattet:
# * das Werk vervielfältigen, verbreiten und öffentlich zugänglich machen
# * Abwandlungen bzw. Bearbeitungen des Inhaltes anfertigen
#
# Zu den folgenden Bedingungen:
# * Namensnennung.
# Sie müssen den Namen des Autors/Rechteinhabers in der von ihm festgelegten Weise nennen.
# * Keine kommerzielle Nutzung.
# Dieses Werk darf nicht für kommerzielle Zwecke verwendet werden.
# * Weitergabe unter gleichen Bedingungen.
# Wenn Sie den lizenzierten Inhalt bearbeiten oder in anderer Weise umgestalten,
# verändern oder als Grundlage für einen anderen Inhalt verwenden,
# dürfen Sie den neu entstandenen Inhalt nur unter Verwendung von Lizenzbedingungen
# weitergeben, die mit denen dieses Lizenzvertrages identisch oder vergleichbar sind.
#
# Im Falle einer Verbreitung müssen Sie anderen die Lizenzbedingungen, unter welche dieses
# Werk fällt, mitteilen. Am Einfachsten ist es, einen Link auf diese Seite einzubinden.
#
# Jede der vorgenannten Bedingungen kann aufgehoben werden, sofern Sie die Einwilligung
# des Rechteinhabers dazu erhalten.
#
# Diese Lizenz lässt die Urheberpersönlichkeitsrechte unberührt.
#
# Weitere Details zur Lizenzbestimmung gibt es hier:
# Kurzform: http://creativecommons.org/licenses/by-nc-sa/3.0/de/
# Komplett: http://creativecommons.org/licenses/by-nc-sa/3.0/de/legalcode
#
###############################################################################
#
# 2009-02-23 0.0.1 rw created
# 2009-04-01 0.1.0 rw RC1
# 2009-05-06 0.1.1 rw NC 0.15c
# 2009-05-17 0.1.2 rw Waypoint Player
# 2009-06-11 0.1.3 rw DE/EN support. English by Mark Griffin
# 2009-07-25 0.1.4 rw KML Player
# 2009-07-26 0.1.5 rw MK System messages
#
###############################################################################
 
$Version{'translate.pl'} = "0.1.5 - 2009-07-26";
 
use Win32::Locale; # http://search.cpan.org/~sburke/Win32-Locale-0.04/Locale.pm
 
%Translate_DE = (
# NC Debug
"Analog_00" => "Angel Nick",
"Analog_01" => "Angel Roll",
"Analog_02" => "ACC Nick",
"Analog_03" => "ACC Roll",
"Analog_05" => "MK-Flags",
"Analog_06" => "NC-Flags",
"Analog_09" => "GPS Data",
"Analog_10" => "Compass Heading",
"Analog_11" => "Gyro Heading",
"Analog_12" => "SPI Error",
"Analog_13" => "SPI OK",
"Analog_14" => "I2C Error",
"Analog_15" => "I2C OK",
"Analog_16" => "FC Kalman K",
"Analog_17" => "ACC Speed North",
"Analog_18" => "ACC Speed East",
"Analog_19" => "GPS ACC",
"Analog_20" => "Max Drift",
"Analog_21" => "Speed North",
"Analog_22" => "Speed East",
"Analog_23" => "P-Part",
"Analog_24" => "I-Part",
"Analog_25" => "D-Part",
"Analog_26" => "PID-Part",
"Analog_27" => "Distance North",
"Analog_28" => "Distance East",
"Analog_29" => "GPS Nick",
"Analog_30" => "GPS Roll",
"Analog_31" => "Empfangene Satellitten",
# NC OSD
"Altimeter" => "Höhe (Luftdruck)",
"AngleNick" => "Winkel Nick",
"AngleRoll" => "Winkel Roll",
"CompassHeading" => "Richtung Kompass",
"CurPos_Alt" => "Position Höhe",
"CurPos_Lat" => "Position Latitude",
"CurPos_Lon" => "Position Longitude",
"CurPos_Stat" => "Position Status",
"Errorcode" => "Fehler Code",
"FlyingTime" => "Flugzeit",
"GroundSpeed" => "Geschw. über Grund",
"Heading" => "Richtung",
"HomePosDev_Bearing" => "Homeposition Richtung",
"HomePosDev_Dist" => "Homeposition Entfernung",
"HomePos_Alt" => "Homeposition Höhe",
"HomePos_Lat" => "Homeposition Latitude",
"HomePos_Lon" => "Homeposition Longitude",
"HomePos_Stat" => "Homeposition Status",
"MKFlags" => "MK Flags",
"NCFlags" => "NC Flags",
"OperatingRadius" => "Operating Radius",
"RC_Quality" => "RC Signalstärke",
"SatsInUse" => "Empfangene Satellitten",
"TargetHoldTime" => "Wartezeit am Ziel",
"TargetPosDev_Bearing" => "Ziel Richtung",
"TargetPosDev_Dist" => "Ziel Entfernung",
"TargetPos_Alt" => "Ziel Höhe",
"TargetPos_Lat" => "Ziel Latitude",
"TargetPos_Lon" => "Ziel Longitude",
"TargetPos_Stat" => "Ziel Status",
"TopSpeed" => "Geschwindigkeit vertikal",
"UBat" => "Batterie Spannung",
"Variometer" => "Variometer",
"WaypointIndex" => "Wegpunkt Index",
"WaypointNumber" => "Wegpunkt Nummer",
"_Timestamp" => "_Zeitstempel",
 
# NC Target
"EventFlag" => "Event Flag",
"Richtung" => "Richtung",
"HoldTime" => "Wartezeit",
"Pos_Alt" => "Position Höhe",
"Pos_Lat" => "Position Latitude",
"Pos_Lon" => "Position Longitude",
"Pos_Stat" => "Position Status",
"ToleranceRadius" => "Toleranz Radius",
 
# Configuration
"geserver" => "Google Earth Server",
"logging" => "Logging",
"map" => "Karte",
"mkcockpit" => "Mission Cockpit",
"mkcomm" => "MK Kommunikation",
"Port" => "Port *",
"track" => "Tracking Antenne",
"waypoint" => "Wegpunkte",
"HttpPort" => "HTTP Port *",
"CsvLogDir" => "CSV Log-Verzeichnis *",
"GpxLogDir" => "GPX Log-Verzeichnis *",
"KmlLogDir" => "KML Log-Verzeichnis *",
"MapDefault" => "Kartendefinition Hintergrundbild *",
"MapDir" => "Verzeichnis fuer Karten *",
"Active" => "Tarcking Antenne aktiv *",
"AltFactor" => "Faktor Höhe(Luftdruck) nach Meter",
"DefaultEventFlag" => "Default Event-Flag",
"DefaultHeading" => "Default Heading",
"DefaultHoldtime" => "Default Holdtime",
"DefaultToleranceRadius"=> "Default Toleranz Radius",
"WpDir" => "Verzeichnis Wegpunkte *",
"ColorAirfield" => "Farbe Flugfeldbegrenzung *",
"ColorHomeDist" => "Farbe Text Entfernung Home *",
"ColorHomeLine" => "Farbe Linie Entfernung Home *",
"ColorKmlTrack" => "Farbe KML Spur",
"ColorMkSatGood" => "Farbe MK guter Sat-Empfang",
"ColorMkSatLow" => "Farbe MK schlechter Sat-Empfang",
"ColorMkSatNo" => "Farbe MK kein Sat-Empfang",
"ColorOsd" => "Farbe OSD *",
"ColorSpeedVector" => "Farbe Speed-Vektor *",
"ColorTargetDist" => "Farbe Text Entfernung Ziel *",
"ColorTargetLine" => "Farbe Linie Entfernung Ziel *",
"ColorVariometer" => "Farbe Variometer-Skala *",
"ColorVariometerPointer"=> "Farbe Variometer-Zeiger *",
"ColorWpConnector" => "Farbe Wegpunkt-Connector",
"ColorWpResend" => "Farbe Wp-Conn. geaendert",
"ColorWpNumber" => "Farbe Wegpunkt Nummer",
"ColorFootprint" => "Farbe Footprint",
"FootprintLength" => "Länge Footprint (in s)",
"IconFox" => "Icon Fuchsjagd *",
"IconHeartLarge" => "Icon Heartbeat gross *",
"IconHeartSmall" => "Icon Heartbeat klein *",
"IconSatellite" => "Icon Satellit *",
"IconTarget" => "Icon Ziel *",
"IconWaypoint" => "Icon Wegpunkt *",
"Intervall" => "Intervall (in s) *",
"UBatWarning" => "Batterie Warnung (in V) ",
"IconFirst" => "Icon erster WP *",
"IconHome" => "Icon Home *",
"IconLast" => "Icon letzter WP *",
"IconNext" => "Icon nächster WP *",
"IconPause" => "Icon Pause *",
"IconPlay" => "Icon Play *",
"IconPrev" => "Icon vorheriger WP *",
"IconStop" => "Icon Stop WP-Player *",
"KmlDir" => "Verzeichnis KML-Files *",
"KmlTimeBase" => "KML Player Zeitbasis (s) *",
"PauseMoveDist" => "Pause-Verschiebe-Länge (m) *",
 
# Waypoint
+
+ "MapY" => "Y-Position auf Karte",
+ "Event_Flag" => "Event Flag",
+ "Holdtime" => "Wartezeit am Wegpunkt",
+
+ # GUI (MkCockpit.pl)
+ "File" => "~Datei",
+ "Preferences" => "Einstellungen",
+ "Exit" => "Ende",
+ "Debug" => "D~ebug",
+ "NcOsdDataset" => "NC ~OSD Datensatz (O)",
+ "NcTargetDataset" => "NC ~Target Datensatz (s)",
+ "NcDebugDataset" => "NC ~Debug Datensatz (D)",
+ "NcOther" => "NC ~Sonstiges",
+ "TrackingDebugDataset" => "Tracking ~Antenne Debug Datensatz",
+ "Help" => "~Hilfe",
+ "Version" => "Version",
+ "About" => "Über",
+ "AboutMissionCockpit" => "Über Mission Cockpit",
+ "StatusLine" => "Statuszeile",
+ "Map" => "Karte",
+
+ "Balloon-MK-Arrow" => "MikroKopter",
+ "Balloon-MK-Home-Line" => "Hier gehts nach Hause",
+ "Balloon-MK-Home-Dist" => "Entfernung nach Hause",
+ "Balloon-MK-Target-Line" => "Hier gehts zum Ziel",
+ "Balloon-MK-Target-Dist" => "Entfernung zum Ziel",
+ "Balloon-MK-Speed" => "Geschwindigkeits-Vektor",
+ "Balloon-Map-Variometer" => "Variometer",
+ "Balloon-Map-Variometer-Pointer" => "Variometer",
+ "Balloon-Map-Variometer-Skala" => "Variometer",
+ "Balloon-Fox" => "Ziel für Fuchsjagd",
+ "Balloon-Heartbeat" => "Aktivität Datenübertragung zum MK",
+ "Balloon-Satellite" => "Guter Satelliten-Empfang",
+ "Balloon-Waypoint" => "Wegpunkt",
+ "Balloon-Map-Border" => "Flugplatz",
+ "Balloon-Waypoint-Connector" => "Verbinder Wegpunkte",
+ "Balloon-Wp-PlayPause" => "Play/Pause (Leertaste)",
+ "Balloon-Wp-Stop" => "Stop (s)",
+ "Balloon-Wp-First" => "Zum ersten Waypoint (f)",
+ "Balloon-Wp-Last" => "Zum letzten Waypoint (l)",
+ "Balloon-Wp-Next" => "Zum nächsten Waypoint (n)",
+ "Balloon-Wp-Prev" => "Zum vorgerigen Waypoint (p)",
+ "Balloon-Wp-Home" => "Zur Home-Position (h)",
+ "Balloon-Wp-WptKml" => "Waypoint oder KML Mode (w,k)",
+
+ "TargetCoordSent" => "Ziel-Koordinaten gesendet",
+ "WpMoved" => "Wegpunkt verschoben",
+ "WpAddAndSend" => "Wegpunkt hinzufügen und senden",
+ "WpSavedAndSent" => "Wegpunkt gespeichert und gesendet",
+ "WpProperties" => "Wegpunkt Eigenschaften",
+ "WpResendAll" => "Alle Wegpunkte erneut senden",
+ "WpAllSent" => "Alle Wegpunkte gesendet",
+ "WpLoadAndSend" => "Wegpunkte laden und senden",
+ "WpLoad" => "Wegpunkte laden",
+ "WpLoadedAndSent" => "Wegpunkte geladen und gesendet",
+ "WpSave" => "Wegpunkte speichern",
+ "WpSaved" => "Wegpunkte in gespeichert",
+ "WpDelete" => "Wegpunkt löschen",
+ "WpDeleted" => "Wegpunkt gelöscht",
+ "WpAllDeleteAndSend" => "Alle Wegpunkte löschen und senden",
+ "WpAllDeleted" => "Alle Wegpunkte gelöscht",
+ "WpFlyImmediately" => "Ziel sofort anfliegen",
+ "Abort" => "Abbruch",
+ "RestartRequired" => "*) Aenderungen werden erst nach Programm-Neustart wirksam!",
+ "KmlLoadAndPlay" => "KML Datei laden",
+ "KmlLoad" => "KML laden",
+ "KmlLoaded" => "KML geladen",
+
+ # MK System Meldungen
+ "MsgNoSatReception" => "Kein SAT Empfang",
+ "MsgWeakSatReception" => "Schwacher SAT Empfang",
+ "MsgBatWarning" => "Akku Warnung",
+ "MsgNoData" => "Kein Daten-Link",
+ "MsgRangeLimit" => "Reichweiten Warnung",
+ "MsgRcWarning" => "RC Reichweiten Warnung",
+ "MsgRcError" => "Kein RC Empfang",
+ "MsgMototOff" => "Motor ist aus",
+ "MsgNotFlying", => "MK ist am Boden",
+ "MsgCalibrate", => "Kalibrierung",
+ "MsgStart" => "Motor start ...",
+ "MsgEmergencyLanding" => "Notlandung",
+
+ );
+
+
+%Translate_EN = (
+ # NC Debug
+ "Analog_00" => "Angel Nick",
+ "Analog_01" => "Angel Roll",
+ "Analog_02" => "ACC Nick",
+ "Analog_03" => "ACC Roll",
+ "Analog_05" => "MK-Flags",
+ "Analog_06" => "NC-Flags",
+ "Analog_09" => "GPS Data",
+ "Analog_10" => "Compass Heading",
+ "Analog_11" => "Gyro Heading",
+ "Analog_12" => "SPI Error",
+ "Analog_13" => "SPI OK",
+ "Analog_14" => "I2C Error",
+ "Analog_15" => "I2C OK",
+ "Analog_16" => "FC Kalman K",
+ "Analog_17" => "ACC Speed North",
+ "Analog_18" => "ACC Speed East",
+ "Analog_19" => "GPS ACC",
+ "Analog_20" => "Max Drift",
+ "Analog_21" => "Speed North",
+ "Analog_22" => "Speed East",
+ "Analog_23" => "P-Part",
+ "Analog_24" => "I-Part",
+ "Analog_25" => "D-Part",
+ "Analog_26" => "PID-Part",
+ "Analog_27" => "Distance North",
+ "Analog_28" => "Distance East",
+ "Analog_29" => "GPS Nick",
+ "Analog_30" => "GPS Roll",
+ "Analog_31" => "Satellite Receive",
+
+ # NC OSD
+ "Altimeter" => "Altitude (air)",
+ "AngleNick" => "Nick angle",
+ "AngleRoll" => "Roll angle",
+ "CompassHeading" => "Compass Heading",
+ "CurPos_Alt" => "Position Höhe",
+ "CurPos_Lat" => "Position Latitude",
+ "CurPos_Lon" => "Position Longitude",
+ "CurPos_Stat" => "Position Status",
+ "Errorcode" => "Error Code",
+ "FlyingTime" => "Flying time",
+ "GroundSpeed" => "Ground speed",
+ "Heading" => "Heading",
+ "HomePosDev_Bearing" => "Homeposition Bearing",
+ "HomePosDev_Dist" => "Homeposition Distance",
+ "HomePos_Alt" => "Homeposition Altitude",
+ "HomePos_Lat" => "Homeposition Latitude",
+ "HomePos_Lon" => "Homeposition Longitude",
+ "HomePos_Stat" => "Homeposition Status",
+ "MKFlags" => "MK Flags",
+ "NCFlags" => "NC Flags",
+ "OperatingRadius" => "Operating Radius",
+ "RC_Quality" => "RC Signal quality",
+ "SatsInUse" => "Satellites in use",
+ "TargetHoldTime" => "Target hold time",
+ "TargetPosDev_Bearing" => "Target Richtung",
+ "TargetPosDev_Dist" => "Target Distance",
+ "TargetPos_Alt" => "Target Altitude",
+ "TargetPos_Lat" => "Target Latitude",
+ "TargetPos_Lon" => "Target Longitude",
+ "TargetPos_Stat" => "Target Status",
+ "TopSpeed" => "Vertical Speed",
+ "UBat" => "Battery Voltage",
+ "Variometer" => "Variometer",
+ "WaypointIndex" => "Waypoint Index",
+ "WaypointNumber" => "Waypoint Number",
+ "_Timestamp" => "_Timestamp",
+
+ # NC Target
+ "EventFlag" => "Event Flag",
+ "Richtung" => "Direction",
+ "HoldTime" => "Hold Time",
+ "Pos_Alt" => "Position Altitude",
+ "Pos_Lat" => "Position Latitude",
+ "Pos_Lon" => "Position Longitude",
+ "Pos_Stat" => "Position Status",
+ "ToleranceRadius" => "Tolerance Radius",
+
+ # Configuration
+ "geserver" => "Google Earth Server",
+ "logging" => "Logging",
+ "map" => "Map",
+ "mkcockpit" => "Mission Cockpit",
+ "mkcomm" => "MK Communication",
+ "Port" => "Port *",
+ "track" => "Tracking Antenna",
+ "waypoint" => "Waypoint",
+ "HttpPort" => "HTTP Port *",
+ "CsvLogDir" => "CSV Log directory *",
+ "GpxLogDir" => "GPX Log directory *",
+ "KmlLogDir" => "KML Log directory *",
+ "MapDefault" => "Map default Background image *",
+ "MapDir" => "Map Directory *",
+ "Active" => "Tracking Antenna active *",
+ "AltFactor" => "Altitude factor (air) in Meter",
+ "DefaultEventFlag" => "Default Event-Flag",
+ "DefaultHeading" => "Default Heading",
+ "DefaultHoldtime" => "Default Holdtime",
+ "DefaultToleranceRadius"=> "Default Tolerance Radius",
+ "WpDir" => "Waypoint directory *",
+ "ColorAirfield" => "Color Airfield boundary *",
+ "ColorHomeDist" => "Color Text Distance Home *",
+ "ColorHomeLine" => "Color Line Distance Home *",
+ "ColorKmlTrack" => "Color KML Track",
+ "ColorMkSatGood" => "Color MK good Sat reception",
+ "ColorMkSatLow" => "Color MK weak Sat reception",
+ "ColorMkSatNo" => "Color MK no Sat reception",
+ "ColorOsd" => "Color OSD *",
+ "ColorSpeedVector" => "Color Speed-Vector *",
+ "ColorTargetDist" => "Color Text Target distance *",
+ "ColorTargetLine" => "Color Linie Target distance *",
+ "ColorVariometer" => "Color Variometer Scale *",
+ "ColorVariometerPointer"=> "Color Variometer pointer *",
+ "ColorWpConnector" => "Color Waypoint connector",
+ "ColorWpResend" => "Color Wp-Conn. changed",
+ "ColorWpNumber" => "Color Waypoint Number",
+ "ColorFootprint" => "Color Footprint",
+ "FootprintLength" => "Länge Footprint (in s)",
+ "IconFox" => "Icon Fox *",
+ "IconHeartLarge" => "Icon Heartbeat large *",
+ "IconHeartSmall" => "Icon Heartbeat small *",
+ "IconSatellite" => "Icon Satellite *",
+ "IconTarget" => "Icon Target *",
+ "IconWaypoint" => "Icon Waypoint *",
+ "Intervall" => "Interval (in s) *",
+ "UBatWarning" => "Battery Warning (in V) ",
+ "IconFirst" => "Icon first WP *",
+ "IconHome" => "Icon Home *",
+ "IconLast" => "Icon last WP *",
+ "IconNext" => "Icon next WP *",
+ "IconPause" => "Icon Pause *",
+ "IconPlay" => "Icon Play *",
+ "IconPrev" => "Icon previous WP *",
+ "IconStop" => "Icon Stop WP-Player *",
+ "KmlDir" => "Directory KML-Files *",
+ "KmlTimeBase" => "KML Player Timebase (s) *",
+ "PauseMoveDist" => "Pause Move Distance (m) *",
+
+ # Waypoint
+
+ "MapY" => "Y-Position on the map",
+ "Event_Flag" => "Event Flag",
+ "Holdtime" => "Waiting time at Waypoint",
+
+ # GUI (MkCockpit.pl)
+ "File" => "~File",
+ "Preferences" => "Preferences",
+ "Exit" => "E~xit",
+ "Debug" => "D~ebug",
+ "NcOsdDataset" => "NC ~OSD Record (O)",
+ "NcTargetDataset" => "NC ~Target Record (s)",
+ "NcDebugDataset" => "NC ~Debug Record (D)",
+ "NcOther" => "NC ~Other",
+ "TrackingDebugDataset" => "Tracking ~Antenna Debug Record",
+ "Help" => "~Help",
+ "Version" => "Version",
+ "About" => "About",
+ "AboutMissionCockpit" => "About Mission Cockpit",
+ "StatusLine" => "Status line",
+ "Map" => "Map",
+
+ "Balloon-MK-Arrow" => "MikroKopter",
+ "Balloon-MK-Home-Line" => "This way to Home",
+ "Balloon-MK-Home-Dist" => "Distance to Home",
+ "Balloon-MK-Target-Line" => "This way to the Target",
+ "Balloon-MK-Target-Dist" => "Distance to Target",
+ "Balloon-MK-Speed" => "Speed vector",
+ "Balloon-Map-Variometer" => "Variometer",
+ "Balloon-Map-Variometer-Pointer" => "Variometer",
+ "Balloon-Map-Variometer-Skala" => "Variometer",
+ "Balloon-Fox" => "Target for Fox hunting",
+ "Balloon-Heartbeat" => "MK data activity",
+ "Balloon-Satellite" => "Good satellite reception",
+ "Balloon-Waypoint" => "Waypoint",
+ "Balloon-Map-Border" => "Airfield",
+ "Balloon-Waypoint-Connector" => "Waypoint connector",
+ "Balloon-Wp-PlayPause" => "Play/Pause (Space)",
+ "Balloon-Wp-Stop" => "Stop (s)",
+ "Balloon-Wp-First" => "Go to 1st Waypoint (f)",
+ "Balloon-Wp-Last" => "Go to last Waypoint (l)",
+ "Balloon-Wp-Next" => "Go to next Waypoint (n)",
+ "Balloon-Wp-Prev" => "Go to previous Waypoint (p)",
+ "Balloon-Wp-Home" => "Coming Home (h)",
+ "Balloon-Wp-WptKml" => "Waypoint or KML Mode (w,k)",
+
+ "TargetCoordSent" => "Target coordinates sent",
+ "WpMoved" => "Waypoint moved",
+ "WpAddAndSend" => "Add and send Waypoint",
+ "WpSavedAndSent" => "Waypoint saved and sent",
+ "WpProperties" => "Waypoint preferneces",
+ "WpResendAll" => "Resend all Wayoints",
+ "WpAllSent" => "All Waypoints sent",
+ "WpLoadAndSend" => "Load and send Waypoints",
+ "WpLoad" => "Load Waypoints",
+ "WpLoadedAndSent" => "Waypoints loaded and sent",
+ "WpSave" => "Save Waypoints",
+ "WpSaved" => "Waypoints saved",
+ "WpDelete" => "Delete Waypoint",
+ "WpDeleted" => "Waypoint deleted",
+ "WpAllDeleteAndSend" => "Delete and send all Waypoints",
+ "WpAllDeleted" => "All Waypoints deleted",
+ "WpFlyImmediately" => "Fly immediately to Target",
+ "Abort" => "Abort",
+ "RestartRequired" => "*) Restart of Program required!",
+ "KmlLoadAndPlay" => "Load KML file",
+ "KmlLoad" => "KML load",
+ "KmlLoaded" => "KML file loaded",
+
+ # MK System Messages
+ "MsgNoSatReception" => "No SAT Reception",
+ "MsgWeakSatReception" => "Weak SAT Reception",
+ "MsgBatWarning" => "Battery Warning",
+ "MsgNoData" => "No Data-Link",
+ "MsgRangeLimit" => "Range Limit Warning",
+ "MsgRcWarning" => "RC Warning",
+ "MsgRcError" => "No RC Reception",
+ "MsgMototOff" => "Engine is OFF",
+ "MsgNotFlying", => "MK on Ground",
+ "MsgCalibrate", => "Calibration",
+ "MsgStart" => "STart Engines ...",
+ "MsgEmergencyLanding" => "Emergency Landing",
+
+ );
+
+
+# automatic language selection according to LOCALE setting
+my $Lang = Win32::Locale::get_language();
+if ( $Lang =~ /de/i )
+ {
+ %Translate = %Translate_DE;
+ }
+else
+ {
+ %Translate = %Translate_EN;
+ }
+
+1;
+
+__END__