Strip weather data, RNG, and negative altitudes from packet comments

Weather packets had raw data strings (e.g. 007/000g000t054...)
stored as the comment instead of the trailing human-readable text.
Now iteratively strips fixed-width APRS weather fields in any order,
handling both position-included (XXX/XXX) and positionless (cXXXsXXX)
formats. Also strips RNG data and fixes altitude regex to handle
negative values. Empty comments after cleaning return nil.
This commit is contained in:
Graham McIntire 2026-02-20 13:15:13 -06:00
parent 2305635a30
commit ddf26a99ce
No known key found for this signature in database
2 changed files with 170 additions and 1 deletions

View file

@ -750,15 +750,66 @@ defmodule Aprsme.Packet do
defp clean_comment(comment) when is_binary(comment) do
comment
|> strip_weather_data()
# Remove altitude
|> String.replace(~r/\s*\/A=\d{6}/, "")
|> String.replace(~r/\s*\/A=-?\d{5,6}/, "")
# Remove PHG
|> String.replace(~r/PHG\d{4}\s*/, "")
# Remove RNG
|> String.replace(~r/RNG\d{4}\s*/, "")
|> String.trim()
|> case do
"" -> nil
cleaned -> cleaned
end
end
defp clean_comment(comment), do: comment
# APRS weather fields: each is a letter prefix + fixed number of digits/spaces
@wx_field_patterns [
~r/^g[\d .]{3}/,
~r/^t[\d .-]{3}/,
~r/^r[\d .]{3}/,
~r/^p[\d .]{3}/,
~r/^P[\d .]{3}/,
~r/^h[\d .]{2}/,
~r/^b[\d .]{5}/,
~r/^s[\d .]{3}/,
~r/^[Ll][\d .]{3}/
]
# Matches position-included format (XXX/XXX) and positionless format (cXXXsXXX)
@wx_preamble_pattern ~r/^_?(?:[\d .]{3}\/[\d .]{3}|c[\d .]{3}s[\d .]{3})/
defp strip_weather_data(comment) do
case Regex.run(@wx_preamble_pattern, comment) do
[match] ->
comment
|> String.slice(String.length(match)..-1//1)
|> strip_wx_fields()
nil ->
comment
end
end
defp strip_wx_fields(str) do
case match_wx_field(str) do
nil -> str
match -> str |> String.slice(String.length(match)..-1//1) |> strip_wx_fields()
end
end
defp match_wx_field(str) do
Enum.find_value(@wx_field_patterns, fn pat ->
case Regex.run(pat, str) do
[match] -> match
nil -> nil
end
end)
end
# Extract altitude from APRS comment field (e.g., "/A=000680" means 680 feet)
defp extract_altitude_from_comment(nil), do: nil

View file

@ -91,4 +91,122 @@ defmodule Aprsme.PacketTest do
assert get_in(result, [:data, "telemetry_bits"]) == nil
end
end
describe "comment cleaning" do
test "strips weather data from weather packet comments" do
attrs = %{
sender: "TEST",
data_type: "weather",
data_extended: %{
comment: "007/000g000t054r000p001P001b10052h90eMB61",
data_type: :weather,
wx: %{temperature: 54, humidity: 90, wind_speed: 0},
latitude: 36.124,
longitude: -75.723
}
}
result = Packet.extract_additional_data(attrs, "test_packet")
assert result[:comment] == "eMB61"
end
test "strips weather data with luminosity and preserves trailing comment" do
attrs = %{
sender: "TEST",
data_type: "weather",
data_extended: %{
comment: "225/004g009t075r000p000h61b10206Plano, TX weather",
data_type: :weather,
wx: %{temperature: 75, humidity: 61, wind_speed: 4},
latitude: 32.0,
longitude: -96.0
}
}
result = Packet.extract_additional_data(attrs, "test_packet")
assert result[:comment] == "Plano, TX weather"
end
test "strips weather data with snow and luminosity fields" do
attrs = %{
sender: "TEST",
data_type: "weather",
data_extended: %{
comment: "082/002g005t074r000p000P000b10099h58L708eMB63",
data_type: :weather,
wx: %{temperature: 74, humidity: 58, wind_speed: 2},
latitude: 30.0,
longitude: -90.0
}
}
result = Packet.extract_additional_data(attrs, "test_packet")
assert result[:comment] == "eMB63"
end
test "handles weather comment that is all weather data" do
attrs = %{
sender: "TEST",
data_type: "weather",
data_extended: %{
comment: "_180/010g015t072r000p000h45b10132s005",
data_type: :weather,
wx: %{temperature: 72, humidity: 45, wind_speed: 10},
latitude: 32.0,
longitude: -96.0
}
}
result = Packet.extract_additional_data(attrs, "test_packet")
assert result[:comment] in [nil, ""]
end
test "strips positionless weather format (cXXXsXXX)" do
attrs = %{
sender: "TEST",
data_type: "weather",
data_extended: %{
comment: "c011s000g000t077r000P000L000h95b10080GW2000C MaxGust:1.1mph",
data_type: :weather,
wx: %{temperature: 77, humidity: 95},
latitude: 6.0,
longitude: 79.0
}
}
result = Packet.extract_additional_data(attrs, "test_packet")
assert result[:comment] == "GW2000C MaxGust:1.1mph"
end
test "strips RNG data from comments" do
attrs = %{
sender: "TEST",
data_type: "position",
data_extended: %{
comment: "RNG0050 2m Voice 145.57500MHz",
latitude: 61.0,
longitude: 14.0,
radiorange: "0050"
}
}
result = Packet.extract_additional_data(attrs, "test_packet")
assert result[:comment] == "2m Voice 145.57500MHz"
end
test "does not modify non-weather, non-RNG comments" do
attrs = %{
sender: "TEST",
data_type: "position",
data_extended: %{
comment: "70cm Voice (D-Star) 447.12500MHz -3.0000MHz",
latitude: 46.0,
longitude: -94.0
}
}
result = Packet.extract_additional_data(attrs, "test_packet")
assert result[:comment] == "70cm Voice (D-Star) 447.12500MHz -3.0000MHz"
end
end
end