mirror of
https://github.com/praktimarc/kst4contest.git
synced 2026-08-23 18:47:34 +02:00
Added 6+4m and 10G and up frequency detection patterns
This commit is contained in:
@@ -101,102 +101,143 @@ public class Utils4KST {
|
||||
}
|
||||
|
||||
/**
|
||||
* Normalizes a chatmembers frequency-string for cluster usage<br/>
|
||||
* <b>returns a frequency String in KHz like = "144300" or "144300.0" to match DXC protocol needs</b>
|
||||
* Converts a frequency detected in a chat message into the kHz representation
|
||||
* used by the DX Cluster protocol.
|
||||
*
|
||||
* @param optionalPrefix: if there is a value like ".300", it have to be decided, wich ".300": 144.300, 432.300, 1296.300 .... prefix means for example "144."
|
||||
* <p>Complete frequencies with two to five MHz digits are supported, for
|
||||
* example 50.200, 144.205, 1296.338, 10368.100 and 24048.100. An optional
|
||||
* second fractional group is retained as sub-kHz precision, for example
|
||||
* 144.205.2 becomes 144205.2 kHz.</p>
|
||||
*
|
||||
* <p>Relative values such as .205 or 205 use the configured fallback-band
|
||||
* prefix. The method only performs this fallback conversion after the general
|
||||
* message parser has already accepted the value as a frequency.</p>
|
||||
*
|
||||
* @param qrgString frequency as detected by the chat parser
|
||||
* @param optionalPrefix fallback MHz prefix for relative frequencies
|
||||
* @return frequency in kHz for a DX Cluster spot, or an empty string if the
|
||||
* value cannot be converted safely
|
||||
*/
|
||||
public static String normalizeFrequencyString(String qrgString, SimpleStringProperty optionalPrefix) {
|
||||
|
||||
// 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)?)";
|
||||
|
||||
try {
|
||||
qrgString = qrgString.replace(" ","");
|
||||
} catch (Exception e) {
|
||||
System.out.println("UTILS: QRG NULL, nothing to convert");
|
||||
// e.printStackTrace();
|
||||
public static String normalizeFrequencyString(
|
||||
String qrgString,
|
||||
SimpleStringProperty optionalPrefix
|
||||
) {
|
||||
if (qrgString == null || qrgString.isBlank()) {
|
||||
return "";
|
||||
}
|
||||
|
||||
/*
|
||||
* A comma is accepted as a decimal separator. Spaces are removed so older
|
||||
* stored formats such as "432 088" remain usable.
|
||||
*/
|
||||
String normalizedValue = qrgString
|
||||
.trim()
|
||||
.replace(" ", "")
|
||||
.replace(',', '.');
|
||||
|
||||
final String PTRN_QRG_CAT2_wholeQRGMHz4Digits = "(([0-9]{4}[\\.|,| ]?[0-9]{3})([\\.|,][\\d]{1,2})?)"; //1296.300.3 etc
|
||||
final String PTRN_QRG_CAT2_wholeQRGMHz3Digits = "(([0-9]{3}[\\.|,| ]?[0-9]{3})([\\.][\\d]{1,2})?)"; //144.300.3 etc
|
||||
final String PTRN_QRG_CAT2_QRGwithoutPrefix = "((\\b[0-4]{1}[\\d]{2}\\b)([\\.|,][\\d]{1,2}\\b)?)"; //144.300.3 etc
|
||||
/*
|
||||
* Complete frequency with an explicit separator:
|
||||
*
|
||||
* 50.200
|
||||
* 144.205
|
||||
* 1296.338
|
||||
* 10368.100
|
||||
* 144.205.2
|
||||
*/
|
||||
Matcher completeFrequencyMatcher = Pattern.compile(
|
||||
"^(\\d{2,5})\\.(\\d{1,3})(?:\\.(\\d{1,2}))?$"
|
||||
).matcher(normalizedValue);
|
||||
|
||||
String stringAggregation = "";
|
||||
|
||||
if (testPattern(qrgString, PTRN_QRG_CAT2_wholeQRGMHz4Digits)) {//case 1296.200 or 1296.200.2 etc.
|
||||
stringAggregation = qrgString;
|
||||
|
||||
stringAggregation = stringAggregation.replace(".","");
|
||||
stringAggregation = stringAggregation.replace(",","");
|
||||
stringAggregation = stringAggregation.replace(" ", "");
|
||||
|
||||
if (stringAggregation.length() == 8) {
|
||||
String stringAggregationNew = stringAggregation.substring(0, stringAggregation.length()-1) + "." + stringAggregation.substring(stringAggregation.length()-1, stringAggregation.length());
|
||||
stringAggregation = stringAggregationNew + ".0";
|
||||
return stringAggregation;
|
||||
|
||||
} else if (stringAggregation.length() == 9) {
|
||||
String stringAggregationNew = stringAggregation.substring(0, stringAggregation.length()-2) + "." + stringAggregation.substring(stringAggregation.length()-2, stringAggregation.length());
|
||||
stringAggregation = stringAggregationNew;
|
||||
return stringAggregation;
|
||||
}
|
||||
|
||||
} else
|
||||
|
||||
if (testPattern(qrgString, PTRN_QRG_CAT2_wholeQRGMHz3Digits)) { //case 144.300 or 144.300.2
|
||||
stringAggregation = qrgString;
|
||||
|
||||
stringAggregation = stringAggregation.replace(".","");
|
||||
stringAggregation = stringAggregation.replace(",","");
|
||||
stringAggregation = stringAggregation.replace(" ", "");
|
||||
|
||||
if (stringAggregation.length() == 6) {
|
||||
stringAggregation = stringAggregation + ".0";
|
||||
return stringAggregation;
|
||||
}
|
||||
if (stringAggregation.length() == 7) {
|
||||
String stringAggregationNew = stringAggregation.substring(0, stringAggregation.length()-1) + "." + stringAggregation.substring(stringAggregation.length()-1, stringAggregation.length());
|
||||
stringAggregation = stringAggregationNew + ".0";
|
||||
return stringAggregation;
|
||||
|
||||
} else if (stringAggregation.length() == 8) {
|
||||
String stringAggregationNew = stringAggregation.substring(0, stringAggregation.length()-2) + "." + stringAggregation.substring(stringAggregation.length()-2, stringAggregation.length());
|
||||
stringAggregation = stringAggregationNew;
|
||||
return stringAggregation;
|
||||
}
|
||||
}
|
||||
else
|
||||
|
||||
if (testPattern(qrgString, PTRN_QRG_CAT2_QRGwithoutPrefix)) { //case ".050 or .300 or something like that"
|
||||
stringAggregation = qrgString;
|
||||
|
||||
stringAggregation = stringAggregation.replace(".", "");
|
||||
stringAggregation = stringAggregation.replace(",", "");
|
||||
stringAggregation = stringAggregation.replace(" ", "");
|
||||
|
||||
if (stringAggregation.length() == 3) { // like 050 or 300
|
||||
String stringAggregationNew = optionalPrefix.getValue() + stringAggregation;
|
||||
stringAggregation = stringAggregationNew + ".0";
|
||||
return stringAggregation;
|
||||
|
||||
} else if (stringAggregation.length() == 4) { //like 050.2 --> 0502
|
||||
|
||||
stringAggregation = optionalPrefix.getValue() + stringAggregation;
|
||||
String stringAggregationNew = stringAggregation.substring(0, stringAggregation.length() - 1) + "." + stringAggregation.substring(stringAggregation.length() - 1, stringAggregation.length());
|
||||
stringAggregation = stringAggregationNew;
|
||||
return stringAggregation;
|
||||
|
||||
} else if (stringAggregation.length() == 5) { //like 050.20 --> 05020
|
||||
|
||||
stringAggregation = optionalPrefix.getValue() + stringAggregation;
|
||||
String stringAggregationNew = stringAggregation.substring(0, stringAggregation.length() - 2) + "." + stringAggregation.substring(stringAggregation.length() - 2, stringAggregation.length());
|
||||
stringAggregation = stringAggregationNew;
|
||||
return stringAggregation;
|
||||
}
|
||||
if (completeFrequencyMatcher.matches()) {
|
||||
return formatDxClusterFrequency(
|
||||
completeFrequencyMatcher.group(1),
|
||||
completeFrequencyMatcher.group(2),
|
||||
completeFrequencyMatcher.group(3)
|
||||
);
|
||||
}
|
||||
|
||||
return stringAggregation; //if nothing else helps
|
||||
/*
|
||||
* Compact complete frequency retained for compatibility:
|
||||
*
|
||||
* 144205
|
||||
* 1296338
|
||||
* 10368100
|
||||
* 432088.2
|
||||
*/
|
||||
Matcher compactFrequencyMatcher = Pattern.compile(
|
||||
"^(\\d{2,5})(\\d{3})(?:\\.(\\d{1,2}))?$"
|
||||
).matcher(normalizedValue);
|
||||
|
||||
if (compactFrequencyMatcher.matches()) {
|
||||
return formatDxClusterFrequency(
|
||||
compactFrequencyMatcher.group(1),
|
||||
compactFrequencyMatcher.group(2),
|
||||
compactFrequencyMatcher.group(3)
|
||||
);
|
||||
}
|
||||
|
||||
/*
|
||||
* Relative frequency. Values above 499 are deliberately rejected, matching
|
||||
* the previous behaviour and preventing a report such as 599 from becoming
|
||||
* a plausible-looking cluster frequency.
|
||||
*/
|
||||
Matcher relativeFrequencyMatcher = Pattern.compile(
|
||||
"^\\.?([0-4]\\d{2})(?:\\.(\\d{1,2}))?$"
|
||||
).matcher(normalizedValue);
|
||||
|
||||
if (!relativeFrequencyMatcher.matches()) {
|
||||
return "";
|
||||
}
|
||||
|
||||
String fallbackPrefix = optionalPrefix == null
|
||||
? null
|
||||
: optionalPrefix.getValue();
|
||||
|
||||
if (fallbackPrefix == null) {
|
||||
return "";
|
||||
}
|
||||
|
||||
fallbackPrefix = fallbackPrefix.trim();
|
||||
|
||||
if (!fallbackPrefix.matches("\\d{2,5}")) {
|
||||
return "";
|
||||
}
|
||||
|
||||
return formatDxClusterFrequency(
|
||||
fallbackPrefix,
|
||||
relativeFrequencyMatcher.group(1),
|
||||
relativeFrequencyMatcher.group(2)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Formats an MHz part, a fractional MHz part and optional sub-kHz digits as a
|
||||
* DX Cluster frequency in kHz.
|
||||
*
|
||||
* <p>The fractional MHz part is padded on the right because 144.2 means
|
||||
* 144.200 MHz, while 144.21 means 144.210 MHz.</p>
|
||||
*
|
||||
* @param mhzPart complete MHz part
|
||||
* @param fractionalMhzPart one to three digits following the first separator
|
||||
* @param subKhzPart optional digits following a second separator
|
||||
* @return DX Cluster frequency in kHz
|
||||
*/
|
||||
private static String formatDxClusterFrequency(
|
||||
String mhzPart,
|
||||
String fractionalMhzPart,
|
||||
String subKhzPart
|
||||
) {
|
||||
String paddedFraction =
|
||||
(fractionalMhzPart + "000").substring(0, 3);
|
||||
|
||||
String subKhz = subKhzPart == null
|
||||
? "0"
|
||||
: subKhzPart;
|
||||
|
||||
return mhzPart
|
||||
+ paddedFraction
|
||||
+ "."
|
||||
+ subKhz;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,63 @@
|
||||
package kst4contest.test;
|
||||
|
||||
import javafx.beans.property.SimpleStringProperty;
|
||||
import kst4contest.controller.Utils4KST;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
|
||||
class Utils4KSTFrequencyTest {
|
||||
|
||||
@Test
|
||||
void convertsCompleteFrequenciesAcrossAllSupportedPrefixLengths() {
|
||||
assertEquals("50200.0", convert("50.200", "144"));
|
||||
assertEquals("70250.0", convert("70,250", "144"));
|
||||
assertEquals("144205.0", convert("144.205", "432"));
|
||||
assertEquals("432088.0", convert("432.088", "144"));
|
||||
assertEquals("1296338.0", convert("1296.338", "144"));
|
||||
assertEquals("10368100.0", convert("10368.100", "144"));
|
||||
assertEquals("24048100.0", convert("24048.100", "144"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void retainsSubKhzPrecision() {
|
||||
assertEquals("144205.2", convert("144.205.2", "432"));
|
||||
assertEquals("1296338.25", convert("1296.338.25", "144"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void convertsCompactLegacyFormats() {
|
||||
assertEquals("432088.0", convert("432088", "144"));
|
||||
assertEquals("10368100.0", convert("10368100", "144"));
|
||||
assertEquals("432088.2", convert("432088.2", "144"));
|
||||
assertEquals("432088.0", convert("432 088", "144"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void appliesFallbackOnlyToRelativeFrequencies() {
|
||||
assertEquals("432205.0", convert(".205", "432"));
|
||||
assertEquals("144205.0", convert("205", "144"));
|
||||
assertEquals("10368300.0", convert(".300", "10368"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void rejectsMissingOrUnsupportedValues() {
|
||||
assertEquals("", convert(null, "144"));
|
||||
assertEquals("", convert("", "144"));
|
||||
assertEquals("", convert("not-a-frequency", "144"));
|
||||
assertEquals("", convert("599", "144"));
|
||||
assertEquals("", convert(".205", null));
|
||||
assertEquals("", convert(".205", "invalid"));
|
||||
}
|
||||
|
||||
private String convert(String frequency, String fallbackPrefix) {
|
||||
SimpleStringProperty fallback = fallbackPrefix == null
|
||||
? null
|
||||
: new SimpleStringProperty(fallbackPrefix);
|
||||
|
||||
return Utils4KST.normalizeFrequencyString(
|
||||
frequency,
|
||||
fallback
|
||||
);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user