Subversion Repositories Projects

Rev

Details | Last modification | View Log | RSS feed

Rev Author Line No. Line
617 rain-er 1
#!/usr/bin/perl
2
#!/usr/bin/perl -d:ptkdb
3
 
4
###############################################################################
5
#
6
# libmkcockpit.pl -  MK Mission Cockpit - Subroutined for GUI Frontend
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-08-09 0.2.5 rw subroutines moved from mkcockpit.pl
41
# 2009-09-05 0.2.6 rw POI heading control added
42
#
43
###############################################################################
44
 
45
$Version{'libmkcockpit.pl'} = "0.2.6 - 2009-09-05";
46
 
47
 
48
# check, if %MkOsd is valid
49
sub MkOsdIsValid()
50
    {
51
    return ( $MkOsd{'_Timestamp'} >= time-2 );
52
    }
53
 
54
# check, if current GPS position is valid
55
sub CurPosIsValid()
56
    {
57
    return ( &MkOsdIsValid()  and  $MkOsd{'SatsInUse'} >= 6  and  $MkOsd{'CurPos_Stat'} == 1 );
58
    }
59
 
60
# check, if home GPS position is valid
61
sub HomePosIsValid()
62
    {
63
    return ( &MkOsdIsValid()  and  $MkOsd{'SatsInUse'} >= 6  and  $MkOsd{'HomePos_Stat'} == 1 );
64
    }
65
 
66
# check, if target GPS position is valid
67
sub TargetIsValid()
68
    {
69
    return ( &MkOsdIsValid()  and  $MkOsd{'SatsInUse'} >= 6  and  $MkOsd{'TargetPos_Stat'} == 1  );
70
    }
71
 
72
# check, if motor are on
73
sub MkIsMotorOn()
74
    {
75
    return ( &MkOsdIsValid()  and  $MkOsd{'MKFlags'} & 0x01  );
76
    }
77
 
78
# check, if MK is flying
79
sub MkIsFlying()
80
    {
81
    return ( &MkOsdIsValid()  and  $MkOsd{'MKFlags'} & 0x02  );
82
    }
83
 
84
# check, if MK is calibrating
85
sub MkIsCalibrating()
86
    {
87
    return ( &MkOsdIsValid()  and  $MkOsd{'MKFlags'} & 0x04  );
88
    }
89
# check, if Motor is starting
90
sub MkIsMotorStarting()
91
    {
92
    return ( &MkOsdIsValid()  and  $MkOsd{'MKFlags'} & 0x08  );
93
    }
94
 
95
# check, Emergency Landing
96
sub MkEmergencyLanding()
97
    {
98
    return ( &MkOsdIsValid()  and  $MkOsd{'MKFlags'} & 0x10  );
99
    }
100
 
101
# check, if MK is FREE Mode
102
sub MkIsFreeMode()
103
    {
104
    return ( &MkOsdIsValid()  and  $MkOsd{'NCFlags'} & 0x01  );
105
    }
106
 
107
# check, if MK is in PH Mode
108
sub MkIsPhMode()
109
    {
110
    return ( &MkOsdIsValid()  and  $MkOsd{'NCFlags'} & 0x02  );
111
    }
112
 
113
# check, if MK is in WPT Mode
114
sub MkIsWptMode()
115
    {
116
    return ( &MkOsdIsValid()  and  $MkOsd{'NCFlags'} & 0x04  );
117
    }
118
 
119
# check, Range Limit
120
sub MkRangeLimit()
121
    {
122
    return ( &MkOsdIsValid()  and  $MkOsd{'NCFlags'} & 0x08  );
123
    }
124
 
125
# check, Serial Link
126
sub MkSerialLink()
127
    {
128
    return ( &MkOsdIsValid()  and  $MkOsd{'NCFlags'} & 0x10  );
129
    }
130
 
131
# check, Target reached
132
sub MkTargetReached()
133
    {
134
    return ( &MkOsdIsValid()  and  $MkOsd{'NCFlags'} & 0x20  );
135
    }
136
 
137
# check, Manual Control
138
sub MkManualControl()
139
    {
140
    return ( &MkOsdIsValid()  and  $MkOsd{'NCFlags'} & 0x40  );
141
    }
142
 
143
 
144
# get battery capacity in %
145
sub BatCapacity()
146
    {
147
    my ($UBat) = @_;
148
 
149
    my $CfgVal = $Cfg->{'mkcockpit'}->{'BatCharacteristics'};
150
    my @Voltage = split ' ', $CfgVal;
151
 
152
    my $Capacity = 0;
153
    if ( $UBat >= $Voltage[0] )
154
        {
155
        $Capacity = 100;
156
        }
157
 
158
    $Cnt = $#Voltage;
159
    for ($i=0; $i < $Cnt; $i++)
160
        {
161
        my $V1 = $Voltage[$i];
162
        my $V2 = $Voltage[$i+1];
163
 
164
        if ( $UBat >= $V1  and  $UBat < $V2  or
165
             $UBat <= $V1  and  $UBat > $V2 )
166
            {
167
            # linear interpolation
168
            my $x = $i + ($UBat - $V1 ) / ($V2 - $V1);
169
            $Capacity = 100 - $x * 100 / $Cnt;
170
            last;
171
            }
172
        }
173
 
174
    return $Capacity;
175
    }
176
 
177
 
178
#
179
# Waypoint handling
180
#
181
 
182
# Add a Waypoint to @Waypoints List
183
sub WpAdd()
184
    {
185
    my ($Wp_x, $Wp_y) = @_;
186
 
187
    # save Wp-Hash in Waypoint-Array
188
    my $Wp = {};        
189
    my $Tag = sprintf "Waypoint-%d.%d", time, int (rand(9)) ;   # kind of unique Tag for this Wp
190
    ($Lat, $Lon) = &MapXY2Gps($Wp_x, $Wp_y);
191
    $Wp->{'Tag'} = $Tag;
192
    $Wp->{'MapX'} = $Wp_x;
193
    $Wp->{'MapY'} = $Wp_y;
194
    $Wp->{'Pos_Lat'} = $Lat;
195
    $Wp->{'Pos_Lon'} = $Lon;
196
    $Wp->{'Pos_Alt'} = $MkOsd{'CurPos_Alt'};
197
    $Wp->{'Heading'}         = $Cfg->{'waypoint'}->{'DefaultHeading'};
198
    $Wp->{'ToleranceRadius'} = $Cfg->{'waypoint'}->{'DefaultToleranceRadius'};
199
    $Wp->{'Holdtime'}        = $Cfg->{'waypoint'}->{'DefaultHoldtime'};
200
    $Wp->{'Event_Flag'}      = $Cfg->{'waypoint'}->{'DefaultEventFlag'};
201
    push @Waypoints, $Wp;
202
    }
203
 
204
 
205
# Delete Waypoint from @Waypoints List
206
sub WpDelete ()
207
    {
208
    my ($WpIndex) = @_;
209
 
210
    # delete Wp in Waypoint-Array
211
    splice @Waypoints, $WpIndex, 1;
212
    }
213
 
214
 
215
# Load @Waypoints from file
216
sub WpLoadFile ()
217
    {
218
    my ($WpFile) = @_;
219
 
220
    # XML in Hash-Ref lesen
221
    my $Wp = XMLin($WpFile, ForceArray => 1);
222
 
223
    # XML Hash-Ref in Wp-Array umkopieren
224
    undef @Waypoints;
225
 
226
    foreach $key (sort keys %$Wp)
227
        {
228
        my $Point = $Wp->{$key}->[0];
229
 
230
        # relative Pixelkoordinaten auf Bildgroesse umrechnen
231
        if ( $Point->{'MapX'} <= 1  and  $Point->{'MapY'} <= 1 )
232
            {
233
            $Point->{'MapX'} = int ( $Point->{'MapX'} * $MapSizeX + 0.5 );
234
            $Point->{'MapY'} = int ( $Point->{'MapY'} * $MapSizeY + 0.5 );
235
            }
236
 
237
        # GPS Koordinaten für die aktuelle Karte neu aus Map x/y berechnen
238
        my ($Lat, $Lon) = &MapXY2Gps($Point->{'MapX'}, $Point->{'MapY'});
239
        $Point->{'Pos_Lat'} = $Lat;
240
        $Point->{'Pos_Lon'} = $Lon;
241
        push @Waypoints, $Point;
242
        }
243
    }
244
 
245
 
246
# Safe @Waypoints to file
247
sub WpSaveFile()
248
    {
249
    my ($WpFile) = @_;
250
 
251
    # Waypoint-Array in Hash umkopieren
252
    for  $i ( 0 .. $#Waypoints )
253
        {
254
        my $key = sprintf ("WP-%04d", $i);
255
        my $Wp = {%{$Waypoints[$i]}};        # copy of Hash-content
256
        $WpOut{$key} = $Wp;
257
 
258
        # Pixelkoordinaten relativ zur Bildgroesse speichern
259
        $WpOut{$key}{'MapX_Pixel'} = $WpOut{$key}{'MapX'};
260
        $WpOut{$key}{'MapY_Pixel'} = $WpOut{$key}{'MapY'};
261
        $WpOut{$key}{'MapX'} /= $MapSizeX;
262
        $WpOut{$key}{'MapY'} /= $MapSizeY;
263
        }
264
 
265
    # WP-Hash als XML speichern
266
    &XMLout (\%WpOut,
267
             'OutputFile' => $WpFile,
268
             'AttrIndent' => '1',
269
             'RootName' => 'Waypoints',
270
            );
271
    }
272
 
273
 
274
# Get Wp Index from Canvas Id
275
sub WpGetIndexFromId()
276
    {
277
    my ($id) = @_;
278
 
279
    my @Tags = $map_canvas->gettags($id);
280
    my $WpTag = $Tags[1];
281
 
282
    for $i (0 .. $#Waypoints)
283
        {
284
        my $Wp = $Waypoints[$i];
285
        if ( $Wp->{'Tag'} eq $WpTag )
286
            {
287
            # got it
288
            return $i;
289
            }
290
        }
291
    return -1;
292
    }
293
 
294
 
295
# Resend all Waypoints to MK
296
sub WpSendAll()
297
    {
298
    # OSD/Debug Abfragefrequenz verringern, sonst kommen nicht alle Wp im MK an
299
    # Sicherheitshalber doppelt senden
300
    $MkSendWp = 1;       # verhindert ueberschreiben im Timer
301
    $MkSendQueue->enqueue( "o", "$AddrNC", pack ("C", 1000) );   # Frequenz OSD Datensatz, * 10ms
302
    $MkSendQueue->enqueue( "d", "$AddrNC", pack ("C", 1000) );   # Frequenz MK Debug Datensatz, * 10ms
303
    usleep (200000);
304
    $MkSendQueue->enqueue( "o", "$AddrNC", pack ("C", 1000) );   # Frequenz OSD Datensatz, * 10ms
305
    $MkSendQueue->enqueue( "d", "$AddrNC", pack ("C", 1000) );   # Frequenz MK Debug Datensatz, * 10ms
306
    usleep (200000);
307
 
308
    # Alte WP-Liste im MK löschen
309
    my $Wp = $Waypoints[0];
310
    &MkFlyTo ( '-lat'  => $Wp->{'Pos_Lat'},
311
               '-lon'  => $Wp->{'Pos_Lon'},
312
               '-mode' => "Waypoint Delete"
313
             );
314
 
315
    for $i (0 .. $#Waypoints)
316
        {
317
        my $Wp = $Waypoints[$i];
318
        &MkFlyTo ( '-lat'             => $Wp->{'Pos_Lat'},
319
                   '-lon'             => $Wp->{'Pos_Lon'},
320
                   '-alt'             => $Wp->{'Pos_Alt'},
321
                   '-heading'         => $Wp->{'Heading'},
322
                   '-toleranceradius' => $Wp->{'ToleranceRadius'},
323
                   '-holdtime'        => $Wp->{'Holdtime'},
324
                   '-eventflag'       => $Wp->{'Event_Flag'},
325
                   '-mode'            => "Waypoint"
326
                 );
327
 
328
        usleep (150000)  # NC Zeit zum Verarbeiten geben
329
        }
330
 
331
    $MkSendWp = 0;  # normale OSD/Debug Abfragefrequenz wird automatisch im 5s Timer wieder eingestellt
332
 
333
    # gray connectors: Wp are sent to MK
334
    $map_canvas->itemconfigure('Waypoint-Connector',
335
                               '-fill' => $Cfg->{'mkcockpit'}->{'ColorWpConnector'},
336
                              );
337
 
338
    # MK ist nun synchron mit @Waypoints
339
    $WaypointsModified = 0;
340
    }          
341
 
342
 
343
# Redraw Waypoint Icons
344
sub WpRedrawIcons()
345
    {
346
    if ( $PlayerWptKmlMode =~ /WPT/i )
347
        {
348
 
349
        # delete old icons and Wp-Number from canvas
350
        $map_canvas->delete('Waypoint');
351
        $map_canvas->delete('WaypointNumber');
352
 
353
        # create new icons
354
        for $i (0 .. $#Waypoints)
355
           {
356
            my $Wp = $Waypoints[$i];
357
            my $x = $Wp->{'MapX'};
358
            my $y = $Wp->{'MapY'};
359
            my $Tag = $Wp->{'Tag'};
360
 
361
            # Waypoint Icon
362
            my $IconHeight = 48;
363
            my $IconWidth = 48;
364
            $map_canvas->createImage($x-$IconWidth/2, $y-$IconHeight,
365
                                     '-tags' => ['Waypoint', $Tag],
366
                                     '-anchor' => 'nw',
367
                                     '-image'  => 'Waypoint-Photo',
368
                                    );
369
            # Waypoint Number
370
            my $WpNumber = $i + 1;
371
            $map_canvas->createText ( $x+3, $y-$IconHeight/2+12,
372
                                      '-tags' => ['WaypointNumber', $Tag],
373
                                      '-text' => $WpNumber,
374
                                      '-font' => '-*-Arial-Bold-R-Normal--*-100-*',
375
                                      '-fill' => $Cfg->{'mkcockpit'}->{'ColorWpNumber'},
376
                                      '-anchor' => 'w',
377
                                     );
378
 
379
            }  
380
        $map_canvas->lower('Waypoint', 'Target');           # waypoint below Target
381
        $map_canvas->lower('WaypointNumber', 'Waypoint');   # waypoint-number below waypoint
382
        }
383
    }
384
 
385
 
386
# Redraw Waypoint connectors
387
sub WpRedrawLines()
388
    {
389
    if ( $PlayerWptKmlMode eq 'WPT'  and  $PlayerRandomMode eq 'STD' )
390
        {
391
        # delete old connectors from canvas
392
        $map_canvas->delete('Waypoint-Connector');  
393
 
394
        my $Color = $Cfg->{'mkcockpit'}->{'ColorWpConnector'};
395
        if ( $WaypointsModified )
396
            {
397
            $Color = $Cfg->{'mkcockpit'}->{'ColorWpResend'};
398
            }
399
 
400
        my $Wp = $Waypoints[0];
401
        my $x_last = $Wp->{'MapX'};
402
        my $y_last = $Wp->{'MapY'};
403
        for $i (1 .. $#Waypoints)
404
            {
405
            my $Wp = $Waypoints[$i];
406
            my $x = $Wp->{'MapX'};
407
            my $y = $Wp->{'MapY'};
408
 
409
            $map_canvas->createLine ( $x_last, $y_last, $x, $y,
410
                                      '-tags' => 'Waypoint-Connector',
411
                                      '-arrow' => 'last',
412
                                      '-arrowshape' => [10, 10, 3 ],
413
                                      '-fill' => $Color,
414
                                      '-width' => 1,
415
                                    );                                           
416
            $x_last = $x;
417
            $y_last = $y;
418
            }
419
 
420
        $map_canvas->lower('Waypoint-Connector', 'Waypoint');   # connector below waypoint
421
        }
422
    }
423
 
424
 
425
# Hide Waypoints and connectors on Canvas
426
sub WpHide()
427
   {
428
   $map_canvas->delete('Waypoint');
429
   $map_canvas->delete('WaypointNumber');
430
   $map_canvas->delete('Waypoint-Connector');
431
   }
432
 
433
 
434
# Hide Kml-Track on Canvas
435
sub KmlHide()
436
   {
437
   $map_canvas->delete('KML-Track');
438
   }
439
 
440
 
441
# Load @KmlTargets from file
442
sub KmlLoadFile()
443
    {
444
    my ($File) = @_;
445
 
446
    # XML in Hash-Ref lesen
447
    my $Kml = XMLin($File);
448
 
449
    # init state maschine
450
    undef @KmlTargets;
451
    $KmlPlayerIndex = 0;
452
 
453
    my $Coordinates = $Kml->{Document}->{Placemark}->{LineString}->{coordinates};
454
    foreach $Line (split "\n", $Coordinates)
455
        {
456
        chomp $Line;
457
        $Line =~ s/\s//g;       # remove white space
458
        if ( $Line ne "" )
459
            {
460
            my ($Lon, $Lat, $Alt) = split ",", $Line;
461
            $Lon = sprintf ("%f", $Lon);
462
            $Lat = sprintf ("%f", $Lat);
463
            $Alt = sprintf ("%f", $Alt);
464
 
465
            push @KmlTargets, {'Lat' => $Lat,
466
                               'Lon' => $Lon,
467
                               'Alt' => $Alt,
468
                              };
469
            }
470
        }
471
    }
472
 
473
# Redraw KML track
474
sub KmlRedraw()
475
    {
476
 
477
    # delete old Track from canvas
478
    $map_canvas->delete('KML-Track');
479
 
480
    my @Track;
481
 
482
    foreach $Target ( @KmlTargets )
483
        {
484
        my $Lat = $Target->{'Lat'};
485
        my $Lon = $Target->{'Lon'};
486
        my $Alt = $Target->{'Alt'};
487
        my ($x, $y) = &MapGps2XY($Lat, $Lon);
488
        push @Track, $x, $y;
489
        }
490
 
491
    if ( scalar @Track >= 4 )  # at least 2 Koordinaten-Paare
492
        {
493
        $map_canvas->createLine ( @Track,
494
                                  '-tags' => 'KML-Track',
495
                                  '-fill' => $Cfg->{'mkcockpit'}->{'ColorKmlTrack'},
496
                                  '-width' => 1,
497
                                );       
498
 
499
        $map_canvas->lower('KML-Track', 'Target');        # Track below Target
500
        }
501
    }
502
 
503
 
504
# Redraw Footprint
505
sub FootprintRedraw()
506
    {
507
    # delete old Footprint from canvas
508
    $map_canvas->delete('Footprint');  
509
 
510
    if ( scalar @Footprint >= 4 )  # at least 2 Koordinaten-Paare
511
        {
512
        $map_canvas->createLine ( @Footprint,
513
                                  '-tags' => 'Footprint',
514
                                  '-fill' => $Cfg->{'mkcockpit'}->{'ColorFootprint'},
515
                                  '-width' => 1,
516
                                );       
517
        }
518
 
519
    $map_canvas->lower('Footprint', 'Target');
520
    }
521
 
522
 
523
# Waypoint Player: Set Waypoint - sequence or random
524
sub WpTargetSet()
525
    {
526
    my ($Index) = @_;
527
 
528
    my $WpCnt = scalar @Waypoints;
529
    if ( $Index < 0  or  $Index >= $WpCnt )
530
        {
531
        # invalid WP number
532
        return 1;
533
        }
534
 
535
    my $Wp = $Waypoints[$Index];
536
    my $Wp_x = $Wp->{'MapX'};
537
    my $Wp_y = $Wp->{'MapY'};
538
 
539
    # is Wp reachable?
540
    if ( ! &IsTargetReachable($Wp_x, $Wp_y) )
541
        {
542
        # new Wp-Target is not reachable
543
        return 1;
544
        }
545
 
546
    # set new Wp-Target 
547
    $WpPlayerIndex = $Index;
548
    $WpPlayerHoldtime = -1;
549
 
550
    return 0;
551
    }
552
 
553
 
554
# Waypoint Player: Goto next Waypoint - sequence or random
555
sub WpTargetNext()
556
    {
557
    my ($ParIndex) = @_;
558
 
559
    my $WpCnt = scalar @Waypoints;
560
 
561
    # Std- or Random Waypoint sequence
562
    if ( $PlayerRandomMode =~ /STD/i  or
563
         $PlayerRandomMode =~ /RND/i )
564
        {
565
        $NewIndex = $WpPlayerIndex;
566
 
567
        # get next Wp
568
        for ( $i=0; $i<5; $i++)        # avoid deadlock, if no WP reachable
569
            {
570
            for ( $j=0; $j<5; $j++ )   # avoid deadlock, if only 1 WP
571
                {
572
 
573
                if ( $PlayerRandomMode =~ /STD/i )
574
                    {
575
                    $NewIndex ++;
576
                    if ( $NewIndex >= $WpCnt )
577
                        {
578
                        # Restart with 1st Wp
579
                        $NewIndex = 0;
580
                        }
581
                    }
582
 
583
                if ( $PlayerRandomMode =~ /RND/i )
584
                    {
585
                    $NewIndex = int (rand($WpCnt));
586
                    }
587
 
588
                # want to have different Wp 
589
                if ( $NewIndex ne $WpPlayerIndex )
590
                    {
591
                    last;
592
                    }
593
                }
594
 
595
            # Set new Target 
596
            if ( &WpTargetSet ($NewIndex) == 0 )
597
                {
598
                # new Wp-Target set
599
                last;
600
                }
601
            }
602
        }
603
 
604
    # Random Map sequence
605
    if ( $PlayerRandomMode =~ /MAP/i )
606
        {
607
        $RandomTarget_x = $MkPos_x;
608
        $RandomTarget_y = $MkPos_y;
609
 
610
        for ( $i=0; $i<50; $i++)        # avoid deadlock, if target not reachable
611
            {
612
            # don't use 10% around the map
613
            my $New_x = int (rand($MapSizeX - 2 * $MapSizeX/10));
614
            my $New_y = int (rand($MapSizeY - 2 * $MapSizeY/10));
615
            $New_x += $MapSizeX/10;
616
            $New_y += $MapSizeY/10;
617
 
618
            # is Target reachable?
619
            if ( &IsTargetReachable($New_x, $New_y) )
620
                {
621
                # new Target found
622
                $RandomTarget_x = $New_x;
623
                $RandomTarget_y = $New_y;
624
                last;
625
                }
626
            }
627
        }
628
 
629
    &TtsSpeak ('MEDIUM', $Translate{'TtsNextTarget'});
630
 
631
    $WpPlayerHoldtime = -1;
632
    }
633
 
634
 
635
# Waypoint Player: Goto previous Waypoint
636
sub WpTargetPrev()
637
    {
638
    if ( $PlayerRandomMode =~ /STD/i )
639
        {
640
        $WpPlayerIndex --;
641
        if ( $WpPlayerIndex < 0 )
642
            {
643
            # Restart with last Wp
644
            $WpPlayerIndex = $#Waypoints;
645
            }
646
        }
647
    else
648
        {
649
        # Next Random Target
650
        &WpTargetNext();
651
        }
652
 
653
    $WpPlayerHoldtime = -1;
654
    }
655
 
656
 
657
# Waypoint Player: Goto first Waypoint
658
sub WpTargetFirst()
659
    {
660
    $WpPlayerIndex = 0;
661
    $WpPlayerHoldtime = -1;
662
    }
663
 
664
# Waypoint Player: Goto last Waypoint
665
sub WpTargetLast()
666
    {
667
    $WpPlayerIndex = $#Waypoints;
668
    $WpPlayerHoldtime = -1;
669
    }
670
 
671
 
672
# Waypoint Player: Waypoint Target reached?
673
sub WpCheckTargetReached()
674
    {
675
    if ( $WpPlayerHoldtime == -1 )
676
        {
677
        lock (%MkOsd);              # until end of block
678
 
679
        if ( &CurPosIsValid() and  &HomePosIsValid() and  &MkIsWptMode() )
680
            {
681
            # Gueltige SAT Daten
682
 
683
            # for Wp mode
684
            my $Wp = $Waypoints[$WpPlayerIndex];
685
            my $WpTarget_Lat = $Wp->{'Pos_Lat'};
686
            my $WpTarget_Lon = $Wp->{'Pos_Lon'};
687
            my $WpTolerance  = $Wp->{'ToleranceRadius'};
688
            my $WpHoldtime   = $Wp->{'Holdtime'};
689
 
690
            # Random-Map Mode
691
            if ( $PlayerRandomMode =~ /MAP/i )
692
                {
693
                ($WpTarget_Lat, $WpTarget_Lon) = &MapXY2Gps ($RandomTarget_x, $RandomTarget_y);
694
                $WpTolerance = $Cfg->{'waypoint'}->{'DefaultToleranceRadius'};
695
                $WpHoldtime  = $Cfg->{'waypoint'}->{'DefaultHoldtime'};
696
                }
697
 
698
            # Operation Radius pruefen
699
            my ($HomeDist, $HomeBearing) = &MapGpsTo($MkOsd{'HomePos_Lat'}, $MkOsd{'HomePos_Lon'}, $WpTarget_Lat, $WpTarget_Lon );
700
            if ( $HomeDist > $MkOsd{'OperatingRadius'} )
701
                {
702
                # Target entsprechend Operation Radius neu berechnen
703
                $HomeDist = $MkOsd{'OperatingRadius'};
704
                ($WpTarget_Lat, $WpTarget_Lon) = &MapGpsAt($MkOsd{'HomePos_Lat'}, $MkOsd{'HomePos_Lon'}, $HomeDist, $HomeBearing);
705
                }
706
 
707
            # Abstand zum Ziel pruefen
708
            my ($Dist, $Bearing) = &MapGpsTo($MkOsd{'CurPos_Lat'}, $MkOsd{'CurPos_Lon'}, $WpTarget_Lat, $WpTarget_Lon );
709
            $Dist = int ($Dist + 0.5);
710
            if ( $Dist <= $WpTolerance )
711
                {
712
                # Target reached - count down Holdtime
713
                $WpPlayerHoldtime = 2 * $WpHoldtime;      # 0..2n - decrement im 0.5s timer
714
 
715
                &TtsSpeak ('MEDIUM', $Translate{'TtsTargetReached'});
716
                }
717
            }
718
        }
719
 
720
    if ( $WpPlayerHoldtime == 0 )  # wird im 0.5s timer runtergezaehlt
721
        {
722
        # Target reached - Holdtime is over
723
        $WpPlayerHoldtime = -1;
724
 
725
        return 1;
726
        }
727
 
728
    # Target NOT reached
729
    return 0;
730
    }
731
 
732
 
733
# KML Player: 10s forward
734
sub KmlTargetNext()
735
    {
736
    $KmlPlayerIndex += int (10 / $Cfg->{waypoint}->{'KmlTimeBase'} + 0.5);
737
    if ( $KmlPlayerIndex > $#KmlTargets )
738
        {
739
        # Next loop
740
        $KmlPlayerIndex -= $#KmlTargets;
741
        }
742
    }
743
 
744
# KML Player: 10s backward
745
sub KmlTargetPrev()
746
    {
747
    $KmlPlayerIndex -= int (10 / $Cfg->{waypoint}->{'KmlTimeBase'} + 0.5);
748
    if ( $KmlPlayerIndex < 0 )
749
        {
750
        # Next loop
751
        $KmlPlayerIndex += $#KmlTargets;
752
        }
753
    }
754
 
755
# KML Player: Goto first Target
756
sub KmlTargetFirst()
757
    {
758
    $KmlPlayerIndex = 0;
759
    }
760
 
761
# KML Player: Goto last Target
762
sub KmlTargetLast()
763
    {
764
    $KmlPlayerIndex = $#KmlTargets;
765
    }
766
 
767
 
768
#
769
# Set Player modes
770
#
771
 
772
# set player to "Play" mode
773
sub PlayerPlay()
774
    {
775
    $PlayerMode = 'Play';
776
    $WpPlayerHoldtime = -1;
777
 
778
    # Play/Pause-Icon loeschen und neu anzeigen
779
    $map_canvas->delete('Wp-PlayPause');
780
    $map_canvas->createImage($MapSizeX/2+150, $MapSizeY-48,
781
                             '-tags' => 'Wp-PlayPause',
782
                             '-anchor' => 'nw',
783
                             '-image'  => 'WpPause-Foto',
784
                             );
785
    &FoxHide();
786
    }
787
 
788
 
789
# set player to "Pause" mode
790
sub PlayerPause()
791
    {
792
    $PlayerMode = 'Pause';
793
    $WpPlayerHoldtime = -1;
794
 
795
    # Play/Pause-Icon loeschen und neu anzeigen
796
    $map_canvas->delete('Wp-PlayPause');
797
    $map_canvas->createImage($MapSizeX/2+150, $MapSizeY-48,
798
                             '-tags' => 'Wp-PlayPause',
799
                             '-anchor' => 'nw',
800
                             '-image'  => 'WpPlay-Foto',
801
                             );
802
 
803
    # momentane Position merken und im Player-Timer senden
804
    $PlayerPause_Lon = "";
805
    $PlayerPause_Lat = "";
806
 
807
    lock (%MkOsd);              # until end of block
808
    if ( &CurPosIsValid() )
809
        {
810
        $PlayerPause_Lon = $MkOsd{'CurPos_Lon'};
811
        $PlayerPause_Lat = $MkOsd{'CurPos_Lat'};
812
        }
813
 
814
    &FoxShow();
815
    }
816
 
817
 
818
# set player to "Home" mode
819
sub PlayerHome()
820
    {
821
 
822
    $PlayerMode = 'Home';
823
    &WpTargetFirst();
824
 
825
    # Play/Pause-Icon loeschen und neu anzeigen
826
    $map_canvas->delete('Wp-PlayPause');
827
    $map_canvas->createImage($MapSizeX/2+150, $MapSizeY-48,
828
                             '-tags'   => 'Wp-PlayPause',
829
                             '-anchor' => 'nw',
830
                             '-image'  => 'WpPlay-Foto',
831
                             );
832
    &FoxHide();
833
    }
834
 
835
 
836
# set player to "Stop" mode
837
sub PlayerStop()
838
    {
839
    $PlayerMode = 'Stop';
840
    &WpTargetFirst();
841
 
842
    # set Play/Pause Icon to "Play
843
    $map_canvas->delete('Wp-PlayPause');
844
    $map_canvas->createImage($MapSizeX/2+150, $MapSizeY-48,
845
                             '-tags'   => 'Wp-PlayPause',
846
                             '-anchor' => 'nw',
847
                             '-image'  => 'WpPlay-Foto',
848
                             );
849
 
850
    # switch player to Wp Mode
851
    &PlayerWpt();
852
 
853
    &FoxHide();
854
    }
855
 
856
 
857
# set player Random Mode to "STD"
858
sub PlayerRandomStd()
859
    {
860
    $PlayerRandomMode = "STD";
861
 
862
    # Set Icon
863
    $map_canvas->delete('Wp-WptRandom');
864
    $map_canvas->createImage($MapSizeX/2-200, $MapSizeY-48,
865
                             '-tags' => 'Wp-WptRandom',
866
                             '-anchor' => 'nw',
867
                             '-image'  => 'WpRandomOn-Foto',
868
                            );
869
 
870
    # redraw connectors and Icons on canvas
871
    &WpRedrawLines();
872
    &WpRedrawIcons();
873
    }
874
 
875
 
876
# set player Random Mode to "RND"
877
sub PlayerRandomRnd()
878
    {
879
    $PlayerRandomMode = "RND";
880
 
881
    # Set Icon
882
    $map_canvas->delete('Wp-WptRandom');
883
    $map_canvas->createImage($MapSizeX/2-200, $MapSizeY-48,
884
                             '-tags' => 'Wp-WptRandom',
885
                             '-anchor' => 'nw',
886
                             '-image'  => 'WpRandomMap-Foto',
887
                            );
888
 
889
    # delete Wp-connectors from canvas
890
    $map_canvas->delete('Waypoint-Connector');  
891
    }
892
 
893
 
894
# set player Random Mode to "MAP"
895
sub PlayerRandomMap()
896
    {
897
    $PlayerRandomMode = "MAP";
898
 
899
    # Set Icon
900
    $map_canvas->delete('Wp-WptRandom');
901
    $map_canvas->createImage($MapSizeX/2-200, $MapSizeY-48,
902
                             '-tags' => 'Wp-WptRandom',
903
                             '-anchor' => 'nw',
904
                             '-image'  => 'WpRandomOff-Foto',
905
                            );
906
 
907
    # Get 1st Target
908
    &WpTargetNext();
909
 
910
    # hide WP and connectors on canvas
911
    &WpHide();
912
    }
913
 
914
 
915
# set player to KML mode
916
sub PlayerKml()
917
    {
918
    $PlayerWptKmlMode = 'KML';
919
 
920
    # Wpt/Kml-Player-Icon loeschen und neu anzeigen
921
    $map_canvas->delete('Wp-WptKml');
922
    $map_canvas->createImage($MapSizeX/2-250, $MapSizeY-48,
923
                             '-tags' => 'Wp-WptKml',
924
                             '-anchor' => 'nw',
925
                             '-image'  => 'WpKml-Foto',
926
                             );
927
 
928
    # delete Waypoints from canvas
929
    &WpHide();
930
 
931
    # show KML Track
932
    &KmlRedraw();
933
    }
934
 
935
 
936
# set player to WPT mode
937
sub PlayerWpt()
938
    {
939
    $PlayerWptKmlMode = 'WPT';
940
 
941
    # Wpt/Kml-Player-Icon loeschen und neu anzeigen
942
    $map_canvas->delete('Wp-WptKml');
943
    $map_canvas->createImage($MapSizeX/2-250, $MapSizeY-48,
944
                             '-tags' => 'Wp-WptKml',
945
                             '-anchor' => 'nw',
946
                             '-image'  => 'WpWpt-Foto',
947
                             );
948
 
949
    # delete Kml-Track from canvas
950
    &KmlHide();
951
 
952
    # Show waypoints, WP resend required
953
    $WaypointsModified = 1;
954
 
955
    if ( $PlayerRandomMode ne 'MAP' )
956
        {
957
        &WpRedrawIcons()
958
        }
959
    if ( $PlayerRandomMode eq 'STD' )
960
        {
961
        &WpRedrawLines()
962
        }
963
 
964
    }
965
 
966
 
967
# Activate Recording mode
968
sub PlayerRecordOn
969
    {
970
    $PlayerRecordMode = "REC";
971
    $map_canvas->itemconfigure ('MK-OSD-Rec-Value', '-text' => "Recording" );
972
 
973
    # Record new KML-Track
974
    undef @KmlTargets;
975
    $KmlPlayerIndex = 0;
976
 
977
    # delete Kml-Track from canvas
978
    &KmlHide();
979
    }
980
 
981
# Deactivate Recording mode
982
sub PlayerRecordOff
983
    {
984
    $PlayerRecordMode = "";
985
    $map_canvas->itemconfigure ('MK-OSD-Rec-Value', '-text' => "" );
986
    }
987
 
988
 
989
# Hide Fox icon on canvas
990
sub FoxHide()
991
    {
992
    $map_canvas->lower('Fox', 'Map');
993
    }
994
 
995
# Show Fox icon on canvas
996
sub FoxShow()
997
    {
998
    $map_canvas->raise('Fox', 'Target');
999
    }
1000
 
1001
# Hide POI icon on canvas
1002
sub PoiHide()
1003
    {
1004
    $map_canvas->lower('POI', 'Map');
1005
    }
1006
 
1007
# Show POI icon on canvas
1008
sub PoiShow()
1009
    {
1010
    $map_canvas->raise('POI', 'Track-Antenna');
1011
    }
1012
 
1013
 
1014
#
1015
# System Messages
1016
#
1017
 
1018
# Init Messages for a Subsystem/timer
1019
sub MkMessageInit ()
1020
    {
1021
    my ($Id) = @_;
1022
 
1023
    $MkMessages{$Id} = [];
1024
    }
1025
 
1026
 
1027
# Register message
1028
sub MkMessage ()
1029
    {
1030
    my ($Message, $Id) = @_;
1031
 
1032
    push @{$MkMessages{$Id}}, $Message;
1033
    }
1034
 
1035
 
1036
# show registered messages
1037
sub MkMessageShow()
1038
    {
1039
    my @Messages;
1040
    my $MsgLines = 0;
1041
    my $MaxMsgLen = 0;
1042
 
1043
    # Collect Messages of each category
1044
    foreach my $Id (keys %MkMessages)
1045
        {
1046
        foreach $i ( 0 .. $#{$MkMessages{$Id}} )
1047
            {
1048
            my $Msg = $MkMessages{$Id}[$i];
1049
            push @Messages, $Msg;
1050
 
1051
            $MsgLines ++;
1052
 
1053
            my $Len = length $Msg;
1054
            if ( $Len > $MaxMsgLen )
1055
                {
1056
                $MaxMsgLen = $Len;
1057
                }
1058
            }
1059
        }
1060
 
1061
    $map_canvas->delete('Message-Balloon');  # delete old Balloon
1062
 
1063
    if ( $MsgLines > 0 )
1064
        {
1065
        # draw Balloon
1066
        my @MsgBalloon = ( $MkPos_x ,                       $MkPos_y,
1067
                           $MkPos_x + 30 ,                  $MkPos_y + 40,
1068
                           $MkPos_x + 30 + $MaxMsgLen * 11, $MkPos_y + 40,
1069
                           $MkPos_x + 30 + $MaxMsgLen * 11, $MkPos_y + 44 + $MsgLines * 20,
1070
                           $MkPos_x + 20,                   $MkPos_y + 44 + $MsgLines * 20,
1071
                           $MkPos_x + 20,                   $MkPos_y + 40,
1072
                           $MkPos_x,                        $MkPos_y,
1073
                          );
1074
 
1075
        $map_canvas->createPolygon( @MsgBalloon,
1076
                                    '-tags' => ['Message-Balloon', 'Message-BalloonBubble'],
1077
                                    '-fill' => 'yellow',
1078
                                    '-outline' => 'yellow',
1079
                                    '-width' => 1,
1080
                                  );
1081
        # draw Messages
1082
        my $MsgLine = 1;
1083
        foreach my $Msg (@Messages)
1084
            {
1085
            $map_canvas->createText ( $MkPos_x + 25, $MkPos_y + 32 + $MsgLine * 20 ,
1086
                                      '-tags' => ['Message-Balloon', 'Message-BalloonText'],
1087
                                      '-text' => $Msg,
1088
                                      '-font' => '-*-Arial-Bold-R-Normal--*-200-*',
1089
                                      '-fill' => 'blue',
1090
                                      '-anchor' => 'w',
1091
                                        );
1092
            $MsgLine ++;
1093
            }
1094
 
1095
 
1096
        $map_canvas->lower('Message-Balloon', 'MK-Arrow');
1097
        }
1098
 
1099
    }
1100
 
1101
 
1102
# Show Balloon, when arproaching Target
1103
sub TargetMessageShow ()
1104
    {
1105
    $map_canvas->delete('Target-Balloon');  # delete old Balloon
1106
 
1107
    if ( $OperationMode ne "Free" and $MkOsd{'TargetPos_Stat'} == 1  and $MkOsd{'TargetPosDev_Dist'} /10 < 25 )
1108
        {
1109
        my $BalloonLines = 0;
1110
        $ColorBalloon = "blue";
1111
        my ($T_x, $T_y) = &MapGps2XY($MkOsd{'TargetPos_Lat'}, $MkOsd{'TargetPos_Lon'});
1112
        my $Wp = $Waypoints[$MkOsd{'WaypointIndex'}];
1113
 
1114
        # Holdtime Wp-Player Mode
1115
        if ( $WpPlayerHoldtime >= 0 )
1116
            {
1117
            # Holdtime
1118
            $ColorBalloon = 'red';
1119
            my $HoldTime = sprintf ("%5s %3d s", "HLD:", int ($WpPlayerHoldtime / 2  + 0.5) );
1120
            $map_canvas->createText ( $T_x + 25, $T_y - 40,
1121
                                      '-tags' => ['Target-Balloon', 'Target-BalloonText'],
1122
                                      '-text' => $HoldTime,
1123
                                      '-font' => '-*-Arial-Bold-R-Normal--*-200-*',
1124
                                      '-fill' => $ColorBalloon,
1125
                                      '-anchor' => 'w',
1126
                                    );
1127
            $BalloonLines ++;
1128
            }
1129
 
1130
        # Holdtime WPT-Mode
1131
        if ( &MkTargetReached()  and  $OperationMode eq "WPT" )
1132
            {
1133
            # Holdtime from MK
1134
            $ColorBalloon = 'red';
1135
            my $HoldTime = sprintf ("%5s %3d s", "HLD:", int ($MkOsd{'TargetHoldTime'} + 0.5) );
1136
            $map_canvas->createText ( $T_x + 25, $T_y - 40,
1137
                                      '-tags' => ['Target-Balloon', 'Target-BalloonText'],
1138
                                      '-text' => $HoldTime,
1139
                                      '-font' => '-*-Arial-Bold-R-Normal--*-200-*',
1140
                                      '-fill' => $ColorBalloon,
1141
                                      '-anchor' => 'w',
1142
                                    );
1143
            $BalloonLines ++;
1144
            }
1145
 
1146
        # Tolerance Radius Player Mode
1147
        if ( &MkIsWptMode()  and  $OperationMode eq "Play" and $PlayerWptKmlMode eq "WPT" )
1148
            {
1149
            my $WpTolerance  = sprintf ("%5s %3d m", "TOL:", $Wp->{'ToleranceRadius'});
1150
            $map_canvas->createText ( $T_x + 25, $T_y - 60,
1151
                                      '-tags' => ['Target-Balloon', 'Target-BalloonText'],
1152
                                      '-text' => $WpTolerance,
1153
                                      '-font' => '-*-Arial-Bold-R-Normal--*-200-*',
1154
                                      '-fill' => $ColorBalloon,
1155
                                      '-anchor' => 'w',
1156
                                    );
1157
            $BalloonLines ++;
1158
            }
1159
 
1160
        # Tolerance WPT-Mode
1161
        if ( &MkIsWptMode  and  $OperationMode eq "WPT" )
1162
            {
1163
            my $WpTolerance  = sprintf ("%5s %3d m", "TOL:", $Wp->{'ToleranceRadius'} );
1164
            $map_canvas->createText ( $T_x + 25, $T_y - 60,
1165
                                      '-tags' => ['Target-Balloon', 'Target-BalloonText'],
1166
                                      '-text' => $WpTolerance,
1167
                                      '-font' => '-*-Arial-Bold-R-Normal--*-200-*',
1168
                                      '-fill' => $ColorBalloon,
1169
                                      '-anchor' => 'w',
1170
                                    );
1171
            $BalloonLines ++;
1172
            }
1173
 
1174
        # Distance to Target
1175
        my $Dist = int ($MkOsd{'TargetPosDev_Dist'} /10 + 0.5);
1176
        $map_canvas->createText ( $T_x + 25, $T_y - 80,
1177
                                  '-tags' => ['Target-Balloon', 'Target-BalloonText'],
1178
                                  '-text' => sprintf ("%5s %3d m", "DST:", $Dist) ,
1179
                                  '-font' => '-*-Arial-Bold-R-Normal--*-200-*',
1180
                                  '-fill' => $ColorBalloon,
1181
                                  '-anchor' => 'w',
1182
                                );
1183
        $BalloonLines ++;
1184
 
1185
        if ( $BalloonLines >= 1 )
1186
            {
1187
            # draw Balloon
1188
            my @TargetBalloon = ( $T_x ,      $T_y,
1189
                                  $T_x + 30,  $T_y - (3 - $BalloonLines) * 20 -27,
1190
                                  $T_x + 150, $T_y - (3 - $BalloonLines) * 20 -27 ,
1191
                                  $T_x + 150, $T_y - 93,
1192
                                  $T_x + 20,  $T_y - 93,
1193
                                  $T_x + 20,  $T_y - (3 - $BalloonLines) * 20 -27,
1194
                                  $T_x,       $T_y,
1195
                                );
1196
 
1197
            $map_canvas->createPolygon( @TargetBalloon,
1198
                                        '-tags' => ['Target-Balloon', 'Target-BalloonBubble'],
1199
                                        '-fill' => 'lightgray',
1200
                                        '-outline' => 'yellow',
1201
                                        '-width' => 1,
1202
                                       );
1203
            }
1204
 
1205
 
1206
        $map_canvas->lower('Target-Balloon', 'MK-Home-Line');
1207
        $map_canvas->lower('Target-BalloonBubble', 'Target-BalloonText');
1208
        }
1209
    }
1210
 
1211
 
1212
#
1213
# Airfield border
1214
#
1215
 
1216
# Are two segments A(a1/a2), B(b1/b2) and C(c1/c2), D(d1/d2) crossing ?
1217
sub SegmentCross()
1218
    {
1219
    my ( $a1, $a2, $b1, $b2, $c1, $c2, $d1, $d2) = @_;
1220
 
1221
    # segment C/D ist vertical, avoid div/0
1222
    if ( $c1 == $d1 )
1223
        {
1224
        $d1 += 0.00001;
1225
        }
1226
 
1227
    my $n = ($b1 - $a1) * ($d2 - $c2) - ($b2 - $a2) * ($d1 - $c1);
1228
    if ( $n == 0.0 )
1229
        {
1230
        # AB und CD sind parallel
1231
        return 0;
1232
        }
1233
 
1234
    my $s = ( ($c1 - $a1) * ($d2 - $c2) - ($c2 - $a2) * ($d1 - $c1) ) / $n;
1235
    my $t = ( $a1 - $c1 + $s * ($b1 - $a1) ) / ( $d1 - $c1 );
1236
    if ( $s >= 0.0  and  $s <= 1.0  and  $t >= 0.0  and  $t <= 1.0 )
1237
        {
1238
        # beide Strecken kreuzen sich
1239
 
1240
        # Schnittpunkt: s_x, s_y
1241
        my $s_x = $a1 + $s * ( $b1 - $a1 );
1242
        my $s_y = $a2 + $s * ( $b2 - $a2 );
1243
 
1244
        return 1;
1245
        }
1246
 
1247
    # beide Strecken kreuzen sich nicht
1248
    return 0;
1249
    }
1250
 
1251
 
1252
# How often does a segment A(a1,a2), B(b1,b2) cross the polygon?
1253
sub SegmentPolygonCross()
1254
    {
1255
    my ( $a1, $a2, $b1, $b2, $Polygon) = @_;
1256
 
1257
    my $Cross = 0;
1258
    my $PolyCnt = scalar @{$Polygon};
1259
    my $PolyPointCnt = $PolyCnt / 2;
1260
 
1261
    my $i = 0;
1262
    for ( $p=0; $p < $PolyPointCnt; $p++ )
1263
        {
1264
        my $c1 = ${$Polygon}[$i++];
1265
        my $c2 = ${$Polygon}[$i++];
1266
 
1267
        if ( $i >= $PolyCnt ) { $i = 0; }
1268
 
1269
        my $d1 = ${$Polygon}[$i];
1270
        my $d2 = ${$Polygon}[$i+1];
1271
 
1272
        # map calibration offsets
1273
        $c1 -= $Map{'Offset_x'};
1274
        $c2 += $Map{'Offset_y'};
1275
        $d1 -= $Map{'Offset_x'};
1276
        $d2 += $Map{'Offset_y'};    
1277
 
1278
        if ( &SegmentCross($a1, $a2, $b1, $b2, $c1, $c2, $d1, $d2) )
1279
            {
1280
            $Cross ++;
1281
            }
1282
        }
1283
 
1284
    return $Cross;
1285
    }
1286
 
1287
 
1288
# Is point A inside airfield border?
1289
sub IsInsideBorder()
1290
    {
1291
    my ($a1, $a2) = @_;
1292
 
1293
    if ( scalar @Map{'Border'} == 0 )
1294
        {
1295
        # no border defined, always inside
1296
        return 1;
1297
        }
1298
 
1299
    my $Cross = &SegmentPolygonCross (-10, -10, $a1, $a2, @Map{'Border'} );
1300
 
1301
    # Ungerade Anzahl Kreuzungen: Inside
1302
    return ( $Cross % 2 );
1303
    }
1304
 
1305
 
1306
 
1307
# Is segment A, B crossing the airfield border?
1308
sub IsCrossingBorder()
1309
    {
1310
    my ($a1, $a2, $b1, $b2) = @_;
1311
 
1312
    if ( scalar @Map{'Border'} == 0 )
1313
        {
1314
        # no border defined, always not crossing
1315
        return 0;
1316
        }
1317
 
1318
    my $Cross = &SegmentPolygonCross ($a1, $a2, $b1, $b2, @Map{'Border'} );
1319
 
1320
    return ( $Cross > 0 );
1321
    }
1322
 
1323
 
1324
# How often is segment A, B crossing the airfield border?
1325
sub CrossingBorderCount()
1326
    {
1327
    my ($a1, $a2, $b1, $b2) = @_;
1328
 
1329
    if ( scalar @Map{'Border'} == 0 )
1330
        {
1331
        # no border defined, not crossing
1332
        return 0;
1333
        }
1334
 
1335
    my $Cross = &SegmentPolygonCross ($a1, $a2, $b1, $b2, @Map{'Border'} );
1336
 
1337
    return ( $Cross );
1338
    }
1339
 
1340
 
1341
# check, if Target is reachable my MK
1342
sub IsTargetReachable()
1343
    {
1344
    my ($T_x, $T_y) = @_;
1345
 
1346
    my $MkIsInside = &IsInsideBorder($MkPos_x, $MkPos_y);
1347
    my $TargetIsInside = &IsInsideBorder($T_x, $T_y);
1348
    my $MkTargetCrossingCount = &CrossingBorderCount($MkPos_x, $MkPos_y, $T_x, $T_y);
1349
 
1350
    if ( ($MkIsInside  and  $MkTargetCrossingCount == 0 )  or
1351
         (! $MkIsInside  and  $TargetIsInside  and  $MkTargetCrossingCount == 1) )
1352
        {
1353
        # Target is reachable
1354
        return 1;
1355
        }
1356
 
1357
    # Target is not reachable
1358
    return 0;
1359
    }
1360
 
1361
 
1362
 
1363
#
1364
# Configuration and data-visualisation
1365
#
1366
 
1367
# Display or Modify Hash
1368
sub DisplayHash()
1369
    {
1370
    my ($hrefData, $Titel, $Mode) = @_;
1371
 
1372
    # $Mode: Display, Edit, Waypoint, Refresh
1373
 
1374
    my %Id;
1375
    my $Label;
1376
    my $Value;
1377
 
1378
    # Neues Fenster aufmachen
1379
    my $popup = $main->Toplevel();
1380
    $popup->title($Titel);
1381
 
1382
    # Buttons
1383
    my $popup_button = $popup->Frame() -> pack('-side' => 'bottom',
1384
                                               '-expand' => 'y',
1385
                                               '-anchor' => 's',
1386
                                               '-padx' => 5,
1387
                                               '-pady' => 5,
1388
                                               );
1389
    $popup_button->Button('-text'    => 'Schließen',
1390
                          '-command' => sub
1391
        {
1392
        if ( $Mode =~ /edit/i  and  $Mode =~ /waypoint/i )
1393
            {
1394
            $WaypointsModified = 1;            
1395
            &WpRedrawLines();
1396
            &WpRedrawIcons();
1397
            }
1398
 
1399
        $popup->destroy()
1400
        })->pack;
1401
 
1402
    # Frame mit den Labels
1403
    my $popup_label = $popup->Frame() -> pack('-side' => 'left',
1404
                                              '-expand' => 'y',
1405
                                              '-anchor' => 'w',
1406
                                              '-padx' => 10,
1407
                                              '-pady' => 10,
1408
                                              );
1409
    # Labels anzeigen                    
1410
    foreach $Label ( sort keys %{$hrefData})
1411
        {
1412
        if ( $Translate{$Label} ne "" )
1413
            {
1414
            $Label = $Translate{$Label};
1415
            }
1416
 
1417
        $popup_label->Label ('-text' => $Label,
1418
                             '-width' => 25,
1419
                             '-anchor' => 'w',
1420
                             ) -> pack();
1421
        }
1422
 
1423
    # Frame mit den Daten
1424
    my $popup_values = $popup->Frame() -> pack('-side' => 'left',
1425
                                               '-expand' => 'y',
1426
                                               '-anchor' => 'w',
1427
                                               '-padx' => 10,
1428
                                               '-pady' => 10,
1429
                                               );
1430
    # Daten anzeigen
1431
    foreach $Value ( sort keys %{$hrefData})
1432
        {                              
1433
        if ( $Mode =~ /display/i )
1434
            {
1435
            # Display
1436
            $Id{$Value} = $popup_values->Label ('-text' => ${$hrefData}{$Value},
1437
                                                '-width' => 20,
1438
                                                '-anchor' => 'e',
1439
                                                '-relief' => 'sunken',
1440
                                                ) -> pack();
1441
            }
1442
        if ( $Mode =~ /edit/i )
1443
            {
1444
            # Edit
1445
            $Id{$Value} = $popup_values->Entry ('-textvariable' => \${$hrefData}{$Value},
1446
                                                '-exportselection' => '1',
1447
                                                '-width' => 20,
1448
                                                '-relief' => 'sunken',
1449
                                               ) -> pack();
1450
            if ( $Mode =~ /waypoint/i )
1451
                {
1452
                # einige Waypoint-Felder nicht aenderbar einstellen
1453
                if ( "MapX MapY Pos_Lat Pos_Lon Tag" =~ /$Value/i )
1454
                    {
1455
                    $Id{$Value}->configure('-state' => 'disabled', );
1456
                    }
1457
                }      
1458
            }
1459
        }
1460
 
1461
    if ( $Mode =~ /refresh/i )
1462
        {
1463
        # Timer: 0.1s
1464
        $popup_values->repeat (100, sub
1465
            {
1466
            # Datenfelder alle 100ms aktualisieren
1467
 
1468
            my $BgColor = 'white';
1469
            if ( $Mode =~ /heartbeat/i )
1470
                {
1471
                $BgColor = 'red';
1472
                if ( &MkOsdIsValid() )
1473
                    {
1474
                    # gültige daten vom MK
1475
                    $BgColor = 'white';
1476
                    }
1477
                }
1478
 
1479
            foreach $Value ( sort keys %{$hrefData} )
1480
                {
1481
                # Eingebbare Waypoint-Felder nicht aktualisieren
1482
                if ( ! ($Mode =~ /waypoint/i  and
1483
                        "Event_Flag Heading ToleranceRadius HoldTime Pos_Alt" =~ /$Value/i) )
1484
                    {                
1485
                    $Id{$Value}->configure('-text' => ${$hrefData}{$Value},
1486
                                           '-background' => "$BgColor",
1487
                                          );
1488
                    }
1489
                }
1490
            });
1491
        }
1492
 
1493
    return 0;
1494
    }
1495
 
1496
 
1497
 
1498
# Konfigurationsdatei mkcockpit.xml im Popup-Fenster editieren
1499
sub Configure()
1500
    {
1501
 
1502
    # Copy Cfg-Hash for editing
1503
    my $CfgEdit = {%{$Cfg}};
1504
    foreach $key (keys %{$Cfg})
1505
        {
1506
        if ( ref $Cfg->{$key} )
1507
            {
1508
            $CfgEdit->{$key} = {%{$Cfg->{$key}}};
1509
            }
1510
        }
1511
 
1512
    # Neues Fenster aufmachen
1513
    my $popup = $main->Toplevel();
1514
    $popup->title("Einstellungen - $XmlConfigFile");
1515
 
1516
    # jede Sektion in einem Tab anzeigen
1517
    my $book = $popup->NoteBook()->pack( -fill=>'both', -expand=>1 );
1518
    foreach $key (sort keys %{$CfgEdit})
1519
        {    
1520
        if ( ! ref $CfgEdit->{$key} )
1521
            {
1522
            next;
1523
            }
1524
 
1525
        my $TabLabel = "$key";
1526
        if ( $Translate{$key} ne "" )
1527
                {
1528
                $TabLabel = $Translate{$key};
1529
                }
1530
 
1531
        my $Tab = $book->add( "$key", -label=>"$TabLabel", );
1532
 
1533
        # Frame fuer Buttons
1534
        my $book_button = $Tab->Frame() -> pack('-side' => 'bottom',
1535
                                                '-expand' => 'y',
1536
                                                '-anchor' => 's',
1537
                                                '-padx' => 5,
1538
                                                '-pady' => 5,
1539
                                                );
1540
 
1541
        $book_button->Button('-text'    => 'OK',
1542
                             '-width' => '10',
1543
                             '-command' => sub
1544
            {
1545
            # Copy back CfgEdit-Hash
1546
            $Cfg = {%{$CfgEdit}};
1547
            foreach $key (keys %{$CfgEdit})
1548
                {
1549
                if ( ref $CfgEdit->{$key} )
1550
                    {
1551
                    $Cfg->{$key} = {%{$CfgEdit->{$key}}};
1552
                    }
1553
                }
1554
 
1555
            # set new timestamp
1556
            my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time);
1557
            my $TimeStamp = sprintf ("%04d%02d%02d-%02d%02d%02d", $year+1900, $mon+1, $mday, $hour, $min, $sec);
1558
            $Cfg->{'CreationDate'} = $TimeStamp;
1559
 
1560
            # Cfg in mkcockpit.xml speichern
1561
            &XMLout ($Cfg,
1562
                     'OutputFile' => $XmlConfigFile,
1563
                     'AttrIndent' => '1',
1564
                     'RootName' => 'mkcockpit-Config',
1565
                    );
1566
 
1567
            $popup->destroy();
1568
            } )->pack ('-side' => 'left',
1569
                       '-expand' => 'y',
1570
                       '-anchor' => 's',
1571
                       '-padx' => 5,
1572
                       '-pady' => 5,
1573
                      );
1574
        $book_button->Button('-text'    => $Translate{'Abort'},
1575
                             '-width' => '10',
1576
                             '-command' => sub { $popup->destroy() },
1577
                            )->pack ('-side' => 'left',
1578
                                     '-expand' => 'y',
1579
                                     '-anchor' => 's',
1580
                                     '-padx' => 5,
1581
                                     '-pady' => 5,
1582
                                     );
1583
        $book_button->Label ('-text' => $Translate{'RestartRequired'},
1584
                             '-anchor' => 'w',
1585
                             '-foreground' => 'red',
1586
                            ) ->pack ('-side' => 'left',
1587
                                      '-expand' => 'y',
1588
                                      '-anchor' => 's',
1589
                                      '-padx' => 10,
1590
                                      '-pady' => 5,
1591
                                      );
1592
 
1593
        # Frame mit den Labels
1594
        my $popup_label = $Tab->Frame() -> pack('-side' => 'left',
1595
                                                '-expand' => 'y',
1596
                                                '-anchor' => 'w',
1597
                                                '-padx' => 10,
1598
                                                '-pady' => 10,
1599
                                                );
1600
        # Labels anzeigen                        
1601
        foreach $Label ( sort keys %{$CfgEdit->{$key}})
1602
            {
1603
            if ( $Translate{$Label} ne "" )
1604
                {
1605
                $Label = $Translate{$Label};
1606
                }
1607
 
1608
            $popup_label->Label ('-text' => $Label,
1609
                                 '-width' => 35,
1610
                                 '-anchor' => 'w',
1611
                                ) -> pack();
1612
            }
1613
 
1614
        # Frame mit den Daten
1615
        my $popup_values = $Tab->Frame() -> pack('-side' => 'left',
1616
                                                 '-expand' => 'y',
1617
                                                 '-anchor' => 'w',
1618
                                                 '-padx' => 10,
1619
                                                 '-pady' => 10,
1620
                                                 );
1621
        # Eingabefelder mit Daten anzeigen
1622
        foreach $Value ( sort keys %{$CfgEdit->{$key}})
1623
            {                          
1624
            $popup_values->Entry ('-textvariable' => \$CfgEdit->{$key}->{$Value},
1625
                                  '-exportselection' => '1',
1626
                                  '-width' => 30,
1627
                                  '-relief' => 'sunken',
1628
                                 ) -> pack();  
1629
            }
1630
        }
1631
    }
1632
 
1633
 
1634
1;
1635
 
1636
__END__