Subversion Repositories Projects

Rev

Details | Last modification | View Log | RSS feed

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