Subversion Repositories Projects

Rev

Details | Last modification | View Log | RSS feed

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