The power_mw field represents the beacon's effective radiated power, not just transmitter output. Relabel it as "TX power (EIRP)" on the form and detail list, "EIRP (mW)" in the index, and drop the now- meaningless tx_gain_dbi constant in RangeEstimate since the stored value already includes antenna gain.
124 lines
4 KiB
Elixir
124 lines
4 KiB
Elixir
defmodule MicrowavepropWeb.BeaconLive.Show do
|
||
@moduledoc false
|
||
use MicrowavepropWeb, :live_view
|
||
|
||
alias Microwaveprop.Beacons
|
||
alias Microwaveprop.Beacons.Beacon
|
||
alias Microwaveprop.Beacons.RangeEstimate
|
||
|
||
@impl true
|
||
def render(assigns) do
|
||
~H"""
|
||
<Layouts.app flash={@flash} current_scope={@current_scope}>
|
||
<.header>
|
||
{@beacon.callsign}
|
||
<:subtitle>{@beacon.frequency_mhz} MHz · {@beacon.grid}</:subtitle>
|
||
<:actions>
|
||
<.button navigate={~p"/beacons"}>
|
||
<.icon name="hero-arrow-left" />
|
||
</.button>
|
||
<.button
|
||
:if={admin?(@current_scope)}
|
||
variant="primary"
|
||
navigate={~p"/beacons/#{@beacon}/edit?return_to=show"}
|
||
>
|
||
<.icon name="hero-pencil-square" /> Edit
|
||
</.button>
|
||
</:actions>
|
||
</.header>
|
||
|
||
<div
|
||
id={"beacon-map-" <> @beacon.id}
|
||
phx-hook="BeaconMap"
|
||
phx-update="ignore"
|
||
class="h-96 w-full rounded-lg border border-base-300 mb-2"
|
||
data-lat={@beacon.lat}
|
||
data-lon={@beacon.lon}
|
||
data-label={"#{@beacon.callsign} · #{@beacon.frequency_mhz} MHz"}
|
||
data-on-the-air={to_string(@beacon.on_the_air)}
|
||
data-rings={Jason.encode!(@estimate.rings)}
|
||
>
|
||
</div>
|
||
|
||
<div class="text-xs opacity-70 mb-4 flex flex-wrap items-center gap-3">
|
||
<span>
|
||
Band <strong>{@estimate.band_label}</strong> ·
|
||
EIRP <strong>{@estimate.eirp_dbm} dBm</strong> ·
|
||
Atm loss <strong>{@estimate.atm_per_km} dB/km</strong>
|
||
</span>
|
||
<span>
|
||
Prop score <strong>{@estimate.score}</strong>
|
||
(<strong>{@estimate.score_mult}×</strong> range)
|
||
<%= if @estimate.valid_time do %>
|
||
at {Calendar.strftime(@estimate.valid_time, "%Y-%m-%d %H:%M UTC")}
|
||
<% else %>
|
||
(no HRRR data — using default 50)
|
||
<% end %>
|
||
</span>
|
||
</div>
|
||
|
||
<div class="flex flex-wrap gap-3 text-xs mb-4">
|
||
<%= for ring <- @estimate.rings do %>
|
||
<div class="flex items-center gap-1.5">
|
||
<span
|
||
class="inline-block w-3 h-3 rounded-full border"
|
||
style={"background-color: #{ring.color}; border-color: #{ring.color}"}
|
||
>
|
||
</span>
|
||
<span>
|
||
<strong>{ring.label}</strong>
|
||
({ring.rx_dbm} dBm) → {ring.radius_km} km
|
||
</span>
|
||
</div>
|
||
<% end %>
|
||
</div>
|
||
|
||
<.list>
|
||
<:item title="Frequency (MHz)">{@beacon.frequency_mhz}</:item>
|
||
<:item title="Callsign">{@beacon.callsign}</:item>
|
||
<:item title="Grid">{@beacon.grid}</:item>
|
||
<:item title="Latitude">{@beacon.lat}</:item>
|
||
<:item title="Longitude">{@beacon.lon}</:item>
|
||
<:item title="TX power (EIRP) (mW)">{@beacon.power_mw}</:item>
|
||
<:item title="Height above ground (ft)">{@beacon.height_ft}</:item>
|
||
<:item title="On the air">{if @beacon.on_the_air, do: "Yes", else: "No"}</:item>
|
||
</.list>
|
||
</Layouts.app>
|
||
"""
|
||
end
|
||
|
||
@impl true
|
||
def mount(%{"id" => id}, _session, socket) do
|
||
if connected?(socket), do: Beacons.subscribe_beacons()
|
||
|
||
beacon = Beacons.get_beacon!(id)
|
||
|
||
{:ok,
|
||
socket
|
||
|> assign(:page_title, "Beacon")
|
||
|> assign(:beacon, beacon)
|
||
|> assign(:estimate, RangeEstimate.estimate(beacon))}
|
||
end
|
||
|
||
@impl true
|
||
def handle_info({:updated, %Beacon{id: id} = beacon}, %{assigns: %{beacon: %{id: id}}} = socket) do
|
||
{:noreply,
|
||
socket
|
||
|> assign(:beacon, beacon)
|
||
|> assign(:estimate, RangeEstimate.estimate(beacon))}
|
||
end
|
||
|
||
def handle_info({:deleted, %Beacon{id: id}}, %{assigns: %{beacon: %{id: id}}} = socket) do
|
||
{:noreply,
|
||
socket
|
||
|> put_flash(:error, "This beacon was deleted.")
|
||
|> push_navigate(to: ~p"/beacons")}
|
||
end
|
||
|
||
def handle_info({type, %Beacon{}}, socket) when type in [:created, :updated, :deleted] do
|
||
{:noreply, socket}
|
||
end
|
||
|
||
defp admin?(%{user: %{is_admin: true}}), do: true
|
||
defp admin?(_), do: false
|
||
end
|