my $wantpsk = 1;
my $wantbeacon = 1;
my $wantdx = 1;
+my $wantft = 1;
+my $wantpsk = 1;
my $wantraw = 0;
my $showrbn;
my $help = 0;
'repeattime|rt=i' => sub { $minspottime = $_[1] * 60 },
'want=s' => sub {
my ($name, $value) = @_;
- $wantcw = $wantrtty = $wantpsk = $wantbeacon = $wantdx = 0;
+ $wantcw = $wantrtty = $wantpsk = $wantbeacon = $wantdx = $wantft = $wantpsk = 0;
for (split /[:,\|]/, $value) {
++$wantcw if /^cw$/i;
++$wantpsk if /^psk$/i;
++$wantrtty if /^rtty$/i;
- ++$wantbeacon if /^beacon$/i;
+ ++$wantbeacon if /^beacon/i;
++$wantdx if /^dx$/i;
+ ++$wantft if /^ft$/;
+ ++$wantft, ++$wantrtty, ++$wantpsk if /^digi/;
}
},
'help|?' => \$help,
last if $sock;
}
-die "ADMIN,Cannot connect to $host:$port after 5 attempts $!" unless $sock;
+die "ADMIN,Cannot connect to $host:$port after 5 attempts $!\n" unless $sock;
say "ADMIN,connected" if $dbg;
+$sock->timeout(0);
+
print $sock "$mycall\r\n";
say "ADMIN,call $mycall sent" if $dbg;
say "RAW,$_" if $wantraw;
if (/call:/) {
-print $sock "$mycall\r\n";
-say "ADMIN,call $mycall sent" if $dbg;
+ print $sock "$mycall\r\n";
+ say "ADMIN,call $mycall sent" if $dbg;
}
my (undef, undef, $origin, $qrg, $call, $mode, $s, $m, $spd, $u, $sort, $t, $tx) = split /[:\s]+/;
+ my $b;
+
if ($t || $tx) {
- # We have an RBN data line, dedupe it very simply on time, ignore QRG completely.
- # This works because the skimmers are NTP controlled (or should be) and will receive
- # the spot at the same time (velocity factor of the atmosphere taken into account :-)
- my $p = "$t|$call";
- ++$noraw;
- next if $d{$p};
-
# fix up times for things like 'NXDXF B' etc
- if ($tx && $t != /^\d{4}Z$/) {
+ if ($tx && $t !~ /^\d{4}Z$/) {
if ($tx =~ /^\d{4}Z$/) {
+ $b = $t;
$t = $tx;
} else {
say "ERR,$_";
}
}
+ # We have an RBN data line, dedupe it very simply on time, ignore QRG completely.
+ # This works because the skimmers are NTP controlled (or should be) and will receive
+ # the spot at the same time (velocity factor of the atmosphere and network delays
+ # carefully (not) taken into account :-)
+
+ # Note, there is no intelligence here, but there are clearly basic heuristics that could
+ # be applied at this point that reject (more likely rewrite) the call of a busted spot that would
+ # useful for a zonal hotspot requirement from the cluster node.
+
+ # In reality, this mechanism would be incorporated within the cluster code, utilising the dxqsl database,
+ # and other resources in DXSpider, thus creating a zone map for an emitted spot. This is then passed through the
+ # normal "to-user" spot system (where normal spots are sent to be displayed per user) and then be
+ # processed through the normal, per user, spot filtering system - like a regular spot.
+
+ # The key to this is deducing the true callsign by "majority voting" (the greater the number of spotters
+ # the more effective this is) together with some lexical analsys probably in conjuction with DXSpider
+ # data sources (for singleton spots) to then generate a "centre" from and to zone (whatever that will mean if it isn't the usual one)
+ # and some heuristical "Kwalitee" rating given distance from the zone centres of spotter, recipient user
+ # and spotted. A map can be generated once per user and spotter as they are essentially mostly static.
+ # The spotted will only get a coarse position unless other info is available. Programs that parse
+ # DX bulletins and the online data online databases could be be used and then cached.
+
+ # Obviously users have to opt in to receiving RBN spots and other users will simply be passed over and
+ # ignored.
+
+ # Clearly this will only work in the 'mojo' branch of DXSpider where it is possible to pass off external
+ # data requests to ephemeral or semi resident forked processes that do any grunt work and the main
+ # process to just the standard "message passing" which has been shown to be able to sustain over 5000
+ # per second (limited by the test program's output and network speed, rather than DXSpider's handling).
+
+ my $p = "$t|$call";
+ ++$noraw;
+ next if $d{$p};
+
# new RBN input
$d{$p} = $tim;
++$norbn;
$qrg = sprintf('%.1f', nearest(.1, $qrg)); # to nearest 100Hz (to catch the odd multiple decpl QRG [eg '7002.07']).
- say join(',', "RBN", $origin, $qrg, $call, $mode, $s, $m, $spd, $u, $sort, $t) if !$wantraw && ($dbg || $showrbn);
+ if (!$wantraw && ($dbg || $showrbn)) {
+ my $s = join(',', "RBN", $origin, $qrg, $call, $mode, $s, $m, $spd, $u, $sort, $t);
+ $s .= ",$b" if $b;
+ say $s;
+ }
# Determine whether to "SPOT" it based on whether we have not seen it before (near this QRG) or,
# if we have, has it been a "while" since the last time we spotted it? If it has been spotted
my $nqrg = nearest(1, $qrg); # normalised to nearest Khz
my $sp = "$call|$nqrg"; # hopefully the skimmers will be calibrated at least this well!
my $ts = $spot{$sp};
-
+
if (!$ts || ($minspottime > 0 && $tim - $ts >= $minspottime)) {
- if ($wantbeacon && $sort =~ /^BEA/) {
- ;
- } else {
- # Haven't used a perl 'goto' like this ever!
- # Clearly I need to use an event driven framework :-)
- goto periodic if !$wantcw && $mode =~ /^CW/;
- goto periodic if !$wantrtty && $mode =~ /^RTTY/;
- goto periodic if !$wantpsk && $mode =~ /^PSK/;
- goto periodic if !$wantdx && $mode =~ /^DX/;
+ my $want;
+
+ ++$want if $wantbeacon && $sort =~ /^BEA|NCD/;
+ ++$want if $wantcw && $mode =~ /^CW/;
+ ++$want if $wantrtty && $mode =~ /^RTTY/;
+ ++$want if $wantpsk && $mode =~ /^PSK/;
+ ++$want if $wantdx && $mode =~ /^DX/;
+ ++$want if $wantft && $mode =~ /^FT/;
+ if ($want) {
+ ++$nospot;
+ my $tag = $ts ? "RESPOT" : "SPOT";
+ $t .= ",$b" if $b;
+ say join(',', $tag, $origin, $qrg, $call, $mode, $s, $m, $spd, $u, $sort, $t);
+ $spot{$sp} = $tim;
}
-
- ++$nospot;
- my $tag = $ts ? "RESPOT" : "SPOT";
- say join(',', $tag, $origin, $qrg, $call, $mode, $s, $m, $spd, $u, $sort, $t);
- $spot{$sp} = $tim;
}
} else {
say "DATA,$_" if $dbg && !$wantraw;
}
- periodic:
# periodic clearing out of the two caches
if (($tim % 60 == 0 && $tim > $last) || ($last && $tim >= $last + 60)) {
my $count = 0;
my $removed = 0;
-
+
while (my ($k,$v) = each %d) {
if ($tim-$v > 60) {
delete $d{$k};
=head1 NAME
-rbn.pl - an experimental RBN filter program that
+rbn.pl - an experimental RBN filter program
=head1 SYNOPSIS
-rbn.pl [options] <your callsign>
+rbn.pl [options] <any callsign>
+
+We read the raw data
+from the RBN. We collect similar spots on a frequency within 100hz and try to
+deduce which if them is likely to be the true callsign. Emitted spots are cached and thereafter ignored
+for a period until it is spotted again, when it may be emitted again - but marked as a RESPOT.
+
+This is just technology demonstrator designed to scope out the issues and make sure that the line decoding works
+in all circumstances. But even on busy weekends it seems to cope just fine deduping away within its limits.
+
+To see it work at its best, run it as: rbn.pl -stats <any callsign>
+
+Leave it running for some time, preferably several (10s of) minutes.
+You will see it slowly reduce the number of new spots until you start to see "RESPOT" lines. Reductions
+of more than one order of magnitude is normal. Particularly when there are many more spotters.
=head1 OPTIONS
As default, this program will connect to port 7000. Use this argument to change that to some other port.
-=item B<-want>=cw,rtty,psk,beacon,dx
+=item B<-want>=cw,rtty,dx,beacon,psk,ft,digital
+
+The program will print all spots in all classes in the 'mode/calling' column [cw, rtty, beacon, dx, psk, ft, digital]. You can choose one or more of
+these classes if you want specific types of spots. The class 'digital' is equivalent to [rtty,psk,ft]. The class 'beacon' includes
+NCDXF beacons.
-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
-these classes if you want specific types of spots.
+E.g. rbn.pl -want=psk,ft,beacon g9tst
=item B<-stats>