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
# mkcomm.pl -  MK Communication Routines
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-02-21 0.0.1 rw created
41
# 2009-03-18 0.0.2 rw NC 0.14e
42
# 2009-04-01 0.1.0 rw RC1
43
# 2009-04-06 0.1.1 rw NC 0.15c
44
# 2009-05-16 0.1.2 rw External control
45
# 2009-08-15 0.1.3 rw SIG-Handler removed
46
# 2009-10-05 0.1.4 rw COM-Ports > 9 + PortSetSkip configuration
47
#                     Export Target-Hash to Simulator
48
# 2009-10-24 0.3.0 rw NC 0.17b
49
# 2010-02-10 0.4.0 rw Port read timout reduced from 100ms to 10ms
50
#                     Serial Channel
51
#                     Extern Control
52
#                     Controls via joystick or 3D-Mouse
53
#                     Navidata Current, Capacity
54
# 2010-02-18 0.4.1 rw LCD Stick/Poti
55
# 2010-03-07 0.4.2 rw MKFLAG: LOWBAT
56
# 2010-07-01 0.5.0 rw WP/POI adjustments for NC 0.19/0.20
57
#                     Hardware Error Codes
58
# 2010-10-16 0.5.4 rw Spline speed controlled mode (SPD)
59
# 2010-11-03 0.6.0 rw NC 0.21, DGPS
60
#
61
###############################################################################
62
 
63
$Version{'mkcomm.pl'} = "0.6.0 - 2010-11-03";
64
 
65
# MK Protokoll
66
#   http://www.mikrokopter.de/ucwiki/en/SerialCommands?highlight=(command)
67
#   http://www.mikrokopter.de/ucwiki/en/SerialProtocol?highlight=(protocol)
68
 
69
#
70
# Parameter
71
#
72
 
73
# Com Port of MK Comm-Device (BT, WI.232)
74
if ( ! defined $Cfg->{'mkcomm'}->{'Port'} )
75
    {
76
    # set default
77
    $Cfg->{'mkcomm'}->{'Port'} = "COM5";
78
    }
79
 
80
$AddrFC     = "b";
81
$AddrNC     = "c";
82
$AddrMK3MAG = "d";
83
 
84
 
85
if ( $0 =~ /mkcomm.pl$/i )
86
    {
87
    # Program wurde direkt aufgerufen
88
 
89
    # change working directory to program path
90
    my $Cwd = substr ($0, 0, rindex ($0, "mkcomm.pl"));
91
    chdir $Cwd;
92
 
93
    # set path for local Perl libs
94
    push @INC, $Cwd . "perl/lib";
95
    }
96
 
97
 
98
# Packages
99
use threads;                # http://search.cpan.org/~jdhedden/threads-1.72/threads.pm
100
                            # http://perldoc.perl.org/threads.html
101
use threads::shared;        # http://search.cpan.org/~jdhedden/threads-shared-1.28/shared.pm
102
use Thread::Queue;          # http://search.cpan.org/dist/Thread-Queue-2.11/lib/Thread/Queue.pm
103
use Time::HiRes qw(usleep); # http://search.cpan.org/~jhi/Time-HiRes-1.9719/HiRes.pm
104
if ( $^O =~ /Win32/i )
105
    {
106
    require Win32::SerialPort;  # http://search.cpan.org/dist/Win32-SerialPort
107
    }
108
else
109
    {
110
    require Device::SerialPort; # http://search.cpan.org/~cook/Device-SerialPort-1.04/SerialPort.pm
111
    }
112
 
113
require "libmap.pl";
114
 
115
# Hashes exported to other threads and main-program
116
share (%MkOsd);
117
share (%MkTarget);
118
share (%MkNcDebug);
119
share (%Mk);
120
share (%MkSSim);     # Target info for simulator
121
share (%MkSerialChannel);
122
 
123
 
124
$Mk{'_RxCrcError'} = 0;    # statistics
125
 
126
# Queue for Sending to MK
127
$MkSendQueue = Thread::Queue->new();
128
 
129
sub MkCommExit()
130
    {  
131
    # close COM port
132
    &MkClose();
133
 
134
    if ( defined threads->self() )
135
        {
136
        threads->exit();
137
        }
138
    exit;
139
    }
140
 
141
 
142
sub MkInit()
143
    {
144
    if ( defined $MkPort )
145
        {
146
        return;   # already open
147
        }
148
 
149
    # open COM-Port             
150
     my $MkComPort = $Cfg->{'mkcomm'}->{'Port'};
151
     if ( $MkComPort =~ /^COM/i )
152
         {
153
         $MkComPort = "\\\\.\\" . $MkComPort;   # \\.\COMnn for nn > 9
154
         }
155
    undef $MkPort;
156
    if ( $^O =~ m/Win32/ )
157
        {
158
        $MkPort = Win32::SerialPort->new ($MkComPort) || die "Error open $MkComPort\n";
159
        }
160
    else
161
        {
162
        $MkPort = Device::SerialPort->new ($MkComPort) || die "Error open $MkComPort\n";
163
        }
164
 
165
    if ( ! ($Cfg->{'mkcomm'}->{'PortSetSkip'} =~ /y/i) )
166
        {
167
        # Set COM parameters, don't set for Bluetooth device
168
        $MkPort->baudrate(57600);
169
        $MkPort->parity("none");
170
        $MkPort->databits(8);
171
        $MkPort->stopbits(1);
172
        $MkPort->handshake('none');
173
        $MkPort->write_settings;
174
        }
175
 
176
    $MkPort->read_const_time(10); # total = (avg * bytes) + const (ms)
177
    }
178
 
179
# Read one line from MK
180
# Check send-queue
181
sub MkIOLine()
182
    {
183
    # Init serial port
184
    &MkInit();
185
 
186
    my $RxLine = "";           
187
    while ( 1 )
188
        {      
189
        # Check Send-Queue
190
        my $Items = $MkSendQueue->pending();
191
        if ( $Items >= 3 )  # Cmd, Addr, Data
192
            {
193
            my ($Id, $Addr, $Data) = $MkSendQueue->dequeue(3);
194
            &MkSend ($Id, $Addr, $Data);       
195
            }
196
 
197
        # Zeichenweise lesen, blockierend mit Timeout
198
        my ($RxLen, $RxChar) = $MkPort->read(1);  
199
        if ( $RxLen == 1 )
200
            {
201
            $Mk{'_BytesRx'} ++;    # Statistics
202
 
203
            if ( "$RxChar" eq "#" )       # 1st char of line
204
                {
205
                $RxLine = "#";
206
                }
207
            elsif ( "$RxChar" eq "\r" )   # last char of line
208
                {
209
                return ($RxLine);
210
                }
211
            else
212
                {
213
                $RxLine = "$RxLine" . "$RxChar";    # collect char
214
                }
215
            }
216
        }
217
    }
218
 
219
 
220
# Read and decode a command from MK
221
# process send queue in &MkIOLine()
222
sub MkIO()
223
    {  
224
    my $RxData = &MkIOLine();     # Blocking Read for complete line
225
 
226
    # Zeile decodieren
227
    if ( substr ($RxData, 0, 1) eq '#' )
228
        {                      
229
        # Zeile decodieren
230
        $Header = substr($RxData, 0, 3);
231
        $Chksum = substr($RxData, -2);
232
        $Data = substr($RxData, 3, length ($RxData) -5);
233
 
234
# print "$Header\n";
235
 
236
        # CRC prüfen
237
        if ( &CrcCheck ("$Header" . "$Data", $Chksum ) )
238
            {
239
            # Base64 decodieren
240
            $Data = &Decode64($Data);
241
 
242
            # Daten auswerten und in shared Hash schreiben
243
            if ( &ProcessRx($Header, $Data) )
244
                {
245
                return 1;    # alles OK
246
                }
247
            }
248
        else
249
            {
250
            $Mk{'_RxCrcError'} ++;    # Statistics
251
            }
252
        }
253
 
254
    return 0;  # keine Daten empfangen
255
    }
256
 
257
 
258
# Send a command to MK
259
sub MkSend()
260
    {
261
    my ($Id, $Addr, $Data) = @_;
262
 
263
    # Init serial port
264
    &MkInit();
265
 
266
    my $Base64Data = &Encode64($Data);
267
 
268
    my $TxData = "#" . "$Addr" . "$Id" . "$Base64Data";
269
    my $Crc = &Crc($TxData);
270
    my $TxSend  = "$TxData" . "$Crc" . "\r";
271
 
272
    $Mk{'_BytesTx'} += length $TxSend;     # Statistics
273
 
274
    $MkPort->write($TxSend);
275
    }
276
 
277
 
278
# close COM-Port
279
sub MkClose()
280
    {
281
    undef $MkPort;
282
    }
283
 
284
 
285
# CRC Prüfung
286
sub CrcCheck ()
287
    {
288
    my ($Data, $Crc) = @_;
289
 
290
    my $Check = &Crc($Data);
291
    if ( $Check ne $Crc )
292
        {
293
        return 0;   # CRC passt nicht
294
        }          
295
    return (1);    # CRC OK
296
    }
297
 
298
 
299
# CRC berechnen
300
sub Crc ()
301
    {
302
    my ($Data) = @_;
303
    my $TmpCrc = 0;
304
    my $Len = length $Data;
305
 
306
    for ($i=0; $i<$Len; $i++)
307
        {
308
        $TmpCrc += ord(substr($Data, $i, 1));
309
        }
310
 
311
    $TmpCrc %= 4096;
312
    my $Crc1 = ord ("=") + $TmpCrc / 64;
313
    my $Crc2 = ord ("=") + $TmpCrc % 64;
314
    $Crc = pack("CC", $Crc1, $Crc2);
315
 
316
    return ($Crc);
317
    }
318
 
319
 
320
# Empfangene Daten decodieren, modifiziertes Base64
321
sub Decode64()
322
    {
323
    my ($DataIn) = @_;
324
 
325
    my $ptrIn  = 0;
326
    my $DataOut  = "";
327
    my $len = length ($DataIn);
328
 
329
    while ( $len > 0 )
330
        {
331
        $a = ord (substr ($DataIn, $ptrIn ++, 1)) - ord ("=");
332
        $b = ord (substr ($DataIn, $ptrIn ++, 1)) - ord ("=");
333
        $c = ord (substr ($DataIn, $ptrIn ++, 1)) - ord ("=");
334
        $d = ord (substr ($DataIn, $ptrIn ++, 1)) - ord ("=");
335
 
336
        $x = ($a << 2) | ($b >> 4);
337
        $y = (($b & 0x0f) << 4) | ($c >> 2);
338
        $z = (($c & 0x03) << 6) | $d;
339
 
340
        foreach $i ( $x, $y, $z )
341
            {
342
            if ( $len--)
343
                {
344
                my $Tmp = pack ('C1', $i);
345
                $DataOut = "$DataOut" . "$Tmp";
346
                }
347
            else
348
                {
349
                last;
350
                }
351
            }
352
        }
353
 
354
    return ($DataOut);
355
    }
356
 
357
 
358
# zu sendende Daten codieren, modifiziertes Base64
359
sub Encode64()
360
    {
361
    my ($Data) = @_;
362
 
363
    my $Length = length $Data;
364
    my $TxBuf = "";
365
    my $ptr = 0;
366
 
367
    while( $Length > 0 )
368
        {
369
        my $a = 0;
370
        my $b = 0;
371
        my $c = 0;
372
        if ($Length) {$a = ord(substr ($Data, $ptr++, $Length--));}
373
        if ($Length) {$b = ord(substr ($Data, $ptr++, $Length--));}
374
        if ($Length) {$c = ord(substr ($Data, $ptr++, $Length--));}
375
 
376
        my $ac = ord("=") +    ($a >> 2);
377
        my $bc = ord("=") + ( (($a & 0x03) << 4) | (($b & 0xf0) >> 4) );
378
        my $cc = ord("=") + ( (($b & 0x0f) << 2) | (($c & 0xc0) >> 6) );
379
        my $dc = ord("=") +    ($c & 0x3f);
380
        $TxBuf = "$TxBuf" . pack ("C4", $ac, $bc, $cc, $dc);
381
        }
382
    return ($TxBuf);
383
    }
384
 
385
 
386
# Empfangenen Datensatz verarbeiten
387
sub ProcessRx()
388
    {
389
    my ($Header, $Data) = @_;
390
 
391
    my $Adr = substr ($Header, 1, 1);    # b=FC, c=NC, d=MK3MAG
392
    my $Id  = substr ($Header, 2, 1);
393
 
394
    if ( $Id eq "O" )
395
        {
396
        #
397
        # OSD-Daten nach %MkOsd einlesen
398
        #
399
 
400
        # Struktur Datensatz:
401
        #     u8  Version                                     // version of the data structure
402
        #     GPS_Pos_t CurrentPosition;
403
        #     GPS_Pos_t TargetPosition;
404
        #     GPS_PosDev_t TargetPositionDeviation;
405
        #     GPS_Pos_t HomePosition;
406
        #     GPS_PosDev_t HomePositionDeviation;
407
        #     u8  WaypointIndex;                              // index of current waypoints running from 0 to WaypointNumber-1
408
        #     u8  WaypointNumber;                             // number of stored waypoints
409
        #     u8  SatsInUse;                                  // no of satellites used for position solution
410
        #     s16 Altimeter;                                  // hight according to air pressure
411
        #     s16 Variometer;                                 // climb(+) and sink(-) rate
412
        #     u16 FlyingTime;                                 // in seconds
413
        #     u8  UBat;                                       // Battery Voltage in 0.1 Volts
414
        #     u16 GroundSpeed;                                // speed over ground in cm/s (2D)
415
        #     s16 Heading;                                    // current flight direction in deg as angle to north
416
        #     s16 CompassHeading;                             // current compass value
417
        #     s8  AngleNick;                                  // current Nick angle in 1°
418
        #     s8  AngleRoll;                                  // current Rick angle in 1°
419
        #     u8  RC_Quality;                                 // RC_Quality
420
        #     u8  MKFlags;                                    // Flags from FC
421
        #     u8  NCFlags;                                    // Flags from NC
422
        #     u8  Errorcode;                                  // 0 --> okay
423
        #     u8  OperatingRadius                             // current operation radius around the Home Position in m
424
        #     s16 TopSpeed;                                   // velocity in vertical direction in cm/s
425
        #     u8  TargetHoldTime;                             // time in s to stay at the given target, counts down to 0 if target has been reached
426
        #     u8  RC_RSSI;                                    // Receiver signal strength (since version 2 added)
427
        #     s16 SetpointAltitude;                           // setpoint for altitude
428
        #     u8  Gas;                                        // for future use
429
        #     u16 Current;                                    // actual current in 0.1A steps
430
        #     u16 UsedCapacity;                               // used capacity in mAh
431
 
432
 
433
        # GPS_Pos_t:
434
        #     s32 Longitude;   // in 1E-7 deg
435
        #     s32 Latitude;    // in 1E-7 deg
436
        #     s32 Altitude;    // in mm
437
        #     u8  Status;      // validity of data
438
 
439
        # GPS_PosDev_t:
440
        #     s16 Distance;                                   // distance to target in dm
441
        #     s16 Bearing;                                    // course to target in deg      
442
 
443
        # Status:
444
        #     INVALID         = 0
445
        #     NEWDATA         = 1
446
        #     PROCESSED       = 2
447
 
448
        # MKFlags  0x01: MOTOR_RUN, 0x02 FLY, 0x04: CALIBRATE, 0x08: START, 0x10: EMERGENCY_LANDING
449
        #          0x20: LOWBAT, 0x40: VARIO_TRIM_UP, 0x40: VARIO_TRIM_DOWN
450
        # NCFlags  0x01: FLAG_FREE, 0x02: FLAG_PH, 0x04: FLAG_CH, 0x08: FLAG_RANGE_LIMIT
451
        #          0x10: FLAG_NOSERIALLINK, 0x20: FLAG_TARGET_REACHED, FLAG_MANUAL_CONTROL: 0x40
452
        #          0x80: FLAG_GPS_OK
453
 
454
        lock (%MkOsd);  # until end of Block
455
 
456
        (
457
        $MkOsd{'Version'},
458
        $MkOsd{'CurPos_Lon'},
459
        $MkOsd{'CurPos_Lat'},
460
        $MkOsd{'CurPos_Alt'},
461
        $MkOsd{'CurPos_Stat'},
462
        $MkOsd{'TargetPos_Lon'},
463
        $MkOsd{'TargetPos_Lat'},
464
        $MkOsd{'TargetPos_Alt'},
465
        $MkOsd{'TargetPos_Stat'},
466
        $MkOsd{'TargetPosDev_Dist'},
467
        $MkOsd{'TargetPosDev_Bearing'},
468
        $MkOsd{'HomePos_Lon'},
469
        $MkOsd{'HomePos_Lat'},
470
        $MkOsd{'HomePos_Alt'},
471
        $MkOsd{'HomePos_Stat'},
472
        $MkOsd{'HomePosDev_Dist'},
473
        $MkOsd{'HomePosDev_Bearing'},
474
        $MkOsd{'WaypointIndex'},
475
        $MkOsd{'WaypointNumber'},
476
        $MkOsd{'SatsInUse'},
477
        $MkOsd{'Altimeter'},
478
        $MkOsd{'Variometer'},
479
        $MkOsd{'FlyingTime'},
480
        $MkOsd{'UBat'},
481
        $MkOsd{'GroundSpeed'},
482
        $MkOsd{'Heading'},
483
        $MkOsd{'CompassHeading'},
484
        $MkOsd{'AngleNick'},
485
        $MkOsd{'AngleRoll'},
486
        $MkOsd{'RC_Quality'},
487
        $MkOsd{'MKFlags'},
488
        $MkOsd{'NCFlags'},
489
        $MkOsd{'Errorcode'},
490
        $MkOsd{'OperatingRadius'},
491
        $MkOsd{'TopSpeed'},
492
        $MkOsd{'TargetHoldTime'},
493
        $MkOsd{'RC_RSSI'},
494
        $MkOsd{'SetPointAltitude'},
495
        $MkOsd{'Gas'},
496
        $MkOsd{'Current'},
497
        $MkOsd{'UsedCapacity'},
498
        ) = unpack ('ClllClllCsslllCssCCCssSCSssccCCCCCsCCsCSS', $Data);
499
 
500
        $MkOsd{'CurPos_Lon'}     = sprintf("%.7f", $MkOsd{'CurPos_Lon'} / 10000000);
501
        $MkOsd{'CurPos_Lat'}     = sprintf("%.7f", $MkOsd{'CurPos_Lat'} / 10000000);
502
        $MkOsd{'CurPos_Alt'}     = sprintf("%.3f", $MkOsd{'CurPos_Alt'} / 1000);
503
        $MkOsd{'TargetPos_Lon'}  = sprintf("%.7f", $MkOsd{'TargetPos_Lon'} / 10000000);
504
        $MkOsd{'TargetPos_Lat'}  = sprintf("%.7f", $MkOsd{'TargetPos_Lat'} / 10000000);
505
        $MkOsd{'TargetPos_Alt'}  = sprintf("%.3f", $MkOsd{'TargetPos_Alt'} / 1000);
506
        $MkOsd{'HomePos_Lon'}    = sprintf("%.7f", $MkOsd{'HomePos_Lon'} / 10000000);
507
        $MkOsd{'HomePos_Lat'}    = sprintf("%.7f", $MkOsd{'HomePos_Lat'} / 10000000);
508
        $MkOsd{'HomePos_Alt'}    = sprintf("%.3f", $MkOsd{'HomePos_Alt'} / 1000);
509
        $MkOsd{'UBat'}           = sprintf("%.1f", $MkOsd{'UBat'} / 10);
510
        $MkOsd{'Current'}        = sprintf("%.1f", $MkOsd{'Current'} / 10);
511
 
512
        # Timestamp, wann der Datensatz geschtieben wurde
513
        $MkOsd{'_Timestamp'} = time;
514
        $MkOsd{'_FrameCount'} ++;
515
        }
516
 
517
    elsif ( $Id eq "s" )
518
        {
519
        #
520
        # NC Target position in %MkTarget
521
        #
522
        # Datenstruktur:
523
        #    GPS_Pos_t Position;     // the gps position of the waypoint, see ubx.h for details
524
        #    s16 Heading;            // orientation, future implementation
525
        #    u8  ToleranceRadius;    // in meters, if the MK is within that range around the target, then the next target is 
526
        #    u8  HoldTime;           // in seconds, if the MK was once in the tolerance area around a WP,
527
        #                            // this time defines the delay before the next WP is triggered
528
        #    u8  Event_Flag;         // future emplementation
529
        #    u8  reserve[12];        // reserved
530
 
531
        lock (%MkTarget);  # until end of block
532
 
533
        (
534
        $MkTarget{'Pos_Lon'},
535
        $MkTarget{'Pos_Lat'},
536
        $MkTarget{'Pos_Alt'},
537
        $MkTarget{'Pos_Stat'},
538
        $MkTarget{'Heading'},
539
        $MkTarget{'ToleranceRadius'},
540
        $MkTarget{'HoldTime'},
541
        $MkTarget{'EventFlag'},
542
        ) = unpack ('lllCsCCC', $Data);
543
 
544
        $MkTarget{'Pos_Lon'} = sprintf("%.7f", $MkTarget{'Pos_Lon'} / 10000000);
545
        $MkTarget{'Pos_Lat'} = sprintf("%.7f", $MkTarget{'Pos_Lat'} / 10000000);
546
        $MkTarget{'Pos_Alt'} = sprintf("%.3f", $MkTarget{'Pos_Alt'} / 1000);
547
 
548
        # Timestamp, wann der Datensatz geschrieben wurde
549
        $MkTarget{'_Timestamp'} = time;
550
        $MkTarget{'_FrameCount'} ++;
551
        }
552
 
553
    elsif ( $Id eq "W" )
554
        {
555
        #
556
        # Request new waypoint
557
        #
558
        # Datenstruktur:
559
        #    u8 Number of waypoint
560
 
561
        ($WpNumber) = unpack ('C', $Data);
562
 
563
        # keine Ahnung wofuer das gut sein soll
564
 
565
        # print "Request new Waypoint Number: $WpNumber\n";
566
 
567
        }
568
 
569
    elsif ( $Id eq "V" )
570
        {
571
        #
572
        # Version
573
        #
574
        # Datenstruktur:
575
        #    u8 SWMajor
576
        #    u8 SWMinor
577
        #    u8 ProtoMajor
578
        #    u8 ProtoMinor
579
        #    u8 SWPatch
580
        #    u8 HardwareError[5]
581
 
582
        (
583
        $Mk{'SWMajor'},
584
        $Mk{'SWMinor'},
585
        $Mk{'ProtoMajor'},
586
        $Mk{'ProtoMinor'},
587
        $Mk{'SWPatch'},
588
        $Mk{'HardwareError1'},
589
        $Mk{'HardwareError2'},
590
        $Mk{'HardwareError3'},
591
        $Mk{'HardwareError4'},
592
        $Mk{'HardwareError5'},
593
        ) = unpack ('C10', $Data);
594
 
595
        $Mk{'_Timestamp'} = time;
596
        $Mk{'_FrameCount'} ++;
597
        }
598
 
599
    elsif ( $Id eq "E" )
600
        {
601
        #
602
        # Error Text
603
        #
604
        # Datenstruktur:
605
        #    s8 ErrorMsg[25]
606
 
607
        $Mk{'ErrorMsg'} = unpack ('Z25', $Data);
608
        }
609
 
610
    elsif ( $Id eq "D" )
611
        {
612
        #
613
        # NC Debug %MkNcDebug
614
        #
615
        # Datenstruktur:
616
        #    u8 Digital[2];
617
        #    u16 Analog[32];
618
 
619
        lock (%MkNcDebug);    # until end of block
620
 
621
        (
622
        $MkNcDebug{'Digital_00'},
623
        $MkNcDebug{'Digital_01'},
624
        $MkNcDebug{'Analog_00'},
625
        $MkNcDebug{'Analog_01'},
626
        $MkNcDebug{'Analog_02'},
627
        $MkNcDebug{'Analog_03'},
628
        $MkNcDebug{'Analog_04'},
629
        $MkNcDebug{'Analog_05'},
630
        $MkNcDebug{'Analog_06'},
631
        $MkNcDebug{'Analog_07'},
632
        $MkNcDebug{'Analog_08'},
633
        $MkNcDebug{'Analog_09'},
634
        $MkNcDebug{'Analog_10'},
635
        $MkNcDebug{'Analog_11'},
636
        $MkNcDebug{'Analog_12'},
637
        $MkNcDebug{'Analog_13'},
638
        $MkNcDebug{'Analog_14'},
639
        $MkNcDebug{'Analog_15'},
640
        $MkNcDebug{'Analog_16'},
641
        $MkNcDebug{'Analog_17'},
642
        $MkNcDebug{'Analog_18'},
643
        $MkNcDebug{'Analog_19'},
644
        $MkNcDebug{'Analog_20'},
645
        $MkNcDebug{'Analog_21'},               
646
        $MkNcDebug{'Analog_22'},
647
        $MkNcDebug{'Analog_23'},
648
        $MkNcDebug{'Analog_24'},
649
        $MkNcDebug{'Analog_25'},
650
        $MkNcDebug{'Analog_26'},
651
        $MkNcDebug{'Analog_27'},
652
        $MkNcDebug{'Analog_28'},
653
        $MkNcDebug{'Analog_29'},
654
        $MkNcDebug{'Analog_30'},
655
        $MkNcDebug{'Analog_31'},
656
        ) = unpack ('C2s32', $Data);
657
 
658
        # Timestamp, wann der Datensatz geschrieben wurde
659
        $MkNcDebug{'_Timestamp'} = time;
660
        $MkNcDebug{'_FrameCount'} ++;
661
        }
662
 
663
    elsif ( $Id eq "B" )
664
        {
665
        #
666
        # External Control
667
        #
668
        # Datenstruktur:
669
        #    u8 ConfirmFrame;
670
 
671
        my ($ConfirmFrame) = unpack ('C5', $Data);
672
 
673
        }
674
    elsif ( $Id eq "L" )
675
        {
676
        #
677
        # LCD Screen
678
        #
679
        # Datenstruktur:
680
        #    u8 Menuitem
681
        #    u8 MaxMenuItem
682
        #    char[80] Display Text
683
 
684
        my ($MenuItem, $MaxMenuItem, $LcdLine) = unpack ('CCA80', $Data);
685
        if ( $LcdLine =~ /Po1:\s*(\d+)\s*Po2:\s*(\d+)\s*Po3:\s*(\d+)\s*Po4:\s*(\d+)/i )
686
            {
687
            $Stick{'RcPoti1'} = $1;
688
            $Stick{'RcPoti2'} = $2;
689
            $Stick{'RcPoti3'} = $3;
690
            $Stick{'RcPoti4'} = $4;
691
            }
692
        elsif ( $LcdLine =~ /Po5:\s*(\d+)\s*Po6:\s*(\d+)\s*Po7:\s*(\d+)\s*Po8:\s*(\d+)/i )
693
            {
694
            $Stick{'RcPoti5'} = $1;
695
            $Stick{'RcPoti6'} = $2;
696
            $Stick{'RcPoti7'} = $3;
697
            $Stick{'RcPoti8'} = $4;
698
            }
699
        elsif ( $LcdLine =~ /Ni:\s*(-*\d+)\s*Ro:\s*(-*\d+)\s*Gs:\s*(-*\d+)\s*Ya:\s*(-*\d+)/i )
700
            {
701
            $Stick{'RcStickNick'} = $1;
702
            $Stick{'RcStickRoll'} = $2;
703
            $Stick{'RcStickGas'} = $3;
704
            $Stick{'RcStickGier'} = $4;
705
            }
706
        elsif ( $LcdLine =~ /Sat:\s*(-*\d+)\s*(-*\S+)\[1\]/i )
707
            {
708
            $Mk{'GpsFix'} = $2;
709
            }
710
 
711
        $Stick{'_RcTimestamp'} = time;
712
        $Stick{'_RcFrameCount'} ++;
713
        }
714
    else
715
        {
716
        print "Unknown Command: $Header $Data\n";
717
        }
718
    }  
719
 
720
 
721
# send Target or Waypoint to MK
722
sub MkFlyTo()
723
    {
724
    my %Param = @_;
725
 
726
    my $x               = $Param{'-x'};
727
    my $y               = $Param{'-y'};
728
    my $Lat             = $Param{'-lat'};
729
    my $Lon             = $Param{'-lon'};
730
    my $Alt             = $Param{'-alt'};
731
    my $Heading         = $Param{'-heading'}; # 0..360: Heading, <0: POI-Index, >360: Invalid
732
    my $ToleranceRadius = $Param{'-toleranceradius'};
733
    my $Holdtime        = $Param{'-holdtime'};
734
    my $EventFlag       = $Param{'-eventflag'};
735
    my $Mode            = $Param{'-mode'};
736
    my $Index           = $Param{'-index'};   # 1..n (dummy ... will be overwritten in NC:uart1.c)
737
    my $Type            = $Param{'-type'};    # 0=WP, 1=POI
738
 
739
    if ( $x eq ""  and  $y eq "" )     { ($x, $y)         = &MapGps2XY($Lat, $Lon); }
740
    if ( $Lat eq ""  and  $Lon eq "" ) { ($Lat, $Lon)     = &MapXY2Gps($x, $y); }
741
    if ( $Alt eq "" )                  { $Alt             = &Altitude(); }
742
    if ( $Heading eq "" )              { $Heading         = $Cfg->{'waypoint'}->{'DefaultHeading'}; }
743
    if ( $ToleranceRadius eq "" )      { $ToleranceRadius = $Cfg->{'waypoint'}->{'DefaultToleranceRadius'}; }
744
    if ( $Holdtime eq "" )             { $Holdtime        = $Cfg->{'waypoint'}->{'DefaultHoldtime'}; }
745
    if ( $EventFlag eq "" )            { $EventFlag       = $Cfg->{'waypoint'}->{'DefaultEventFlag'}; }
746
 
747
    my $Status = 1;     # valid
748
    if ( $Mode =~ /delete/i )
749
        {
750
        $Status = 0;    # invalid -> delete NC WP-List
751
        $Index = -1;    # required from NC0.19 onward
752
        }
753
 
754
    # set System information
755
    $System{'TargetSetpoint_x'} = $x;
756
    $System{'TargetSetpoint_y'} = $y;
757
    $System{'TargetSetpoint_Lat'} = $Lat;
758
    $System{'TargetSetpoint_Lon'} = $Lon;
759
    $System{'TargetSetpoint_Alt'} = $Alt;
760
 
761
    my $Lat_i = sprintf "%d", $Lat * 10000000;
762
    my $Lon_i = sprintf "%d", $Lon * 10000000;
763
    my $Alt_i = sprintf "%d", $Alt * 1000;
764
 
765
    # Datenstruktur:
766
    #    GPS_Pos_t Position;     // the gps position of the waypoint, see ubx.h for details
767
    #    s16 Heading;            // orientation, future implementation
768
    #    u8  ToleranceRadius;    // in meters, if the MK is within that range around the target, then the next target is 
769
    #    u8  HoldTime;           // in seconds, if the MK was once in the tolerance area around a WP,
770
    #                            // this time defines the delay before the next WP is triggered
771
    #    u8  Event_Flag;         // future emplementation
772
    #    u8  Index;              // to indentify different waypoints, workaround for bad communications PC <-> NC
773
    #    u8  Type;               // typeof Waypoint (0=WP, 1=POI)
774
    #    u8  reserve[10];        // reserved
775
 
776
    my $Wp = pack ('lllCsC15',
777
                   $Lon_i,
778
                   $Lat_i,
779
                   $Alt_i,
780
                   $Status,
781
                   $Heading,
782
                   $ToleranceRadius,
783
                   $Holdtime,
784
                   $EventFlag,
785
                   $Index + 1,
786
                   $Type,
787
                   0,0,0,0,0,0,0,0,0,0,
788
                  );
789
 
790
    if ( $Mode =~ /waypoint/i )
791
        {
792
        $MkSendQueue->enqueue( "w", "$AddrNC", $Wp );
793
        # &MkSend( "w", "$AddrNC", $Wp );
794
        }
795
    elsif ( $Mode =~ /target/i )
796
        {
797
        $MkSendQueue->enqueue( "s", "$AddrNC", $Wp );
798
        # &MkSend( "s", "$AddrNC", $Wp );
799
 
800
        # set Target information for Simulator
801
        $MkSim{'Target_Lat'} = $Lat;
802
        $MkSim{'Target_Lon'} = $Lon;
803
        $MkSim{'Target_Alt'} = $Alt;
804
        $MkSim{'Target_Status'} = $Status;
805
        $MkSim{'Target_Heading'} = $Heading;
806
        $MkSim{'Target_ToleranceRadius'} = $ToleranceRadius;
807
        $MkSim{'Target_Holdtime'} = $Holdtime;
808
        $MkSim{'Target_EventFlag'} = $EventFlag;
809
 
810
        # Timestamp, wann der Datensatz geschtieben wurde
811
        $MkSim{'_Timestamp'} = time;
812
        }
813
    else
814
        {
815
        # ignore
816
        }
817
 
818
    return 0;
819
    }
820
 
821
 
822
# send External control to MK
823
sub SendExternalControl()
824
    {
825
    my %Param = @_;
826
 
827
    my $RemoteButtons = $Param{'-remotebuttons'};
828
    my $Nick   = $Param{'-nick'};
829
    my $Roll   = $Param{'-roll'};
830
    my $Gier   = $Param{'-gier'};
831
    my $Gas    = $Param{'-gas'};
832
    my $Hight  = $Param{'-hight'};
833
    my $Free   = $Param{'-free'};
834
    my $Frame  = $Param{'-frame'};
835
    my $Config = $Param{'-config'};
836
 
837
    # Datenstruktur:
838
    #    u8      Digital[2];
839
    #    u8      RemoteButtons;
840
    #    s8      Nick;
841
    #    s8      Roll;
842
    #    s8      Yaw;
843
    #    u8      Gas;
844
    #    s8      Height;
845
    #    u8      free;
846
    #    u8      Frame;
847
    #    u8      Config;
848
 
849
    # Config/Bit 0 and FC-Parameter ExternControl > 128:
850
    #    Nich/Roll/Yaw added to RC-Channel
851
    #    Gas wird auf max. RC-Gas begrenzt
852
 
853
    my $Ec = pack ('CCCcccCcCCC',
854
                   0, 0,
855
                   $RemoteButtons,
856
                   $Nick,
857
                   $Roll,
858
                   $Gier,
859
                   $Gas,
860
                   $Hight,
861
                   $Free,
862
                   $Frame,       # Frame/Command counter, ungleich 0
863
                   $Config,
864
                  );
865
 
866
    $MkSendQueue->enqueue( "b", "$AddrFC", $Ec );
867
    # &MkSend( "b", "$AddrFC", $Ec );
868
 
869
    return 0;
870
    }
871
 
872
 
873
# send serial Channel values from %MkSerialChannel to MK/FC
874
sub SendSerialChannel()
875
    {
876
 
877
    # Datenstruktur:
878
    #    s8      Channel[12];
879
 
880
    lock (%MkSerialChannel);    # until end of block
881
 
882
    my $SP = pack ('c12',
883
                   $MkSerialChannel{'SerialChannel01'},
884
                   $MkSerialChannel{'SerialChannel02'},
885
                   $MkSerialChannel{'SerialChannel03'},
886
                   $MkSerialChannel{'SerialChannel04'},
887
                   $MkSerialChannel{'SerialChannel05'},
888
                   $MkSerialChannel{'SerialChannel06'},
889
                   $MkSerialChannel{'SerialChannel07'},
890
                   $MkSerialChannel{'SerialChannel08'},
891
                   $MkSerialChannel{'SerialChannel09'},
892
                   $MkSerialChannel{'SerialChannel10'},
893
                   $MkSerialChannel{'SerialChannel11'},
894
                   $MkSerialChannel{'SerialChannel12'},
895
                  );
896
 
897
    $MkSendQueue->enqueue( "y", "$AddrFC", $SP );
898
    # &MkSend( "y", "$AddrFC", $SP );
899
 
900
    return 0;
901
    }
902
 
903
 
904
# when called as thread
905
sub MkCommLoop()
906
    {
907
    while (1)
908
       {
909
       &MkIO();
910
       }
911
    }
912
 
913
 
914
#
915
# Hauptprgramm
916
#       
917
 
918
if ( $0 =~ /mkcomm.pl$/i )
919
    {
920
    # Program wurde direkt aufgerufen
921
    &MkCommLoop();
922
 
923
    # should never exit
924
    }
925
 
926
1;
927
 
928
__END__
929