Subversion Repositories Projects

Rev

Details | Last modification | View Log | RSS feed

Rev Author Line No. Line
739 rain-er 1
#!/usr/bin/perl
2
#!/usr/bin/perl -d:ptkdb
3
 
4
###############################################################################
5
#
6
# libmktime.pl -  MK Mission Cockpit - Timer 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 Timer moved from mkcockpit.pl
41
#                     Optional Player home-pos in map configuration
42
# 2009-08-23 0.2.6 rw Tracking-Antenna Icon
43
#                     POI heading control
44
# 2009-10-11 0.2.7 rw Tracker control changed
45
# 2010-02-10 0.4.0 rw Altitude average changed
46
#                     Event Engine timer
47
#                     Serial channel timer
48
#                     External Control rimer
49
#                     Crosshair Timer
50
#                     5s timer changed to 3s
51
#                     Current, UsedCapacity, Power added
52
# 2010-02-13 0.4.1 rw bugfix JoystickButton min-value
53
# 2010-02-14 0.4.2 rw Input chontrol parser added
54
#                     Request LCD Stick/Poti
55
#
56
###############################################################################
57
 
58
$Version{'libmktimer.pl'}  = "0.4.1 - 2010-02-13";
59
 
60
use Math::Trig;
61
use Time::HiRes qw(gettimeofday);   # http://search.cpan.org/~jhi/Time-HiRes-1.9719/HiRes.pm
62
 
63
#
64
# Timer: 3s
65
#
66
$main->repeat (3000, sub
67
    {
68
    if ( ! $MkSendWp )
69
        {
70
        # Abfragefrequenz OSD und Debug regelmäßig neu einstellen, falls Übertragungsfehler
71
        $MkSendQueue->enqueue( "o", "$AddrNC", pack ("C", 10) );          # Frequenz OSD Datensatz, * 10ms
72
        $MkSendQueue->enqueue( "d", "$AddrNC", pack ("C", 10) );          # Frequenz MK Debug Datensatz, * 10ms
73
        $MkSendQueue->enqueue( "v", "$AddrNC", "");   # Version
74
        $MkSendQueue->enqueue( "e", "$AddrNC", "");   # Error Text Request
75
        }
76
 
77
    lock (%MkOsd);              # until end of block
78
 
79
    # Draw Operation Radius Border
80
    $map_canvas->delete('Map-Border-OperatingRadius');
81
    if ( &HomePosIsValid() )
82
        {
83
        my $Radius = $MkOsd{'OperatingRadius'};
84
        my $H_Lat = $MkOsd{'HomePos_Lat'};
85
        my $H_Lon = $MkOsd{'HomePos_Lon'};
86
        my $Angel = &MapAngel();
87
 
88
        my ($T_Lat, $T_Lon) = &MapGpsAt ($H_Lat, $H_Lon, $Radius, $Angel -90);
89
        my ($R_Lat, $R_Lon) = &MapGpsAt ($H_Lat, $H_Lon, $Radius, $Angel);
90
        my ($B_Lat, $B_Lon) = &MapGpsAt ($H_Lat, $H_Lon, $Radius, $Angel + 90);
91
        my ($L_Lat, $L_Lon) = &MapGpsAt ($H_Lat, $H_Lon, $Radius, $Angel + 180);
92
 
93
        my ($T_x, $T_y) = &MapGps2XY ($T_Lat, $T_Lon);
94
        my ($R_x, $R_y) = &MapGps2XY ($R_Lat, $R_Lon);
95
        my ($B_x, $B_y) = &MapGps2XY ($B_Lat, $B_Lon);
96
        my ($L_x, $L_y) = &MapGps2XY ($L_Lat, $L_Lon);
97
 
98
        $map_canvas->createArc ( $L_x, $B_y, $R_x, $T_y,
99
                                 '-tags' => 'Map-Border-OperatingRadius',
100
                                 '-extent' => '359',
101
                                 '-start' => '0',
102
                                 '-style' => 'chord',
103
                                 '-outline' => $Cfg->{'mkcockpit'}->{'ColorAirfield'},
104
                                 '-width' => '1',
105
                               );
106
        $map_canvas->raise('Map-Border-OperatingRadius', 'Map');  # Border above Map
107
        }
108
 
109
    });
110
 
111
 
112
 
113
#       
114
# Timer: 0.1s - Map Overlay aktualisieren
115
#
116
$frame_map_top->repeat (100, sub
117
    {
118
 
119
    # Clear old messages from this timer
120
    &MkMessageInit ("Timer-MapOverlay");
121
 
122
    lock (%MkOsd);              # until end of block
123
    lock (%MkNcDebug);          # until end of block
124
 
125
    # Aktuell gültige Karte
126
    %Map = %{$Maps{'Current'}};
127
 
128
    if ( &MkOsdIsValid() )
129
        {
130
        # Gueltige OSD Daten
131
 
132
        # Operation Mode
133
        $OperationMode = "";
134
        if ( &MkIsWptMode() )         { $OperationMode = "WPT"; }
135
        if ( $PlayerMode eq "Play" )  { $OperationMode = "Play"; }
136
        if ( $PlayerMode eq "Pause" ) { $OperationMode = "Paus"; }
137
        if ( $PlayerMode eq "Home" )  { $OperationMode = "Home"; }
138
        if ( &MkIsPhMode() )          { $OperationMode = "PH"; }
139
        if ( &MkIsFreeMode() )        { $OperationMode = "Free"; }
140
 
141
        my $SatsInUse = $MkOsd{'SatsInUse'};
142
        if ( &CurPosIsValid()  and  &HomePosIsValid() )
143
            {
144
            # ausreichender GPS Empfang
145
 
146
            # get x,y map coords of current position
147
            my ($C_x, $C_y, $C_Angel) = &MapGps2XY($MkOsd{'CurPos_Lat'}, $MkOsd{'CurPos_Lon'}, $MkOsd{'CompassHeading'});
148
            $MkPos_x = $C_x;
149
            $MkPos_y = $C_y;
150
 
151
            $System{'CurrPos_x'} = $C_x;
152
            $System{'CurrPos_y'} = $C_y;
153
 
154
            # rotate MK arrow
155
            my $dy = sin (deg2rad $C_Angel) * ($MapMkLen/2);
156
            my $dx = cos (deg2rad $C_Angel) * ($MapMkLen/2);
157
            my $x0 = $C_x - $dx;
158
            my $y0 = $C_y - $dy;
159
            my $x1 = $C_x + $dx;
160
            my $y1 = $C_y + $dy;
161
            $map_canvas->coords ('MK-Arrow', $x0, $y0, $x1, $y1);
162
 
163
            # Update speed vector
164
            my $MapAngel = &MapAngel();   # North to Map-Horizont
165
            my $GpsSpeedNorth = $MkNcDebug{'Analog_21'};
166
            my $GpsSpeedEast  = $MkNcDebug{'Analog_22'};
167
            my $PhiGpsSpeed = rad2deg atan2 ( $GpsSpeedEast, $GpsSpeedNorth );
168
            $PhiMapSpeed = $PhiGpsSpeed - $MapAngel;
169
 
170
            # 555 cm/s ~ 20 km/h -> Zeigerlänge = $MkSpeedLen bei 20 km/h
171
            my $dy = sin (deg2rad $PhiMapSpeed) * $MapMkSpeedLen * $MkOsd{'GroundSpeed'} / 555;
172
            my $dx = cos (deg2rad $PhiMapSpeed) * $MapMkSpeedLen * $MkOsd{'GroundSpeed'} / 555;
173
            my $x0 = $C_x;
174
            my $y0 = $C_y;
175
            my $x1 = $C_x + $dx;
176
            my $y1 = $C_y + $dy;
177
            $map_canvas->coords ('MK-Speed', $x0, $y0, $x1, $y1);
178
 
179
            # Update Line between Home and MK
180
            my ($H_x, $H_y) = &MapGps2XY($MkOsd{'HomePos_Lat'}, $MkOsd{'HomePos_Lon'});
181
            $map_canvas->coords ('MK-Home-Line', $H_x, $H_y, $C_x, $C_y);
182
 
183
            # Update Distance between Home and MK
184
            my ($Dist, $Bearing) = &MapGpsTo($MkOsd{'CurPos_Lat'}, $MkOsd{'CurPos_Lon'},
185
                                                    $MkOsd{'HomePos_Lat'}, $MkOsd{'HomePos_Lon'} );
186
            my $x = ($C_x - $H_x) / 2 + $H_x + 8;
187
            my $y = ($C_y - $H_y) / 2 + $H_y + 8;
188
            $map_canvas->coords ('MK-Home-Dist', $x, $y);
189
            $map_canvas->itemconfigure ('MK-Home-Dist',
190
                                        '-text' => sprintf ("%4d m", int ($Dist + 0.5) ),
191
                                       );
192
            $System{'HomePos_y'} = $H_x;
193
            $System{'HomePos_x'} = $H_y;
194
            $System{'HomeDist'} = $Dist;
195
            $System{'HomeBearing'} = $Bearing;
196
 
197
            # Update OSD - Sat dependent values
198
            $map_canvas->itemconfigure ('MK-OSD-Spd-Value', '-text' => sprintf ("%3d km/h", $MkOsd{'GroundSpeed'} * 0.036) );
199
 
200
            # Alt = average Luftdruck und Sat
201
            my $Alt = int (&Altitude() + 0.5);
202
            $map_canvas->itemconfigure ('MK-OSD-Alt-Value', '-text' => sprintf ("%3d m", $Alt) );
203
            $System{'Alt'} = $Alt;
204
 
205
            if ( &TargetIsValid() )
206
                {
207
                # Valid Target
208
 
209
                # Update Line between Target and MK
210
                my ($T_x, $T_y) = &MapGps2XY($MkOsd{'TargetPos_Lat'}, $MkOsd{'TargetPos_Lon'});
211
                $map_canvas->coords ('MK-Target-Line', $C_x, $C_y, $T_x, $T_y);
212
 
213
                # Update Distance between Target and MK
214
                my ($Dist, $Bearing) = &MapGpsTo($MkOsd{'CurPos_Lat'}, $MkOsd{'CurPos_Lon'},
215
                                                         $MkOsd{'TargetPos_Lat'}, $MkOsd{'TargetPos_Lon'} );
216
                $System{'TargetPos_x'} = $T_x;
217
                $System{'TargetPos_y'} = $T_y;
218
                $System{'TargetDist'} = $Dist;
219
                $System{'TargetBearing'} = $Bearing;
220
 
221
                if ( $Dist >= 25 )  
222
                    {
223
                    my $x = ($C_x - $T_x) / 2 + $T_x - 8;
224
                    my $y = ($C_y - $T_y) / 2 + $T_y + 8;
225
                    $map_canvas->coords ('MK-Target-Dist', $x, $y);
226
                    $map_canvas->itemconfigure ('MK-Target-Dist',
227
                                                '-text' => sprintf ("%4d m", int ($Dist + 0.5) ),
228
                                               );
229
                    }
230
                else
231
                    {
232
                    # Don't show distance < 25m
233
                    $map_canvas->coords ('MK-Target-Dist', 0, -100);
234
                    }
235
 
236
                # show target icon
237
                my $IconHeight = 48;
238
                my $IconWidth = 48;
239
                $map_canvas->coords('Target', $T_x - $IconWidth/2, $T_y - $IconHeight );
240
 
241
                $System{'CrossingBorder'} = 0;
242
                if ( &MkIsFlying()  and  &IsCrossingBorder($MkPos_x, $MkPos_y, $T_x, $T_y) )
243
                    {
244
                    # only, if MK is flying
245
                    $System{'CrossingBorder'} = 1;
246
                    &MkMessage ($Translate{'MsgCrossingBorder'}, "Timer-MapOverlay");
247
                    }
248
                }
249
            else
250
                {
251
                # No valid Target, move target line out of sight/canvas
252
                $map_canvas->coords ('MK-Target-Line', 0, -100, 0, -100);
253
                $map_canvas->coords ('MK-Target-Dist', 0, -100);
254
 
255
                # hide target icon
256
                $map_canvas->coords('Target', 0, -100, );
257
                }
258
 
259
            # Update Line between MK and POI
260
            if ( $PoiMode )
261
                {
262
                my ($P_x, $P_y) = &MapGps2XY($Poi_Lat, $Poi_Lon);
263
                $map_canvas->coords ('MK-POI-Line', $P_x, $P_y, $C_x, $C_y);
264
 
265
                $System{'PoiPos_x'} = $P_x;
266
                $System{'PoiPos_y'} = $P_y;
267
                }
268
            else
269
                {
270
                $map_canvas->coords ('MK-POI-Line', 0, -200, 0, -200);
271
                }
272
            }
273
        else
274
            {
275
            # kein ausreichender Sat-Empfang
276
            $map_canvas->itemconfigure ('MK-OSD-Spd-Value', '-text' => sprintf ("%3d km/h", 0 ) );
277
            }
278
 
279
        # Update OSD - non Sat dependent values
280
        $map_canvas->itemconfigure ('MK-OSD-Odo-Value', '-text' => sprintf ("%3.3f km", $OdoMeter / 1000) );
281
        $map_canvas->itemconfigure ('MK-OSD-Tim-Value', '-text' => sprintf ("%02d:%02d", $MkFlyingTime / 60, $MkFlyingTime % 60) );
282
        $map_canvas->itemconfigure ('MK-OSD-Sat-Value', '-text' => $MkOsd{'SatsInUse'} );
283
        $map_canvas->itemconfigure ('MK-OSD-Cur-Value', '-text' => sprintf ("%.1f A", $MkOsd{'Current'}) );
284
 
285
        # Used Capacity
286
        my $UsedCapacityFactor = $Cfg->{'map'}->{'UsedCapacityFactor'} || "1.0";
287
        $System{'UsedCapacity'} = $MkOsd{'UsedCapacity'} * $UsedCapacityFactor;
288
        $map_canvas->itemconfigure ('MK-OSD-Cap-Value', '-text' => sprintf ("%.2f Ah", $System{'UsedCapacity'} / 1000) );
289
 
290
        # Power
291
        $System{'Power'} = int ($MkOsd{'Current'} * $MkOsd{'UBat'} + 0.5);
292
        $map_canvas->itemconfigure ('MK-OSD-Pow-Value', '-text' => sprintf ("%d W", $System{'Power'} ) );
293
 
294
        # battery - OSD and warning
295
        my $UBat = sprintf ("%3.1f", $MkOsd{'UBat'});
296
        $System{'UBat'} = $UBat;
297
        $map_canvas->itemconfigure ('MK-OSD-Bat-Value', '-text' => "$UBat V" );
298
 
299
        $System{'BatWarning'} = 0;
300
        $map_canvas->itemconfigure ('MK-OSD-Bat-Value', '-fill' => $Cfg->{'mkcockpit'}->{'ColorOsd'});
301
        if ( $MkOsd{'UBat'}  <  $Cfg->{'map'}->{'UBatWarning'} )
302
            {
303
            if ( time %2 )
304
                {
305
                $map_canvas->itemconfigure ('MK-OSD-Bat-Value', '-fill' => 'red');
306
                }
307
 
308
            &MkMessage ($Translate{'MsgBatWarning'}, "Timer-MapOverlay");
309
            $System{'BatWarning'} = 1;
310
            }
311
 
312
 
313
        # Display Operation Mode
314
        my $DisplayMode = $OperationMode;
315
        if ( &MkIsWptMode()  and  $OperationMode eq "Play" )
316
            {
317
            my %ModeMatrix =
318
               (
319
               "KML-STD" => "Play KML",
320
               "KML-RND" => "Play KML",
321
               "KML-MAP" => "Play KML",
322
               "WPT-STD" => "Play WPT",
323
               "WPT-RND" => "Rand WPT",
324
               "WPT-MAP" => "Rand MAP",
325
               );
326
            my $Key = "${PlayerWptKmlMode}-${PlayerRandomMode}";
327
            $DisplayMode = $ModeMatrix{$Key};
328
            }
329
 
330
        if ( &MkIsWptMode()  and  $OperationMode eq "Paus" )
331
            {
332
            $DisplayMode = $DisplayMode . " " . $PlayerPauseMode;
333
            }
334
 
335
        $System{'RangeWarning'} = 0;
336
        if ( &MkRangeLimit() )
337
            {
338
            $DisplayMode  = "$DisplayMode" . " !!";   # Range Warning
339
            $System{'RangeWarning'} = 1;
340
            }
341
        $map_canvas->itemconfigure ('MK-OSD-Mode-Value', '-text' => $DisplayMode );
342
 
343
 
344
        # Waypoints abhaengig vom Modus NC/Player
345
        my $WpValue = "-- / --";
346
        if ( $MkOsd{'WaypointNumber'} > 0)
347
            {
348
            $WpValue = sprintf ("%d / %d", $MkOsd{'WaypointIndex'}, $MkOsd{'WaypointNumber'});
349
            }
350
        if ($PlayerMode ne "Stop" and $PlayerWptKmlMode eq "WPT")
351
            {
352
            $WpValue = sprintf ("%d / %d", $WpPlayerIndex +1, scalar @Waypoints);
353
            }
354
        if ($PlayerMode ne "Stop" and $PlayerWptKmlMode eq "KML" )
355
            {
356
            my $KmlTimeBase = $Cfg->{'waypoint'}->{'KmlTimeBase'} || 1.0;
357
            my $CurrTime = int ($KmlPlayerIndex * $KmlTimeBase + 0.5);
358
            my $TotTime = int (scalar @KmlTargets * $KmlTimeBase + 0.5);
359
            $WpValue = sprintf ("%02d:%02d / %02d:%02d", $CurrTime / 60, $CurrTime % 60, $TotTime / 60, $TotTime % 60);
360
            }
361
        $map_canvas->itemconfigure ('MK-OSD-Wp-Value',  '-text' => "$WpValue");
362
 
363
        # Recording Mode
364
        my $RecordText = "";
365
        if ( $PlayerRecordMode =~ /REC/i )
366
            {
367
            my $KmlTimeBase = $Cfg->{'waypoint'}->{'KmlTimeBase'} || 1.0;
368
            my $TotTime = int (scalar @KmlTargets * $KmlTimeBase + 0.5);
369
            $RecordText = sprintf ("Recording %02d:%02d", $TotTime / 60, $TotTime % 60);
370
            }
371
        $map_canvas->itemconfigure ('MK-OSD-Rec-Value', '-text' => $RecordText );
372
 
373
 
374
        # Farbe MK-Zeiger abhängig vom GPS Empfang
375
        my $MkCol= $Cfg->{'mkcockpit'}->{'ColorMkSatNo'};
376
        if ( $SatsInUse >= 1 ) { $MkCol = $Cfg->{'mkcockpit'}->{'ColorMkSatLow'} ; }
377
        if ( $SatsInUse >= 6 ) { $MkCol = $Cfg->{'mkcockpit'}->{'ColorMkSatGood'}; }
378
        $map_canvas->itemconfigure ('MK-Arrow', '-fill' => $MkCol);
379
 
380
 
381
        # Show/Hide SatFix Icon
382
        if ($SatsInUse >= 6 )
383
            {
384
            $map_canvas->coords('Satellite', $MapSizeX-100, 10, );
385
            }
386
        else
387
            {
388
            # move icon out of sight
389
            $map_canvas->coords('Satellite', 0, -100, );
390
            }
391
 
392
 
393
        # Variometer Pointer
394
        my $dy = -$MkOsd{'Variometer'} * 10;
395
        $map_canvas->coords('Map-Variometer-Pointer', 5, $MapSizeY/2+$dy, 20, $MapSizeY/2+10+$dy, 20, $MapSizeY/2-10+$dy);
396
 
397
        #
398
        # System checks
399
        #
400
 
401
        if ( ! &MkIsMotorOn() )      { &MkMessage ($Translate{'MsgMotorOff'}, "Timer-MapOverlay"); }
402
        if ( ! &MkIsFlying() )       { &MkMessage ($Translate{'MsgNotFlying'}, "Timer-MapOverlay"); }
403
        if ( &MkIsCalibrating() )    { &MkMessage ($Translate{'MsgCalibrate'}, "Timer-MapOverlay"); }
404
        if ( &MkIsMotorStarting() )  { &MkMessage ($Translate{'MsgStart'}, "Timer-MapOverlay") }
405
        if ( &MkEmergencyLanding() ) { &MkMessage ($Translate{'MsgEmergencyLanding'}, "Timer-MapOverlay"); }
406
        if ( &MkRangeLimit() )       { &MkMessage ($Translate{'MsgRangeLimit'}, "Timer-MapOverlay"); }
407
 
408
        # RC range check
409
        my $RcQuality = $MkOsd{'RC_Quality'};
410
        $System{'RCQuality'} = "";
411
 
412
        if ( $RcQuality < 100 )
413
            {
414
            $System{'RCQuality'} = "NO";
415
            &MkMessage ($Translate{'MsgRcError'}, "Timer-MapOverlay");
416
            }
417
        elsif ( $RcQuality < 150 )
418
            {
419
            $System{'RCQuality'} = "WEAK";
420
            &MkMessage ($Translate{'MsgRcWarning'}, "Timer-MapOverlay");
421
            }
422
 
423
        # Sat reception quality
424
        if ( $SatsInUse == 0 )
425
            {
426
            &MkMessage ($Translate{'MsgNoSatReception'}, "Timer-MapOverlay");
427
            }
428
        elsif ( $SatsInUse > 0  and $SatsInUse < 6 )
429
            {
430
            &MkMessage ($Translate{'MsgWeakSatReception'}, "Timer-MapOverlay");
431
            }
432
 
433
        # MK Border check
434
        $System{'OutsideBorder'} = "0";
435
        if ( &MkIsFlying() and ! &IsInsideBorder($MkPos_x, $MkPos_y) )
436
            {
437
            # only, if MK is flying
438
            $System{'OutsideBorder'} = "1";
439
            &MkMessage ($Translate{'MsgOutsideBorder'}, "Timer-MapOverlay");
440
            }
441
 
442
        # Show Balloon, when aproaching Target
443
        &TargetMessageShow();
444
 
445
        }
446
    else
447
        {
448
        # keine aktuellen OSD Daten vom MK verfügbar
449
        &MkMessage ($Translate{'MsgNoData'}, "Timer-MapOverlay");
450
        }
451
 
452
 
453
    # Wp-Number input from keyboard
454
    $KbTimer++;
455
    if ( $CbPlayerKey ne "" )
456
        {
457
        # Key pressed
458
        $KbNum = "$KbNum" . "$CbPlayerKey";
459
 
460
        $CbPlayerKey = "";
461
        $KbTimer = 0;
462
        }
463
    if ( $KbTimer > 7  and $KbNum ne "" )
464
        {
465
        # number complete, set target
466
        my $WpIndex = sprintf ("%d", $KbNum);
467
        &WpTargetSet ($WpIndex - 1);
468
 
469
        # prepare for next number
470
        $KbNum = "";
471
        }
472
 
473
 
474
    # Show System Messages
475
    &MkMessageShow();
476
 
477
 
478
    # display transmit status in top status line
479
    my $Color  = 'lightgray';
480
    my $Relief = 'flat';
481
    if ( $Cfg->{'serialchannel'}->{'SerialChannelSend'} =~ /y/i )
482
        {
483
        $Color = 'green';
484
        $Relief = 'sunken';
485
        }
486
    if ($TxExtOn == 0 )
487
        {
488
        $Color = 'red';
489
        $Relief = 'sunken';
490
        }
491
    $map_status_top[0]->configure (-text       => "Tx:$Translate{'serialchannel'}",
492
                                   -background => $Color,
493
                                   -relief     => $Relief,
494
                                  );
495
    my $Color = 'lightgray';
496
    my $Relief = 'flat';
497
    if ( $Cfg->{'externcontrol'}->{'ExternControlSend'} =~ /y/i )
498
        {
499
        $Color  = 'green';
500
        $Relief = 'sunken';
501
        }
502
    if ($TxExtOn == 0 )
503
        {
504
        $Color  = 'red';
505
        $Relief = 'sunken';
506
        }
507
    $map_status_top[1]->configure (-text       => "Tx:$Translate{'externcontrol'}",
508
                                   -background => $Color,
509
                                   -relief     => $Relief,
510
                                  );
511
 
512
    # display event status in lower status line, max. 10 fields
513
    my @Event = sort keys %{$Event};
514
    my $EventIndex = 0;
515
    while ( $EventIndex < 10 )
516
        {
517
        my $Key = pop @Event;
518
        my $Color = 'lightgray';
519
        my $Relief = 'flat';
520
 
521
        if ( $Key eq "" )
522
            {
523
            # clear unused fields
524
            $map_status_event[$EventIndex]->configure (-text       => $Key,
525
                                                       -background => $Color,
526
                                                       -relief     => $Relief,
527
                                                      );
528
            $EventIndex ++;
529
            }
530
 
531
        elsif ( ref $Event->{$Key} ne ""  and  $Event->{$Key}->{'Active'} =~ /y/i )
532
            {
533
            if ( $EventStat{$Key}{'Status'} eq "ON" )
534
                {
535
                $Color  = 'green';
536
                $Relief = 'sunken';
537
                }
538
            elsif ( $EventStat{$Key}{'Status'} eq "OFF" )
539
                {
540
                $Color  = 'red';
541
                $Relief = 'sunken';
542
                }
543
 
544
            $map_status_event[$EventIndex]->configure (-text       => $Key,
545
                                                       -background => $Color,
546
                                                       -relief     => $Relief,
547
                                                      );
548
            $EventIndex ++;
549
            }
550
         else
551
            {
552
            # don't display disabled events
553
            }
554
        }
555
    });
556
 
557
 
558
#       
559
# Timer: 0.25s - request Poti LCD-Info Screen 9,10  (8= Sticks)
560
#
561
my $LcdScreen = 9;
562
if ( $Cfg->{'mkcomm'}->{'RequestRcChannel'} =~ /y/i )
563
    {
564
    $frame_map_top->repeat (250, sub
565
        {
566
        # request only one screen at each timer run to avoid data link overload
567
        $MkSendQueue->enqueue( "l", "$AddrNC", pack ("C", $LcdScreen) );
568
 
569
        $LcdScreen ++;
570
        if ( $LcdScreen > 10 )
571
            {
572
            $LcdScreen = 9;
573
            }
574
        });
575
    }
576
 
577
#       
578
# Timer: 0.1s - Tracking Anzeige aktualisieren
579
#
580
if ( $Cfg->{'track'}->{'Active'} =~ /y/i )
581
    {
582
    $TrackMkCalibrateEdge = 0;  # calibrate edge detection
583
 
584
    $frame_map_top->repeat (100, sub
585
        {
586
        # Clear old messages from this timer
587
        &MkMessageInit ("Timer-Tracking");    
588
 
589
        lock (%MkOsd);              # until end of block
590
        lock (%MkTrack);            # until end of block
591
 
592
        # Aktuell gültige Karte
593
        %Map = %{$Maps{'Current'}};
594
 
595
        # Zeiger neu zeichnen
596
        my $AngelPan  = @ServoPos[$MkTrack{'ServoPan'}];
597
        my $AngelTilt = @ServoPos[$MkTrack{'ServoTilt'}];
598
        if ( $AngelPan ne "" and $AngelTilt ne "" )
599
            {
600
            my $Angel = $AngelPan;
601
            if ( $AngelTilt > 90 )
602
                {
603
                $Angel += 180;
604
                }
605
 
606
            # Pan
607
            my $x0 = $TrackSizeX/2;    
608
            my $y0 = $TrackSizeY - 0 - $TrackOffY;
609
            my $x1 = $TrackSizeX/2 - ($TrackPtrLen-22) * cos( deg2rad $Angel);
610
            my $y1 = $TrackSizeY   - ($TrackPtrLen-22) * sin (deg2rad $Angel) - $TrackOffY;
611
            $track_canvas->coords ('Track-Ptr-Pan', $x0, $y0, $x1, $y1);
612
 
613
            # Tilt
614
            my $x0 = $TrackSizeX/2;    
615
            my $y0 = $TrackSizeY - 0 - $TrackOffY;
616
            my $x1 = $TrackSizeX/2 - ($TrackPtrLen-22) * cos( deg2rad 180 - $AngelTilt);
617
            my $y1 = $TrackSizeY   - ($TrackPtrLen-22) * sin (deg2rad 180 - $AngelTilt) - $TrackOffY;
618
            $track_canvas->coords ('Track-Ptr-Tilt', $x0, $y0, $x1, $y1);
619
            }
620
 
621
        # Farbe Zeiger abhängig vom GPS Empfang
622
        my $SatsInUse = $MkOsd{'SatsInUse'};
623
        my $TrackPtrCol= 'red';
624
        if ( $SatsInUse >= 1 ) { $TrackPtrCol = 'orange'; }
625
        if ( $SatsInUse >= 6 ) { $TrackPtrCol = 'green'; }
626
        $track_canvas->itemconfigure ('Track-Ptr-Pan', '-fill' => $TrackPtrCol);
627
 
628
        # tracker coldstart condition at MK calibration, rising edge of signal
629
        if ( &MkIsCalibrating() )
630
            {
631
            if ( $TrackMkCalibrateEdge == 0 )
632
                {
633
                $TrackMkCalibrateEdge = 1;
634
 
635
                # send coldstart command to tracker
636
                $TrackQueue->enqueue("COLDSTART");
637
                }
638
            }
639
        else
640
            {
641
            $TrackMkCalibrateEdge = 0;
642
            }
643
        });
644
    }
645
 
646
 
647
#       
648
# Timer: 0.5s - Waypoint Player
649
#
650
$frame_map_top->repeat (500, sub
651
    {
652
    # Clear old messages from this timer
653
    &MkMessageInit ("Timer-Player");        
654
 
655
    lock (%MkOsd);              # until end of block
656
 
657
    if ( &MkIsWptMode() )
658
        {
659
        # NC is in WPT Mode 
660
 
661
        my $PoiBearing = $Cfg->{'waypoint'}->{'DefaultHeading'};
662
        if ( &CurPosIsValid()  and  $PoiMode )
663
            {
664
            $PoiBearing = &MapGpsTo ( $MkOsd{'CurPos_Lat'}, $MkOsd{'CurPos_Lon'},
665
                                      $Poi_Lat,  $Poi_Lon );
666
            $System{'PoiBearing'} = $PoiBearing;
667
            }
668
 
669
        if ( $PlayerMode eq "Pause" )
670
            {
671
            if ( $PlayerPause_Lat ne ""  and  $PlayerPause_Lon ne "" )
672
                {
673
                # Gespeicherte Pausen-Pos senden
674
                &MkFlyTo ( '-lat'  => $PlayerPause_Lat,
675
                           '-lon'  => $PlayerPause_Lon,
676
                           '-holdtime' => "60",
677
                           '-heading' => $PoiBearing,
678
                           '-mode' => "Target",
679
                         );
680
                }
681
            }
682
 
683
        if ( $PlayerMode eq "Home" and &HomePosIsValid() )
684
            {
685
            # Gespeicherte oder eingestellte Home-Pos senden
686
 
687
            my $Home_Lat = $Map{'Home_Lat'} || $MkOsd{'HomePos_Lat'};
688
            my $Home_Lon = $Map{'Home_Lon'} || $MkOsd{'HomePos_Lon'};
689
 
690
            &MkFlyTo ( '-lat'  => $Home_Lat,
691
                       '-lon'  => $Home_Lon,
692
                       '-holdtime' => "60",
693
                       '-heading' => $PoiBearing,
694
                       '-mode' => "Target",
695
                     );
696
            }
697
 
698
 
699
        if ( $PlayerWptKmlMode ne 'WPT' )
700
            {
701
            # not in Wp mode
702
            return;
703
            }
704
 
705
 
706
        if ( $PlayerMode eq "Play"  )
707
            {
708
 
709
            if ( $PlayerRandomMode =~ /RND/i  or $PlayerRandomMode =~ /STD/i )
710
                {
711
                my $WpCnt = scalar @Waypoints;
712
                if ( $WpCnt > 0  and  $WpPlayerIndex < $WpCnt )
713
                    {
714
                    # Target WP-Pos senden
715
                    my $Wp = $Waypoints[$WpPlayerIndex];
716
                    my $Wp_Lon = $Wp->{'Pos_Lon'};
717
                    my $Wp_Lat = $Wp->{'Pos_Lat'};
718
                    if ( $Wp_Lat ne ""  and  $Wp_Lon ne "" )
719
                        {
720
                        &MkFlyTo ( '-lat'  => $Wp_Lat,
721
                                   '-lon'  => $Wp_Lon,
722
                                   '-holdtime' => "60",
723
                                   '-heading' => $PoiBearing,
724
                                   '-mode' => "Target",
725
                                 );
726
                        }
727
                    }
728
                }
729
 
730
            if ( $PlayerRandomMode =~ /MAP/i )
731
                {
732
                # Target Map-Pos senden
733
                &MkFlyTo ( '-x'  => $RandomTarget_x ,
734
                           '-y'  => $RandomTarget_y ,
735
                           '-holdtime' => "60",
736
                           '-heading' => $PoiBearing,
737
                           '-mode' => "Target",
738
                         );
739
                }
740
 
741
            # Ziel erreicht?
742
            if ( &WpCheckTargetReached() )
743
                {
744
                &WpTargetNext();
745
                }
746
            }
747
        }
748
 
749
    # WP Player Holdtime count down
750
    if ( $WpPlayerHoldtime > 0  )
751
        {
752
        $WpPlayerHoldtime --;
753
        }
754
    });
755
 
756
 
757
#       
758
# Timer: variabel - KML Player
759
#
760
my $KmlTimeBase = $Cfg->{'waypoint'}->{'KmlTimeBase'} || 1.0;
761
$KmlTimeBase *= 1000;
762
 
763
$frame_map_top->repeat ($KmlTimeBase, sub
764
    {
765
 
766
    # Clear old messages from this timer
767
    &MkMessageInit ("Timer-KMLPlayer");    
768
 
769
    lock (%MkOsd);              # until end of block
770
 
771
    if ( &CurPosIsValid() and $PlayerRecordMode =~ /REC/i )
772
        {
773
        # record current position
774
        push @KmlTargets, {
775
                          'Lat' => $MkOsd{'CurPos_Lat'},
776
                          'Lon' => $MkOsd{'CurPos_Lon'},
777
                          'Alt' => $MkOsd{'CurPos_Alt'},
778
                          };
779
        }
780
 
781
 
782
    if ( &MkIsWptMode() and  $PlayerMode eq "Play"  and  $PlayerWptKmlMode eq 'KML')
783
        {
784
        # Play KML
785
 
786
        # Pause, Home is handled in WPT-Timer
787
 
788
        my $KmlCnt = scalar @KmlTargets;
789
        if ( $KmlCnt > 0  and  $KmlPlayerIndex < $KmlCnt )
790
            {
791
            my $Lat = $KmlTargets[$KmlPlayerIndex]->{'Lat'};
792
            my $Lon = $KmlTargets[$KmlPlayerIndex]->{'Lon'};
793
            my $Alt = $KmlTargets[$KmlPlayerIndex]->{'Alt'};
794
 
795
            &MkFlyTo ( '-lat'             => $Lat,
796
                       '-lon'             => $Lon,
797
                       '-alt'             => $Alt,
798
                       '-holdtime'        => "60",
799
                       '-mode'            => "Target",
800
                     );
801
 
802
            # proceed to next Target
803
            $KmlPlayerIndex ++;
804
            if ( $KmlPlayerIndex >= $KmlCnt )
805
                {
806
                $KmlPlayerIndex = 0;
807
                }
808
            }
809
        }
810
 
811
    });
812
 
813
 
814
#       
815
# Timer: 1s
816
#
817
$frame_map_top->repeat (1000, sub
818
    {
819
    # Clear old messages from this timer
820
    &MkMessageInit ("Timer-Misc-1s");    
821
 
822
    lock (%MkOsd);              # until end of block
823
 
824
    # Aktuell gültige Karte
825
    %Map = %{$Maps{'Current'}};
826
 
827
    if ( &MkOsdIsValid() )
828
        {
829
 
830
        # Heartbeat MK Datenübertragung
831
        if ( time %2 )
832
            {
833
            $map_canvas->itemconfigure('Heartbeat', '-image' => 'HeartbeatLarge', );
834
            }
835
        else
836
            {
837
            $map_canvas->itemconfigure('Heartbeat', '-image' => 'HeartbeatSmall', );
838
            }
839
 
840
        # Flugzeit aktualisieren
841
        # Flugzeit selber mitzählen, da $MkOsd{'FlyingTime'} immer 0 (0.14b)
842
        if ( &MkIsFlying() )
843
            {
844
            $MkFlyingTime += 1;
845
            }
846
 
847
        # Update ODO-Meter
848
        if ( &CurPosIsValid() )
849
            {
850
            my $C_Lat = $MkOsd{'CurPos_Lat'};
851
            my $C_Lon = $MkOsd{'CurPos_Lon'};
852
 
853
            if ( $OdoFirst ne "" )
854
                {
855
                my ($Dist, $Bearing) = &MapGpsTo($C_Lat, $C_Lon, $OdoPos_Lat, $OdoPos_Lon );
856
                $OdoMeter += $Dist;
857
                }
858
            $OdoPos_Lat = $C_Lat;
859
            $OdoPos_Lon = $C_Lon;
860
            $OdoFirst = "1";
861
            }
862
 
863
 
864
        # # Estimate remaining flight time
865
        # my $TextTimeLeft = "";
866
        # if ( &MkIsFlying()  and  $Capacity <= 90 )
867
        #     {
868
        #     my $MaxTime = 100.0 / (100.0 - $Capacity) * $MkFlyingTime;
869
        #     my $TimeLeft = $MaxTime - $MkFlyingTime;
870
        #     $TextTimeLeft = sprintf ("(%d min)", int ($TimeLeft / 60.0 + 0.8) );
871
        #     }
872
        # $map_canvas->itemconfigure ('MK-OSD-Tim-Left', '-text' => $TextTimeLeft );
873
 
874
 
875
        # Footprint
876
        if ( $Cfg->{'map'}->{'FootprintLength'} > 0 )
877
            {
878
            if ( &CurPosIsValid() )
879
                {
880
                # neuen Footprint hinten anhaengen
881
                my ($x, $y) = &MapGps2XY($MkOsd{'CurPos_Lat'}, $MkOsd{'CurPos_Lon'});
882
                push @Footprint, $x, $y;
883
                }
884
 
885
            while ( $#Footprint / 2  >  $Cfg->{'map'}->{'FootprintLength'} )
886
                {
887
                # alte Footprints entfernen
888
                splice @Footprint, 0, 2;
889
                }
890
 
891
            &FootprintRedraw();
892
            }
893
 
894
        # show tracking antenna icon
895
        if ( $MkTrack{'HomePos_Lat'} ne ""  and  $MkTrack{'HomePos_Lon'} ne ""  )
896
            {
897
            # show antenna icon
898
            my ($x, $y) = &MapGps2XY($MkTrack{'HomePos_Lat'}, $MkTrack{'HomePos_Lon'});
899
 
900
            my $IconHeight = 48;
901
            my $IconWidth = 48;
902
            $map_canvas->coords('Track-Antenna', $x - $IconWidth/2, $y - $IconHeight );
903
            }
904
        else
905
            {
906
            # move icon out of sight
907
            $map_canvas->coords('Track-Antenna', 0, -50 );
908
            }
909
 
910
        if ( $GridIsOn )
911
            {
912
            # redraw Grid on canvas
913
            &GridHide();
914
            &GridShow();
915
            }
916
 
917
        # fun :-)
918
        if ( int rand (100) == 43 )
919
            {
920
            &TtsSpeak ('LOW', $Translate{'TtsFun'});
921
            }
922
        }
923
 
924
    });
925
 
926
 
927
#       
928
# Timer: 1s - TTS Sprachausgabe
929
#
930
$frame_map_top->repeat (1000, sub
931
    {
932
    # Aktuell gültige Karte
933
    %Map = %{$Maps{'Current'}};
934
 
935
    lock (%MkOsd);              # until end of block
936
 
937
    my $StatusInterval = $Cfg->{'tts'}->{'StatusInterval'}  || "30";  
938
    $SpeechTimer ++;
939
    my @TtsMsg;
940
 
941
    if ( &MkOsdIsValid() )
942
        {
943
        # Gueltige OSD Daten
944
 
945
        if ( $SpeechTimer % $StatusInterval == 0 )
946
            {
947
            #
948
            # give system status, low prio messages
949
            #
950
 
951
            # clear old low prio messages
952
            &TtsClear('LOW');
953
 
954
            # get messages from configuration
955
            for ( $Message = 1; $Message < 10; $Message ++)
956
                {
957
                my $MsgId = sprintf ("Message%d", $Message);
958
                my $Msg = $Cfg->{'tts'}{$MsgId};
959
 
960
                if ( $Msg =~ /FLIGHT_TIME/i )
961
                    {
962
                    # Flight time
963
                    my $Min = int ( ($MkFlyingTime +1) / 60);
964
                    my $Sec = int ( ($MkFlyingTime +1) % 60);
965
                    my $Text = sprintf ("$Translate{'TtsFlightTimeMinSec'}", $Min, $Sec);
966
                    if ( $Min == 0 )
967
                        {
968
                        $Text = sprintf ("$Translate{'TtsFlightTimeSec'}", $Sec);
969
                        }
970
                   &TtsSpeak ('LOW', "$Text");
971
                   }
972
 
973
                elsif ( $Msg =~ /BATTERY/i )
974
                    {
975
                    # Battery
976
                    my ($Volt, $Tenth) = split '\.', $System{'UBat'};
977
                    &TtsSpeak ('LOW', sprintf ("$Translate{'TtsBattery'}", $Volt, $Tenth));
978
                    }
979
 
980
                elsif ( $Msg =~ /ALTITUDE/i )
981
                    {
982
                    # Altitude
983
                    if ( $System{'Alt'} < 0 )
984
                        {
985
                        &TtsSpeak ('LOW', sprintf ("$Translate{'TtsAltitudeNegative'}", abs($System{'Alt'}) ) );
986
                        }
987
                    else
988
                        {
989
                        &TtsSpeak ('LOW', sprintf ("$Translate{'TtsAltitude'}", $System{'Alt'} ) );
990
                        }
991
                    }
992
 
993
                elsif ( $Msg =~ /SATELLITES/i )
994
                    {
995
                    # Satellites
996
                    &TtsSpeak ('LOW', sprintf ("$Translate{'TtsSatellite'}",  $MkOsd{'SatsInUse'}) );
997
                    }
998
 
999
                elsif ( $Msg =~ /HOME_DIST/i )
1000
                    {
1001
                    # Home Distance
1002
                    &TtsSpeak ('LOW', sprintf ("$Translate{'TtsHomeDist'}",  $System{'HomeDist'}) );
1003
                    }
1004
 
1005
                elsif ( $Msg =~ /TARGET_DIST/i )
1006
                    {
1007
                    # Target Distance
1008
                    &TtsSpeak ('LOW', sprintf ("$Translate{'TtsTargetDist'}",  $System{'TargetDist'}) );
1009
                    }
1010
 
1011
                elsif ( $Msg ne "" )
1012
                    {
1013
                    # text or perl code
1014
                    &TtsSpeak ('LOW', eval "$Msg" );
1015
                    }
1016
                }
1017
            }
1018
 
1019
       # high prio messages
1020
       if ( $SpeechTimer % 5 == 0 )
1021
            {
1022
            if ( $System{'BatWarning'} )          { push @TtsMsg, $Translate{'TtsBatteryWarning'}; }
1023
            if ( $System{'RCQuality'} eq "WEAK" ) { push @TtsMsg, $Translate{'TtsRcWeak'}; }
1024
            if ( $System{'RCQuality'} eq "NO" )   { push @TtsMsg, $Translate{'TtsRcNo'}; }
1025
            if ( $System{'CrossingBorder'} )      { push @TtsMsg, $Translate{'TtsCrossingBorder'}; }
1026
            if ( $System{'OutsideBorder'} )       { push @TtsMsg, $Translate{'TtsOutsideAirfield'}; }
1027
            if ( $System{'RangeWarning'} )        { push @TtsMsg, $Translate{'TtsRange'}; }
1028
            }
1029
        }
1030
    else
1031
        {
1032
        # no data link
1033
        if ( $SpeechTimer % 5 == 0 )
1034
            {
1035
            push @TtsMsg, $Translate{'TtsNoDataLink'};
1036
            }
1037
        }
1038
 
1039
    # speak high prio messages 
1040
    if ( scalar @TtsMsg > 0 )
1041
        {
1042
        # Clear pending messsages
1043
        &TtsClear('HIGH');
1044
 
1045
        # Speak collected messages
1046
        foreach $Msg (@TtsMsg)
1047
            {
1048
            &TtsSpeak ('HIGH', $Msg);
1049
            }
1050
        }
1051
    });
1052
 
1053
 
1054
 
1055
#
1056
#  Event engine
1057
#
1058
 
1059
$frame_map_top->repeat (50, sub     # 50ms
1060
    {
1061
    foreach $Key (keys %{$Event})
1062
        {
1063
        if ( ref $Event->{$Key} eq "" )
1064
            {
1065
            # ignore: CreationDate, Version
1066
            next;
1067
            }
1068
 
1069
        my $Active      = $Event->{$Key}->{'Active'};
1070
        my $Trigger     = $Event->{$Key}->{'Trigger'};       # RISE, FALL, TOGGLE_RISE, TOGGLE_FALL, TRUE, FALSE
1071
        my $Condition   = $Event->{$Key}->{'Condition'};     # Perl commands, Return 1, if Condition is true
1072
        my $Action      = $Event->{$Key}->{'Action'};        # Perl commands
1073
        my $ActionElse  = $Event->{$Key}->{'ActionElse'};    # Perl commands
1074
        my $Delay       = $Event->{$Key}->{'Delay'};         # Action rearm delay in ms
1075
        my $Repeat      = $Event->{$Key}->{'Repeat'};        # Action repeat time in  ms
1076
        my $RepeatElse  = $Event->{$Key}->{'RepeatElse'};    # ActionElse repeat time in ms
1077
 
1078
        my $DoAction = 0;
1079
 
1080
        if ( $Active =~ /y/i  and  $Condition ne "" )
1081
            {
1082
 
1083
            # Provide info about current Event
1084
            %MyEvent = %{$Event->{$Key}};
1085
            $MyEvent{'EventName'} = $Key;
1086
 
1087
            # lock all shared hashes, which might me used in eval-code
1088
            lock (%MkOsd);           # until end of block
1089
            lock (%MkSerialChannel); # until end of block
1090
            lock (%Stick);           # until end of block
1091
            lock (%MkNcDebug);       # until end of block
1092
 
1093
            # compile and execute condition at runtime. Return 1, if Condition is true
1094
            my $CondStat = eval "$Condition";    
1095
            if ( $@ ne "" )
1096
                {
1097
                # print compiler message to STDOUT
1098
                print "Event $Key: Condition: $@";
1099
                }
1100
 
1101
            # current time for Delay, Repeat timing
1102
            my ($t0_s, $t0_us) = gettimeofday;
1103
 
1104
            # for Condition edge detection
1105
            my $LastCondStat = $EventStat{$Key}{'LastCondValue'};
1106
            $EventStat{$Key}{'LastCondValue'} = $CondStat;
1107
 
1108
            # some flags
1109
            my $Rise  = (! $LastCondStat  and    $CondStat);
1110
            my $Fall  = (  $LastCondStat  and  ! $CondStat);
1111
            my $True  =   $CondStat;
1112
            my $False = ! $CondStat;
1113
 
1114
            # Delay time check
1115
            my $DelayCheck = 1;
1116
            my $t1_s  =  $EventStat{$Key}{'DelayTime_s'};
1117
            my $t1_us =  $EventStat{$Key}{'DelayTime_us'};
1118
            if ( $Delay ne "" and  $t1_s ne ""  and  $t1_us ne "" )
1119
                {
1120
                my $Diff_ms = ($t0_s - $t1_s) * 1000 + ($t0_us - $t1_us) / 1000;
1121
                if ( $Diff_ms < $Delay )
1122
                    {
1123
                    $DelayCheck = 0;
1124
                    }
1125
                }
1126
 
1127
            #
1128
            # rising or falling edge
1129
            #
1130
            if ( ($Trigger eq "RISE" and  $Rise)  or
1131
                 ($Trigger eq "FALL" and  $Fall) )
1132
                {
1133
                $DoAction = $DelayCheck;
1134
 
1135
                if ( $DoAction )
1136
                    {
1137
                    $EventStat{$Key}{'DelayTime_s'}  = $t0_s;
1138
                    $EventStat{$Key}{'DelayTime_us'} = $t0_us;
1139
 
1140
                    # reset Repeat time when event gets active
1141
                    $EventStat{$Key}{'RepeatTime_s'}  = 0;
1142
                    $EventStat{$Key}{'RepeatTime_us'} = 0;
1143
 
1144
                    # reset event counter
1145
                    $EventStat{$Key}{'EventCnt'} = 0;
1146
                    }
1147
                }
1148
 
1149
            #
1150
            # toggle on rising/falling edge
1151
            #
1152
            elsif ( $Trigger =~ /TOGGLE/i )
1153
                {
1154
                $DoAction = 0;
1155
                if ( ($Rise  and  $Trigger eq "TOGGLE_RISE")  or
1156
                     ($Fall  and  $Trigger eq "TOGGLE_FALL") )
1157
                    {
1158
                    $DoAction = $DelayCheck;
1159
 
1160
                    if ( $DoAction )
1161
                        {
1162
                        $EventStat{$Key}{'DelayTime_s'}  = $t0_s;
1163
                        $EventStat{$Key}{'DelayTime_us'} = $t0_us;
1164
 
1165
                        # reset Repeat time when event gets active
1166
                        $EventStat{$Key}{'RepeatTime_s'}  = 0;
1167
                        $EventStat{$Key}{'RepeatTime_us'} = 0;
1168
 
1169
                        # reset event counter
1170
                        $EventStat{$Key}{'EventCnt'} = 0;
1171
                        }
1172
                    }
1173
 
1174
                if ( $DoAction )
1175
                    {
1176
                    $EventStat{$Key}{'ToggleOnOff'} ^= 1;
1177
                    }
1178
 
1179
                $DoAction = $EventStat{$Key}{'ToggleOnOff'};
1180
                }
1181
 
1182
 
1183
            #
1184
            # TRUE Condition
1185
            #
1186
            elsif ( $Trigger eq "TRUE" )
1187
                {
1188
                if ( $True )
1189
                    {
1190
                    $DoAction = $DelayCheck;
1191
 
1192
                    if ( $DoAction and $Rise)
1193
                        {
1194
                        # reset Repeat time when event gets active
1195
                        $EventStat{$Key}{'RepeatTime_s'}  = 0;
1196
                        $EventStat{$Key}{'RepeatTime_us'} = 0;
1197
 
1198
                        # reset event counter
1199
                        $EventStat{$Key}{'EventCnt'} = 0;
1200
                        }
1201
                    }
1202
 
1203
                if ( $Fall )
1204
                    {
1205
                    # set Delay reference from falling edge
1206
 
1207
                    if ( $DelayCheck )
1208
                        {
1209
                        # timestamp of last falling Condition edge change
1210
                        $EventStat{$Key}{'DelayTime_s'}  = $t0_s;
1211
                        $EventStat{$Key}{'DelayTime_us'} = $t0_us;
1212
 
1213
                        # reset event counter
1214
                        $EventStat{$Key}{'EventCnt'} = 0;
1215
                        }
1216
                    }
1217
                }
1218
 
1219
            #
1220
            # FALSE Condition
1221
            #
1222
            elsif ( $Trigger eq "FALSE" )
1223
                {
1224
                if ( $False )
1225
                    {
1226
                    $DoAction = $DelayCheck;
1227
 
1228
                    if ( $DoAction and $Fall)
1229
                        {
1230
                        # reset Repeat time when event gets active
1231
                        $EventStat{$Key}{'RepeatTime_s'}  = 0;
1232
                        $EventStat{$Key}{'RepeatTime_us'} = 0;
1233
 
1234
                        # reset event counter
1235
                        $EventStat{$Key}{'EventCnt'} = 0;
1236
                        }
1237
                    }
1238
 
1239
                if ( $Rise )
1240
                    {
1241
                    # set Delay reference from rising edge
1242
 
1243
                    if ( $DelayCheck )
1244
                        {
1245
                        # timestamp of last rising Condition edge change
1246
                        $EventStat{$Key}{'DelayTime_s'}  = $t0_s;
1247
                        $EventStat{$Key}{'DelayTime_us'} = $t0_us;
1248
 
1249
                        # reset event counter
1250
                        $EventStat{$Key}{'EventCnt'} = 0;
1251
                        }
1252
                    }
1253
                }
1254
 
1255
            else
1256
                {
1257
                # unkown, fall through
1258
                }
1259
 
1260
 
1261
            # undefined Status, neither ON, nor OFF
1262
            $EventStat{$Key}{'Status'} = "";
1263
 
1264
            #
1265
            # Process Action
1266
            #
1267
            if ( $DoAction  and  $Action ne "" )
1268
                {
1269
                $EventStat{$Key}{'Status'} = "ON";
1270
                my $Execute = 1;
1271
 
1272
                my $t1_s  =  $EventStat{$Key}{'RepeatTime_s'};
1273
                my $t1_us =  $EventStat{$Key}{'RepeatTime_us'};
1274
                if ( $Repeat ne "" and  $t1_s ne ""  and  $t1_us ne "" )
1275
                    {
1276
                    my $Diff_ms = ($t0_s - $t1_s) * 1000 + ($t0_us - $t1_us) / 1000;
1277
                    if ( $Diff_ms < $Repeat )
1278
                        {
1279
                        $Execute = 0;
1280
                        }
1281
                    }
1282
 
1283
                if ( $Execute )
1284
                    {
1285
                    # Accounting
1286
                    $MyEvent{'EventCnt'} = $EventStat{$Key}{'EventCnt'} ++;
1287
 
1288
                    # compile and execute action at runtime
1289
                    eval "$Action";
1290
                    if ( $@ ne "" )
1291
                        {
1292
                        # print compiler message to STDOUT
1293
                        print "Event $Key: Action: $@";
1294
                        }
1295
 
1296
                    # Timestamp, when Action was executed
1297
                    $EventStat{$Key}{'RepeatTime_s'}  = $t0_s;
1298
                    $EventStat{$Key}{'RepeatTime_us'} = $t0_us;
1299
 
1300
                    # reset ActionElse repeat timing
1301
                    $EventStat{$Key}{'RepeatTimeElse_s'}  = 0;
1302
                    $EventStat{$Key}{'RepeatTimeElse_us'} = 0;
1303
                    }
1304
                }
1305
 
1306
            #
1307
            # Process ActionElse
1308
            #
1309
            if (  ! $DoAction  and  $ActionElse ne "" )
1310
                {
1311
                $EventStat{$Key}{'Status'} = "OFF";
1312
                my $Execute = 1;
1313
 
1314
                # check repeat time
1315
                my $t1_s  =  $EventStat{$Key}{'RepeatTimeElse_s'};
1316
                my $t1_us =  $EventStat{$Key}{'RepeatTimeElse_us'};
1317
 
1318
                if ( $RepeatElse ne "" and  $t1_s ne ""  and  $t1_us ne "" )
1319
                    {
1320
                    my $Diff_ms = ($t0_s - $t1_s) * 1000 + ($t0_us - $t1_us) / 1000;
1321
                    if ( $Diff_ms < $RepeatElse )
1322
                        {
1323
                        $Execute = 0;
1324
                        }
1325
                    }
1326
 
1327
                if ( $Execute )
1328
                    {
1329
                    # Accounting
1330
                    $MyEvent{'EventCnt'} = $EventStat{$Key}{'EventCnt'} ++;
1331
 
1332
                    # compile and execute action at runtime
1333
                    eval "$ActionElse";
1334
                    if ( $@ ne "" )
1335
                        {
1336
                        # print compiler message to STDOUT
1337
                        print "Event $Key: ActionElse: $@";
1338
                        }
1339
 
1340
                    # Timestamp, when ActionElse was executed
1341
                    $EventStat{$Key}{'RepeatTimeElse_s'}  = $t0_s;
1342
                    $EventStat{$Key}{'RepeatTimeElse_us'} = $t0_us;
1343
                    }
1344
                }
1345
            }
1346
 
1347
        else
1348
            {
1349
            $EventStat{$Key}{'Status'} = "DISABLED";
1350
            }
1351
        }
1352
    });
1353
 
1354
 
1355
#
1356
# Send Serial Channel + stick button/analog handling
1357
#
1358
 
1359
my $SerialChannelTiming = $Cfg->{'serialchannel'}->{'SerialChannelFrquency'} || 20;
1360
$SerialChannelTiming = int (1 / $SerialChannelTiming * 1000);
1361
if ( $SerialChannelTiming < 10 )
1362
    {
1363
    $SerialChannelTiming = 10;
1364
    }
1365
 
1366
# Timer: variable timing
1367
$frame_map_top->repeat ($SerialChannelTiming, sub
1368
    {
1369
    # 12 serial Channel
1370
    for ( my $ChannelInd = 0; $ChannelInd < 12; $ChannelInd ++)
1371
        {
1372
        my $Channel = sprintf ("SerialChannel%02d", $ChannelInd + 1);  # key for Cfg-hash
1373
 
1374
        my $Control = $Cfg->{'serialchannel'}->{$Channel};
1375
        if ( $Control ne "" )
1376
            {
1377
            my $Expo    = $Cfg->{'serialchannel'}->{$Channel . "Expo"};
1378
            my $Limit   = $Cfg->{'serialchannel'}->{$Channel . "Limit"};
1379
            my $ChannelVal = &ParseControls($Channel, $Control, $Expo, $Limit, $SerialChannelTiming);
1380
 
1381
            &SerialChannel($ChannelInd, $ChannelVal);
1382
            }
1383
        }
1384
 
1385
    $MkSerialChannel{'SerialChannelSend'}   = $Cfg->{'serialchannel'}->{'SerialChannelSend'};
1386
    $MkSerialChannel{'SerialChannelTiming'} = $SerialChannelTiming;
1387
 
1388
    # send serial Channel to MK, nicht wenn deaktiviert  oder WP uebertragen werden
1389
    if ( ! $MkSendWp  and  $Cfg->{'serialchannel'}->{'SerialChannelSend'} =~ /y/i  and  $TxExtOn == 1)
1390
        {
1391
        &SendSerialChannel();  # send values in %MkSerialChannel
1392
        }
1393
    });
1394
 
1395
 
1396
#
1397
# Extern Control
1398
#
1399
 
1400
my $ExternControlTiming = $Cfg->{'externcontrol'}->{'ExternControlFrquency'} || 20;
1401
$ExternControlTiming = int (1 / $ExternControlTiming * 1000);
1402
if ( $ExternControlTiming < 10 )
1403
    {
1404
    $ExternControlTiming = 10;
1405
    }
1406
 
1407
# Timer: variable timing
1408
$frame_map_top->repeat ($ExternControlTiming, sub
1409
    {
1410
    foreach my $Channel ( qw(ExternControlRoll ExternControlNick ExternControlGier ExternControlGas ExternControlHeight) )
1411
        {
1412
        my $Control = $Cfg->{'externcontrol'}->{$Channel};
1413
        if ( $Control ne "" )
1414
            {
1415
            my $Expo    = $Cfg->{'externcontrol'}->{$Channel . "Expo"};
1416
            my $Limit   = $Cfg->{'externcontrol'}->{$Channel . "Limit"};
1417
            $MkExternControl{$Channel} = &ParseControls($Channel, $Control, $Expo, $Limit, $ExternControlTiming);
1418
            }
1419
        }
1420
 
1421
    $MkExternControl{'ExternControlSend'}   = $Cfg->{'externcontrol'}->{'ExternControlSend'};
1422
    $MkExternControl{'ExternControlTimimg'} = $ExternControlTiming;
1423
 
1424
    # send extern control to MK, nicht wenn deaktiviert oder WP uebertragen werden
1425
    if ( ! $MkSendWp  and  $Cfg->{'externcontrol'}->{'ExternControlSend'} =~ /y/i  and  $TxExtOn == 1 )
1426
        {
1427
        &SendExternalControl ( '-nick'   => $MkExternControl{'ExternControlNick'},
1428
                               '-roll'   => $MkExternControl{'ExternControlRoll'},
1429
                               '-gier'   => $MkExternControl{'ExternControlGier'},
1430
                               '-gas'    => $MkExternControl{'ExternControlGas'},
1431
                               '-height' => $MkExternControl{'ExternControlHeight'},
1432
                               '-remotebuttons' => 0,
1433
                               '-config' => 1,
1434
                               '-frame'  => 1,
1435
                             );
1436
 
1437
        $MkExternControl{'_Timestamp'} = time;   # time when command was sent
1438
        }
1439
    });
1440
 
1441
 
1442
#
1443
# mouse/joystick crosshair move in player pause mode
1444
#
1445
 
1446
$frame_map_top->repeat (50, sub     # 50ms
1447
    {
1448
    if ( $PlayerMode eq 'Pause'  and
1449
         $PlayerPause_Lat ne ""  and  $PlayerPause_Lon ne "" )
1450
        {
1451
        lock (%MkOsd);           # until end of block
1452
 
1453
        my $ControlX = $Cfg->{'map'}->{'CrosshairMoveX'};
1454
        my $ControlY = $Cfg->{'map'}->{'CrosshairMoveY'};
1455
 
1456
        if ( $ControlX ne ""  and $ControlY ne "" )
1457
            {
1458
            my $StickRange = 250;
1459
            my $Bias = $StickRange /2;
1460
 
1461
            my $ExpoX    = $Cfg->{'map'}->{'CrosshairMoveXExpo'};
1462
            my $LimitX   = $Cfg->{'map'}->{'CrosshairMoveXLimit'};
1463
            my $SpeedX  = &ParseControls('CrosshairMoveX', $ControlX, $ExpoX, $LimitX, 50);
1464
 
1465
            my $ExpoY    = $Cfg->{'map'}->{'CrosshairMoveYExpo'};
1466
            my $LimitY   = $Cfg->{'map'}->{'CrosshairMoveYLimit'};
1467
            my $SpeedY  = &ParseControls('CrosshairMoveY', $ControlY, $ExpoY, $LimitY, 50);
1468
 
1469
            my $BearingTop = &MapAngel() - 90.0;
1470
            my $BearingMouse = rad2deg atan2($SpeedX, $SpeedY);
1471
            my $Bearing = $BearingTop + $BearingMouse;
1472
 
1473
            if ( $PlayerPauseMode eq "MK" )
1474
                {
1475
                # MK Reference
1476
                $Bearing = $MkOsd{'CompassHeading'} + $BearingMouse;
1477
                }
1478
 
1479
            # max. 30m/s at 50ms frame time
1480
            my $Speed = sqrt ($SpeedX * $SpeedX + $SpeedY * $SpeedY);
1481
            my $Dist = 40*50/1000 * $Speed / $Bias;
1482
            ($PlayerPause_Lat, $PlayerPause_Lon) = &MapGpsAt($PlayerPause_Lat, $PlayerPause_Lon, $Dist, $Bearing);
1483
 
1484
            if ( $SpeedX != 0  or  $SpeedY != 0 )
1485
                {
1486
                $CrosshairTimerCnt = 0;
1487
                }
1488
            }
1489
 
1490
        #
1491
        # show/update Crosshair for 5 sec after move
1492
        #
1493
 
1494
        if ( $CrosshairTimerCnt < 5 * 1000/50)
1495
           {
1496
           &CrosshairShow ($PlayerPause_Lat, $PlayerPause_Lon);
1497
           }
1498
        elsif ( $CrosshairTimerCnt ==  5 * 1000/50)
1499
           {
1500
           &CrosshairHide();
1501
           }
1502
 
1503
        $CrosshairTimerCnt ++;
1504
        }
1505
 
1506
    });
1507
 
1508
 
1509
__END__