Subversion Repositories Projects

Rev

Details | Last modification | View Log | RSS feed

Rev Author Line No. Line
544 rain-er 1
#!/usr/bin/perl
2
#!/usr/bin/perl -d:ptkdb
3
 
4
###############################################################################
5
#
6
# mkcockpit.pl -  MK Mission Cockpit - GUI
7
#
8
# Copyright (C) 2009  Rainer Walther  (rainerwalther-mail@web.de)
9
#
10
# Creative Commons Lizenz mit den Zusaetzen (by, nc, sa)
11
#
12
# Es ist Ihnen gestattet: 
13
#     * das Werk vervielfältigen, verbreiten und öffentlich zugänglich machen
14
#     * Abwandlungen bzw. Bearbeitungen des Inhaltes anfertigen
15
# 
16
# Zu den folgenden Bedingungen:
17
#     * Namensnennung.
18
#       Sie müssen den Namen des Autors/Rechteinhabers in der von ihm festgelegten Weise nennen.
19
#     * Keine kommerzielle Nutzung.
20
#       Dieses Werk darf nicht für kommerzielle Zwecke verwendet werden.
21
#     * Weitergabe unter gleichen Bedingungen.
22
#       Wenn Sie den lizenzierten Inhalt bearbeiten oder in anderer Weise umgestalten,
23
#       verändern oder als Grundlage für einen anderen Inhalt verwenden,
24
#       dürfen Sie den neu entstandenen Inhalt nur unter Verwendung von Lizenzbedingungen
25
#       weitergeben, die mit denen dieses Lizenzvertrages identisch oder vergleichbar sind.
26
# 
27
# Im Falle einer Verbreitung müssen Sie anderen die Lizenzbedingungen, unter welche dieses
28
# Werk fällt, mitteilen. Am Einfachsten ist es, einen Link auf diese Seite einzubinden.
29
# 
30
# Jede der vorgenannten Bedingungen kann aufgehoben werden, sofern Sie die Einwilligung
31
# des Rechteinhabers dazu erhalten.
32
# 
33
# Diese Lizenz lässt die Urheberpersönlichkeitsrechte unberührt.
34
# 
35
# Weitere Details zur Lizenzbestimmung gibt es hier:
36
#   Kurzform: http://creativecommons.org/licenses/by-nc-sa/3.0/de/
37
#   Komplett: http://creativecommons.org/licenses/by-nc-sa/3.0/de/legalcode
38
#
39
###############################################################################
40
# 2009-02-20 0.0.1 rw created
41
# 2009-04-01 0.1.0 rw RC1
42
# 2009-04-16 0.1.1 rw Bugfix, ALT= average of airsensor and Sat
43
# 2009-05-14 0.2.0 rw Waypoint Player
44
# 2009-05-17 0.2.1 rw Cursor-Steuerung fuer WP-Player. Cmdline-Parameter "-geometry"
45
# 2009-07-18 0.2.2 rw DE/EN multinational
46
#                     Target-Balloon with Distance, Tolerance and Holdtime
47
#                     Fix footprint "Ausreiser"
48
#                     JPEG and PNG maps supported
49
#                     Player for KML Files
50
# 2009-07-26 0.2.3 rw System Messages Balloon
51
# 2009-07-31 0.2.4 rw ODO Kilometerzähler
52
#                     Enter WP-Number from Keyboard
53
#                     Random WP-Player (Waypoint and Map)
54
#                     Check Airfield Border
55
#                     Draw Calibration points on map
56
#
57
###############################################################################
58
 
59
$Version = "0.2.4 - 2009-07-31";
60
 
61
use threads;            # http://search.cpan.org/~jdhedden/threads-1.72/threads.pm
62
                        # http://perldoc.perl.org/threads.html
63
use threads::shared;    # http://search.cpan.org/~jdhedden/threads-shared-1.28/shared.pm
64
use Thread::Queue;      # http://search.cpan.org/dist/Thread-Queue-2.11/lib/Thread/Queue.pm
65
use Tk;
66
use Tk::Balloon;
67
use Tk::Dialog;
68
use Tk::Notebook;
69
use Tk::JPEG;           # http://search.cpan.org/~srezic/Tk-804.028/JPEG/JPEG.pm
70
use Tk::PNG;            # http://search.cpan.org/~srezic/Tk-804.028/PNG/PNG.pm
71
use Math::Trig;
72
use Time::HiRes qw(usleep);  # http://search.cpan.org/~jhi/Time-HiRes-1.9719/HiRes.pm
73
use XML::Simple;             # http://search.cpan.org/dist/XML-Simple-2.18/lib/XML/Simple.pm
74
 
75
# change working directory to program path
76
my $Cwd = substr ($0, 0, rindex ($0, "mkcockpit.pl"));
77
chdir $Cwd;
78
 
79
# set path for local Perl libs
80
push @INC, $Cwd . "perl/lib", $Cwd . "perl/site/lib";
81
 
82
# Version setting
83
share (%Version);
84
$Version{'mkcockpit.pl'}  = $Version;
85
 
86
# Read configuration
87
 
88
$XmlConfigFile = "mkcockpit.xml";
89
$Cfg = XMLin($XmlConfigFile);
90
 
91
require "track.pl";     # Tracking antenna
92
require "mkcomm.pl";    # MK communication
93
require "logging.pl";   # CSV and GPX Logging
94
require "geserver.pl";  # Google Earth Server
95
require "$Cfg->{'map'}->{'MapDir'}/map.pl";   # Landkarte
96
require "libmap.pl";    # map subs
97
require "translate.pl"; # Übersetzungstable
98
 
99
# Commandline parameter
100
my %CmdLine = @ARGV;
101
 
102
# Thread fuer Kommunikation mit MK starten
103
# Output: %MkOsd, %MkTarget, %MkNcDebug, %Mk
104
# Input:  Thread-Queue: $MkSendQueue
105
$mk_thr = threads->create (\&MkCommLoop) -> detach();
106
 
107
# Start Logging Thread
108
$log_thr = threads->create (\&MkLogLoop) -> detach();
109
 
110
# Start GoogleEarth Thread
111
$ge_thr = threads->create (\&GeServer) -> detach();
112
 
113
# Aktuell gültige Karte
114
my %Map = %{$Maps{'Current'}};
115
 
116
# Hauptfenster
117
my $main = new MainWindow;
118
$main->title ("MK Mission Cockpit - Version $Version");
119
 
120
if ( $CmdLine{'-geometry'} ne "" )
121
    {
122
    $main->geometry( "$CmdLine{'-geometry'}" );
123
    }
124
 
125
#-----------------------------------------------------------------
126
# Menu
127
#-----------------------------------------------------------------
128
 
129
# Menu bar
130
my $menu_bar = $main->Menu;
131
$main->optionAdd("*tearOff", "false");
132
$main->configure ('-menu' => $menu_bar);
133
 
134
my $menu_file = $menu_bar->cascade('-label' => $Translate{'File'});
135
    $menu_file->command('-label' => $Translate{'Preferences'},
136
                        '-command' => [\&Configure],
137
                       );
138
    $menu_file->separator;                                     
139
    $menu_file->command('-label' => $Translate{'Exit'},
140
                        '-command' => sub{exit(0)},
141
                        );
142
 
143
my $menu_debug = $menu_bar->cascade(-label => $Translate{'Debug'});
144
    $menu_debug->command('-label' => $Translate{'NcOsdDataset'},
145
                         '-command' => [\&DisplayHash, \%MkOsd, $Translate{'NcOsdDataset'}, "Display Refresh Heartbeat"],
146
                        );
147
    $menu_debug->command('-label' => $Translate{'NcTargetDataset'},
148
                         '-command' => [\&DisplayHash, \%MkTarget, $Translate{'NcTargetDataset'}, "Display Refresh Heartbeat"],
149
                        );
150
    $menu_debug->command('-label' => $Translate{'NcDebugDataset'},
151
                         '-command' => [\&DisplayHash, \%MkNcDebug, $Translate{'NcDebugDataset'}, "Display Refresh Heartbeat"],
152
                                        );             
153
    $menu_debug->command('-label' => $Translate{'NcOther'},
154
                         '-command' => [\&DisplayHash, \%Mk, $Translate{'NcOther'}, "Display Refresh Heartbeat"],
155
                                        );
156
    $menu_debug->separator;                                    
157
    $menu_debug->command('-label' => $Translate{'TrackingDebugDataset'},
158
                         '-command' => [\&DisplayHash, \%MkTrack, $Translate{'TrackingDebugDataset'}, "Display Refresh Heartbeat"],
159
                        );
160
 
161
my $menu_help = $menu_bar->cascade(-label => $Translate{'Help'});
162
    $menu_help->command('-label' => 'Version',
163
                        '-command' => [\&DisplayHash, \%Version, $Translate{'Version'}, "Display"],
164
                       );
165
    $menu_help->separator;
166
    $menu_help->command('-label' => $Translate{'About'},
167
                        '-command' => sub
168
        {
169
        my $License = <<EOF;
170
Copyright (C) 2009  Rainer Walther (rainerwalther-mail\@web.de)
171
 
172
Creative Commons Lizenz mit den Zusaetzen (by, nc, sa)
173
 
174
See LICENSE.TXT
175
EOF
176
 
177
        my $DlgAbout = $frame_map->Dialog('-title' => $Translate{'AboutMissionCockpit'},
178
                                          '-text' => "$License",
179
                                          '-buttons' => ['OK'],
180
                                          '-bitmap' => 'info',
181
                                         );
182
        $DlgAbout->Show;
183
        });  
184
 
185
 
186
# Hauptfenster Statuszeile
187
$frame_status = $main->Frame( '-background' => 'lightgray',
188
                         ) -> pack('-side' => 'bottom',
189
                                   '-anchor' => 'w',
190
                                   '-fill' => 'none',
191
                                   '-expand' => 'y',
192
                                    );
193
$status_line = $frame_status->Label ('-text' => $Translate{'StatusLine'},
194
                                    ) -> pack ('-side' => 'bottom',
195
                                               );
196
 
197
 
198
#-----------------------------------------------------------------
199
# Frames
200
#-----------------------------------------------------------------                        
201
 
202
#
203
# Frame: Map
204
#
205
 
206
$frame_map = $main->Frame( '-background' => 'lightgray',
207
                           '-relief' => 'sunken',
208
                           '-borderwidth' => 5,
209
                          ) -> pack('-side' => 'top',
210
                                    '-fill' => 'x',
211
                                    );
212
 
213
# Map Überschrift
214
$frame_map_top = $frame_map->Frame() -> pack( '-side' => 'top',
215
                                              '-expand' => 'x',
216
                                              '-anchor' => 'w',
217
                                            );
218
 
219
$frame_map_top->Label ('-text' => "$Translate{'Map'}: $Map{'Name'} ($Map{'File'})",
220
                       '-background' => 'lightgray',
221
                       '-relief' => 'flat',
222
                       ) -> pack( '-side' => 'left' );
223
 
224
# Map Statuszeile
225
$map_status = $frame_map->Frame( '-background' => 'lightgray',
226
                               ) -> pack('-side' => 'bottom',
227
                                         '-anchor' => 'w',
228
                                         '-fill' => 'none',
229
                                         '-expand' => 'y',
230
                                        );
231
$map_status_line = $map_status->Label ( '-text' => $Translate{'StatusLine'},
232
                                        '-background' => 'lightgray',
233
                                       ) -> pack ('-side' => 'bottom',);
234
 
235
# Map Canvas
236
 
237
# Canvas size
238
$MapSizeX  = $Map{'Size_X'};
239
$MapSizeY  = $Map{'Size_Y'};
240
 
241
$map_canvas = $frame_map->Canvas( '-width'  => $MapSizeX,
242
                                  '-height' => $MapSizeY,
243
                                  '-cursor' => 'cross',
244
                                ) -> pack();
245
 
246
# Images and Icons on canvas
247
my @Icons = (
248
            # Image             Tag             File                                       Pos_x            Pos_y
249
            'Map',              'Map',          "$Cfg->{'map'}->{'MapDir'}/$Map{'File'}",  0,               0,
250
            'HeartbeatSmall',   'Heartbeat',    "$Cfg->{'mkcockpit'}->{'IconHeartSmall'}", $MapSizeX/4,     10,
251
            'HeartbeatLarge',   'Heartbeat',    "$Cfg->{'mkcockpit'}->{'IconHeartLarge'}", $MapSizeX/4,     -100,
252
            'Satellite-Photo',  'Satellite',    "$Cfg->{'mkcockpit'}->{'IconSatellite'}",  $MapSizeX-300,   -100,
253
            'Waypoint-Photo',   'Waypoint',     "$Cfg->{'mkcockpit'}->{'IconWaypoint'}",   0,               -150,
254
            'Target-Photo',     'Target',       "$Cfg->{'mkcockpit'}->{'IconTarget'}",     0,               -100,
255
            'Fox-Photo',        'Fox',          "$Cfg->{'mkcockpit'}->{'IconFox'}",        $MapSizeX/2-100, $MapSizeY/2,
256
            'WpPlay-Foto',      'Wp-PlayPause', "$Cfg->{'waypoint'}->{'IconPlay'}",        $MapSizeX/2+100, $MapSizeY-48,
257
            'WpPause-Foto',     'Wp-PlayPause', "$Cfg->{'waypoint'}->{'IconPause'}",       $MapSizeX/2+100, -100,
258
            'WpStop-Foto',      'Wp-Stop',      "$Cfg->{'waypoint'}->{'IconStop'}",        $MapSizeX/2+150, $MapSizeY-48,
259
            'WpNext-Foto',      'Wp-Next',      "$Cfg->{'waypoint'}->{'IconNext'}",        $MapSizeX/2,     $MapSizeY-48,
260
            'WpPrev-Foto',      'Wp-Prev',      "$Cfg->{'waypoint'}->{'IconPrev'}",        $MapSizeX/2-50,  $MapSizeY-48,
261
            'WpFirst-Foto',     'Wp-First',     "$Cfg->{'waypoint'}->{'IconFirst'}",       $MapSizeX/2-100, $MapSizeY-48,
262
            'WpLast-Foto',      'Wp-Last',      "$Cfg->{'waypoint'}->{'IconLast'}",        $MapSizeX/2+50,  $MapSizeY-48,
263
            'WpHome-Foto',      'Wp-Home',      "$Cfg->{'waypoint'}->{'IconHome'}",        $MapSizeX/2-150, $MapSizeY-48,
264
            'WpWpt-Foto',       'Wp-WptKml',    "$Cfg->{'waypoint'}->{'IconWpt'}",         $MapSizeX/2-250, $MapSizeY-48,
265
            'WpKml-Foto',       'Wp-WptKml',    "$Cfg->{'waypoint'}->{'IconKml'}",         $MapSizeX/2-250, -100 ,
266
            'WpRandomOff-Foto', 'Wp-WptRandom', "$Cfg->{'waypoint'}->{'IconRandomOff'}",   $MapSizeX/2-200, -100,
267
            'WpRandomOn-Foto',  'Wp-WptRandom', "$Cfg->{'waypoint'}->{'IconRandomOn'}",    $MapSizeX/2-200, $MapSizeY-48,
268
            'WpRandomMap-Foto', 'Wp-WptRandom', "$Cfg->{'waypoint'}->{'IconRandomMap'}",   $MapSizeX/2-200, -100,
269
            );
270
 
271
my $i = 0;
272
for $Icon (0 .. $#Icons/5)
273
    {
274
    my $Image =  $Icons[$i++];
275
    my $Tag =    $Icons[$i++];
276
    my $File =   $Icons[$i++];
277
    my $Pos_x =  $Icons[$i++];
278
    my $Pos_y =  $Icons[$i++];
279
 
280
    $map_canvas->Photo( $Image,
281
                        '-file' => $File,
282
                      );
283
    $map_canvas->createImage( $Pos_x, $Pos_y,
284
                              '-tags'   => $Tag,
285
                              '-anchor' => 'nw',
286
                              '-image'  => $Image,
287
                            );
288
    }
289
 
290
# Calibration Points
291
$map_canvas->createLine ( $Map{'P1_x'}-8, $Map{'P1_y'},
292
                          $Map{'P1_x'}+8, $Map{'P1_y'},
293
                          $Map{'P1_x'},   $Map{'P1_y'},
294
                          $Map{'P1_x'},   $Map{'P1_y'}-8,
295
                          $Map{'P1_x'},   $Map{'P1_y'}+8,
296
                          '-tags'  => 'Calibration',
297
                          '-arrow' => 'none',
298
                          '-fill'  => 'red',
299
                          '-width' => 1,
300
                         );
301
$map_canvas->createLine ( $Map{'P2_x'}-8, $Map{'P2_y'},
302
                          $Map{'P2_x'}+8, $Map{'P2_y'},
303
                          $Map{'P2_x'},   $Map{'P2_y'},
304
                          $Map{'P2_x'},   $Map{'P2_y'}-8,
305
                          $Map{'P2_x'},   $Map{'P2_y'}+8,
306
                          '-tags'  => 'Calibration',
307
                          '-arrow' => 'none',
308
                          '-fill'  => 'red',
309
                          '-width' => 1,
310
                         );
311
 
312
# border polygon
313
$map_canvas->createPolygon( @Map{'Border'},
314
                           '-tags' => 'Map-Border',
315
                           '-fill' => '',
316
                           '-outline' => $Cfg->{'mkcockpit'}->{'ColorAirfield'}, '-width' => 2,
317
                          );
318
$map_canvas->raise('Map-Border', 'Map');  # Border above Map
319
 
320
 
321
# Balloon attached to Canvas
322
$map_balloon = $frame_map->Balloon('-statusbar' => $status_line, );
323
$map_balloon->attach($map_canvas,
324
                     '-balloonposition' => 'mouse',
325
                     '-state' => 'balloon',
326
                     '-msg' => { 'MK-Arrow'               => $Translate{'Balloon-MK-Arrow'},
327
                                 'MK-Home-Line'           => $Translate{'Balloon-MK-Home-Line'},
328
                                 'MK-Home-Dist'           => $Translate{'Balloon-MK-Home-Dist'},
329
                                 'MK-Target-Line'         => $Translate{'Balloon-MK-Target-Line' },
330
                                 'MK-Target-Dist'         => $Translate{'Balloon-MK-Target-Dist'},
331
                                 'MK-Speed'               => $Translate{'Balloon-MK-Speed'},
332
                                 'Map-Variometer'         => $Translate{'Balloon-Map-Variometer' },
333
                                 'Map-Variometer-Pointer' => $Translate{'Balloon-Map-Variometer-Pointer'},
334
                                 'Map-Variometer-Skala'   => $Translate{'Balloon-Map-Variometer-Pointer'},
335
                                 'Fox'                    => $Translate{'Balloon-Fox'},
336
                                 'Heartbeat'              => $Translate{'Balloon-Heartbeat'},
337
                                 'Satellite'              => $Translate{'Balloon-Satellite'},
338
                                 'Waypoint'               => $Translate{'Balloon-Waypoint'},
339
                                 'Map-Border'             => $Translate{'Balloon-Map-Border'},
340
                                 'Waypoint-Connector'     => $Translate{'Balloon-Waypoint-Connector'},
341
                                 'Wp-PlayPause'           => $Translate{'Balloon-Wp-PlayPause'},
342
                                 'Wp-Stop'                => $Translate{'Balloon-Wp-Stop'},
343
                                 'Wp-First'               => $Translate{'Balloon-Wp-First'},
344
                                 'Wp-Last'                => $Translate{'Balloon-Wp-Last'},
345
                                 'Wp-Next'                => $Translate{'Balloon-Wp-Next'},
346
                                 'Wp-Prev'                => $Translate{'Balloon-Wp-Prev'},
347
                                 'Wp-Home'                => $Translate{'Balloon-Wp-Home'},
348
                                 'Wp-WptKml'              => $Translate{'Balloon-Wp-WptKml'},
349
                                 'Wp-WptRandom'           => $Translate{'Balloon-Wp-WptRandom'},
350
                               },
351
                    );
352
 
353
#
354
# Mouse buttons
355
#
356
 
357
# general Mouse button 1
358
$map_canvas->CanvasBind("<Button-1>", sub
359
    {
360
    # print coords in status line
361
    my ($x, $y) = ($Tk::event->x, $Tk::event->y);
362
    my ($Lat, $Lon) = &MapXY2Gps($x, $y);
363
    $map_status_line->configure ('-text' => "Lat: $Lat  Lon: $Lon     x: $x  y: $y");
364
    });
365
 
366
# Mouse button 1 for Fox
367
my $FoxOldx = 0;
368
my $FoxOldy = 0;
369
 
370
# Pick Fox
371
$map_canvas->bind('Fox' => '<Button-1>' => sub
372
    {
373
    # prepare to move Fox
374
    my ($x, $y) = ($Tk::event->x, $Tk::event->y);
375
    $FoxOldx = $x;
376
    $FoxOldy = $y;
377
    $FoxTime = time;
378
    });
379
 
380
# Move Fox
381
$map_canvas->bind('Fox' => '<Button1-Motion>' => sub
382
    {
383
    my ($x, $y) = ($Tk::event->x, $Tk::event->y);
384
    my $id      = $map_canvas->find('withtag', 'current');
385
 
386
    $map_canvas->move($id => $x - $FoxOldx, $y - $FoxOldy);
387
    $FoxOldx = $x;
388
    $FoxOldy = $y;
389
 
390
    if ( time > $FoxTime )
391
        {
392
          # wenn in Bewegung Koordinaten nur 1/s senden
393
        my ($x0, $y0, $x1, $y1) = $map_canvas->bbox ($id);
394
        $x = $x0 + ($x1 - $x0)/2;
395
        $y = $y1;
396
 
397
        my ($Lat, $Lon) = &MapXY2Gps($x, $y);
398
        &MkFlyTo ( '-lat' => $Lat,
399
                   '-lon' => $Lon,
400
                   '-mode' => "Target",
401
                 );
402
        $FoxTime = time;
403
 
404
        $map_status_line->configure ('-text' => "$Translate{'TargetCoordSent'} -> Lat: $Lat  Lon: $Lon     x: $x  y: $y");
405
        }
406
    });
407
 
408
# Release Fox
409
$map_canvas->bind('Fox' => '<Button1-ButtonRelease>' => sub
410
    {
411
    my ($x, $y) = ($Tk::event->x, $Tk::event->y);
412
    my $id      = $map_canvas->find('withtag', 'current');
413
 
414
    my ($x0, $y0, $x1, $y1) = $map_canvas->bbox ($id);
415
    $x = $x0 + ($x1 - $x0)/2;
416
    $y = $y1;
417
 
418
    my ($Lat, $Lon) = &MapXY2Gps($x, $y);
419
    &MkFlyTo ( '-lat' => $Lat,
420
               '-lon' => $Lon,
421
               '-mode' => "Target",
422
             );
423
 
424
    # Show user that Waypoints in MK are cleared
425
    $WaypointsModified = 1;
426
    &WpRedrawLines();
427
 
428
    $map_status_line->configure ('-text' => "$Translate{'TargetCoordSent'} -> Lat: $Lat  Lon: $Lon     x: $x  y: $y");
429
    });
430
 
431
# Pick Waypoint
432
$map_canvas->bind('Waypoint' => '<Button-1>' => sub
433
    {
434
    # prepare to move
435
    my ($x, $y) = ($Tk::event->x, $Tk::event->y);
436
    $WpOldx = $x;
437
    $WpOldy = $y;
438
    });
439
 
440
# Move Waypoint
441
$map_canvas->bind('Waypoint' => '<Button1-Motion>' => sub
442
    {
443
    my ($x, $y) = ($Tk::event->x, $Tk::event->y);
444
    my $id      = $map_canvas->find('withtag', 'current');
445
 
446
    # move icon and Wp-Number
447
    my $WpIndex = &WpGetIndexFromId($id);
448
    if ( $WpIndex >= 0 )
449
        {
450
        my $Tag = $Waypoints[$WpIndex]{'Tag'};
451
        $map_canvas->move($Tag => $x - $WpOldx, $y - $WpOldy);
452
        }
453
 
454
    $WpOldx = $x;
455
    $WpOldy = $y;
456
    });
457
 
458
# Release Wp
459
$map_canvas->bind('Waypoint' => '<Button1-ButtonRelease>' => sub
460
    {
461
    my ($x, $y) = ($Tk::event->x, $Tk::event->y);
462
    my $id      = $map_canvas->find('withtag', 'current');
463
 
464
    # take coords from lower/middle icon position
465
    my ($x0, $y0, $x1, $y1) = $map_canvas->bbox ($id);
466
    $x = $x0 + ($x1 - $x0)/2;
467
    $y = $y1;
468
 
469
    # update Waypoint-Array
470
    my $WpIndex = &WpGetIndexFromId($id);
471
    if ( $WpIndex >= 0 )
472
            {
473
        # got it: set new coords
474
 
475
        my ($Lat, $Lon) = &MapXY2Gps($x, $y);
476
        my $Wp = $Waypoints[$WpIndex];
477
        $Wp->{'MapX'} = $x;
478
        $Wp->{'MapY'} = $y;
479
        $Wp->{'Pos_Lat'} = $Lat;
480
        $Wp->{'Pos_Lon'} = $Lon;
481
 
482
        # redraw connector-lines
483
        &WpRedrawLines();
484
 
485
        # red connectors: Wp still have to be sent to MK
486
        $map_canvas->itemconfigure('Waypoint-Connector',
487
                                           '-fill' => $Cfg->{'mkcockpit'}->{'ColorWpResend'},
488
                                  );
489
        $WaypointsModified = 1;
490
 
491
        my $WpNum = $WpIndex + 1;
492
        $map_status_line->configure ('-text' => "$Translate{'WpMoved'}: $WpNum -> Lat: $Lat  Lon: $Lon     x: $x  y: $y");
493
        }
494
    });
495
 
496
 
497
#
498
# Player:
499
#    Waypoint-List:   @Waypoints
500
#    KML-Target-List: @KmlTargets
501
#
502
 
503
# Player state machine
504
$PlayerMode = 'Stop';     # Start, Stop, Pause, Home ...
505
$PlayerWptKmlMode = 'WPT';  # WPT, KML
506
$PlayerRandomMode = 'STD';  # STD, RND, MAP
507
$WpPlayerIndex = 0;
508
$WpPlayerHoldtime = -1;
509
$KmlPlayerIndex = 0;
510
$PlayerPause_Lat = "";
511
$PlayerPause_Lon = "";
512
 
513
# Mouse bindings
514
$map_canvas->bind('Wp-PlayPause' => '<Button-1>' => \&CbPlayerPlayPause );
515
$map_canvas->bind('Wp-Next'      => '<Button-1>' => \&CbPlayerNext );
516
$map_canvas->bind('Wp-Prev'      => '<Button-1>' => \&CbPlayerPrev );
517
$map_canvas->bind('Wp-First'     => '<Button-1>' => \&CbPlayerFirst );
518
$map_canvas->bind('Wp-Last'      => '<Button-1>' => \&CbPlayerLast );
519
$map_canvas->bind('Wp-Home'      => '<Button-1>' => \&CbPlayerHome );
520
$map_canvas->bind('Wp-Stop'      => '<Button-1>' => \&CbPlayerStop );
521
$map_canvas->bind('Wp-WptKml'    => '<Button-1>' => \&CbPlayerWptKml );
522
$map_canvas->bind('Wp-WptRandom' => '<Button-1>' => \&CbPlayerWptRandom );
523
 
524
 
525
# Focus Canvas, if any key pressed. Needed for the following key-bindings
526
$main->bind('<Any-Enter>' => sub { $map_canvas->Tk::focus });
527
 
528
# Disable default arrow-key bindings on canvas
529
$main->bind('Tk::Canvas',"<$_>",undef)for qw /Left Right Up Down/;
530
 
531
# keyboard bindings
532
$map_canvas->Tk::bind( '<Key-space>' , \&CbPlayerPlayPause );
533
$map_canvas->Tk::bind( '<Key-n>'     , \&CbPlayerNext );
534
$map_canvas->Tk::bind( '<Key-p>'     , \&CbPlayerPrev );
535
$map_canvas->Tk::bind( '<Key-f>'     , \&CbPlayerFirst );
536
$map_canvas->Tk::bind( '<Key-l>'     , \&CbPlayerLast );
537
$map_canvas->Tk::bind( '<Key-h>'     , \&CbPlayerHome );
538
$map_canvas->Tk::bind( '<Key-s>'     , \&CbPlayerStop );
539
$map_canvas->Tk::bind( '<Key-w>'     , \&CbPlayerWptKml );
540
$map_canvas->Tk::bind( '<Key-k>'     , \&CbPlayerWptKml );
541
$map_canvas->Tk::bind( '<Key-r>'     , \&CbPlayerWptRandom );
542
$map_canvas->Tk::bind( '<Key-0>'     , [\&CbPlayerNum, "0"] );
543
$map_canvas->Tk::bind( '<Key-1>'     , [\&CbPlayerNum, "1"] );
544
$map_canvas->Tk::bind( '<Key-2>'     , [\&CbPlayerNum, "2"] );
545
$map_canvas->Tk::bind( '<Key-3>'     , [\&CbPlayerNum, "3"] );
546
$map_canvas->Tk::bind( '<Key-4>'     , [\&CbPlayerNum, "4"] );
547
$map_canvas->Tk::bind( '<Key-5>'     , [\&CbPlayerNum, "5"] );
548
$map_canvas->Tk::bind( '<Key-6>'     , [\&CbPlayerNum, "6"] );
549
$map_canvas->Tk::bind( '<Key-7>'     , [\&CbPlayerNum, "7"] );
550
$map_canvas->Tk::bind( '<Key-8>'     , [\&CbPlayerNum, "8"] );
551
$map_canvas->Tk::bind( '<Key-9>'     , [\&CbPlayerNum, "9"] );
552
$map_canvas->Tk::bind( '<Key-Left>'  , [\&CbPlayerMove, -1,  0] );
553
$map_canvas->Tk::bind( '<Key-Right>' , [\&CbPlayerMove,  1,  0] );
554
$map_canvas->Tk::bind( '<Key-Up>'    , [\&CbPlayerMove,  0,  1] );
555
$map_canvas->Tk::bind( '<Key-Down>'  , [\&CbPlayerMove,  0, -1] );
556
$map_canvas->Tk::bind( '<Key-Escape>', sub { exit; } );
557
 
558
 
559
 
560
# Mouse button 3 context menu
561
my $map_menu = $map_canvas->Menu('-tearoff' => 0,
562
                                 '-title' =>'None',
563
                                 '-menuitems' =>
564
    [
565
     [Button => $Translate{'WpAddAndSend'},  -command => sub
566
        {
567
        # send Wp to MK         
568
        ($Lat, $Lon) = &MapXY2Gps($Wp_x, $Wp_y);
569
        &MkFlyTo ( '-lat' => $Lat,
570
                   '-lon' => $Lon,
571
                   '-mode' => "Waypoint"
572
                 );
573
 
574
        # Add Wp to Waypoints list
575
        &WpAdd ($MapCanvasX, $MapCanvasY);
576
 
577
        # switch player to Wp mode and redraw waypoints
578
        &WptKmlSwitch ('WPT');  
579
 
580
        $map_status_line->configure ('-text' => "$Translate{'WpSavedAndSent'} -> Lat: $Lat Lon: $Lon");
581
        }],
582
 
583
 
584
     [Button => $Translate{'WpProperties'},  -command => sub
585
        {
586
        # find Wp-Hash for selected icon/tag
587
        my $WpIndex = &WpGetIndexFromId($MapCanvasId);
588
        if ( $WpIndex >= 0 )
589
            {
590
            my $Wp = $Waypoints[$WpIndex];
591
            my $WpNum = $WpIndex + 1;
592
 
593
            &DisplayHash ($Wp, "$Translate{'WpProperties'} $WpNum", "Edit Waypoint Refresh");
594
 
595
            $map_status_line->configure ('-text' => "$Translate{'WpProperties'} $WpNum");
596
            }
597
        }],
598
 
599
     [Button => $Translate{'WpResendAll'},  -command => sub
600
        {
601
        &WpSendAll();
602
 
603
        $map_status_line->configure ('-text' => $Translate{'WpAllSent'});
604
        }],
605
 
606
      '',   # Separator
607
 
608
     [Button => $Translate{'WpLoadAndSend'},  -command => sub
609
        {
610
        my $WpFile = $main->getOpenFile('-defaultextension' => ".xml",
611
                                        '-filetypes'        =>
612
                                            [['Waypoints',     '.xml' ],
613
                                             ['All Files',     '*', ],
614
                                            ],
615
                                        '-initialdir' => $Cfg->{'waypoint'}->{'WpDir'},
616
                                        '-title' => $Translate{'WpLoad'},
617
                                       );
618
        if ( -f $WpFile )
619
            {
620
            &WpLoadFile ($WpFile);
621
 
622
            # send all Wp to MK
623
            &WpSendAll();
624
 
625
            # switch player to Wp mode and redraw waypoints
626
            $PlayerRandomMode  = 'STD';
627
            &WptKmlSwitch ('WPT');
628
 
629
            $map_status_line->configure ('-text' => "$Translate{'WpLoadedAndSent'}: $WpFile");
630
            }
631
        }],    
632
 
633
     [Button => $Translate{'WpSave'},  -command => sub
634
        {
635
        my $WpFile = $main->getSaveFile('-defaultextension' => ".xml",
636
                                        '-filetypes'        =>
637
                                          [['Waypoints',     '.xml' ],
638
                                           ['All Files',     '*', ],
639
                                          ],
640
                                        '-initialdir' => $Cfg->{'waypoint'}->{'WpDir'},
641
                                        '-title' => $Translate{'WpSave'},
642
                                       );
643
 
644
        &WpSaveFile ($WpFile);
645
 
646
        $map_status_line->configure ('-text' => "$Translate{'WpSaved'}: $WpFile");
647
        }],
648
 
649
     '',   # Separator
650
 
651
     [Button => $Translate{'WpDelete'},  -command => sub
652
        {
653
        # find Wp-Hash for selected icon/tag
654
        my $WpIndex = &WpGetIndexFromId($MapCanvasId);
655
        if ( $WpIndex >= 0 )
656
            {
657
            &WpDelete ($WpIndex);
658
 
659
            # redraw connector-lines
660
            $WaypointsModified = 1;
661
            &WpRedrawLines();  
662
            &WpRedrawIcons();  # wg. Wp-Nummern
663
 
664
            my $WpNum = $WpIndex + 1;
665
            $map_status_line->configure ('-text' => "$Translate{'WpDeleted'}: $WpNum");
666
            }
667
        }],
668
 
669
     [Button => $Translate{'WpAllDeleteAndSend'},  -command => sub
670
        {
671
        undef @Waypoints;
672
        $WpPlayerIndex = 0;
673
        $WpPlayerHoldtime = -1;
674
 
675
        # remove all Wp-Icons and Wp-Number on canvas
676
        &WpHide();
677
 
678
        &WpSendAll();
679
 
680
        $map_status_line->configure ('-text' => "$Translate{'WpAllDeleted'}: $WpIndex");
681
        }],
682
 
683
    '',   # Separator
684
 
685
     [Button => $Translate{'KmlLoadAndPlay'},  -command => sub
686
        {
687
        $KmlFile = $main->getOpenFile('-defaultextension' => ".kml",
688
                                     '-filetypes'        =>
689
                                         [['KML',           '.kml' ],
690
                                          ['All Files',     '*', ],
691
                                         ],
692
                                     '-initialdir' => $Cfg->{'waypoint'}->{'KmlDir'},
693
                                     '-title' => $Translate{'KmlLoad'},
694
                                    );
695
        if ( -f $KmlFile )
696
            {
697
            &KmlLoadFile($KmlFile);
698
 
699
            # switch player to KML mode and redraw track
700
            &WptKmlSwitch ('KML');
701
 
702
            $map_status_line->configure ('-text' => "$Translate{'KmlLoaded'}: $KmlFile" );
703
            }
704
 
705
        }],
706
 
707
    '',   # Separator
708
 
709
     [Button => $Translate{'WpFlyImmediately'},  -command => sub
710
        {
711
        &MkFlyTo ( '-x' => $MapCanvasX,
712
                   '-y' => $MapCanvasY,
713
                   '-mode' => "Target"
714
                 );
715
 
716
        # redraw connector-lines
717
        $WaypointsModified = 1;
718
        &WpRedrawLines();  
719
 
720
        $map_status_line->configure ('-text' => "$Translate{'TargetCoordSent'} -> Lat: $Lat  Lon: $Lon     x: $MapCanvasX  y: $MapCanvasY");
721
        }],
722
    ]
723
                                    );
724
$map_canvas->CanvasBind("<Button-3>" => [ sub
725
    {
726
    $map_canvas->focus;
727
    my($w, $x, $y) = @_;
728
    ($MapCanvasX, $MapCanvasY) = ($Tk::event->x, $Tk::event->y);
729
    $MapCanvasId = $map_canvas->find('withtag', 'current');
730
    $map_menu->post($x, $y);
731
    }, Ev('X'), Ev('Y') ] );
732
 
733
 
734
 
735
# Line from MK to Home
736
$map_canvas->createLine ( $MapSizeX/2, $MapSizeY/2, $MapSizeX/2, $MapSizeY/2,
737
                          '-tags' => 'MK-Home-Line',
738
                          '-arrow' => 'none',
739
                          '-fill' => $Cfg->{'mkcockpit'}->{'ColorHomeLine'},
740
                          '-width' => 3,
741
                         );
742
 
743
# Text Entfernung positioniert an der Home-Linie
744
$map_canvas->createText ( $MapSizeX/2 + 8, $MapSizeY/2 - 8,
745
                          '-tags' => 'MK-Home-Dist',
746
                          '-text' => '0 m',
747
                          '-anchor' => 'w',
748
                          '-font' => '-*-Arial-Bold-R-Normal--*-200-*',
749
                          '-fill' => $Cfg->{'mkcockpit'}->{'ColorHomeDist'},
750
                          );
751
 
752
# Line from MK to Target, draw invisible out of sight
753
$map_canvas->createLine ( 0, -100, 0, -100,
754
                          '-tags' => 'MK-Target-Line',
755
                          '-arrow' => 'none',
756
                          '-fill' => $Cfg->{'mkcockpit'}->{'ColorTargetLine'},
757
                          '-width' => 3,
758
                         );
759
 
760
# Text Entfernung positioniert an der Target-Linie
761
$map_canvas->createText ( 0, -100,
762
                          '-tags' => 'MK-Target-Dist',
763
                          '-text' => '0 m',
764
                          '-anchor' => 'w',
765
                          '-font' => '-*-Arial-Bold-R-Normal--*-200-*',
766
                          '-fill' => $Cfg->{'mkcockpit'}->{'ColorTargetDist'},
767
                          );
768
 
769
# MK Geschwindigkeits-Vektor
770
$MapMkSpeedLen = 60;    # Länge Speed-Zeiger
771
my $x0 = $MapSizeX/2;
772
my $y0 = $MapSizeY/2;
773
my $x1 = $MapSizeX/2;
774
my $y1 = $MapSizeY/2 - $MapMkSpeedLen;
775
$map_canvas->createLine ( $x0, $y0, $x1, $y1,
776
                          '-tags' => 'MK-Speed',
777
                          '-arrow' => 'last',
778
                          '-arrowshape' => [10, 10, 3 ],
779
                          '-fill' => $Cfg->{'mkcockpit'}->{'ColorSpeedVector'},
780
                          '-width' => 4,
781
                         );
782
 
783
# MK als Pfeilspitze einer Linie darstellen
784
$MapMkLen = 25;
785
my $x0 = $MapSizeX/2;
786
my $y0 = $MapSizeY/2 + $MapMkLen/2;
787
my $x1 = $MapSizeX/2;
788
my $y1 = $MapSizeY/2 - $MapMkLen/2;
789
$map_canvas->createLine ( $x0, $y0, $x1, $y1,
790
                          '-tags' => 'MK-Arrow',
791
                          '-arrow' => 'last',
792
                          '-arrowshape' => [25, 30, 10 ],
793
                          '-fill' => $Cfg->{'mkcockpit'}->{'ColorMkSatNo'},
794
                          '-width' => 1
795
                         );
796
$MkPos_x = $MapSizeX/2;
797
$MkPos_y = $MapSizeY/2;
798
 
799
 
800
 
801
# OSD Daten auf Karte anzeigen
802
 
803
my @Texts = (
804
            # Tag                 Text        Pos_x          Pos_y  Font
805
            'MK-OSD-Tim-Label',   "TIM",      $MapSizeX/2 - 40, 20, '-*-Arial-Bold-R-Normal--*-150-*',
806
            'MK-OSD-Tim-Value',   "00:00",    $MapSizeX/2,      20, '-*-Arial-Bold-R-Normal--*-270-*',
807
            'MK-OSD-Bat-Label',   "BAT",      $MapSizeX/2 - 40, 50, '-*-Arial-Bold-R-Normal--*-150-*',
808
            'MK-OSD-Bat-Value',   "0.0 V",    $MapSizeX/2,      50, '-*-Arial-Bold-R-Normal--*-270-*',
809
            'MK-OSD-Spd-Label',   "SPD",      10,               20, '-*-Arial-Bold-R-Normal--*-150-*',
810
            'MK-OSD-Spd-Value',   "0.0 km/h", 60,               20, '-*-Arial-Bold-R-Normal--*-270-*',
811
            'MK-OSD-Alt-Label',   "ALT",      10,               50, '-*-Arial-Bold-R-Normal--*-150-*',
812
            'MK-OSD-Alt-Value',   "0 m",      60,               50, '-*-Arial-Bold-R-Normal--*-270-*',
813
            'MK-OSD-Odo-Label',   "ODO",      10,               80, '-*-Arial-Bold-R-Normal--*-150-*',
814
            'MK-OSD-Odo-Value',   "0.000 km", 60,               80, '-*-Arial-Bold-R-Normal--*-270-*',
815
            'MK-OSD-Sat-Label',   "SAT",      $MapSizeX - 230,  20, '-*-Arial-Bold-R-Normal--*-150-*',
816
            'MK-OSD-Sat-Value',   "0",        $MapSizeX - 180,  20, '-*-Arial-Bold-R-Normal--*-270-*',
817
            'MK-OSD-Wp-Label',    "WPT",      $MapSizeX - 230,  50, '-*-Arial-Bold-R-Normal--*-150-*',
818
            'MK-OSD-Wp-Value',    "0 / 0",    $MapSizeX - 180,  50, '-*-Arial-Bold-R-Normal--*-270-*',
819
            'MK-OSD-Mode-Label',  "MOD",      $MapSizeX - 230,  80, '-*-Arial-Bold-R-Normal--*-150-*',
820
            'MK-OSD-Mode-Value',  "",         $MapSizeX - 180,  80, '-*-Arial-Bold-R-Normal--*-270-*',
821
            );
822
 
823
my $i = 0;
824
for $Text (0 .. $#Texts/5)
825
    {
826
    my $Tag =   $Texts[$i++];
827
    my $Text =  $Texts[$i++];
828
    my $Pos_x = $Texts[$i++];
829
    my $Pos_y = $Texts[$i++];
830
    my $Font =  $Texts[$i++];
831
 
832
    $map_canvas->createText ( $Pos_x, $Pos_y,
833
                              '-tags' => $Tag,
834
                              '-text' => $Text,
835
                              '-font' => $Font,
836
                              '-fill' => $Cfg->{'mkcockpit'}->{'ColorOsd'},
837
                              '-anchor' => 'w',
838
                             );
839
 
840
    }
841
 
842
 
843
# Variometer on canvas
844
my @Polygon;
845
for ( $y = -100; $y <= 100; $y += 10)
846
    {
847
    my $Len = 5;
848
    if ( ($y % 50) == 0 )
849
        {
850
        $Len = 10;
851
        $map_canvas->createText ( $Len+5, $MapSizeY/2 + $y,
852
                                  '-tags' => 'Map-Variometer-Skala',
853
                                  '-text' => sprintf ("%3d", -$y / 10),
854
                                  '-anchor' => 'w',
855
                                  '-font' => '-*-Arial-Normal-R-Normal--*-150-*',
856
                          '-fill' => $Cfg->{'mkcockpit'}->{'ColorVariometer'},
857
                          );
858
        }
859
    push @Polygon, (   0, $MapSizeY/2 + $y);
860
    push @Polygon, ($Len, $MapSizeY/2 + $y);
861
    push @Polygon, (   0, $MapSizeY/2 + $y);
862
    }
863
 
864
$map_canvas->createLine(@Polygon,
865
                        '-tags' => 'Map-Variometer',
866
                        '-fill' => $Cfg->{'mkcockpit'}->{'ColorVariometer'},
867
                        '-width' => 2,
868
                        '-arrow' => 'none',
869
                       );
870
# Vario Pointer
871
$map_canvas->createPolygon( 5, $MapSizeY/2, 20, $MapSizeY/2+10, 20, $MapSizeY/2-10,
872
                           '-tags' => 'Map-Variometer-Pointer',
873
                           '-fill' => $Cfg->{'mkcockpit'}->{'ColorVariometerPointer'},
874
                           '-outline' => 'black', '-width' => 1,
875
                          );
876
 
877
# Tracking Canvas
878
 
879
if ( $Cfg->{'track'}->{'Active'} =~ /y/i )
880
    {
881
    # Canvas size
882
    $TrackSizeX  = 125;
883
    $TrackSizeY  = 100;
884
    $TrackOffY   = $TrackSizeY - $MapSizeY + 20;
885
    $TrackPtrLen = 50;    # Länge Zeiger
886
 
887
    # draw in map-canvas
888
    $track_canvas = $map_canvas;
889
 
890
    # Ziffernblatt
891
    my $x0 = $TrackSizeX/2 - $TrackPtrLen;
892
    my $y0 = $TrackSizeY + $TrackPtrLen - $TrackOffY;
893
    my $x1 = $TrackSizeX/2 + $TrackPtrLen;
894
    my $y1 = $TrackSizeY   - $TrackPtrLen - $TrackOffY;
895
    $track_canvas->createArc ( $x0, $y0, $x1, $y1,
896
                               '-extent' => '200',
897
                               '-start' => '-10',
898
                               '-style' => 'chord',
899
                               '-outline' => 'gray', '-width' => '1',
900
                             );
901
 
902
    # Skala Ziffernblatt
903
    for ($i=0; $i<=180; $i+=15)
904
        {
905
        my $pi = 3.14159265358979;
906
        my $x0 = $TrackSizeX/2 - ($TrackPtrLen - 20) * cos($i / 180 * $pi);
907
        my $y0 = $TrackSizeY   - ($TrackPtrLen - 20) * sin($i / 180 * $pi) - $TrackOffY;
908
        my $x1 = $TrackSizeX/2 - ($TrackPtrLen - 28) * cos($i / 180 * $pi);
909
        my $y1 = $TrackSizeY   - ($TrackPtrLen - 28) * sin($i / 180 * $pi) - $TrackOffY;
910
        $track_canvas->createLine ( $x0, $y0, $x1, $y1,
911
                                   '-fill' => 'white',
912
                                   '-width' => 1,
913
                                  );
914
        }
915
 
916
    # Skala Beschriftung Ziffernblatt
917
    for ($i=0; $i<=180; $i+=45)
918
        {
919
        my $pi = 3.14159265358979;
920
        my $x0 = $TrackSizeX/2 - ($TrackPtrLen - 12) * cos($i / 180 * $pi);
921
        my $y0 = $TrackSizeY   - ($TrackPtrLen - 12) * sin($i / 180 * $pi) - $TrackOffY;
922
        $track_canvas->createText ( $x0, $y0,
923
                                   '-text' => $i - 90,
924
                                   '-fill' => 'white',
925
                                  );
926
        }
927
 
928
    # Ziffernblatt Beschriftung Einheit
929
    my $x0 = $TrackSizeX/2;
930
    my $y0 = $MapSizeY -6;
931
    $track_canvas->createText ( $x0, $y0,
932
                                '-text' => "Antenne Winkel",
933
                                '-justify' => 'center',
934
                                '-fill' => 'white',
935
                                );
936
 
937
    # Zeiger
938
    my $x0 = $TrackSizeX/2;
939
    my $y0 = $TrackSizeY - 0 - $TrackOffY;
940
    my $x1 = $TrackSizeX/2;
941
    my $y1 = $TrackSizeY - ($TrackPtrLen - 22) - $TrackOffY;
942
    $track_ptr_id= $track_canvas->createLine ( $x0, $y0, $x1, $y1,
943
                                               '-tags' => 'Track-Ptr',
944
                                               '-arrow' => 'last',
945
                                               '-arrowshape' => [20, 30, 5 ],
946
                                               '-fill' => 'red',
947
                                               '-width' => 8,
948
                                              );
949
    # Zeiger Center
950
    my $Dia = 7;
951
    my $x0 = $TrackSizeX/2 - $Dia;
952
    my $y0 = $TrackSizeY + $Dia - $TrackOffY;
953
    my $x1 = $TrackSizeX/2 + $Dia;
954
    my $y1 = $TrackSizeY   - $Dia - $TrackOffY;
955
    $track_canvas->createArc ( $x0, $y0, $x1, $y1,
956
                               '-extent' => '359',
957
                               '-outline' => 'gray', '-width' => 1,
958
                               '-fill' => 'gray',
959
                             );
960
    }
961
 
962
#-----------------------------------------------------------------
963
# Timer
964
#-----------------------------------------------------------------                        
965
 
966
#
967
# Timer: 5s
968
#
969
$main->repeat (5000, sub
970
    {
971
    if ( ! $MkSendWp )
972
        {
973
        # Abfragefrequenz OSD und Debug regelmäßig neu einstellen, falls Übertragungsfehler
974
        $MkSendQueue->enqueue( "o", "$AddrNC", pack ("C", 10) );   # Frequenz OSD Datensatz, * 10ms
975
        $MkSendQueue->enqueue( "d", "$AddrNC", pack ("C", 10) );   # Frequenz MK Debug Datensatz, * 10ms
976
        $MkSendQueue->enqueue( "v", "$AddrNC", "");   # Version
977
        $MkSendQueue->enqueue( "e", "$AddrNC", "");   # Error Text Request
978
        }
979
    });
980
 
981
 
982
#       
983
# Timer: 0.1s - Map Overlay aktualisieren
984
#
985
$frame_map_top->repeat (100, sub
986
    {
987
 
988
    # Clear old messages from this timer
989
    &MkMessageInit ("Timer-MapOverlay");
990
 
991
    lock (%MkOsd);              # until end of block
992
 
993
    # Aktuell gültige Karte
994
    my %Map = %{$Maps{'Current'}};
995
 
996
    if ( $MkOsd{'_Timestamp'} >= time-2 )
997
        {
998
        # Gueltige OSD Daten
999
 
1000
        my $SatsInUse = $MkOsd{'SatsInUse'};
1001
        if ( $SatsInUse > 0  and  $MkOsd{'CurPos_Stat'} == 1 and $MkOsd{'HomePos_Stat'} == 1 )
1002
            {
1003
            # ausreichender GPS Empfang
1004
 
1005
            # get x,y map coords of current position
1006
            my ($C_x, $C_y, $C_Angel) = &MapGps2XY($MkOsd{'CurPos_Lat'}, $MkOsd{'CurPos_Lon'}, $MkOsd{'CompassHeading'});
1007
            $MkPos_x = $C_x;
1008
            $MkPos_y = $C_y;
1009
 
1010
            # rotate MK arrow
1011
            my $dy = sin (deg2rad $C_Angel) * ($MapMkLen/2);
1012
            my $dx = cos (deg2rad $C_Angel) * ($MapMkLen/2);
1013
            my $x0 = $C_x - $dx;
1014
            my $y0 = $C_y - $dy;
1015
            my $x1 = $C_x + $dx;
1016
            my $y1 = $C_y + $dy;
1017
            $map_canvas->coords ('MK-Arrow', $x0, $y0, $x1, $y1);
1018
 
1019
            # Update speed vector
1020
            my $MapAngel = &MapAngel();   # Norh to Map-Horizont
1021
            my $GpsSpeedNorth = $MkNcDebug{'Analog_21'};
1022
            my $GpsSpeedEast  = $MkNcDebug{'Analog_22'};
1023
            my $PhiGpsSpeed = rad2deg atan2 ( $GpsSpeedEast, $GpsSpeedNorth );
1024
            $PhiMapSpeed = $PhiGpsSpeed - $MapAngel;
1025
 
1026
            # 555 cm/s ~ 20 km/h -> Zeigerlänge = $MkSpeedLen bei 20 km/h
1027
            my $dy = sin (deg2rad $PhiMapSpeed) * $MapMkSpeedLen * $MkOsd{'GroundSpeed'} / 555;
1028
            my $dx = cos (deg2rad $PhiMapSpeed) * $MapMkSpeedLen * $MkOsd{'GroundSpeed'} / 555;
1029
            my $x0 = $C_x;
1030
            my $y0 = $C_y;
1031
            my $x1 = $C_x + $dx;
1032
            my $y1 = $C_y + $dy;
1033
            $map_canvas->coords ('MK-Speed', $x0, $y0, $x1, $y1);
1034
 
1035
            # Update Line between Home and MK
1036
            my ($H_x, $H_y) = &MapGps2XY($MkOsd{'HomePos_Lat'}, $MkOsd{'HomePos_Lon'});
1037
            $map_canvas->coords ('MK-Home-Line', $H_x, $H_y, $C_x, $C_y);
1038
 
1039
            # Update Distance between Home and MK
1040
            my ($Dist, $Bearing) = MapGpsTo($MkOsd{'CurPos_Lat'}, $MkOsd{'CurPos_Lon'},
1041
                                                   $MkOsd{'HomePos_Lat'}, $MkOsd{'HomePos_Lon'} );
1042
            my $x = ($C_x - $H_x) / 2 + $H_x + 8;
1043
            my $y = ($C_y - $H_y) / 2 + $H_y + 8;
1044
            $map_canvas->coords ('MK-Home-Dist', $x, $y);
1045
            $map_canvas->itemconfigure ('MK-Home-Dist',
1046
                                        '-text' => sprintf ("%4d m", int ($Dist + 0.5) ),
1047
                                       );
1048
 
1049
            # Update OSD - Sat dependent values
1050
            $map_canvas->itemconfigure ('MK-OSD-Spd-Value', '-text' => sprintf ("%3d km/h", $MkOsd{'GroundSpeed'} * 0.036) );
1051
 
1052
            # Alt = average Luftdruck und Sat
1053
            my $Alt = int ( ($MkOsd{'Altimeter'} / $Cfg->{'mkcockpit'}->{'AltFactor'} +
1054
                             $MkOsd{'CurPos_Alt'} - $MkOsd{'HomePos_Alt'} ) / 2 + 0.5 );
1055
            $map_canvas->itemconfigure ('MK-OSD-Alt-Value', '-text' => sprintf ("%3d m", $Alt) );
1056
 
1057
            if ( $MkOsd{'TargetPos_Stat'} == 1 )
1058
                {
1059
                # Valid Target
1060
                # Update Line between Target and MK
1061
                my ($T_x, $T_y) = &MapGps2XY($MkOsd{'TargetPos_Lat'}, $MkOsd{'TargetPos_Lon'});
1062
                $map_canvas->coords ('MK-Target-Line', $C_x, $C_y, $T_x, $T_y);
1063
 
1064
                # Update Distance between Target and MK
1065
                my ($Dist, $Bearing) = MapGpsTo($MkOsd{'CurPos_Lat'}, $MkOsd{'CurPos_Lon'},
1066
                                                        $MkOsd{'TargetPos_Lat'}, $MkOsd{'TargetPos_Lon'} );
1067
 
1068
                if ( $Dist >= 25 )  
1069
                    {
1070
                    my $x = ($C_x - $T_x) / 2 + $T_x - 8;
1071
                    my $y = ($C_y - $T_y) / 2 + $T_y + 8;
1072
                    $map_canvas->coords ('MK-Target-Dist', $x, $y);
1073
                    $map_canvas->itemconfigure ('MK-Target-Dist',
1074
                                                '-text' => sprintf ("%4d m", int ($Dist + 0.5) ),
1075
                                               );
1076
                    }
1077
                else
1078
                    {
1079
                    # Don't show distance < 25m
1080
                    $map_canvas->coords ('MK-Target-Dist', 0, -100);
1081
                    }
1082
 
1083
                # show target icon
1084
                my $IconHeight = 48;
1085
                my $IconWidth = 48;
1086
                $map_canvas->coords('Target', $T_x - $IconWidth/2, $T_y - $IconHeight );
1087
 
1088
                if ( &IsCrossingBorder($MkPos_x, $MkPos_y, $T_x, $T_y) )
1089
                    {
1090
                    &MkMessage ($Translate{'MsgCrossingBorder'}, "Timer-MapOverlay");
1091
                    }
1092
                }
1093
            else
1094
                {
1095
                # No valid Target, move target line out of sight/canvas
1096
                $map_canvas->coords ('MK-Target-Line', 0, -100, 0, -100);
1097
                $map_canvas->coords ('MK-Target-Dist', 0, -100);
1098
 
1099
                # hide target icon
1100
                $map_canvas->coords('Target', 0, -100, );
1101
                }
1102
            }
1103
        else
1104
            {
1105
            # kein ausreichender Sat-Empfang
1106
            $map_canvas->itemconfigure ('MK-OSD-Spd-Value', '-text' => sprintf ("%3d km/h", 0 ) );
1107
            }
1108
 
1109
        # Update OSD - non Sat dependent values
1110
        $map_canvas->itemconfigure ('MK-OSD-Bat-Value', '-text' => sprintf ("%3.1f V", $MkOsd{'UBat'}) );
1111
        $map_canvas->itemconfigure ('MK-OSD-Vsi-Value', '-text' => sprintf ("%3d", $MkOsd{'Variometer'}) );
1112
        $map_canvas->itemconfigure ('MK-OSD-Odo-Value', '-text' => sprintf ("%3.3f km", $OdoMeter / 1000) );
1113
        $map_canvas->itemconfigure ('MK-OSD-Tim-Value', '-text' => sprintf ("%02d:%02d", $MkFlyingTime / 60, $MkFlyingTime % 60) );
1114
        $map_canvas->itemconfigure ('MK-OSD-Sat-Value', '-text' => $MkOsd{'SatsInUse'} );
1115
 
1116
        # blink battery warning
1117
        $map_canvas->itemconfigure ('MK-OSD-Bat-Value', '-fill' => $Cfg->{'mkcockpit'}->{'ColorOsd'});
1118
        if ( $MkOsd{'UBat'}  <  $Cfg->{'mkcockpit'}->{'UBatWarning'} )
1119
            {
1120
            if ( time %2 )
1121
                {
1122
                $map_canvas->itemconfigure ('MK-OSD-Bat-Value', '-fill' => 'red');
1123
                }
1124
 
1125
            &MkMessage ($Translate{'MsgBatWarning'}, "Timer-MapOverlay");
1126
            }
1127
 
1128
 
1129
        # Operation Mode
1130
        my $Mode = "";
1131
        if ($MkOsd{'NCFlags'} & 0x04) { $Mode = "WPT"; }
1132
        if ($PlayerMode eq "Play")    { $Mode = "Play"; }
1133
        if ($PlayerMode eq "Pause")   { $Mode = "Paus"; }
1134
        if ($PlayerMode eq "Home")    { $Mode = "Home"; }
1135
        if ($MkOsd{'NCFlags'} & 0x02) { $Mode = "PH"; }
1136
        if ($MkOsd{'NCFlags'} & 0x01) { $Mode = "Free"; }
1137
 
1138
        # Display Operation Mode
1139
        my $DisplayMode = $Mode;
1140
        if ( ($MkOsd{'NCFlags'} & 0x04)  and  $Mode eq "Play" )
1141
            {
1142
            my %ModeMatrix =
1143
               (
1144
               "KML-STD" => "Play KML",
1145
               "KML-RND" => "Play KML",
1146
               "KML-MAP" => "Play KML",
1147
               "WPT-STD" => "Play WPT",
1148
               "WPT-RND" => "Rand WPT",
1149
               "WPT-MAP" => "Rand MAP",
1150
               );
1151
            my $Key = "${PlayerWptKmlMode}-${PlayerRandomMode}";
1152
            $DisplayMode = $ModeMatrix{$Key};
1153
            }
1154
        if ($MkOsd{'NCFlags'} & 0x08)
1155
            {
1156
            $DisplayMode  = "$DisplayMode" . " !!";   # Range Warning
1157
            }
1158
        $map_canvas->itemconfigure ('MK-OSD-Mode-Value', '-text' => $DisplayMode );
1159
 
1160
 
1161
        # Waypoints abhaengig vom Modus NC/Player
1162
        my $WpValue = "--/--";
1163
        if ( $MkOsd{'WaypointNumber'} > 0)
1164
            {
1165
            $WpValue = sprintf ("%d / %d", $MkOsd{'WaypointIndex'} + 1, $MkOsd{'WaypointNumber'});
1166
            }
1167
        if ($PlayerMode ne "Stop" and $PlayerWptKmlMode eq "WPT")
1168
            {
1169
            $WpValue = sprintf ("%d / %d", $WpPlayerIndex +1, scalar @Waypoints);
1170
            }
1171
        if ($PlayerMode ne "Stop" and $PlayerWptKmlMode eq "KML" )
1172
            {
1173
            my $KmlTimeBase = $Cfg->{'waypoint'}->{'KmlTimeBase'} || 1.0;
1174
            my $CurrTime = int ($KmlPlayerIndex * $KmlTimeBase + 0.5);
1175
            my $TotTime = int (scalar @KmlTargets * $KmlTimeBase + 0.5);
1176
            $WpValue = sprintf ("%02d:%02d / %02d:%02d", $CurrTime / 60, $CurrTime % 60, $TotTime / 60, $TotTime % 60);
1177
            }
1178
        $map_canvas->itemconfigure ('MK-OSD-Wp-Value',  '-text' => "$WpValue");
1179
 
1180
        # Farbe MK-Zeiger abhängig vom GPS Empfang
1181
        my $MkCol= $Cfg->{'mkcockpit'}->{'ColorMkSatNo'};
1182
        if ( $SatsInUse >= 1 ) { $MkCol = $Cfg->{'mkcockpit'}->{'ColorMkSatLow'} ; }
1183
        if ( $SatsInUse >= 6 ) { $MkCol = $Cfg->{'mkcockpit'}->{'ColorMkSatGood'}; }
1184
        $map_canvas->itemconfigure ('MK-Arrow', '-fill' => $MkCol);
1185
 
1186
 
1187
        # Show/Hide SatFix Icon
1188
        if ($SatsInUse >= 6 )
1189
            {
1190
            $map_canvas->coords('Satellite', $MapSizeX-300, 10, );
1191
            }
1192
        else
1193
            {
1194
            # move icon out of sight
1195
            $map_canvas->coords('Satellite', 0, -100, );
1196
            }
1197
 
1198
 
1199
        # Variometer Pointer
1200
        my $dy = -$MkOsd{'Variometer'} * 10;
1201
        $map_canvas->coords('Map-Variometer-Pointer', 5, $MapSizeY/2+$dy, 20, $MapSizeY/2+10+$dy, 20, $MapSizeY/2-10+$dy);
1202
 
1203
        #
1204
        # System checks
1205
        #
1206
 
1207
        if (($MkOsd{'MKFlags'} & 0x01) == 0) { &MkMessage ($Translate{'MsgMotorOff'}, "Timer-MapOverlay"); }
1208
        if (($MkOsd{'MKFlags'} & 0x02) == 0) { &MkMessage ($Translate{'MsgNotFlying'}, "Timer-MapOverlay"); }
1209
        if ($MkOsd{'MKFlags'} & 0x04 )       { &MkMessage ($Translate{'MsgCalibrate'}, "Timer-MapOverlay"); }
1210
        if ($MkOsd{'MKFlags'} & 0x08 )       { &MkMessage ($Translate{'MsgStart'}, "Timer-MapOverlay") }
1211
        if ($MkOsd{'MKFlags'} & 0x10 )       { &MkMessage ($Translate{'MsgEmergencyLanding'}, "Timer-MapOverlay"); }
1212
        if ($MkOsd{'NCFlags'} & 0x08)        { &MkMessage ($Translate{'MsgRangeLimit'}, "Timer-MapOverlay"); }
1213
 
1214
        # RC range check
1215
        my $RcQuality = $MkOsd{'RC_Quality'};
1216
        if ( $RcQuality < 100 )
1217
            {
1218
            &MkMessage ($Translate{'MsgRcError'}, "Timer-MapOverlay");
1219
            }
1220
        elsif ( $RcQuality < 150 )
1221
            {
1222
            &MkMessage ($Translate{'MsgRcWarning'}, "Timer-MapOverlay");
1223
            }
1224
 
1225
        # Sat reception quality
1226
        if ( $SatsInUse == 0 )
1227
            {
1228
            &MkMessage ($Translate{'MsgNoSatReception'}, "Timer-MapOverlay");
1229
            }
1230
        elsif ( $SatsInUse > 0  and $SatsInUse < 6 )
1231
            {
1232
            &MkMessage ($Translate{'MsgWeakSatReception'}, "Timer-MapOverlay");
1233
            }
1234
 
1235
        # MK Border check
1236
        if ( ! &IsInsideBorder($MkPos_x, $MkPos_y) )
1237
            {
1238
            &MkMessage ($Translate{'MsgOutsideBorder'}, "Timer-MapOverlay");
1239
            }
1240
 
1241
 
1242
        #
1243
        # Show Balloon, when aproaching Target
1244
        #
1245
 
1246
        $map_canvas->delete('Target-Balloon');  # delete old Balloon
1247
 
1248
 
1249
        if ( $Mode ne "Free" and $MkOsd{'TargetPos_Stat'} == 1  and $MkOsd{'TargetPosDev_Dist'} /10 < 25 )
1250
            {
1251
            my $BalloonLines = 1;
1252
            $ColorBalloon = "blue";
1253
            my ($T_x, $T_y) = &MapGps2XY($MkOsd{'TargetPos_Lat'}, $MkOsd{'TargetPos_Lon'});
1254
            my $Wp = $Waypoints[$MkOsd{'WaypointIndex'}];
1255
 
1256
            # Holdtime Wp-Player Mode
1257
            if ( $WpPlayerHoldtime >= 0 )
1258
                {
1259
                # Holdtime
1260
                $ColorBalloon = 'red';
1261
                my $HoldTime = sprintf ("%5s %3d s", "HLD:", int ($WpPlayerHoldtime / 2  + 0.5) );
1262
                $map_canvas->createText ( $T_x + 25, $T_y - 40,
1263
                                          '-tags' => ['Target-Balloon', 'Target-BalloonText'],
1264
                                          '-text' => $HoldTime,
1265
                                          '-font' => '-*-Arial-Bold-R-Normal--*-200-*',
1266
                                          '-fill' => $ColorBalloon,
1267
                                          '-anchor' => 'w',
1268
                                        );
1269
                $BalloonLines ++;
1270
                }
1271
 
1272
            # Holdtime WPT-Mode
1273
            if ( $MkOsd{'NCFlags'} & 0x20  and  $Mode eq "WPT" )
1274
                {
1275
                # Holdtime from MK
1276
                $ColorBalloon = 'red';
1277
                my $HoldTime = sprintf ("%5s %3d s", "HLD:", int ($MkOsd{'TargetHoldTime'} + 0.5) );
1278
                $map_canvas->createText ( $T_x + 25, $T_y - 40,
1279
                                          '-tags' => ['Target-Balloon', 'Target-BalloonText'],
1280
                                          '-text' => $HoldTime,
1281
                                          '-font' => '-*-Arial-Bold-R-Normal--*-200-*',
1282
                                          '-fill' => $ColorBalloon,
1283
                                          '-anchor' => 'w',
1284
                                        );
1285
                $BalloonLines ++;
1286
                }
1287
 
1288
 
1289
            # Tolerance Radius Player Mode
1290
            if ( $MkOsd{'NCFlags'} & 0x04  and  $Mode eq "Play" and $PlayerWptKmlMode eq "WPT" )
1291
                {
1292
                my $WpTolerance  = sprintf ("%5s %3d m", "TOL:", $Wp->{'ToleranceRadius'});
1293
                $map_canvas->createText ( $T_x + 25, $T_y - 60,
1294
                                          '-tags' => ['Target-Balloon', 'Target-BalloonText'],
1295
                                          '-text' => $WpTolerance,
1296
                                          '-font' => '-*-Arial-Bold-R-Normal--*-200-*',
1297
                                          '-fill' => $ColorBalloon,
1298
                                          '-anchor' => 'w',
1299
                                        );
1300
                $BalloonLines ++;
1301
                }
1302
 
1303
 
1304
            # Tolerance WPT-Mode
1305
            if ( $MkOsd{'NCFlags'} & 0x04  and  $Mode eq "WPT" )
1306
                {
1307
                my $WpTolerance  = sprintf ("%5s %3d m", "TOL:", $Wp->{'ToleranceRadius'} );
1308
                $map_canvas->createText ( $T_x + 25, $T_y - 60,
1309
                                          '-tags' => ['Target-Balloon', 'Target-BalloonText'],
1310
                                          '-text' => $WpTolerance,
1311
                                          '-font' => '-*-Arial-Bold-R-Normal--*-200-*',
1312
                                          '-fill' => $ColorBalloon,
1313
                                          '-anchor' => 'w',
1314
                                        );
1315
                $BalloonLines ++;
1316
                }
1317
 
1318
            # Distance to Target
1319
            my $Dist = int ($MkOsd{'TargetPosDev_Dist'} /10 + 0.5);
1320
            $map_canvas->createText ( $T_x + 25, $T_y - 80,
1321
                                      '-tags' => ['Target-Balloon', 'Target-BalloonText'],
1322
                                      '-text' => sprintf ("%5s %3d m", "DST:", $Dist) ,
1323
                                      '-font' => '-*-Arial-Bold-R-Normal--*-200-*',
1324
                                      '-fill' => $ColorBalloon,
1325
                                      '-anchor' => 'w',
1326
                                    );
1327
 
1328
            if ( $BalloonLines >= 1 )
1329
                {
1330
                # draw Balloon
1331
                my @TargetBalloon = ( $T_x ,      $T_y,
1332
                                      $T_x + 30,  $T_y - (3 - $BalloonLines) * 20 -27,
1333
                                      $T_x + 150, $T_y - (3 - $BalloonLines) * 20 -27 ,
1334
                                      $T_x + 150, $T_y - 93,
1335
                                      $T_x + 20,  $T_y - 93,
1336
                                      $T_x + 20,  $T_y - (3 - $BalloonLines) * 20 -27,
1337
                                      $T_x,       $T_y,
1338
                                    );
1339
 
1340
                $map_canvas->createPolygon( @TargetBalloon,
1341
                                           '-tags' => ['Target-Balloon', 'Target-BalloonBubble'],
1342
                                           '-fill' => 'lightgray',
1343
                                           '-outline' => 'yellow',
1344
                                           '-width' => 1,
1345
                                          );
1346
                }
1347
 
1348
 
1349
            $map_canvas->lower('Target-Balloon', 'MK-Home-Line');
1350
            $map_canvas->lower('Target-BalloonBubble', 'Target-BalloonText');
1351
            }
1352
        }
1353
    else
1354
        {
1355
        # keine aktuellen OSD Daten vom MK verfügbar
1356
        &MkMessage ($Translate{'MsgNoData'}, "Timer-MapOverlay");
1357
        }
1358
 
1359
 
1360
    # Wp-Number input from keyboard
1361
    $KbTimer++;
1362
    if ( $CbPlayerKey ne "" )
1363
        {
1364
        # Key pressed
1365
        $KbNum = "$KbNum" . "$CbPlayerKey";
1366
 
1367
        $CbPlayerKey = "";
1368
        $KbTimer = 0;
1369
        }
1370
    if ( $KbTimer > 7  and $KbNum ne "" )
1371
        {
1372
        # number complete, set target
1373
        my $WpIndex = sprintf ("%d", $KbNum);
1374
        &WpTargetSet ($WpIndex - 1);
1375
 
1376
        # prepare for next number
1377
        $KbNum = "";
1378
        }
1379
 
1380
 
1381
    # Show System Messages
1382
    &MkMessageShow();
1383
 
1384
    });
1385
 
1386
#       
1387
# Timer: 0.1s - Tracking Anzeige aktualisieren
1388
#
1389
if ( $Cfg->{'track'}->{'Active'} =~ /y/i )
1390
    {
1391
    $frame_map_top->repeat (100, sub
1392
        {
1393
        # Clear old messages from this timer
1394
        &MkMessageInit ("Timer-Tracking");    
1395
 
1396
        lock (%MkOsd);              # until end of block
1397
 
1398
        # Aktuell gültige Karte
1399
        my %Map = %{$Maps{'Current'}};
1400
 
1401
        # Zeiger neu zeichnen
1402
        my $ServoPan = @ServoPos[$MkTrack{'ServoPan'}];
1403
        if ( $ServoPan ne ""  )
1404
            {
1405
            my $x0 = $TrackSizeX/2;    
1406
            my $y0 = $TrackSizeY - 0 - $TrackOffY;
1407
            my $x1 = $TrackSizeX/2 - ($TrackPtrLen-22) * cos( deg2rad $ServoPan);
1408
            my $y1 = $TrackSizeY   - ($TrackPtrLen-22) * sin (deg2rad $ServoPan) - $TrackOffY;
1409
            $track_canvas->coords ('Track-Ptr', $x0, $y0, $x1, $y1);
1410
            }
1411
 
1412
        # Farbe Zeiger abhängig vom GPS Empfang
1413
        my $SatsInUse = $MkOsd{'SatsInUse'};
1414
        my $TrackPtrCol= 'red';
1415
        if ( $SatsInUse >= 1 ) { $TrackPtrCol = 'orange'; }
1416
        if ( $SatsInUse >= 6 ) { $TrackPtrCol = 'green'; }
1417
        $track_canvas->itemconfigure ('Track-Ptr', '-fill' => $TrackPtrCol);
1418
        });
1419
    }
1420
 
1421
 
1422
#       
1423
# Timer: 0.5s - Waypoint Player
1424
#
1425
$frame_map_top->repeat (500, sub
1426
    {
1427
    # Clear old messages from this timer
1428
    &MkMessageInit ("Timer-Player");        
1429
 
1430
    lock (%MkOsd);              # until end of block
1431
 
1432
    if ($MkOsd{'NCFlags'} & 0x04)
1433
        {
1434
        # NC is in WPT Mode 
1435
 
1436
        if ( $PlayerMode eq "Pause" )
1437
            {
1438
            if ( $PlayerPause_Lat ne ""  and  $PlayerPause_Lon ne "" )
1439
                {
1440
                # Gespeicherte Pausen-Pos senden
1441
                &MkFlyTo ( '-lat'  => $PlayerPause_Lat,
1442
                           '-lon'  => $PlayerPause_Lon,
1443
                           '-holdtime' => "60",
1444
                           '-mode' => "Target",
1445
                         );
1446
                }
1447
            }
1448
 
1449
        if ( $PlayerMode eq "Home" )
1450
            {
1451
            if ( $MkOsd{'HomePos_Stat'} == 1 )
1452
                {
1453
                # Gespeicherte Home-Pos senden
1454
                &MkFlyTo ( '-lat'  => $MkOsd{'HomePos_Lat'},
1455
                           '-lon'  => $MkOsd{'HomePos_Lon'},
1456
                           '-holdtime' => "60",
1457
                           '-mode' => "Target",
1458
                         );
1459
                }
1460
            }
1461
 
1462
 
1463
        if ( $PlayerWptKmlMode ne 'WPT' )
1464
            {
1465
            # not in Wp mode
1466
            return;
1467
            }
1468
 
1469
 
1470
        if ( $PlayerMode eq "Play"  )
1471
            {
1472
 
1473
            if ( $PlayerRandomMode =~ /RND/i  or $PlayerRandomMode =~ /STD/i )
1474
                {
1475
                my $WpCnt = scalar @Waypoints;
1476
                if ( $WpCnt > 0  and  $WpPlayerIndex < $WpCnt )
1477
                    {
1478
                    # Target WP-Pos senden
1479
                    my $Wp = $Waypoints[$WpPlayerIndex];
1480
                    my $Wp_Lon = $Wp->{'Pos_Lon'};
1481
                    my $Wp_Lat = $Wp->{'Pos_Lat'};
1482
                    if ( $Wp_Lat ne ""  and  $Wp_Lon ne "" )
1483
                        {
1484
                        &MkFlyTo ( '-lat'  => $Wp_Lat,
1485
                                   '-lon'  => $Wp_Lon,
1486
                                   '-holdtime' => "60",
1487
                                   '-mode' => "Target",
1488
                                 );
1489
                        }
1490
                    }
1491
                }
1492
 
1493
            if ( $PlayerRandomMode =~ /MAP/i )
1494
                {
1495
                # Target Map-Pos senden
1496
                &MkFlyTo ( '-x'  => $RandomTarget_x ,
1497
                           '-y'  => $RandomTarget_y ,
1498
                           '-holdtime' => "60",
1499
                           '-mode' => "Target",
1500
                         );
1501
                }
1502
 
1503
            # Ziel erreicht?
1504
            if ( &WpCheckTargetReached() )
1505
                {
1506
                &WpTargetNext();
1507
                }
1508
            }
1509
        }
1510
 
1511
    # WP Player Holdtime count down
1512
    if ( $WpPlayerHoldtime > 0  )
1513
        {
1514
        $WpPlayerHoldtime --;
1515
        }
1516
    });
1517
 
1518
 
1519
#       
1520
# Timer: variabel - KML Player
1521
#
1522
my $KmlTimeBase = $Cfg->{'waypoint'}->{'KmlTimeBase'} || 1.0;
1523
$KmlTimeBase *= 1000;
1524
 
1525
$frame_map_top->repeat ($KmlTimeBase, sub
1526
    {
1527
 
1528
    # Clear old messages from this timer
1529
    &MkMessageInit ("Timer-KMLPlayer");    
1530
 
1531
    if ( $PlayerWptKmlMode ne 'KML' )
1532
        {
1533
        # not in KML mode
1534
        return;
1535
        }
1536
 
1537
    lock (%MkOsd);              # until end of block
1538
 
1539
    if ($MkOsd{'NCFlags'} & 0x04)
1540
        {
1541
        # NC is in WPT Mode 
1542
 
1543
        # Pause, Home is handled in WPT-Timer
1544
 
1545
        my $KmlCnt = scalar @KmlTargets;
1546
        if ( $PlayerMode eq "Play"  and  $KmlCnt > 0  and  $KmlPlayerIndex < $KmlCnt )
1547
            {
1548
 
1549
            my $Lat = $KmlTargets[$KmlPlayerIndex]->{'Lat'};
1550
            my $Lon = $KmlTargets[$KmlPlayerIndex]->{'Lon'};
1551
            my $Alt = $KmlTargets[$KmlPlayerIndex]->{'Alt'};
1552
 
1553
            &MkFlyTo ( '-lat'             => $Lat,
1554
                       '-lon'             => $Lon,
1555
                       '-alt'             => $Alt,
1556
                       '-holdtime'        => "60",
1557
                       '-mode'            => "Target",
1558
                     );
1559
 
1560
            # proceed to next Target
1561
            $KmlPlayerIndex ++;
1562
            if ( $KmlPlayerIndex >= scalar @KmlTargets )
1563
                {
1564
                $KmlPlayerIndex = 0;
1565
                }
1566
            }
1567
        }
1568
    });
1569
 
1570
 
1571
#       
1572
# Timer: 1s
1573
#
1574
$frame_map_top->repeat (1000, sub
1575
    {
1576
    # Clear old messages from this timer
1577
    &MkMessageInit ("Timer-Misc-1s");    
1578
 
1579
    lock (%MkOsd);              # until end of block
1580
 
1581
    # Aktuell gültige Karte
1582
    my %Map = %{$Maps{'Current'}};
1583
 
1584
    if ( $MkOsd{'_Timestamp'} >= time -2 )
1585
        {
1586
 
1587
        # Heartbeat MK Datenübertragung
1588
        if ( time %2 )
1589
            {
1590
            $map_canvas->itemconfigure('Heartbeat', '-image' => 'HeartbeatLarge', );
1591
            }
1592
        else
1593
            {
1594
            $map_canvas->itemconfigure('Heartbeat', '-image' => 'HeartbeatSmall', );
1595
            }
1596
 
1597
        # Flugzeit aktualisieren
1598
        # Flugzeit selber mitzählen, da $MkOsd{'FlyingTime'} immer 0 (0.14b)
1599
        if ( $MkOsd{'MKFlags'} & 0x02 )
1600
            {
1601
            $MkFlyingTime += 1;
1602
            }
1603
 
1604
        # Update ODO-Meter
1605
        if ( $MkOsd{'SatsInUse'} >= 6  and  $MkOsd{'CurPos_Stat'} == 1 )
1606
            {
1607
            my $C_Lat = $MkOsd{'CurPos_Lat'};
1608
            my $C_Lon = $MkOsd{'CurPos_Lon'};
1609
 
1610
            if ( $OdoFirst ne "" )
1611
                {
1612
                my ($Dist, $Bearing) = MapGpsTo($C_Lat, $C_Lon, $OdoPos_Lat, $OdoPos_Lon );
1613
                $OdoMeter += $Dist;
1614
                }
1615
            $OdoPos_Lat = $C_Lat;
1616
            $OdoPos_Lon = $C_Lon;
1617
            $OdoFirst = "1";
1618
            }
1619
 
1620
        # Footprint
1621
        if ( $Cfg->{'mkcockpit'}->{'FootprintLength'} > 0 )
1622
            {
1623
            if ( $MkOsd{'SatsInUse'} > 4  and  $MkOsd{'CurPos_Stat'} == 1 )
1624
                {
1625
                # neuen Footprint hinten anhaengen
1626
                my ($x, $y) = &MapGps2XY($MkOsd{'CurPos_Lat'}, $MkOsd{'CurPos_Lon'});
1627
                push @Footprint, $x, $y;
1628
                }
1629
 
1630
            while ( $#Footprint / 2  >  $Cfg->{'mkcockpit'}->{'FootprintLength'} )
1631
                {
1632
                # alte Footprints entfernen
1633
                splice @Footprint, 0, 2;
1634
                }
1635
 
1636
            &FootprintRedraw();
1637
            }
1638
 
1639
 
1640
        # tracking antenne
1641
        if ( $MkOsd{'MKFlags'} & 0x01  and  ! $MkTrack{'IsRunning'} and
1642
             $Cfg->{'track'}->{'Active'} =~ /y/i )
1643
            {
1644
            # start track at 1st motor start
1645
            $track_thr = threads->create (\&TrackAntennaGps)->detach();
1646
            $MkTrack{'IsRunning'} = "Running";
1647
            }
1648
        }
1649
 
1650
    });
1651
 
1652
 
1653
MainLoop();   # should never end
1654
 
1655
 
1656
#-----------------------------------------------------------------
1657
# Subroutines
1658
#-----------------------------------------------------------------                        
1659
 
1660
 
1661
# Add a Waypoint to @Waypoints List
1662
sub WpAdd()
1663
    {
1664
    my ($Wp_x, $Wp_y) = @_;
1665
 
1666
    # save Wp-Hash in Waypoint-Array
1667
    my $Wp = {};        
1668
    my $Tag = sprintf "Waypoint-%d.%d", time, int (rand(9)) ;   # kind of unique Tag for this Wp
1669
    ($Lat, $Lon) = &MapXY2Gps($Wp_x, $Wp_y);
1670
    $Wp->{'Tag'} = $Tag;
1671
    $Wp->{'MapX'} = $Wp_x;
1672
    $Wp->{'MapY'} = $Wp_y;
1673
    $Wp->{'Pos_Lat'} = $Lat;
1674
    $Wp->{'Pos_Lon'} = $Lon;
1675
    $Wp->{'Pos_Alt'} = $MkOsd{'CurPos_Alt'};
1676
    $Wp->{'Heading'}         = $Cfg->{'waypoint'}->{'DefaultHeading'};
1677
    $Wp->{'ToleranceRadius'} = $Cfg->{'waypoint'}->{'DefaultToleranceRadius'};
1678
    $Wp->{'Holdtime'}        = $Cfg->{'waypoint'}->{'DefaultHoldtime'};
1679
    $Wp->{'Event_Flag'}      = $Cfg->{'waypoint'}->{'DefaultEventFlag'};
1680
    push @Waypoints, $Wp;
1681
    }
1682
 
1683
 
1684
# Delete Waypoint from @Waypoints List
1685
sub WpDelete ()
1686
    {
1687
    my ($WpIndex) = @_;
1688
 
1689
    # delete Wp in Waypoint-Array
1690
    splice @Waypoints, $WpIndex, 1;
1691
    }
1692
 
1693
 
1694
# Load @Waypoints from file
1695
sub WpLoadFile ()
1696
    {
1697
    my ($WpFile) = @_;
1698
 
1699
    # XML in Hash-Ref lesen
1700
    my $Wp = XMLin($WpFile, ForceArray => 1);
1701
 
1702
    # XML Hash-Ref in Wp-Array umkopieren
1703
    undef @Waypoints;
1704
 
1705
    foreach $key (sort keys %$Wp)
1706
        {
1707
        my $Point = $Wp->{$key}->[0];
1708
 
1709
        # relative Pixelkoordinaten auf Bildgroesse umrechnen
1710
        if ( $Point->{'MapX'} <= 1  and  $Point->{'MapY'} <= 1 )
1711
            {
1712
            $Point->{'MapX'} = int ( $Point->{'MapX'} * $MapSizeX + 0.5 );
1713
            $Point->{'MapY'} = int ( $Point->{'MapY'} * $MapSizeY + 0.5 );
1714
            }
1715
 
1716
        # GPS Koordinaten für die aktuelle Karte neu aus Map x/y berechnen
1717
        my ($Lat, $Lon) = &MapXY2Gps($Point->{'MapX'}, $Point->{'MapY'});
1718
        $Point->{'Pos_Lat'} = $Lat;
1719
        $Point->{'Pos_Lon'} = $Lon;
1720
        push @Waypoints, $Point;
1721
        }
1722
    }
1723
 
1724
 
1725
# Safe @Waypoints to file
1726
sub WpSaveFile()
1727
    {
1728
    my ($WpFile) = @_;
1729
 
1730
    # Waypoint-Array in Hash umkopieren
1731
    for  $i ( 0 .. $#Waypoints )
1732
        {
1733
        my $key = sprintf ("WP-%04d", $i);
1734
        my $Wp = {%{$Waypoints[$i]}};        # copy of Hash-content
1735
        $WpOut{$key} = $Wp;
1736
 
1737
        # Pixelkoordinaten relativ zur Bildgroesse speichern
1738
        $WpOut{$key}{'MapX_Pixel'} = $WpOut{$key}{'MapX'};
1739
        $WpOut{$key}{'MapY_Pixel'} = $WpOut{$key}{'MapY'};
1740
        $WpOut{$key}{'MapX'} /= $MapSizeX;
1741
        $WpOut{$key}{'MapY'} /= $MapSizeY;
1742
        }
1743
 
1744
    # WP-Hash als XML speichern
1745
    &XMLout (\%WpOut,
1746
             'OutputFile' => $WpFile,
1747
             'AttrIndent' => '1',
1748
             'RootName' => 'Waypoints',
1749
            );
1750
    }
1751
 
1752
 
1753
 
1754
# Get Wp Index from Canvas Id
1755
sub WpGetIndexFromId()
1756
    {
1757
    my ($id) = @_;
1758
 
1759
    my @Tags = $map_canvas->gettags($id);
1760
    my $WpTag = $Tags[1];
1761
 
1762
    for $i (0 .. $#Waypoints)
1763
        {
1764
        my $Wp = $Waypoints[$i];
1765
        if ( $Wp->{'Tag'} eq $WpTag )
1766
            {
1767
            # got it
1768
            return $i;
1769
            }
1770
        }
1771
    return -1;
1772
    }
1773
 
1774
 
1775
# Resend all Waypoints to MK
1776
sub WpSendAll()
1777
    {
1778
    # OSD/Debug Abfragefrequenz verringern, sonst kommen nicht alle Wp im MK an
1779
    # Sicherheitshalber doppelt senden
1780
    $MkSendWp = 1;       # verhindert ueberschreiben im Timer
1781
    $MkSendQueue->enqueue( "o", "$AddrNC", pack ("C", 1000) );   # Frequenz OSD Datensatz, * 10ms
1782
    $MkSendQueue->enqueue( "d", "$AddrNC", pack ("C", 1000) );   # Frequenz MK Debug Datensatz, * 10ms
1783
    usleep (200000);
1784
    $MkSendQueue->enqueue( "o", "$AddrNC", pack ("C", 1000) );   # Frequenz OSD Datensatz, * 10ms
1785
    $MkSendQueue->enqueue( "d", "$AddrNC", pack ("C", 1000) );   # Frequenz MK Debug Datensatz, * 10ms
1786
    usleep (200000);
1787
 
1788
    # Alte WP-Liste im MK löschen
1789
    my $Wp = $Waypoints[0];
1790
    &MkFlyTo ( '-lat'  => $Wp->{'Pos_Lat'},
1791
               '-lon'  => $Wp->{'Pos_Lon'},
1792
               '-mode' => "Waypoint Delete"
1793
             );
1794
 
1795
    for $i (0 .. $#Waypoints)
1796
        {
1797
        my $Wp = $Waypoints[$i];
1798
        &MkFlyTo ( '-lat'             => $Wp->{'Pos_Lat'},
1799
                   '-lon'             => $Wp->{'Pos_Lon'},
1800
                   '-alt'             => $Wp->{'Pos_Alt'},
1801
                   '-heading'         => $Wp->{'Heading'},
1802
                   '-toleranceradius' => $Wp->{'ToleranceRadius'},
1803
                   '-holdtime'        => $Wp->{'Holdtime'},
1804
                   '-eventflag'       => $Wp->{'Event_Flag'},
1805
                   '-mode'            => "Waypoint"
1806
                 );
1807
 
1808
        usleep (150000)  # NC Zeit zum Verarbeiten geben
1809
        }
1810
 
1811
    $MkSendWp = 0;  # normale OSD/Debug Abfragefrequenz wird automatisch im 5s Timer wieder eingestellt
1812
 
1813
    # gray connectors: Wp are sent to MK
1814
    $map_canvas->itemconfigure('Waypoint-Connector',
1815
                               '-fill' => $Cfg->{'mkcockpit'}->{'ColorWpConnector'},
1816
                              );
1817
 
1818
    # MK ist nun synchron mit @Waypoints
1819
    $WaypointsModified = 0;
1820
    }          
1821
 
1822
 
1823
# Redraw Waypoint Icons
1824
sub WpRedrawIcons()
1825
    {
1826
    if ( $PlayerWptKmlMode =~ /WPT/i )
1827
        {
1828
 
1829
        # delete old icons and Wp-Number from canvas
1830
        $map_canvas->delete('Waypoint');
1831
        $map_canvas->delete('WaypointNumber');
1832
 
1833
        # create new icons
1834
        for $i (0 .. $#Waypoints)
1835
           {
1836
            my $Wp = $Waypoints[$i];
1837
            my $x = $Wp->{'MapX'};
1838
            my $y = $Wp->{'MapY'};
1839
            my $Tag = $Wp->{'Tag'};
1840
 
1841
            # Waypoint Icon
1842
            my $IconHeight = 48;
1843
            my $IconWidth = 48;
1844
            $map_canvas->createImage($x-$IconWidth/2, $y-$IconHeight,
1845
                                     '-tags' => ['Waypoint', $Tag],
1846
                                     '-anchor' => 'nw',
1847
                                     '-image'  => 'Waypoint-Photo',
1848
                                    );
1849
            # Waypoint Number
1850
            my $WpNumber = $i + 1;
1851
            $map_canvas->createText ( $x+3, $y-$IconHeight/2+12,
1852
                                      '-tags' => ['WaypointNumber', $Tag],
1853
                                      '-text' => $WpNumber,
1854
                                      '-font' => '-*-Arial-Bold-R-Normal--*-100-*',
1855
                                      '-fill' => $Cfg->{'mkcockpit'}->{'ColorWpNumber'},
1856
                                      '-anchor' => 'w',
1857
                                     );
1858
 
1859
            }  
1860
        $map_canvas->lower('Waypoint', 'Fox');              # waypoint below Fox
1861
        $map_canvas->lower('WaypointNumber', 'Waypoint');   # waypoint-number below waypoint
1862
        }
1863
    }
1864
 
1865
 
1866
# Redraw Waypoint connectors
1867
sub WpRedrawLines()
1868
    {
1869
    if ( $PlayerWptKmlMode =~ /WPT/i )
1870
        {
1871
        # delete old connectors from canvas
1872
        $map_canvas->delete('Waypoint-Connector');  
1873
 
1874
        my $Color = $Cfg->{'mkcockpit'}->{'ColorWpConnector'};
1875
        if ( $WaypointsModified )
1876
            {
1877
            $Color = $Cfg->{'mkcockpit'}->{'ColorWpResend'};
1878
            }
1879
 
1880
        my $Wp = $Waypoints[0];
1881
        my $x_last = $Wp->{'MapX'};
1882
        my $y_last = $Wp->{'MapY'};
1883
        for $i (1 .. $#Waypoints)
1884
            {
1885
            my $Wp = $Waypoints[$i];
1886
            my $x = $Wp->{'MapX'};
1887
            my $y = $Wp->{'MapY'};
1888
 
1889
            $map_canvas->createLine ( $x_last, $y_last, $x, $y,
1890
                                      '-tags' => 'Waypoint-Connector',
1891
                                      '-arrow' => 'last',
1892
                                      '-arrowshape' => [10, 10, 3 ],
1893
                                      '-fill' => $Color,
1894
                                      '-width' => 1,
1895
                                    );                                           
1896
            $x_last = $x;
1897
            $y_last = $y;
1898
            }
1899
 
1900
        $map_canvas->lower('Waypoint-Connector', 'Waypoint');   # connector below waypoint
1901
        }
1902
    }
1903
 
1904
 
1905
# Hide Waypoints on Canvas
1906
sub WpHide()
1907
   {
1908
   $map_canvas->delete('Waypoint');
1909
   $map_canvas->delete('WaypointNumber');
1910
   $map_canvas->delete('Waypoint-Connector');
1911
   }
1912
 
1913
 
1914
 
1915
# Redraw Footprint
1916
sub FootprintRedraw()
1917
    {
1918
    # delete old Footprint from canvas
1919
    $map_canvas->delete('Footprint');  
1920
 
1921
    if ( scalar @Footprint >= 4 )  # at least 2 Koordinaten-Paare
1922
        {
1923
        $map_canvas->createLine ( @Footprint,
1924
                                  '-tags' => 'Footprint',
1925
                                  '-fill' => $Cfg->{'mkcockpit'}->{'ColorFootprint'},
1926
                                  '-width' => 1,
1927
                                );       
1928
        }
1929
 
1930
    $map_canvas->lower('Footprint', 'Fox');
1931
    }
1932
 
1933
 
1934
 
1935
# Load @KmlTargets from file
1936
sub KmlLoadFile()
1937
    {
1938
    my ($File) = @_;
1939
 
1940
    # XML in Hash-Ref lesen
1941
    my $Kml = XMLin($File);
1942
 
1943
    # init state maschine
1944
    undef @KmlTargets;
1945
    $KmlPlayerIndex = 0;
1946
 
1947
    my $Coordinates = $Kml->{Document}->{Placemark}->{LineString}->{coordinates};
1948
    foreach $Line (split "\n", $Coordinates)
1949
        {
1950
        chomp $Line;
1951
        $Line =~ s/\s//g;       # remove white space
1952
        if ( $Line ne "" )
1953
            {
1954
            my ($Lon, $Lat, $Alt) = split ",", $Line;
1955
            $Lon = sprintf ("%f", $Lon);
1956
            $Lat = sprintf ("%f", $Lat);
1957
            $Alt = sprintf ("%f", $Alt);
1958
 
1959
            push @KmlTargets, {'Lat' => $Lat,
1960
                               'Lon' => $Lon,
1961
                               'Alt' => $Alt,
1962
                              };
1963
            }
1964
        }
1965
    }
1966
 
1967
 
1968
 
1969
# Redraw KML track
1970
sub KmlRedraw()
1971
    {
1972
 
1973
    # delete old Track from canvas
1974
    $map_canvas->delete('KML-Track');
1975
 
1976
    my @Track;
1977
 
1978
    foreach $Target ( @KmlTargets )
1979
        {
1980
        my $Lat = $Target->{'Lat'};
1981
        my $Lon = $Target->{'Lon'};
1982
        my $Alt = $Target->{'Alt'};
1983
        my ($x, $y) = &MapGps2XY($Lat, $Lon);
1984
        push @Track, $x, $y;
1985
        }
1986
 
1987
    if ( scalar @Track >= 4 )  # at least 2 Koordinaten-Paare
1988
        {
1989
        $map_canvas->createLine ( @Track,
1990
                                  '-tags' => 'KML-Track',
1991
                                  '-fill' => $Cfg->{'mkcockpit'}->{'ColorKmlTrack'},
1992
                                  '-width' => 1,
1993
                                );       
1994
 
1995
        $map_canvas->lower('KML-Track', 'Target');        # Track below Target
1996
        }
1997
    }
1998
 
1999
 
2000
# Hide Kml-Track on Canvas
2001
sub KmlHide()
2002
   {
2003
   $map_canvas->delete('KML-Track');
2004
   }
2005
 
2006
 
2007
# Switch player between WPT and KML
2008
sub WptKmlSwitch()
2009
    {
2010
    my ($Mode) = @_;
2011
 
2012
    # Wpt/Kml-Player-Icon loeschen und neu anzeigen
2013
    $map_canvas->delete('Wp-WptKml');
2014
 
2015
    if ( $Mode =~ /KML/i )
2016
        {
2017
        $PlayerWptKmlMode = 'KML';
2018
 
2019
        # set player button to KML
2020
        $map_canvas->createImage($MapSizeX/2-250, $MapSizeY-48,
2021
                                 '-tags' => 'Wp-WptKml',
2022
                                 '-anchor' => 'nw',
2023
                                 '-image'  => 'WpKml-Foto',
2024
                                 );
2025
 
2026
        # delete Waypoints from canvas
2027
        &WpHide();
2028
 
2029
        # show KML Track
2030
        &KmlRedraw();
2031
        }
2032
 
2033
    if ( $Mode =~ /WPT/i )
2034
        {
2035
        $PlayerWptKmlMode = 'WPT';
2036
 
2037
        # set player button to WPT
2038
        $map_canvas->createImage($MapSizeX/2-250, $MapSizeY-48,
2039
                                 '-tags' => 'Wp-WptKml',
2040
                                 '-anchor' => 'nw',
2041
                                 '-image'  => 'WpWpt-Foto',
2042
                                 );
2043
 
2044
        # delete Kml-Track from canvas
2045
        &KmlHide();
2046
 
2047
        # Show waypoints
2048
        if ( $PlayerRandomMode ne 'MAP' )
2049
            {
2050
            &WpRedrawIcons()
2051
            }
2052
        if ( $PlayerRandomMode eq 'STD' )
2053
            {
2054
            &WpRedrawLines()
2055
            }
2056
        }
2057
    }
2058
 
2059
 
2060
# Waypoint Player: Set Waypoint - sequence or random
2061
sub WpTargetSet()
2062
    {
2063
    my ($Index) = @_;
2064
 
2065
    my $WpCnt = scalar @Waypoints;
2066
 
2067
    if ( $Index < 0  or  $Index >= $WpCnt )
2068
        {
2069
        # invalid WP number
2070
        return 1;
2071
        }
2072
 
2073
    my $Wp = $Waypoints[$Index];
2074
    my $Wp_x = $Wp->{'MapX'};
2075
    my $Wp_y = $Wp->{'MapY'};
2076
 
2077
    # is Wp reachable?
2078
    if ( ! &IsTargetReachable($Wp_x, $Wp_y) )
2079
        {
2080
        # new Wp-Target is not reachable
2081
        return 1;
2082
        }
2083
 
2084
    # set new Wp-Target 
2085
    $WpPlayerIndex = $Index;
2086
    $WpPlayerHoldtime = -1;
2087
 
2088
    return 0;
2089
    }
2090
 
2091
 
2092
# Waypoint Player: Goto next Waypoint - sequence or random
2093
sub WpTargetNext()
2094
    {
2095
    my ($ParIndex) = @_;
2096
 
2097
    my $WpCnt = scalar @Waypoints;
2098
 
2099
    # Std- or Random Waypoint sequence
2100
    if ( $PlayerRandomMode =~ /STD/i  or
2101
         $PlayerRandomMode =~ /RND/i )
2102
        {
2103
        $NewIndex = $WpPlayerIndex;
2104
 
2105
        # get next Wp
2106
        for ( $i=0; $i<5; $i++)        # avoid deadlock, if no WP reachable
2107
            {
2108
            for ( $j=0; $j<5; $j++ )   # avoid deadlock, if only 1 WP
2109
                {
2110
 
2111
                if ( $PlayerRandomMode =~ /STD/i )
2112
                    {
2113
                    $NewIndex ++;
2114
                    if ( $NewIndex >= $WpCnt )
2115
                        {
2116
                        # Restart with 1st Wp
2117
                        $NewIndex = 0;
2118
                        }
2119
                    }
2120
 
2121
                if ( $PlayerRandomMode =~ /RND/i )
2122
                    {
2123
                    $NewIndex = int (rand($WpCnt));
2124
                    }
2125
 
2126
                # want to have different Wp 
2127
                if ( $NewIndex ne $WpPlayerIndex )
2128
                    {
2129
                    last;
2130
                    }
2131
                }
2132
 
2133
            # Set new Target 
2134
            if ( &WpTargetSet ($NewIndex) == 0 )
2135
                {
2136
                # new Wp-Target set
2137
                last;
2138
                }
2139
            }
2140
        }
2141
 
2142
    # Random Map sequence
2143
    if ( $PlayerRandomMode =~ /MAP/i )
2144
        {
2145
        $RandomTarget_x = $MkPos_x;
2146
        $RandomTarget_y = $MkPos_y;
2147
 
2148
        for ( $i=0; $i<50; $i++)        # avoid deadlock, if target not reachable
2149
            {
2150
            # don't use 10% around the map
2151
            my $New_x = int (rand($MapSizeX - 2 * $MapSizeX/10));
2152
            my $New_y = int (rand($MapSizeY - 2 * $MapSizeY/10));
2153
            $New_x += $MapSizeX/10;
2154
            $New_y += $MapSizeY/10;
2155
 
2156
            # is Target reachable?
2157
            if ( &IsTargetReachable($New_x, $New_y) )
2158
                {
2159
                # new Target found
2160
                $RandomTarget_x = $New_x;
2161
                $RandomTarget_y = $New_y;
2162
                last;
2163
                }
2164
            }
2165
        }
2166
 
2167
    $WpPlayerHoldtime = -1;
2168
    }
2169
 
2170
 
2171
# Waypoint Player: Goto previous Waypoint
2172
sub WpTargetPrev()
2173
    {
2174
    if ( $PlayerRandomMode =~ /STD/i )
2175
        {
2176
        $WpPlayerIndex --;
2177
        if ( $WpPlayerIndex < 0 )
2178
            {
2179
            # Restart with last Wp
2180
            $WpPlayerIndex = $#Waypoints;
2181
            }
2182
        }
2183
    else
2184
        {
2185
        # Next Random Target
2186
        &WpTargetNext();
2187
        }
2188
 
2189
    $WpPlayerHoldtime = -1;
2190
    }
2191
 
2192
 
2193
# Waypoint Player: Goto first Waypoint
2194
sub WpTargetFirst()
2195
    {
2196
    $WpPlayerIndex = 0;
2197
    $WpPlayerHoldtime = -1;
2198
    }
2199
 
2200
# Waypoint Player: Goto last Waypoint
2201
sub WpTargetLast()
2202
    {
2203
    $WpPlayerIndex = $#Waypoints;
2204
    $WpPlayerHoldtime = -1;
2205
    }
2206
 
2207
 
2208
# Waypoint Player: Waypoint Target reached?
2209
sub WpCheckTargetReached()
2210
    {
2211
    if ( $WpPlayerHoldtime == -1 )
2212
        {
2213
        lock (%MkOsd);              # until end of block
2214
 
2215
        if ( $MkOsd{'_Timestamp'} >= time-2  and   # Gueltige OSD Daten
2216
             $MkOsd{'NCFlags'} & 0x04  and         # WPT-Mode
2217
             $MkOsd{'SatsInUse'} >= 6  and  $MkOsd{'CurPos_Stat'} == 1 and  $MkOsd{'HomePos_Stat'} == 1)
2218
            {
2219
            # Gueltige SAT Daten
2220
 
2221
            # for Wp mode
2222
            my $Wp = $Waypoints[$WpPlayerIndex];
2223
            my $WpTarget_Lat = $Wp->{'Pos_Lat'};
2224
            my $WpTarget_Lon = $Wp->{'Pos_Lon'};
2225
            my $WpTolerance  = $Wp->{'ToleranceRadius'};
2226
            my $WpHoldtime   = $Wp->{'Holdtime'};
2227
 
2228
            # Random-Map Mode
2229
            if ( $PlayerRandomMode =~ /MAP/i )
2230
                {
2231
                ($WpTarget_Lat, $WpTarget_Lon) = &MapXY2Gps ($RandomTarget_x, $RandomTarget_y);
2232
                $WpTolerance = $Cfg->{'waypoint'}->{'DefaultToleranceRadius'};
2233
                $WpHoldtime  = $Cfg->{'waypoint'}->{'DefaultHoldtime'};
2234
                }
2235
 
2236
            # Operation Radius pruefen
2237
            my ($HomeDist, $HomeBearing) = &MapGpsTo($MkOsd{'HomePos_Lat'}, $MkOsd{'HomePos_Lon'}, $WpTarget_Lat, $WpTarget_Lon );
2238
            if ( $HomeDist > $MkOsd{'OperatingRadius'} )
2239
                {
2240
                # Target entsprechend Operation Radius neu berechnen
2241
                $HomeDist = $MkOsd{'OperatingRadius'};
2242
                ($WpTarget_Lat, $WpTarget_Lon) = &MapGpsAt($MkOsd{'HomePos_Lat'}, $MkOsd{'HomePos_Lon'}, $HomeDist, $HomeBearing);
2243
                }
2244
 
2245
            # Abstand zum Ziel pruefen
2246
            my ($Dist, $Bearing) = &MapGpsTo($MkOsd{'CurPos_Lat'}, $MkOsd{'CurPos_Lon'}, $WpTarget_Lat, $WpTarget_Lon );
2247
            $Dist = int ($Dist + 0.5);
2248
            if ( $Dist <= $WpTolerance )
2249
                {
2250
                # Target reached - count down Holdtime
2251
                $WpPlayerHoldtime = 2 * $WpHoldtime;      # 0..2n - decrement im 0.5s timer
2252
                }
2253
            }
2254
        }
2255
 
2256
    if ( $WpPlayerHoldtime == 0 )  # wird im 0.5s timer runtergezaehlt
2257
        {
2258
        # Target reached - Holdtime is over
2259
        $WpPlayerHoldtime = -1;
2260
        return 1;
2261
        }
2262
 
2263
    # Target NOT reached
2264
    return 0;
2265
    }
2266
 
2267
 
2268
# KML Player: 10s forward
2269
sub KmlTargetNext()
2270
    {
2271
    $KmlPlayerIndex += int (10 / $Cfg->{waypoint}->{'KmlTimeBase'} + 0.5);
2272
    if ( $KmlPlayerIndex > $#KmlTargets )
2273
        {
2274
        # Next loop
2275
        $KmlPlayerIndex -= $#KmlTargets;
2276
        }
2277
    }
2278
 
2279
# KML Player: 10s backward
2280
sub KmlTargetPrev()
2281
    {
2282
    $KmlPlayerIndex -= int (10 / $Cfg->{waypoint}->{'KmlTimeBase'} + 0.5);
2283
    if ( $KmlPlayerIndex < 0 )
2284
        {
2285
        # Next loop
2286
        $KmlPlayerIndex += $#KmlTargets;
2287
        }
2288
    }
2289
 
2290
# KML Player: Goto first Target
2291
sub KmlTargetFirst()
2292
    {
2293
    $KmlPlayerIndex = 0;
2294
    }
2295
 
2296
# KML Player: Goto last Target
2297
sub KmlTargetLast()
2298
    {
2299
    $KmlPlayerIndex = $#KmlTargets;
2300
    }
2301
 
2302
 
2303
#
2304
# GUI Call Back
2305
# 
2306
 
2307
# Player CallBack: Play/Pause button
2308
sub CbPlayerPlayPause()
2309
    {
2310
    # Play/Pause-Icon loeschen und neu anzeigen
2311
    $map_canvas->delete('Wp-PlayPause');
2312
 
2313
    if ( ($PlayerMode eq "Pause") or  ($PlayerMode eq "Stop") or  ($PlayerMode eq "Home") )
2314
        {
2315
        $PlayerMode = 'Play';
2316
        $WpPlayerHoldtime = -1;
2317
        $map_canvas->createImage($MapSizeX/2+100, $MapSizeY-48,
2318
                                 '-tags' => 'Wp-PlayPause',
2319
                                 '-anchor' => 'nw',
2320
                                 '-image'  => 'WpPause-Foto',
2321
                                 );
2322
        }
2323
    else
2324
        {
2325
        $PlayerMode = 'Pause';
2326
        $WpPlayerHoldtime = -1;
2327
        $map_canvas->createImage($MapSizeX/2+100, $MapSizeY-48,
2328
                                 '-tags' => 'Wp-PlayPause',
2329
                                 '-anchor' => 'nw',
2330
                                 '-image'  => 'WpPlay-Foto',
2331
                                 );
2332
 
2333
        # momentane Position merken und im Player-Timer senden
2334
        $PlayerPause_Lon = "";
2335
        $PlayerPause_Lat = "";
2336
 
2337
        lock (%MkOsd);              # until end of block
2338
        if ( $MkOsd{'_Timestamp'} >= time-2 )
2339
            {
2340
            # Gueltige OSD Daten
2341
            if ( $MkOsd{'SatsInUse'} >= 6  and  $MkOsd{'CurPos_Stat'} == 1 )
2342
                {
2343
                $PlayerPause_Lon = $MkOsd{'CurPos_Lon'};
2344
                $PlayerPause_Lat = $MkOsd{'CurPos_Lat'};
2345
                }
2346
            }
2347
        }
2348
    }
2349
 
2350
 
2351
# Player CallBack: Next
2352
sub CbPlayerNext()
2353
    {
2354
    if ( $PlayerMode ne 'Stop' )
2355
        {
2356
 
2357
        if ( $PlayerWptKmlMode eq 'WPT' )
2358
           {
2359
           &WpTargetNext();
2360
           }
2361
 
2362
        if ( $PlayerWptKmlMode eq 'KML' )
2363
           {
2364
           &KmlTargetNext();
2365
           }
2366
 
2367
        }
2368
    }
2369
 
2370
 
2371
# Player CallBack: Prev
2372
sub CbPlayerPrev()
2373
    {
2374
    if ( $PlayerMode ne 'Stop' )
2375
        {
2376
 
2377
        if ( $PlayerWptKmlMode eq 'WPT' )
2378
           {
2379
           &WpTargetPrev();
2380
           }
2381
 
2382
        if ( $PlayerWptKmlMode eq 'KML' )
2383
           {
2384
           &KmlTargetPrev();
2385
           }
2386
 
2387
        }
2388
    }
2389
 
2390
 
2391
# Player CallBack: First
2392
sub CbPlayerFirst()
2393
    {
2394
    if ( $PlayerMode ne 'Stop' )
2395
        {
2396
 
2397
        if ( $PlayerWptKmlMode eq 'WPT' )
2398
           {
2399
           &WpTargetFirst();
2400
           }
2401
 
2402
        if ( $PlayerWptKmlMode eq 'KML' )
2403
           {
2404
           &KmlTargetFirst();
2405
           }
2406
 
2407
        }
2408
    }
2409
 
2410
# Player CallBack: Last
2411
sub CbPlayerLast()
2412
    {
2413
    if ( $PlayerMode ne 'Stop' )
2414
        {
2415
 
2416
        if ( $PlayerWptKmlMode eq 'WPT' )
2417
           {
2418
           &WpTargetLast();
2419
           }
2420
 
2421
        if ( $PlayerWptKmlMode eq 'KML' )
2422
           {
2423
           &KmlTargetLast();
2424
           }
2425
 
2426
        }
2427
    }
2428
 
2429
 
2430
# Player CallBack: Home
2431
sub CbPlayerHome()
2432
    {
2433
    if ( $PlayerMode ne 'Stop' )
2434
        {
2435
        $PlayerMode = 'Home';
2436
        &WpTargetFirst();
2437
 
2438
        $map_canvas->delete('Wp-PlayPause');
2439
        $map_canvas->createImage($MapSizeX/2+100, $MapSizeY-48,
2440
                                 '-tags'   => 'Wp-PlayPause',
2441
                                 '-anchor' => 'nw',
2442
                                 '-image'  => 'WpPlay-Foto',
2443
                                 );
2444
        }
2445
    }
2446
 
2447
 
2448
# Player CallBack: Stop
2449
sub CbPlayerStop()
2450
    {
2451
    if ( $PlayerMode ne 'Stop' )
2452
        {
2453
        $PlayerMode = 'Stop';
2454
        &WpTargetFirst();
2455
 
2456
        # set Play/Pause Icon to "Play
2457
        $map_canvas->delete('Wp-PlayPause');
2458
        $map_canvas->createImage($MapSizeX/2+100, $MapSizeY-48,
2459
                                 '-tags'   => 'Wp-PlayPause',
2460
                                 '-anchor' => 'nw',
2461
                                 '-image'  => 'WpPlay-Foto',
2462
                                 );
2463
 
2464
 
2465
        # WP resend required
2466
        $WaypointsModified = 1;
2467
 
2468
        # switch player to Wp Mode
2469
        &WptKmlSwitch ('WPT');
2470
        }
2471
    }
2472
 
2473
 
2474
# Player CallBack: Move MK in Pause-Mode
2475
sub CbPlayerMove()
2476
    {
2477
    my ($Id, $DirX, $DirY) = @_;
2478
 
2479
    if ( $PlayerMode eq 'Pause'  and
2480
         $PlayerPause_Lat ne ""  and  $PlayerPause_Lon ne "" )
2481
        {
2482
        my $Dist = $Cfg->{'waypoint'}->{'PauseMoveDist'} || 1;  # 1m default
2483
 
2484
        my $BearingTop = &MapAngel() - 90.0;
2485
        my $BearingKey = rad2deg atan2($DirX, $DirY);
2486
        my $Bearing = $BearingTop + $BearingKey;
2487
 
2488
        ($PlayerPause_Lat, $PlayerPause_Lon) = &MapGpsAt($PlayerPause_Lat, $PlayerPause_Lon, $Dist, $Bearing)
2489
        }
2490
    }
2491
 
2492
 
2493
# Player CallBack: Toggle WPT/KML button
2494
sub CbPlayerWptKml()
2495
    {
2496
 
2497
    if ( $PlayerWptKmlMode =~ /WPT/i )
2498
        {
2499
        # switch player to KML Mode
2500
        &WptKmlSwitch ('KML');
2501
        }
2502
 
2503
    elsif ( $PlayerWptKmlMode =~ /KML/i )
2504
        {
2505
        # WP resend required
2506
        $WaypointsModified = 1;
2507
 
2508
        # switch player to Wp Mode
2509
        &WptKmlSwitch ('WPT');
2510
        }
2511
 
2512
    }
2513
 
2514
 
2515
# Player CallBack: Toggle Random modes
2516
sub CbPlayerWptRandom()
2517
    {
2518
    # Hide old Icon
2519
    $map_canvas->delete('Wp-WptRandom');
2520
 
2521
    if ( $PlayerRandomMode eq "STD" )
2522
        {
2523
        $PlayerRandomMode = "RND";
2524
        $map_canvas->createImage($MapSizeX/2-200, $MapSizeY-48,
2525
                                 '-tags' => 'Wp-WptRandom',
2526
                                 '-anchor' => 'nw',
2527
                                 '-image'  => 'WpRandomMap-Foto',
2528
                                );
2529
 
2530
        # delete old connectors from canvas
2531
        $map_canvas->delete('Waypoint-Connector');  
2532
        }
2533
 
2534
    elsif ( $PlayerRandomMode eq "RND" )
2535
        {
2536
        $PlayerRandomMode = "MAP";
2537
        $map_canvas->createImage($MapSizeX/2-200, $MapSizeY-48,
2538
                                 '-tags' => 'Wp-WptRandom',
2539
                                 '-anchor' => 'nw',
2540
                                 '-image'  => 'WpRandomOff-Foto',
2541
                                );
2542
 
2543
        # Get 1st Target
2544
        &WpTargetNext();
2545
 
2546
        # hide WP and connectors on canvas
2547
        &WpHide();
2548
        }
2549
 
2550
    else
2551
        {
2552
        $PlayerRandomMode = "STD";
2553
        $map_canvas->createImage($MapSizeX/2-200, $MapSizeY-48,
2554
                                 '-tags' => 'Wp-WptRandom',
2555
                                 '-anchor' => 'nw',
2556
                                 '-image'  => 'WpRandomOn-Foto',
2557
                                );
2558
 
2559
        # redraw connectors and Icons on canvas
2560
        &WpRedrawLines();
2561
        &WpRedrawIcons();
2562
        }
2563
    }
2564
 
2565
# Player CallBack: Number Keys
2566
sub CbPlayerNum()
2567
    {
2568
    my ($Id, $Num) = @_;
2569
 
2570
    $CbPlayerKey = "$CbPlayerKey" . "$Num";
2571
    }
2572
 
2573
#
2574
# System Messages
2575
#
2576
 
2577
# Init Messages for a Subsystem/timer
2578
sub MkMessageInit ()
2579
    {
2580
    my ($Id) = @_;
2581
 
2582
    $MkMessages{$Id} = [];
2583
    }
2584
 
2585
 
2586
# Register message
2587
sub MkMessage ()
2588
    {
2589
    my ($Message, $Id) = @_;
2590
 
2591
    push @{$MkMessages{$Id}}, $Message;
2592
    }
2593
 
2594
 
2595
# show registered messages
2596
sub MkMessageShow()
2597
    {
2598
    my @Messages;
2599
    my $MsgLines = 0;
2600
    my $MaxMsgLen = 0;
2601
 
2602
    # Collect Messages of each category
2603
    foreach my $Id (keys %MkMessages)
2604
        {
2605
        foreach $i ( 0 .. $#{$MkMessages{$Id}} )
2606
            {
2607
            my $Msg = $MkMessages{$Id}[$i];
2608
            push @Messages, $Msg;
2609
 
2610
            $MsgLines ++;
2611
 
2612
            my $Len = length $Msg;
2613
            if ( $Len > $MaxMsgLen )
2614
                {
2615
                $MaxMsgLen = $Len;
2616
                }
2617
            }
2618
        }
2619
 
2620
    $map_canvas->delete('Message-Balloon');  # delete old Balloon
2621
 
2622
    if ( $MsgLines > 0 )
2623
        {
2624
        # draw Balloon
2625
        my @MsgBalloon = ( $MkPos_x ,                       $MkPos_y,
2626
                           $MkPos_x + 30 ,                  $MkPos_y + 40,
2627
                           $MkPos_x + 30 + $MaxMsgLen * 11, $MkPos_y + 40,
2628
                           $MkPos_x + 30 + $MaxMsgLen * 11, $MkPos_y + 44 + $MsgLines * 20,
2629
                           $MkPos_x + 20,                   $MkPos_y + 44 + $MsgLines * 20,
2630
                           $MkPos_x + 20,                   $MkPos_y + 40,
2631
                           $MkPos_x,                        $MkPos_y,
2632
                          );
2633
 
2634
        $map_canvas->createPolygon( @MsgBalloon,
2635
                                    '-tags' => ['Message-Balloon', 'Message-BalloonBubble'],
2636
                                    '-fill' => 'yellow',
2637
                                    '-outline' => 'yellow',
2638
                                    '-width' => 1,
2639
                                  );
2640
        # draw Messages
2641
        my $MsgLine = 1;
2642
        foreach my $Msg (@Messages)
2643
            {
2644
            $map_canvas->createText ( $MkPos_x + 25, $MkPos_y + 32 + $MsgLine * 20 ,
2645
                                      '-tags' => ['Message-Balloon', 'Message-BalloonText'],
2646
                                      '-text' => $Msg,
2647
                                      '-font' => '-*-Arial-Bold-R-Normal--*-200-*',
2648
                                      '-fill' => 'blue',
2649
                                      '-anchor' => 'w',
2650
                                        );
2651
            $MsgLine ++;
2652
            }
2653
 
2654
 
2655
        $map_canvas->lower('Message-Balloon', 'MK-Arrow');
2656
        }
2657
 
2658
    }
2659
 
2660
 
2661
#
2662
# Airfield border
2663
#
2664
 
2665
# Are two segments A(a1/a2), B(b1/b2) and C(c1/c2), D(d1/d2) crossing ?
2666
sub SegmentCross()
2667
    {
2668
    my ( $a1, $a2, $b1, $b2, $c1, $c2, $d1, $d2) = @_;
2669
 
2670
    # segment C/D ist vertical, avoid div/0
2671
    if ( $c1 == $d1 )
2672
        {
2673
        $d1 += 0.00001;
2674
        }
2675
 
2676
    my $n = ($b1 - $a1) * ($d2 - $c2) - ($b2 - $a2) * ($d1 - $c1);
2677
    if ( $n == 0.0 )
2678
        {
2679
        # AB und CD sind parallel
2680
        return 0;
2681
        }
2682
 
2683
    my $s = ( ($c1 - $a1) * ($d2 - $c2) - ($c2 - $a2) * ($d1 - $c1) ) / $n;
2684
    my $t = ( $a1 - $c1 + $s * ($b1 - $a1) ) / ( $d1 - $c1 );
2685
    if ( $s >= 0.0  and  $s <= 1.0  and  $t >= 0.0  and  $t <= 1.0 )
2686
        {
2687
        # beide Strecken kreuzen sich
2688
 
2689
        # Schnittpunkt: s_x, s_y
2690
        my $s_x = $a1 + $s * ( $b1 - $a1 );
2691
        my $s_y = $a2 + $s * ( $b2 - $a2 );
2692
 
2693
        return 1;
2694
        }
2695
 
2696
    # beide Strecken kreuzen sich nicht
2697
    return 0;
2698
    }
2699
 
2700
 
2701
# How often does a segment A(a1,a2), B(b1,b2) cross the polygon?
2702
sub SegmentPolygonCross()
2703
    {
2704
    my ( $a1, $a2, $b1, $b2, $Polygon) = @_;
2705
 
2706
    my $Cross = 0;
2707
    my $PolyCnt = scalar @{$Polygon};
2708
    my $PolyPointCnt = $PolyCnt / 2;
2709
 
2710
    my $i = 0;
2711
    for ( $p=0; $p < $PolyPointCnt; $p++ )
2712
        {
2713
        my $c1 = ${$Polygon}[$i++];
2714
        my $c2 = ${$Polygon}[$i++];
2715
 
2716
        if ( $i >= $PolyCnt ) { $i = 0; }
2717
 
2718
        my $d1 = ${$Polygon}[$i];
2719
        my $d2 = ${$Polygon}[$i+1];
2720
 
2721
        # map calibration offsets
2722
        $c1 -= $Map{'Offset_x'};
2723
        $c2 += $Map{'Offset_y'};
2724
        $d1 -= $Map{'Offset_x'};
2725
        $d2 += $Map{'Offset_y'};    
2726
 
2727
        if ( &SegmentCross($a1, $a2, $b1, $b2, $c1, $c2, $d1, $d2) )
2728
            {
2729
            $Cross ++;
2730
            }
2731
        }
2732
 
2733
    return $Cross;
2734
    }
2735
 
2736
 
2737
# Is point A inside airfield border?
2738
sub IsInsideBorder()
2739
    {
2740
    my ($a1, $a2) = @_;
2741
 
2742
    if ( scalar @Map{'Border'} == 0 )
2743
        {
2744
        # no border defined, always inside
2745
        return 1;
2746
        }
2747
 
2748
    my $Cross = &SegmentPolygonCross (-10, -10, $a1, $a2, @Map{'Border'} );
2749
 
2750
    # Ungerade Anzahl Kreuzungen: Inside
2751
    return ( $Cross % 2 );
2752
    }
2753
 
2754
 
2755
 
2756
# Is segment A, B crossing the airfield border?
2757
sub IsCrossingBorder()
2758
    {
2759
    my ($a1, $a2, $b1, $b2) = @_;
2760
 
2761
    if ( scalar @Map{'Border'} == 0 )
2762
        {
2763
        # no border defined, always not crossing
2764
        return 0;
2765
        }
2766
 
2767
    my $Cross = &SegmentPolygonCross ($a1, $a2, $b1, $b2, @Map{'Border'} );
2768
 
2769
    return ( $Cross > 0 );
2770
    }
2771
 
2772
 
2773
# How often is segment A, B crossing the airfield border?
2774
sub CrossingBorderCount()
2775
    {
2776
    my ($a1, $a2, $b1, $b2) = @_;
2777
 
2778
    if ( scalar @Map{'Border'} == 0 )
2779
        {
2780
        # no border defined, not crossing
2781
        return 0;
2782
        }
2783
 
2784
    my $Cross = &SegmentPolygonCross ($a1, $a2, $b1, $b2, @Map{'Border'} );
2785
 
2786
    return ( $Cross );
2787
    }
2788
 
2789
 
2790
# check, if Target is reachable my MK
2791
sub IsTargetReachable()
2792
    {
2793
    my ($T_x, $T_y) = @_;
2794
 
2795
    my $MkIsInside = &IsInsideBorder($MkPos_x, $MkPos_y);
2796
    my $TargetIsInside = &IsInsideBorder($T_x, $T_y);
2797
    my $MkTargetCrossingCount = &CrossingBorderCount($MkPos_x, $MkPos_y, $T_x, $T_y);
2798
 
2799
    if ( ($MkIsInside  and  $MkTargetCrossingCount == 0 )  or
2800
         (! $MkIsInside  and  $TargetIsInside  and  $MkTargetCrossingCount == 1) )
2801
        {
2802
        # Target is reachable
2803
        return 1;
2804
        }
2805
 
2806
    # Target is not reachable
2807
    return 0;
2808
    }
2809
 
2810
 
2811
 
2812
#
2813
# Configuration and data-visualisation
2814
#
2815
 
2816
# Display or Modify Hash
2817
sub DisplayHash()
2818
    {
2819
    my ($hrefData, $Titel, $Mode) = @_;
2820
 
2821
    # $Mode: Display, Edit, Waypoint, Refresh
2822
 
2823
    my %Id;
2824
    my $Label;
2825
    my $Value;
2826
 
2827
    # Neues Fenster aufmachen
2828
    my $popup = $main->Toplevel();
2829
    $popup->title($Titel);
2830
 
2831
    # Buttons
2832
    my $popup_button = $popup->Frame() -> pack('-side' => 'bottom',
2833
                                               '-expand' => 'y',
2834
                                               '-anchor' => 's',
2835
                                               '-padx' => 5,
2836
                                               '-pady' => 5,
2837
                                               );
2838
    $popup_button->Button('-text'    => 'Schließen',
2839
                          '-command' => sub
2840
        {
2841
        if ( $Mode =~ /edit/i  and  $Mode =~ /waypoint/i )
2842
            {
2843
            $WaypointsModified = 1;            
2844
            &WpRedrawLines();
2845
            &WpRedrawIcons();
2846
            }
2847
 
2848
        $popup->destroy()
2849
        })->pack;
2850
 
2851
    # Frame mit den Labels
2852
    my $popup_label = $popup->Frame() -> pack('-side' => 'left',
2853
                                              '-expand' => 'y',
2854
                                              '-anchor' => 'w',
2855
                                              '-padx' => 10,
2856
                                              '-pady' => 10,
2857
                                              );
2858
    # Labels anzeigen                    
2859
    foreach $Label ( sort keys %{$hrefData})
2860
        {
2861
        if ( $Translate{$Label} ne "" )
2862
            {
2863
            $Label = $Translate{$Label};
2864
            }
2865
 
2866
        $popup_label->Label ('-text' => $Label,
2867
                             '-width' => 25,
2868
                             '-anchor' => 'w',
2869
                             ) -> pack();
2870
        }
2871
 
2872
    # Frame mit den Daten
2873
    my $popup_values = $popup->Frame() -> pack('-side' => 'left',
2874
                                               '-expand' => 'y',
2875
                                               '-anchor' => 'w',
2876
                                               '-padx' => 10,
2877
                                               '-pady' => 10,
2878
                                               );
2879
    # Daten anzeigen
2880
    foreach $Value ( sort keys %{$hrefData})
2881
        {                              
2882
        if ( $Mode =~ /display/i )
2883
            {
2884
            # Display
2885
            $Id{$Value} = $popup_values->Label ('-text' => ${$hrefData}{$Value},
2886
                                                '-width' => 20,
2887
                                                '-anchor' => 'e',
2888
                                                '-relief' => 'sunken',
2889
                                                ) -> pack();
2890
            }
2891
        if ( $Mode =~ /edit/i )
2892
            {
2893
            # Edit
2894
            $Id{$Value} = $popup_values->Entry ('-textvariable' => \${$hrefData}{$Value},
2895
                                                '-exportselection' => '1',
2896
                                                '-width' => 20,
2897
                                                '-relief' => 'sunken',
2898
                                               ) -> pack();
2899
            if ( $Mode =~ /waypoint/i )
2900
                {
2901
                # einige Waypoint-Felder nicht aenderbar einstellen
2902
                if ( "MapX MapY Pos_Lat Pos_Lon Tag" =~ /$Value/i )
2903
                    {
2904
                    $Id{$Value}->configure('-state' => 'disabled', );
2905
                    }
2906
                }      
2907
            }
2908
        }
2909
 
2910
    if ( $Mode =~ /refresh/i )
2911
        {
2912
        # Timer: 0.1s
2913
        $popup_values->repeat (100, sub
2914
            {
2915
            # Datenfelder alle 100ms aktualisieren
2916
 
2917
            my $BgColor = 'white';
2918
            if ( $Mode =~ /heartbeat/i )
2919
                {
2920
                $BgColor = 'red';
2921
                if ( $MkOsd{'_Timestamp'} >= time-2 )
2922
                    {
2923
                    # gültige daten vom MK
2924
                    $BgColor = 'white';
2925
                    }
2926
                }
2927
 
2928
            foreach $Value ( sort keys %{$hrefData} )
2929
                {
2930
                # Eingebbare Waypoint-Felder nicht aktualisieren
2931
                if ( ! ($Mode =~ /waypoint/i  and
2932
                        "Event_Flag Heading ToleranceRadius HoldTime Pos_Alt" =~ /$Value/i) )
2933
                    {                
2934
                    $Id{$Value}->configure('-text' => ${$hrefData}{$Value},
2935
                                           '-background' => "$BgColor",
2936
                                          );
2937
                    }
2938
                }
2939
            });
2940
        }
2941
 
2942
    return 0;
2943
    }
2944
 
2945
 
2946
 
2947
# Konfigurationsdatei mkcockpit.xml im Popup-Fenster editieren
2948
sub Configure()
2949
    {
2950
 
2951
    # Copy Cfg-Hash for editing
2952
    my $CfgEdit = {%{$Cfg}};
2953
    foreach $key (keys %{$Cfg})
2954
        {
2955
        if ( ref $Cfg->{$key} )
2956
            {
2957
            $CfgEdit->{$key} = {%{$Cfg->{$key}}};
2958
            }
2959
        }
2960
 
2961
    # Neues Fenster aufmachen
2962
    my $popup = $main->Toplevel();
2963
    $popup->title("Einstellungen - $XmlConfigFile");
2964
 
2965
    # jede Sektion in einem Tab anzeigen
2966
    my $book = $popup->NoteBook()->pack( -fill=>'both', -expand=>1 );
2967
    foreach $key (sort keys %{$CfgEdit})
2968
        {    
2969
        if ( ! ref $CfgEdit->{$key} )
2970
            {
2971
            next;
2972
            }
2973
 
2974
        my $TabLabel = "$key";
2975
        if ( $Translate{$key} ne "" )
2976
                {
2977
                $TabLabel = $Translate{$key};
2978
                }
2979
 
2980
        my $Tab = $book->add( "$key", -label=>"$TabLabel", );
2981
 
2982
        # Frame fuer Buttons
2983
        my $book_button = $Tab->Frame() -> pack('-side' => 'bottom',
2984
                                                '-expand' => 'y',
2985
                                                '-anchor' => 's',
2986
                                                '-padx' => 5,
2987
                                                '-pady' => 5,
2988
                                                );
2989
 
2990
        $book_button->Button('-text'    => 'OK',
2991
                             '-width' => '10',
2992
                             '-command' => sub
2993
            {
2994
            # Copy back CfgEdit-Hash
2995
            $Cfg = {%{$CfgEdit}};
2996
            foreach $key (keys %{$CfgEdit})
2997
                {
2998
                if ( ref $CfgEdit->{$key} )
2999
                    {
3000
                    $Cfg->{$key} = {%{$CfgEdit->{$key}}};
3001
                    }
3002
                }
3003
 
3004
            # set new timestamp
3005
            my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time);
3006
            my $TimeStamp = sprintf ("%04d%02d%02d-%02d%02d%02d", $year+1900, $mon+1, $mday, $hour, $min, $sec);
3007
            $Cfg->{'CreationDate'} = $TimeStamp;
3008
 
3009
            # Cfg in mkcockpit.xml speichern
3010
            &XMLout ($Cfg,
3011
                     'OutputFile' => $XmlConfigFile,
3012
                     'AttrIndent' => '1',
3013
                     'RootName' => 'mkcockpit-Config',
3014
                    );
3015
 
3016
            $popup->destroy();
3017
            } )->pack ('-side' => 'left',
3018
                       '-expand' => 'y',
3019
                       '-anchor' => 's',
3020
                       '-padx' => 5,
3021
                       '-pady' => 5,
3022
                      );
3023
        $book_button->Button('-text'    => $Translate{'Abort'},
3024
                             '-width' => '10',
3025
                             '-command' => sub { $popup->destroy() },
3026
                            )->pack ('-side' => 'left',
3027
                                     '-expand' => 'y',
3028
                                     '-anchor' => 's',
3029
                                     '-padx' => 5,
3030
                                     '-pady' => 5,
3031
                                     );
3032
        $book_button->Label ('-text' => $Translate{'RestartRequired'},
3033
                             '-anchor' => 'w',
3034
                             '-foreground' => 'red',
3035
                            ) ->pack ('-side' => 'left',
3036
                                      '-expand' => 'y',
3037
                                      '-anchor' => 's',
3038
                                      '-padx' => 10,
3039
                                      '-pady' => 5,
3040
                                      );
3041
 
3042
        # Frame mit den Labels
3043
        my $popup_label = $Tab->Frame() -> pack('-side' => 'left',
3044
                                                '-expand' => 'y',
3045
                                                '-anchor' => 'w',
3046
                                                '-padx' => 10,
3047
                                                '-pady' => 10,
3048
                                                );
3049
        # Labels anzeigen                        
3050
        foreach $Label ( sort keys %{$CfgEdit->{$key}})
3051
            {
3052
            if ( $Translate{$Label} ne "" )
3053
                {
3054
                $Label = $Translate{$Label};
3055
                }
3056
 
3057
            $popup_label->Label ('-text' => $Label,
3058
                                 '-width' => 30,
3059
                                 '-anchor' => 'w',
3060
                                ) -> pack();
3061
            }
3062
 
3063
        # Frame mit den Daten
3064
        my $popup_values = $Tab->Frame() -> pack('-side' => 'left',
3065
                                                 '-expand' => 'y',
3066
                                                 '-anchor' => 'w',
3067
                                                 '-padx' => 10,
3068
                                                 '-pady' => 10,
3069
                                                 );
3070
        # Eingabefelder mit Daten anzeigen
3071
        foreach $Value ( sort keys %{$CfgEdit->{$key}})
3072
            {                          
3073
            $popup_values->Entry ('-textvariable' => \$CfgEdit->{$key}->{$Value},
3074
                                  '-exportselection' => '1',
3075
                                  '-width' => 30,
3076
                                  '-relief' => 'sunken',
3077
                                 ) -> pack();  
3078
            }
3079
        }
3080
    }
3081
 
3082
 
3083
1;
3084
__END__