Subversion Repositories Projects

Rev

Details | Last modification | View Log | RSS feed

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