462 lines
		
	
	
		
			11 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			462 lines
		
	
	
		
			11 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
package main
 | 
						|
 | 
						|
import (
 | 
						|
	"regexp"
 | 
						|
	"strconv"
 | 
						|
	"strings"
 | 
						|
	"sync"
 | 
						|
 | 
						|
	_ "github.com/mattn/go-sqlite3"
 | 
						|
)
 | 
						|
 | 
						|
type TelnetSpot struct {
 | 
						|
	DX             string
 | 
						|
	Spotter        string
 | 
						|
	Frequency      string
 | 
						|
	Mode           string
 | 
						|
	Band           string
 | 
						|
	Time           string
 | 
						|
	DXCC           string
 | 
						|
	CountryName    string
 | 
						|
	Comment        string
 | 
						|
	CommandNumber  int
 | 
						|
	FlexSpotNumber int
 | 
						|
	NewDXCC        bool
 | 
						|
	NewBand        bool
 | 
						|
	NewMode        bool
 | 
						|
	NewSlot        bool
 | 
						|
	CallsignWorked bool
 | 
						|
}
 | 
						|
 | 
						|
func ProcessTelnetSpot(re *regexp.Regexp, spotRaw string, SpotChanToFlex chan TelnetSpot, SpotChanToHTTPServer chan TelnetSpot, Countries Countries, contactRepo *Log4OMContactsRepository) {
 | 
						|
 | 
						|
	match := re.FindStringSubmatch(spotRaw)
 | 
						|
 | 
						|
	if len(match) == 0 {
 | 
						|
		IncrementSpotsRejected()
 | 
						|
		Log.Warnf("❌ Regex no match: %s", spotRaw)
 | 
						|
		return
 | 
						|
	}
 | 
						|
 | 
						|
	spot := TelnetSpot{
 | 
						|
		DX:        match[3],
 | 
						|
		Spotter:   match[1],
 | 
						|
		Frequency: match[2],
 | 
						|
		Mode:      match[4],
 | 
						|
		Comment:   strings.Trim(match[5], " "),
 | 
						|
		Time:      match[6],
 | 
						|
	}
 | 
						|
 | 
						|
	DXCC := GetDXCC(spot.DX, Countries)
 | 
						|
	spot.DXCC = DXCC.DXCC
 | 
						|
	spot.CountryName = DXCC.CountryName
 | 
						|
 | 
						|
	if spot.DXCC == "" {
 | 
						|
		IncrementSpotsRejected()
 | 
						|
		Log.Warnf("❌ DXCC not found: %s", spot.DX)
 | 
						|
		return
 | 
						|
	}
 | 
						|
 | 
						|
	spot.GetBand()
 | 
						|
	spot.GuessMode(spotRaw)
 | 
						|
	spot.CallsignWorked = false
 | 
						|
	spot.NewBand = false
 | 
						|
	spot.NewMode = false
 | 
						|
	spot.NewDXCC = false
 | 
						|
	spot.NewSlot = false
 | 
						|
 | 
						|
	contactsChan := make(chan []Contact)
 | 
						|
	contactsModeChan := make(chan []Contact)
 | 
						|
	contactsModeBandChan := make(chan []Contact)
 | 
						|
	contactsBandChan := make(chan []Contact)
 | 
						|
	contactsCallChan := make(chan []Contact)
 | 
						|
 | 
						|
	wg := new(sync.WaitGroup)
 | 
						|
	wg.Add(5)
 | 
						|
 | 
						|
	go contactRepo.ListByCountry(spot.DXCC, contactsChan, wg)
 | 
						|
	contacts := <-contactsChan
 | 
						|
 | 
						|
	go contactRepo.ListByCountryMode(spot.DXCC, spot.Mode, contactsModeChan, wg)
 | 
						|
	contactsMode := <-contactsModeChan
 | 
						|
 | 
						|
	go contactRepo.ListByCountryBand(spot.DXCC, spot.Band, contactsBandChan, wg)
 | 
						|
	contactsBand := <-contactsBandChan
 | 
						|
 | 
						|
	go contactRepo.ListByCallSign(spot.DX, spot.Band, spot.Mode, contactsCallChan, wg)
 | 
						|
	contactsCall := <-contactsCallChan
 | 
						|
 | 
						|
	go contactRepo.ListByCountryModeBand(spot.DXCC, spot.Band, spot.Mode, contactsModeBandChan, wg)
 | 
						|
	contactsModeBand := <-contactsModeBandChan
 | 
						|
 | 
						|
	wg.Wait()
 | 
						|
 | 
						|
	// ✅ Déterminer le statut
 | 
						|
	if len(contacts) == 0 {
 | 
						|
		spot.NewDXCC = true
 | 
						|
	}
 | 
						|
	if len(contactsMode) == 0 {
 | 
						|
		spot.NewMode = true
 | 
						|
	}
 | 
						|
	if len(contactsBand) == 0 {
 | 
						|
		spot.NewBand = true
 | 
						|
	}
 | 
						|
	if len(contactsModeBand) == 0 && !spot.NewDXCC && !spot.NewBand && !spot.NewMode {
 | 
						|
		spot.NewSlot = true
 | 
						|
	}
 | 
						|
	if len(contactsCall) > 0 {
 | 
						|
		spot.CallsignWorked = true
 | 
						|
	}
 | 
						|
 | 
						|
	// ✅ Envoyer le spot
 | 
						|
	select {
 | 
						|
	case SpotChanToHTTPServer <- spot:
 | 
						|
		IncrementSpotsProcessed()
 | 
						|
	default:
 | 
						|
		IncrementSpotsRejected()
 | 
						|
		Log.Errorf("❌ Spot dropped (channel full): %s @ %s", spot.DX, spot.Frequency)
 | 
						|
		return
 | 
						|
	}
 | 
						|
 | 
						|
	// ✅ LOGS CONCIS ET ADAPTES
 | 
						|
	statusIcon := ""
 | 
						|
	statusText := ""
 | 
						|
 | 
						|
	if spot.NewDXCC {
 | 
						|
		statusIcon = "🆕"
 | 
						|
		statusText = "NEW DXCC"
 | 
						|
	} else if spot.NewBand && spot.NewMode {
 | 
						|
		statusIcon = "📻"
 | 
						|
		statusText = "NEW BAND+MODE"
 | 
						|
	} else if spot.NewBand {
 | 
						|
		statusIcon = "📡"
 | 
						|
		statusText = "NEW BAND"
 | 
						|
	} else if spot.NewMode {
 | 
						|
		statusIcon = "🔧"
 | 
						|
		statusText = "NEW MODE"
 | 
						|
	} else if spot.NewSlot {
 | 
						|
		statusIcon = "✨"
 | 
						|
		statusText = "NEW SLOT"
 | 
						|
	} else if spot.CallsignWorked {
 | 
						|
		statusIcon = "✓"
 | 
						|
		statusText = "WORKED"
 | 
						|
	} else {
 | 
						|
		statusIcon = "·"
 | 
						|
		statusText = "SPOT"
 | 
						|
	}
 | 
						|
 | 
						|
	// ✅ Log unique et concis
 | 
						|
	Log.Debugf("%s [%s] %s on %.1f kHz (%s %s) - %s @ %s",
 | 
						|
		statusIcon,
 | 
						|
		statusText,
 | 
						|
		spot.DX,
 | 
						|
		mustParseFloat(spot.Frequency),
 | 
						|
		spot.Band,
 | 
						|
		spot.Mode,
 | 
						|
		spot.CountryName,
 | 
						|
		spot.Time,
 | 
						|
	)
 | 
						|
}
 | 
						|
 | 
						|
// ✅ Helper pour convertir la fréquence
 | 
						|
func mustParseFloat(s string) float64 {
 | 
						|
	f, _ := strconv.ParseFloat(s, 64)
 | 
						|
	return f
 | 
						|
}
 | 
						|
 | 
						|
func (spot *TelnetSpot) GetBand() {
 | 
						|
	freq := FreqMhztoHz(spot.Frequency)
 | 
						|
	switch true {
 | 
						|
	case strings.HasPrefix(freq, "1.8"):
 | 
						|
		spot.Band = "160M"
 | 
						|
		if spot.Mode == "SSB" {
 | 
						|
			spot.Mode = "LSB"
 | 
						|
		}
 | 
						|
	case strings.HasPrefix(freq, "3."):
 | 
						|
		spot.Band = "80M"
 | 
						|
		if spot.Mode == "SSB" {
 | 
						|
			spot.Mode = "LSB"
 | 
						|
		}
 | 
						|
	case strings.HasPrefix(freq, "5."):
 | 
						|
		spot.Band = "60M"
 | 
						|
		if spot.Mode == "SSB" {
 | 
						|
			spot.Mode = "LSB"
 | 
						|
		}
 | 
						|
	case strings.HasPrefix(freq, "7."):
 | 
						|
		spot.Band = "40M"
 | 
						|
		if spot.Mode == "SSB" {
 | 
						|
			spot.Mode = "LSB"
 | 
						|
		}
 | 
						|
	case strings.HasPrefix(freq, "10."):
 | 
						|
		spot.Band = "30M"
 | 
						|
	case strings.HasPrefix(freq, "14."):
 | 
						|
		spot.Band = "20M"
 | 
						|
		if spot.Mode == "SSB" {
 | 
						|
			spot.Mode = "USB"
 | 
						|
		}
 | 
						|
	case strings.HasPrefix(freq, "18."):
 | 
						|
		spot.Band = "17M"
 | 
						|
		if spot.Mode == "SSB" {
 | 
						|
			spot.Mode = "USB"
 | 
						|
		}
 | 
						|
	case strings.HasPrefix(freq, "21."):
 | 
						|
		spot.Band = "15M"
 | 
						|
		if spot.Mode == "SSB" {
 | 
						|
			spot.Mode = "USB"
 | 
						|
		}
 | 
						|
	case strings.HasPrefix(freq, "24."):
 | 
						|
		spot.Band = "12M"
 | 
						|
		if spot.Mode == "SSB" {
 | 
						|
			spot.Mode = "USB"
 | 
						|
		}
 | 
						|
	case strings.HasPrefix(freq, "28."):
 | 
						|
		spot.Band = "10M"
 | 
						|
		if spot.Mode == "SSB" {
 | 
						|
			spot.Mode = "USB"
 | 
						|
		}
 | 
						|
	case strings.HasPrefix(freq, "29."):
 | 
						|
		spot.Band = "10M"
 | 
						|
		if spot.Mode == "SSB" {
 | 
						|
			spot.Mode = "USB"
 | 
						|
		}
 | 
						|
	case strings.HasPrefix(freq, "50."):
 | 
						|
		spot.Band = "6M"
 | 
						|
		if spot.Mode == "SSB" {
 | 
						|
			spot.Mode = "USB"
 | 
						|
		}
 | 
						|
	default:
 | 
						|
		spot.Band = "N/A"
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
func (spot *TelnetSpot) GuessMode(rawSpot string) {
 | 
						|
	// ✅ D'ABORD : Chercher le mode dans le commentaire
 | 
						|
	if spot.Mode == "" {
 | 
						|
		spot.Mode = extractModeFromComment(spot.Comment)
 | 
						|
		if spot.Mode != "" {
 | 
						|
			Log.Debugf("Mode extracted from comment: %s", spot.Mode)
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	// ✅ Normaliser SSB avant de deviner
 | 
						|
	if spot.Mode == "SSB" {
 | 
						|
		if spot.Band == "10M" || spot.Band == "12M" || spot.Band == "6M" || spot.Band == "15M" || spot.Band == "17M" || spot.Band == "20M" {
 | 
						|
			spot.Mode = "USB"
 | 
						|
		} else {
 | 
						|
			spot.Mode = "LSB"
 | 
						|
		}
 | 
						|
		Log.Debugf("Converted SSB to %s for band %s", spot.Mode, spot.Band)
 | 
						|
		return
 | 
						|
	}
 | 
						|
 | 
						|
	// ✅ Si pas de mode, deviner depuis la fréquence
 | 
						|
	if spot.Mode == "" {
 | 
						|
		freqInt, err := strconv.ParseFloat(spot.Frequency, 32)
 | 
						|
		Log.Debugf("No mode specified in spot, will guess from frequency %v with spot: %s", spot.Frequency, strings.TrimSpace(rawSpot))
 | 
						|
		if err != nil {
 | 
						|
			Log.Errorf("could not convert frequency string to float: %v", err)
 | 
						|
			return
 | 
						|
		}
 | 
						|
 | 
						|
		switch spot.Band {
 | 
						|
		case "160M": // 1.800 - 2.000 MHz
 | 
						|
			if freqInt < 1838 {
 | 
						|
				spot.Mode = "CW"
 | 
						|
			} else if freqInt < 1843 {
 | 
						|
				spot.Mode = "FT8"
 | 
						|
			} else {
 | 
						|
				spot.Mode = "LSB"
 | 
						|
			}
 | 
						|
 | 
						|
		case "80M": // 3.500 - 4.000 MHz
 | 
						|
			if freqInt < 3560 {
 | 
						|
				spot.Mode = "CW"
 | 
						|
			} else if freqInt < 3575 {
 | 
						|
				spot.Mode = "FT8"
 | 
						|
			} else if freqInt < 3578 {
 | 
						|
				spot.Mode = "FT4"
 | 
						|
			} else if freqInt < 3590 {
 | 
						|
				spot.Mode = "RTTY"
 | 
						|
			} else {
 | 
						|
				spot.Mode = "LSB"
 | 
						|
			}
 | 
						|
 | 
						|
		case "60M": // 5.330 - 5.405 MHz
 | 
						|
			if freqInt < 5357 {
 | 
						|
				spot.Mode = "CW"
 | 
						|
			} else if freqInt < 5359 {
 | 
						|
				spot.Mode = "FT8"
 | 
						|
			} else {
 | 
						|
				spot.Mode = "USB"
 | 
						|
			}
 | 
						|
 | 
						|
		case "40M": // 7.000 - 7.300 MHz
 | 
						|
			if freqInt < 7040 {
 | 
						|
				spot.Mode = "CW"
 | 
						|
			} else if freqInt < 7047 {
 | 
						|
				spot.Mode = "RTTY"
 | 
						|
			} else if freqInt < 7050 {
 | 
						|
				spot.Mode = "FT4"
 | 
						|
			} else if freqInt < 7080 {
 | 
						|
				spot.Mode = "FT8" // ✅ 7.056 = FT8
 | 
						|
			} else if freqInt < 7125 {
 | 
						|
				spot.Mode = "RTTY" // ✅ 7.112 = RTTY
 | 
						|
			} else {
 | 
						|
				spot.Mode = "LSB"
 | 
						|
			}
 | 
						|
 | 
						|
		case "30M": // 10.100 - 10.150 MHz (CW/Digital seulement)
 | 
						|
			if freqInt < 10130 {
 | 
						|
				spot.Mode = "CW"
 | 
						|
			} else if freqInt < 10142 {
 | 
						|
				spot.Mode = "FT8"
 | 
						|
			} else {
 | 
						|
				spot.Mode = "FT4"
 | 
						|
			}
 | 
						|
 | 
						|
		case "20M": // 14.000 - 14.350 MHz
 | 
						|
			if freqInt < 14070 {
 | 
						|
				spot.Mode = "CW"
 | 
						|
			} else if freqInt < 14078 {
 | 
						|
				spot.Mode = "FT8"
 | 
						|
			} else if freqInt < 14083 {
 | 
						|
				spot.Mode = "FT4"
 | 
						|
			} else if freqInt < 14095 {
 | 
						|
				spot.Mode = "FT8"
 | 
						|
			} else if freqInt < 14112 {
 | 
						|
				spot.Mode = "RTTY"
 | 
						|
			} else {
 | 
						|
				spot.Mode = "USB"
 | 
						|
			}
 | 
						|
 | 
						|
		case "17M": // 18.068 - 18.168 MHz
 | 
						|
			if freqInt < 18090 {
 | 
						|
				spot.Mode = "CW"
 | 
						|
			} else if freqInt < 18104 {
 | 
						|
				spot.Mode = "FT8"
 | 
						|
			} else if freqInt < 18106 {
 | 
						|
				spot.Mode = "FT4"
 | 
						|
			} else if freqInt < 18110 {
 | 
						|
				spot.Mode = "RTTY"
 | 
						|
			} else {
 | 
						|
				spot.Mode = "USB"
 | 
						|
			}
 | 
						|
 | 
						|
		case "15M": // 21.000 - 21.450 MHz
 | 
						|
			if freqInt < 21070 {
 | 
						|
				spot.Mode = "CW"
 | 
						|
			} else if freqInt < 21078 {
 | 
						|
				spot.Mode = "FT8"
 | 
						|
			} else if freqInt < 21120 {
 | 
						|
				spot.Mode = "RTTY"
 | 
						|
			} else if freqInt < 21143 {
 | 
						|
				spot.Mode = "FT4"
 | 
						|
			} else {
 | 
						|
				spot.Mode = "USB"
 | 
						|
			}
 | 
						|
 | 
						|
		case "12M": // 24.890 - 24.990 MHz
 | 
						|
			if freqInt < 24910 {
 | 
						|
				spot.Mode = "CW" // ✅ 24.896 = CW
 | 
						|
			} else if freqInt < 24918 {
 | 
						|
				spot.Mode = "FT8"
 | 
						|
			} else if freqInt < 24922 {
 | 
						|
				spot.Mode = "FT4"
 | 
						|
			} else if freqInt < 24930 {
 | 
						|
				spot.Mode = "RTTY"
 | 
						|
			} else {
 | 
						|
				spot.Mode = "USB"
 | 
						|
			}
 | 
						|
 | 
						|
		case "10M": // 28.000 - 29.700 MHz
 | 
						|
			if freqInt < 28070 {
 | 
						|
				spot.Mode = "CW"
 | 
						|
			} else if freqInt < 28096 {
 | 
						|
				spot.Mode = "FT8"
 | 
						|
			} else if freqInt < 28179 {
 | 
						|
				spot.Mode = "RTTY"
 | 
						|
			} else if freqInt < 28190 {
 | 
						|
				spot.Mode = "FT4"
 | 
						|
			} else if freqInt < 29000 {
 | 
						|
				spot.Mode = "USB"
 | 
						|
			} else {
 | 
						|
				spot.Mode = "FM"
 | 
						|
			}
 | 
						|
 | 
						|
		case "6M": // 50.000 - 54.000 MHz
 | 
						|
			if freqInt < 50100 {
 | 
						|
				spot.Mode = "CW"
 | 
						|
			} else if freqInt < 50313 {
 | 
						|
				spot.Mode = "USB" // ✅ DX Window + général
 | 
						|
			} else if freqInt < 50318 {
 | 
						|
				spot.Mode = "FT8" // ✅ 50.313-50.318
 | 
						|
			} else if freqInt < 50323 {
 | 
						|
				spot.Mode = "FT4" // ✅ 50.318-50.323
 | 
						|
			} else if freqInt < 51000 {
 | 
						|
				spot.Mode = "USB" // ✅ Retour à USB
 | 
						|
			} else {
 | 
						|
				spot.Mode = "FM"
 | 
						|
			}
 | 
						|
 | 
						|
		default:
 | 
						|
			// ✅ Bande inconnue
 | 
						|
			if freqInt < 10.0 {
 | 
						|
				spot.Mode = "LSB"
 | 
						|
			} else {
 | 
						|
				spot.Mode = "USB"
 | 
						|
			}
 | 
						|
		}
 | 
						|
 | 
						|
		if spot.Mode != "" {
 | 
						|
			Log.Debugf("✅ Guessed mode %s for %s on %s MHz (band %s)", spot.Mode, spot.DX, spot.Frequency, spot.Band)
 | 
						|
		} else {
 | 
						|
			Log.Warnf("❌ Could not guess mode for %s on %s MHz (band %s), raw spot: %s", spot.DX, spot.Frequency, spot.Band, rawSpot)
 | 
						|
		}
 | 
						|
	} else {
 | 
						|
		spot.Mode = strings.ToUpper(spot.Mode)
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
// ✅ Extraire le mode depuis le commentaire
 | 
						|
func extractModeFromComment(comment string) string {
 | 
						|
	commentUpper := strings.ToUpper(comment)
 | 
						|
 | 
						|
	// ✅ 1. Détecter FT8/FT4 avec leurs patterns typiques (dB + Hz)
 | 
						|
	if strings.Contains(commentUpper, "FT8") ||
 | 
						|
		(strings.Contains(commentUpper, "DB") && strings.Contains(commentUpper, "HZ")) {
 | 
						|
		return "FT8"
 | 
						|
	}
 | 
						|
 | 
						|
	if strings.Contains(commentUpper, "FT4") {
 | 
						|
		return "FT4"
 | 
						|
	}
 | 
						|
 | 
						|
	// ✅ 2. Détecter CW avec WPM (Words Per Minute)
 | 
						|
	if strings.Contains(commentUpper, "WPM") || strings.Contains(commentUpper, " CW ") ||
 | 
						|
		strings.HasSuffix(commentUpper, "CW") || strings.HasPrefix(commentUpper, "CW ") {
 | 
						|
		return "CW"
 | 
						|
	}
 | 
						|
 | 
						|
	// ✅ 3. Autres modes digitaux
 | 
						|
	digitalModes := []string{"RTTY", "PSK31", "PSK63", "PSK", "MFSK", "OLIVIA", "CONTESTIA", "JT65", "JT9"}
 | 
						|
	for _, mode := range digitalModes {
 | 
						|
		if strings.Contains(commentUpper, mode) {
 | 
						|
			return mode
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	// ✅ 4. Modes voice
 | 
						|
	voiceModes := []string{"USB", "LSB", "SSB", "FM", "AM"}
 | 
						|
	for _, mode := range voiceModes {
 | 
						|
		// Chercher le mode comme mot complet (pas dans "SSBC" par exemple)
 | 
						|
		if strings.Contains(commentUpper, " "+mode+" ") ||
 | 
						|
			strings.HasPrefix(commentUpper, mode+" ") ||
 | 
						|
			strings.HasSuffix(commentUpper, " "+mode) ||
 | 
						|
			commentUpper == mode {
 | 
						|
			return mode
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	return ""
 | 
						|
}
 |