Subversion Repositories Projects

Rev

Details | Last modification | View Log | RSS feed

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