Subversion Repositories Projects

Rev

Rev 475 | Details | Compare with Previous | Last modification | View Log | RSS feed

Rev Author Line No. Line
475 MarkG 1
#!/usr/bin/perl
2
#y!/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
476 MarkG 44
# 2009-05-25 0.2.1 rw Modified screen text to English by Mark Griffin
475 MarkG 45
###############################################################################
46
 
476 MarkG 47
$Version = "0.2.1 - 2009-05-25";
475 MarkG 48
 
49
use threads;            # http://search.cpan.org/~jdhedden/threads-1.72/threads.pm
50
                        # http://perldoc.perl.org/threads.html
51
use threads::shared;    # http://search.cpan.org/~jdhedden/threads-shared-1.28/shared.pm
52
use Thread::Queue;      # http://search.cpan.org/dist/Thread-Queue-2.11/lib/Thread/Queue.pm
53
use Tk;
54
use Tk::Balloon;
55
use Tk::Dialog;
56
use Tk::Notebook;
57
use Math::Trig;
58
use Time::HiRes qw(usleep);  # http://search.cpan.org/~jhi/Time-HiRes-1.9719/HiRes.pm
59
use XML::Simple;             # http://search.cpan.org/dist/XML-Simple-2.18/lib/XML/Simple.pm
60
 
61
# change working directory to program path
62
my $Cwd = substr ($0, 0, rindex ($0, "mkcockpit.pl"));
63
chdir $Cwd;
64
 
65
# set path for local Perl libs
66
push @INC, $Cwd . "perl/lib", $Cwd . "perl/site/lib";
67
 
68
# Version setting
69
share (%Version);
70
$Version{'mkcockpit.pl'}  = $Version;
71
 
72
# Read configuration
73
 
74
$XmlConfigFile = "mkcockpit.xml";
75
$Cfg = XMLin($XmlConfigFile);
76
 
77
require "track.pl";     # Tracking antenna
78
require "mkcomm.pl";    # MK communication
79
require "logging.pl";   # CSV and GPX Logging
80
require "geserver.pl";  # Google Earth Server
81
require "$Cfg->{'map'}->{'MapDir'}/map.pl";   # Landkarte
82
require "libmap.pl";    # map subs
83
require "translate.pl"; # Übersetzungstable
84
 
85
# Thread fuer Kommunikation mit MK starten
86
# Output: %MkOsd, %MkTarget, %MkNcDebug, %Mk
87
# Input:  Thread-Queue: $MkSendQueue
88
$mk_thr = threads->create (\&MkCommLoop) -> detach();
89
 
90
# Start Logging Thread
91
$log_thr = threads->create (\&MkLogLoop) -> detach();
92
 
93
# Start GoogleEarth Thread
94
$ge_thr = threads->create (\&GeServer) -> detach();
95
 
96
# Aktuell gültige Karte
97
my %Map = %{$Maps{'Current'}};
98
 
99
# Hauptfenster
100
my $main = new MainWindow;
101
$main->title ("MK Mission Cockpit");
102
 
103
#-----------------------------------------------------------------
104
# Menu
105
#-----------------------------------------------------------------
106
 
107
# Menu bar
108
my $menu_bar = $main->Menu;
109
$main->optionAdd("*tearOff", "false");
110
$main->configure ('-menu' => $menu_bar);
111
 
112
my $menu_file = $menu_bar->cascade('-label' => "~File");
113
    $menu_file->command('-label' => 'Preferences',
114
                        '-command' => [\&Configure],
115
                       );
116
    $menu_file->separator;                                     
117
    $menu_file->command('-label' => 'E~xit',
118
                        '-command' => sub{exit(0)},
119
                        );
120
 
121
my $menu_debug = $menu_bar->cascade(-label => "D~ebug");
122
    $menu_debug->command('-label' => 'NC ~OSD Record (O)',
123
                         '-command' => [\&DisplayHash, \%MkOsd, "NC OSD Record (O)", "Display Refresh Heartbeat"],
124
                        );
125
    $menu_debug->command('-label' => 'NC ~Target Record (s)',
126
                         '-command' => [\&DisplayHash, \%MkTarget, "NC Target Record (s)", "Display Refresh Heartbeat"],
127
                        );
128
    $menu_debug->command('-label' => 'NC ~Debug Record (D)',
129
                         '-command' => [\&DisplayHash, \%MkNcDebug, "NC Debug Record (D)", "Display Refresh Heartbeat"],
130
                                        );             
131
    $menu_debug->command('-label' => 'NC ~Other',
132
                         '-command' => [\&DisplayHash, \%Mk, "NC Other", "Display Refresh Heartbeat"],
133
                                        );
134
    $menu_debug->separator;                                    
135
    $menu_debug->command('-label' => 'Tracking ~Antenna Debug Record',
136
                         '-command' => [\&DisplayHash, \%MkTrack, "Tracking Antenna Debug Record", "Display Refresh Heartbeat"],
137
                        );
138
 
139
my $menu_help = $menu_bar->cascade(-label => "~Help");
140
    $menu_help->command('-label' => 'Version',
141
                        '-command' => [\&DisplayHash, \%Version, "Version", "Display"],
142
                       );
143
    $menu_help->separator;
144
    $menu_help->command('-label' => 'About',
145
                        '-command' => sub
146
        {
147
        my $License = <<EOF;
148
Copyright (C) 2009  Rainer Walther (rainerwalther-mail\@web.de)
149
 
150
Creative Commons Lizenz mit den Zusaetzen (by, nc, sa)
151
 
152
Siehe LICENSE.TXT
153
EOF
154
 
155
        my $DlgAbout = $frame_map->Dialog('-title' => 'About MK Mission Cockpit',
156
                                          '-text' => "$License",
157
                                          '-buttons' => ['OK'],
158
                                          '-bitmap' => 'info',
159
                                         );
160
        $DlgAbout->Show;
161
        });  
162
 
163
 
164
# Main window Status line
165
$frame_status = $main->Frame( '-background' => 'lightgray',
166
                         ) -> pack('-side' => 'bottom',
167
                                   '-anchor' => 'w',
168
                                   '-fill' => 'none',
169
                                   '-expand' => 'y',
170
                                    );
171
$status_line = $frame_status->Label ('-text' => 'Status line',
172
                                    ) -> pack ('-side' => 'bottom',
173
                                               );
174
 
175
 
176
#-----------------------------------------------------------------
177
# Frames
178
#-----------------------------------------------------------------                        
179
 
180
#
181
# Frame: Map
182
#
183
 
184
$frame_map = $main->Frame( '-background' => 'lightgray',
185
                           '-relief' => 'sunken',
186
                           '-borderwidth' => 5,
187
                          ) -> pack('-side' => 'top',
188
                                    '-fill' => 'x',
189
                                    );
190
 
191
# Map Überschrift
192
$frame_map_top = $frame_map->Frame() -> pack( '-side' => 'top',
193
                                              '-expand' => 'x',
194
                                              '-anchor' => 'w',
195
                                            );
196
 
197
$frame_map_top->Label ('-text' => "Karte: $Map{'Name'} ($Map{'File'})",
198
                       '-background' => 'lightgray',
199
                       '-relief' => 'flat',
200
                       ) -> pack( '-side' => 'left' );
201
 
202
# Map Statuszeile
203
$map_status = $frame_map->Frame( '-background' => 'lightgray',
204
                               ) -> pack('-side' => 'bottom',
205
                                         '-anchor' => 'w',
206
                                         '-fill' => 'none',
207
                                         '-expand' => 'y',
208
                                        );
209
$map_status_line = $map_status->Label ( '-text' => 'Statuszeile',
210
                                        '-background' => 'lightgray',
211
                                       ) -> pack ('-side' => 'bottom',);
212
 
213
# Map Canvas
214
 
215
# Canvas size
216
$MapSizeX  = $Map{'Size_X'};
217
$MapSizeY  = $Map{'Size_Y'};
218
 
219
$map_canvas = $frame_map->Canvas( '-width'  => $MapSizeX,
220
                                  '-height' => $MapSizeY,
221
                                  '-cursor' => 'cross',
222
                                ) -> pack();
223
 
224
# load Map photo
225
$map_canvas->Photo( 'Map',
226
                    '-file' => "$Cfg->{'map'}->{'MapDir'}/$Map{'File'}",
227
                  );
228
 
229
$map_canvas->createImage( 0, 0,
230
                          '-tags' => 'Map',
231
                          '-anchor' => 'nw',
232
                          '-image'  => 'Map',
233
                        );             
234
 
235
# border polygon
236
$map_canvas->createPolygon( @Map{'Border'},
237
                           '-tags' => 'Map-Border',
238
                           '-fill' => '',
239
                           '-outline' => $Cfg->{'mkcockpit'}->{'ColorAirfield'}, '-width' => 2,
240
                          );
241
 
242
# load Heartbeat icon
243
$map_canvas->Photo( 'HeartbeatSmall',
244
                    '-file' => "$Cfg->{'mkcockpit'}->{'IconHeartSmall'}",
245
                  );
246
$map_canvas->Photo( 'HeartbeatLarge',
247
                    '-file' => "$Cfg->{'mkcockpit'}->{'IconHeartLarge'}",
248
                  );
249
$map_canvas->createImage( $MapSizeX/4, 10,
250
                          '-tags' => 'Heartbeat',
251
                          '-anchor' => 'nw',
252
                          '-image'  => 'HeartbeatSmall',
253
                        );
254
 
255
# load Satellite icon
256
$map_canvas->Photo( 'Satellite-Photo',
257
                    '-file' => "$Cfg->{'mkcockpit'}->{'IconSatellite'}",
258
                               );
259
$map_canvas->createImage($MapSizeX-190, -100,   # hide photo 
260
                         '-tags' => 'Satellite',
261
                         '-anchor' => 'nw',
262
                         '-image'  => 'Satellite-Photo',
263
                        );
264
 
265
# load Waypoint icon
266
$map_canvas->Photo( 'Waypoint-Photo',
267
                    '-file' => "$Cfg->{'mkcockpit'}->{'IconWaypoint'}",
268
                  );
269
# load Target icon
270
$map_canvas->Photo( 'Target-Photo',
271
                    '-file' => "$Cfg->{'mkcockpit'}->{'IconTarget'}",
272
                  );
273
 
274
$map_canvas->createImage(0, -100,   # hide photo 
275
                         '-tags' => 'Target',
276
                         '-anchor' => 'nw',
277
                         '-image'  => 'Target-Photo',
278
                        );
279
 
280
# load Fox icon
281
$map_canvas->Photo( 'Fox-Photo',
282
                    '-file' => "$Cfg->{'mkcockpit'}->{'IconFox'}",
283
                  );
284
$map_canvas->createImage($MapSizeX/2+50, $MapSizeY/2,
285
                         '-tags' => 'Fox',
286
                         '-anchor' => 'nw',
287
                         '-image'  => 'Fox-Photo',
288
                        );
289
 
290
# load WP-Player icons
291
$map_canvas->Photo( 'WpPlay-Foto',  '-file' => "$Cfg->{'waypoint'}->{'IconPlay'}");
292
$map_canvas->Photo( 'WpStop-Foto',  '-file' => "$Cfg->{'waypoint'}->{'IconStop'}");
293
$map_canvas->Photo( 'WpPause-Foto', '-file' => "$Cfg->{'waypoint'}->{'IconPause'}");
294
$map_canvas->Photo( 'WpNext-Foto',  '-file' => "$Cfg->{'waypoint'}->{'IconNext'}");
295
$map_canvas->Photo( 'WpPrev-Foto',  '-file' => "$Cfg->{'waypoint'}->{'IconPrev'}");
296
$map_canvas->Photo( 'WpFirst-Foto', '-file' => "$Cfg->{'waypoint'}->{'IconFirst'}");
297
$map_canvas->Photo( 'WpLast-Foto',  '-file' => "$Cfg->{'waypoint'}->{'IconLast'}");
298
$map_canvas->Photo( 'WpHome-Foto',  '-file' => "$Cfg->{'waypoint'}->{'IconHome'}");
299
 
300
$map_canvas->createImage($MapSizeX/2+100, $MapSizeY-48,
301
                         '-tags' => 'Wp-PlayPause',
302
                         '-anchor' => 'nw',
303
                         '-image'  => 'WpPlay-Foto',
304
                        );
305
$map_canvas->createImage($MapSizeX/2+150, $MapSizeY-48,
306
                         '-tags' => 'Wp-Stop',
307
                         '-anchor' => 'nw',
308
                         '-image'  => 'WpStop-Foto',
309
                        );
310
$map_canvas->createImage($MapSizeX/2, $MapSizeY-48,
311
                         '-tags' => 'Wp-Next',
312
                         '-anchor' => 'nw',
313
                         '-image'  => 'WpNext-Foto',
314
                        );
315
$map_canvas->createImage($MapSizeX/2-50, $MapSizeY-48,
316
                         '-tags' => 'Wp-Prev',
317
                         '-anchor' => 'nw',
318
                         '-image'  => 'WpPrev-Foto',
319
                        );
320
$map_canvas->createImage($MapSizeX/2-100, $MapSizeY-48,
321
                         '-tags' => 'Wp-First',
322
                         '-anchor' => 'nw',
323
                         '-image'  => 'WpFirst-Foto',
324
                        );
325
$map_canvas->createImage($MapSizeX/2+50, $MapSizeY-48,
326
                         '-tags' => 'Wp-Last',
327
                         '-anchor' => 'nw',
328
                         '-image'  => 'WpLast-Foto',
329
                        );
330
$map_canvas->createImage($MapSizeX/2-150, $MapSizeY-48,
331
                         '-tags' => 'Wp-Home',
332
                         '-anchor' => 'nw',
333
                         '-image'  => 'WpHome-Foto',
334
                        );
335
 
336
 
337
# Balloon attached to Canvas
338
$map_balloon = $frame_map->Balloon('-statusbar' => $status_line, );
339
$map_balloon->attach($map_canvas,
340
                     '-balloonposition' => 'mouse',
341
                     '-state' => 'balloon',
342
                     '-msg' => { 'MK-Arrow' => "MikroKopter",
343
                                 'MK-Home-Line' => "Go Home",
344
                                 'MK-Home-Dist' => "Distance to Home",
345
                                 'MK-Target-Line' => "This way to the target",
346
                                 'MK-Target-Dist' => "Distance to target",
347
                                 'MK-Speed' => 'Velocity vector',
348
                                 'Map-Variometer' => 'Variometer',
349
                                 'Map-Variometer-Pointer' => 'Variometer',
350
                                 'Map-Variometer-Skala' => 'Variometer',
351
                                 'Fox' => 'Target for fox hunting',
352
                                 'Heartbeat' => 'MK data activity',
353
                                 'Satellite' => 'Good satellite reception',
354
                                 'Waypoint' => 'Waypoint',
355
                                 'Map-Border' => 'Map border',
356
                                 'Waypoint-Connector' => 'Waypoint Connector',
357
                                 'Wp-PlayPause' => 'Play/Pause',
358
                                 'Wp-Stop' => 'Stop',
359
                                 'Wp-First' => 'Go to First Waypoint',
360
                                 'Wp-Last' => 'Go to Last Waypoint',
361
                                 'Wp-Next' => 'Go to Next Waypoint',
362
                                 'Wp-Prev' => 'Go to Previous Waypoint',
363
                                 'Wp-Home' => 'Go Home',
364
                               },
365
                    );
366
#
367
# Mouse buttons
368
#
369
 
370
# general Mouse button 1
371
$map_canvas->CanvasBind("<Button-1>", sub
372
    {
373
    # print coords in status line
374
    my ($x, $y) = ($Tk::event->x, $Tk::event->y);
375
    my ($Lat, $Lon) = &MapXY2Gps($x, $y);
376
    $map_status_line->configure ('-text' => "Lat: $Lat  Lon: $Lon     x: $x  y: $y");
377
    });
378
 
379
# Mouse button 1 for Fox
380
my $FoxOldx = 0;
381
my $FoxOldy = 0;
382
 
383
# Pick Fox
384
$map_canvas->bind('Fox' => '<Button-1>' => sub
385
    {
386
    # prepare to move Fox
387
    my ($x, $y) = ($Tk::event->x, $Tk::event->y);
388
    $FoxOldx = $x;
389
    $FoxOldy = $y;
390
    $FoxTime = time;
391
    });
392
 
393
# Move Fox
394
$map_canvas->bind('Fox' => '<Button1-Motion>' => sub
395
    {
396
    my ($x, $y) = ($Tk::event->x, $Tk::event->y);
397
    my $id      = $map_canvas->find('withtag', 'current');
398
 
399
    $map_canvas->move($id => $x - $FoxOldx, $y - $FoxOldy);
400
    $FoxOldx = $x;
401
    $FoxOldy = $y;
402
 
403
    if ( time > $FoxTime )
404
        {
405
          # wenn in Bewegung Koordinaten nur 1/s senden
406
        my ($x0, $y0, $x1, $y1) = $map_canvas->bbox ($id);
407
        $x = $x0 + ($x1 - $x0)/2;
408
        $y = $y1;
409
 
410
        my ($Lat, $Lon) = &MapXY2Gps($x, $y);
411
        &MkFlyTo ( '-lat' => $Lat,
412
                   '-lon' => $Lon,
413
                   '-mode' => "Target",
414
                 );
415
        $FoxTime = time;
416
 
417
        $map_status_line->configure ('-text' => "Target coordinates sent -> Lat: $Lat  Lon: $Lon     x: $x  y: $y");
418
        }
419
    });
420
 
421
# Release Fox
422
$map_canvas->bind('Fox' => '<Button1-ButtonRelease>' => sub
423
    {
424
    my ($x, $y) = ($Tk::event->x, $Tk::event->y);
425
    my $id      = $map_canvas->find('withtag', 'current');
426
 
427
    my ($x0, $y0, $x1, $y1) = $map_canvas->bbox ($id);
428
    $x = $x0 + ($x1 - $x0)/2;
429
    $y = $y1;
430
 
431
    my ($Lat, $Lon) = &MapXY2Gps($x, $y);
432
    &MkFlyTo ( '-lat' => $Lat,
433
               '-lon' => $Lon,
434
               '-mode' => "Target",
435
             );
436
 
437
    # Show user that Waypoints in MK are cleared
438
    $WaypointsModified = 1;
439
    &WpRedrawLines();
440
 
441
    $map_status_line->configure ('-text' => "Target coordinates sent -> Lat: $Lat  Lon: $Lon     x: $x  y: $y");
442
    });
443
 
444
# Pick Waypoint
445
$map_canvas->bind('Waypoint' => '<Button-1>' => sub
446
    {
447
    # prepare to move
448
    my ($x, $y) = ($Tk::event->x, $Tk::event->y);
449
    $WpOldx = $x;
450
    $WpOldy = $y;
451
    });
452
 
453
# Move Waypoint
454
$map_canvas->bind('Waypoint' => '<Button1-Motion>' => sub
455
    {
456
    my ($x, $y) = ($Tk::event->x, $Tk::event->y);
457
    my $id      = $map_canvas->find('withtag', 'current');
458
 
459
    # move icon and Wp-Number
460
    my $WpIndex = &WpGetIndexFromId($id);
461
    if ( $WpIndex >= 0 )
462
        {
463
        my $Tag = $Waypoints[$WpIndex]{'Tag'};
464
        $map_canvas->move($Tag => $x - $WpOldx, $y - $WpOldy);
465
        }
466
 
467
    $WpOldx = $x;
468
    $WpOldy = $y;
469
    });
470
 
471
# Release Wp
472
$map_canvas->bind('Waypoint' => '<Button1-ButtonRelease>' => sub
473
    {
474
    my ($x, $y) = ($Tk::event->x, $Tk::event->y);
475
    my $id      = $map_canvas->find('withtag', 'current');
476
 
477
    # take coords from lower/middle icon position
478
    my ($x0, $y0, $x1, $y1) = $map_canvas->bbox ($id);
479
    $x = $x0 + ($x1 - $x0)/2;
480
    $y = $y1;
481
 
482
    # update Waypoint-Array
483
    my $WpIndex = &WpGetIndexFromId($id);
484
    if ( $WpIndex >= 0 )
485
            {
486
        # got it: set new coords
487
 
488
        my ($Lat, $Lon) = &MapXY2Gps($x, $y);
489
        my $Wp = $Waypoints[$WpIndex];
490
        $Wp->{'MapX'} = $x;
491
        $Wp->{'MapY'} = $y;
492
        $Wp->{'Pos_Lat'} = $Lat;
493
        $Wp->{'Pos_Lon'} = $Lon;
494
 
495
        # redraw connector-lines
496
        &WpRedrawLines();
497
 
498
        # red connectors: Wp still have to be sent to MK
499
        $map_canvas->itemconfigure('Waypoint-Connector',
500
                                       '-fill' => $Cfg->{'mkcockpit'}->{'ColorWpResend'},
501
                                      );
502
        $WaypointsModified = 1;
503
 
504
        my $WpNum = $WpIndex + 1;
505
        $map_status_line->configure ('-text' => "Waypoint $WpNum postponed  Lat: $Lat  Lon: $Lon     x: $x  y: $y");
506
        }
507
    });
508
 
509
 
510
#
511
# Waypoint-Player - uses Waypoint-List: @Waypoints
512
#
513
 
514
# WP Player state machine
515
$WpPlayerMode = 'Stop';     # Start, Stop, Pause
516
$WpPlayerIndex = 0;
517
$WpPlayerHoldtime = -1;
518
 
519
# Mouse bindings
520
$map_canvas->bind('Wp-PlayPause' => '<Button-1>' => \&CbWpPlayerPlayPause );
521
$map_canvas->bind('Wp-Next'      => '<Button-1>' => \&CbWpPlayerNext );
522
$map_canvas->bind('Wp-Prev'      => '<Button-1>' => \&CbWpPlayerPrev );
523
$map_canvas->bind('Wp-First'     => '<Button-1>' => \&CbWpPlayerFirst );
524
$map_canvas->bind('Wp-Last'      => '<Button-1>' => \&CbWpPlayerLast );
525
$map_canvas->bind('Wp-Home'      => '<Button-1>' => \&CbWpPlayerHome );
526
$map_canvas->bind('Wp-Stop'      => '<Button-1>' => \&CbWpPlayerStop );
527
 
528
 
529
# Focus Canvas, if any key pressed. Needed for the following key-bindings
530
$main->bind('<Any-Enter>' => sub { $map_canvas->Tk::focus });
531
 
532
# Disable default bindings on canvas
533
$main->bind('Tk::Canvas',"<$_>",undef)for qw /Left Right Up Down/;
534
 
535
# keyboard bindings
536
$map_canvas->Tk::bind( '<Key-space>', \&CbWpPlayerPlayPause );
537
$map_canvas->Tk::bind( '<Key-n>'    , \&CbWpPlayerNext );
538
$map_canvas->Tk::bind( '<Key-p>'    , \&CbWpPlayerPrev );
539
$map_canvas->Tk::bind( '<Key-f>'    , \&CbWpPlayerFirst );
540
$map_canvas->Tk::bind( '<Key-l>'    , \&CbWpPlayerLast );
541
$map_canvas->Tk::bind( '<Key-h>'    , \&CbWpPlayerHome );
542
$map_canvas->Tk::bind( '<Key-s>'    , \&CbWpPlayerStop );
543
 
544
 
545
# Mouse button 3 context menu
546
my $map_menu = $map_canvas->Menu('-tearoff' => 0,
547
                                 '-title' =>'None',
548
                                 '-menuitems' =>
549
    [
550
     [Button => "Add Waypoint and send",  -command => sub
551
        {
552
        my $Tag = sprintf "Waypoint-%d.%d", time, int (rand(9)) ;   # kind of unique Tag for this Wp
553
 
554
        # Waypoint Icon
555
        my $IconHeight = 48;
556
        my $IconWidth = 48;
557
        $map_canvas->createImage($MapCanvasX-$IconWidth/2, $MapCanvasY-$IconHeight,
558
                                 '-tags' => ['Waypoint', $Tag],
559
                                 '-anchor' => 'nw',
560
                                 '-image'  => 'Waypoint-Photo',
561
                                );
562
 
563
        # Waypoint Number
564
        my $WpNumber = scalar @Waypoints + 1;
565
        $map_canvas->createText ( $MapCanvasX+3, $MapCanvasY-$IconHeight/2+12,
566
                                  '-tags' => ['WaypointNumber', $Tag],
567
                                  '-text' => $WpNumber,
568
                                  '-font' => '-*-Arial-Bold-R-Normal--*-100-*',
569
                                  '-fill' => $Cfg->{'mkcockpit'}->{'ColorWpNumber'},
570
                                  '-anchor' => 'w',
571
                                 );    
572
 
573
        $map_canvas->lower('Waypoint', 'Fox');            # waypoint below Fox
574
        $map_canvas->lower('WaypointNumber', 'Waypoint'); # Nr below waypoint
575
 
576
        # send Wp to MK         
577
        ($Lat, $Lon) = &MapXY2Gps($MapCanvasX, $MapCanvasY);
578
        &MkFlyTo ( '-lat' => $Lat,
579
                   '-lon' => $Lon,
580
                   '-mode' => "Waypoint"
581
                 );
582
 
583
        # save Wp-Hash in Waypoint-Array
584
        my $Wp = {};        
585
        $Wp->{'Tag'} = $Tag;
586
        $Wp->{'MapX'} = $MapCanvasX;
587
        $Wp->{'MapY'} = $MapCanvasY;
588
        $Wp->{'Pos_Lat'} = $Lat;
589
        $Wp->{'Pos_Lon'} = $Lon;
590
        $Wp->{'Pos_Alt'} = $MkOsd{'CurPos_Alt'};
591
        $Wp->{'Heading'}         = $Cfg->{'waypoint'}->{'DefaultHeading'};
592
        $Wp->{'ToleranceRadius'} = $Cfg->{'waypoint'}->{'DefaultToleranceRadius'};
593
        $Wp->{'Holdtime'}        = $Cfg->{'waypoint'}->{'DefaultHoldtime'};
594
        $Wp->{'Event_Flag'}      = $Cfg->{'waypoint'}->{'DefaultEventFlag'};
595
        push @Waypoints, $Wp;
596
 
597
        # redraw connector-lines
598
        &WpRedrawLines();  
599
 
600
        $map_status_line->configure ('-text' => "Waypoint is stored and sent -> Lat: $Lat Lon: $Lon");
601
        }],
602
 
603
 
604
     [Button => "Waypoint Properties",  -command => sub
605
        {
606
        # find Wp-Hash for selected icon/tag
607
        my $WpIndex = &WpGetIndexFromId($MapCanvasId);
608
        if ( $WpIndex >= 0 )
609
            {
610
            my $Wp = $Waypoints[$WpIndex];
611
            my $WpNum = $WpIndex + 1;
612
 
613
            &DisplayHash ($Wp, "Waypoint Properties $WpNum", "Edit Waypoint Refresh");
614
 
615
            $map_status_line->configure ('-text' => "Wegpunkt $WpNum Eigenschaften");
616
            }
617
        }],
618
 
619
     [Button => " Send all Waypoints again",  -command => sub
620
        {
621
        &WpSendAll();
622
 
623
        $map_status_line->configure ('-text' => "All waypoints sent");
624
        }],
625
 
626
      '',   # Separator
627
 
628
     [Button => "Waypoint loaded and sent",  -command => sub
629
        {
630
        $WpFile = $main->getOpenFile('-defaultextension' => ".xml",
631
                                     '-filetypes'        =>
632
                                         [['Waypoints',     '.xml' ],
633
                                          ['All Files',     '*', ],
634
                                         ],
635
                                     '-initialdir' => $Cfg->{'waypoint'}->{'WpDir'},
636
                                     '-title' => "Wegpunkte laden",
637
                                    );
638
        if ( -f $WpFile )
639
            {
640
            # XML in Hash-Ref lesen
641
            my $Wp = XMLin($WpFile, ForceArray => 1);
642
 
643
            # XML Hash-Ref in Wp-Array umkopieren
644
            undef @Waypoints;
645
            foreach $key (sort keys %$Wp)
646
                {
647
                my $Point = $Wp->{$key}->[0];
648
 
649
                # relative Pixelkoordinaten auf Bildgroesse umrechnen
650
                if ( $Point->{'MapX'} <= 1  and  $Point->{'MapY'} <= 1 )
651
                    {
652
                    $Point->{'MapX'} = int ( $Point->{'MapX'} * $MapSizeX + 0.5 );
653
                    $Point->{'MapY'} = int ( $Point->{'MapY'} * $MapSizeY + 0.5 );
654
                    }
655
 
656
                # GPS Koordinaten für die aktuelle Karte neu aus Map x/y berechnen
657
                my ($Lat, $Lon) = &MapXY2Gps($Point->{'MapX'}, $Point->{'MapY'});
658
                $Point->{'Pos_Lat'} = $Lat;
659
                $Point->{'Pos_Lon'} = $Lon;
660
 
661
                push @Waypoints, $Point;
662
                }
663
 
664
            &WpSendAll();
665
            &WpRedrawLines();
666
            &WpRedrawIcons();
667
 
668
            $map_status_line->configure ('-text' => "Waypoints from $WpFile loaded and sent");
669
            }
670
        }],    
671
 
672
     [Button => "Save Waypoints",  -command => sub
673
        {
674
        $WpFile = $main->getSaveFile('-defaultextension' => ".xml",
675
                                     '-filetypes'        =>
676
                                       [['Waypoints',     '.xml' ],
677
                                        ['All Files',     '*', ],
678
                                       ],
679
                                     '-initialdir' => $Cfg->{'waypoint'}->{'WpDir'},
680
                                     '-title' => "Save Waypoints",
681
                                    );
682
 
683
        # Waypoint-Array in Hash umkopieren
684
        my %Wp;
685
        for  $i ( 0 .. $#Waypoints )
686
            {
687
 
688
            my $key = sprintf ("WP-%04d", $i);
689
            $Wp{$key} = $Waypoints[$i];
690
 
691
            # Pixelkoordinaten relativ zur Bildgroesse speichern
692
            $Wp{$key}{'MapX'} /= $MapSizeX;
693
            $Wp{$key}{'MapY'} /= $MapSizeY;
694
            }
695
 
696
        # WP-Hash als XML speichern
697
        &XMLout (\%Wp,
698
                 'OutputFile' => $WpFile,
699
                 'AttrIndent' => '1',
700
                 'RootName' => 'Waypoints',
701
                );
702
 
703
        $map_status_line->configure ('-text' => "Waypoints stored in $WpFile");
704
        }],
705
 
706
     '',   # Separator
707
 
708
     [Button => "Delete waypoint",  -command => sub
709
        {
710
        # find Wp-Hash for selected icon/tag
711
        my $WpIndex = &WpGetIndexFromId($MapCanvasId);
712
        if ( $WpIndex >= 0 )
713
            {
714
            my $Wp = $Waypoints[$WpIndex];
715
 
716
            # remove icon and Wp-Number on canvas;
717
            $map_canvas->delete($Wp->{'Tag'});
718
 
719
            # delete Wp in Waypoint-Array
720
            splice @Waypoints, $WpIndex, 1;
721
 
722
            # redraw connector-lines
723
            $WaypointsModified = 1;
724
            &WpRedrawLines();  
725
            &WpRedrawIcons();  # wg. Wp-Nummern
726
 
727
            $WpNum = $WpIndex + 1;
728
            $map_status_line->configure ('-text' => "Waypoint $WpNum deleted");
729
            }
730
        }],
731
 
732
     [Button => "Delete all waypoints and send",  -command => sub
733
        {
734
        undef @Waypoints;
735
 
736
        # remove all Wp-Icons and Wp-Number on canvas
737
        $map_canvas->delete('Waypoint');
738
        $map_canvas->delete('WaypointNumber');
739
 
740
        # redraw connector-lines
741
        &WpRedrawLines();  
742
 
743
        &WpSendAll();
744
 
745
        $map_status_line->configure ('-text' => "All Waypoints $WpIndex deleted");
746
        }],
747
 
748
    '',   # Separator
749
 
750
     [Button => "Fly immediately to Target",  -command => sub
751
        {
752
        &MkFlyTo ( '-x' => $MapCanvasX,
753
                   '-y' => $MapCanvasY,
754
                   '-mode' => "Target"
755
                 );
756
 
757
        # redraw connector-lines
758
        $WaypointsModified = 1;
759
        &WpRedrawLines();  
760
 
761
        $map_status_line->configure ('-text' => "Target coordinates sent -> Lat: $Lat  Lon: $Lon     x: $x  y: $y");
762
        }],
763
    ]
764
                                    );
765
$map_canvas->CanvasBind("<Button-3>" => [ sub
766
    {
767
    $map_canvas->focus;
768
    my($w, $x, $y) = @_;
769
    ($MapCanvasX, $MapCanvasY) = ($Tk::event->x, $Tk::event->y);
770
    $MapCanvasId = $map_canvas->find('withtag', 'current');
771
    $map_menu->post($x, $y);
772
    }, Ev('X'), Ev('Y') ] );
773
 
774
 
775
#
776
# Objects on canvas
777
#
778
 
779
# Line from MK to Home
780
$map_canvas->createLine ( $MapSizeX/2, $MapSizeY/2, $MapSizeX/2, $MapSizeY/2,
781
                          '-tags' => 'MK-Home-Line',
782
                          '-arrow' => 'none',
783
                          '-fill' => $Cfg->{'mkcockpit'}->{'ColorHomeLine'},
784
                          '-width' => 3,
785
                         );
786
 
787
# Text Entfernung positioniert an der Home-Linie
788
$map_canvas->createText ( $MapSizeX/2 + 8, $MapSizeY/2 - 8,
789
                          '-tags' => 'MK-Home-Dist',
790
                          '-text' => '0 m',
791
                          '-anchor' => 'w',
792
                          '-font' => '-*-Arial-Bold-R-Normal--*-200-*',
793
                          '-fill' => $Cfg->{'mkcockpit'}->{'ColorHomeDist'},
794
                          );
795
 
796
# Line from MK to Target, draw invisible out of sight
797
$map_canvas->createLine ( 0, -100, 0, -100,
798
                          '-tags' => 'MK-Target-Line',
799
                          '-arrow' => 'none',
800
                          '-fill' => $Cfg->{'mkcockpit'}->{'ColorTargetLine'},
801
                          '-width' => 3,
802
                         );
803
 
804
# Text Entfernung positioniert an der Target-Linie
805
$map_canvas->createText ( 0, -100,
806
                          '-tags' => 'MK-Target-Dist',
807
                          '-text' => '0 m',
808
                          '-anchor' => 'w',
809
                          '-font' => '-*-Arial-Bold-R-Normal--*-200-*',
810
                          '-fill' => $Cfg->{'mkcockpit'}->{'ColorTargetDist'},
811
                          );
812
 
813
# MK Geschwindigkeits-Vektor
814
$MapMkSpeedLen = 60;    # Länge Speed-Zeiger
815
my $x0 = $MapSizeX/2;
816
my $y0 = $MapSizeY/2;
817
my $x1 = $MapSizeX/2;
818
my $y1 = $MapSizeY/2 - $MapMkSpeedLen;
819
$map_canvas->createLine ( $x0, $y0, $x1, $y1,
820
                          '-tags' => 'MK-Speed',
821
                          '-arrow' => 'last',
822
                          '-arrowshape' => [10, 10, 3 ],
823
                          '-fill' => $Cfg->{'mkcockpit'}->{'ColorSpeedVector'},
824
                          '-width' => 4,
825
                         );
826
 
827
# MK als Pfeilspitze einer Linie darstellen
828
$MapMkLen = 25;
829
my $x0 = $MapSizeX/2;
830
my $y0 = $MapSizeY/2 + $MapMkLen/2;
831
my $x1 = $MapSizeX/2;
832
my $y1 = $MapSizeY/2 - $MapMkLen/2;
833
$map_canvas->createLine ( $x0, $y0, $x1, $y1,
834
                          '-tags' => 'MK-Arrow',
835
                          '-arrow' => 'last',
836
                          '-arrowshape' => [25, 30, 10 ],
837
                          '-fill' => $Cfg->{'mkcockpit'}->{'ColorMkSatNo'},
838
                          '-width' => 1
839
                         );
840
 
841
 
842
# OSD Daten auf Karte anzeigen
843
 
844
# Flugzeit
845
$map_canvas->createText ( $MapSizeX/2 - 40, 20,
846
                          '-tags' => 'MK-OSD-Tim-Label',
847
                          '-text' => 'TIM',
848
                          '-font' => '-*-Arial-Bold-R-Normal--*-150-*',
849
                          '-fill' => $Cfg->{'mkcockpit'}->{'ColorOsd'},
850
                          '-anchor' => 'w',
851
                         );
852
 
853
$map_canvas->createText ( $MapSizeX/2, 20,
854
                          '-tags' => 'MK-OSD-Tim-Value',
855
                          '-text' => $MkFlyingTime,            # $MkOsd{'FlyingTime'},
856
                          '-font' => '-*-Arial-Bold-R-Normal--*-270-*',
857
                          '-fill' => $Cfg->{'mkcockpit'}->{'ColorOsd'},
858
                          '-anchor' => 'w',
859
                         );
860
 
861
# Batterie Spannung
862
$map_canvas->createText ( $MapSizeX/2 - 40, 50,
863
                          '-tags' => 'MK-OSD-Bat-Label',
864
                          '-text' => 'BAT',
865
                          '-font' => '-*-Arial-Bold-R-Normal--*-150-*',
866
                          '-fill' => $Cfg->{'mkcockpit'}->{'ColorOsd'},
867
                          '-anchor' => 'w',
868
                         );
869
 
870
$map_canvas->createText ( $MapSizeX/2, 50,
871
                          '-tags' => 'MK-OSD-Bat-Value',
872
                          '-text' => sprintf ("%3.1f V", $MkOsd{'UBat'}),
873
                          '-font' => '-*-Arial-Bold-R-Normal--*-270-*',
874
                          '-fill' => $Cfg->{'mkcockpit'}->{'ColorOsd'},
875
                          '-anchor' => 'w',
876
                         );
877
 
878
# Ground speed
879
$map_canvas->createText ( 10,  20,
880
                          '-tags' => 'MK-OSD-Spd-Label',
881
                          '-text' => 'SPD',
882
                          '-font' => '-*-Arial-Bold-R-Normal--*-150-*',
883
                          '-fill' => $Cfg->{'mkcockpit'}->{'ColorOsd'},
884
                          '-anchor' => 'w',
885
                         );
886
 
887
$map_canvas->createText ( 50,  20,
888
                          '-tags' => 'MK-OSD-Spd-Value',
889
                          '-text' => sprintf ("%3d km/h", $MkOsd{'GroundSpeed'} * 0.036),
890
                          '-font' => '-*-Arial-Bold-R-Normal--*-270-*',
891
                          '-fill' => $Cfg->{'mkcockpit'}->{'ColorOsd'},
892
                          '-anchor' => 'w',
893
                         );
894
 
895
# Hoehe (Luftdruck)
896
$map_canvas->createText ( 10,  50,
897
                          '-tags' => 'MK-OSD-Alt-Label',
898
                          '-text' => 'ALT',
899
                          '-font' => '-*-Arial-Bold-R-Normal--*-150-*',
900
                          '-fill' => $Cfg->{'mkcockpit'}->{'ColorOsd'},
901
                          '-anchor' => 'w',
902
                         );
903
 
904
$map_canvas->createText ( 50,  50,
905
                          '-tags' => 'MK-OSD-Alt-Value',
906
                          '-text' => sprintf ("%3d m", $MkOsd{'Altimeter'}/$Cfg->{'mkcockpit'}->{'AltFactor'}),
907
                          '-font' => '-*-Arial-Bold-R-Normal--*-270-*',
908
                          '-fill' => $Cfg->{'mkcockpit'}->{'ColorOsd'},
909
                          '-anchor' => 'w',
910
                         );
911
 
912
# Variometer 
913
$map_canvas->createText ( 10,  80,
914
                          '-tags' => 'MK-OSD-Vsi-Label',
915
                          '-text' => 'VSI',
916
                          '-font' => '-*-Arial-Bold-R-Normal--*-150-*',
917
                          '-fill' => $Cfg->{'mkcockpit'}->{'ColorOsd'},
918
                          '-anchor' => 'w',
919
                         );
920
 
921
$map_canvas->createText ( 50,  80,
922
                          '-tags' => 'MK-OSD-Vsi-Value',
923
                          '-text' => sprintf ("%3d", $MkOsd{'Variometer'}),
924
                          '-font' => '-*-Arial-Bold-R-Normal--*-270-*',
925
                          '-fill' => $Cfg->{'mkcockpit'}->{'ColorOsd'},
926
                          '-anchor' => 'w',
927
                         );
928
 
929
# Anzahl Satelitten
930
$map_canvas->createText ( $MapSizeX - 130, 20,
931
                          '-tags' => 'MK-OSD-Sat-Label',
932
                          '-text' => 'SAT',
933
                          '-font' => '-*-Arial-Bold-R-Normal--*-150-*',
934
                          '-fill' => $Cfg->{'mkcockpit'}->{'ColorOsd'},
935
                          '-anchor' => 'w',
936
                         );
937
 
938
$map_canvas->createText ( $MapSizeX - 80, 20,
939
                          '-tags' => 'MK-OSD-Sat-Value',
940
                          '-text' => "$MkOsd{'SatsInUse'}",
941
                          '-font' => '-*-Arial-Bold-R-Normal--*-270-*',
942
                          '-fill' => $Cfg->{'mkcockpit'}->{'ColorOsd'},
943
                          '-anchor' => 'w',
944
                         );
945
 
946
# Wegpunkte
947
$map_canvas->createText ( $MapSizeX - 130, 50,
948
                          '-tags' => 'MK-OSD-Wp-Label',
949
                          '-text' => 'WPT',
950
                          '-font' => '-*-Arial-Bold-R-Normal--*-150-*',
951
                          '-fill' => $Cfg->{'mkcockpit'}->{'ColorOsd'},
952
                          '-anchor' => 'w',
953
                         );
954
 
955
$map_canvas->createText ( $MapSizeX - 80, 50,
956
                          '-tags' => 'MK-OSD-Wp-Value',
957
                          '-text' => $MkOsd{'WaypointIndex'} . "/" . $MkOsd{'WaypointNumber'} ,
958
                          '-font' => '-*-Arial-Bold-R-Normal--*-270-*',
959
                          '-fill' => $Cfg->{'mkcockpit'}->{'ColorOsd'},
960
                          '-anchor' => 'w',
961
                         );
962
# Navigation Mode
963
$map_canvas->createText ( $MapSizeX - 130, 80,
964
                          '-tags' => 'MK-OSD-Mode-Label',
965
                          '-text' => 'MOD',
966
                          '-font' => '-*-Arial-Bold-R-Normal--*-150-*',
967
                          '-fill' => $Cfg->{'mkcockpit'}->{'ColorOsd'},
968
                          '-anchor' => 'w',
969
                         );
970
 
971
$map_canvas->createText ( $MapSizeX - 80, 80,
972
                          '-tags' => 'MK-OSD-Mode-Value',
973
                          '-text' => '' ,
974
                          '-font' => '-*-Arial-Bold-R-Normal--*-270-*',
975
                          '-fill' => $Cfg->{'mkcockpit'}->{'ColorOsd'},
976
                          '-anchor' => 'w',
977
                         );
978
 
979
# Waypoint Holdtime count down
980
$map_canvas->createText ( $MapSizeX - 130, -110,
981
                          '-tags' => 'MK-OSD-Holdtime-Label',
982
                          '-text' => 'HLD',                             # Holdtime Count Down
983
                          '-font' => '-*-Arial-Bold-R-Normal--*-150-*',
984
                          '-fill' => $Cfg->{'mkcockpit'}->{'ColorOsd'},
985
                          '-anchor' => 'w',
986
                         );
987
 
988
$map_canvas->createText ( $MapSizeX - 80, -110,
989
                          '-tags' => 'MK-OSD-Holdtime-Value',
990
                          '-text' => '--' ,
991
                          '-font' => '-*-Arial-Bold-R-Normal--*-270-*',
992
                          '-fill' => $Cfg->{'mkcockpit'}->{'ColorOsd'},
993
                          '-anchor' => 'w',
994
                         );                      
995
 
996
# Variometer on canvas
997
my @Polygon;
998
for ( $y = -100; $y <= 100; $y += 10)
999
    {
1000
    my $Len = 5;
1001
    if ( ($y % 50) == 0 )
1002
        {
1003
        $Len = 10;
1004
        $map_canvas->createText ( $Len+5, $MapSizeY/2 + $y,
1005
                                  '-tags' => 'Map-Variometer-Skala',
1006
                                  '-text' => sprintf ("%3d", -$y / 10),
1007
                                  '-anchor' => 'w',
1008
                                  '-font' => '-*-Arial-Normal-R-Normal--*-150-*',
1009
                          '-fill' => $Cfg->{'mkcockpit'}->{'ColorVariometer'},
1010
                          );
1011
        }
1012
    push @Polygon, (   0, $MapSizeY/2 + $y);
1013
    push @Polygon, ($Len, $MapSizeY/2 + $y);
1014
    push @Polygon, (   0, $MapSizeY/2 + $y);
1015
    }
1016
 
1017
$map_canvas->createLine(@Polygon,
1018
                        '-tags' => 'Map-Variometer',
1019
                        '-fill' => $Cfg->{'mkcockpit'}->{'ColorVariometer'},
1020
                        '-width' => 2,
1021
                        '-arrow' => 'none',
1022
                       );
1023
# Vario Pointer
1024
$map_canvas->createPolygon( 5, $MapSizeY/2, 20, $MapSizeY/2+10, 20, $MapSizeY/2-10,
1025
                           '-tags' => 'Map-Variometer-Pointer',
1026
                           '-fill' => $Cfg->{'mkcockpit'}->{'ColorVariometerPointer'},
1027
                           '-outline' => 'black', '-width' => 1,
1028
                          );
1029
 
1030
# Tracking Canvas
1031
 
1032
if ( $Cfg->{'track'}->{'Active'} =~ /y/i )
1033
    {
1034
    # Canvas size
1035
    $TrackSizeX  = 125;
1036
    $TrackSizeY  = 100;
1037
    $TrackOffY   = $TrackSizeY - $MapSizeY + 20;
1038
    $TrackPtrLen = 50;    # Länge Zeiger
1039
 
1040
    # draw in map-canvas
1041
    $track_canvas = $map_canvas;
1042
 
1043
    # Ziffernblatt
1044
    my $x0 = $TrackSizeX/2 - $TrackPtrLen;
1045
    my $y0 = $TrackSizeY + $TrackPtrLen - $TrackOffY;
1046
    my $x1 = $TrackSizeX/2 + $TrackPtrLen;
1047
    my $y1 = $TrackSizeY   - $TrackPtrLen - $TrackOffY;
1048
    $track_canvas->createArc ( $x0, $y0, $x1, $y1,
1049
                               '-extent' => '200',
1050
                               '-start' => '-10',
1051
                               '-style' => 'chord',
1052
                               '-outline' => 'gray', '-width' => '1',
1053
                             );
1054
 
1055
    # Skala Ziffernblatt
1056
    for ($i=0; $i<=180; $i+=15)
1057
        {
1058
        my $pi = 3.14159265358979;
1059
        my $x0 = $TrackSizeX/2 - ($TrackPtrLen - 20) * cos($i / 180 * $pi);
1060
        my $y0 = $TrackSizeY   - ($TrackPtrLen - 20) * sin($i / 180 * $pi) - $TrackOffY;
1061
        my $x1 = $TrackSizeX/2 - ($TrackPtrLen - 28) * cos($i / 180 * $pi);
1062
        my $y1 = $TrackSizeY   - ($TrackPtrLen - 28) * sin($i / 180 * $pi) - $TrackOffY;
1063
        $track_canvas->createLine ( $x0, $y0, $x1, $y1,
1064
                                   '-fill' => 'white',
1065
                                   '-width' => 1,
1066
                                  );
1067
        }
1068
 
1069
    # Skala Beschriftung Ziffernblatt
1070
    for ($i=0; $i<=180; $i+=45)
1071
        {
1072
        my $pi = 3.14159265358979;
1073
        my $x0 = $TrackSizeX/2 - ($TrackPtrLen - 12) * cos($i / 180 * $pi);
1074
        my $y0 = $TrackSizeY   - ($TrackPtrLen - 12) * sin($i / 180 * $pi) - $TrackOffY;
1075
        $track_canvas->createText ( $x0, $y0,
1076
                                   '-text' => $i - 90,
1077
                                   '-fill' => 'white',
1078
                                  );
1079
        }
1080
 
1081
    # Ziffernblatt Beschriftung Einheit
1082
    my $x0 = $TrackSizeX/2;
1083
    my $y0 = $MapSizeY -6;
1084
    $track_canvas->createText ( $x0, $y0,
1085
                                '-text' => "Antenna Angle",
1086
                                '-justify' => 'center',
1087
                                '-fill' => 'white',
1088
                                );
1089
 
1090
    # Zeiger
1091
    my $x0 = $TrackSizeX/2;
1092
    my $y0 = $TrackSizeY - 0 - $TrackOffY;
1093
    my $x1 = $TrackSizeX/2;
1094
    my $y1 = $TrackSizeY - ($TrackPtrLen - 22) - $TrackOffY;
1095
    $track_ptr_id= $track_canvas->createLine ( $x0, $y0, $x1, $y1,
1096
                                               '-tags' => 'Track-Ptr',
1097
                                               '-arrow' => 'last',
1098
                                               '-arrowshape' => [20, 30, 5 ],
1099
                                               '-fill' => 'red',
1100
                                               '-width' => 8,
1101
                                              );
1102
    # Zeiger Center
1103
    my $Dia = 7;
1104
    my $x0 = $TrackSizeX/2 - $Dia;
1105
    my $y0 = $TrackSizeY + $Dia - $TrackOffY;
1106
    my $x1 = $TrackSizeX/2 + $Dia;
1107
    my $y1 = $TrackSizeY   - $Dia - $TrackOffY;
1108
    $track_canvas->createArc ( $x0, $y0, $x1, $y1,
1109
                               '-extent' => '359',
1110
                               '-outline' => 'gray', '-width' => 1,
1111
                               '-fill' => 'gray',
1112
                             );
1113
    }
1114
 
1115
#-----------------------------------------------------------------
1116
# Timer
1117
#-----------------------------------------------------------------                        
1118
 
1119
#
1120
# Timer: 5s
1121
#
1122
$main->repeat (5000, sub
1123
    {
1124
    if ( ! $MkSendWp )
1125
        {
1126
        # Query Frequency OSD und Debug regelmäßig neu einstellen, falls Übertragungsfehler
1127
        $MkSendQueue->enqueue( "o", "$AddrNC", pack ("C", 10) );   # Frequency OSD Record, * 10ms
1128
        $MkSendQueue->enqueue( "d", "$AddrNC", pack ("C", 10) );   # Frequency MK Debug Record, * 10ms
1129
        $MkSendQueue->enqueue( "v", "$AddrNC", "");   # Version
1130
        $MkSendQueue->enqueue( "e", "$AddrNC", "");   # Error Text Request
1131
        }
1132
    });
1133
 
1134
#       
1135
# Timer: 0.1s - Map Overlay aktualisieren
1136
#
1137
$frame_map_top->repeat (100, sub
1138
    {
1139
    lock (%MkOsd);              # until end of block
1140
 
1141
    # Aktuell gültige Karte
1142
    my %Map = %{$Maps{'Current'}};
1143
 
1144
    if ( $MkOsd{'_Timestamp'} >= time-2 )
1145
        {
1146
        # Gueltige OSD Daten
1147
        my $SatsInUse = $MkOsd{'SatsInUse'};
1148
        if ( $SatsInUse > 0  and  $MkOsd{'CurPos_Stat'} == 1 and $MkOsd{'HomePos_Stat'} == 1 )
1149
            {
1150
            # ausreichender GPS Empfang
1151
 
1152
            # get x,y map coords of current position
1153
            my ($C_x, $C_y, $C_Angel) = &MapGps2XY($MkOsd{'CurPos_Lat'}, $MkOsd{'CurPos_Lon'}, $MkOsd{'CompassHeading'});
1154
 
1155
            # rotate MK arrow
1156
            my $dy = sin (deg2rad $C_Angel) * ($MapMkLen/2);
1157
            my $dx = cos (deg2rad $C_Angel) * ($MapMkLen/2);
1158
            my $x0 = $C_x - $dx;
1159
            my $y0 = $C_y - $dy;
1160
            my $x1 = $C_x + $dx;
1161
            my $y1 = $C_y + $dy;
1162
            $map_canvas->coords ('MK-Arrow', $x0, $y0, $x1, $y1);
1163
 
1164
            # Update speed vector
1165
            my $MapAngel = &MapAngel();   # Norh to Map-Horizont
1166
            my $GpsSpeedNorth = $MkNcDebug{'Analog_21'};
1167
            my $GpsSpeedEast  = $MkNcDebug{'Analog_22'};
1168
            my $PhiGpsSpeed = rad2deg atan2 ( $GpsSpeedEast, $GpsSpeedNorth );
1169
            $PhiMapSpeed = $PhiGpsSpeed - $MapAngel;
1170
 
1171
            # 555 cm/s ~ 20 km/h -> Zeigerlänge = $MkSpeedLen bei 20 km/h
1172
            my $dy = sin (deg2rad $PhiMapSpeed) * $MapMkSpeedLen * $MkOsd{'GroundSpeed'} / 555;
1173
            my $dx = cos (deg2rad $PhiMapSpeed) * $MapMkSpeedLen * $MkOsd{'GroundSpeed'} / 555;
1174
            my $x0 = $C_x;
1175
            my $y0 = $C_y;
1176
            my $x1 = $C_x + $dx;
1177
            my $y1 = $C_y + $dy;
1178
            $map_canvas->coords ('MK-Speed', $x0, $y0, $x1, $y1);
1179
 
1180
            # Update Line between Home and MK
1181
            my ($H_x, $H_y) = &MapGps2XY($MkOsd{'HomePos_Lat'}, $MkOsd{'HomePos_Lon'});
1182
            $map_canvas->coords ('MK-Home-Line', $H_x, $H_y, $C_x, $C_y);
1183
 
1184
            # Update Distance between Home and MK
1185
            my ($Dist, $Bearing) = MapGpsTo($MkOsd{'CurPos_Lat'}, $MkOsd{'CurPos_Lon'},
1186
                                                   $MkOsd{'HomePos_Lat'}, $MkOsd{'HomePos_Lon'} );
1187
            my $x = ($C_x - $H_x) / 2 + $H_x + 8;
1188
            my $y = ($C_y - $H_y) / 2 + $H_y + 8;
1189
            $map_canvas->coords ('MK-Home-Dist', $x, $y);
1190
            $map_canvas->itemconfigure ('MK-Home-Dist',
1191
                                        '-text' => sprintf ("%4d m", $Dist),
1192
                                       );
1193
 
1194
            # Update OSD - Sat dependent values
1195
            $map_canvas->itemconfigure ('MK-OSD-Spd-Value', '-text' => sprintf ("%3d km/h", $MkOsd{'GroundSpeed'} * 0.036) );
1196
 
1197
            # Alt = average Luftdruck und Sat
1198
            my $Alt = int ( ($MkOsd{'Altimeter'} / $Cfg->{'mkcockpit'}->{'AltFactor'} +
1199
                             $MkOsd{'CurPos_Alt'} - $MkOsd{'HomePos_Alt'} + 0.5) / 2 );
1200
            $map_canvas->itemconfigure ('MK-OSD-Alt-Value', '-text' => sprintf ("%d m", $Alt) );
1201
 
1202
            if ( $MkOsd{'TargetPos_Stat'} == 1 )
1203
                {
1204
                # Valid Target
1205
                # Update Line between Target and MK
1206
                my ($T_x, $T_y) = &MapGps2XY($MkOsd{'TargetPos_Lat'}, $MkOsd{'TargetPos_Lon'});
1207
                $map_canvas->coords ('MK-Target-Line', $C_x, $C_y, $T_x, $T_y);
1208
 
1209
                # Update Distance between Target and MK
1210
                my ($Dist, $Bearing) = MapGpsTo($MkOsd{'CurPos_Lat'}, $MkOsd{'CurPos_Lon'},
1211
                                                        $MkOsd{'TargetPos_Lat'}, $MkOsd{'TargetPos_Lon'} );
1212
                my $x = ($C_x - $T_x) / 2 + $T_x - 8;
1213
                my $y = ($C_y - $T_y) / 2 + $T_y + 8;
1214
                $map_canvas->coords ('MK-Target-Dist', $x, $y);
1215
                $map_canvas->itemconfigure ('MK-Target-Dist',
1216
                                            '-text' => sprintf ("%4d m", $Dist),
1217
                                           );
1218
                # show target icon
1219
                my $IconHeight = 48;
1220
                my $IconWidth = 48;
1221
                $map_canvas->coords('Target', $T_x - $IconWidth/2, $T_y - $IconHeight );
1222
                }
1223
            else
1224
                {
1225
                # No valid Target, move target line out of sight/canvas
1226
                $map_canvas->coords ('MK-Target-Line', 0, -100, 0, -100);
1227
                $map_canvas->coords ('MK-Home-Dist', 0, -100);
1228
 
1229
                # hide target icon
1230
                $map_canvas->coords('Target', 0, -100, );
1231
                }
1232
            }
1233
        else
1234
            {
1235
            # kein ausreichender Sat-Empfang
1236
            $map_canvas->itemconfigure ('MK-OSD-Spd-Value', '-text' => sprintf ("%3d km/h", 0 ) );
1237
            }
1238
 
1239
        # Update OSD - non Sat dependent values
1240
        $map_canvas->itemconfigure ('MK-OSD-Bat-Value', '-text' => sprintf ("%3.1f V", $MkOsd{'UBat'}) );
1241
        $map_canvas->itemconfigure ('MK-OSD-Vsi-Value', '-text' => sprintf ("%3d", $MkOsd{'Variometer'}) );
1242
        $map_canvas->itemconfigure ('MK-OSD-Tim-Value', '-text' => sprintf ("%02d:%02d", $MkFlyingTime / 60, $MkFlyingTime % 60) );
1243
        $map_canvas->itemconfigure ('MK-OSD-Sat-Value', '-text' => $MkOsd{'SatsInUse'} );
1244
 
1245
        # Waypoints abhaengig vom Modus NC/Player
1246
        my $WpValue = "--/--";
1247
        if ( $MkOsd{'WaypointNumber'} > 0)
1248
            {
1249
            $WpValue = sprintf ("%d/%d", $MkOsd{'WaypointIndex'} +1, $MkOsd{'WaypointNumber'});
1250
            }
1251
        if ($WpPlayerMode ne "Stop")
1252
            {
1253
            $WpValue = sprintf ("%d/%d", $WpPlayerIndex +1, scalar @Waypoints);
1254
            }
1255
        $map_canvas->itemconfigure ('MK-OSD-Wp-Value',  '-text' => "$WpValue");
1256
 
1257
        # blink battery warning
1258
        $map_canvas->itemconfigure ('MK-OSD-Bat-Value', '-fill' => $Cfg->{'mkcockpit'}->{'ColorOsd'});
1259
        if ( $MkOsd{'UBat'}  <  $Cfg->{'mkcockpit'}->{'UBatWarning'} )
1260
            {
1261
            if ( time %2 )
1262
                {
1263
                $map_canvas->itemconfigure ('MK-OSD-Bat-Value', '-fill' => 'red');
1264
                }
1265
            }
1266
 
1267
        my $Mode = "";
1268
        if ($MkOsd{'NCFlags'} & 0x04) { $Mode = "WPT"};
1269
        if ($WpPlayerMode eq "Play")  { $Mode = "Play" };
1270
        if ($WpPlayerMode eq "Pause") { $Mode = "Paus" };
1271
        if ($WpPlayerMode eq "Home")  { $Mode = "Home" };
1272
        if ($MkOsd{'NCFlags'} & 0x01) { $Mode = "Free"};
1273
        if ($MkOsd{'NCFlags'} & 0x02) { $Mode = "PH"};
1274
        if ($MkOsd{'NCFlags'} & 0x08) { $Mode = "$Mode" . " !"};  # Range Warning
1275
        $map_canvas->itemconfigure ('MK-OSD-Mode-Value', '-text' => "$Mode" );
1276
 
1277
        # Holdtime count down
1278
        if ( $WpPlayerHoldtime >= 0 )
1279
            {
1280
            my $Holdtime = int ($WpPlayerHoldtime / 2  + 0.5);
1281
            $map_canvas->itemconfigure ('MK-OSD-Holdtime-Value', '-text' => sprintf ("%d s", $Holdtime) );
1282
            $map_canvas->coords ('MK-OSD-Holdtime-Label', $MapSizeX - 130, 110);
1283
            $map_canvas->coords ('MK-OSD-Holdtime-Value', $MapSizeX - 80, 110);
1284
            }
1285
        else
1286
            {
1287
            # move out of sight
1288
            $map_canvas->coords ('MK-OSD-Holdtime-Label', $MapSizeX - 130, -110);
1289
            $map_canvas->coords ('MK-OSD-Holdtime-Value', $MapSizeX - 80, -110);
1290
            }
1291
 
1292
 
1293
        # Farbe MK-Zeiger abhängig vom GPS Empfang
1294
        my $MkCol= $Cfg->{'mkcockpit'}->{'ColorMkSatNo'};
1295
        if ( $SatsInUse >= 1 ) { $MkCol = $Cfg->{'mkcockpit'}->{'ColorMkSatLow'} ; }
1296
        if ( $SatsInUse >= 6 ) { $MkCol = $Cfg->{'mkcockpit'}->{'ColorMkSatGood'}; }
1297
        $map_canvas->itemconfigure ('MK-Arrow', '-fill' => $MkCol);
1298
 
1299
        # Variometer Pointer
1300
        my $dy = -$MkOsd{'Variometer'} * 10;
1301
        $map_canvas->coords('Map-Variometer-Pointer', 5, $MapSizeY/2+$dy, 20, $MapSizeY/2+10+$dy, 20, $MapSizeY/2-10+$dy);
1302
 
1303
        # Show/Hide SatFix Icon
1304
        if ($MkOsd{'SatsInUse'} >= 6 )
1305
            {
1306
            $map_canvas->coords('Satellite', $MapSizeX-190, 10, );
1307
            }
1308
        else
1309
            {
1310
            # move icon out of sight
1311
            $map_canvas->coords('Satellite', 0, -100, );
1312
            }
1313
        }
1314
    else
1315
        {
1316
        # keine aktuellen OSD Daten vom MK verfügbar
1317
        }
1318
 
1319
    });
1320
 
1321
#       
1322
# Timer: 0.1s - Tracking Anzeige aktualisieren
1323
#
1324
if ( $Cfg->{'track'}->{'Active'} =~ /y/i )
1325
    {
1326
    $frame_map_top->repeat (100, sub
1327
        {
1328
        # Aktuell gültige Karte
1329
        my %Map = %{$Maps{'Current'}};
1330
 
1331
        # Zeiger neu zeichnen
1332
        my $ServoPan = @ServoPos[$MkTrack{'ServoPan'}];
1333
        if ( $ServoPan ne ""  )
1334
            {
1335
            my $x0 = $TrackSizeX/2;    
1336
            my $y0 = $TrackSizeY - 0 - $TrackOffY;
1337
            my $x1 = $TrackSizeX/2 - ($TrackPtrLen-22) * cos( deg2rad $ServoPan);
1338
            my $y1 = $TrackSizeY   - ($TrackPtrLen-22) * sin (deg2rad $ServoPan) - $TrackOffY;
1339
            $track_canvas->coords ('Track-Ptr', $x0, $y0, $x1, $y1);
1340
            }
1341
 
1342
        # Farbe Zeiger abhängig vom GPS Empfang
1343
        my $SatsInUse = $MkOsd{'SatsInUse'};
1344
        my $TrackPtrCol= 'red';
1345
        if ( $SatsInUse >= 1 ) { $TrackPtrCol = 'orange'; }
1346
        if ( $SatsInUse >= 6 ) { $TrackPtrCol = 'green'; }
1347
        $track_canvas->itemconfigure ('Track-Ptr', '-fill' => $TrackPtrCol);
1348
        });
1349
    }
1350
 
1351
 
1352
#       
1353
# Timer: 0.5s - Waypoint Player
1354
#
1355
$frame_map_top->repeat (500, sub
1356
    {
1357
    if ($MkOsd{'NCFlags'} & 0x04)
1358
        {
1359
        # NC is in WPT Mode 
1360
 
1361
        if ( $WpPlayerMode eq "Pause" )
1362
            {
1363
            if ( $WpPlayerPause_Lat ne ""  and  $WpPlayerPause_Lon ne "" )
1364
                {
1365
                # Gespeicherte Pausen-Pos senden
1366
                &MkFlyTo ( '-lat'  => $WpPlayerPause_Lat,
1367
                           '-lon'  => $WpPlayerPause_Lon,
1368
                           '-holdtime' => "60",
1369
                           '-mode' => "Target",
1370
                         );
1371
                }
1372
            }
1373
 
1374
        if ( $WpPlayerMode eq "Home" )
1375
            {
1376
            lock (%MkOsd);              # until end of block
1377
            if ( $MkOsd{'HomePos_Stat'} == 1 )
1378
                {
1379
                &MkFlyTo ( '-lat'  => $MkOsd{'HomePos_Lat'},
1380
                           '-lon'  => $MkOsd{'HomePos_Lon'},
1381
                           '-holdtime' => "60",
1382
                           '-mode' => "Target",
1383
                         );
1384
                }
1385
            }
1386
 
1387
        my $WpCnt = scalar @Waypoints;
1388
        if ( $WpPlayerMode eq "Play"  and  $WpCnt > 0  and  $WpPlayerIndex < $WpCnt )
1389
            {
1390
            # Target WP-Pos senden
1391
            my $Wp_Lon = $Waypoints[$WpPlayerIndex]{'Pos_Lon'};
1392
            my $Wp_Lat = $Waypoints[$WpPlayerIndex]{'Pos_Lat'};
1393
            if ( $Wp_Lat ne ""  and  $Wp_Lon ne "" )
1394
                {
1395
                &MkFlyTo ( '-lat'  => $Wp_Lat,
1396
                           '-lon'  => $Wp_Lon,
1397
                           '-holdtime' => "60",
1398
                           '-mode' => "Target",
1399
                         );
1400
                }
1401
            }
1402
 
1403
        if ( $WpPlayerMode eq "Play" )
1404
            {
1405
            # Ziel erreicht?
1406
            if ( &WpCheckTargetReached() )
1407
                {
1408
                &WpTargetNext();
1409
                }
1410
            }
1411
        }
1412
 
1413
    # WP Player Holdtime count down
1414
    if ( $WpPlayerHoldtime > 0  )
1415
        {
1416
        $WpPlayerHoldtime --;
1417
        }
1418
    });
1419
 
1420
 
1421
#       
1422
# Timer: 1s
1423
#
1424
$frame_map_top->repeat (1000, sub
1425
    {
1426
    # Aktuell gültige Karte
1427
    my %Map = %{$Maps{'Current'}};
1428
 
1429
    if ( $MkOsd{'_Timestamp'} >= time -2 )
1430
        {
1431
 
1432
        # Heartbeat MK Datenübertragung
1433
        if ( time %2 )
1434
            {
1435
            $map_canvas->itemconfigure('Heartbeat', '-image' => 'HeartbeatLarge', );
1436
            }
1437
        else
1438
            {
1439
            $map_canvas->itemconfigure('Heartbeat', '-image' => 'HeartbeatSmall', );
1440
            }
1441
 
1442
        # Flugzeit aktualisieren
1443
        # Flugzeit selber mitzählen, da $MkOsd{'FlyingTime'} immer 0 (0.14b)
1444
        if ( $MkOsd{'MKFlags'} & 0x02 )
1445
            {
1446
            $MkFlyingTime += 1;
1447
            }
1448
 
1449
        # Footprint
1450
        if ( $Cfg->{'mkcockpit'}->{'FootprintLength'} > 0 )
1451
            {
1452
            if ( $MkOsd{'SatsInUse'} > 0  and  $MkOsd{'CurPos_Stat'} == 1 )
1453
                {
1454
                # neuen Footprint hinten anhaengen
1455
                my ($x, $y) = &MapGps2XY($MkOsd{'CurPos_Lat'}, $MkOsd{'CurPos_Lon'});
1456
                push @Footprint, $x, $y;
1457
                }
1458
 
1459
            while ( $#Footprint / 2  >  $Cfg->{'mkcockpit'}->{'FootprintLength'} )
1460
                {
1461
                # alte Footprints entfernen
1462
                splice @Footprint, 0, 2;
1463
                }
1464
 
1465
            &FootprintRedraw();
1466
            }
1467
 
1468
 
1469
        # tracking antenne
1470
        if ( $MkOsd{'MKFlags'} & 0x01  and  ! $MkTrack{'IsRunning'} and
1471
             $Cfg->{'track'}->{'Active'} =~ /y/i )
1472
            {
1473
            # start track at 1st motor start
1474
            $track_thr = threads->create (\&TrackAntennaGps)->detach();
1475
            $MkTrack{'IsRunning'} = "Running";
1476
            }
1477
        }
1478
    });
1479
 
1480
 
1481
MainLoop();   # should never end
1482
 
1483
 
1484
#-----------------------------------------------------------------
1485
# Subroutines
1486
#-----------------------------------------------------------------                        
1487
 
1488
# Get Wp Index from Canvas Id
1489
sub WpGetIndexFromId()
1490
    {
1491
    my ($id) = @_;
1492
 
1493
    my @Tags = $map_canvas->gettags($id);
1494
    my $WpTag = $Tags[1];
1495
 
1496
    for $i (0 .. $#Waypoints)
1497
        {
1498
        my $Wp = $Waypoints[$i];
1499
        if ( $Wp->{'Tag'} eq $WpTag )
1500
            {
1501
            # got it
1502
            return $i;
1503
            }
1504
        }
1505
    return -1;
1506
    }
1507
 
1508
 
1509
# Resend all Waypoints to MK
1510
sub WpSendAll()
1511
    {
1512
    # OSD/Debug Query Frequency verringern, sonst kommen nicht alle Wp im MK an
1513
    # Sicherheitshalber doppelt senden
1514
    $MkSendWp = 1;       # verhindert ueberschreiben im Timer
1515
    $MkSendQueue->enqueue( "o", "$AddrNC", pack ("C", 1000) );   # Frequency OSD Record, * 10ms
1516
    $MkSendQueue->enqueue( "d", "$AddrNC", pack ("C", 1000) );   # Frequency MK Debug Record, * 10ms
1517
    usleep (200000);
1518
    $MkSendQueue->enqueue( "o", "$AddrNC", pack ("C", 1000) );   # Frequency OSD Record, * 10ms
1519
    $MkSendQueue->enqueue( "d", "$AddrNC", pack ("C", 1000) );   # Frequency MK Debug Record, * 10ms
1520
    usleep (200000);
1521
 
1522
    # Alte WP-Liste im MK löschen
1523
    my $Wp = $Waypoints[0];
1524
    &MkFlyTo ( '-lat'  => $Wp->{'Pos_Lat'},
1525
               '-lon'  => Wp->{'Pos_Lon'},
1526
               '-mode' => "Waypoint Delete"
1527
             );
1528
 
1529
    for $i (0 .. $#Waypoints)
1530
        {
1531
        my $Wp = $Waypoints[$i];
1532
        &MkFlyTo ( '-lat'             => $Wp->{'Pos_Lat'},
1533
                   '-lon'             => $Wp->{'Pos_Lon'},
1534
                   '-alt'             => $Wp->{'Pos_Alt'},
1535
                   '-heading'         => $Wp->{'Heading'},
1536
                   '-toleranceradius' => $Wp->{'ToleranceRadius'},
1537
                   '-holdtime'        => $Wp->{'Holdtime'},
1538
                   '-eventflag'       => $Wp->{'Event_Flag'},
1539
                   '-mode'            => "Waypoint"
1540
                 );
1541
 
1542
        usleep (150000)  # NC Zeit zum Verarbeiten geben
1543
        }
1544
 
1545
    $MkSendWp = 0;  # normale OSD/Debug Query Frequency wird automatisch im 5s Timer wieder eingestellt
1546
 
1547
    # gray connectors: Wp are sent to MK
1548
    $map_canvas->itemconfigure('Waypoint-Connector',
1549
                               '-fill' => $Cfg->{'mkcockpit'}->{'ColorWpConnector'},
1550
                              );
1551
 
1552
    # MK ist nun synchron mit @Waypoints
1553
    $WaypointsModified = 0;
1554
    }          
1555
 
1556
 
1557
# Redraw Waypoint Icons
1558
sub WpRedrawIcons()
1559
    {
1560
    # delete old icons and Wp-Number from canvas
1561
    $map_canvas->delete('Waypoint');
1562
    $map_canvas->delete('WaypointNumber');
1563
 
1564
    # create new icons
1565
    for $i (0 .. $#Waypoints)
1566
        {
1567
        my $Wp = $Waypoints[$i];
1568
        my $x = $Wp->{'MapX'};
1569
        my $y = $Wp->{'MapY'};
1570
        my $Tag = $Wp->{'Tag'};
1571
 
1572
        # Waypoint Icon
1573
        my $IconHeight = 48;
1574
        my $IconWidth = 48;
1575
        $map_canvas->createImage($x-$IconWidth/2, $y-$IconHeight,
1576
                                 '-tags' => ['Waypoint', $Tag],
1577
                                 '-anchor' => 'nw',
1578
                                 '-image'  => 'Waypoint-Photo',
1579
                                );
1580
        # Waypoint Number
1581
        my $WpNumber = $i + 1;
1582
        $map_canvas->createText ( $x+3, $y-$IconHeight/2+12,
1583
                                  '-tags' => ['WaypointNumber', $Tag],
1584
                                  '-text' => $WpNumber,
1585
                                  '-font' => '-*-Arial-Bold-R-Normal--*-100-*',
1586
                                  '-fill' => $Cfg->{'mkcockpit'}->{'ColorWpNumber'},
1587
                                  '-anchor' => 'w',
1588
                                 );    
1589
 
1590
        }      
1591
    $map_canvas->lower('Waypoint', 'Fox');              # waypoint below Fox
1592
    $map_canvas->lower('WaypointNumber', 'Waypoint');   # waypoint-number below waypoint
1593
    }
1594
 
1595
 
1596
# Redraw Waypoint connectors
1597
sub WpRedrawLines()
1598
    {
1599
    # delete old connectors from canvas
1600
    $map_canvas->delete('Waypoint-Connector');  
1601
 
1602
    my $Color = $Cfg->{'mkcockpit'}->{'ColorWpConnector'};
1603
    if ( $WaypointsModified )
1604
        {
1605
        $Color = $Cfg->{'mkcockpit'}->{'ColorWpResend'};
1606
        }
1607
 
1608
    my $Wp = $Waypoints[0];
1609
    my $x_last = $Wp->{'MapX'};
1610
    my $y_last = $Wp->{'MapY'};
1611
    for $i (1 .. $#Waypoints)
1612
        {
1613
        my $Wp = $Waypoints[$i];
1614
        my $x = $Wp->{'MapX'};
1615
        my $y = $Wp->{'MapY'};
1616
 
1617
        $map_canvas->createLine ( $x_last, $y_last, $x, $y,
1618
                                  '-tags' => 'Waypoint-Connector',
1619
                                  '-arrow' => 'last',
1620
                                  '-arrowshape' => [10, 10, 3 ],
1621
                                  '-fill' => $Color,
1622
                                  '-width' => 1,
1623
                                );                                               
1624
        $x_last = $x;
1625
        $y_last = $y;
1626
        }
1627
 
1628
    $map_canvas->lower('Waypoint-Connector', 'Waypoint');   # connector below waypoint
1629
    }
1630
 
1631
 
1632
# Redraw Footprint
1633
sub FootprintRedraw()
1634
    {
1635
    # delete old Footprint fom canvas
1636
    $map_canvas->delete('Footprint');  
1637
 
1638
    if ( scalar @Footprint >= 4 )  # at least 2 Koordinaten-Paare
1639
        {
1640
        $map_canvas->createLine ( @Footprint,
1641
                                  '-tags' => 'Footprint',
1642
                                  '-fill' => $Cfg->{'mkcockpit'}->{'ColorFootprint'},
1643
                                  '-width' => 1,
1644
                                );       
1645
        }
1646
 
1647
    $map_canvas->lower('Footprint', 'Fox');
1648
    }
1649
 
1650
 
1651
# Display or Modify Hash
1652
sub DisplayHash()
1653
    {
1654
    my ($hrefData, $Titel, $Mode) = @_;
1655
 
1656
    # $Mode: Display, Edit, Waypoint, Refresh
1657
 
1658
    my %Id;
1659
    my $Label;
1660
    my $Value;
1661
 
1662
    # Neues Fenster aufmachen
1663
    my $popup = $main->Toplevel();
1664
    $popup->title($Titel);
1665
 
1666
    # Buttons
1667
    my $popup_button = $popup->Frame() -> pack('-side' => 'bottom',
1668
                                               '-expand' => 'y',
1669
                                               '-anchor' => 's',
1670
                                               '-padx' => 5,
1671
                                               '-pady' => 5,
1672
                                               );
1673
    $popup_button->Button('-text'    => 'Close',
1674
                          '-command' => sub
1675
        {
1676
        if ( $Mode =~ /edit/i  and  $Mode =~ /waypoint/i )
1677
            {
1678
            $WaypointsModified = 1;            
1679
            &WpRedrawLines();
1680
            &WpRedrawIcons();
1681
            }
1682
 
1683
        $popup->destroy()
1684
        })->pack;
1685
 
1686
    # Frame mit den Labels
1687
    my $popup_label = $popup->Frame() -> pack('-side' => 'left',
1688
                                              '-expand' => 'y',
1689
                                              '-anchor' => 'w',
1690
                                              '-padx' => 10,
1691
                                              '-pady' => 10,
1692
                                              );
1693
    # Labels anzeigen                    
1694
    foreach $Label ( sort keys %{$hrefData})
1695
        {
1696
        if ( $Translate{$Label} ne "" )
1697
            {
1698
            $Label = $Translate{$Label};
1699
            }
1700
 
1701
        $popup_label->Label ('-text' => $Label,
1702
                             '-width' => 25,
1703
                             '-anchor' => 'w',
1704
                             ) -> pack();
1705
        }
1706
 
1707
    # Frame mit den Daten
1708
    my $popup_values = $popup->Frame() -> pack('-side' => 'left',
1709
                                               '-expand' => 'y',
1710
                                               '-anchor' => 'w',
1711
                                               '-padx' => 10,
1712
                                               '-pady' => 10,
1713
                                               );
1714
    # Daten anzeigen
1715
    foreach $Value ( sort keys %{$hrefData})
1716
        {                              
1717
        if ( $Mode =~ /display/i )
1718
            {
1719
            # Display
1720
            $Id{$Value} = $popup_values->Label ('-text' => ${$hrefData}{$Value},
1721
                                                '-width' => 20,
1722
                                                '-anchor' => 'e',
1723
                                                '-relief' => 'sunken',
1724
                                                ) -> pack();
1725
            }
1726
        if ( $Mode =~ /edit/i )
1727
            {
1728
            # Edit
1729
            $Id{$Value} = $popup_values->Entry ('-textvariable' => \${$hrefData}{$Value},
1730
                                                '-exportselection' => '1',
1731
                                                '-width' => 20,
1732
                                                '-relief' => 'sunken',
1733
                                               ) -> pack();
1734
            if ( $Mode =~ /waypoint/i )
1735
                {
1736
                # einige Waypoint-Felder nicht aenderbar einstellen
1737
                if ( "MapX MapY Pos_Lat Pos_Lon Tag" =~ /$Value/i )
1738
                    {
1739
                    $Id{$Value}->configure('-state' => 'disabled', );
1740
                    }
1741
                }      
1742
            }
1743
        }
1744
 
1745
    if ( $Mode =~ /refresh/i )
1746
        {
1747
        # Timer: 0.1s
1748
        $popup_values->repeat (100, sub
1749
            {
1750
            # Datenfelder alle 100ms aktualisieren
1751
 
1752
            my $BgColor = 'white';
1753
            if ( $Mode =~ /heartbeat/i )
1754
                {
1755
                $BgColor = 'red';
1756
                if ( $MkOsd{'_Timestamp'} >= time-2 )
1757
                    {
1758
                    # gültige daten vom MK
1759
                    $BgColor = 'white';
1760
                    }
1761
                }
1762
 
1763
            foreach $Value ( sort keys %{$hrefData} )
1764
                {
1765
                $Id{$Value}->configure('-text' => ${$hrefData}{$Value},
1766
                                       '-background' => "$BgColor",
1767
                                      );
1768
                }
1769
            });
1770
        }
1771
 
1772
    return 0;
1773
    }
1774
 
1775
 
1776
 
1777
# KonfigurationsClose mkcockpit.xml im Popup-Fenster editieren
1778
sub Configure()
1779
    {
1780
 
1781
    # Copy Cfg-Hash for editing
1782
    my $CfgEdit = {%{$Cfg}};
1783
    foreach $key (keys %{$Cfg})
1784
        {
1785
        if ( ref $Cfg->{$key} )
1786
            {
1787
            $CfgEdit->{$key} = {%{$Cfg->{$key}}};
1788
            }
1789
        }
1790
 
1791
    # Neues Fenster aufmachen
1792
    my $popup = $main->Toplevel();
1793
    $popup->title("Preferences - $XmlConfigFile");
1794
 
1795
    # jede Sektion in einem Tab anzeigen
1796
    my $book = $popup->NoteBook()->pack( -fill=>'both', -expand=>1 );
1797
    foreach $key (sort keys %{$CfgEdit})
1798
        {    
1799
        if ( ! ref $CfgEdit->{$key} )
1800
            {
1801
            next;
1802
            }
1803
 
1804
        my $TabLabel = "$key";
1805
        if ( $Translate{$key} ne "" )
1806
                {
1807
                $TabLabel = $Translate{$key};
1808
                }
1809
 
1810
        my $Tab = $book->add( "$key", -label=>"$TabLabel", );
1811
 
1812
        # Frame fuer Buttons
1813
        my $book_button = $Tab->Frame() -> pack('-side' => 'bottom',
1814
                                                '-expand' => 'y',
1815
                                                '-anchor' => 's',
1816
                                                '-padx' => 5,
1817
                                                '-pady' => 5,
1818
                                                );
1819
 
1820
        $book_button->Button('-text'    => 'OK',
1821
                             '-width' => '10',
1822
                             '-command' => sub
1823
            {
1824
            # Copy back CfgEdit-Hash
1825
            $Cfg = {%{$CfgEdit}};
1826
            foreach $key (keys %{$CfgEdit})
1827
                {
1828
                if ( ref $CfgEdit->{$key} )
1829
                    {
1830
                    $Cfg->{$key} = {%{$CfgEdit->{$key}}};
1831
                    }
1832
                }
1833
 
1834
            # set new timestamp
1835
            my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time);
1836
            my $TimeStamp = sprintf ("%04d%02d%02d-%02d%02d%02d", $year+1900, $mon+1, $mday, $hour, $min, $sec);
1837
            $Cfg->{'CreationDate'} = $TimeStamp;
1838
 
1839
            # Cfg in mkcockpit.xml speichern
1840
            &XMLout ($Cfg,
1841
                     'OutputFile' => $XmlConfigFile,
1842
                     'AttrIndent' => '1',
1843
                     'RootName' => 'mkcockpit-Config',
1844
                    );
1845
 
1846
            $popup->destroy();
1847
            } )->pack ('-side' => 'left',
1848
                       '-expand' => 'y',
1849
                       '-anchor' => 's',
1850
                       '-padx' => 5,
1851
                       '-pady' => 5,
1852
                      );
1853
        $book_button->Button('-text'    => 'Abort',
1854
                             '-width' => '10',
1855
                             '-command' => sub { $popup->destroy() },
1856
                            )->pack ('-side' => 'left',
1857
                                     '-expand' => 'y',
1858
                                     '-anchor' => 's',
1859
                                     '-padx' => 5,
1860
                                     '-pady' => 5,
1861
                                     );
1862
        $book_button->Label ('-text' => "*) Changes are effective only after program restart!",
1863
                             '-anchor' => 'w',
1864
                             '-foreground' => 'red',
1865
                            ) ->pack ('-side' => 'left',
1866
                                      '-expand' => 'y',
1867
                                      '-anchor' => 's',
1868
                                      '-padx' => 10,
1869
                                      '-pady' => 5,
1870
                                      );
1871
 
1872
        # Frame mit den Labels
1873
        my $popup_label = $Tab->Frame() -> pack('-side' => 'left',
1874
                                                '-expand' => 'y',
1875
                                                '-anchor' => 'w',
1876
                                                '-padx' => 10,
1877
                                                '-pady' => 10,
1878
                                                );
1879
        # Labels anzeigen                        
1880
        foreach $Label ( sort keys %{$CfgEdit->{$key}})
1881
            {
1882
            if ( $Translate{$Label} ne "" )
1883
                {
1884
                $Label = $Translate{$Label};
1885
                }
1886
 
1887
            $popup_label->Label ('-text' => $Label,
1888
                                 '-width' => 30,
1889
                                 '-anchor' => 'w',
1890
                                ) -> pack();
1891
            }
1892
 
1893
        # Frame mit den Daten
1894
        my $popup_values = $Tab->Frame() -> pack('-side' => 'left',
1895
                                                 '-expand' => 'y',
1896
                                                 '-anchor' => 'w',
1897
                                                 '-padx' => 10,
1898
                                                 '-pady' => 10,
1899
                                                 );
1900
        # Eingabefelder mit Daten anzeigen
1901
        foreach $Value ( sort keys %{$CfgEdit->{$key}})
1902
            {                          
1903
            $popup_values->Entry ('-textvariable' => \$CfgEdit->{$key}->{$Value},
1904
                                  '-exportselection' => '1',
1905
                                  '-width' => 30,
1906
                                  '-relief' => 'sunken',
1907
                                 ) -> pack();  
1908
            }
1909
        }
1910
    }
1911
 
1912
 
1913
# Waypoint Player: Goto next Waypoint
1914
sub WpTargetNext()
1915
    {
1916
    $WpPlayerIndex ++;
1917
    if ( $WpPlayerIndex > $#Waypoints )
1918
        {
1919
        # Restart with 1st Wp
1920
        $WpPlayerIndex = 0;
1921
        }
1922
 
1923
    $WpPlayerHoldtime = -1;
1924
    }
1925
 
1926
# Waypoint Player: Goto previous Waypoint
1927
sub WpTargetPrev()
1928
    {
1929
    $WpPlayerIndex --;
1930
    if ( $WpPlayerIndex < 0 )
1931
        {
1932
        # Restart with last Wp
1933
        $WpPlayerIndex = $#Waypoints;
1934
        }
1935
 
1936
    $WpPlayerHoldtime = -1;
1937
    }
1938
 
1939
# Waypoint Player: Goto first Waypoint
1940
sub WpTargetFirst()
1941
    {
1942
    $WpPlayerIndex = 0;
1943
    $WpPlayerHoldtime = -1;
1944
    }
1945
 
1946
# Waypoint Player: Goto last Waypoint
1947
sub WpTargetLast()
1948
    {
1949
    $WpPlayerIndex = $#Waypoints;
1950
    $WpPlayerHoldtime = -1;
1951
    }
1952
 
1953
 
1954
# Waypoint Player: Waypoint Target reached?
1955
sub WpCheckTargetReached()
1956
    {
1957
    if ( $WpPlayerHoldtime == -1 )
1958
        {
1959
        lock (%MkOsd);              # until end of block
1960
 
1961
        if ( $MkOsd{'_Timestamp'} >= time-2  and   # Gueltige OSD Daten
1962
             $MkOsd{'NCFlags'} & 0x04  and         # WPT-Mode
1963
             $MkOsd{'SatsInUse'} >= 6  and  $MkOsd{'CurPos_Stat'} == 1 and  $MkOsd{'HomePos_Stat'} == 1)
1964
            {
1965
            # Gueltige SAT Daten
1966
            my $WpTarget_Lat = $Waypoints[$WpPlayerIndex]{'Pos_Lat'};
1967
            my $WpTarget_Lon = $Waypoints[$WpPlayerIndex]{'Pos_Lon'};
1968
            my $WpTolerance  = $Waypoints[$WpPlayerIndex]{'ToleranceRadius'};
1969
            my $WpHoldtime   = $Waypoints[$WpPlayerIndex]{'Holdtime'};
1970
 
1971
            # Operation Radius pruefen
1972
            my ($HomeDist, $HomeBearing) = &MapGpsTo($MkOsd{'HomePos_Lat'}, $MkOsd{'HomePos_Lon'}, $WpTarget_Lat, $WpTarget_Lon );
1973
            if ( $HomeDist > $MkOsd{'OperatingRadius'} )
1974
                {
1975
                # Target entsprechend Operation Radius neu berechnen
1976
                $HomeDist = $MkOsd{'OperatingRadius'};
1977
                ($WpTarget_Lat, $WpTarget_Lon) = &MapGpsAt($MkOsd{'HomePos_Lat'}, $MkOsd{'HomePos_Lon'}, $HomeDist, $HomeBearing);
1978
                }
1979
 
1980
 
1981
            # Abstand zum Ziel pruefen
1982
            my ($Dist, $Bearing) = &MapGpsTo($MkOsd{'CurPos_Lat'}, $MkOsd{'CurPos_Lon'}, $WpTarget_Lat, $WpTarget_Lon );
1983
            if ( $Dist <= $WpTolerance )
1984
                {
1985
                # Target reached - count down Holdtime
1986
                $WpPlayerHoldtime = 2 * $WpHoldtime;      # 0..2n - decrement im 0.5s timer
1987
                }
1988
            }
1989
        }
1990
 
1991
    if ( $WpPlayerHoldtime == 0 )  # wird im 0.5s timer runtergezaehlt
1992
        {
1993
        # Target reached - Holdtime is over
1994
        $WpPlayerHoldtime = -1;
1995
        return 1;
1996
        }
1997
 
1998
    # Target NOT reached
1999
    return 0;
2000
    }
2001
 
2002
 
2003
#
2004
# Call Back
2005
# 
2006
 
2007
# Wp-Player CallBack: Play/Pause button
2008
sub CbWpPlayerPlayPause()
2009
    {
2010
    # Play/Pause-Icon loeschen und neu anzeigen
2011
    $map_canvas->delete('Wp-PlayPause');
2012
 
2013
    if ( ($WpPlayerMode eq "Pause") or  ($WpPlayerMode eq "Stop") or  ($WpPlayerMode eq "Home") )
2014
        {
2015
        $WpPlayerMode = 'Play';
2016
        $WpPlayerHoldtime = -1;
2017
        $map_canvas->createImage($MapSizeX/2+100, $MapSizeY-48,
2018
                                 '-tags' => 'Wp-PlayPause',
2019
                                 '-anchor' => 'nw',
2020
                                 '-image'  => 'WpPause-Foto',
2021
                                 );
2022
        }
2023
    else
2024
        {
2025
        $WpPlayerMode = 'Pause';
2026
        $WpPlayerHoldtime = -1;
2027
        $map_canvas->createImage($MapSizeX/2+100, $MapSizeY-48,
2028
                                 '-tags' => 'Wp-PlayPause',
2029
                                 '-anchor' => 'nw',
2030
                                 '-image'  => 'WpPlay-Foto',
2031
                                 );
2032
 
2033
        # momentane Position merken und im Wp-Timer dauernd senden
2034
        $WpPlayerPause_Lon = "";
2035
        $WpPlayerPause_Lat = "";
2036
 
2037
        lock (%MkOsd);              # until end of block
2038
        if ( $MkOsd{'_Timestamp'} >= time-2 )
2039
            {
2040
            # Gueltige OSD Daten
2041
            if ( $MkOsd{'SatsInUse'} >= 6  and  $MkOsd{'CurPos_Stat'} == 1 )
2042
                {
2043
                $WpPlayerPause_Lon = $MkOsd{'CurPos_Lon'};
2044
                $WpPlayerPause_Lat = $MkOsd{'CurPos_Lat'};
2045
                }
2046
            }
2047
        }
2048
    }
2049
 
2050
 
2051
# Wp-Player CallBack: Next
2052
sub CbWpPlayerNext()
2053
    {
2054
    if ( $WpPlayerMode ne 'Stop' )
2055
        {
2056
        &WpTargetNext();
2057
        }
2058
    }
2059
 
2060
 
2061
# Wp-Player CallBack: Prev
2062
sub CbWpPlayerPrev()
2063
    {
2064
    if ( $WpPlayerMode ne 'Stop' )
2065
        {
2066
        &WpTargetPrev();
2067
        }
2068
    }
2069
 
2070
 
2071
# Wp-Player CallBack: First
2072
sub CbWpPlayerFirst()
2073
    {
2074
    if ( $WpPlayerMode ne 'Stop' )
2075
        {
2076
        &WpTargetFirst();
2077
        }
2078
    }
2079
 
2080
# Wp-Player CallBack: Last
2081
sub CbWpPlayerLast()
2082
    {
2083
    if ( $WpPlayerMode ne 'Stop' )
2084
        {
2085
        &WpTargetLast();
2086
        }
2087
    }
2088
 
2089
 
2090
# Wp-Player CallBack: Home
2091
sub CbWpPlayerHome()
2092
    {
2093
    if ( $WpPlayerMode ne 'Stop' )
2094
        {
2095
        $WpPlayerMode = 'Home';
2096
        &WpTargetFirst();
2097
 
2098
        $map_canvas->delete('Wp-PlayPause');
2099
        $map_canvas->createImage($MapSizeX/2+100, $MapSizeY-48,
2100
                                 '-tags' => 'Wp-PlayPause',
2101
                                 '-anchor' => 'nw',
2102
                                 '-image'  => 'WpPlay-Foto',
2103
                                 );
2104
        }
2105
    }
2106
 
2107
 
2108
# Wp-Player CallBack: Stop
2109
sub CbWpPlayerStop()
2110
    {
2111
    if ( $WpPlayerMode ne 'Stop' )
2112
        {
2113
        $WpPlayerMode = 'Stop';
2114
        &WpFirstFirst();
2115
 
2116
        $map_canvas->delete('Wp-PlayPause');
2117
        $map_canvas->createImage($MapSizeX/2+100, $MapSizeY-48,
2118
                                 '-tags' => 'Wp-PlayPause',
2119
                                 '-anchor' => 'nw',
2120
                                 '-image'  => 'WpPlay-Foto',
2121
                                 );
2122
 
2123
        # Show user that Waypoints in MK are cleared
2124
        $WaypointsModified = 1;
2125
        &WpRedrawLines();
2126
        }
2127
    }
2128
 
2129
 
2130
1;
2131
__END__