mirror of
https://github.com/praktimarc/kst4contest.git
synced 2026-09-11 11:45:27 +02:00
Refactored QRG recognition (added some band context to increase the quality of the values + changed cluster fallback-band-value to dropdown switch + normalizing qrg values)
This commit is contained in:
@@ -46,6 +46,49 @@ public class MessageBusManagementThread extends Thread {
|
||||
private final String PTRN_QRG_CAT2 = "(([0-9]{3,4}[\\.|,| ]?[0-9]{3})([\\.|,][\\d]{1,2})?)|(([a-zA-Z][0-4]{1}[\\d]{2}\\b)([\\.|,][\\d]{1,2}\\b)?)|((\\b[0-4]{1}[\\d]{2}\\b)([\\.|,][\\d]{1,2}\\b)?)";
|
||||
private final String PTRN_QRG_CAT3 = "(([0-9]{3,5}[\\.|,| ]?[0-9]{3})([\\.|,][\\d]{1,2})?)|(([a-zA-Z][0-4]{1}[\\d]{2}\\b)([\\.|,][\\d]{1,2}\\b)?)|((\\b[0-4]{1}[\\d]{2}\\b)([\\.|,][\\d]{1,2}\\b)?)";
|
||||
|
||||
/*
|
||||
* Frequency formats handled by the smart parser:
|
||||
*
|
||||
* Group 1: full frequencies, for example 144.210 or 10368.100
|
||||
* Group 2: relative frequencies with a separator, for example .210 or ,210
|
||||
* Group 3: bare three-digit values, for example 210
|
||||
*
|
||||
* Group 3 is deliberately subjected to an additional context check. Without
|
||||
* that check, ordinary chat values such as 599 or a bare band name such as 144
|
||||
* would be converted into plausible but incorrect frequencies.
|
||||
*/
|
||||
private static final Pattern SMART_FREQUENCY_PATTERN = Pattern.compile(
|
||||
"(?<![\\d])(\\d{3,5}[.,]\\d{1,3}(?:[.,]\\d{1,3})?)(?![\\d])"
|
||||
+ "|(?<![\\d])([.,]\\d{3}(?:[.,]\\d{1,3})?)(?![\\d])"
|
||||
+ "|(?<=\\s|^)(\\d{3})(?=\\s|$)"
|
||||
);
|
||||
|
||||
/*
|
||||
* A bare three-digit value is accepted only when the nearby text makes its
|
||||
* meaning sufficiently clear. Examples:
|
||||
*
|
||||
* qrg 210
|
||||
* QRG: 210
|
||||
* freq is 210
|
||||
* on 210
|
||||
* pse 210
|
||||
* at 210
|
||||
* 210 MHz
|
||||
* 210 qrg
|
||||
*/
|
||||
private static final Pattern BARE_FREQUENCY_PREFIX_CONTEXT_PATTERN =
|
||||
Pattern.compile(
|
||||
"(?i)\\b(?:qrg|freq(?:uency)?|on|pse|at)\\b"
|
||||
+ "\\s*(?:is\\s*)?[:=@-]?\\s*$"
|
||||
);
|
||||
|
||||
private static final Pattern BARE_FREQUENCY_SUFFIX_CONTEXT_PATTERN =
|
||||
Pattern.compile(
|
||||
"(?i)^\\s*(?:mhz|qrg|freq(?:uency)?)\\b"
|
||||
);
|
||||
|
||||
private static final int BARE_FREQUENCY_CONTEXT_CHARACTERS = 32;
|
||||
|
||||
|
||||
// ==== Auto-answer flood/ping-pong protection ====
|
||||
private static final String AUTOANSWER_PREFIX = ApplicationConstants.AUTOANSWER_PREFIX;
|
||||
@@ -200,145 +243,220 @@ public class MessageBusManagementThread extends Thread {
|
||||
}
|
||||
|
||||
/**
|
||||
* Smart Frequency Parser (V1.32)
|
||||
* Replaces the old RegEx logic.
|
||||
* Features:
|
||||
* 1. Handles full frequencies (144.210) and short forms (.210, 210).
|
||||
* 2. Handles extended precision/weird formatting (144.210.10, 144,210,10).
|
||||
* 3. Prioritizes USER CONTEXT (History) over GLOBAL CONTEXT (Preferences).
|
||||
* Detects complete and relative frequencies in a chat message and stores the
|
||||
* result on the sender.
|
||||
*
|
||||
* <p>Complete frequencies determine their band directly. Relative frequencies
|
||||
* first use the sender's most recent band context if that context is not older
|
||||
* than 30 minutes. Only when no suitable sender context exists does the parser
|
||||
* use the globally configured fallback band.</p>
|
||||
*
|
||||
* <p>A relative value beginning with a dot or comma is sufficiently explicit
|
||||
* on its own. A bare three-digit value is ambiguous and is therefore accepted
|
||||
* only with nearby frequency-related text.</p>
|
||||
*
|
||||
* @param message message whose text is inspected
|
||||
* @param prefs preferences containing the global fallback band
|
||||
*/
|
||||
private void smartFrequencyExtraction(ChatMessage message, ChatPreferences prefs) {
|
||||
|
||||
// Regex Explanation:
|
||||
// Part 1 (Full): Start (not digit), 3-5 digits, sep, 1-3 digits, OPTIONAL (sep, 1-3 digits)
|
||||
// Matches: 144.210, 144.210.10, 10368.100
|
||||
// Part 2 (Short1): Start (not digit), sep, 3 digits, OPTIONAL (sep, 1-3 digits)
|
||||
// Matches: .210, .210.10, ,210
|
||||
// Part 3 (Short2): Whitespace/Start, 3 digits, Whitespace/End
|
||||
// Matches: " 210 ", " 144 "
|
||||
String smartPattern = "(?<![\\d])(\\d{3,5}[.,]\\d{1,3}(?:[.,]\\d{1,3})?)(?![\\d])|(?<![\\d])([.,]\\d{3}(?:[.,]\\d{1,3})?)(?![\\d])|(?<=\\s|^)(\\d{3})(?=\\s|$)";
|
||||
|
||||
Pattern pattern = Pattern.compile(smartPattern);
|
||||
Matcher matcher = pattern.matcher(message.getMessageText());
|
||||
if (message == null || message.getMessageText() == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
ChatMember sender = message.getSender();
|
||||
// Safety check, in case sender is null (e.g., server message)
|
||||
if (sender == null) return;
|
||||
if (sender == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
String messageText = message.getMessageText();
|
||||
Matcher matcher = SMART_FREQUENCY_PATTERN.matcher(messageText);
|
||||
|
||||
while (matcher.find()) {
|
||||
String foundRaw = matcher.group().trim();
|
||||
boolean dottedShortForm = matcher.group(2) != null;
|
||||
boolean bareShortForm = matcher.group(3) != null;
|
||||
boolean shortForm = dottedShortForm || bareShortForm;
|
||||
|
||||
// --- PRE-PROCESSING: Normalize separators ---
|
||||
// 1. Replace all commas with dots to unify format (144,210,10 -> 144.210.10)
|
||||
foundRaw = foundRaw.replace(",", ".");
|
||||
if (bareShortForm
|
||||
&& !hasExplicitBareFrequencyContext(
|
||||
messageText,
|
||||
matcher.start(),
|
||||
matcher.end()
|
||||
)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
String foundRaw = matcher.group().trim().replace(',', '.');
|
||||
|
||||
double finalDetectedFrequency = 0.0;
|
||||
Band finalDetectedBand = null;
|
||||
boolean isShortForm = false;
|
||||
|
||||
// --- STEP 1: Type Determination (Short or Full?) ---
|
||||
if (shortForm) {
|
||||
if (foundRaw.startsWith(".")) {
|
||||
foundRaw = foundRaw.substring(1);
|
||||
}
|
||||
|
||||
// Check if it starts with a dot (e.g. ".210") OR is just 3 digits ("210")
|
||||
if (foundRaw.startsWith(".") || foundRaw.length() == 3) {
|
||||
/*
|
||||
* Priority 1: use this sender's most recently observed band if its
|
||||
* information is not older than 30 minutes and the reconstructed
|
||||
* frequency lies inside that band.
|
||||
*/
|
||||
long bestTimestamp = 0L;
|
||||
|
||||
// It is a short form.
|
||||
// We strip the leading dot for calculation if present -> "210.10" or "210"
|
||||
if (foundRaw.startsWith(".")) foundRaw = foundRaw.substring(1);
|
||||
isShortForm = true;
|
||||
|
||||
} else {
|
||||
// It is a full frequency (e.g., 144.210.10 or 144.210)
|
||||
try {
|
||||
// Normalize "144.210.10" to "144.21010" for Double.parseDouble
|
||||
String normalizedFull = normalizeFrequencyString(foundRaw);
|
||||
|
||||
finalDetectedFrequency = Double.parseDouble(normalizedFull);
|
||||
finalDetectedBand = Band.fromFrequency(finalDetectedFrequency);
|
||||
} catch (NumberFormatException e) { continue; }
|
||||
}
|
||||
|
||||
// --- STEP 2: Context Resolution (Only needed for Short Forms) ---
|
||||
if (isShortForm) {
|
||||
|
||||
// A) HISTORY CHECK (Priority 1: What did THIS USER do recently?)
|
||||
// We search for the most recent band where this short form makes physical sense.
|
||||
long bestTimestamp = 0;
|
||||
|
||||
// Iterate over all bands where the user is known
|
||||
// (Assumption: ChatMember has a getter getKnownActiveBands())
|
||||
if (sender.getKnownActiveBands() != null) {
|
||||
for (java.util.Map.Entry<Band, ChatMember.ActiveFrequencyInfo> entry : sender.getKnownActiveBands().entrySet()) {
|
||||
for (java.util.Map.Entry<Band, ChatMember.ActiveFrequencyInfo> entry
|
||||
: sender.getKnownActiveBands().entrySet()) {
|
||||
|
||||
Band candidateBand = entry.getKey();
|
||||
ChatMember.ActiveFrequencyInfo info = entry.getValue();
|
||||
|
||||
// Timeout Check: Info must not be older than 30 mins (1,800,000 ms)
|
||||
if (System.currentTimeMillis() - info.timestampEpoch > 1800000) continue;
|
||||
if (candidateBand == null || info == null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Try Reconstruction: Band Prefix + ShortForm
|
||||
// Example: Band 144 (Prefix "144") + "." + "210.10" -> "144.210.10"
|
||||
if (System.currentTimeMillis() - info.timestampEpoch > 1_800_000L) {
|
||||
continue;
|
||||
}
|
||||
|
||||
try {
|
||||
String reconstructedStr = candidateBand.getPrefix() + "." + foundRaw;
|
||||
String normalizedReconstruction = normalizeFrequencyString(reconstructedStr);
|
||||
|
||||
double attemptFreq = Double.parseDouble(normalizedReconstruction);
|
||||
String reconstructed =
|
||||
candidateBand.getPrefix() + "." + foundRaw;
|
||||
double candidateFrequency = Double.parseDouble(
|
||||
normalizeFrequencyString(reconstructed)
|
||||
);
|
||||
|
||||
// Does this frequency fit into the candidate band?
|
||||
if (candidateBand.isPlausible(attemptFreq)) {
|
||||
// If we have multiple matches, pick the most recent one
|
||||
if (info.timestampEpoch > bestTimestamp) {
|
||||
finalDetectedFrequency = attemptFreq;
|
||||
finalDetectedBand = candidateBand;
|
||||
bestTimestamp = info.timestampEpoch;
|
||||
}
|
||||
if (candidateBand.isPlausible(candidateFrequency)
|
||||
&& info.timestampEpoch > bestTimestamp) {
|
||||
finalDetectedFrequency = candidateFrequency;
|
||||
finalDetectedBand = candidateBand;
|
||||
bestTimestamp = info.timestampEpoch;
|
||||
}
|
||||
} catch (Exception e) { /* Ignore parsing errors */ }
|
||||
}
|
||||
}
|
||||
|
||||
// B) GLOBAL PREFERENCES CHECK (Priority 2: Fallback if history is empty/old)
|
||||
if (finalDetectedBand == null) {
|
||||
// Get standard band from prefs (e.g., "144" or "432")
|
||||
String defaultPrefix = prefs.getNotify_optionalFrequencyPrefix().get();
|
||||
try {
|
||||
String reconstructedStr = defaultPrefix + "." + foundRaw;
|
||||
String normalizedReconstruction = normalizeFrequencyString(reconstructedStr);
|
||||
|
||||
double attemptFreq = Double.parseDouble(normalizedReconstruction);
|
||||
|
||||
// Check if this results in a valid amateur radio band
|
||||
Band defaultBandCandidate = Band.fromFrequency(attemptFreq);
|
||||
|
||||
if (defaultBandCandidate != null) {
|
||||
finalDetectedFrequency = attemptFreq;
|
||||
finalDetectedBand = defaultBandCandidate;
|
||||
} catch (NumberFormatException ignored) {
|
||||
// Try the next known band.
|
||||
}
|
||||
} catch (NumberFormatException e) {
|
||||
// Number was likely not a frequency (e.g., "73" or "599") and didn't fit any band
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// --- STEP 3: Process Result ---
|
||||
if (finalDetectedBand != null && finalDetectedFrequency > 0) {
|
||||
|
||||
/*
|
||||
* Store the detected QRG in the thread-safe active-member model.
|
||||
* The UI table is only a JavaFX mirror, so the MessageBus must not scan
|
||||
* or mutate getLst_chatMemberList() here.
|
||||
* Priority 2: use the configured fallback band. Invalid values from
|
||||
* an older hand-edited configuration fall back safely to 144 MHz.
|
||||
* The current UI itself offers only values from Band.values().
|
||||
*/
|
||||
client.applyDetectedFrequencyToActiveMembers(sender, finalDetectedBand, finalDetectedFrequency);
|
||||
if (finalDetectedBand == null) {
|
||||
String configuredPrefix = null;
|
||||
|
||||
System.out.println("[SmartParser] Detected for " + sender.getCallSign() + ": " +
|
||||
finalDetectedFrequency + " MHz (" + finalDetectedBand + ") " +
|
||||
(isShortForm ? "[derived from " + foundRaw + "]" : "[full match]"));
|
||||
if (prefs != null
|
||||
&& prefs.getNotify_optionalFrequencyPrefix() != null) {
|
||||
configuredPrefix =
|
||||
prefs.getNotify_optionalFrequencyPrefix().get();
|
||||
}
|
||||
|
||||
// Optional: Trigger Cluster-Spot here if enabled
|
||||
Band fallbackBand = Band.fromPrefix(configuredPrefix);
|
||||
if (fallbackBand == null) {
|
||||
fallbackBand = Band.B_144;
|
||||
}
|
||||
|
||||
try {
|
||||
String reconstructed =
|
||||
fallbackBand.getPrefix() + "." + foundRaw;
|
||||
double candidateFrequency = Double.parseDouble(
|
||||
normalizeFrequencyString(reconstructed)
|
||||
);
|
||||
|
||||
if (fallbackBand.isPlausible(candidateFrequency)) {
|
||||
finalDetectedFrequency = candidateFrequency;
|
||||
finalDetectedBand = fallbackBand;
|
||||
}
|
||||
} catch (NumberFormatException ignored) {
|
||||
// The matched value cannot be converted into a frequency.
|
||||
}
|
||||
}
|
||||
} else {
|
||||
try {
|
||||
finalDetectedFrequency = Double.parseDouble(
|
||||
normalizeFrequencyString(foundRaw)
|
||||
);
|
||||
finalDetectedBand = Band.fromFrequency(finalDetectedFrequency);
|
||||
} catch (NumberFormatException ignored) {
|
||||
// Continue with the next possible match in the message.
|
||||
}
|
||||
}
|
||||
|
||||
if (finalDetectedBand == null || finalDetectedFrequency <= 0.0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
/*
|
||||
* Store the result in the thread-safe active-member model. The existing
|
||||
* compatibility property used by the TableView and DX-Cluster code is
|
||||
* updated by applyDetectedFrequencyToActiveMembers(...), too.
|
||||
*/
|
||||
client.applyDetectedFrequencyToActiveMembers(
|
||||
sender,
|
||||
finalDetectedBand,
|
||||
finalDetectedFrequency
|
||||
);
|
||||
|
||||
System.out.println(
|
||||
"[SmartParser] Detected for "
|
||||
+ sender.getCallSign()
|
||||
+ ": "
|
||||
+ finalDetectedFrequency
|
||||
+ " MHz ("
|
||||
+ finalDetectedBand
|
||||
+ ") "
|
||||
+ (shortForm
|
||||
? "[derived from " + foundRaw + "]"
|
||||
: "[full match]")
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Checks whether a bare three-digit value is surrounded by text that identifies
|
||||
* it as a frequency.
|
||||
*
|
||||
* <p>The check intentionally uses only a small area around the value. A remote
|
||||
* occurrence of the word "QRG" elsewhere in a long message must not turn every
|
||||
* three-digit number in that message into a frequency.</p>
|
||||
*
|
||||
* @param messageText complete chat message
|
||||
* @param matchStart start index of the three-digit match
|
||||
* @param matchEnd end index of the three-digit match
|
||||
* @return {@code true} if the value has explicit frequency context
|
||||
*/
|
||||
static boolean hasExplicitBareFrequencyContext(
|
||||
String messageText,
|
||||
int matchStart,
|
||||
int matchEnd
|
||||
) {
|
||||
if (messageText == null
|
||||
|| matchStart < 0
|
||||
|| matchEnd < matchStart
|
||||
|| matchEnd > messageText.length()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
int prefixStart = Math.max(
|
||||
0,
|
||||
matchStart - BARE_FREQUENCY_CONTEXT_CHARACTERS
|
||||
);
|
||||
int suffixEnd = Math.min(
|
||||
messageText.length(),
|
||||
matchEnd + BARE_FREQUENCY_CONTEXT_CHARACTERS
|
||||
);
|
||||
|
||||
String prefixContext = messageText.substring(prefixStart, matchStart);
|
||||
String suffixContext = messageText.substring(matchEnd, suffixEnd);
|
||||
|
||||
return BARE_FREQUENCY_PREFIX_CONTEXT_PATTERN
|
||||
.matcher(prefixContext)
|
||||
.find()
|
||||
|| BARE_FREQUENCY_SUFFIX_CONTEXT_PATTERN
|
||||
.matcher(suffixContext)
|
||||
.find();
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper: Normalizes weird frequency formats to valid Double strings.
|
||||
* Example: "144.210.10" -> "144.21010"
|
||||
|
||||
+81
-4
@@ -1,4 +1,81 @@
|
||||
package kst4contest.controller;
|
||||
|
||||
public class MessageBusManagementThreadFrequencyContextTest {
|
||||
}
|
||||
//package kst4contest.controller;
|
||||
//
|
||||
//import kst4contest.model.Band;
|
||||
//import org.junit.jupiter.api.Test;
|
||||
//import org.junit.jupiter.params.ParameterizedTest;
|
||||
//import org.junit.jupiter.params.provider.ValueSource;
|
||||
//
|
||||
//import java.util.regex.Matcher;
|
||||
//import java.util.regex.Pattern;
|
||||
//
|
||||
//import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
//import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||
//import static org.junit.jupiter.api.Assertions.assertNotNull;
|
||||
//import static org.junit.jupiter.api.Assertions.assertNull;
|
||||
//import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
//
|
||||
//class MessageBusManagementThreadFrequencyContextTest {
|
||||
//
|
||||
// private static final Pattern THREE_DIGIT_VALUE =
|
||||
// Pattern.compile("\\b\\d{3}\\b");
|
||||
//
|
||||
// @ParameterizedTest
|
||||
// @ValueSource(strings = {
|
||||
// "qrg 210",
|
||||
// "QRG: 210",
|
||||
// "freq is 210",
|
||||
// "frequency = 210",
|
||||
// "on 210",
|
||||
// "210 MHz",
|
||||
// "210 qrg"
|
||||
// })
|
||||
// void acceptsBareThreeDigitValueWithFrequencyContext(String messageText) {
|
||||
// Matcher matcher = findThreeDigitValue(messageText);
|
||||
//
|
||||
// assertTrue(
|
||||
// MessageBusManagementThread
|
||||
// .hasExplicitBareFrequencyContext(
|
||||
// messageText,
|
||||
// matcher.start(),
|
||||
// matcher.end()
|
||||
// )
|
||||
// );
|
||||
// }
|
||||
//
|
||||
// @ParameterizedTest
|
||||
// @ValueSource(strings = {
|
||||
// "599",
|
||||
// "144",
|
||||
// "serial 210",
|
||||
// "score 210",
|
||||
// "worked 210 stations"
|
||||
// })
|
||||
// void rejectsBareThreeDigitValueWithoutFrequencyContext(String messageText) {
|
||||
// Matcher matcher = findThreeDigitValue(messageText);
|
||||
//
|
||||
// assertFalse(
|
||||
// MessageBusManagementThread
|
||||
// .hasExplicitBareFrequencyContext(
|
||||
// messageText,
|
||||
// matcher.start(),
|
||||
// matcher.end()
|
||||
// )
|
||||
// );
|
||||
// }
|
||||
//
|
||||
// @Test
|
||||
// void resolvesOnlySupportedFallbackPrefixes() {
|
||||
// assertEquals(Band.B_144, Band.fromPrefix("144"));
|
||||
// assertEquals(Band.B_432, Band.fromPrefix(" 432 "));
|
||||
// assertEquals(Band.B_10G, Band.fromPrefix("10368"));
|
||||
// assertNull(Band.fromPrefix("999"));
|
||||
// assertNull(Band.fromPrefix(null));
|
||||
// }
|
||||
//
|
||||
// private Matcher findThreeDigitValue(String messageText) {
|
||||
// Matcher matcher = THREE_DIGIT_VALUE.matcher(messageText);
|
||||
// assertTrue(matcher.find(), "Test message must contain a three-digit value");
|
||||
// assertNotNull(matcher.group());
|
||||
// return matcher;
|
||||
// }
|
||||
//}
|
||||
@@ -291,14 +291,6 @@ public class ReadUDPbyUCXMessageThread extends Thread {
|
||||
|
||||
ChatMember modifyThat = null;
|
||||
|
||||
// System.out.println("ReadUDPByUCX, message catched: " + udpMsg);
|
||||
|
||||
// String[] threadStatusMessage = new String[2];
|
||||
// threadStatusMessage = new String[3];
|
||||
// threadStatusMessage[0] = "on";
|
||||
// threadStatusMessage[1] = "received message:";
|
||||
// threadStatusMessage[2] = udpMsg;
|
||||
|
||||
ThreadStateMessage threadStateMessage = new ThreadStateMessage(this.ThreadNickName, true, "received Message\n" + udpMsg, false);
|
||||
|
||||
callBackToController.onThreadStatus(ThreadNickName,threadStateMessage);
|
||||
@@ -307,7 +299,7 @@ public class ReadUDPbyUCXMessageThread extends Thread {
|
||||
try {
|
||||
dbf.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true);
|
||||
} catch (ParserConfigurationException e1) {
|
||||
// TODO Auto-generated catch block
|
||||
|
||||
e1.printStackTrace();
|
||||
}
|
||||
|
||||
|
||||
@@ -29,6 +29,35 @@ public enum Band {
|
||||
return prefix;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Resolves a configured MHz prefix to one of the bands supported by the
|
||||
* frequency parser.
|
||||
*
|
||||
* <p>The former free-text preference accepted arbitrary numeric values even
|
||||
* though only prefixes represented by this enum can be used for a plausible
|
||||
* frequency. Keeping the lookup here gives the UI and the parser one common
|
||||
* definition of a valid fallback band.</p>
|
||||
*
|
||||
* @param prefix configured MHz prefix, for example {@code 144} or {@code 10368}
|
||||
* @return matching band, or {@code null} if the prefix is not supported
|
||||
*/
|
||||
public static Band fromPrefix(String prefix) {
|
||||
if (prefix == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
String normalizedPrefix = prefix.trim();
|
||||
|
||||
for (Band band : values()) {
|
||||
if (band.prefix.equals(normalizedPrefix)) {
|
||||
return band;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the lower edge used as practical analysis frequency when only the band
|
||||
* is known. This keeps the batch reachability calculation deterministic.
|
||||
|
||||
@@ -31,6 +31,7 @@ import javafx.scene.layout.*;
|
||||
import javafx.scene.media.Media;
|
||||
import javafx.scene.media.MediaPlayer;
|
||||
import javafx.util.Duration;
|
||||
import javafx.util.StringConverter;
|
||||
import kst4contest.ApplicationConstants;
|
||||
import kst4contest.controller.ChatController;
|
||||
import kst4contest.controller.MessageVariableResolver;
|
||||
@@ -1504,19 +1505,13 @@ public class Kst4ContestApplication extends Application implements StatusUpdateL
|
||||
|
||||
@Override
|
||||
public ObservableValue<String> call(CellDataFeatures<ChatMember, String> cellDataFeatures) {
|
||||
// StringProperty qrg = new SimpleStringProperty();
|
||||
|
||||
// qrg.setValue(cellDataFeatures.getValue().getFrequency());
|
||||
// qrg = (cellDataFeatures.getValue().getFrequency());
|
||||
|
||||
// if (!qrg.getValue().equals("")) {
|
||||
//
|
||||
// }
|
||||
|
||||
return cellDataFeatures.getValue().getFrequency();
|
||||
}
|
||||
|
||||
});
|
||||
applyQrgUiFormatting(qrgCol); //insert zero until qrg string looks pretty
|
||||
|
||||
|
||||
TableColumn<ChatMember, String> airScoutCol = new TableColumn<ChatMember, String>("AP [minutes / pot%]");
|
||||
airScoutCol.setCellValueFactory(new Callback<CellDataFeatures<ChatMember, String>, ObservableValue<String>>() {
|
||||
@@ -9854,53 +9849,84 @@ public class Kst4ContestApplication extends Application implements StatusUpdateL
|
||||
}
|
||||
);
|
||||
|
||||
TextField txtFld_notify_DXclusterServerFrequencyPrefix =
|
||||
new TextField(
|
||||
this.chatcontroller
|
||||
.getChatPreferences()
|
||||
.getNotify_optionalFrequencyPrefix()
|
||||
.getValue()
|
||||
ComboBox<Band> cmbBx_notifyFrequencyFallbackBand =
|
||||
new ComboBox<>(
|
||||
FXCollections.observableArrayList(Band.values())
|
||||
);
|
||||
|
||||
txtFld_notify_DXclusterServerFrequencyPrefix
|
||||
.focusedProperty()
|
||||
cmbBx_notifyFrequencyFallbackBand.setEditable(false);
|
||||
cmbBx_notifyFrequencyFallbackBand.setMaxWidth(Double.MAX_VALUE);
|
||||
cmbBx_notifyFrequencyFallbackBand.setTooltip(
|
||||
new Tooltip(
|
||||
"Used for relative QRG values such as .210 when no band "
|
||||
+ "has been recognized for the sender during the "
|
||||
+ "previous 30 minutes. This setting affects the "
|
||||
+ "general QRG detection, not only DX-Cluster spots."
|
||||
)
|
||||
);
|
||||
|
||||
cmbBx_notifyFrequencyFallbackBand.setConverter(
|
||||
new StringConverter<Band>() {
|
||||
@Override
|
||||
public String toString(Band band) {
|
||||
if (band == null) {
|
||||
return "";
|
||||
}
|
||||
|
||||
String displayLabel = band.getDisplayLabel();
|
||||
if (band.getPrefix().equals(displayLabel)) {
|
||||
return band.getPrefix() + " MHz";
|
||||
}
|
||||
|
||||
return band.getPrefix()
|
||||
+ " MHz ("
|
||||
+ displayLabel
|
||||
+ ")";
|
||||
}
|
||||
|
||||
@Override
|
||||
public Band fromString(String displayedValue) {
|
||||
if (displayedValue == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
int firstSpace = displayedValue.indexOf(' ');
|
||||
String prefix = firstSpace >= 0
|
||||
? displayedValue.substring(0, firstSpace)
|
||||
: displayedValue;
|
||||
|
||||
return Band.fromPrefix(prefix);
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
Band configuredFallbackBand = Band.fromPrefix(
|
||||
chatcontroller
|
||||
.getChatPreferences()
|
||||
.getNotify_optionalFrequencyPrefix()
|
||||
.get()
|
||||
);
|
||||
|
||||
if (configuredFallbackBand == null) {
|
||||
configuredFallbackBand = Band.B_144;
|
||||
chatcontroller
|
||||
.getChatPreferences()
|
||||
.setNotify_optionalFrequencyPrefix(
|
||||
configuredFallbackBand.getPrefix()
|
||||
);
|
||||
}
|
||||
|
||||
cmbBx_notifyFrequencyFallbackBand.setValue(configuredFallbackBand);
|
||||
|
||||
cmbBx_notifyFrequencyFallbackBand
|
||||
.valueProperty()
|
||||
.addListener(
|
||||
(observable, oldValue, focused) -> {
|
||||
if (focused) {
|
||||
return;
|
||||
}
|
||||
|
||||
String bandInMHz =
|
||||
txtFld_notify_DXclusterServerFrequencyPrefix
|
||||
.getText()
|
||||
.trim();
|
||||
|
||||
if (bandInMHz.matches(
|
||||
"[1-9]\\d{1,4}"
|
||||
)) {
|
||||
(observable, oldBand, newBand) -> {
|
||||
if (newBand != null) {
|
||||
chatcontroller
|
||||
.getChatPreferences()
|
||||
.setNotify_optionalFrequencyPrefix(
|
||||
bandInMHz
|
||||
);
|
||||
|
||||
txtFld_notify_DXclusterServerFrequencyPrefix
|
||||
.setText(bandInMHz);
|
||||
} else {
|
||||
showUserInputErrorWindow(
|
||||
"\""
|
||||
+ bandInMHz
|
||||
+ "\" is not a valid fallback band. "
|
||||
+ "Enter the MHz part as an integer, "
|
||||
+ "for example 144, 432 or 1296."
|
||||
);
|
||||
|
||||
txtFld_notify_DXclusterServerFrequencyPrefix
|
||||
.setText(
|
||||
chatcontroller
|
||||
.getChatPreferences()
|
||||
.getNotify_optionalFrequencyPrefix()
|
||||
.getValue()
|
||||
newBand.getPrefix()
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -10075,14 +10101,13 @@ public class Kst4ContestApplication extends Application implements StatusUpdateL
|
||||
|
||||
grdPnlNotify.add(
|
||||
new Label(
|
||||
"Fallback band in MHz for relative "
|
||||
+ "frequencies [default: 144]:"
|
||||
"Fallback band for relative QRG detection:"
|
||||
),
|
||||
0,
|
||||
8
|
||||
);
|
||||
grdPnlNotify.add(
|
||||
txtFld_notify_DXclusterServerFrequencyPrefix,
|
||||
cmbBx_notifyFrequencyFallbackBand,
|
||||
1,
|
||||
8
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user