ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/cvsroot/aor/aorscan
Revision: 1.3
Committed: Sat Oct 1 11:29:14 2005 UTC (21 years ago) by root
Branch: MAIN
Changes since 1.2: +1 -0 lines
Log Message:
*** empty log message ***

File Contents

# User Rev Content
1 root 1.1 #!/opt/bin/perl
2    
3     # http://www.beepworld.de/members81/frequenzen/flughaefen.htm
4     # http://www.dia.unisa.it/professori/ads/corso-security/www/CORSO-9900/a5/Netsec/netsec.html
5     # http://www.scnt01426.pwp.blueyonder.co.uk/Articles/RS-232/AR-8000.htm
6    
7     use List::Util;
8     use Array::Heap2;
9    
10     use Fcntl;
11     use POSIX;
12     use Time::HiRes;
13    
14     use Event;
15    
16     use Coro;
17 root 1.2 use Coro::Event;
18 root 1.1 use Coro::Timer qw(sleep);
19     use Coro::Signal;
20    
21 root 1.2 use PDL ();
22     #use PDL::FFTW;
23     #use PDL::Audio;
24     #use PDL::IO::Pic;
25    
26 root 1.1 use PApp::SQL;
27    
28 root 1.2 use strict;
29    
30 root 1.1 sub BAUD() { 9600 }
31     sub RESOLUTION() { 50 } # in Hz
32     sub round($) { int ($_[0] / RESOLUTION + 0.5) * RESOLUTION }
33    
34 root 1.2 sub SEARCH_INTERVAL_MIN() { 555 }
35     sub AFT_INTERVAL_MIN() { 3333 }
36 root 1.1
37     $PApp::SQL::DBH = PApp::SQL::connect_cached __FILE__, "DBI:mysql:database=aor;mysql_read_default_file=/root/.my.cnf"
38     or die "unable to open mysql database aor";
39    
40 root 1.2 my $tty = $ARGV[0] || "/dev/ttyS0";
41    
42     sub AGC_MAX() { 26000 }
43    
44     my $record_rate = 48000;
45     my $record_init = "amixer sset Mic cap";
46     my $record_set = "amixer sset Capture {}%";
47     my $record_cmd = "arecord -traw -c1 -fS16_LE -r$record_rate 2>/dev/null";
48     my $playback_on = "amixer sset Mic unmute";
49     my $playback_off = "amixer sset Mic mute";
50     my $playback_set = "amixer sset Mic {}%";
51    
52     sub imag($) {
53     my ($pdl) = @_;
54     wpic $pdl, "/tmp/aor.pgm";
55     system "cv", "/tmp/aor.pgm";
56     }
57    
58     sub acmd($;$) {
59     my ($cmd, $arg) = @_;
60    
61     $cmd =~ s/\{\}/$arg/g;
62     system "$cmd >/dev/null";
63     }
64    
65     my $record_buf;
66     my $record_vol = 100;
67    
68     sub record($$) {
69     my ($data_cb, $done_cb) = @_;
70    
71     $record_buf = "";
72    
73     open my $fh, "-|", $record_cmd
74     or die "$record_cmd: $!";
75    
76     Event->io (fd => $fh, poll => 'r', cb => sub {
77     if (sysread $fh, $record_buf, 65536, length $record_buf) {
78     if (4096 < length $record_buf) {
79     # do the agc adjustment
80     my $pdl = PDL::short (unpack "v*", substr $record_buf, -4096);
81     if (AGC_MAX < $pdl->abs->max) {
82     acmd $record_set, --$record_vol;
83     # printf "AGC %d $record_vol\n", max abs $pdl;
84     }
85     }
86    
87     unless ($data_cb->()) {
88     $_[0]->w->cancel;
89     $done_cb->();
90     }
91     } else {
92     $_[0]->w->cancel;
93     $done_cb->();
94     }
95     });
96     }
97    
98     sub record_nsamples($) {
99     my $bytes = $_[0] * 2;
100    
101     my $done = new Coro::Signal;
102    
103     record sub {
104     $bytes > length $record_buf
105     }, sub {
106     $done->send;
107     };
108    
109     $done->wait;
110    
111     substr $record_buf, -$bytes
112     }
113    
114 root 1.1 if ($ARGV[0] eq "--addrange") {
115     my (undef, $mode, $lo, $hi, $step, $level) = @ARGV;
116    
117     my $cnt;
118     while () {
119     my $f = $lo + $cnt++ * $step;
120    
121     last if $f > $hi;
122    
123     eval {
124     sql_exec "insert into freq (mode, freq, width, activity_level) values (?, ?, ?, ?)",
125     $mode, round $f, $step, $level;
126     };
127     }
128    
129     exit;
130     } elsif ($ARGV[0] eq "--daemon") {
131     # nop
132     } else {
133     # exit
134     }
135    
136 root 1.2 my $NOW = time;
137 root 1.1
138 root 1.2 Event->timer (after => 0, interval => 1, hard => 1, prio => 3, cb => sub { $NOW = time });
139 root 1.1
140     open my $aor, "+<:raw", $tty
141     or die "$tty: $!";
142    
143     {
144     my $tio = new POSIX::Termios;
145    
146     $tio->getattr (fileno $aor);
147    
148     $tio->setiflag (POSIX::IXOFF | POSIX::IXON);
149     $tio->setoflag (0);
150     $tio->setcflag (POSIX::CLOCAL | POSIX::CREAD | POSIX::CSTOPB | POSIX::CS8);
151     $tio->setlflag (0);
152     $tio->setispeed (&POSIX::B9600);
153     $tio->setospeed (&POSIX::B9600);
154    
155     $tio->setattr (fileno $aor);
156     }
157    
158     my %mode = (
159     0 => "WFM",
160     1 => "NFM",
161     2 => "AM",
162     3 => "USB",
163     4 => "LSB",
164     5 => "CW",
165     );
166    
167     sub cmd($) {
168     # print ">$_[0]<\n";#d#
169     # fcntl $aor, F_SETFL, 0;
170     syswrite $aor, "$_[0]\015\012";
171     }
172    
173     sub delay($) {
174     my $bytes = int ($_[0] * (BAUD / 11)) - 2;
175    
176     " " x ($bytes - 2)
177     }
178    
179     cmd "MC1"; # mic off (0 = on, 1 = off, 2 = squelch)
180     cmd "ST000050"; # minimum stepsize
181 root 1.2 acmd $record_init;
182     acmd $record_set, $record_vol;
183     acmd $playback_off;
184 root 1.1
185     my @send_jobs;
186     my ($curfreq, $curmode);
187 root 1.2 my $pipeline = 4;
188 root 1.1
189     my $rbuf;
190     my @resp_jobs;
191    
192     sub send_scheduler {
193 root 1.2 while (@send_jobs
194     && @resp_jobs < $pipeline
195     && (!@resp_jobs || !$resp_jobs[-1]{exclusive})
196     ) {
197 root 1.1 my $job = (pop_heap @send_jobs)->[1];
198    
199     my ($cmd, @exp);
200    
201     if ($curmode != $job->{mode}) {
202     $cmd .= "MD$job->{mode}\015\012";
203     $curmode = $job->{mode};
204     }
205    
206     if ($curfreq != $job->{freq}) {
207     $cmd .= "RF$job->{freq}\015\012";
208     $curfreq = $job->{freq};
209     }
210    
211     $job->{exp} = [];
212     $job->{res} = [];
213    
214     for (@{ $job->{cmd} }) {
215     push @exp, $1 if s/^(..)=//;
216     $cmd .= "$_\015\012";
217     }
218    
219     $job->{exp} = \@exp;
220     $job->{res} = [];
221    
222 root 1.2 syswrite $aor, $cmd;
223    
224 root 1.1 if (@exp) {
225     push @resp_jobs, $job;
226     } else {
227     $job->{done}->send;
228     }
229     }
230     }
231    
232     Event->io (fd => $aor, prio => 1, poll => 'r', cb => sub {
233 root 1.2 sysread $aor, $rbuf, 4096, length $rbuf;
234 root 1.1
235     while ($rbuf =~ s/^([^\015\012]*)[\012\015]+//s) {
236     my $line = $1;
237     $line =~ s/[\012\015]//g;
238    
239     next unless $line =~ /^\S/;
240    
241     @resp_jobs or die "out of sync: expected nothing, but got '$line'\n";
242    
243     my $exp = shift @{ $resp_jobs[0]{exp} };
244    
245     $exp eq substr $line, 0, 2
246     or die "sync error: expected '$exp', got '$line'\n";
247    
248     push @{ $resp_jobs[0]{res} }, $line;
249    
250     unless (@{ $resp_jobs[0]{exp} }) {
251 root 1.2 my $job = shift @resp_jobs;
252    
253     $job->{exclusive}->() if $job->{exclusive};
254     $job->{done}->send;
255    
256 root 1.1 send_scheduler;
257     }
258    
259     next;
260     }
261     });
262    
263     sub job::result {
264     my ($self) = @_;
265    
266     $self->{done}->wait;
267    
268     wantarray ? @{ $self->{res} } : $self->{res}[0]
269     }
270    
271     sub job {
272     my ($nice, $mode, $freq, @cmd) = @_;
273    
274     my $job = bless {
275     freq => round $freq,
276     mode => $mode,
277     cmd => \@cmd,
278     done => new Coro::Signal,
279     }, job::;
280    
281 root 1.2 if (@cmd && ref $cmd[-1]) {
282     $job->{exclusive} = pop @cmd;
283     }
284    
285 root 1.1 push_heap @send_jobs, [$nice, $job];
286    
287     send_scheduler;
288    
289     $job
290     }
291    
292     sub lm {
293     map +(hex substr $_, 2) & 0x7f, @_
294     }
295    
296 root 1.2 sub aft($$$$) {
297     my ($nice, $mode, $center, $radius) = @_;
298 root 1.1
299     $radius = List::Util::max RESOLUTION, $radius * 0.1;
300    
301     my @level = map [$_->{freq}, (List::Util::sum lm $_->result) / 5],
302 root 1.2 map +(job $nice, $mode, $center + $radius * $_, (delay 0.0020, "LM=LM") x 5),
303 root 1.1 -10 .. 10;
304    
305     my ($tune, $weight);
306     for (@level) {
307     my ($f, $w) = @$_;
308    
309     $w **= 8;
310    
311     $tune += $f * $w;
312     $weight += $w;
313     }
314    
315     (int ($tune / $weight / RESOLUTION) * RESOLUTION, $weight ** 0.1 * 100)
316     }
317    
318     sub sweep {
319 root 1.2 my ($nice, $freqs, $cb) = @_;
320 root 1.1
321     my $job;
322    
323     for (@$freqs, undef) {
324     my ($mode, $freq, $width) = $_ ? @$_ : ();
325    
326 root 1.2 my $next_job = $freq && job $nice, $mode, $freq, delay 0.050, "LM=LM";
327    
328     $cb->($mode, $freq, $width, lm $job->result)
329     if $job;
330 root 1.1
331 root 1.2 $job = $next_job;
332     }
333     }
334 root 1.1
335 root 1.2 #############################################################################
336     # raw aearch
337    
338     async {
339     my $nice = 1000;
340 root 1.1
341 root 1.2 while () {
342     my @freq = sql_fetchall "select mode, freq, width from freq
343     where search_time < ?
344     order by search_time, mode, freq
345     limit 1000",
346     $NOW - SEARCH_INTERVAL_MIN;
347    
348     if (@freq) {
349     sweep $nice, \@freq, sub {
350     my ($mode, $freq, $width, $lm) = @_;
351    
352     sql_exec "update freq set search_time = ?, search_level = ?
353     where mode = ? and freq = ?",
354     $NOW, $lm,
355     $mode, $freq;
356     };
357     } else {
358     Coro::Timer::sleep 60;
359 root 1.1 }
360 root 1.2 }
361     };
362    
363     #############################################################################
364     # aft of active freqs
365    
366     async {
367     my $nice = 500;
368 root 1.1
369 root 1.2 while () {
370     my $st = sql_exec \my ($aft_time, $mode, $freq, $width, $activity_level),
371     "select aft_time, mode, freq, width, activity_level
372     from freq
373     where (search_level >= activity_level
374     or aft_time > 0)
375     and aft_time < ?
376     order by aft_time, mode, freq
377     limit 10",
378     $NOW;
379    
380     if ($st->rows) {
381     while ($st->fetch) {
382     my @lm = lm +(job $nice, $mode, $freq, delay 0.050, "LM=LM")->result;
383    
384     if ($lm[0] >= $activity_level) {
385     my ($tune, $weight) = aft $nice, $mode, $freq, $width * 0.9;
386    
387     if ($freq - 0.5 * $width <= $tune && $tune <= $freq + 0.5 * $width) {
388     sql_exec "update freq set aft_time = ?, aft_freq = ?, aft_level = ?
389     where mode = ? and freq = ?",
390     $NOW + 86400 - 3600, $tune, $weight,
391     $mode, $freq;
392     } else {
393 root 1.3 # outlier
394 root 1.2 sql_exec "update freq set aft_time = ?, aft_level = 0
395     where mode = ? and freq = ?",
396     $NOW + 86400 + 3600,
397     $mode, $freq;
398     }
399     } else {
400     # currently inactive
401     sql_exec "update freq set aft_time = ?
402     where mode = ? and freq = ?",
403     $NOW + 3600,
404     $mode, $freq;
405     }
406     }
407     } else {
408     Coro::Timer::sleep 60;
409     }
410 root 1.1 }
411 root 1.2 };
412    
413     #############################################################################
414     # scan active freqs
415 root 1.1
416     async {
417 root 1.2 return;
418     my $nice = 100;
419    
420 root 1.1 while () {
421 root 1.2 my $st = sql_exec \my ($mode, $freq, $width, $activity_level),
422     "select mode, aft_freq, width, activity_level
423     from freq
424     where aft_level > 0 and aft_freq > 0
425     order by freq, mode";
426    
427     if ($st->rows) {
428     my @jobs;
429    
430     while ($st->fetch) {
431     push @jobs, [$activity_level, job $nice, $mode, $freq, delay 0.030, "LM=LM"];
432     }
433    
434     for (@jobs) {
435     my ($activity_level, $job) = @$_;
436    
437     my @lm = lm $job->result;
438    
439     if ($lm[0] >= $activity_level) {
440     warn "scan $job->{mode} $job->{freq} @lm\n";
441     (job $nice-1, $mode, $job->{freq}, "LM=LM", sub {
442     # (job $nice-1, 0, 98400000, "LM=LM", sub {
443     acmd $playback_on;
444     Coro::Timer::sleep 0.5;
445     # my $data = 0.5 + (1 / 65536) * float unpack "v*", record_nsamples $record_rate * 0.1;
446     acmd $playback_off;
447     #$data = sin +(1/480) * xvals zeroes 48000;
448     # $data->reshape (100, $record_rate / 100);
449     #
450     # my $spectrum = 120 + cat map spectrum ($data->slice ("($_),"), "dB", "KAISER"), 0..99;
451     # printf "%s %s\n", (min $spectrum), (max $spectrum);
452     # imag short $spectrum;
453     })->result;
454     }
455     }
456 root 1.1
457 root 1.2 Coro::Timer::sleep 60;
458     } else {
459     Coro::Timer::sleep 60;
460     }
461 root 1.1 }
462     };
463    
464 root 1.2 #############################################################################
465    
466 root 1.1 Coro::Event::loop;
467    
468     __END__
469     mysql -e "delete from freq" aor
470 root 1.2 ./aorscan --addrange 1 26565000 27405000 10000 25
471     ./aorscan --addrange 1 34360000 35800000 20000 25
472     ./aorscan --addrange 1 38460000 39840000 20000 25
473     ./aorscan --addrange 1 74215000 77455000 20000 25
474     ./aorscan --addrange 1 84015000 87255000 20000 24
475     ./aorscan --addrange 2 108000000 144000000 25000 20
476     ./aorscan --addrange 1 118000000 136000000 8333.3333333333333 24
477     ./aorscan --addrange 1 108000000 144000000 25000 24
478     ./aorscan --addrange 4 165210000 169380000 20000 24
479     ./aorscan --addrange 3 169810000 173980000 20000 24
480     ./aorscan --addrange 1 230000000 328000000 8333.3333333333333 10
481     ./aorscan --addrange 2 230000000 328000000 50000 20
482     ./aorscan --addrange 1 438650000 439425000 25000 10
483     ./aorscan --addrange 4 443600000 444962500 12500 10
484     ./aorscan --addrange 3 448600000 449962500 12500 10
485     ./aorscan --addrange 1 1270200000 1270700000 25000 10
486     ./aorscan --addrange 1 1298200000 1298700000 25000 10
487    
488     ./aorscan --addrange 3 2690000 2690000 25000 10
489     ./aorscan --addrange 3 3413000 3413000 25000 10
490     ./aorscan --addrange 4 3413000 3413000 25000 10
491     ./aorscan --addrange 3 5640000 5640000 25000 10
492     ./aorscan --addrange 4 5640000 5640000 25000 10
493     ./aorscan --addrange 3 8957000 8957000 25000 10
494     ./aorscan --addrange 4 8957000 8957000 25000 10
495     ./aorscan --addrange 3 13264000 13264000 25000 10
496     ./aorscan --addrange 4 13264000 13264000 25000 10
497 root 1.1