X-Git-Url: http://dxcluster.net/gitweb/gitweb.cgi?a=blobdiff_plain;f=perl%2FRBN.pm;h=b78e9d4be24cffc77f45730580a49936b9321e38;hb=ea366aefc6157af48d60f939f6b6d00e6d55f438;hp=492b1ac5331d68cddae9939c37e380595537ad45;hpb=535e44d1449d219bb8479d6f824118e3b31dde7a;p=spider.git diff --git a/perl/RBN.pm b/perl/RBN.pm index 492b1ac5..b78e9d4b 100644 --- a/perl/RBN.pm +++ b/perl/RBN.pm @@ -19,21 +19,25 @@ use DXChannel; use Math::Round qw(nearest); use Date::Parse; use Time::HiRes qw(clock_gettime CLOCK_REALTIME); +use Spot; our @ISA = qw(DXChannel); -our $startup_delay =0;#3*60; # don't send anything out until this timer has expired +our $startup_delay = 5*60; # don't send anything out until this timer has expired # this is to allow the feed to "warm up" with duplicates - # so that the "big rush" doesn't happen. + # so that the "big rush" doesn't happen. our $minspottime = 60*60; # the time between respots of a callsign - if a call is # still being spotted (on the same freq) and it has been # spotted before, it's spotted again after this time # until the next minspottime has passed. +our $beacontime = 5*60; # same as minspottime, but for beacons (and shorter) + our $dwelltime = 6; # the amount of time to wait for duplicates before issuing # a spot to the user (no doubt waiting with bated breath). +our $filterdef = $Spot::filterdef; # we use the same filter as the Spot system. Can't think why. sub new { @@ -53,6 +57,7 @@ sub new $self->{sort} = 'N'; $self->{lasttime} = $main::systime; $self->{minspottime} = $minspottime; + $self->{beacontime} = $beacontime; $self->{showstats} = 0; return $self; @@ -184,20 +189,9 @@ sub normal if ($mode eq 'RTTY') { $mode = 'RTT'; } - - # 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 main de-duping key is [call, $frequency], but we probe a bit around that frequency to find a + # range of concurrent frequencies that might be in play. # 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 @@ -215,33 +209,46 @@ sub normal # 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 $nqrg = nearest(.5, $qrg); # normalised to nearest Khz + my $nqrg = nearest(1, $qrg); # normalised to nearest Khz my $sp = "$call|$nqrg"; # hopefully the skimmers will be calibrated at least this well! + my $spp = sprintf("$call|%d", $nqrg+1); # but, clearly, my hopes are rudely dashed + my $spm = sprintf("$call|%d", $nqrg-1); # in BOTH directions! # do we have it? my $spot = $spots->{$sp}; + $spot = $spots->{$spp}, $sp = $spp, dbg(qq{RBN: SPP using $spp for $sp}) if !$spot && exists $spots->{$spp}; + $spot = $spots->{$spm}, $sp = $spm, dbg(qq{RBN: SPM using $spm for $sp}) if !$spot && exists $spots->{$spm}; + # if we have one and there is only one slot and that slot's time isn't expired for respot then return my $respot = 0; - if ($spot && @$spot == 1) { - unless ($self->{minspottime} > 0 && $tim - $spot->[0] >= $self->{minspottime}) { - dbg("RBN: key: '$sp' call: $call qrg: $qrg DUPE \@ ". atime(int $spot->[0])) if isdbg('rbn'); - return; + if ($spot && ref $spot) { + if (@$spot == 1) { + unless ($self->{minspottime} > 0 && $tim - $spot->[0] >= $self->{minspottime}) { + dbg("RBN: key: '$sp' call: $call qrg: $qrg DUPE \@ ". atime(int $spot->[0])) if isdbg('rbn'); + return; + } + + dbg("RBN: key: '$sp' RESPOTTING call: $call qrg: $qrg last seen \@ ". atime(int $spot->[0])) if isdbg('rbn'); + undef $spot; # it's about to be recreated (in one place) + ++$respot; } - dbg("RBN: key: '$sp' RESPOTTING call: $call qrg: $qrg last seen \@ ". atime(int $spot->[0])) if isdbg('rbn'); - ++$respot; + + # otherwise we have a spot being built up at the moment + } elsif ($spot) { + dbg("RBN: key '$sp' = '$spot' not ref"); + return; } # here we either have an existing spot record buildup on the go, or we need to create the first one unless ($spot) { - $spot = [clock_gettime(CLOCK_REALTIME)]; - $spots->{$sp} = $spot; - dbg("RBN: key: '$sp' call: $call qrg: $qrg NEW") if isdbg('rbn'); + $spots->{$sp} = $spot = [clock_gettime(CLOCK_REALTIME)];; + dbg("RBN: key: '$sp' call: $call qrg: $qrg NEW" . $respot ? ' RESPOT' : '') if isdbg('rbn'); } # add me to the display queue unless we are waiting for initial in rush to finish return unless $self->{inrushpreventor} < $main::systime; - push @queue, $sp if @$spot == 1; # queue the KEY (not the record) + push @{$self->{queue}}, $sp if @$spot == 1; # queue the KEY (not the record) # build up a new record and store it in the buildup # deal with the unix time @@ -250,8 +257,10 @@ sub normal $utz -= 86400 if $utz > $tim+3600; # too far ahead, drag it back one day # create record and add into the buildup - my $r = [$origin, nearest(.1, $qrg), $call, $mode, $s, $t, $utz, $respot]; + my $r = [$origin, nearest(.1, $qrg), $call, $mode, $s, $t, $utz, $respot, $u]; dbg("RBN: key: '$sp' ADD RECORD call: $call qrg: $qrg origin: $origin") if isdbg('rbn'); + my @s = Spot::prepare($r->[1], $r->[2], $r->[6], '', $r->[0]); + $r->[9] = \@s; push @$spot, $r; @@ -259,13 +268,17 @@ sub normal my $now = clock_gettime(CLOCK_REALTIME); # now run the waiting queue which just contains KEYS ($call|$qrg) - foreach $sp (@queue) { + foreach $sp (@{$self->{queue}}) { my $cand = $spots->{$sp}; - if ($now >= $cand->[0]+$dwelltime ) { + unless ($cand && $cand->[0]) { + dbg "RBN Cand " . ($cand ? 'def' : 'undef') . " [0] " . ($cand->[0] ? 'def' : 'undef') . " dwell $dwelltime"; + next; + } + if ($now >= $cand->[0] + $dwelltime ) { # we have a candidate, create qualitee value(s); unless (@$cand > 1) { dbg "RBN: QUEUE key '$sp' MISSING RECORDS " . dd($cand) if isdbg 'rbn'; - shift @queue; + shift @{$self->{queue}}; next; } my $savedtime = shift @$cand; # save the start time @@ -283,8 +296,9 @@ sub normal # clear out the data and make this now just "spotted", but no further action required until respot time dbg "RBN: QUEUE key '$sp' cleared" if isdbg 'rbn'; + $spots->{$sp} = [$savedtime]; - shift @queue; + shift @{$self->{queue}}; } else { dbg sprintf("RBN: QUEUE key: '$sp' SEND time not yet reached %.1f secs left", $spot->[0] + $dwelltime - $now) if isdbg 'rbnqueue'; } @@ -308,7 +322,7 @@ sub normal ++$count; } } - dbg "RBN:ADMIN,spot cache: $removed removed $count remain"; # if isdbg('rbn'); + dbg "RBN:ADMIN,$self->{call},spot cache remain: $count removed: $removed"; # if isdbg('rbn'); dbg "RBN:" . join(',', "STAT", $self->{noraw}, $self->{norbn}, $self->{nospot}) if $self->{showstats}; $self->{noraw} = $self->{norbn} = $self->{nospot} = 0; $self->{last} = int($tim / 60) * 60; @@ -366,48 +380,83 @@ sub dx_spot my $saver; my %zone; - + my %qrg; + my $respot; + my $qra; + foreach my $r (@$spot) { - # $r = [$origin, $qrg, $call, $mode, $s, $t, $utz, $respot]; + # $r = [$origin, $qrg, $call, $mode, $s, $t, $utz, $respot, $qra]; # Spot::prepare($qrg, $call, $utz, $comment, $origin); my $comment = sprintf "%-3s %2ddB $quality", $r->[3], $r->[4]; - my @s = Spot::prepare($r->[1], $r->[2], $r->[6], $comment, $r->[0]); + $respot = 1 if $r->[7]; + $qra = $r->[8] if !$qra && $r->[8] && is_qra($r->[8]); - ++$zone{$s[11]}; # save the spotter's zone + my $s = $r->[9]; # the prepared spot + $s->[3] = $comment; # apply new generated comment - # save the highest strength one - if ($r->[4] < $strength) { - $strength = $r->[4]; - $saver = \@s; - dbg("RBN: STRENGTH call: $s[1] qrg: $s[0] origin: $s[4] dB: $r->[4]") if isdbg 'rbn'; - } + + ++$zone{$s->[11]}; # save the spotter's zone + ++$qrg{$s->[0]}; # and the qrg + my $filter = 0; if ($dxchan->{rbnfilter}) { - ($filter, undef) = $dxchan->{rbnfilter}->it(\@s); + ($filter, undef) = $dxchan->{rbnfilter}->it($s); next unless $filter; - $saver = \@s; - dbg("RBN: FILTERED call: $s[1] qrg: $s[0] origin: $s[4] dB: $r->[4]") if isdbg 'rbn'; + $saver = $s; + dbg("RBN: FILTERED call: $s->[1] qrg: $s->[0] origin: $s->[4] dB: $r->[4]") if isdbg 'rbn'; last; } + # save the lowest strength one + if ($r->[4] < $strength) { + $strength = $r->[4]; + $saver = $s; + dbg("RBN: STRENGTH call: $s->[1] qrg: $s->[0] origin: $s->[4] dB: $r->[4]") if isdbg 'rbn'; + } } if ($saver) { my $buf; + # create a zone list of spotters delete $zone{$saver->[11]}; # remove this spotter's zone (leaving all the other zones) my $z = join ',', sort {$a <=> $b} keys %zone; - $saver->[3] .= " Z:$z" if length $z; + + # determine the most likely qrg and then set it + my $mv = 0; + my $fk; + my $c = 0; + while (my ($k, $v) = each %qrg) { + $fk = $k, $mv = $v if $v > $mv; + ++$c; + } + $saver->[0] = $fk; + $saver->[3] .= '*' if $c > 1; + $saver->[3] .= '+' if $respot; + $saver->[3] .= " Z:$z" if $z; + dbg("RBN: SENDING call: $saver->[1] qrg: $saver->[0] origin: $saver->[4] $saver->[3]") if isdbg 'rbn'; if ($dxchan->{ve7cc}) { + my $call = $saver->[1]; + $saver->[1] .= '-#'; $buf = VE7CC::dx_spot($dxchan, @$saver); + $saver->[1] = $call; } else { $buf = $dxchan->format_dx_spot(@$saver); } $buf =~ s/^DX/RB/; $dxchan->local_send('N', $buf); + + if ($qra) { + my $user = DXUser::get_current($saver->[1]) || DXUser->new($saver->[1]); + unless ($user->qra && is_qra($user->qra)) { + $user->qra($qra); + dbg("RBN: update qra on $saver->[1] to $qra"); + $user->put; + } + } } }