package ft8 import ( "bufio" "git.wntrmute.dev/kyle/goutils/die" "os" "os/user" "path/filepath" "regexp" "runtime" "strconv" "strings" "time" ) func getHomeDirectory() string { u, err := user.Current() if err != nil { return "" } return u.HomeDir } func GetDefaultDirectory() string { switch runtime.GOOS { case "linux": return filepath.Join( getHomeDirectory(), ".local", "share", "WSJT-X", ) case "windows": return filepath.Join( getHomeDirectory(), "AppData", "Local", "WSJT-X", ) default: die.With("Operating system '%s' isn't supported.", runtime.GOOS) } return "" } const ( RecordTypeCQ = iota + 1 RecordTypeCQReply RecordTypeSignal RecordTypeSignoff ) // call ([\w\d/]+) // grid (\w{2}\d{2}) var ( RegexpCQ = regexp.MustCompile(`^CQ ([\w/]+) (\w{2}\d{2})$`) RegexpCQReply = regexp.MustCompile(`^([\w/]+) ([\w/]+) (\w{2}\d{2})$`) RegexpRR73 = regexp.MustCompile(`^([\w\d/]+) ([\w\d/]+) RR73$`) ) type Record struct { Type uint8 Time time.Time Freq float64 Tone int Mode string IsTX bool De string To string Received int Grid string } func (rec *Record) HasGrid() bool { return rec.Grid != "" } func ParseRecordHeader(line string) (rec *Record, err error) { // 0 1 2 3 4 5 6 7 // 231215_021330 14.074 Rx FT8 -6 0.1 1713 CQ N6ACA CM97 fields := strings.Fields(line) rec = &Record{} rec.Time, err = time.Parse("060102_150405", fields[0]) if err != nil { return } rec.Freq, err = strconv.ParseFloat(fields[1], 64) if err != nil { return } if fields[2] == "Tx" { rec.IsTX = true } rec.Mode = fields[3] rec.Received, err = strconv.Atoi(fields[4]) if err != nil { return } rec.Tone, err = strconv.Atoi(fields[6]) if err != nil { return } return } func ParseRecord(line string) (*Record, error) { rec, err := ParseRecordHeader(line) if err != nil { return nil, err } fields := strings.Fields(line) line = strings.Join(fields[7:len(fields)], " ") switch { case RegexpRR73.MatchString(line): rec.Type = RecordTypeSignoff match := RegexpRR73.FindStringSubmatch(line) rec.De = match[2] rec.To = match[1] case RegexpCQ.MatchString(line): rec.Type = RecordTypeCQ match := RegexpCQ.FindStringSubmatch(line) rec.De = match[1] rec.Grid = match[2] case RegexpCQReply.MatchString(line): rec.Type = RecordTypeCQReply match := RegexpCQReply.FindStringSubmatch(line) rec.To = match[1] rec.De = match[2] rec.Grid = match[3] } return rec, err } func ProcessFile(path string) ([]*Record, error) { file, err := os.Open(path) if err != nil { return nil, err } defer file.Close() var records []*Record scanner := bufio.NewScanner(file) for scanner.Scan() { rec, err := ParseRecord(scanner.Text()) if err != nil { return nil, err } records = append(records, rec) } return records, nil }