cloudlog/assets/js/dxcluster-utils.js
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

193 lines
8 KiB
JavaScript

/**
* DX Cluster shared utility functions.
* Used by the DX Cluster page (dxcluster/index.php) and the
* QSO entry DX Cluster tab (qso/index.php).
*
* Exposed as: DXCluster.isRbnSpot(), DXCluster.getBandFromFrequency(),
* DXCluster.detectModeFromFrequency(), DXCluster.detectMode()
*/
var DXCluster = (function () {
'use strict';
/**
* Returns true if the spotter callsign is an RBN (Reverse Beacon Network) skimmer.
* RBN skimmers always end with -# (literal hash character).
*/
function isRbnSpot(spotter) {
if (!spotter) return false;
return /\-#+$/.test(spotter.toString().trim());
}
/**
* Returns the amateur band name for a given frequency in kHz,
* or 'unknown' if it falls outside any recognised band.
*/
function getBandFromFrequency(freqKhz) {
var freq = parseFloat(freqKhz);
if (freq >= 1800 && freq <= 2000) return '160m';
if (freq >= 3500 && freq <= 4000) return '80m';
if (freq >= 5250 && freq <= 5450) return '60m';
if (freq >= 7000 && freq <= 7300) return '40m';
if (freq >= 10100 && freq <= 10150) return '30m';
if (freq >= 14000 && freq <= 14350) return '20m';
if (freq >= 18068 && freq <= 18168) return '17m';
if (freq >= 21000 && freq <= 21450) return '15m';
if (freq >= 24890 && freq <= 24990) return '12m';
if (freq >= 28000 && freq <= 29700) return '10m';
if (freq >= 50000 && freq <= 54000) return '6m';
if (freq >= 70000 && freq <= 71000) return '4m';
if (freq >= 144000 && freq <= 148000) return '2m';
if (freq >= 420000 && freq <= 450000) return '70cm';
if (freq >= 1240000 && freq <= 1300000) return '23cm';
if (freq >= 1300000) return 'ghz';
return 'unknown';
}
/**
* Estimates the operating mode from a frequency in kHz using:
* 1. Known FT8/FT4 spot frequencies (±2 kHz tolerance)
* 2. IARU Region 1 band-plan segments
* Returns 'cw', 'digi', or 'ssb'.
*/
function detectModeFromFrequency(freqKhz) {
// Known FT8 / FT4 spot frequencies (kHz)
var ft8ft4Freqs = [
1836, // FT8 160m
1840, // FT8 160m
1844, // FT4 160m
3567, // FT8 80m
3573, // FT8 80m
3575.5, // FT4 80m
5357, // FT8 60m
7047.5, // FT4 40m
7074, // FT8 40m
10130, // FT8 30m
10136, // FT8 30m
10140, // FT8 30m
14074, // FT8 20m
14080, // FT4 20m
14090, // FT8 20m
18095, // FT8 17m
18100, // FT8 17m
18104, // FT4 17m
21074, // FT8 15m
21090, // FT8 15m
21140, // FT4 15m
24911, // FT8 12m
24915, // FT8 12m
24919, // FT4 12m
28074, // FT8 10m
28090, // FT8 10m
28095, // FT8 10m
28180, // FT4 10m
50313, // FT8 6m
50318, // FT4 6m
50323, // FT8 6m
70154, // FT8 4m
144174, // FT8 2m
144170, // FT4 2m
432174 // FT8 70cm
];
var tolerance = 2; // kHz
for (var i = 0; i < ft8ft4Freqs.length; i++) {
if (Math.abs(freqKhz - ft8ft4Freqs[i]) <= tolerance) return 'digi';
}
// 160m
if (freqKhz >= 1800 && freqKhz < 1840) return 'cw';
if (freqKhz >= 1840 && freqKhz < 1843) return 'digi';
if (freqKhz >= 1843 && freqKhz <= 2000) return 'ssb';
// 80m
if (freqKhz >= 3500 && freqKhz < 3570) return 'cw';
if (freqKhz >= 3570 && freqKhz < 3600) return 'digi';
if (freqKhz >= 3600 && freqKhz <= 4000) return 'ssb';
// 60m
if (freqKhz >= 5351 && freqKhz < 5354) return 'cw';
if (freqKhz >= 5354 && freqKhz < 5358) return 'digi';
if (freqKhz >= 5358 && freqKhz <= 5366) return 'ssb';
// 40m
if (freqKhz >= 7000 && freqKhz < 7040) return 'cw';
if (freqKhz >= 7040 && freqKhz < 7060) return 'digi';
if (freqKhz >= 7060 && freqKhz <= 7300) return 'ssb';
// 30m — CW + digi only
if (freqKhz >= 10100 && freqKhz < 10130) return 'cw';
if (freqKhz >= 10130 && freqKhz <= 10150) return 'digi';
// 20m
if (freqKhz >= 14000 && freqKhz < 14070) return 'cw';
if (freqKhz >= 14070 && freqKhz < 14112) return 'digi';
if (freqKhz >= 14112 && freqKhz <= 14350) return 'ssb';
// 17m
if (freqKhz >= 18068 && freqKhz < 18095) return 'cw';
if (freqKhz >= 18095 && freqKhz < 18110) return 'digi';
if (freqKhz >= 18110 && freqKhz <= 18168) return 'ssb';
// 15m
if (freqKhz >= 21000 && freqKhz < 21070) return 'cw';
if (freqKhz >= 21070 && freqKhz < 21151) return 'digi';
if (freqKhz >= 21151 && freqKhz <= 21450) return 'ssb';
// 12m
if (freqKhz >= 24890 && freqKhz < 24915) return 'cw';
if (freqKhz >= 24915 && freqKhz < 24930) return 'digi';
if (freqKhz >= 24930 && freqKhz <= 24990) return 'ssb';
// 10m
if (freqKhz >= 28000 && freqKhz < 28070) return 'cw';
if (freqKhz >= 28070 && freqKhz < 28190) return 'digi';
if (freqKhz >= 28190 && freqKhz <= 29700) return 'ssb';
// 6m
if (freqKhz >= 50000 && freqKhz < 50100) return 'cw';
if (freqKhz >= 50100 && freqKhz < 50300) return 'ssb';
if (freqKhz >= 50300 && freqKhz <= 50500) return 'digi';
if (freqKhz > 50500 && freqKhz <= 54000) return 'ssb';
// 4m
if (freqKhz >= 70000 && freqKhz < 70100) return 'cw';
if (freqKhz >= 70100 && freqKhz < 70200) return 'digi';
if (freqKhz >= 70200 && freqKhz <= 70500) return 'ssb';
// 2m
if (freqKhz >= 144000 && freqKhz < 144173) return 'cw';
if (freqKhz >= 144170 && freqKhz < 144176) return 'digi';
if (freqKhz >= 144176 && freqKhz <= 148000) return 'ssb';
// 70cm
if (freqKhz >= 432000 && freqKhz < 432170) return 'cw';
if (freqKhz >= 432170 && freqKhz < 432175) return 'digi';
if (freqKhz >= 432175 && freqKhz <= 450000) return 'ssb';
// Other VHF/UHF/SHF — default to SSB
return 'ssb';
}
/**
* Detects the mode of a DX cluster spot.
* Priority: comment keywords → RBN skimmer → frequency band-plan.
* Returns 'cw', 'digi', or 'ssb'.
*/
function detectMode(spot) {
var comment = ((spot.comment || '') + '').toUpperCase();
// Digital modes — checked first so RTTY/FT8 etc. are not misclassified as CW/SSB
var digiKeywords = ['FT8', 'FT4', 'JS8CALL', 'JS8', 'WSPR', 'JT65', 'JT9',
'PSK31', 'PSK63', 'PSK', 'RTTY', 'BPSK', 'QPSK',
'OLIVIA', 'MFSK', 'MT63', 'FREEDV', 'WINLINK', 'VARA',
'PACKET', 'APRS', 'SSTV', 'DIGI', 'DIGITAL'];
for (var i = 0; i < digiKeywords.length; i++) {
if (comment.indexOf(digiKeywords[i]) !== -1) return 'digi';
}
if (comment.indexOf('CW') !== -1 || comment.indexOf('MORSE') !== -1) return 'cw';
if (comment.indexOf('SSB') !== -1 || comment.indexOf('LSB') !== -1 ||
comment.indexOf('USB') !== -1 || comment.indexOf(' AM ') !== -1 ||
comment.indexOf('PHONE') !== -1 || comment.indexOf('FM ') !== -1 ||
comment.indexOf(' FM') !== -1) return 'ssb';
// RBN skimmer spots are always CW
if (isRbnSpot(spot.spotter)) return 'cw';
// Fall back to frequency band-plan detection
return detectModeFromFrequency(parseFloat(spot.frequency));
}
return {
isRbnSpot: isRbnSpot,
getBandFromFrequency: getBandFromFrequency,
detectModeFromFrequency: detectModeFromFrequency,
detectMode: detectMode
};
}());