fix weather

This commit is contained in:
Graham McIntire 2025-07-12 10:53:11 -05:00
parent ac415368e8
commit 4348d34f62
No known key found for this signature in database
7 changed files with 110 additions and 15 deletions

View file

@ -93,7 +93,7 @@ defmodule Aprsme.CachedQueries do
result
_ ->
result = Packets.get_latest_weather_packet_optimized(callsign)
result = Packets.get_latest_weather_packet(callsign)
# Cache for 5 minutes since weather updates are less frequent
Cachex.put(:query_cache, cache_key, result, ttl: @cache_ttl_short)
result
@ -118,7 +118,9 @@ defmodule Aprsme.CachedQueries do
query =
from p in Packet,
where: p.sender == ^callsign,
where: p.data_type == "weather" or (p.symbol_table_id == "/" and p.symbol_code == "_"),
where:
not is_nil(p.temperature) or not is_nil(p.humidity) or not is_nil(p.pressure) or
not is_nil(p.wind_speed) or not is_nil(p.wind_direction) or not is_nil(p.rain_1h),
limit: 1,
select: true

View file

@ -737,10 +737,10 @@ defmodule Aprsme.Packets do
@doc """
Gets the most recent weather packet for a callsign.
Optimized to check only recent data first before expanding the search window.
Looks for any packet containing weather data fields.
"""
@spec get_latest_weather_packet_optimized(String.t()) :: struct() | nil
def get_latest_weather_packet_optimized(callsign) when is_binary(callsign) do
@spec get_latest_weather_packet(String.t()) :: struct() | nil
def get_latest_weather_packet(callsign) when is_binary(callsign) do
# First try last 24 hours
one_day_ago = TimeUtils.one_day_ago()
@ -748,7 +748,9 @@ defmodule Aprsme.Packets do
Repo.one(
from(p in Packet,
where: p.sender == ^callsign,
where: p.data_type == "weather",
where:
not is_nil(p.temperature) or not is_nil(p.humidity) or not is_nil(p.pressure) or
not is_nil(p.wind_speed) or not is_nil(p.wind_direction) or not is_nil(p.rain_1h),
where: p.received_at >= ^one_day_ago,
order_by: [desc: p.received_at],
limit: 1
@ -763,7 +765,9 @@ defmodule Aprsme.Packets do
Repo.one(
from(p in Packet,
where: p.sender == ^callsign,
where: p.data_type == "weather",
where:
not is_nil(p.temperature) or not is_nil(p.humidity) or not is_nil(p.pressure) or
not is_nil(p.wind_speed) or not is_nil(p.wind_direction) or not is_nil(p.rain_1h),
where: p.received_at >= ^one_week_ago,
order_by: [desc: p.received_at],
limit: 1

View file

@ -76,7 +76,11 @@ defmodule Aprsme.Packets.QueryBuilder do
@spec weather_only(Ecto.Query.t()) :: Ecto.Query.t()
def weather_only(query) do
from p in query,
where: p.data_type == "weather" or (p.symbol_table_id == "/" and p.symbol_code == "_")
where:
not is_nil(p.temperature) or not is_nil(p.humidity) or not is_nil(p.pressure) or
not is_nil(p.wind_speed) or not is_nil(p.wind_direction) or not is_nil(p.rain_1h) or
not is_nil(p.rain_24h) or not is_nil(p.rain_since_midnight) or not is_nil(p.snow) or
not is_nil(p.luminosity)
end
@doc """

View file

@ -1464,7 +1464,9 @@ defmodule AprsmeWeb.MapLive.Index do
query =
from p in Aprsme.Packet,
where: fragment("UPPER(?)", p.sender) in ^normalized_callsigns,
where: p.data_type == "weather" or (p.symbol_table_id == "/" and p.symbol_code == "_"),
where:
not is_nil(p.temperature) or not is_nil(p.humidity) or not is_nil(p.pressure) or
not is_nil(p.wind_speed) or not is_nil(p.wind_direction) or not is_nil(p.rain_1h),
select: fragment("UPPER(?)", p.sender),
distinct: true

View file

@ -76,10 +76,17 @@ defmodule AprsmeWeb.MapLive.PacketUtils do
"""
@spec weather_packet?(map()) :: boolean()
def weather_packet?(packet) do
data_type = get_packet_field(packet, :data_type, "")
{symbol_table_id, symbol_code} = get_symbol_info(packet)
data_type == "weather" or (symbol_table_id == "/" and symbol_code == "_")
# Check if packet contains any weather data fields
not is_nil(get_packet_field(packet, :temperature, nil)) or
not is_nil(get_packet_field(packet, :humidity, nil)) or
not is_nil(get_packet_field(packet, :pressure, nil)) or
not is_nil(get_packet_field(packet, :wind_speed, nil)) or
not is_nil(get_packet_field(packet, :wind_direction, nil)) or
not is_nil(get_packet_field(packet, :rain_1h, nil)) or
not is_nil(get_packet_field(packet, :rain_24h, nil)) or
not is_nil(get_packet_field(packet, :rain_since_midnight, nil)) or
not is_nil(get_packet_field(packet, :snow, nil)) or
not is_nil(get_packet_field(packet, :luminosity, nil))
end
@doc """

View file

@ -74,8 +74,13 @@ defmodule AprsmeWeb.WeatherLive.CallsignView do
# Keep the old handler for backward compatibility
def handle_info({:postgres_packet, packet}, socket) do
# Only process if it's a weather packet for this callsign
if packet.sender == socket.assigns.callsign && packet.data_type == "weather" do
# Only process if it's a packet with weather data for this callsign
has_weather_data =
not is_nil(packet.temperature) or not is_nil(packet.humidity) or
not is_nil(packet.pressure) or not is_nil(packet.wind_speed) or
not is_nil(packet.wind_direction) or not is_nil(packet.rain_1h)
if packet.sender == socket.assigns.callsign && has_weather_data do
handle_info({:weather_packet, packet}, socket)
else
{:noreply, socket}

View file

@ -0,0 +1,71 @@
defmodule Aprsme.PacketWeatherExtractionTest do
use Aprsme.DataCase
alias Aprsme.Packet
describe "extract_additional_data/2 with weather data" do
test "extracts weather data from parsed APRS weather packet" do
# Simulate a real weather packet
raw_packet = "AE5PL-13>API510,DSTAR*:!3240.46N/09657.49W_225/004g009t075r000p000h61b10206Plano, TX weather"
# Parse the packet using the real APRS parser
{:ok, parsed_packet} = Aprs.parse(raw_packet)
# Create packet data as it would come from the parser
attrs = %{
base_callsign: "AE5PL",
ssid: "13",
sender: "AE5PL-13",
destination: "API510",
data_type: to_string(parsed_packet[:data_type]),
path: "DSTAR*",
information_field: "!3240.46N/09657.49W_225/004g009t075r000p000h61b10206Plano, TX weather",
data_extended: parsed_packet
}
# Extract additional data
result = Packet.extract_additional_data(attrs, raw_packet)
# Verify weather data was extracted
assert result.temperature == 75
assert result.humidity == 61
assert result.wind_direction == 225
assert result.wind_speed == 4
assert result.wind_gust == 9
assert result.pressure == 1020.6
assert result.rain_1h == 0
end
test "extracts weather data from pure weather packet type" do
# Simulate a pure weather packet (starts with _)
raw_packet = "AE5PL-WX>APRS:_07121545c220s004g009t075r000p000h61b10206"
# Parse the packet using the real APRS parser
{:ok, parsed_packet} = Aprs.parse(raw_packet)
# Create packet data as it would come from the parser
attrs = %{
base_callsign: "AE5PL",
ssid: "WX",
sender: "AE5PL-WX",
destination: "APRS",
data_type: to_string(parsed_packet[:data_type]),
path: nil,
information_field: "_07121545c220s004g009t075r000p000h61b10206",
data_extended: parsed_packet
}
# Extract additional data
result = Packet.extract_additional_data(attrs, raw_packet)
# Verify weather data was extracted
assert result.temperature == 75
assert result.humidity == 61
assert result.wind_direction == 220
assert result.wind_speed == 4
assert result.wind_gust == 9
assert result.pressure == 1020.6
assert result.rain_1h == 0
end
end
end