add more device ids
This commit is contained in:
parent
369ce05f1a
commit
5c171e6acb
6 changed files with 226 additions and 41 deletions
|
|
@ -13,13 +13,6 @@ config :aprs, AprsWeb.Endpoint, cache_static_manifest: "priv/static/cache_manife
|
||||||
|
|
||||||
# Runtime production configuration, including reading
|
# Runtime production configuration, including reading
|
||||||
|
|
||||||
# Do not print debug messages in production
|
|
||||||
# of environment variables, is done on config/runtime.exs.
|
|
||||||
config :logger, level: :info
|
|
||||||
|
|
||||||
# Configures Swoosh API Client
|
|
||||||
config :swoosh, :api_client, Aprs.Finch
|
|
||||||
|
|
||||||
config :esbuild,
|
config :esbuild,
|
||||||
version: "0.17.11",
|
version: "0.17.11",
|
||||||
default: [
|
default: [
|
||||||
|
|
@ -27,3 +20,10 @@ config :esbuild,
|
||||||
cd: Path.expand("../assets", __DIR__),
|
cd: Path.expand("../assets", __DIR__),
|
||||||
env: %{"NODE_PATH" => Path.expand("../deps", __DIR__)}
|
env: %{"NODE_PATH" => Path.expand("../deps", __DIR__)}
|
||||||
]
|
]
|
||||||
|
|
||||||
|
# Do not print debug messages in production
|
||||||
|
# of environment variables, is done on config/runtime.exs.
|
||||||
|
config :logger, level: :info
|
||||||
|
|
||||||
|
# Configures Swoosh API Client
|
||||||
|
config :swoosh, :api_client, Aprs.Finch
|
||||||
|
|
|
||||||
80
lib/aprs/device_identification.ex
Normal file
80
lib/aprs/device_identification.ex
Normal file
|
|
@ -0,0 +1,80 @@
|
||||||
|
defmodule Aprs.DeviceIdentification do
|
||||||
|
@moduledoc """
|
||||||
|
Handles APRS device identification based on the APRS device identification database.
|
||||||
|
"""
|
||||||
|
|
||||||
|
@doc """
|
||||||
|
Identifies the manufacturer and model of an APRS device based on its symbol pattern.
|
||||||
|
Returns a tuple of {manufacturer, model} or "Unknown" if the device cannot be identified.
|
||||||
|
"""
|
||||||
|
@spec identify_device(String.t()) :: String.t()
|
||||||
|
def identify_device(symbols) do
|
||||||
|
case symbols do
|
||||||
|
# Original MIC-E devices
|
||||||
|
" " <> <<0, 0>> -> "Original MIC-E"
|
||||||
|
# Kenwood devices
|
||||||
|
">" <> <<0>> <> "^" -> "Kenwood TH-D74"
|
||||||
|
">" <> <<0, 0>> -> "Kenwood TH-D74A"
|
||||||
|
"]" <> <<0>> <> "=" -> "Kenwood DM-710"
|
||||||
|
"]" <> <<0, 0>> -> "Kenwood DM-700"
|
||||||
|
# Yaesu devices
|
||||||
|
"`_" <> " " -> "Yaesu VX-8"
|
||||||
|
"`_" <> "\"" -> "Yaesu FTM-350"
|
||||||
|
"`_" <> "#" -> "Yaesu VX-8G"
|
||||||
|
"`_" <> "$" -> "Yaesu FT1D"
|
||||||
|
"`_" <> "%" -> "Yaesu FTM-400DR"
|
||||||
|
"`_" <> ")" -> "Yaesu FTM-100D"
|
||||||
|
"`_" <> "(" -> "Yaesu FT2D"
|
||||||
|
# Other devices
|
||||||
|
"` X" -> "AP510"
|
||||||
|
"`" <> <<0, 0>> -> "Mic-Emsg"
|
||||||
|
"'|3" -> "Byonics TinyTrack3"
|
||||||
|
"'|4" -> "Byonics TinyTrack4"
|
||||||
|
"':4" -> "SCS GmbH & Co. P4dragon DR-7400 modems"
|
||||||
|
"':8" -> "SCS GmbH & Co. P4dragon DR-7800 modems"
|
||||||
|
"'" <> <<0, 0>> -> "McTrackr"
|
||||||
|
<<0>> <> "\"" <> <<0>> -> "Hamhud"
|
||||||
|
<<0>> <> "/" <> <<0>> -> "Argent"
|
||||||
|
<<0>> <> "^" <> <<0>> -> "HinzTec anyfrog"
|
||||||
|
<<0>> <> "*" <> <<0>> -> "APOZxx www.KissOZ.dk Tracker. OZ1EKD and OZ7HVO"
|
||||||
|
<<0>> <> "~" <> <<0>> -> "Other"
|
||||||
|
# Unknown devices
|
||||||
|
_ -> "Unknown"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
@doc """
|
||||||
|
Returns a list of all known device manufacturers.
|
||||||
|
"""
|
||||||
|
@spec known_manufacturers() :: [String.t()]
|
||||||
|
def known_manufacturers do
|
||||||
|
[
|
||||||
|
"Original MIC-E",
|
||||||
|
"Kenwood",
|
||||||
|
"Yaesu",
|
||||||
|
"AP510",
|
||||||
|
"Byonics",
|
||||||
|
"SCS GmbH & Co.",
|
||||||
|
"McTrackr",
|
||||||
|
"Hamhud",
|
||||||
|
"Argent",
|
||||||
|
"HinzTec",
|
||||||
|
"APOZxx",
|
||||||
|
"Other"
|
||||||
|
]
|
||||||
|
end
|
||||||
|
|
||||||
|
@doc """
|
||||||
|
Returns a list of all known device models for a given manufacturer.
|
||||||
|
"""
|
||||||
|
@spec known_models(String.t()) :: [String.t()]
|
||||||
|
def known_models(manufacturer) do
|
||||||
|
case manufacturer do
|
||||||
|
"Kenwood" -> ["TH-D74", "TH-D74A", "DM-710", "DM-700"]
|
||||||
|
"Yaesu" -> ["VX-8", "FTM-350", "VX-8G", "FT1D", "FTM-400DR", "FTM-100D", "FT2D"]
|
||||||
|
"Byonics" -> ["TinyTrack3", "TinyTrack4"]
|
||||||
|
"SCS GmbH & Co." -> ["P4dragon DR-7400 modems", "P4dragon DR-7800 modems"]
|
||||||
|
_ -> []
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
@ -9,8 +9,16 @@
|
||||||
</.live_title>
|
</.live_title>
|
||||||
<link phx-track-static rel="stylesheet" href={~p"/assets/app.css"} />
|
<link phx-track-static rel="stylesheet" href={~p"/assets/app.css"} />
|
||||||
<link phx-track-static rel="stylesheet" href={~p"/assets/vendor/leaflet/leaflet.css"} />
|
<link phx-track-static rel="stylesheet" href={~p"/assets/vendor/leaflet/leaflet.css"} />
|
||||||
<script defer phx-track-static type="text/javascript" src={~p"/assets/vendor/leaflet/leaflet.js"} data-source-map="false"></script>
|
<script
|
||||||
<script defer phx-track-static type="text/javascript" src={~p"/assets/app.js"}></script>
|
defer
|
||||||
|
phx-track-static
|
||||||
|
type="text/javascript"
|
||||||
|
src={~p"/assets/vendor/leaflet/leaflet.js"}
|
||||||
|
data-source-map="false"
|
||||||
|
>
|
||||||
|
</script>
|
||||||
|
<script defer phx-track-static type="text/javascript" src={~p"/assets/app.js"}>
|
||||||
|
</script>
|
||||||
</head>
|
</head>
|
||||||
<body class={body_class(assigns)}>
|
<body class={body_class(assigns)}>
|
||||||
{@inner_content}
|
{@inner_content}
|
||||||
|
|
|
||||||
|
|
@ -644,31 +644,9 @@ defmodule Parser do
|
||||||
end
|
end
|
||||||
|
|
||||||
@spec parse_manufacturer(binary()) :: String.t()
|
@spec parse_manufacturer(binary()) :: String.t()
|
||||||
def parse_manufacturer(<<" ", _::binary-size(2)>>), do: "Original MIC-E"
|
def parse_manufacturer(symbols) do
|
||||||
def parse_manufacturer(<<">", _, "^">>), do: "Kenwood TH-D74"
|
Aprs.DeviceIdentification.identify_device(symbols)
|
||||||
def parse_manufacturer(<<">", _::binary-size(2)>>), do: "Kenwood TH-D74A"
|
end
|
||||||
def parse_manufacturer(<<"]", _, "=">>), do: "Kenwood DM-710"
|
|
||||||
def parse_manufacturer(<<"]", _::binary-size(2)>>), do: "Kenwood DM-700"
|
|
||||||
def parse_manufacturer(<<"`", "_", " ">>), do: "Yaesu VX-8"
|
|
||||||
def parse_manufacturer(<<"`", "_", "\"">>), do: "Yaesu FTM-350"
|
|
||||||
def parse_manufacturer(<<"`", "_", "#">>), do: "Yaesu VX-8G"
|
|
||||||
def parse_manufacturer(<<"`", "_", "$">>), do: "Yaesu FT1D"
|
|
||||||
def parse_manufacturer(<<"`", "_", "%">>), do: "Yaesu FTM-400DR"
|
|
||||||
def parse_manufacturer(<<"`", "_", ")">>), do: "Yaesu FTM-100D"
|
|
||||||
def parse_manufacturer(<<"`", "_", "(">>), do: "Yaesu FT2D"
|
|
||||||
def parse_manufacturer(<<"`", " ", "X">>), do: "AP510"
|
|
||||||
def parse_manufacturer(<<"`", _::binary-size(2)>>), do: "Mic-Emsg"
|
|
||||||
def parse_manufacturer(<<"'", "|", "3">>), do: "Byonics TinyTrack3"
|
|
||||||
def parse_manufacturer(<<"'", "|", "4">>), do: "Byonics TinyTrack4"
|
|
||||||
def parse_manufacturer(<<"'", ":", "4">>), do: "SCS GmbH & Co. P4dragon DR-7400 modems"
|
|
||||||
def parse_manufacturer(<<"'", ":", "8">>), do: "SCS GmbH & Co. P4dragon DR-7800 modems"
|
|
||||||
def parse_manufacturer(<<"'", _::binary-size(2)>>), do: "McTrackr"
|
|
||||||
def parse_manufacturer(<<_, "\"", _>>), do: "Hamhud ?"
|
|
||||||
def parse_manufacturer(<<_, "/", _>>), do: "Argent ?"
|
|
||||||
def parse_manufacturer(<<_, "^", _>>), do: "HinzTec anyfrog"
|
|
||||||
def parse_manufacturer(<<_, "*", _>>), do: "APOZxx www.KissOZ.dk Tracker. OZ1EKD and OZ7HVO"
|
|
||||||
def parse_manufacturer(<<_, "~", _>>), do: "Other"
|
|
||||||
def parse_manufacturer(_), do: "Unknown"
|
|
||||||
|
|
||||||
@spec find_matches(Regex.t(), String.t()) :: [String.t()]
|
@spec find_matches(Regex.t(), String.t()) :: [String.t()]
|
||||||
defp find_matches(regex, text) do
|
defp find_matches(regex, text) do
|
||||||
|
|
|
||||||
92
test/aprs/device_identification_test.exs
Normal file
92
test/aprs/device_identification_test.exs
Normal file
|
|
@ -0,0 +1,92 @@
|
||||||
|
defmodule Aprs.DeviceIdentificationTest do
|
||||||
|
use ExUnit.Case
|
||||||
|
|
||||||
|
alias Aprs.DeviceIdentification
|
||||||
|
|
||||||
|
describe "identify_device/1" do
|
||||||
|
test "identifies Original MIC-E devices" do
|
||||||
|
assert DeviceIdentification.identify_device(" " <> <<0, 0>>) == "Original MIC-E"
|
||||||
|
end
|
||||||
|
|
||||||
|
test "identifies Kenwood devices" do
|
||||||
|
assert DeviceIdentification.identify_device(">" <> <<0>> <> "^") == "Kenwood TH-D74"
|
||||||
|
assert DeviceIdentification.identify_device(">" <> <<0, 0>>) == "Kenwood TH-D74A"
|
||||||
|
assert DeviceIdentification.identify_device("]" <> <<0>> <> "=") == "Kenwood DM-710"
|
||||||
|
assert DeviceIdentification.identify_device("]" <> <<0, 0>>) == "Kenwood DM-700"
|
||||||
|
end
|
||||||
|
|
||||||
|
test "identifies Yaesu devices" do
|
||||||
|
assert DeviceIdentification.identify_device("`_" <> " ") == "Yaesu VX-8"
|
||||||
|
assert DeviceIdentification.identify_device("`_" <> "\"") == "Yaesu FTM-350"
|
||||||
|
assert DeviceIdentification.identify_device("`_" <> "#") == "Yaesu VX-8G"
|
||||||
|
assert DeviceIdentification.identify_device("`_" <> "$") == "Yaesu FT1D"
|
||||||
|
assert DeviceIdentification.identify_device("`_" <> "%") == "Yaesu FTM-400DR"
|
||||||
|
assert DeviceIdentification.identify_device("`_" <> ")") == "Yaesu FTM-100D"
|
||||||
|
assert DeviceIdentification.identify_device("`_" <> "(") == "Yaesu FT2D"
|
||||||
|
end
|
||||||
|
|
||||||
|
test "identifies other devices" do
|
||||||
|
assert DeviceIdentification.identify_device("` X") == "AP510"
|
||||||
|
assert DeviceIdentification.identify_device("`" <> <<0, 0>>) == "Mic-Emsg"
|
||||||
|
assert DeviceIdentification.identify_device("'|3") == "Byonics TinyTrack3"
|
||||||
|
assert DeviceIdentification.identify_device("'|4") == "Byonics TinyTrack4"
|
||||||
|
|
||||||
|
assert DeviceIdentification.identify_device("':4") ==
|
||||||
|
"SCS GmbH & Co. P4dragon DR-7400 modems"
|
||||||
|
|
||||||
|
assert DeviceIdentification.identify_device("':8") ==
|
||||||
|
"SCS GmbH & Co. P4dragon DR-7800 modems"
|
||||||
|
|
||||||
|
assert DeviceIdentification.identify_device("'" <> <<0, 0>>) == "McTrackr"
|
||||||
|
assert DeviceIdentification.identify_device(<<0>> <> "\"" <> <<0>>) == "Hamhud"
|
||||||
|
assert DeviceIdentification.identify_device(<<0>> <> "/" <> <<0>>) == "Argent"
|
||||||
|
assert DeviceIdentification.identify_device(<<0>> <> "^" <> <<0>>) == "HinzTec anyfrog"
|
||||||
|
|
||||||
|
assert DeviceIdentification.identify_device(<<0>> <> "*" <> <<0>>) ==
|
||||||
|
"APOZxx www.KissOZ.dk Tracker. OZ1EKD and OZ7HVO"
|
||||||
|
|
||||||
|
assert DeviceIdentification.identify_device(<<0>> <> "~" <> <<0>>) == "Other"
|
||||||
|
end
|
||||||
|
|
||||||
|
test "returns Unknown for unidentified devices" do
|
||||||
|
assert DeviceIdentification.identify_device(<<0, 0, 0>>) == "Unknown"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe "known_manufacturers/0" do
|
||||||
|
test "returns list of known manufacturers" do
|
||||||
|
manufacturers = DeviceIdentification.known_manufacturers()
|
||||||
|
assert is_list(manufacturers)
|
||||||
|
assert "Kenwood" in manufacturers
|
||||||
|
assert "Yaesu" in manufacturers
|
||||||
|
assert "Byonics" in manufacturers
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe "known_models/1" do
|
||||||
|
test "returns list of known models for Kenwood" do
|
||||||
|
models = DeviceIdentification.known_models("Kenwood")
|
||||||
|
assert is_list(models)
|
||||||
|
assert "TH-D74" in models
|
||||||
|
assert "TH-D74A" in models
|
||||||
|
assert "DM-710" in models
|
||||||
|
assert "DM-700" in models
|
||||||
|
end
|
||||||
|
|
||||||
|
test "returns list of known models for Yaesu" do
|
||||||
|
models = DeviceIdentification.known_models("Yaesu")
|
||||||
|
assert is_list(models)
|
||||||
|
assert "VX-8" in models
|
||||||
|
assert "FTM-350" in models
|
||||||
|
assert "VX-8G" in models
|
||||||
|
assert "FT1D" in models
|
||||||
|
assert "FTM-400DR" in models
|
||||||
|
assert "FTM-100D" in models
|
||||||
|
assert "FT2D" in models
|
||||||
|
end
|
||||||
|
|
||||||
|
test "returns empty list for unknown manufacturer" do
|
||||||
|
assert DeviceIdentification.known_models("Unknown") == []
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
@ -297,16 +297,34 @@ defmodule Parser.ParserTest do
|
||||||
|
|
||||||
test "handles various timestamp errors" do
|
test "handles various timestamp errors" do
|
||||||
# Invalid hour (>23)
|
# Invalid hour (>23)
|
||||||
result = Parser.parse_position_with_timestamp(false, "252345z4903.50N/07201.75W>", :timestamped_position)
|
result =
|
||||||
|
Parser.parse_position_with_timestamp(
|
||||||
|
false,
|
||||||
|
"252345z4903.50N/07201.75W>",
|
||||||
|
:timestamped_position
|
||||||
|
)
|
||||||
|
|
||||||
assert result.data_type == :position
|
assert result.data_type == :position
|
||||||
assert result.time =~ "252345"
|
assert result.time =~ "252345"
|
||||||
|
|
||||||
# Invalid minute (>59)
|
# Invalid minute (>59)
|
||||||
result = Parser.parse_position_with_timestamp(false, "096045z4903.50N/07201.75W>", :timestamped_position)
|
result =
|
||||||
|
Parser.parse_position_with_timestamp(
|
||||||
|
false,
|
||||||
|
"096045z4903.50N/07201.75W>",
|
||||||
|
:timestamped_position
|
||||||
|
)
|
||||||
|
|
||||||
assert result.data_type == :position
|
assert result.data_type == :position
|
||||||
|
|
||||||
# Invalid second (>59)
|
# Invalid second (>59)
|
||||||
result = Parser.parse_position_with_timestamp(false, "092361z4903.50N/07201.75W>", :timestamped_position)
|
result =
|
||||||
|
Parser.parse_position_with_timestamp(
|
||||||
|
false,
|
||||||
|
"092361z4903.50N/07201.75W>",
|
||||||
|
:timestamped_position
|
||||||
|
)
|
||||||
|
|
||||||
assert result.data_type == :position
|
assert result.data_type == :position
|
||||||
|
|
||||||
# Wrong format/length
|
# Wrong format/length
|
||||||
|
|
@ -316,7 +334,13 @@ defmodule Parser.ParserTest do
|
||||||
|
|
||||||
test "handles MDHM timestamp format errors" do
|
test "handles MDHM timestamp format errors" do
|
||||||
# Invalid MDHM format
|
# Invalid MDHM format
|
||||||
result = Parser.parse_position_with_timestamp(false, "9999999/4903.50N/07201.75W>", :timestamped_position)
|
result =
|
||||||
|
Parser.parse_position_with_timestamp(
|
||||||
|
false,
|
||||||
|
"9999999/4903.50N/07201.75W>",
|
||||||
|
:timestamped_position
|
||||||
|
)
|
||||||
|
|
||||||
assert result.data_type == :position
|
assert result.data_type == :position
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
@ -475,10 +499,13 @@ defmodule Parser.ParserTest do
|
||||||
%{matcher: "':4", result: "SCS GmbH & Co. P4dragon DR-7400 modems"},
|
%{matcher: "':4", result: "SCS GmbH & Co. P4dragon DR-7400 modems"},
|
||||||
%{matcher: "':8", result: "SCS GmbH & Co. P4dragon DR-7800 modems"},
|
%{matcher: "':8", result: "SCS GmbH & Co. P4dragon DR-7800 modems"},
|
||||||
%{matcher: "'" <> <<0, 0>>, result: "McTrackr"},
|
%{matcher: "'" <> <<0, 0>>, result: "McTrackr"},
|
||||||
%{matcher: <<0>> <> "\"" <> <<0>>, result: "Hamhud ?"},
|
%{matcher: <<0>> <> "\"" <> <<0>>, result: "Hamhud"},
|
||||||
%{matcher: <<0>> <> "/" <> <<0>>, result: "Argent ?"},
|
%{matcher: <<0>> <> "/" <> <<0>>, result: "Argent"},
|
||||||
%{matcher: <<0>> <> "^" <> <<0>>, result: "HinzTec anyfrog"},
|
%{matcher: <<0>> <> "^" <> <<0>>, result: "HinzTec anyfrog"},
|
||||||
%{matcher: <<0>> <> "*" <> <<0>>, result: "APOZxx www.KissOZ.dk Tracker. OZ1EKD and OZ7HVO"},
|
%{
|
||||||
|
matcher: <<0>> <> "*" <> <<0>>,
|
||||||
|
result: "APOZxx www.KissOZ.dk Tracker. OZ1EKD and OZ7HVO"
|
||||||
|
},
|
||||||
%{matcher: <<0>> <> "~" <> <<0>>, result: "Other"},
|
%{matcher: <<0>> <> "~" <> <<0>>, result: "Other"},
|
||||||
%{matcher: <<0, 0, 0>>, result: "Unknown"}
|
%{matcher: <<0, 0, 0>>, result: "Unknown"}
|
||||||
],
|
],
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue