Subversion Repositories Projects

Rev

Details | Last modification | View Log | RSS feed

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