DX Monitor
Real-time DX Cluster monitor with intelligent Telegram alerts and a web dashboard. Runs as a Docker container on any NAS or Linux server, or as a standalone Windows executable.
DX Monitor connects to any DX Cluster (CC11/VE7CC protocol), cross-references incoming spots against your own logbook, and fires Telegram alerts only for contacts that are relevant to you — new DXCC, new band, new mode, or unconfirmed QSLs. The web dashboard lets you configure everything without touching any file.
DX Monitor
Monitor de DX Cluster en tiempo real con alertas Telegram inteligentes y dashboard web. Se ejecuta como contenedor Docker en cualquier NAS o servidor Linux, o como ejecutable standalone para Windows.
DX Monitor se conecta a cualquier DX Cluster (protocolo CC11/VE7CC), cruza los spots entrantes con tu propio libro de QSO y lanza alertas Telegram solo para los contactos relevantes: nuevo DXCC, nueva banda, nuevo modo o QSL pendiente. El dashboard web permite configurarlo todo sin tocar ningún fichero.
Features
Funcionalidades
Architecture
A single Python process runs Flask (web) and background threads (cluster, log reload, CTY update) together in one container. No inter-process communication needed.
Docker (NAS/Linux)
- Image: ea3tb/dx-monitor:latest
- Single container, port 8765
- Volume: dx_docker_data
- Hostfs mounted read-only at /hostfs
- No env vars — config via dashboard
- Restart: unless-stopped
Windows (standalone)
- DXMonitor.exe — no install
- System tray icon, auto-opens browser
- Same Flask + thread architecture
- Swisslog MDB: Access Engine 2016
- Built with PyInstaller
- Windows 10/11 64-bit
Arquitectura
Un único proceso Python ejecuta Flask (web) y hilos de fondo (cluster, recarga de log, actualización CTY) en el mismo contenedor. Sin comunicación entre procesos.
Docker (NAS/Linux)
- Imagen: ea3tb/dx-monitor:latest
- Contenedor único, puerto 8765
- Volumen: dx_docker_data
- Hostfs montado solo lectura en /hostfs
- Sin variables de entorno
- Restart: unless-stopped
Windows (standalone)
- DXMonitor.exe — sin instalación
- Icono en bandeja, abre navegador automáticamente
- Misma arquitectura Flask + hilos
- Swisslog MDB: Access Engine 2016
- Compilado con PyInstaller
- Windows 10/11 64-bit
DX Cluster
DX Monitor speaks the CC11/VE7CC protocol over plain TCP. One persistent connection per session.
Connection flow
password: prompt (e.g. ve7cc.net:23) are handled gracefully — the connection proceeds without hanging.
VE7CC initialisation
After login, the following commands are sent automatically to configure the CC11 stream:
set/ve7cc # enable CC11 format set/page 9999 # disable paging unset/echo # suppress command echo set/ft8 # enable FT8 spots set/skimmer # enable RBN/skimmer spots
Then sh/dx 20 is sent to fetch the last 20 spots immediately on connect.
Auto-connect
If cluster_host and cluster_login are set in config.json, the cluster thread connects automatically on startup. Otherwise it waits for the Connect button on the dashboard.
Keepalive & reconnect
Sends sh/dx 1 every 180 seconds to prevent the server from closing the connection. On unexpected disconnect, waits 30 seconds and reconnects automatically. Manual disconnect (via dashboard) stops the reconnect loop.
Pause/resume
Connect and Disconnect buttons act on a threading.Event (_cluster_paused). The cluster thread respects this flag without restarting the process.
Anti-duplicate
Spots for the same (callsign, band, mode) are suppressed within a 10-minute sliding window. This prevents alert floods when multiple stations spot the same DX simultaneously.
Deleted DXCC entities
If a spot's prefix resolves to a [deleted] entity in Big CTY, the engine automatically falls back to the longest matching active (non-deleted) prefix in the database. This prevents spots for callsigns from regions like Yugoslavia or Czechoslovakia from being misclassified as deleted entities when a current DXCC assignment exists.
DX Cluster
DX Monitor habla el protocolo CC11/VE7CC sobre TCP plano. Una conexión persistente por sesión.
Flujo de conexión
password: (p.ej. ve7cc.net:23) se gestionan correctamente sin que la conexión se cuelgue.
Inicialización VE7CC
Tras el login, se envían automáticamente estos comandos para configurar el stream CC11:
set/ve7cc # activar formato CC11 set/page 9999 # desactivar paginación unset/echo # suprimir eco de comandos set/ft8 # activar spots FT8 set/skimmer # activar spots RBN/skimmer
A continuación se envía sh/dx 20 para obtener los últimos 20 spots al conectar.
Auto-conexión
Si cluster_host y cluster_login están en config.json, el hilo del cluster conecta automáticamente al arrancar. Si no, espera al botón Conectar del dashboard.
Keepalive y reconexión
Envía sh/dx 1 cada 180 segundos para evitar que el servidor cierre la conexión. Ante una desconexión inesperada, espera 30 s y reconecta automáticamente. La desconexión manual (desde el dashboard) detiene el bucle de reconexión.
Pausa/reanudación
Los botones Conectar y Desconectar actúan sobre un threading.Event (_cluster_paused). El hilo del cluster respeta este flag sin reiniciar el proceso.
Anti-duplicados
Los spots para el mismo (indicativo, banda, modo) se suprimen dentro de una ventana deslizante de 10 minutos. Evita inundaciones de alertas cuando varias estaciones spotean el mismo DX simultáneamente.
Entidades DXCC eliminadas
Si el prefijo de un spot resuelve a una entidad [deleted] en Big CTY, el motor intenta automáticamente el prefijo activo (no eliminado) más largo que coincida en la base de datos. Esto evita que spots de indicativos de regiones como Yugoslavia o Checoslovaquia se clasifiquen como entidades eliminadas cuando existe una asignación DXCC actual.
Propagation
DX Monitor calculates a propagation score for every qualifying spot before firing a Telegram alert. The score is computed locally using real-time space weather data fetched from NOAA and hamqsl.
Data sources
| Parameter | Source | Cache TTL |
|---|---|---|
| Kp index | NOAA SWPC planetary_k_index_1m.json | 15 min |
| Solar Flux Index (SFI) | hamqsl.com solarxml.php | 15 min |
Both are fetched in the background and cached for 15 minutes. If either fetch fails, the score is marked as Fail and the spot is not filtered out.
Score calculation
A score 0–99% is computed for both Short Path (SP) and Long Path (LP). The model factors in: estimated MUF from SFI, geomagnetic attenuation from Kp, solar hour at the path midpoint, high-latitude penalty (|lat| > 60°), and night enhancement for low bands (≤ 7 MHz). Both SP and LP are calculated independently using their respective midpoint coordinates.
In Telegram alerts
The score appears at the end of every alert message:
Propagation filter (dashboard slider)
The Alert Filters section of the dashboard includes a 📶 Min. propagation slider (0–99%). When set above 0, a spot is discarded if max(SP, LP) < prop_min. At 0 (default) no propagation filtering is applied.
prop_min value is stored in flags.json and takes effect immediately — no restart needed. It is applied after DXCC/band/mode classification and before the Telegram message is sent.
Propagación
DX Monitor calcula un score de propagación para cada spot que pasa los filtros, antes de enviar la alerta Telegram. El cálculo es local, usando datos de tiempo espacial obtenidos en tiempo real de NOAA y hamqsl.
Fuentes de datos
| Parámetro | Fuente | Caché TTL |
|---|---|---|
| Índice Kp | NOAA SWPC planetary_k_index_1m.json | 15 min |
| Solar Flux Index (SFI) | hamqsl.com solarxml.php | 15 min |
Ambos se obtienen en segundo plano y se cachean 15 minutos. Si alguna descarga falla, el score aparece como Fail y el spot no se descarta.
Cálculo del score
Se calcula un score 0–99% para Vía Corta (SP) y Vía Larga (LP) independientemente. El modelo tiene en cuenta: MUF estimada a partir del SFI, atenuación geomagnética por Kp, hora solar en el punto medio del trayecto, penalización por latitudes altas (|lat| > 60°), y mejora nocturna en bandas bajas (≤ 7 MHz).
En las alertas Telegram
El score aparece al final de cada mensaje de alerta:
Filtro de propagación (slider del dashboard)
La sección de filtros del dashboard incluye un slider 📶 Propagación mín. (0–99%). Si se establece por encima de 0, un spot se descarta si max(SP, LP) < prop_min. En 0 (valor por defecto) no hay filtrado por propagación.
prop_min se guarda en flags.json y se aplica de inmediato, sin reiniciar. Se aplica después de la clasificación DXCC/banda/modo y antes de enviar el mensaje Telegram.
Telegram Alerts
DX Monitor sends formatted Telegram messages via the Bot API using your personal bot token and chat ID. All alert parameters are configured from the dashboard — no manual file editing.
Alert types
Alert content
Each alert includes: DX callsign and entity, frequency, band, mode, spotter, azimuth Short Path / Long Path (degrees), distance (km), propagation score SP/LP, and timestamp in local or UTC time.
Mode inference logic
Mode is determined in this order:
- Spot comment explicitly mentions CW / SSB / RTTY / FT8 / FT4 → use that mode
- No mode in comment + frequency matches FT8 or FT4 nominal (±1 kHz) → infer FT8 / FT4
- No mode, not FT8/FT4 → infer CW or SSB by band plan. Never RTTY.
- Frequency in exclusive CW segment → force CW regardless of comment
Language & time
Alert messages are sent in Spanish or English (alert_lang). Timestamps use the configured timezone (40+ grouped zones + manual IANA entry) in local or UTC mode (time_mode).
https://api.telegram.org/bot<TOKEN>/getUpdates
Alertas Telegram
DX Monitor envía mensajes Telegram formateados via Bot API usando tu token de bot personal y tu chat ID. Todos los parámetros se configuran desde el dashboard, sin editar ficheros.
Tipos de alerta
Contenido de la alerta
Cada alerta incluye: indicativo y entidad DX, frecuencia, banda, modo, spotter, azimut vía corta / vía larga (grados), distancia (km), score de propagación SP/LP y timestamp en hora local o UTC.
Lógica de inferencia de modo
El modo se determina en este orden:
- El comentario del spot menciona explícitamente CW / SSB / RTTY / FT8 / FT4 → se usa ese modo
- Sin modo en el comentario + frecuencia coincide con nominal FT8 o FT4 (±1 kHz) → se infiere FT8 / FT4
- Sin modo, no FT8/FT4 → se infiere CW o SSB por band plan. Nunca RTTY.
- Frecuencia en segmento exclusivo CW → se fuerza CW independientemente del comentario
Idioma y hora
Los mensajes se envían en español o inglés (alert_lang). Los timestamps usan la zona horaria configurada (40+ zonas agrupadas + campo IANA manual) en modo local o UTC (time_mode).
https://api.telegram.org/bot<TOKEN>/getUpdates
Log Sources
DX Monitor reads your logbook to know which DXCCs, bands and modes you have already worked. Four formats are supported and selected from the dashboard.
| Format | Notes |
|---|---|
| HRD XML | Ham Radio Deluxe — automatic background export. DX Monitor watches a directory and reloads XML files at the configured interval. Path navigable from the dashboard browser. |
| Swisslog MDB | Microsoft Access database (.mdb). Requires Access Database Engine 2016 on Windows. The app offers automatic installation if missing. |
| Log4OM SQLite | Log4OM v2 — reads the SQLite database directly. No export needed. |
| ADIF | Universal format exported by any logging software. Reload on demand or at interval. |
Reload interval
Configurable in minutes from the dashboard. The log thread wakes at each interval, reads the source, and updates the in-memory DXCC/band/mode database used by the alert engine.
Docker path handling
Inside the container, the host filesystem is mounted read-only at /hostfs. The function host_path() transparently converts host paths entered in the dashboard to their /hostfs-prefixed equivalents. The directory browser (📂) navigates /hostfs and displays the real host paths.
Fuentes de log
DX Monitor lee tu libro de QSO para saber qué DXCCs, bandas y modos ya tienes trabajados. Se soportan cuatro formatos, seleccionables desde el dashboard.
| Formato | Notas |
|---|---|
| HRD XML | Ham Radio Deluxe — exportación automática en segundo plano. DX Monitor vigila un directorio y recarga los ficheros XML al intervalo configurado. Ruta navegable desde el explorador del dashboard. |
| Swisslog MDB | Base de datos Microsoft Access (.mdb). Requiere Access Database Engine 2016 en Windows. La app ofrece instalación automática si falta. |
| Log4OM SQLite | Log4OM v2 — lee la base de datos SQLite directamente. Sin necesidad de exportar. |
| ADIF | Formato universal exportado por cualquier programa de log. Recarga bajo demanda o al intervalo configurado. |
Intervalo de recarga
Configurable en minutos desde el dashboard. El hilo de log se activa en cada intervalo, lee la fuente y actualiza la base de datos en memoria de DXCC/banda/modo que usa el motor de alertas.
Paths en Docker
Dentro del contenedor, el filesystem del host se monta en solo lectura en /hostfs. La función host_path() convierte de forma transparente los paths del host introducidos en el dashboard a sus equivalentes prefijados con /hostfs. El explorador de directorios (📂) navega /hostfs y muestra los paths reales del host.
Real-time Log & Alerts
The bottom of the dashboard shows two live panels updated by SSE — no page refresh, no polling.
How SSE works
No watcher thread. _escribir_status() writes status.json atomically with os.replace and calls sse_push() directly — zero latency between event and browser update. If the connection drops, the dashboard reconnects automatically after 5 seconds.
Live DX alerts panel
Each incoming alert that passes all filters appears instantly: callsign, entity, frequency, band, mode, azimuth SP/LP, distance, spotter, and timestamp. Anti-duplicate window (10 min) prevents repeated entries for the same spot.
System log panel
Last 40 lines of dx_monitor.log, color-coded by level:
Log lines are translated to the dashboard language (ES/EN), including internal key names like pais_nuevo, modo_sin_qsl, etc. Log timestamps respect the timezone configured in config.json.
Log y alertas en tiempo real
La parte inferior del dashboard muestra dos paneles en vivo actualizados por SSE — sin recarga de página, sin polling.
Cómo funciona SSE
Sin hilo vigilante. _escribir_status() escribe status.json atómicamente con os.replace y llama a sse_push() directamente — latencia cero entre evento y actualización en el navegador. Si se pierde la conexión, el dashboard reconecta automáticamente tras 5 segundos.
Panel de alertas DX en vivo
Cada alerta entrante que pasa todos los filtros aparece al instante: indicativo, entidad, frecuencia, banda, modo, azimut SP/LP, distancia, spotter y timestamp. La ventana antiduplicados (10 min) evita entradas repetidas para el mismo spot.
Panel de log del sistema
Últimas 40 líneas de dx_monitor.log, coloreadas por nivel:
Las líneas del log se traducen al idioma del dashboard (ES/EN), incluidas las claves internas como pais_nuevo, modo_sin_qsl, etc. Los timestamps respetan la zona horaria configurada en config.json.
Dashboard
The web dashboard at http://<nas-ip>:8765 is the single interface for all configuration and monitoring. No file editing required. Language (ES/EN) and theme (dark/light) are stored in localStorage.
Sections
| Section | Description |
|---|---|
| HRD Log | Stats (QSOs, DXCC confirmed/worked), callsign, locator, XML directory with 📂 file browser that navigates the host filesystem via /hostfs and shows real host paths. |
| DX Cluster | Connection state, Connect/Disconnect buttons, inline cluster config (host, port, login, password with 👁 toggle), last spot time. |
| Telegram | Token (👁 toggle), Chat ID, language selector, timezone selector (40+ grouped zones + manual IANA field), local/UTC time toggle, test button. |
| Alert Filters | IARU region, active bands, active modes, 6 alert-type toggles, propagation minimum slider. All persisted in flags.json with 300 ms debounce. |
| Live Alerts | Last 10 alerts updated in real time via SSE. Shows callsign, entity, frequency, band, mode, spotter, azimuth SP/LP, distance. |
| System Log | Last 40 lines of dx_monitor.log, colour-coded by level, translated to the dashboard language (ES/EN). |
Config persistence
All fields save on blur via POST /api/config. Filter toggles and the propagation slider save with 300 ms debounce via POST /api/flags. Config and flags are loaded once at startup (loadInitialData()) — polling never overwrites user edits in progress.
SSE connection
The dashboard opens a persistent EventSource to /api/alerts/stream. If the connection drops it reconnects automatically after 5 seconds. Current alerts are pushed immediately on connect — no separate polling needed.
Dashboard
El dashboard web en http://<ip-del-nas>:8765 es la interfaz única para toda la configuración y monitorización. Sin edición de ficheros. El idioma (ES/EN) y el tema (dark/light) se guardan en localStorage.
Secciones
| Sección | Descripción |
|---|---|
| HRD Log | Estadísticas (QSOs, DXCC confirmados/trabajados), indicativo, locator, directorio XML con explorador de archivos 📂 que navega el filesystem del host via /hostfs y muestra los paths reales del host. |
| DX Cluster | Estado de conexión, botones Conectar/Desconectar, configuración inline del cluster (host, puerto, login, contraseña con toggle 👁), hora del último spot. |
| Telegram | Token (toggle 👁), Chat ID, selector de idioma, selector de zona horaria (40+ zonas agrupadas + campo IANA manual), toggle hora local/UTC, botón de test. |
| Filtros de alerta | Región IARU, bandas activas, modos activos, 6 toggles de tipo de alerta, slider de propagación mínima. Todo persistido en flags.json con debounce de 300 ms. |
| Alertas en vivo | Últimas 10 alertas actualizadas en tiempo real por SSE. Muestra indicativo, entidad, frecuencia, banda, modo, spotter, azimut SP/LP, distancia. |
| Log del sistema | Últimas 40 líneas de dx_monitor.log, coloreadas por nivel, traducidas al idioma del dashboard (ES/EN). |
Persistencia de configuración
Todos los campos se guardan al perder el foco (blur) via POST /api/config. Los toggles de filtros y el slider de propagación se guardan con debounce de 300 ms via POST /api/flags. Config y flags se cargan una sola vez al inicio (loadInitialData()) — el polling nunca sobreescribe ediciones en curso.
Conexión SSE
El dashboard abre un EventSource persistente a /api/alerts/stream. Si se pierde la conexión, reconecta automáticamente tras 5 segundos. Las alertas actuales se envían de inmediato al conectar — sin polling adicional.
Docker Installation
Quick start
Create a docker-compose.yml:
services: dxmonitor: image: ea3tb/dx-monitor:latest container_name: dx_monitor_docker restart: unless-stopped ports: - "8765:8765" volumes: - /:/hostfs:ro - dx_docker_data:/opt/dx_monitor_docker networks: - dx_net volumes: dx_docker_data: driver: local networks: dx_net: driver: bridge
# Deploy docker compose up -d # Open dashboard http://<nas-ip>:8765
Useful commands
# Real-time logs docker logs -f dx_monitor_docker # Status docker compose ps # Stop / restart docker compose down docker compose up -d # Shell access docker exec -it dx_monitor_docker bash # View active config docker exec dx_monitor_docker cat /opt/dx_monitor_docker/config.json
Update to latest image
# Pull new image and redeploy
docker compose pull
docker compose up -d
config.json is created with empty defaults. Fill in callsign, log path, cluster and Telegram from the dashboard — no file editing needed.
Instalación Docker
Inicio rápido
Crea un docker-compose.yml:
services: dxmonitor: image: ea3tb/dx-monitor:latest container_name: dx_monitor_docker restart: unless-stopped ports: - "8765:8765" volumes: - /:/hostfs:ro - dx_docker_data:/opt/dx_monitor_docker networks: - dx_net volumes: dx_docker_data: driver: local networks: dx_net: driver: bridge
# Desplegar docker compose up -d # Abrir dashboard http://<ip-del-nas>:8765
Comandos útiles
# Logs en tiempo real docker logs -f dx_monitor_docker # Estado docker compose ps # Parar / reiniciar docker compose down docker compose up -d # Acceso al shell docker exec -it dx_monitor_docker bash # Ver config activa docker exec dx_monitor_docker cat /opt/dx_monitor_docker/config.json
Actualizar a la última imagen
# Descargar nueva imagen y redesplegar
docker compose pull
docker compose up -d
config.json se crea con defaults vacíos. Rellena indicativo, ruta del log, cluster y Telegram desde el dashboard — sin editar ficheros.
Configuration
All settings are stored in /opt/dx_monitor_docker/config.json and edited from the dashboard. Fields save automatically on blur.
| Field | Description |
|---|---|
| callsign | Your amateur radio callsign |
| locator | Maidenhead grid locator — auto-calculates lat/lon for azimuth and distance |
| cluster_host | DX Cluster hostname or IP |
| cluster_port | TCP port (usually 7300 or 7304) |
| cluster_login | Login callsign sent to the cluster |
| cluster_password | Cluster password, hideable with 👁 button |
| telegram_token | Bot API token, hideable with 👁 button |
| telegram_chat_id | Destination chat ID |
| alert_lang | Alert message language: es or en |
| timezone | 40+ grouped zones + manual IANA entry (e.g. Europe/Madrid) |
| time_mode | local (per timezone) or utc |
| hrd_xml_dir | Directory containing HRD XML export files |
| hrd_xml_glob | Glob pattern to match XML files (e.g. *.xml) |
Configuración
Todos los ajustes se guardan en /opt/dx_monitor_docker/config.json y se editan desde el dashboard. Los campos se guardan automáticamente al perder el foco.
| Campo | Descripción |
|---|---|
| callsign | Tu indicativo de radioaficionado |
| locator | Locator Maidenhead — calcula automáticamente lat/lon para azimut y distancia |
| cluster_host | Hostname o IP del DX Cluster |
| cluster_port | Puerto TCP (habitualmente 7300 o 7304) |
| cluster_login | Indicativo de login enviado al cluster |
| cluster_password | Contraseña del cluster, ocultable con el botón 👁 |
| telegram_token | Token de la Bot API, ocultable con el botón 👁 |
| telegram_chat_id | Chat ID de destino |
| alert_lang | Idioma de las alertas: es o en |
| timezone | 40+ zonas agrupadas + campo IANA manual (ej. Europe/Madrid) |
| time_mode | local (según timezone) o utc |
| hrd_xml_dir | Directorio con los ficheros de exportación XML de HRD |
| hrd_xml_glob | Patrón glob para los ficheros XML (ej. *.xml) |
Alert Filters
Stored in flags.json. All toggles are available from the dashboard and take effect immediately without restarting.
| Filter | Values | Description |
|---|---|---|
| iaru_region | R1 / R2 / R3 | Band plan used for mode inference and frequency validation |
| bandas_activas | 160m – 23cm | Only spots on selected bands trigger alerts |
| modos_activos | CW, SSB, RTTY, FT8, FT4 | Only spots in selected modes trigger alerts |
| pais_nuevo | on/off | Alert for new DXCC entity |
| pais_trabajado | on/off | Alert for worked DXCC without QSL |
| banda_nueva | on/off | Alert for new band on known DXCC |
| banda_sin_qsl | on/off | Alert for band worked, QSL pending |
| modo_nuevo | on/off | Alert for new mode |
| modo_sin_qsl | on/off | Alert for mode worked, QSL pending |
| prop_min | 0–99 | Minimum propagation score. Spots where max(SP, LP) is below this value are silently discarded. 0 = no filter (default). |
Filtros de alerta
Guardados en flags.json. Todos los toggles están disponibles desde el dashboard y surten efecto inmediatamente sin reiniciar.
| Filtro | Valores | Descripción |
|---|---|---|
| iaru_region | R1 / R2 / R3 | Band plan usado para inferencia de modo y validación de frecuencia |
| bandas_activas | 160m – 23cm | Solo los spots en las bandas seleccionadas generan alertas |
| modos_activos | CW, SSB, RTTY, FT8, FT4 | Solo los spots en los modos seleccionados generan alertas |
| pais_nuevo | on/off | Alerta para nueva entidad DXCC |
| pais_trabajado | on/off | Alerta para DXCC trabajado sin QSL |
| banda_nueva | on/off | Alerta para nueva banda en DXCC conocido |
| banda_sin_qsl | on/off | Alerta para banda trabajada con QSL pendiente |
| modo_nuevo | on/off | Alerta para nuevo modo |
| modo_sin_qsl | on/off | Alerta para modo trabajado con QSL pendiente |
| prop_min | 0–99 | Score mínimo de propagación. Los spots donde max(SP, LP) queda por debajo de este valor se descartan silenciosamente. 0 = sin filtro (por defecto). |