Subversion Repositories Projects

Rev

Details | Last modification | View Log | RSS feed

Rev Author Line No. Line
598 rain-er 1
#!/usr/bin/perl
2
#!/usr/bin/perl -d:ptkdb
3
 
4
###############################################################################
5
#
6
# mktrack.pl -  Tracking Antenne 
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-14 0.0.1 rw created
41
# 2009-04-01 0.1.0 rw RC1
42
# 2009-06-14 0.1.1 rw Tilt-Servo added
43
# 2009-08-15 0.1.2 rw Flip Pan/Tilt servo for >180 degree
44
#                     config servo direction and range
45
#                     optional get antenna home position from Map-config
46
#                     Signal handler replaced by command-queue
47
#
48
###############################################################################
49
 
50
$Version{'track.pl'} = "0.1.1 - 2009-08-15";
51
 
52
# Packages
53
use Time::HiRes qw(usleep gettimeofday);   # http://search.cpan.org/~jhi/Time-HiRes-1.9719/HiRes.pm
54
use threads;           # http://search.cpan.org/~jdhedden/threads-1.72/threads.pm
55
                       # http://perldoc.perl.org/threads.html
56
use threads::shared;   # http://search.cpan.org/~jdhedden/threads-shared-1.28/shared.pm
57
use Thread::Queue;     # http://search.cpan.org/dist/Thread-Queue-2.11/lib/Thread/Queue.pm
58
use Math::Trig;
59
use Geo::Ellipsoid;    # http://search.cpan.org/dist/Geo-Ellipsoid-1.12/lib/Geo/Ellipsoid.pm
60
                       # http://www.kompf.de/gps/distcalc.html
61
                       # http://www.herrmann-w.de/Geocaching/Downloads/Richt.XLS
62
                       # http://williams.best.vwh.net/avform.htm
63
                       # http://williams.best.vwh.net/gccalc.html
64
                       # http://de.wikipedia.org/wiki/Orthodrome
65
if ( $^O =~ /Win32/i )
66
    {
67
    require Win32::SerialPort;  # http://search.cpan.org/dist/Win32-SerialPort
68
    }
69
else
70
    {
71
    require Device::SerialPort; # http://search.cpan.org/~cook/Device-SerialPort-1.04/SerialPort.pm
72
    }
73
 
74
require "mkcomm.pl";   # MK communication
75
 
76
# Sharing for Threads
77
share (@ServoPos);
78
share (%MkTrack);
79
 
80
# Queue for receiving commands
81
$TrackQueue = Thread::Queue->new();
82
 
83
#
84
# Parameter
85
#
86
 
87
# System Timer
88
$SysTimerResolution = 1000;  # Resolution in us
89
 
90
# Com Port for Pololu Mikro-Servoboard
91
# http://www.shop.robotikhardware.de/shop/catalog/product_info.php?cPath=65&products_id=118
92
my $ComPort = $Cfg->{'track'}->{'Port'}  || "COM8";
93
 
94
my $ServoPanCtrl  = $Cfg->{'track'}->{'ServoPanCtrl'}  || (0x00 | 29);
95
my $ServoTiltCtrl = $Cfg->{'track'}->{'ServoTiltCtrl'} || (0x20 | 27);
96
$MkTrack{'ServoPanCtrl'} = $ServoPanCtrl;
97
$MkTrack{'ServoTiltCtrl'} = $ServoPanCtrl;
98
 
99
# Servo parameter
100
$ServoPan   = 0;            # Servo channel Pan
101
$ServoTilt  = 1;            # Servo channel Tilt
102
$MkTrack{'ServoPan'} = $ServoPan;
103
$MkTrack{'ServoTilt'} = $ServoTilt;
104
@ServoSpeed = (100/40, 100/40, 100/40, 100/40, 100/40, 100/40, 100/40, 100/40); # ms/degree
105
$ServoConstPpm = 20;        # PPM protocol overhead in ms
106
 
107
@ServoTest = ( [  90,   0 ],       # Pan, Tilt in degrees for servo test
108
               [ 180,   0 ],
109
               [ 180,  90 ],
110
               [  90,   0 ],
111
               [   0,   0 ],
112
               [   0,  90 ],
113
               [  90,   0 ],
114
               [  90, 180 ],
115
               [  90,   0 ], );
116
 
117
# Tracking
118
$TrackInterval = 50;    # in ms
119
 
120
#
121
# Timer
122
#
123
 
124
sub SetTimer_ms()
125
    {
126
    return $SysTimerCount_ms + $_[0];
127
    }
128
 
129
sub CheckTimer_ms()
130
    {
131
    my $Diff = $_[0] - $SysTimerCount_ms;
132
    return ($Diff <= 0);
133
    }
134
 
135
#
136
# Servo
137
#
138
 
139
sub ServoInit()
140
    {
141
    # open COM-Port
142
    undef $ServoPort;
143
    if ( $^O =~ m/Win32/ )
144
        {
145
        $ServoPort = Win32::SerialPort->new ($ComPort) || die "Error open $ComPort\n";
146
        }
147
    else
148
        {
149
        $ServoPort = Device::SerialPort->new ($ComPort) || die "Error open $ComPort\n";
150
        }
151
    # Set COM parameters
152
    $ServoPort->baudrate(38400);
153
    $ServoPort->parity("none");
154
    $ServoPort->databits(8);
155
    $ServoPort->stopbits(1);
156
    $ServoPort->handshake('none');
157
    $ServoPort->write_settings;
158
 
159
    # Byte 1: sync - Pololu Mode
160
    # Byte 2: device
161
    # Byte 3: command
162
    # Byte 4: Servo num
163
    # Byte 5: data
164
    #         Bit 6: Servo on/off 
165
    #         Bit 5: Direction 
166
    #         Bit 4-0: Servo Range
167
 
168
    # Pan servo #0
169
    my $Output = pack('C*', 0x80, 0x01, 0x00, 0, 0x40 | $ServoPanCtrl );
170
    $ServoPort->write($Output);
171
 
172
    # Tilt servo #1
173
    my $Output = pack('C*', 0x80, 0x01, 0x00, 1, 0x40 | $ServoTiltCtrl );
174
    $ServoPort->write($Output);
175
 
176
    @ServoStartTime = (0, 0, 0, 0, 0, 0, 0, 0);   # Timestamp of last ServoMove() call
177
    @ServoEndTime   = (0, 0, 0, 0, 0, 0, 0, 0);   # Timestamp of estimated arrival at end position
178
    @ServoPos       = (0, 0, 0, 0, 0, 0, 0, 0);   # Current servo position 0..180 degree
179
    }
180
 
181
 
182
sub ServoMove()
183
    {
184
    my ($Num, $Angel, $Time) = @_;
185
 
186
    my $Overhead = 0;
187
 
188
    if ( $Angel != $ServoPos[$Num] )
189
        {
190
        if ( $Angel < 0)   {$Angel = 0;}
191
        if ( $Angel > 180) {$Angel = 180;}
192
 
193
        my $Pos = int ($Angel * 127/180 + 0.5);   # angel 0..180 degree to servo position 0..127
194
 
195
        # output to COM port
196
        # Byte 1: sync - Pololu Mode
197
        # Byte 2: device
198
        # Byte 3: command
199
        # Byte 4: Servo num
200
        # Byte 5: servo position 0..127
201
 
202
        my $Output = pack('C*', 0x80, 0x01, 0x02, $Num, $Pos );
203
        $ServoPort->write($Output);
204
 
205
        $Overhead += $ServoConstPpm;   # PPM protocol overhead
206
        }
207
 
208
    # set timer stuff for travel time predicion
209
    my $LastAngel = $ServoPos[$Num];
210
    my $EstimatedTime = abs($Angel - $LastAngel) * $ServoSpeed[$Num] + $Overhead;
211
    if ( $Time > 0 )
212
        {
213
        # Parameter override
214
        $EstimatedTime = $Time;
215
        }
216
    $ServoStartTime[$Num] = $SysTimerCount_ms;
217
    $ServoEndTime[$Num]   = $SysTimerCount_ms + $EstimatedTime;
218
    $ServoPos[$Num] = $Angel;
219
 
220
    return $ServoEndTime[$Num];
221
    }
222
 
223
 
224
# Check, if servo has reached end position
225
sub ServoCheck()
226
    {
227
    my $Num = $_[0];
228
    return &CheckTimer_ms($ServoEndTime[$Num]);
229
    }
230
 
231
 
232
sub ServoClose()
233
    {
234
    # close COM-Port
235
    undef $ServoPort;
236
    }
237
 
238
#
239
# Track it
240
#
241
 
242
sub TrackAntennaGps()
243
    {
244
 
245
    #
246
    # State maschine
247
    #
248
 
249
    $MkTrack{'IsRunning'} = "Running";
250
 
251
    my $State = "ColdStart";
252
    while (1)
253
        {
254
 
255
        if ( $State eq "ColdStart" )
256
            {
257
            &ServoInit();
258
 
259
            # initialize system-timer
260
            $SysTimerCount_ms = 0;
261
            $SysTimerError = 0;
262
            ($t0_s, $t0_us) = gettimeofday;
263
 
264
            $ServoTestIndex = 0;
265
 
266
            $State = "InitServoTest";
267
            }
268
 
269
        #
270
        # Start servo test
271
        # doesn't really make much sense, but looks cool:-)
272
        #
273
 
274
        elsif ( $State eq "InitServoTest")
275
            {
276
            if ( &ServoCheck ($ServoPan)  and  &ServoCheck ($ServoTilt) )
277
                {
278
 
279
                my $PanPos  = $ServoTest[$ServoTestIndex][0];
280
                my $TiltPos = $ServoTest[$ServoTestIndex][1];
281
                $ServoTestIndex ++;
282
 
283
                if ( defined $PanPos  and  defined $TiltPos )
284
                    {
285
                    &ServoMove ($ServoPan,  $PanPos,  1000);  # override 1s travel time
286
                    &ServoMove ($ServoTilt, $TiltPos, 1000);  # override 1s travel time
287
                    }
288
                else
289
                    {
290
                    # complete
291
                    $ServoTestIndex = 0;
292
                    $State = "WaitGps";
293
                    }
294
                }
295
            }
296
 
297
        #
298
        # Servo test finisched
299
        #
300
        # Wait for GPS Home position and compass
301
        #
302
 
303
        elsif ( $State eq "WaitGps" )
304
            {
305
            if ( &ServoCheck ($ServoPan) )
306
                {
307
                lock (%MkOsd);     # until end of block
308
                lock (%MkTrack);
309
 
310
                if ( $MkOsd{'_Timestamp'} >= time-2  and
311
                     $MkOsd{'SatsInUse'} >= 6 )
312
                    {
313
                    # gültige OSD daten vom MK und guter Satellitenempfang
314
 
315
                    # take GPS and compass from map definition or MK as antenna home-position
316
                    $MkTrack{'HomePos_Lon'} = $Map{'Track_Lon'}  ||  $MkOsd{'HomePos_Lon'};
317
                    $MkTrack{'HomePos_Lat'} = $Map{'Track_Lat'}  ||  $MkOsd{'HomePos_Lat'};
318
                    $MkTrack{'HomePos_Alt'} = $Map{'Track_Alt'}  ||  $MkOsd{'HomePos_Alt'};
319
                    if ( $MkTrack{'CompassHeading'} eq "" )
320
                        {
321
                        # nur beim ersten mal
322
                        $MkTrack{'CompassHeading'} = $Map{'Track_Bearing'}  ||  $MkOsd{'CompassHeading'};
323
                        }
324
 
325
                    $TrackTimer = &SetTimer_ms($TrackInterval);
326
 
327
                    $State = "TrackGps";
328
                    }
329
                }
330
            }
331
 
332
        #
333
        # GPS Fix Home position
334
        # Track now
335
        #               
336
        elsif ( $State eq "TrackGps" )
337
            {
338
            if ( &CheckTimer_ms($TrackTimer) and &ServoCheck($ServoPan) )
339
                {
340
                $TrackTimer = &SetTimer_ms($TrackInterval);   # reload Timer
341
 
342
                lock (%MkOsd);     # until end of block
343
                lock (%MkTrack);
344
 
345
                if ( $MkOsd{'_Timestamp'} >= time -2  and
346
                     $MkOsd{'SatsInUse'} >= 4 )
347
                    {
348
                    # valid OSD data from the MK and sufficient satellites
349
 
350
                    my $Track_Geo = Geo::Ellipsoid->new( 'units' => 'degrees',
351
                                                         'distance_units' => 'meter',
352
                                                         'ellipsoid' => 'WGS84',
353
                                                       );
354
 
355
                    my ($Dist, $Bearing) = $Track_Geo->to($MkTrack{'HomePos_Lat'}, $MkTrack{'HomePos_Lon'},
356
                                                          $MkOsd{'CurPos_Lat'}, $MkOsd{'CurPos_Lon'}); 
357
                    my $Dir_h = $MkTrack{'CompassHeading'};
358
                    my $Dir_c = $MkOsd{'CompassHeading'};
359
 
360
                    if ( $Dist < 4 )  # meter
361
                        {
362
                        # Too close to Home-Position. Set antenna to middle position                                            
363
                        $Bearing = $Dir_h;
364
                        }
365
 
366
                    $MkTrack{'Bearing'}    = sprintf ("%d", $Bearing);
367
                    $MkTrack{'Dist'}       = sprintf ("%d", $Dist);
368
                    $MkTrack{'CurPos_Lon'} = $MkOsd{'CurPos_Lon'};
369
                    $MkTrack{'CurPos_Lat'} = $MkOsd{'CurPos_Lat'};     
370
                    $MkTrack{'CurPos_Alt'} = $MkOsd{'CurPos_Alt'};
371
 
372
                    # antenna pan direction: 0..180 degree, centre = 90
373
                    my $AngelPan = $Bearing - $Dir_h + 90;
374
                    $AngelPan = $AngelPan % 360;
375
 
376
                    # antenna tilt direction: 0..180 degree, centre is up, 0 is front
377
                    my $AngelTilt = rad2deg(atan2(($MkOsd{'CurPos_Alt'} - $MkTrack{'HomePos_Alt'}), $Dist));
378
                    if ( $AngelTilt < 0 )   { $AngelTilt = 0; }
379
                    if ( $AngelTilt > 180 ) { $AngelTilt = 180; }
380
 
381
                    if ( $AngelPan >= 180 )
382
                        {
383
                        # Flip Pan/Tilt
384
                        $AngelPan = $AngelPan - 180;
385
                        $AngelTilt = 180 - $AngelTilt;
386
                        }
387
 
388
                    $MkTrack{'AngelPan'} = $AngelPan;
389
                    $MkTrack{'AngelTilt'} = $AngelTilt;
390
 
391
                    &ServoMove ($ServoPan, $AngelPan);
392
                    &ServoMove ($ServoTilt, $AngelTilt);
393
 
394
                    # Timestamp, wann der Datensatz geschtieben wurde
395
                    $MkTrack{'_Timestamp'} = time;
396
                    }
397
                }
398
            }
399
 
400
        else
401
            {
402
            # Restart
403
            $State = "ColdStart";
404
            }
405
 
406
        #
407
        # check command queue
408
        #
409
        while ( $TrackQueue->pending() > 0 )
410
            {
411
            my $Cmd = $TrackQueue->dequeue(1);
412
            if ( $Cmd =~ /EXIT/i )
413
                {
414
                &TrackExit();
415
                }
416
            }
417
 
418
        #
419
        # update system-timer
420
        #
421
        ($t1_s, $t1_us) = gettimeofday;
422
        $SysTimerSleep_us = ($t0_s - $t1_s) * 1000000 + $t0_us - $t1_us + $SysTimerCount_ms * $SysTimerResolution;
423
 
424
        if ($SysTimerSleep_us > 0)
425
            {
426
            usleep ($SysTimerSleep_us);
427
            }
428
        else
429
            {
430
            $SysTimerError ++;
431
            }
432
 
433
        $SysTimerCount_ms ++;
434
        }
435
    }
436
 
437
 
438
sub TrackExit()
439
    {
440
    $MkTrack{'IsRunning'} = "";
441
    $State = "ColdStart";
442
 
443
    # move all Servo to neutral position
444
    &ServoMove ($ServoPan, 90);
445
    &ServoMove ($ServoTilt, 0);
446
    sleep 2;
447
 
448
    # &ServoClose();
449
 
450
    # if ( defined threads->self() )
451
    #     {
452
    #     threads->exit();
453
    #     }
454
    # exit;
455
    }
456
 
457
 
458
#
459
# Main Program
460
#
461
 
462
if ( $0 =~ /track.pl$/i )
463
    {
464
    # Program wurde direkt aufgerufen
465
 
466
    # Kommunikation zum MK herstellen
467
    # Input: %MkOsd, %MkTarget, %MkNcDebug
468
    # Ouput: Thread-Queue: $MkSendQueue
469
    $mk_thr = threads->create (\&MkCommLoop) -> detach();
470
 
471
    &TrackAntennaGps();
472
 
473
    # should never exit
474
    }
475
 
476
1;
477
 
478
__END__