162 lines
2.9 KiB
Go
162 lines
2.9 KiB
Go
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
|
|
}
|