diff --git a/lib/microwaveprop_web/live/contact_live/show.ex b/lib/microwaveprop_web/live/contact_live/show.ex index 32bd1fcc..26116642 100644 --- a/lib/microwaveprop_web/live/contact_live/show.ex +++ b/lib/microwaveprop_web/live/contact_live/show.ex @@ -38,6 +38,7 @@ defmodule MicrowavepropWeb.ContactLive.Show do contact = maybe_enqueue_weather(weather, contact) elevation_profile = compute_elevation_profile(contact, hrrr_path, weather.soundings) propagation_analysis = build_propagation_analysis(contact, hrrr, terrain, elevation_profile, weather.soundings) + data_sources = build_data_sources(hrrr, hrrr_path, terrain, weather, elevation_profile) {:ok, assign(socket, @@ -50,6 +51,7 @@ defmodule MicrowavepropWeb.ContactLive.Show do terrain: terrain, elevation_profile: elevation_profile, propagation_analysis: propagation_analysis, + data_sources: data_sources, terrain_expanded: false, hrrr_profile_expanded: false, obs_sort_by: "station_name", @@ -776,6 +778,84 @@ defmodule MicrowavepropWeb.ContactLive.Show do <% end %> + + <%= if @data_sources do %> +
+ +

Data Sources

+
+
+
HRRR Model
+ <%= if @data_sources.hrrr do %> +
{@data_sources.hrrr.profile_count} path profiles
+
{@data_sources.hrrr.levels} pressure levels each
+
Valid: {@data_sources.hrrr.valid_time}
+
Grid: {@data_sources.hrrr.lat}, {@data_sources.hrrr.lon}
+ <% else %> +
Not available
+ <% end %> +
+ +
+
Elevation Profile
+ <%= if @data_sources.elevation do %> +
{@data_sources.elevation.samples} samples along path
+
{@data_sources.elevation.dist} path distance
+
Source: {@data_sources.elevation.source}
+
{@data_sources.elevation.duct_count} duct layers detected
+ <% else %> +
Not available
+ <% end %> +
+ +
+
Terrain Analysis
+ <%= if @data_sources.terrain do %> +
{@data_sources.terrain.samples} profile samples
+
Verdict: {@data_sources.terrain.verdict}
+
Max elevation: {@data_sources.terrain.max_elev} m
+
Diffraction: {@data_sources.terrain.diffraction} dB
+ <% else %> +
Not available
+ <% end %> +
+ +
+
Surface Observations
+
{length(@surface_observations)} stations
+ <%= if @surface_observations != [] do %> +
+ {Calendar.strftime(hd(@surface_observations).observed_at, "%H:%M")} – + {Calendar.strftime(List.last(@surface_observations).observed_at, "%H:%M")} UTC +
+ <% end %> +
+ +
+
Soundings (RAOB)
+
{length(@soundings)} profiles
+ <%= if @soundings != [] do %> +
+ {Enum.map_join(@soundings, ", ", fn s -> + "#{s.station.station_code} (#{s.level_count} levels)" + end)} +
+
{Enum.count(@soundings, & &1.ducting_detected)} with ducting
+ <% end %> +
+ +
+
Solar Indices
+ <%= if @solar do %> +
SFI: {@solar.sfi || "—"}
+
SSN: {@solar.sunspot_number || "—"}
+
Ap: {@solar.ap_index || "—"}
+ <% else %> +
Not available
+ <% end %> +
+
+ <% end %> """ end @@ -1081,6 +1161,54 @@ defmodule MicrowavepropWeb.ContactLive.Show do defp rad_to_deg(rad), do: rad * 180 / :math.pi() defp rem_float(a, b), do: a - Float.floor(a / b) * b + # ── Data sources summary ──────────────────────────────────────── + + defp build_data_sources(hrrr, hrrr_path, terrain, weather, elevation_profile) do + %{ + hrrr: build_hrrr_source(hrrr, hrrr_path), + elevation: build_elevation_source(elevation_profile), + terrain: build_terrain_source(terrain), + obs_count: length(weather.surface_observations), + sounding_count: length(weather.soundings) + } + end + + defp build_hrrr_source(nil, _), do: nil + + defp build_hrrr_source(hrrr, hrrr_path) do + levels = if hrrr.profile, do: length(hrrr.profile), else: 0 + + %{ + profile_count: length(hrrr_path), + levels: levels, + valid_time: Calendar.strftime(hrrr.valid_time, "%Y-%m-%d %H:%M UTC"), + lat: :erlang.float_to_binary(hrrr.lat / 1, decimals: 2), + lon: :erlang.float_to_binary(hrrr.lon / 1, decimals: 2) + } + end + + defp build_elevation_source(nil), do: nil + + defp build_elevation_source(ep) do + %{ + samples: length(ep.points), + dist: format_dist(ep.dist_km), + source: "SRTM 1-arcsecond", + duct_count: length(ep.ducts || []) + } + end + + defp build_terrain_source(nil), do: nil + + defp build_terrain_source(terrain) do + %{ + samples: terrain.sample_count, + verdict: terrain.verdict, + max_elev: :erlang.float_to_binary((terrain.max_elevation_m || 0) / 1, decimals: 0), + diffraction: :erlang.float_to_binary((terrain.diffraction_db || 0) / 1, decimals: 1) + } + end + # ── Propagation analysis ────────────────────────────────────────── defp build_propagation_analysis(contact, hrrr, terrain, elevation_profile, soundings) do