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 |