@@ -471,10 +471,12 @@ func RunApplication(ctx context.Context, args []string) int {
471471 verbose := os .Getenv ("DX_API_VERBOSE_SPOT_PIPELINE" ) == "1" || strings .ToLower (os .Getenv ("DX_API_VERBOSE_SPOT_PIPELINE" )) == "true"
472472 spotCount := 0
473473
474- // Duplicate detection: track spotter+spotted pairs with timestamp
474+ // Duplicate detection: track spotter+spotted+band with timestamp
475+ // (same station can be on multiple bands simultaneously)
475476 type spotKey struct {
476477 spotter string
477478 spotted string
479+ band string
478480 }
479481 recentSpots := make (map [spotKey ]time.Time )
480482 const dedupeWindow = 30 * time .Second
@@ -500,10 +502,15 @@ func RunApplication(ctx context.Context, args []string) int {
500502 continue
501503 }
502504
503- // Check for duplicate spot (same spotter+spotted within 30 seconds)
505+ // Determine band from frequency for duplicate detection
506+ // (same station can be on multiple bands simultaneously)
507+ band := spot .BandFromName (receivedSpot .Frequency )
508+
509+ // Check for duplicate spot (same spotter+spotted+band within 30 seconds)
504510 key := spotKey {
505511 spotter : receivedSpot .Spotter ,
506512 spotted : receivedSpot .Spotted ,
513+ band : band ,
507514 }
508515 now := time .Now ()
509516
@@ -517,8 +524,8 @@ func RunApplication(ctx context.Context, args []string) int {
517524 // Check if this is a duplicate
518525 if lastSeen , exists := recentSpots [key ]; exists {
519526 if now .Sub (lastSeen ) < dedupeWindow {
520- logging .Debug ("DUPLICATE SPOT FILTERED: %s -> %s @ %.3f kHz [%s] (seen %.1f seconds ago)" ,
521- receivedSpot .Spotter , receivedSpot .Spotted , receivedSpot .Frequency , receivedSpot .Source , now .Sub (lastSeen ).Seconds ())
527+ logging .Debug ("DUPLICATE SPOT FILTERED: %s -> %s @ %.3f kHz (%s) [%s] (seen %.1f seconds ago)" ,
528+ receivedSpot .Spotter , receivedSpot .Spotted , receivedSpot .Frequency , band , receivedSpot .Source , now .Sub (lastSeen ).Seconds ())
522529 continue
523530 }
524531 }
@@ -621,18 +628,28 @@ func RunApplication(ctx context.Context, args []string) int {
621628
622629// enrichSpot enriches a raw spot with DXCC and LoTW information.
623630func enrichSpot (ctx context.Context , s spot.Spot , dxccClient * dxcc.Client , lotwClient * lotw.Client ) (spot.Spot , error ) {
624- // Enrich Spotter Info
625- spotterDxcc , err := dxccClient . GetDxccInfo ( ctx , s . Spotter , nil ) // No historical lookup date
626- if err != nil {
627- logging . Warn ( "DXCC lookup failed for spotter %s: %v" , s . Spotter , err )
631+ // Known pseudo-callsigns used by automated systems (skip DXCC/LoTW lookups)
632+ pseudoCallsigns := map [ string ] bool {
633+ "RBNHOLE" : true , // Reverse Beacon Network aggregated spots
634+ "SOTAMAT" : true , // SOTA automated spotting system
628635 }
629- spotterLoTW , err := lotwClient .GetLoTWUserActivity (ctx , s .Spotter )
630- if err != nil {
631- logging .Warn ("LoTW lookup failed for spotter %s: %v" , s .Spotter , err )
636+
637+ // Enrich Spotter Info (skip if pseudo-callsign)
638+ if ! pseudoCallsigns [s .Spotter ] {
639+ spotterDxcc , err := dxccClient .GetDxccInfo (ctx , s .Spotter , nil ) // No historical lookup date
640+ if err != nil {
641+ logging .Warn ("DXCC lookup failed for spotter %s: %v" , s .Spotter , err )
642+ }
643+ spotterLoTW , err := lotwClient .GetLoTWUserActivity (ctx , s .Spotter )
644+ if err != nil {
645+ logging .Warn ("LoTW lookup failed for spotter %s: %v" , s .Spotter , err )
646+ }
647+ s .SpotterInfo .DXCC = spotterDxcc
648+ s .SpotterInfo .LoTW = spotterLoTW
649+ s .SpotterInfo .IsLoTWUser = spotterLoTW != nil // Convenience field
650+ } else {
651+ logging .Debug ("Skipping DXCC/LoTW lookup for pseudo-callsign spotter: %s" , s .Spotter )
632652 }
633- s .SpotterInfo .DXCC = spotterDxcc
634- s .SpotterInfo .LoTW = spotterLoTW
635- s .SpotterInfo .IsLoTWUser = spotterLoTW != nil // Convenience field
636653
637654 // Enrich Spotted Info
638655 spottedDxcc , err := dxccClient .GetDxccInfo (ctx , s .Spotted , nil )
0 commit comments