Subversion Repositories Projects

Rev

Details | Last modification | View Log | RSS feed

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