package modes import ( "regexp" "strings" "github.com/user/flexdxcluster2/internal/bands" ) type ModeRange struct { MinFreqMHz float64 MaxFreqMHz float64 Mode string } var BandModeRanges = map[string][]ModeRange{ "160M": { {1.800, 1.838, "CW"}, {1.838, 1.843, "FT8"}, {1.843, 2.000, "LSB"}, }, "80M": { {3.500, 3.560, "CW"}, {3.560, 3.575, "FT8"}, {3.575, 3.578, "FT4"}, {3.578, 3.590, "RTTY"}, {3.590, 3.800, "LSB"}, }, "60M": { {5.330, 5.357, "CW"}, {5.357, 5.359, "FT8"}, {5.359, 5.405, "USB"}, }, "40M": { {7.000, 7.040, "CW"}, {7.040, 7.047, "RTTY"}, {7.047, 7.050, "FT4"}, {7.050, 7.100, "FT8"}, {7.100, 7.300, "LSB"}, }, "30M": { {10.100, 10.130, "CW"}, {10.130, 10.142, "FT8"}, {10.142, 10.150, "FT4"}, }, "20M": { {14.000, 14.070, "CW"}, {14.070, 14.078, "FT8"}, {14.078, 14.083, "FT4"}, {14.083, 14.100, "FT8"}, {14.100, 14.112, "RTTY"}, {14.112, 14.350, "USB"}, }, "17M": { {18.068, 18.090, "CW"}, {18.090, 18.104, "FT8"}, {18.104, 18.106, "FT4"}, {18.106, 18.110, "FT8"}, {18.110, 18.168, "USB"}, }, "15M": { {21.000, 21.070, "CW"}, {21.070, 21.100, "FT8"}, {21.100, 21.130, "RTTY"}, {21.130, 21.143, "FT4"}, {21.143, 21.450, "USB"}, }, "12M": { {24.890, 24.910, "CW"}, {24.910, 24.918, "FT8"}, {24.918, 24.930, "FT4"}, {24.930, 24.990, "USB"}, }, "10M": { {28.000, 28.070, "CW"}, {28.070, 28.110, "FT8"}, {28.110, 28.179, "RTTY"}, {28.179, 28.190, "FT4"}, {28.190, 29.000, "USB"}, {29.000, 29.700, "FM"}, }, "6M": { {50.000, 50.100, "CW"}, {50.100, 50.313, "USB"}, {50.313, 50.318, "FT8"}, {50.318, 50.323, "FT4"}, {50.323, 51.000, "USB"}, {51.000, 54.000, "FM"}, }, "QO-100": { {10489.500, 10489.540, "CW"}, {10489.540, 10489.650, "FT8"}, {10489.650, 10489.990, "USB"}, }, } func GuessMode(freqMHz float64) string { band := bands.FrequencyToBand(freqMHz) if band == "N/A" { if freqMHz < 10.0 { return "LSB" } return "USB" } return GuessModeForBand(freqMHz, band) } func GuessModeForBand(freqMHz float64, band string) string { ranges, exists := BandModeRanges[band] if !exists { if bands.IsUSBBand(band) { return "USB" } return "LSB" } for _, r := range ranges { if freqMHz >= r.MinFreqMHz && freqMHz < r.MaxFreqMHz { return r.Mode } } if bands.IsUSBBand(band) { return "USB" } return "LSB" } func ExtractModeFromComment(comment string) string { if comment == "" { return "" } commentUpper := strings.ToUpper(comment) if strings.Contains(commentUpper, "FT8") || (strings.Contains(commentUpper, "DB") && strings.Contains(commentUpper, "HZ")) { return "FT8" } if strings.Contains(commentUpper, "FT4") { return "FT4" } if strings.Contains(commentUpper, "WPM") || strings.Contains(commentUpper, " CW ") || strings.HasSuffix(commentUpper, "CW") || strings.HasPrefix(commentUpper, "CW ") { return "CW" } digitalModes := []string{"RTTY", "PSK31", "PSK63", "PSK", "MFSK", "OLIVIA", "JT65", "JT9"} for _, mode := range digitalModes { if strings.Contains(commentUpper, mode) { return mode } } voiceModes := []string{"USB", "LSB", "SSB", "FM", "AM"} for _, mode := range voiceModes { if strings.Contains(commentUpper, " "+mode+" ") || strings.HasPrefix(commentUpper, mode+" ") || strings.HasSuffix(commentUpper, " "+mode) || commentUpper == mode { return mode } } return "" } // DetermineMode — priorité : mode explicite > commentaire > fréquence func DetermineMode(explicitMode string, comment string, freqMHz float64) string { if explicitMode != "" { explicitMode = strings.ToUpper(explicitMode) if explicitMode == "SSB" { return bands.NormalizeSSBModeByFrequency(explicitMode, freqMHz) } return explicitMode } modeFromComment := ExtractModeFromComment(comment) if modeFromComment != "" { if modeFromComment == "SSB" { return bands.NormalizeSSBModeByFrequency(modeFromComment, freqMHz) } return modeFromComment } return GuessMode(freqMHz) } func IsCWMode(mode string) bool { return strings.ToUpper(mode) == "CW" } func IsSSBMode(mode string) bool { m := strings.ToUpper(mode) return m == "SSB" || m == "USB" || m == "LSB" } func IsDigitalMode(mode string) bool { m := strings.ToUpper(mode) for _, dm := range []string{"FT8", "FT4", "RTTY", "PSK31", "PSK63", "PSK", "MFSK", "OLIVIA", "JT65", "JT9"} { if m == dm { return true } } return false } func IsPhoneMode(mode string) bool { m := strings.ToUpper(mode) for _, pm := range []string{"SSB", "USB", "LSB", "FM", "AM"} { if m == pm { return true } } return false } func ParseModeFromRawSpot(rawSpot string) string { re := regexp.MustCompile(`\b(CW|SSB|USB|LSB|FM|AM|FT8|FT4|RTTY|PSK\d*)\b`) return re.FindString(strings.ToUpper(rawSpot)) } func GetModeColor(mode string) string { switch strings.ToUpper(mode) { case "CW": return "#10b981" case "FT8", "FT4": return "#8b5cf6" case "RTTY": return "#f59e0b" case "FM": return "#ec4899" } if IsSSBMode(mode) { return "#3b82f6" } return "#6b7280" }