Adds a per-contact enrichment pipeline that determines whether a QSO was
most likely carried by rain scatter, tropospheric ducting, or ordinary
troposcatter — using IEM n0q composite reflectivity sampled inside the
lens-shaped intersection of 400 km-radius disks around each endpoint.
Pieces:
* Microwaveprop.Propagation.CommonVolume — lens geometry (haversine,
in-CV test, bbox, area).
* contact_common_volume_radar table (1:1 per contact) storing
aggregate dBZ stats inside the CV + radar_status column on contacts.
* Microwaveprop.Workers.CommonVolumeRadarWorker — Oban :radar queue,
fetches the n0q frame at QSO time, iterates pixels inside the CV
bbox, aggregates rain/heavy/core-pixel counts, max/mean dBZ, and
coverage percentage.
* Microwaveprop.Propagation.RainScatterClassifier — rule-based mapper
from (band, distance, radar stats, duct flags) to one of
:likely_rainscatter | :rainscatter_possible | :tropo_duct |
:troposcatter | :unknown.
* ContactWeatherEnqueueWorker learns a :radar enrichment type and
enqueues the CV worker on contact submission; pre-2014 contacts
(outside IEM n0q coverage) are pinned to :unavailable.
* `mix radar_backfill` bulk-enqueues historical contacts with
--year / --limit / --dry-run.
* Contact detail page renders a mechanism badge with supporting
stats (common-volume area, max dBZ, heavy-rain pixel count,
coverage %).
36 lines
1.2 KiB
Elixir
36 lines
1.2 KiB
Elixir
defmodule Microwaveprop.Repo.Migrations.CreateContactCommonVolumeRadar do
|
|
use Ecto.Migration
|
|
|
|
def change do
|
|
create table(:contact_common_volume_radar, primary_key: false) do
|
|
add :id, :binary_id, primary_key: true
|
|
|
|
add :contact_id, references(:contacts, type: :binary_id, on_delete: :delete_all),
|
|
null: false
|
|
|
|
add :observed_at, :utc_datetime, null: false
|
|
add :common_volume_km2, :float
|
|
add :pixel_count, :integer, null: false, default: 0
|
|
add :rain_pixel_count, :integer, null: false, default: 0
|
|
add :heavy_rain_pixel_count, :integer, null: false, default: 0
|
|
add :core_pixel_count, :integer, null: false, default: 0
|
|
add :max_dbz, :float
|
|
add :mean_dbz, :float
|
|
add :coverage_pct, :float
|
|
|
|
timestamps(type: :utc_datetime)
|
|
end
|
|
|
|
create unique_index(:contact_common_volume_radar, [:contact_id])
|
|
create index(:contact_common_volume_radar, [:observed_at])
|
|
|
|
alter table(:contacts) do
|
|
add :radar_status, :string, null: false, default: "pending"
|
|
end
|
|
|
|
create index(:contacts, [:radar_status],
|
|
where: "radar_status::text <> 'complete'::text",
|
|
name: :contacts_radar_status_pending_index
|
|
)
|
|
end
|
|
end
|