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 $!\n" unless $sock;
76 say "ADMIN,connected" if $dbg;
79 print $sock "$mycall\r\n";
80 say "ADMIN,call $mycall sent" if $dbg;
95 say "RAW,$_" if $wantraw;
98 print $sock "$mycall\r\n";
99 say "ADMIN,call $mycall sent" if $dbg;
102 my (undef, undef, $origin, $qrg, $call, $mode, $s, $m, $spd, $u, $sort, $t, $tx) = split /[:\s]+/;
105 # We have an RBN data line, dedupe it very simply on time, ignore QRG completely.
106 # This works because the skimmers are NTP controlled (or should be) and will receive
107 # the spot at the same time (velocity factor of the atmosphere taken into account :-)
112 # fix up times for things like 'NXDXF B' etc
113 if ($tx && $t != /^\d{4}Z$/) {
114 if ($tx =~ /^\d{4}Z$/) {
125 $qrg = sprintf('%.1f', nearest(.1, $qrg)); # to nearest 100Hz (to catch the odd multiple decpl QRG [eg '7002.07']).
126 say join(',', "RBN", $origin, $qrg, $call, $mode, $s, $m, $spd, $u, $sort, $t) if !$wantraw && ($dbg || $showrbn);
128 # Determine whether to "SPOT" it based on whether we have not seen it before (near this QRG) or,
129 # if we have, has it been a "while" since the last time we spotted it? If it has been spotted
130 # before then "RESPOT" it.
131 my $nqrg = nearest(1, $qrg); # normalised to nearest Khz
132 my $sp = "$call|$nqrg"; # hopefully the skimmers will be calibrated at least this well!
135 if (!$ts || ($minspottime > 0 && $tim - $ts >= $minspottime)) {
136 if ($wantbeacon && $sort =~ /^BEA/) {
139 # Haven't used a perl 'goto' like this ever!
140 # Clearly I need to use an event driven framework :-)
141 goto periodic if !$wantcw && $mode =~ /^CW/;
142 goto periodic if !$wantrtty && $mode =~ /^RTTY/;
143 goto periodic if !$wantpsk && $mode =~ /^PSK/;
144 goto periodic if !$wantdx && $mode =~ /^DX/;
148 my $tag = $ts ? "RESPOT" : "SPOT";
149 say join(',', $tag, $origin, $qrg, $call, $mode, $s, $m, $spd, $u, $sort, $t);
153 say "DATA,$_" if $dbg && !$wantraw;
157 # periodic clearing out of the two caches
158 if (($tim % 60 == 0 && $tim > $last) || ($last && $tim >= $last + 60)) {
162 while (my ($k,$v) = each %d) {
170 say "ADMIN,rbn cache: $removed removed $count remain" if $dbg;
171 $count = $removed = 0;
172 while (my ($k,$v) = each %spot) {
173 if ($tim-$v > $minspottime*2) {
180 say "ADMIN,spot cache: $removed removed $count remain" if $dbg;
182 say join(',', "STAT", $noraw, $norbn, $nospot) if $showstats;
183 $noraw = $norbn = $nospot = 0;
185 $last = int($tim / 60) * 60;
197 rbn.pl - an experimental RBN filter program that
201 rbn.pl [options] <your callsign>
209 Print a brief help message and exits.
213 Prints the manual page and exits.
215 =item B<-host>=telnet.reversebeacon.net
217 As default, this program will connect to C<telnet.reversebeacon.net>. Use this argument to change that.
221 As default, this program will connect to port 7000. Use this argument to change that to some other port.
223 =item B<-want>=cw,rtty,psk,beacon,dx
225 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
226 these classes if you want specific types of spots.
230 Print a comma separated line of statistics once a minute which consists of:
232 STAT,E<lt>raw RBN spotsE<gt>,E<lt>de-duped RBN spotsE<gt>,E<lt>new spotsE<gt>
234 =item B<-repeattime=60>
236 A cache of callsigns and QRGs is kept. If a SPOT comes in after B<repeattime> minutes then it re-emitted
237 but with a RESPOT tag instead. Set this argument to 0 (or less) if you do not want any repeats.
241 Show the de-duplicated RBN lines as they come in.
245 Show the raw RBN lines as they come in.
251 B<This program> connects (as default) to RBN C<telnet.reversebeacon.net:7000> and parses the raw output
252 which it deduplicates and then outputs unique spots. It is possible to select one or more types of spot.
254 The output is the RBN spot line which has been separated out into a comma separated list. One line per spot.
258 SPOT,DK3UA-#,3560.0,DL6ZB,CW,27,dB,26,WPM,CQ,2152Z
259 SPOT,WB6BEE-#,14063.0,KD6SX,CW,24,dB,15,WPM,CQ,2152Z
260 RESPOT,S50ARX-#,1811.5,OM0CS,CW,37,dB,19,WPM,CQ,2152Z
261 SPOT,DF4UE-#,3505.0,TA1PT,CW,11,dB,23,WPM,CQ,2152Z
262 SPOT,AA4VV-#,14031.0,TF3Y,CW,16,dB,22,WPM,CQ,2152Z
263 SPOT,SK3W-#,3600.0,OK0EN,CW,13,dB,11,WPM,BEACON,2152Z
266 If the -raw flag is set then these lines will be interspersed with the raw line from the RBN source, prefixed
267 with "RAW,". For example:
269 RAW,DX de PJ2A-#: 14025.4 IP0TRC CW 16 dB 31 WPM CQ 1307Z
270 RAW,DX de PJ2A-#: 10118.9 K1JD CW 2 dB 28 WPM CQ 1307Z
271 RAW,DX de K2PO-#: 1823.4 HL5IV CW 8 dB 22 WPM CQ 1307Z
272 SPOT,K2PO-#,1823.4,HL5IV,CW,8,dB,22,WPM,CQ,1307Z
273 RAW,DX de LZ7AA-#: 14036.6 HA8GZ CW 7 dB 27 WPM CQ 1307Z
274 RAW,DX de DF4UE-#: 14012.0 R7KM CW 32 dB 33 WPM CQ 1307Z
275 RAW,DX de G7SOZ-#: 14012.2 R7KM CW 17 dB 31 WPM CQ 1307Z