diff --git a/lib/aprsme/packets.ex b/lib/aprsme/packets.ex
index 0d8c191..ecd3675 100644
--- a/lib/aprsme/packets.ex
+++ b/lib/aprsme/packets.ex
@@ -404,6 +404,34 @@ defmodule Aprsme.Packets do
get_packets_for_replay(opts_with_time)
end
+ @doc """
+ Gets weather packets for a specific callsign within a time range.
+ This is optimized for weather queries by filtering at the database level.
+ """
+ @impl true
+ @spec get_weather_packets(String.t(), DateTime.t(), DateTime.t(), map()) :: [struct()]
+ def get_weather_packets(callsign, start_time, end_time, opts \\ %{}) do
+ limit = Map.get(opts, :limit, 500)
+
+ base_query = from(p in Packet, order_by: [asc: p.received_at])
+
+ query =
+ base_query
+ |> filter_by_time(%{start_time: start_time, end_time: end_time})
+ |> filter_by_callsign(%{callsign: callsign})
+ |> filter_weather_packets()
+ |> limit_results(%{limit: limit})
+ |> select_with_virtual_coordinates()
+
+ Repo.all(query)
+ end
+
+ # Filter for weather packets at the database level
+ defp filter_weather_packets(query) do
+ from p in query,
+ where: p.data_type == "weather" or (p.symbol_table_id == "/" and p.symbol_code == "_")
+ end
+
@doc """
Retrieves a continuous stream of stored packets for replay in chronological order.
diff --git a/lib/aprsme/packets_behaviour.ex b/lib/aprsme/packets_behaviour.ex
index 9d21bed..df817fc 100644
--- a/lib/aprsme/packets_behaviour.ex
+++ b/lib/aprsme/packets_behaviour.ex
@@ -7,6 +7,7 @@ defmodule Aprsme.PacketsBehaviour do
@callback stream_packets_for_replay(map()) :: Enumerable.t()
@callback get_packets_for_replay(map()) :: list()
@callback get_recent_packets(map()) :: list()
+ @callback get_weather_packets(String.t(), DateTime.t(), DateTime.t(), map()) :: list()
@callback clean_old_packets() :: {:ok, non_neg_integer()} | {:error, any()}
@callback clean_packets_older_than(pos_integer()) :: {:ok, non_neg_integer()} | {:error, any()}
end
diff --git a/lib/aprsme_web/live/weather_live/callsign_view.ex b/lib/aprsme_web/live/weather_live/callsign_view.ex
index efa82a2..cc808b6 100644
--- a/lib/aprsme_web/live/weather_live/callsign_view.ex
+++ b/lib/aprsme_web/live/weather_live/callsign_view.ex
@@ -2,8 +2,6 @@ defmodule AprsmeWeb.WeatherLive.CallsignView do
@moduledoc false
use AprsmeWeb, :live_view
- import Jason
-
alias Aprsme.Packets
alias AprsmeWeb.MapLive.PacketUtils
@@ -57,59 +55,12 @@ defmodule AprsmeWeb.WeatherLive.CallsignView do
end
defp get_weather_history(callsign, start_time, end_time) do
- opts = %{callsign: callsign, start_time: start_time, end_time: end_time, limit: 1000}
-
- opts
- |> Packets.get_packets_for_replay()
- |> Enum.filter(&PacketUtils.weather_packet?/1)
- |> Enum.sort_by(& &1.received_at)
+ Packets.get_weather_packets(callsign, start_time, end_time, %{limit: 500})
end
defp default_time_range do
now = DateTime.utc_now()
- {DateTime.add(now, -48 * 3600, :second), now}
- end
-
- defp build_temp_chart_svg(history) do
- data =
- history
- |> Enum.map(fn pkt ->
- [pkt.received_at, pkt.temperature, pkt.dew_point]
- end)
- |> Enum.filter(fn [_, t, d] -> not is_nil(t) and not is_nil(d) end)
-
- if data == [] do
- ""
- else
- dataset = Dataset.new(data, ["Time", "Temperature", "Dew Point"])
-
- dataset
- |> Plot.new(LinePlot, 600, 300, smoothed: false)
- |> Plot.titles("Temperature & Dew Point (°F)", nil)
- |> Plot.axis_labels("Time", "°F")
- |> Plot.to_svg()
- end
- end
-
- defp build_humidity_chart_svg(history) do
- data =
- history
- |> Enum.map(fn pkt ->
- [pkt.received_at, pkt.humidity]
- end)
- |> Enum.filter(fn [_, h] -> not is_nil(h) end)
-
- if data == [] do
- ""
- else
- dataset = Dataset.new(data, ["Time", "Humidity"])
-
- dataset
- |> Plot.new(LinePlot, 600, 300, smoothed: false)
- |> Plot.titles("Humidity (%)", nil)
- |> Plot.axis_labels("Time", "%")
- |> Plot.to_svg()
- end
+ {DateTime.add(now, -24 * 3600, :second), now}
end
defp calc_dew_point(temp, humidity) when is_number(temp) and is_number(humidity) do
diff --git a/priv/repo/migrations/20250703180500_add_sender_index_to_packets.exs b/priv/repo/migrations/20250703180500_add_sender_index_to_packets.exs
new file mode 100644
index 0000000..be505f2
--- /dev/null
+++ b/priv/repo/migrations/20250703180500_add_sender_index_to_packets.exs
@@ -0,0 +1,14 @@
+defmodule Aprsme.Repo.Migrations.AddSenderIndexToPackets do
+ use Ecto.Migration
+
+ def change do
+ # Add index on sender field for efficient callsign filtering
+ create index(:packets, [:sender])
+
+ # Add compound index for sender + received_at for efficient time-based callsign queries
+ create index(:packets, [:sender, :received_at])
+
+ # Add compound index for sender + has_position for weather packet queries
+ create index(:packets, [:sender, :has_position])
+ end
+end