Subversion Repositories Projects

Rev

Details | Last modification | View Log | RSS feed

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