Commit graph

7817 commits

Author SHA1 Message Date
Peter Goodhall
4caceba540
2.8.12 2026-04-14 14:19:30 +01:00
Peter Goodhall
24f7561a63 Add migration to bump version to 2.8.12
Add migration (application/migrations/266_tag_2_8_12.php) to update the stored application version to 2.8.12. The up() method sets options.version to '2.8.12' and resets user_options where option_type='version_dialog' and option_name='confirmed' to 'false' to trigger the version info dialog. The down() method reverts the options.version back to '2.8.11'.
2026-04-14 14:14:35 +01:00
Peter Goodhall
e23decde7e Conditional action buttons in logbooks list
Make action buttons in the logbooks index conditional while keeping layout intact. Always show the Edit button; show Embed only when a public_slug exists; show Share only for the owner or admin; show Delete only when the logbook is not the active station logbook and the user is the owner. Hidden placeholder buttons include tabindex and aria-hidden to preserve spacing and accessibility. Added inline comments for clarity.
2026-04-13 22:28:00 +01:00
Peter Goodhall
b1fcc8ffa1 Stop resetting band filter to 'all' on radio none
Remove fallback logic that forced the cluster band filter to 'all' when no radio (or radio '0') was selected. The syncBandFromRadio() method now simply applies the current #band value if present and otherwise leaves the filter unchanged. Also cleaned up related comments to reflect the simplified behavior, so band changes always sync without implicitly switching to 'all'.
2026-04-13 17:53:58 +01:00
Peter Goodhall
244d4c235e Use innerHTML swap for #qso-last-table
Change htmx.ajax swap option from 'outerHTML' to 'innerHTML' when updating the #qso-last-table. This ensures only the container's contents are replaced (preserving the container element and any bindings/listeners) and avoids DOM reattachment issues.
2026-04-13 14:49:25 +01:00
Peter Goodhall
16e47ac92b Preserve DXCC badge when resetting QSO fields
Add an optional preserveDxccState parameter to resetDefaultQSOFields to keep DXCC-related UI state (country, callsign_info text/title/class, dxcc_id, CQ zone, ITU zone) when requested, preventing badge/country flicker. Update the callsign focusout flow to use this option. Also remove unnecessary .trigger('change') calls on dxcc_id assignments to avoid redundant change events and adjust field clearing/restoration order accordingly.
2026-04-13 14:12:16 +01:00
Peter Goodhall
9913eb08a2 Improve callsign lookup, caching & recent details
Add a cached DXCC lookup and a jsondxcc endpoint to speed up country resolution and reduce DB load (file-cache with 1h TTL). Introduce cached_dxcc_lookup(), build_confirmation_where(), and callsign_status() in the Logbook controller; refactor confirmed_grid_before() and worked_grid_before() to accept logbook relationships and confirmation prefs to avoid redundant queries. Use a new Logbook_model::get_recent_callsign_details() to populate recent name/gridsquare/qth/iota/qsl/state/county values in a single query. Update JS to better manage lookup state, invalidate in-flight requests, and perform a debounced quick DXCC lookup to improve UX. Tweak previous-contacts pagination to a compact sliding window and remove a redundant UI note.
2026-04-13 13:57:03 +01:00
Peter Goodhall
c7b0a75055 Pause auto-refresh during callsign lookup
Prevent the logbook auto-refresh from overwriting callsign lookup results by introducing a previousContactsLookupMode flag and pauseAutoRefresh()/resumeAutoRefresh() helpers that remove/restore the htmx hx-trigger on the qso list element. Clear and hide partial_view on reset and resume auto-refresh there. Show an explicit "no past QSOs" info message when a callsign is found but has no previous QSOs, and ensure the previous-contacts panel state is set deterministically. Also harden CAT radioID checking to treat undefined and falsy values as invalid. Changes touch qso.js and the footer view.
2026-04-13 13:36:45 +01:00
Peter Goodhall
9e18606812 Safely handle partial lookup and toggle panel
Treat lookup.partial defensively (ensure it's a string) before injecting into #partial_view, and determine whether to show previous-contacts panel based on trimmed content instead of always enabling it. Clear #partial_view when hiding the panel to avoid stale content, and simplify the htmx init check to rely on the presence of content rather than visibility. These changes prevent accidental HTML insertion and make panel toggling deterministic.
2026-04-10 22:12:59 +01:00
Peter Goodhall
12f20780dc Hide empty partial view and load DXX summary
Hide the #partial_view container by default and only reveal the previous-contacts panel when it contains non-empty HTML. Invoke getDxccResult after injecting the partial result so the DXX summary is fetched and displayed. Add handling to reset QSO fields and switch back to the Previous Contacts tab when the callsign is cleared, and remove duplicated/unused code in the callsign focusout handler. This prevents showing an empty lookup panel and cleans up the logic around loading lookup details.
2026-04-10 14:00:55 +01:00
Peter Goodhall
6e8d73e07b Recalculate highlight DX using grid/DXCC
Add helper methods to determine a QSO's primary grid, lookup DXCC coordinates (with caching and logbook_model.dxcc_lookup fallback), and resolve candidate distances by preferring grid-to-grid QRA distance with a DXCC-coordinate fallback. Update highlight DX queries to select time, DXCC and DXCC lat/long and join dxcc_entities. Replace duplicated inline distance logic with a single build_highlight_dx_from_candidates routine that picks the farthest candidate and sets COL_DISTANCE (rounded to int). This centralizes distance computation and ensures QSOs without stored distances but with grids or DXCC data are correctly considered.
2026-04-10 11:36:40 +01:00
Peter Goodhall
79084da3cd Show QSP tab and clear additional fields
After saving a QSO, switch to the QSP tab if present (using bootstrap.Tab.show()). Enhance reset_fields() to clear wwff and pota info (HTML and title) and safely clear the sota_ref Selectize control (if initialized) instead of using .val(""), preventing leftover values/tooltips and avoiding errors when Selectize isn't present.
2026-04-10 11:23:54 +01:00
Peter Goodhall
d6ffc494f7 Remove redundant CSS from user edit view
Remove inline CSS rules from application/views/user/edit.php: deleted .user_edit custom properties (--gh-border, --gh-muted, --gh-bg-subtle) and .settings-nav .list-group-item styles (cursor, margin-bottom). This cleans up duplicated/unused styling that should be provided by global stylesheets.
2026-04-10 11:19:25 +01:00
Peter Goodhall
929d428e54 Refactor user settings UI to two-column layout
Revamp user edit view into a responsive two-column settings UI: add sidebar navigation, GitHub-like styles, and JS to switch setting panes. Reorganize and present existing options (main menu, map params, default values, Hams.at, AMSAT, Mastodon, hardware, QSO form) as cards inside accordion panes, remove duplicated blocks, and align the save button. Changes improve usability and navigation in application/views/user/edit.php.
2026-04-08 22:43:18 +01:00
Peter Goodhall
e197a5c1f2 Add toggle for QSL Cards menu item
Introduce a user-controllable option to show/hide the "View QSL Cards" link in the Logbook menu. Controller: initialize menu_show_qsl_cards, load/save the 'show_qsl_cards' menu option and sync it to the session during profile updates. Model: fetch the 'show_qsl_cards' option and expose user_show_qsl_cards in the user data. Views: add a checkbox in the user edit form to control the setting, remove the duplicate SSTV storage card UI, and update the header to robustly read the session value and conditionally render the QSL menu item. Defaults to showing the menu item when the option is unset.
2026-04-08 22:26:05 +01:00
Peter Goodhall
de293f6ed3 Relocate SSTV menu toggle into Storage Options
Move the "Show SSTV Images" menu checkbox out of its previous standalone section and into a new "Storage Options" card. Removed the old <hr> and explanatory paragraph and added a new card column with a header, brief description, and the existing checkbox so image-related menu controls are grouped together and the form layout is cleaner.
2026-04-08 22:19:42 +01:00
Peter Goodhall
297ff0af42 Add SSTV menu toggle for users
Introduce a user preference to show or hide the "View SSTV Images" menu item. Controller: initialize menu_show_sstv_images, load/save the 'menu:show_sstv_images' option and set session user_show_sstv_images when profile is saved. Model: fetches the menu option and exposes user_show_sstv_images (and has_eqsl_credentials) in the returned user data. Views: add a checkbox to the user edit form to toggle the setting, and conditionally render the eQSL/SSTV menu items in the header based on session flags. This lets users control visibility of the SSTV menu entry from their profile.
2026-04-08 22:18:02 +01:00
Peter Goodhall
fc6e0a7a8b Manage previous contacts panel visibility
Add setPreviousContactsPanelState(showLookupDetails) to centralize show/hide logic for #qso-last-table, its trailing <small>, and #partial_view. Replace inline show/hide calls with this helper, update resetToPreviousContactsTab to use it, and add an htmx:afterSwap listener to reapply the visibility state after HTMX updates the previous contacts markup. This ensures the lookup details and default previous-contacts table remain consistent across dynamic updates.
2026-04-08 16:06:03 +01:00
Peter Goodhall
830df2d00e Clear CAT value cache on UI reset
When resetting the UI, clear the stored CAT value data on frequency, satellite and mode inputs so re-selecting a radio with identical values will still repopulate fields. Adds a jQuery removeData('catValue') call for #frequency, #frequency_rx, #sat_name, #sat_mode, #transmit_power, #selectPropagation and #mode, with a comment explaining the intent.
2026-04-07 16:21:30 +01:00
Peter Goodhall
766a940003 Suppress reset handler for programmatic resets
Add a suppressNextResetHandler flag to avoid running the reset event handler when the QSO form is reset programmatically (set the flag before calling qsoFormElement.reset()). Introduce clearCatTrackedFieldState() to remove stored 'catValue' data from frequency/satellite/mode/power fields and call it from reset_fields(). Also initialize suppressNextResetHandler and short-circuit the '#qso_input' reset handler when the flag is set to prevent unintended UI side effects.
2026-04-07 16:12:10 +01:00
Peter Goodhall
b96b205b64 Improve CAT sync and prevent stale updates
Add request/version tracking to CAT JSON handling to avoid applying stale responses when radio selection changes. Introduce catRequestCounter, lastProcessedCatRequest and catSelectionContextVersion, compare request IDs and context version before updating UI, and increment context version on radio selection change. Also add a syncFromSelectedRadioAfterReset helper in qso.js (with a throttled user notice) and call it after form reset and on escape-based reset so the form prefers live CAT values from the selected radio.
2026-04-07 14:45:22 +01:00
Peter Goodhall
fdba5ab166 Preserve band/mode/satellite on form reset
Before resetting the QSO form, capture the user's current operating context (band, mode, satellite name/mode, propagation). After qsoForm.reset() and the built-in reset handlers run, reapply those captured values (with a 150ms timeout) so the UI doesn't revert to server-session defaults or the previous QSO values. Also call setRst(mode) if available to update RST for the restored mode.
2026-04-07 14:36:05 +01:00
Peter Goodhall
da5fa486d8 Update htmx target IDs in previous contacts
Rename the container id from "qso-last-table" to "qso-last-table-content" and update all pagination links' hx-target attributes accordingly. This ensures htmx swaps and the 5s auto-refresh target the correct element for the previous contacts component.
2026-04-07 10:04:15 +01:00
Peter Goodhall
d4251cdf41 Add no-cache headers and localize HTMX polling
Prevent caching for the HTMX endpoint and move polling into the component. Qso controller now sets Cache-Control/Pragma/Expires headers for the component_past_contacts endpoint to ensure fresh data. The previous_contacts view was updated to self-poll (hx-get with page param, hx-trigger every 5s, hx-target="this", and hx-vals timestamp) to include a cache-busting timestamp. The main qso index removed the redundant recurring poll (now only triggers on load) to avoid duplicate requests.
2026-04-06 16:04:26 +01:00
Peter Goodhall
8716e6caae Clear satellite fields when not SAT and on reset
Ensure satellite fields are cleared whenever propagation mode is not 'SAT' and when the QSO form is reset. In Qso.php, sat_name and sat_mode are set to empty strings if prop_mode != 'SAT' (added in two handling points). In assets/js/sections/qso.js, added clearSatelliteFields() to clear sat_name, sat_mode, satellite_modes_list and related state, call it from reset_fields(), and invoke the form reset flow to ensure no stale satellite data remains after resetting or changing propagation mode.
2026-04-06 14:37:56 +01:00
Peter Goodhall
fcd1b6242e Remove QSO count from View QSO List summary
Removed the dynamic contact count from the 'View QSO List' summary in application/views/station_diary/public_index.php. The inline '<span class="qso-count">' count display was removed, leaving only the static summary text; data attributes and table markup remain unchanged.
2026-04-06 11:21:06 +01:00
Peter Goodhall
e904595a9d Lazy-load QSO lists; add time index migration
Bump migration version to 265 and add a migration (265_add_station_time_on_index) that creates idx_station_time_on on (station_id, COL_TIME_ON) for faster time-based queries. Introduce lazy QSO loading: controller now sets defer_qso_list and uses a new POST endpoint get_filtered_qsos to fetch filtered QSO lists and summaries; also updated cache keys/render version and added per-entry cache deletion. Note model: add include_qso_list flag, memoize QSO summaries/lists and station IDs, replace DATE(COL_TIME_ON) filters with half-open datetime ranges (start inclusive, end exclusive) via helper methods to improve index use and performance. View: update public_index to render QSO list containers with data attributes, add JS to fetch and render QSO rows on demand, and support highlight DX updates. Overall changes optimize QSO queries and enable deferred loading to reduce initial page load and DB cost.
2026-04-06 11:11:03 +01:00
Peter Goodhall
5edc233960 Reapply mode and satellite fields after QSO save
When a QSO is saved, preserve the selected mode, satellite name, and satellite mode so the user doesn't have to reselect them for the next entry. Capture savedMode, savedSatName, and savedSatMode into postSaveDefaults and call a new reapplyPostSaveDefaults() after reset_fields(). Added reapplyPostSaveDefaults() to restore band, mode, sat_name, sat_mode and call setRst() if available.
2026-04-05 17:53:22 +01:00
Peter Goodhall
85d5ad6958 Reapply default RST on reset_fields
When resetting QSO fields, reapply the default RST for the current mode. The change calls setRst($('.mode').val()) if the setRst function exists, ensuring mode-specific defaults (e.g., CW => 599) are restored after a form reset to avoid stale RST values.
2026-04-05 17:51:03 +01:00
Peter Goodhall
b8b87e5d50 Escape QSO notice values and include band
Add escapeNoticeValue to sanitize user-provided strings (&, <, >, ", ') to prevent XSS in notice messages. Use a new savedBand variable and update saveMessage assembly to include both callsign and band when available, with fallbacks for when only one is present. Changes are in assets/js/sections/qso.js.
2026-04-05 17:38:46 +01:00
Peter Goodhall
5d7135124d Add AJAX QSO save endpoint and client handler
Introduce an AJAX-based QSO save flow: add QSO::ajax_saveqso() in the controller to validate input, set session/cookies, create the QSO and return JSON success/error payloads. Update the QSO view to include a data-ajax-save-url on the form and add a notice-alerts container location. Enhance the qso.js client: add isSubmitting guard, showQsoNotice helper, intercept form submit to POST serialized form data to the AJAX endpoint, handle success (reset form, show notice, refresh recent QSOs via htmx), display validation/server errors, and restore UI state when complete. These changes avoid full page reloads on save and provide inline validation feedback.
2026-04-05 17:36:52 +01:00
Peter Goodhall
aaf3aa85a6 Add API endpoint for accessible public slugs
Introduce API method logbook_public_slugs_accessible($key) that authorizes the API key, updates last-used timestamp, and returns JSON of all station logbooks (owned or shared) that have non-empty public slugs for the key owner. Add Logbooks_model::public_slugs_accessible_by_user($user_id) to select matching logbooks, label access_level as "owner" for owners or the stored permission_level for shared entries, and order results by logbook_name. Endpoint returns 401 for missing/invalid keys and a success payload with status, count, and logbooks on success.
2026-04-03 22:35:25 +01:00
Peter Goodhall
78209d497b Add API endpoint for logbook public slugs
Introduce GET /api/logbook_public_slugs/{key} in the API controller to return a JSON list of the API key owner's station logbooks that have non-empty public slugs. The endpoint authorizes the key, updates last-used timestamp, responds with 401 for missing/invalid keys or 200 with status, count and logbook entries. Add Logbooks_model::public_slugs_by_user to query station_logbooks for non-null/non-empty public_slug values ordered by logbook_name.
2026-04-03 22:00:35 +01:00
Peter Goodhall
41bc5a2104 Add gridsquare support to worked status API
Expose an optional `gridsquare` parameter to logbook_worked_status: update the docblock, parse and normalize the incoming gridsquare, and include it in the response payload. Add worked flags `grid_overall` and `grid_band` (and set them based on logbook_model->check_if_grid_worked_in_logbook results) so clients can check whether a grid square has been worked overall or on a specific band. Input is upper-cased/trimmed like other fields; existing DB logic is left intact.
2026-04-02 14:28:36 +01:00
Peter Goodhall
4ab0bff623 Add logbook_worked_status API endpoint
Introduce POST /api/logbook_worked_status to check "worked" and "confirmed" status for a given callsign (and derived country) against a public logbook. The endpoint requires an API key, logbook_public_slug and callsign, and accepts optional band and mode. It validates input and logbook/station relationships, resolves country via DXCC lookup, maps mode to a main mode, and queries the logbook table for overall and band-specific worked status as well as LoTW/QSL confirmations. Returns structured JSON with worked and confirmed subfields and appropriate HTTP status codes (400/401/404/200). Models used: api_model, logbook_model, logbooks_model; uses configured table_name for DB queries.
2026-04-02 14:06:05 +01:00
Peter Goodhall
868638e1b2 Render Markdown-like formatting in version dialog
Add a lightweight Markdown-like parser for admin-provided Version Dialog text and release notes. The changes convert inline code, headers, horizontal rules, blockquotes, strikethrough, bold/italic, bullet and numbered lists (including task list checkboxes), images, links, GitHub username mentions, and auto-link URLs. It also protects block elements from nl2br, removes excessive <br> tags, and makes images responsive. The same processing is applied in both display locations to improve readability and preserve existing behavior when no text is set.
2026-04-01 13:16:28 +01:00
Peter Goodhall
b3cad5b953 Handle Markdown horizontal rules
Convert markdown horizontal rules (---, ***, ___) to <hr> and treat <hr> as a block-level element when protecting content from nl2br. Updated regexes to insert <hr>, include hr in the block marker replacement, and clean up surrounding <br> tags so horizontal rules are not polluted by extra line breaks.
2026-04-01 12:58:27 +01:00
Peter Goodhall
53a80b9e08
2.8.11 2026-04-01 12:49:56 +01:00
Peter Goodhall
e5cd953d2d Add migration 264 tagging v2.8.11
Bump migration_version to 264 and add Migration_tag_2_8_11. The new migration updates the 'options' table to set version = '2.8.11' and resets users' 'version_dialog' confirmed flag to 'false' to trigger the version info dialog. The down() method reverts the stored version back to '2.8.10'.
2026-04-01 12:44:53 +01:00
Peter Goodhall
8fcbfafbc8 Allow overriding displayed callsign in widgets
Add an optional $display_callsign parameter to on_air and on_air_image so embedders can show a different callsign than the one used to look up the user (e.g., /widgets/on_air/M0ABC/GB2XXX). Update embed examples and use the display_callsign when provided (sanitised with xss_clean and strtoupper) while preserving the original lookup behavior.
2026-03-30 10:45:51 +01:00
Peter Goodhall
3be655e64d Add migration to convert notes to utf8mb4
Bump migration_version to 263 and add Migration_notes_utf8mb4_columns. The new migration converts the notes table to use utf8mb4 with utf8mb4_unicode_ci to support emoji and other 4-byte characters; down() reverts the table back to utf8 with utf8_general_ci.
2026-03-29 23:07:32 +01:00
Peter Goodhall
7d442c8116 Add on_air_image SVG badge endpoint
Introduce Widgets::on_air_image(callsign) — an embeddable SVG "on air" status badge (usage: /widgets/on_air_image/YOURCALL). The method validates the callsign, looks up the user and their recent radio status via user_model and cat, builds a status string (including SAT, frequency+mode, or "qrt" if offline), and chooses a badge color. It computes dynamic badge widths using an approximate Verdana character width, sanitizes text for XML output, includes accessible title/aria-label, and returns the SVG with Content-Type image/svg+xml and Cache-Control: no-cache, max-age=60. Errors are shown for missing callsign or unknown user.
2026-03-29 14:25:41 +01:00
Peter Goodhall
b6a1eb830c Add embeddable on-air widget
Introduce an embeddable "on air" status widget. Adds Widgets::on_air controller action to lookup a user by callsign, sanitize input, and render a new widgets/on_air view (iframe-friendly). Adds Cat::recent_status_by_user_id to fetch recent CAT entries (last 15 minutes) for public display. View shows ON AIR/ QRT badges, radio/satellite details, and includes auto-refresh and minimal styling; usage: /widgets/on_air/YOURCALL.
2026-03-29 14:24:26 +01:00
Peter Goodhall
9bbc58d805 Use COL_VUCC_GRIDS as fallback for grids
Prefer COL_GRIDSQUARE when present and fall back to COL_VUCC_GRIDS when building grid information. Added COL_VUCC_GRIDS to select lists in Note model queries and updated Logbook_model to set plot HTML to VUCC grids if gridsquare is missing. Also updated the public station diary view to display VUCC grids in the table when COL_GRIDSQUARE is empty. Files changed: Logbook_model.php, Note.php, public_index.php.
2026-03-29 14:12:25 +01:00
Peter Goodhall
0b272dc1c8 Improve highlight DX selection using gridsquares
Expand highlight DX query to include QSOs with gridsquares/VUCC when stored distance is missing. Join station_profile to access station_gridsquare, select up to 50 candidate QSOs, prefer stored COL_DISTANCE but compute distances via the Qra library when needed, and pick the farthest candidate (rounded to int). Apply the same logic for daily and date-range queries and preserve the satellite-only filter. Replaces the previous strict COL_DISTANCE>0 single-row lookup to avoid excluding valid DXs derivable from grids.
2026-03-29 14:09:31 +01:00
Peter Goodhall
950f5a37b0 Improve DX Cluster status badge and JS state handling
Replace plain text status with a badge containing an icon and tooltip in the DX Cluster tab (increase font-size to 0.85rem and set cursor to default). Update setStatus() to drive the icon class and tooltip text via a stateMap for common states (Connected, Connecting..., Reconnecting..., Error, Disconnected), set the badge class, and update both title and data-bs-original-title for Bootstrap tooltip compatibility. Adds a fallback for unknown states.
2026-03-29 14:02:23 +01:00
Peter Goodhall
0f62a7178a Add client-side QRA utils and use in QSO
Introduce a frontend QRA utilities module and switch QSO locator/bearing/distance logic to run client-side. Added assets/js/qra-utils.js (ported from the PHP Qra library), injected measurement_base and station_gridsquares into the QSO view, and updated application/controllers/Qso.php to provide the measurement_base for the frontend. Footer now loads qra-utils.js before qso.js. Modified assets/js/sections/qso.js to debounce locator input, place map markers from local grid→lat/lng math, and compute bearing/distance via QraUtils (removes several AJAX calls to server endpoints). This reduces server round-trips and keeps frontend behavior consistent with the PHP implementation.
2026-03-29 13:57:36 +01:00
Peter Goodhall
107bbb3572 Use sanitized $manual variable instead of $_GET
Read and cast the manual mode once into $manual using $this->input->get(...) with a default of 0, and replace all direct $_GET['manual'] usages with $manual. Updates include the JavaScript qso_manual value, form action and resetTimers call, and multiple conditional checks and disabled attributes to improve input handling and consistency. Also ensures EOF newline is present.
2026-03-29 13:47:54 +01:00
Peter Goodhall
cee6cd2def Refactor DXCC checks; add CW RBN lock UI
Replace inline DB queries in Dxcluster with a new Logbook_model::check_if_dxcc_worked_in_logbook method to centralize DXCC existence checks (per-band and overall). Remove several debug log statements from Dxcluster. Add client-side logic in dxcluster and qso views to enforce a CW↔RBN rule: when mode is 'cw' the "hide RBN" option is forced off and the checkbox is disabled (and restored when leaving CW), and apply that lock on load and mode changes. Also disable track-band when the user manually changes band in the QSO cluster UI.
2026-03-29 13:37:16 +01:00
Peter Goodhall
4190b4fd35 Refine FT8/FT4 frequencies and mode ranges
Add/adjust known FT8/FT4 spot frequencies and tighten band mode detection ranges. New FT8/FT4 frequencies were added across multiple bands (160m, 80m, 30m, 20m, 17m, 15m, 12m, 10m, 6m, etc.). Mode boundary logic was refined for 160m, 80m, 60m (added), 40m, 6m, 4m, 2m and 70cm to better reflect common CW/DIGI/SSB allocations and specific FT8 spots. These changes improve automatic mode detection from frequency values.
2026-03-29 11:58:33 +01:00