This commit is contained in:
2026-06-05 17:22:38 +02:00
parent cf9dbf26f3
commit 88623f55df
21 changed files with 2123 additions and 50 deletions
+119
View File
@@ -0,0 +1,119 @@
package award
import "strings"
// wpxPrefix derives the CQ WPX prefix from a callsign. This is an approximation
// of the official WPX rules — good enough to count distinct prefixes worked:
// - standard call: letters+digits up to and including the LAST digit of the
// first group (F4BPO→F4, EA8ABC→EA8, 9A1AA→9A1, OH2BH→OH2)
// - no digit: first two letters + "0" (RAEM→RA0)
// - portable "A/B": a short alpha(+digit) segment is treated as the prefix
// designator; a lone-digit segment replaces the call's digit (F4BPO/9→F9)
func wpxPrefix(call string) string {
c := strings.ToUpper(strings.TrimSpace(call))
if c == "" {
return ""
}
if strings.Contains(c, "/") {
return portablePrefix(c)
}
return standardPrefix(c)
}
func portablePrefix(c string) string {
parts := strings.Split(c, "/")
// Drop pure operating-modifier suffixes.
kept := make([]string, 0, len(parts))
for _, p := range parts {
switch p {
case "P", "M", "MM", "AM", "QRP", "A", "R", "B", "LH":
continue
}
if p != "" {
kept = append(kept, p)
}
}
if len(kept) == 0 {
kept = parts
}
// Pick a base = the longest segment (the actual call).
base := kept[0]
for _, p := range kept[1:] {
if len(p) > len(base) {
base = p
}
}
for _, p := range kept {
if p == base {
continue
}
if isAllDigits(p) {
// Lone digit replaces the call's region digit: F4BPO/9 → F9.
return replaceLastDigit(standardPrefix(base), p)
}
if len(p) <= 4 && hasLetter(p) {
// Prefix designator wins: VP8/F4BPO → VP8 (+digit if missing).
return ensureTrailingDigit(p)
}
}
return standardPrefix(base)
}
// standardPrefix applies the basic WPX rule to a plain callsign: the prefix is
// the call up to and including its last digit (9A1AA→9A1, EA8ABC→EA8). Standard
// callsigns carry no digit in the suffix, so "last digit" is the prefix digit.
func standardPrefix(c string) string {
lastDigit := -1
for i := 0; i < len(c); i++ {
if c[i] >= '0' && c[i] <= '9' {
lastDigit = i
}
}
if lastDigit < 0 {
// No digit at all: first two letters + 0.
if len(c) >= 2 {
return c[:2] + "0"
}
return c + "0"
}
return c[:lastDigit+1]
}
func ensureTrailingDigit(p string) string {
for i := 0; i < len(p); i++ {
if p[i] >= '0' && p[i] <= '9' {
return p
}
}
return p + "0"
}
func replaceLastDigit(prefix, digit string) string {
for i := len(prefix) - 1; i >= 0; i-- {
if prefix[i] >= '0' && prefix[i] <= '9' {
return prefix[:i] + digit
}
}
return prefix + digit
}
func isAllDigits(s string) bool {
if s == "" {
return false
}
for i := 0; i < len(s); i++ {
if s[i] < '0' || s[i] > '9' {
return false
}
}
return true
}
func hasLetter(s string) bool {
for i := 0; i < len(s); i++ {
if (s[i] >= 'A' && s[i] <= 'Z') || (s[i] >= 'a' && s[i] <= 'z') {
return true
}
}
return false
}