fix stations that heard call

This commit is contained in:
Graham McIntire 2025-07-13 18:55:51 -05:00
parent 610a5b8949
commit 766a1301e0
No known key found for this signature in database
3 changed files with 101 additions and 47 deletions

View file

@ -6,6 +6,7 @@ defmodule Aprsme.CachedQueries do
alias Aprsme.Packet
alias Aprsme.Packets
alias Aprsme.Repo
alias Ecto.Adapters.SQL
# 1 minute for frequently changing data
@cache_ttl_short to_timeout(minute: 1)
@ -195,7 +196,7 @@ defmodule Aprsme.CachedQueries do
"""
result =
case Ecto.Adapters.SQL.query(Repo, query, [callsigns]) do
case SQL.query(Repo, query, [callsigns]) do
{:ok, %{rows: rows}} ->
Enum.map(rows, fn [callsign, lat, lon] ->
%{

View file

@ -546,7 +546,7 @@ defmodule AprsmeWeb.MapLive.Index do
defp update_map_state(socket, map_center, zoom) do
old_zoom = socket.assigns.map_zoom
crossing_threshold = is_crossing_zoom_threshold?(old_zoom, zoom)
crossing_threshold = crossing_zoom_threshold?(old_zoom, zoom)
socket = assign(socket, map_center: map_center, map_zoom: zoom)
@ -557,7 +557,7 @@ defmodule AprsmeWeb.MapLive.Index do
end
end
defp is_crossing_zoom_threshold?(old_zoom, new_zoom) do
defp crossing_zoom_threshold?(old_zoom, new_zoom) do
(old_zoom <= 8 and new_zoom > 8) or (old_zoom > 8 and new_zoom <= 8)
end
@ -2050,17 +2050,6 @@ defmodule AprsmeWeb.MapLive.Index do
})
end
# Helper function to parse coordinates from various formats
@spec parse_coordinate(any()) :: float()
defp parse_coordinate(coord) do
cond do
is_binary(coord) -> String.to_float(coord)
is_integer(coord) -> coord / 1.0
is_float(coord) -> coord
true -> 0.0
end
end
defp trigger_marker_display(socket) do
# Clear heat map and show markers
socket = push_event(socket, "show_markers", %{})
@ -2100,7 +2089,7 @@ defmodule AprsmeWeb.MapLive.Index do
# Only clear historical packets if:
# 1. Bounds actually changed AND
# 2. This is not the initial load AND
# 2. This is not the initial load AND
# 3. We've already completed the initial historical load
socket =
if bounds_changed and not is_initial_load and initial_historical_completed do
@ -2138,21 +2127,20 @@ defmodule AprsmeWeb.MapLive.Index do
path
|> String.split(",")
|> Enum.map(&String.trim/1)
|> Enum.reject(&(&1 == ""))
# Skip TCPIP entries and qA* entries (but preserve the actual digipeater/igate callsigns)
|> Enum.reject(&String.contains?(&1, "TCPIP"))
|> Enum.reject(&String.starts_with?(&1, "qA"))
# Skip empty strings, TCPIP entries and qA* entries
|> Enum.reject(fn callsign ->
callsign == "" || String.contains?(callsign, "TCPIP") || String.starts_with?(callsign, "qA")
end)
|> Enum.map(fn callsign ->
# Remove any asterisk (used to mark heard stations) and WIDE patterns
callsign
|> String.replace("*", "")
|> String.trim()
end)
# Filter out WIDE patterns (both WIDE1-1 and WIDE1 forms)
# Filter out APRS built-in beacon patterns
|> Enum.reject(fn callsign ->
String.starts_with?(callsign, "WIDE")
callsign == "" || aprs_beacon?(callsign)
end)
|> Enum.reject(&(&1 == ""))
|> Enum.uniq()
end
@ -2165,4 +2153,59 @@ defmodule AprsmeWeb.MapLive.Index do
end
defp get_path_station_positions(_, _), do: []
# Check if a callsign is an APRS built-in beacon pattern
@spec aprs_beacon?(String.t()) :: boolean()
defp aprs_beacon?(callsign) do
# Common APRS beacon patterns to exclude
patterns = [
# WIDE1, WIDE2, WIDE1-1, etc.
~r/^WIDE\d(-\d)?$/i,
# TRACE patterns
~r/^TRACE\d(-\d)?$/i,
# RELAY
~r/^RELAY$/i,
# ECHO
~r/^ECHO$/i,
# GATE
~r/^GATE$/i,
# UNPROTO
~r/^UNPROTO$/i,
# BEACON
~r/^BEACON$/i,
# CQ
~r/^CQ$/i,
# QTH
~r/^QTH$/i,
# MAIL
~r/^MAIL$/i,
# TEMP1, TEMP2, etc.
~r/^TEMP\d+$/i,
# ID
~r/^ID$/i,
# GPS
~r/^GPS$/i,
# DF
~r/^DF$/i,
# DGPS
~r/^DGPS$/i,
# PHG patterns
~r/^PHG\d+$/i,
# RNG patterns
~r/^RNG\d+$/i,
# DFS patterns
~r/^DFS\d+$/i,
# HOP patterns
~r/^HOP\d+(-\d)?$/i,
# REGION patterns
~r/^REGION\d+(-\d)?$/i,
# STATE patterns
~r/^STATE\d+(-\d)?$/i
]
# Check if the callsign matches any beacon pattern
Enum.any?(patterns, fn pattern ->
Regex.match?(pattern, callsign)
end)
end
end

View file

@ -73,15 +73,12 @@ defmodule AprsmeWeb.MapLive.RfPathTest do
test "handles paths with asterisks correctly", %{conn: conn} do
{:ok, _station} =
Repo.insert(%Packet{
create_test_packet(%{
sender: "WA5VHU-8",
base_callsign: "WA5VHU",
ssid: "8",
lat: Decimal.new("33.1500"),
lon: Decimal.new("-96.5500"),
has_position: true,
received_at: DateTime.truncate(DateTime.utc_now(), :second),
data_type: "position"
lon: Decimal.new("-96.5500")
})
{:ok, view, _html} = live(conn, "/")
@ -113,6 +110,31 @@ defmodule AprsmeWeb.MapLive.RfPathTest do
assert view.module == Index
end
test "filters out APRS beacon patterns from paths", %{conn: conn} do
# Create a real station that should be included
{:ok, _station} =
create_test_packet(%{
sender: "KC5ABC-9",
base_callsign: "KC5ABC",
ssid: "9",
lat: Decimal.new("33.2500"),
lon: Decimal.new("-96.6500")
})
{:ok, view, _html} = live(conn, "/")
# Send a hover event with various beacon patterns that should be filtered
render_hook(view, "marker_hover_start", %{
"id" => "test-beacons",
"path" => "KC5ABC-9,WIDE1-1,WIDE2-1,TRACE3-3,RELAY,ECHO,HOP7-7",
"lat" => 33.2837,
"lng" => -96.5728
})
# Should filter out all beacon patterns but keep KC5ABC-9
assert view.module == Index
end
test "handles empty paths gracefully", %{conn: conn} do
{:ok, view, _html} = live(conn, "/")
@ -138,27 +160,21 @@ defmodule AprsmeWeb.MapLive.RfPathTest do
setup do
# Create test packets with positions
{:ok, _digi1} =
Repo.insert(%Packet{
create_test_packet(%{
sender: "K5GVL-10",
base_callsign: "K5GVL",
ssid: "10",
lat: Decimal.new("33.1000"),
lon: Decimal.new("-96.6000"),
has_position: true,
received_at: DateTime.truncate(DateTime.utc_now(), :second),
data_type: "position"
lon: Decimal.new("-96.6000")
})
{:ok, _digi2} =
Repo.insert(%Packet{
create_test_packet(%{
sender: "N5TXZ-10",
base_callsign: "N5TXZ",
ssid: "10",
lat: Decimal.new("33.2000"),
lon: Decimal.new("-96.5000"),
has_position: true,
received_at: DateTime.truncate(DateTime.utc_now(), :second),
data_type: "position"
lon: Decimal.new("-96.5000")
})
:ok
@ -185,32 +201,26 @@ defmodule AprsmeWeb.MapLive.RfPathTest do
# Create test packets - one inside a small bounds, one outside
# Station inside typical Texas bounds
{:ok, _inside_station} =
Repo.insert(%Packet{
create_test_packet(%{
sender: "K5INSIDE-10",
base_callsign: "K5INSIDE",
ssid: "10",
# Inside Texas area
lat: Decimal.new("32.5000"),
# Inside Texas area
lon: Decimal.new("-96.5000"),
has_position: true,
received_at: DateTime.truncate(DateTime.utc_now(), :second),
data_type: "position"
lon: Decimal.new("-96.5000")
})
# Station outside bounds (way outside in Boston area)
{:ok, _outside_station} =
Repo.insert(%Packet{
create_test_packet(%{
sender: "W1OUTSIDE-10",
base_callsign: "W1OUTSIDE",
ssid: "10",
# Boston area - far from Texas
lat: Decimal.new("42.0000"),
# Boston area - far from Texas
lon: Decimal.new("-71.0000"),
has_position: true,
received_at: DateTime.truncate(DateTime.utc_now(), :second),
data_type: "position"
lon: Decimal.new("-71.0000")
})
:ok