Subversion Repositories Projects

Rev

Details | Last modification | View Log | RSS feed

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