3 # An RBN deduping filter
5 # Copyright (c) 2017 Dirk Koopman G1TLH
10 use IO::Socket::IP -register;
11 use Math::Round qw(nearest);
15 my $host = 'telnet.reversebeacon.net';
18 my $minspottime = 60*60; # minimum length of time between successive identical spots
19 my $showstats; # show RBN and Spot stats
35 #Getopt::Long::Configure( qw(auto_abbrev) );
36 GetOptions('host=s' => \$host,
40 'stats' => \$showstats,
42 'repeattime|rt=i' => sub { $minspottime = $_[1] * 60 },
44 my ($name, $value) = @_;
45 $wantcw = $wantrtty = $wantpsk = $wantbeacon = $wantdx = 0;
46 for (split /[:,\|]/, $value) {
48 ++$wantpsk if /^psk$/i;
49 ++$wantrtty if /^rtty$/i;
50 ++$wantbeacon if /^beacon$/i;
56 '<>' => sub { $mycall = shift },
61 pod2usage(1) if $help || !$mycall;
62 pod2usage(-exitval => 0, -verbose => 2) if $man;
65 for ($attempts = 1; $attempts <= 5; ++$attempts) {
66 say "ADMIN,connecting to $host $port.. (attempt $attempts) " if $dbg;
67 $sock = IO::Socket::IP->new(
75 die "ADMIN,Cannot connect to $host:$port after 5 attempts $!" unless $sock;
76 say "ADMIN,connected" if $dbg;
77 #print $sock "$mycall\r\n";
78 #say "ADMIN,call $mycall sent" if $dbg;
93 say "RAW,$_" if $wantraw;
96 print $sock "$mycall\r\n";
97 say "ADMIN,call $mycall sent" if $dbg;
100 my (undef, undef, $origin, $qrg, $call, $mode, $s, $m, $spd, $u, $sort, $t, $tx) = split /[:\s]+/;
103 # We have an RBN data line, dedupe it very simply on time, ignore QRG completely.
104 # This works because the skimmers are NTP controlled (or should be) and will receive
105 # the spot at the same time (velocity factor of the atmosphere taken into account :-)
110 # fix up times for things like 'NXDXF B' etc
111 if ($tx && $t != /^\d{4}Z$/) {
112 if ($tx =~ /^\d{4}Z$/) {
123 $qrg = sprintf('%.1f', nearest(.1, $qrg)); # to nearest 100Hz (to catch the odd multiple decpl QRG [eg '7002.07']).
124 say join(',', "RBN", $origin, $qrg, $call, $mode, $s, $m, $spd, $u, $sort, $t) if !$wantraw && ($dbg || $showrbn);
126 # Determine whether to "SPOT" it based on whether we have not seen it before (near this QRG) or,
127 # if we have, has it been a "while" since the last time we spotted it? If it has been spotted
128 # before then "RESPOT" it.
129 my $nqrg = nearest(1, $qrg); # normalised to nearest Khz
130 my $sp = "$call|$nqrg"; # hopefully the skimmers will be calibrated at least this well!
133 if (!$ts || ($minspottime > 0 && $tim - $ts >= $minspottime)) {
134 if ($wantbeacon && $sort =~ /^BEA/) {
137 # Haven't used a perl 'goto' like this ever!
138 # Clearly I need to use an event driven framework :-)
139 goto periodic if !$wantcw && $mode =~ /^CW/;
140 goto periodic if !$wantrtty && $mode =~ /^RTTY/;
141 goto periodic if !$wantpsk && $mode =~ /^PSK/;
142 goto periodic if !$wantdx && $mode =~ /^DX/;
146 my $tag = $ts ? "RESPOT" : "SPOT";
147 say join(',', $tag, $origin, $qrg, $call, $mode, $s, $m, $spd, $u, $sort, $t);
151 say "DATA,$_" if $dbg && !$wantraw;
155 # periodic clearing out of the two caches
156 if (($tim % 60 == 0 && $tim > $last) || ($last && $tim >= $last + 60)) {
160 while (my ($k,$v) = each %d) {
168 say "ADMIN,rbn cache: $removed removed $count remain" if $dbg;
169 $count = $removed = 0;
170 while (my ($k,$v) = each %spot) {
171 if ($tim-$v > $minspottime*2) {
178 say "ADMIN,spot cache: $removed removed $count remain" if $dbg;
180 say join(',', "STAT", $noraw, $norbn, $nospot) if $showstats;
181 $noraw = $norbn = $nospot = 0;
183 $last = int($tim / 60) * 60;
195 rbn.pl - an experimental RBN filter program that
199 rbn.pl [options] <your callsign>
207 Print a brief help message and exits.
211 Prints the manual page and exits.
213 =item B<-host>=telnet.reversebeacon.net
215 As default, this program will connect to C<telnet.reversebeacon.net>. Use this argument to change that.
219 As default, this program will connect to port 7000. Use this argument to change that to some other port.
221 =item B<-want>=cw,rtty,psk,beacon,dx
223 The program will print all spots in all classes in the 'mode/calling' column [cw, rtty, psk, beacon, dx]. You can choose one or more of
224 these classes if you want specific types of spots.
228 Print a comma separated line of statistics once a minute which consists of:
230 STAT,E<lt>raw RBN spotsE<gt>,E<lt>de-duped RBN spotsE<gt>,E<lt>new spotsE<gt>
232 =item B<-repeattime=60>
234 A cache of callsigns and QRGs is kept. If a SPOT comes in after B<repeattime> minutes then it re-emitted
235 but with a RESPOT tag instead. Set this argument to 0 (or less) if you do not want any repeats.
239 Show the de-duplicated RBN lines as they come in.
243 Show the raw RBN lines as they come in.
249 B<This program> connects (as default) to RBN C<telnet.reversebeacon.net:7000> and parses the raw output
250 which it deduplicates and then outputs unique spots. It is possible to select one or more types of spot.
252 The output is the RBN spot line which has been separated out into a comma separated list. One line per spot.
256 SPOT,DK3UA-#,3560.0,DL6ZB,CW,27,dB,26,WPM,CQ,2152Z
257 SPOT,WB6BEE-#,14063.0,KD6SX,CW,24,dB,15,WPM,CQ,2152Z
258 RESPOT,S50ARX-#,1811.5,OM0CS,CW,37,dB,19,WPM,CQ,2152Z
259 SPOT,DF4UE-#,3505.0,TA1PT,CW,11,dB,23,WPM,CQ,2152Z
260 SPOT,AA4VV-#,14031.0,TF3Y,CW,16,dB,22,WPM,CQ,2152Z
261 SPOT,SK3W-#,3600.0,OK0EN,CW,13,dB,11,WPM,BEACON,2152Z
264 If the -raw flag is set then these lines will be interspersed with the raw line from the RBN source, prefixed
265 with "RAW,". For example:
267 RAW,DX de PJ2A-#: 14025.4 IP0TRC CW 16 dB 31 WPM CQ 1307Z
268 RAW,DX de PJ2A-#: 10118.9 K1JD CW 2 dB 28 WPM CQ 1307Z
269 RAW,DX de K2PO-#: 1823.4 HL5IV CW 8 dB 22 WPM CQ 1307Z
270 SPOT,K2PO-#,1823.4,HL5IV,CW,8,dB,22,WPM,CQ,1307Z
271 RAW,DX de LZ7AA-#: 14036.6 HA8GZ CW 7 dB 27 WPM CQ 1307Z
272 RAW,DX de DF4UE-#: 14012.0 R7KM CW 32 dB 33 WPM CQ 1307Z
273 RAW,DX de G7SOZ-#: 14012.2 R7KM CW 17 dB 31 WPM CQ 1307Z