Subversion Repositories Projects

Rev

Details | Last modification | View Log | RSS feed

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