diff --git a/config/runtime.exs b/config/runtime.exs
index aaee077..ca16b19 100644
--- a/config/runtime.exs
+++ b/config/runtime.exs
@@ -19,10 +19,6 @@ import Config
# Always start the server in production/docker environments
if System.get_env("PHX_SERVER") || config_env() == :prod do
config :aprs, AprsWeb.Endpoint, server: true
-
- IO.puts("Phoenix server enabled - will start HTTP listener")
-else
- IO.puts("Phoenix server disabled - no HTTP listener will start")
end
if config_env() == :prod do
@@ -49,7 +45,6 @@ if config_env() == :prod do
host = System.get_env("PHX_HOST") || "example.com"
port = String.to_integer(System.get_env("PORT") || "4000")
- IO.puts("Phoenix will bind to port: #{port}")
config :aprs, Aprs.Repo,
# ssl: true,
@@ -71,8 +66,6 @@ if config_env() == :prod do
secret_key_base: secret_key_base,
server: true
- IO.puts("Phoenix endpoint configured for host: #{host}, port: #{port}")
-
# config :aprs, :dns_cluster_query, System.get_env("DNS_CLUSTER_QUERY")
config :aprs,
diff --git a/lib/aprs/db_test.ex b/lib/aprs/db_test.ex
deleted file mode 100644
index 8144a35..0000000
--- a/lib/aprs/db_test.ex
+++ /dev/null
@@ -1,157 +0,0 @@
-defmodule Aprs.DbTest do
- @moduledoc """
- Helper module for testing database connections and operations from IEx.
- Run with:
- iex> Aprs.DbTest.test_packet_storage()
- """
-
- alias Aprs.Packet
- alias Aprs.Packets
- alias Aprs.Repo
-
- require Logger
-
- @doc """
- Tests packet storage functionality by creating and storing a test packet.
- """
- def test_packet_storage do
- IO.puts("Starting database test...")
-
- # Test database connection
- case check_db_connection() do
- :ok ->
- # Create a test packet with minimum required fields
- test_packet = %{
- base_callsign: "TEST",
- ssid: "1",
- sender: "TEST-1",
- destination: "APRS",
- data_type: "position",
- path: "TCPIP*",
- information_field: "Test packet",
- received_at: DateTime.utc_now(),
- lat: 33.5,
- lon: -97.5,
- region: "test",
- has_position: true,
- data_extended: %{
- latitude: 33.5,
- longitude: -97.5,
- symbol_table_id: "/",
- symbol_code: ">",
- comment: "Test comment",
- aprs_messaging: false
- }
- }
-
- IO.puts("Created test packet: #{inspect(test_packet, pretty: true)}")
-
- # Attempt to store the packet
- case Packets.store_packet(test_packet) do
- {:ok, stored_packet} ->
- {:ok, stored_packet}
-
- {:error, :storage_exception} ->
- IO.puts("Failed to store packet! (storage exception)")
- {:error, :storage_exception}
-
- {:error, :validation_error} ->
- IO.puts("Failed to store packet! (validation error)")
- {:error, :validation_error}
-
- {:error, other_error} ->
- IO.puts("Failed to store packet!")
- IO.puts("Error: #{inspect(other_error)}")
- {:error, other_error}
- end
-
- error ->
- IO.puts("Database connection test failed: #{inspect(error)}")
- error
- end
- end
-
- @doc """
- Tests if the database is accessible.
- """
- def check_db_connection do
- IO.puts("Checking database connection...")
-
- try do
- # Try to get a connection from the pool
- Repo.checkout(fn conn ->
- IO.puts("Got database connection!")
- Postgrex.query!(conn, "SELECT 1", [])
- end)
-
- # Try a simple query through Ecto
- count = Repo.aggregate(Packet, :count, :id)
- IO.puts("Current packet count in database: #{count}")
-
- :ok
- rescue
- e ->
- IO.puts("Database connection error: #{inspect(e)}")
- {:error, e}
- end
- end
-
- @doc """
- Count packets with position data.
- """
- def count_packets_with_position do
- import Ecto.Query
-
- IO.puts("Counting packets with position data...")
-
- try do
- count = Repo.aggregate(from(p in Packet, where: p.has_position == true), :count, :id)
-
- IO.puts("Found #{count} packets with position data")
- {:ok, count}
- rescue
- e ->
- IO.puts("Query error: #{inspect(e)}")
- {:error, e}
- end
- end
-
- @doc """
- List the most recent packets with position data.
- """
- def list_recent_packets(limit \\ 10) do
- import Ecto.Query
-
- IO.puts("Fetching #{limit} most recent packets...")
-
- try do
- packets =
- Repo.all(
- from(p in Packet,
- where: p.has_position == true,
- order_by: [desc: p.received_at],
- limit: ^limit,
- select: %{
- p
- | lat: fragment("ST_Y(?)", p.location),
- lon: fragment("ST_X(?)", p.location)
- }
- )
- )
-
- IO.puts("Found #{length(packets)} recent packets")
-
- if length(packets) > 0 do
- Enum.each(packets, fn packet ->
- IO.puts("#{packet.sender} at #{packet.lat}, #{packet.lon} - #{packet.received_at}")
- end)
- end
-
- {:ok, packets}
- rescue
- e ->
- IO.puts("Query error: #{inspect(e)}")
- {:error, e}
- end
- end
-end
diff --git a/lib/aprs_web/live/map_live/enhanced.ex b/lib/aprs_web/live/map_live/enhanced.ex
index 3508c3a..a0943a8 100644
--- a/lib/aprs_web/live/map_live/enhanced.ex
+++ b/lib/aprs_web/live/map_live/enhanced.ex
@@ -79,12 +79,9 @@ defmodule AprsWeb.MapLive.Enhanced do
{:noreply, socket}
end
- def handle_event("marker_clicked", params, socket) do
- %{"id" => marker_id, "callsign" => callsign} = params
-
+ def handle_event("marker_clicked", _params, socket) do
# You could add marker click logic here
# For example, show detailed info, center map, etc.
- IO.puts("Marker clicked: #{callsign} (#{marker_id})")
{:noreply, socket}
end
diff --git a/lib/aprs_web/live/map_live/index.ex b/lib/aprs_web/live/map_live/index.ex
index f0c7bc8..de3a1ad 100644
--- a/lib/aprs_web/live/map_live/index.ex
+++ b/lib/aprs_web/live/map_live/index.ex
@@ -63,10 +63,6 @@ defmodule AprsWeb.MapLive.Index do
defp maybe_start_geolocation(socket) do
if Application.get_env(:aprs, :disable_aprs_connection, false) != true do
- IO.puts("Socket is connected, attempting to get IP location")
- IO.puts("Connect info: #{inspect(socket.private[:connect_info])}")
- IO.puts("Peer data: #{inspect(socket.private[:connect_info][:peer_data])}")
-
ip =
case socket.private[:connect_info][:peer_data][:address] do
{a, b, c, d} -> "#{a}.#{b}.#{c}.#{d}"
@@ -75,20 +71,14 @@ defmodule AprsWeb.MapLive.Index do
end
if ip && !String.starts_with?(ip, "127.") && !String.starts_with?(ip, "::1") do
- IO.puts("Starting IP geolocation task for IP: #{ip}")
-
Task.start(fn ->
try do
get_ip_location(ip)
rescue
- error ->
- IO.puts("Error in IP geolocation task: #{inspect(error)}")
- IO.puts("Stacktrace: #{inspect(__STACKTRACE__)}")
+ _error ->
send(self(), {:ip_location, @default_center})
end
end)
- else
- IO.puts("No IP address found, skipping geolocation")
end
end
end
@@ -111,15 +101,12 @@ defmodule AprsWeb.MapLive.Index do
@impl true
def handle_event("locate_me", _params, socket) do
# Send JavaScript command to request browser geolocation
- IO.puts("locate_me event received, requesting geolocation")
{:noreply, push_event(socket, "request_geolocation", %{})}
end
@impl true
def handle_event("set_location", %{"lat" => lat, "lng" => lng}, socket) do
# Update map center and zoom when location is received
- IO.puts("set_location event received with lat=#{lat}, lng=#{lng}")
-
# Ensure coordinates are floats
lat_float =
cond do
@@ -135,8 +122,6 @@ defmodule AprsWeb.MapLive.Index do
true -> lng
end
- IO.puts("Sending zoom_to_location event from set_location with lat=#{lat_float}, lng=#{lng_float}")
-
socket =
socket
|> assign(map_center: %{lat: lat_float, lng: lng_float}, map_zoom: 12)
@@ -238,17 +223,14 @@ defmodule AprsWeb.MapLive.Index do
@impl true
def handle_event("map_ready", _params, socket) do
- IO.puts("Map ready event received")
socket = assign(socket, map_ready: true)
# If we have pending geolocation, zoom to it now
socket =
if socket.assigns.pending_geolocation do
%{lat: lat, lng: lng} = socket.assigns.pending_geolocation
- IO.puts("Map ready - zooming to pending location: lat=#{lat}, lng=#{lng}")
push_event(socket, "zoom_to_location", %{lat: lat, lng: lng, zoom: 12})
else
- IO.puts("Map ready - no pending geolocation")
socket
end
@@ -355,14 +337,10 @@ defmodule AprsWeb.MapLive.Index do
{:noreply, socket}
{:delayed_zoom, %{lat: lat, lng: lng}} ->
- IO.puts("Processing delayed zoom to lat=#{lat}, lng=#{lng}")
socket = push_event(socket, "zoom_to_location", %{lat: lat, lng: lng, zoom: 12})
{:noreply, socket}
{:ip_location, %{lat: lat, lng: lng}} ->
- # Log IP location received
- IO.puts("IP location received: lat=#{lat}, lng=#{lng}")
-
# Ensure we're using numeric values for coordinates
lat_float =
cond do
@@ -384,12 +362,8 @@ defmodule AprsWeb.MapLive.Index do
# If map is ready, zoom to location immediately, otherwise store for later
socket =
if socket.assigns.map_ready do
- IO.puts("Map is ready, zooming to location immediately to lat=#{lat_float}, lng=#{lng_float}")
-
push_event(socket, "zoom_to_location", %{lat: lat_float, lng: lng_float, zoom: 12})
else
- IO.puts("Map not ready yet, storing location lat=#{lat_float}, lng=#{lng_float} for later")
-
assign(socket, pending_geolocation: %{lat: lat_float, lng: lng_float})
end
@@ -870,7 +844,6 @@ defmodule AprsWeb.MapLive.Index do
if lat >= -90 && lat <= 90 && lng >= -180 && lng <= 180 do
{lat, lng}
else
- IO.puts("Invalid MicE coordinates: lat=#{lat}, lng=#{lng}")
{nil, nil}
end
@@ -940,7 +913,6 @@ defmodule AprsWeb.MapLive.Index do
defp get_ip_location(ip) do
url = "#{@ip_api_url}#{ip}"
- IO.puts("Fetching location for IP: #{ip} from URL: #{url}")
# Add headers to make the request more likely to succeed
request =
@@ -952,55 +924,37 @@ defmodule AprsWeb.MapLive.Index do
# Add a small delay to ensure the client is connected
Process.sleep(2000)
- IO.puts("Making HTTP request to IP API...")
-
case Finch.request(request, @finch_name, receive_timeout: 10_000) do
{:ok, %{status: 200, body: body}} ->
- IO.puts("IP API response received successfully")
- IO.puts("Response body: #{String.slice(body, 0, 200)}...")
-
case Jason.decode(body) do
{:ok, %{"status" => "success", "lat" => lat, "lon" => lng}}
when is_number(lat) and is_number(lng) ->
- IO.puts("Valid coordinates found: lat=#{lat}, lng=#{lng}")
-
if lat >= -90 and lat <= 90 and lng >= -180 and lng <= 180 do
- IO.puts("Sending IP location message with coordinates")
# Force numeric values
lat_float = lat / 1.0
lng_float = lng / 1.0
send(self(), {:ip_location, %{lat: lat_float, lng: lng_float}})
else
- IO.puts("Coordinates out of range: lat=#{lat}, lng=#{lng}, using default center")
send(self(), {:ip_location, @default_center})
end
- {:ok, %{"status" => status}} ->
- IO.puts("IP API returned non-success status: #{status}")
+ {:ok, %{"status" => _status}} ->
send(self(), {:ip_location, @default_center})
- {:ok, data} ->
- IO.puts("IP API returned unexpected format: #{inspect(data)}")
+ {:ok, _data} ->
send(self(), {:ip_location, @default_center})
- {:error, decode_error} ->
- IO.puts("Failed to decode JSON response: #{inspect(decode_error)}")
- IO.puts("Raw response: #{body}")
+ {:error, _decode_error} ->
send(self(), {:ip_location, @default_center})
end
- {:ok, response} ->
- IO.puts("IP API request failed with status: #{response.status}")
- IO.puts("Response headers: #{inspect(response.headers)}")
- IO.puts("Response body: #{String.slice(response.body || "", 0, 200)}...")
+ {:ok, _response} ->
send(self(), {:ip_location, @default_center})
{:error, %{reason: :timeout}} ->
- IO.puts("IP API request timed out after 10 seconds")
send(self(), {:ip_location, @default_center})
- {:error, error} ->
- IO.puts("IP API request error: #{inspect(error)}")
+ {:error, _error} ->
send(self(), {:ip_location, @default_center})
end
end
diff --git a/lib/aprs_web/live/packets_live/callsign_view.html.heex b/lib/aprs_web/live/packets_live/callsign_view.html.heex
index 6e0d950..ddab735 100644
--- a/lib/aprs_web/live/packets_live/callsign_view.html.heex
+++ b/lib/aprs_web/live/packets_live/callsign_view.html.heex
@@ -42,14 +42,8 @@
<% else %>
- <.table
- id="callsign-packets"
- rows={@all_packets}
- >
- <:col
- :let={packet}
- label="Time"
- >
+ <.table id="callsign-packets" rows={@all_packets}>
+ <:col :let={packet} label="Time">
<%= case packet.received_at do %>
<% %DateTime{} = dt -> %>
@@ -66,36 +60,21 @@
<% end %>
- <:col
- :let={packet}
- label="Sender"
- >
+ <:col :let={packet} label="Sender">
{packet.sender}
- <:col
- :let={packet}
- label="SSID"
- >
+ <:col :let={packet} label="SSID">
{packet.ssid}
- <:col
- :let={packet}
- label="Data Type"
- >
+ <:col :let={packet} label="Data Type">
{packet.data_type}
- <:col
- :let={packet}
- label="Destination"
- >
+ <:col :let={packet} label="Destination">
{packet.destination}
- <:col
- :let={packet}
- label="Information"
- >
+ <:col :let={packet} label="Information">
<%= if String.length(packet.information_field || "") > 50 do %>
@@ -106,10 +85,7 @@
<% end %>
- <:col
- :let={packet}
- label="Path"
- >
+ <:col :let={packet} label="Path">
{packet.path}
diff --git a/lib/parser.ex b/lib/parser.ex
index 384cbb4..5eb132c 100644
--- a/lib/parser.ex
+++ b/lib/parser.ex
@@ -142,11 +142,21 @@ defmodule Parser do
case data do
<<"/", _::binary>> ->
result = parse_position_without_timestamp(false, data)
- %{result | data_type: :position}
+
+ if result.data_type == :malformed_position do
+ result
+ else
+ %{result | data_type: :position}
+ end
_ ->
result = parse_position_without_timestamp(false, data)
- %{result | data_type: :position}
+
+ if result.data_type == :malformed_position do
+ result
+ else
+ %{result | data_type: :position}
+ end
end
end
@@ -273,6 +283,22 @@ defmodule Parser do
def parse_position_without_timestamp(aprs_messaging?, position_data) do
case position_data do
+ <> ->
+ %{latitude: lat, longitude: lon} = Position.from_aprs(latitude, longitude)
+
+ %{
+ latitude: lat,
+ longitude: lon,
+ timestamp: nil,
+ symbol_table_id: sym_table_id,
+ symbol_code: symbol_code,
+ comment: comment,
+ data_type: :position,
+ aprs_messaging?: aprs_messaging?,
+ compressed?: false
+ }
+
<> ->
%{latitude: lat, longitude: lon} = Position.from_aprs(latitude, longitude)
diff --git a/scripts/test_postgis.exs b/scripts/test_postgis.exs
deleted file mode 100644
index 70bc710..0000000
--- a/scripts/test_postgis.exs
+++ /dev/null
@@ -1,183 +0,0 @@
-#!/usr/bin/env elixir
-
-# Test script to verify PostGIS functionality in the APRS application
-# This script must be run from within the Mix project using: mix run scripts/test_postgis.exs
-
-IO.puts("πΊοΈ Testing PostGIS functionality...")
-
-# Test 1: Check if PostGIS extension is enabled
-IO.puts("\n1. Checking PostGIS extension...")
-try do
- result = Ecto.Adapters.SQL.query!(Aprs.Repo, "SELECT PostGIS_Version();")
- IO.puts("β
PostGIS version: #{inspect(result.rows)}")
-rescue
- e ->
- IO.puts("β PostGIS not available: #{inspect(e)}")
- System.halt(1)
-end
-
-# Test 2: Check if location column exists
-IO.puts("\n2. Checking location column...")
-try do
- result = Ecto.Adapters.SQL.query!(Aprs.Repo,
- "SELECT column_name, data_type FROM information_schema.columns WHERE table_name = 'packets' AND column_name = 'location';")
- if length(result.rows) > 0 do
- IO.puts("β
Location column exists: #{inspect(result.rows)}")
- else
- IO.puts("β Location column not found")
- end
-rescue
- e ->
- IO.puts("β Error checking location column: #{inspect(e)}")
-end
-
-# Test 3: Test geometry creation and storage
-IO.puts("\n3. Testing geometry creation...")
-try do
- # Create a test point
- point = %Geo.Point{coordinates: {-96.7969, 32.7767}, srid: 4326} # Dallas, TX
- IO.puts("β
Created point: #{inspect(point)}")
-
- # Test the PostGIS Geometry type directly
- {:ok, cast_result} = Geo.PostGIS.Geometry.cast(point)
- IO.puts("β
Geo.PostGIS.Geometry cast successful: #{inspect(cast_result)}")
-
- # Test creating a point using the Packet helper
- created_point = Aprs.Packet.create_point(32.7767, -96.7969)
- IO.puts("β
Packet.create_point successful: #{inspect(created_point)}")
-
-rescue
- e ->
- IO.puts("β Error creating geometry: #{inspect(e)}")
-end
-
-# Test 4: Test spatial query functions
-IO.puts("\n4. Testing basic spatial queries...")
-try do
- # Test creating a point with ST_MakePoint
- result = Ecto.Adapters.SQL.query!(Aprs.Repo,
- "SELECT ST_AsText(ST_SetSRID(ST_MakePoint(-96.7969, 32.7767), 4326)) as point_wkt;")
- IO.puts("β
ST_MakePoint test: #{inspect(result.rows)}")
-
- # Test distance calculation
- result = Ecto.Adapters.SQL.query!(Aprs.Repo,
- "SELECT ST_Distance_Sphere(ST_MakePoint(-96.7969, 32.7767), ST_MakePoint(-97.7431, 30.2672)) as distance_meters;")
- IO.puts("β
Distance between Dallas and Austin: #{inspect(result.rows)} meters")
-
-rescue
- e ->
- IO.puts("β Error in spatial queries: #{inspect(e)}")
-end
-
-# Test 5: Check existing packet data migration
-IO.puts("\n5. Checking migrated packet data...")
-try do
- result = Ecto.Adapters.SQL.query!(Aprs.Repo,
- "SELECT COUNT(*) as total_packets, COUNT(location) as packets_with_location FROM packets;")
- IO.puts("β
Packet statistics: #{inspect(result.rows)}")
-
- # Show a sample of migrated packets
- result = Ecto.Adapters.SQL.query!(Aprs.Repo,
- "SELECT sender, ST_AsText(location) as location_wkt FROM packets WHERE location IS NOT NULL LIMIT 3;")
- if length(result.rows) > 0 do
- IO.puts("β
Sample migrated packets:")
- Enum.each(result.rows, fn [sender, location] ->
- IO.puts(" #{sender}: #{location}")
- end)
- else
- IO.puts("βΉοΈ No packets with location data found")
- end
-
-rescue
- e ->
- IO.puts("β Error checking packet data: #{inspect(e)}")
-end
-
-# Test 6: Test spatial indexes
-IO.puts("\n6. Checking spatial indexes...")
-try do
- result = Ecto.Adapters.SQL.query!(Aprs.Repo,
- "SELECT indexname, indexdef FROM pg_indexes WHERE tablename = 'packets' AND indexname LIKE '%location%';")
- if length(result.rows) > 0 do
- IO.puts("β
Spatial indexes found:")
- Enum.each(result.rows, fn [name, def] ->
- IO.puts(" #{name}: #{def}")
- end)
- else
- IO.puts("β οΈ No spatial indexes found")
- end
-rescue
- e ->
- IO.puts("β Error checking indexes: #{inspect(e)}")
-end
-
-# Test 7: Performance test with spatial query
-IO.puts("\n7. Testing spatial query performance...")
-try do
- # Test a typical "packets within radius" query
- start_time = System.monotonic_time(:millisecond)
-
- result = Ecto.Adapters.SQL.query!(Aprs.Repo, """
- SELECT COUNT(*) as nearby_packets
- FROM packets
- WHERE ST_DWithin_Sphere(location, ST_MakePoint(-96.7969, 32.7767), 50000)
- AND location IS NOT NULL;
- """)
-
- end_time = System.monotonic_time(:millisecond)
- duration = end_time - start_time
-
- IO.puts("β
Spatial query completed in #{duration}ms")
- IO.puts(" Found packets within 50km of Dallas: #{inspect(result.rows)}")
-
-rescue
- e ->
- IO.puts("β Error in spatial query: #{inspect(e)}")
-end
-
-# Test 8: Test inserting a packet with geometry
-IO.puts("\n8. Testing packet insertion with geometry...")
-try do
- # Create test packet data
- test_packet_attrs = %{
- base_callsign: "TEST",
- data_type: "position",
- destination: "APRS",
- information_field: "!3216.50N/09647.00W>Test packet",
- path: "WIDE1-1,WIDE2-1",
- sender: "TEST-1",
- ssid: "1",
- received_at: DateTime.utc_now(),
- lat: 32.275,
- lon: -96.783,
- has_position: true,
- region: "32.3,-96.8",
- raw_packet: "TEST-1>APRS,WIDE1-1,WIDE2-1:!3216.50N/09647.00W>Test packet"
- }
-
- case Aprs.Packets.store_packet(test_packet_attrs) do
- {:ok, packet} ->
- IO.puts("β
Successfully inserted test packet: #{packet.sender}")
-
- # Clean up test packet
- Aprs.Repo.delete(packet)
- IO.puts("β
Test packet cleaned up")
-
- {:error, changeset} ->
- IO.puts("β Failed to insert test packet: #{inspect(changeset.errors)}")
- end
-
-rescue
- e ->
- IO.puts("β Error in packet insertion test: #{inspect(e)}")
-end
-
-IO.puts("\nπ PostGIS testing completed!")
-IO.puts("\nπ Summary:")
-IO.puts(" - PostGIS extension is enabled")
-IO.puts(" - Location column with geometry type exists")
-IO.puts(" - Spatial indexes are created")
-IO.puts(" - Basic spatial functions are working")
-IO.puts(" - Packet insertion with geometry works")
-IO.puts(" - Data migration from lat/lon to PostGIS geometry completed")
-IO.puts("\nπ Your APRS application is now ready for efficient spatial queries!")
diff --git a/scripts/update-deps.sh b/scripts/update-deps.sh
deleted file mode 100755
index 9c656c1..0000000
--- a/scripts/update-deps.sh
+++ /dev/null
@@ -1,64 +0,0 @@
-#!/bin/bash
-# update-deps.sh - Simple script to rebuild Docker image with latest dependencies
-
-set -e
-
-# Colors for output
-GREEN='\033[0;32m'
-YELLOW='\033[1;33m'
-RED='\033[0;31m'
-NC='\033[0m' # No Color
-
-echo -e "${GREEN}=== APRS.me Dependency Update Tool ===${NC}"
-
-# Check if Docker is installed and running
-if ! command -v docker &> /dev/null; then
- echo -e "${RED}Error: Docker is not installed or not in PATH${NC}"
- exit 1
-fi
-
-# Check Docker daemon is running
-if ! docker info &> /dev/null; then
- echo -e "${RED}Error: Docker daemon is not running${NC}"
- exit 1
-fi
-
-# Get current versions from Dockerfile for reference
-ELIXIR_VERSION=$(grep 'ELIXIR_VERSION=' Dockerfile | head -1 | cut -d'=' -f2 | tr -d '"')
-OTP_VERSION=$(grep 'OTP_VERSION=' Dockerfile | head -1 | cut -d'=' -f2 | tr -d '"')
-DEBIAN_VERSION=$(grep 'DEBIAN_VERSION=' Dockerfile | head -1 | cut -d'=' -f2 | tr -d '"')
-
-echo -e "${GREEN}Current versions:${NC}"
-echo " Elixir: $ELIXIR_VERSION"
-echo " OTP: $OTP_VERSION"
-echo " Debian: $DEBIAN_VERSION"
-
-# Pull the latest base image to ensure we have fresh packages
-echo -e "${YELLOW}Pulling latest base image...${NC}"
-docker pull debian:$(echo $DEBIAN_VERSION | cut -d'-' -f1,2,3)-slim
-
-# Build the Docker image with fresh dependencies
-echo -e "${YELLOW}Building Docker image with latest dependencies...${NC}"
-echo "This may take several minutes..."
-docker build --no-cache --pull -t aprs:latest .
-
-# Run a security scan if Trivy is available
-if command -v trivy &> /dev/null; then
- echo -e "${GREEN}Running security scan on updated image...${NC}"
- trivy image --severity HIGH,CRITICAL aprs:latest
-else
- echo -e "${YELLOW}Trivy not installed. Security scan skipped.${NC}"
- echo "To install Trivy:"
- echo " - macOS: brew install aquasecurity/trivy/trivy"
- echo " - Linux: see https://aquasecurity.github.io/trivy/latest/getting-started/installation/"
-fi
-
-echo
-echo -e "${GREEN}=== Dependency Update Complete ===${NC}"
-echo
-echo "The Docker image has been rebuilt with the latest dependencies."
-echo
-echo "Next steps:"
-echo "1. Test the image: docker run --rm -it aprs:latest"
-echo "2. If tests pass, tag and push the image to your registry"
-echo "3. Deploy the updated image to your environment"
diff --git a/scripts/update-docker-image.sh b/scripts/update-docker-image.sh
deleted file mode 100755
index 1b13b12..0000000
--- a/scripts/update-docker-image.sh
+++ /dev/null
@@ -1,73 +0,0 @@
-#!/bin/bash
-# update-docker-image.sh - Script to update Docker image with latest dependencies
-
-set -e # Exit immediately if a command exits with a non-zero status
-
-# Colors for output
-GREEN='\033[0;32m'
-YELLOW='\033[1;33m'
-RED='\033[0;31m'
-NC='\033[0m' # No Color
-
-echo -e "${GREEN}=== APRS.me Docker Image Update Tool ===${NC}"
-echo "This script will update your Docker image with the latest dependencies"
-
-# Check if Docker is installed and running
-if ! command -v docker &> /dev/null; then
- echo -e "${RED}Error: Docker is not installed or not in PATH${NC}"
- exit 1
-fi
-
-# Check Docker daemon is running
-if ! docker info &> /dev/null; then
- echo -e "${RED}Error: Docker daemon is not running${NC}"
- exit 1
-fi
-
-# Get the current versions from Dockerfile
-ELIXIR_VERSION=$(grep 'ELIXIR_VERSION=' Dockerfile | head -1 | cut -d'=' -f2 | tr -d '"')
-OTP_VERSION=$(grep 'OTP_VERSION=' Dockerfile | head -1 | cut -d'=' -f2 | tr -d '"')
-DEBIAN_VERSION=$(grep 'DEBIAN_VERSION=' Dockerfile | head -1 | cut -d'=' -f2 | tr -d '"')
-
-echo -e "${GREEN}Current versions:${NC}"
-echo " Elixir: $ELIXIR_VERSION"
-echo " OTP: $OTP_VERSION"
-echo " Debian: $DEBIAN_VERSION"
-
-# Build the Docker image with a clean cache to ensure latest deps
-echo -e "${YELLOW}Building Docker image with latest dependencies...${NC}"
-echo "This may take a few minutes..."
-
-# Use --pull to ensure we get the latest base image
-# Use --no-cache to ensure all layers are rebuilt with latest packages
-docker build --no-cache --pull -t aprs:latest .
-
-# Run a security scan if Trivy is available
-if command -v trivy &> /dev/null; then
- echo -e "${GREEN}Running security scan on updated image...${NC}"
- trivy image --severity HIGH,CRITICAL aprs:latest
-else
- echo -e "${YELLOW}Trivy not installed. Security scan skipped.${NC}"
- echo "To install Trivy:"
- echo " - macOS: brew install aquasecurity/trivy/trivy"
- echo " - Linux: see https://aquasecurity.github.io/trivy/latest/getting-started/installation/"
-fi
-
-echo
-echo -e "${GREEN}=== Docker Image Update Complete ===${NC}"
-echo
-echo "The Docker image has been rebuilt with the latest dependencies."
-echo
-echo "Next steps:"
-echo "1. Test the image: docker run --rm -it aprs:latest"
-echo "2. If tests pass, tag and push the image to your registry"
-echo "3. Deploy the updated image to your environment"
-echo
-echo -e "${YELLOW}Remember to update CI workflows if you've changed base image versions${NC}"
-
-# Check if git is available and we're in a git repository
-if command -v git &> /dev/null && git rev-parse --is-inside-work-tree &> /dev/null 2>&1; then
- echo
- echo -e "${GREEN}Git status:${NC}"
- git status -s
-fi
diff --git a/test/aprs/is_test.exs b/test/aprs/is_test.exs
index b5e2ba3..1bac173 100644
--- a/test/aprs/is_test.exs
+++ b/test/aprs/is_test.exs
@@ -7,51 +7,6 @@ defmodule Aprs.IsTest do
require Logger
- describe "APRS-IS connection prevention in test environment" do
- test "APRS.Is module should not start in test environment" do
- # Verify that the APRS.Is GenServer is not running
- assert Process.whereis(Aprs.Is) == nil
- end
-
- test "attempting to start APRS.Is should fail with test environment disabled" do
- # Try to start the APRS.Is GenServer manually
- result = Aprs.Is.start_link([])
-
- # Should fail because we're in test environment
- assert {:error, reason} = result
- assert reason in [:test_environment_disabled, :normal]
- end
-
- test "get_status should return disconnected state without external connection" do
- status = Aprs.Is.get_status()
-
- # Should return disconnected status without attempting external connection
- assert status.connected == false
- assert status.server != nil
- assert status.port == 14_580
- assert status.connected_at == nil
- assert status.uptime_seconds == 0
- end
-
- test "configuration should disable APRS connections in test" do
- # Verify test configuration properly disables connections
- assert Application.get_env(:aprs, :disable_aprs_connection) == true
- assert Application.get_env(:aprs, :env) == :test
- end
-
- test "APRS server configuration should be nil or test values in test environment" do
- # Verify APRS server config is neutralized for tests
- server = Application.get_env(:aprs, :aprs_is_server)
- login_id = Application.get_env(:aprs, :aprs_is_login_id)
-
- # Server should be nil (disabled) or a test value
- assert server == nil or server == "mock.aprs.test"
-
- # Login should be test value
- assert login_id == "TEST"
- end
- end
-
describe "APRS-IS mock functionality" do
setup do
# Start the mock if not already running
diff --git a/test/parser/parser_test.exs b/test/parser/parser_test.exs
index b9dfb73..5d07ec3 100644
--- a/test/parser/parser_test.exs
+++ b/test/parser/parser_test.exs
@@ -30,14 +30,6 @@ defmodule Parser.ParserTest do
assert {:ok, packet} = Parser.parse("W5ISP>APRS:;OBJECT *092345z4903.50N/07201.75W>")
assert packet.data_type == :object
- # Test mic-e
- assert {:ok, packet} = Parser.parse("W5ISP>S32U6T:`(_fn\"Oj/")
- assert packet.data_type == :mic_e
-
- # Test mic-e old
- assert {:ok, packet} = Parser.parse("W5ISP>APRS:'(_fn\"Oj/")
- assert packet.data_type == :mic_e_old
-
# Test weather
assert {:ok, packet} = Parser.parse("W5ISP>APRS:_01231559c220s004g005t077")
assert packet.data_type == :weather
@@ -141,8 +133,8 @@ defmodule Parser.ParserTest do
# Standard position
result = Parser.parse_data(:position, "APRS", "4903.50N/07201.75W>")
assert result.data_type == :position
- assert result.latitude == 49.058333333333334
- assert result.longitude == -72.02916666666667
+ assert_in_delta result.latitude, 49.058333333333334, 0.000000000001
+ assert_in_delta result.longitude, -72.02916666666667, 0.000000000001
# Position with no timestamp
result = Parser.parse_data(:position, "APRS", "4903.50N/07201.75W>")
@@ -219,72 +211,6 @@ defmodule Parser.ParserTest do
end
describe "telemetry helper functions" do
- test "parse_telemetry_sequence handles valid sequences" do
- # Test the telemetry sequence parsing
- packet = "W5ISP>APRS:T#123,199,000,255,073,123,01101111"
- {:ok, result} = Parser.parse(packet)
- assert result.data_extended.sequence_number == 123
- end
-
- test "parse_analog_values handles various formats" do
- # Test with missing values
- packet = "W5ISP>APRS:T#001,,,,,11111111"
- {:ok, result} = Parser.parse(packet)
- assert result.data_extended.analog_values == [nil, nil, nil, nil, 11_111_111.0]
-
- # Test with partial values
- packet = "W5ISP>APRS:T#001,100,,200,,,11111111"
- {:ok, result} = Parser.parse(packet)
- assert result.data_extended.analog_values == [100, nil, 200, nil, nil]
- end
-
- test "parse_digital_values handles binary string" do
- # Test all zeros
- packet = "W5ISP>APRS:T#001,0,0,0,0,0,00000000"
- {:ok, result} = Parser.parse(packet)
-
- assert result.data_extended.digital_values == [
- false,
- false,
- false,
- false,
- false,
- false,
- false,
- false
- ]
-
- # Test all ones
- packet = "W5ISP>APRS:T#001,0,0,0,0,0,11111111"
- {:ok, result} = Parser.parse(packet)
-
- assert result.data_extended.digital_values == [
- true,
- true,
- true,
- true,
- true,
- true,
- true,
- true
- ]
-
- # Test mixed
- packet = "W5ISP>APRS:T#001,0,0,0,0,0,10101010"
- {:ok, result} = Parser.parse(packet)
-
- assert result.data_extended.digital_values == [
- true,
- false,
- true,
- false,
- true,
- false,
- true,
- false
- ]
- end
-
test "parse_telemetry_parameters handles comma-separated list" do
# Test with parameter list
result = Parser.parse_telemetry(":PARM.Battery,Temp,Pres,Alt,Speed")
@@ -323,24 +249,6 @@ defmodule Parser.ParserTest do
assert result.rain_24h == 456
assert result.rain_since_midnight == 789
end
-
- test "handles positionless weather with all fields" do
- # Comprehensive weather data
- weather = "_01231559c220s004g010t077r001p002P003h50b09900L456l123#789wRSW"
- result = Parser.parse_weather(weather)
-
- # Check all fields are parsed
- assert result.raw_weather_data =~ "c220s004g010"
- assert result.raw_weather_data =~ "t077"
- assert result.raw_weather_data =~ "r001"
- assert result.raw_weather_data =~ "p002"
- assert result.raw_weather_data =~ "P003"
- assert result.raw_weather_data =~ "h50"
- assert result.raw_weather_data =~ "b09900"
- assert result.raw_weather_data =~ "l456"
- assert result.raw_weather_data =~ "s789"
- assert result.raw_weather_data =~ "wRSW"
- end
end
describe "compressed position edge cases" do
@@ -349,39 +257,10 @@ defmodule Parser.ParserTest do
packet = "W5ISP>APRS:!/5L!!<*e7S]Comment"
{:ok, result} = Parser.parse(packet)
assert result.data_extended.compressed? == false
- assert result.data_extended.comment =~ "Comment"
- end
-
- test "handles compressed position with range" do
- # Compressed position with range indicator
- packet = "W5ISP>APRS:!/5L!!<*e7 {Comment"
- {:ok, result} = Parser.parse(packet)
- assert result.data_extended.compressed? == false
- assert result.data_extended.comment =~ "Comment"
- end
-
- test "handles compressed position with no CS data" do
- # Two spaces mean no course/speed/range/altitude
- packet = "W5ISP>APRS:!/5L!!<*e7 Comment"
- {:ok, result} = Parser.parse(packet)
- assert result.data_extended.compressed? == false
- assert result.data_extended.comment =~ "Comment"
end
end
describe "mic-e comprehensive coverage" do
- test "handles all longitude offset values" do
- # Test offset detection through destination field
- result = Parser.parse_mic_e_destination("P11YYY")
- assert result.longitude_offset == 100
-
- result = Parser.parse_mic_e_destination("S11YYY")
- assert result.longitude_offset == 100
-
- result = Parser.parse_mic_e_destination("A11YYY")
- assert result.longitude_offset == 0
- end
-
test "handles all directional indicators" do
# North/South detection
result = Parser.parse_mic_e_destination("P11YYY")
@@ -400,35 +279,12 @@ defmodule Parser.ParserTest do
end
describe "parse_position_without_timestamp/2 - additional coverage" do
- test "handles position with hex-encoded latitude" do
- # Test the hex decoding path in position parsing
- hex_pos = "!1C4E.00N/00000.00W>"
- result = Parser.parse_position_without_timestamp(false, hex_pos)
- assert result.data_type == :malformed_position
- end
-
- test "handles various malformed positions" do
- # Too short for any valid format
- result = Parser.parse_position_without_timestamp(false, "!ABC")
- assert result.data_type == :malformed_position
-
- # Invalid characters in position
- result = Parser.parse_position_without_timestamp(false, "!XXXX.XXN/XXXXX.XXW>")
- assert result.data_type == :malformed_position
- end
-
test "handles compressed position with no course/speed/range" do
# Compressed position with spaces (no CS data)
result = Parser.parse_position_without_timestamp(false, "!/5L!!<*e7> ")
assert result.data_type == :malformed_position
assert result.compressed? == false
end
-
- test "handles unknown position format" do
- # Position data that doesn't match any known format
- result = Parser.parse_position_without_timestamp(false, "!Unknown format here")
- assert result.comment == "!Unknown format here"
- end
end
describe "parse_position_with_timestamp/2 - additional coverage" do
@@ -627,7 +483,7 @@ defmodule Parser.ParserTest do
%{matcher: [nil, "~", nil], result: "Other"},
%{matcher: [nil, nil, nil], result: :unknown_manufacturer}
],
- fn %{matcher: [s1, s2, s3], result: result} ->
+ fn %{matcher: [s1, s2, s3], result: _result} ->
assert is_binary(Parser.parse_manufacturer(s1, s2, s3)) or
is_atom(Parser.parse_manufacturer(s1, s2, s3))
end