|
|
|
package wsjtx
|
|
|
|
|
|
|
|
import (
|
|
|
|
"strconv"
|
|
|
|
"strings"
|
|
|
|
"time"
|
|
|
|
"github.com/mmcloughlin/geohash"
|
|
|
|
"github.com/tzneal/ham-go/dxcc"
|
|
|
|
log "github.com/sirupsen/logrus"
|
|
|
|
)
|
|
|
|
|
|
|
|
type Row struct {
|
|
|
|
Time time.Time
|
|
|
|
Bandf float64
|
|
|
|
Direction string
|
|
|
|
Mode string
|
|
|
|
Strength int
|
|
|
|
Offset float64
|
|
|
|
Freq string
|
|
|
|
First string
|
|
|
|
Second string
|
|
|
|
Third string
|
|
|
|
Fourth string
|
|
|
|
}
|
|
|
|
|
|
|
|
type Result struct {
|
|
|
|
Station string
|
|
|
|
Identifier string
|
|
|
|
Ent dxcc.Entity
|
|
|
|
Call string
|
|
|
|
Grid string
|
|
|
|
Band string
|
|
|
|
Mode string
|
|
|
|
Signal int
|
|
|
|
GeoHash string
|
|
|
|
Timestamp time.Time
|
|
|
|
Rx int
|
|
|
|
}
|
|
|
|
|
|
|
|
var blackList = map[string]bool {
|
|
|
|
"73": true,
|
|
|
|
"RR73;": true,
|
|
|
|
"GL": true,
|
|
|
|
"TNX": true,
|
|
|
|
"<...>": true,
|
|
|
|
"QSO": true,
|
|
|
|
"QSY": true,
|
|
|
|
"TIME": true,
|
|
|
|
"TIMESYNC": true,
|
|
|
|
}
|
|
|
|
|
|
|
|
func GetBand(freq float64) (string){
|
|
|
|
band := "unknown"
|
|
|
|
if (freq>1 && freq<2) {
|
|
|
|
band = "160m"
|
|
|
|
}
|
|
|
|
if (freq>3 && freq<4) {
|
|
|
|
band = "80m"
|
|
|
|
}
|
|
|
|
if (freq>5 && freq<6) {
|
|
|
|
band = "60m"
|
|
|
|
}
|
|
|
|
if (freq>7.0 && freq<8.0) {
|
|
|
|
band = "40m"
|
|
|
|
}
|
|
|
|
if (freq>10 && freq<11) {
|
|
|
|
band = "30m"
|
|
|
|
}
|
|
|
|
if (freq>14 && freq<15) {
|
|
|
|
band = "20m"
|
|
|
|
}
|
|
|
|
if (freq>18 && freq<19) {
|
|
|
|
band = "17m"
|
|
|
|
}
|
|
|
|
if (freq>21 && freq<22) {
|
|
|
|
band = "15m"
|
|
|
|
}
|
|
|
|
if (freq>24 && freq<25) {
|
|
|
|
band = "12m"
|
|
|
|
}
|
|
|
|
if (freq>28 && freq<30) {
|
|
|
|
band = "10m"
|
|
|
|
}
|
|
|
|
if (freq>144 && freq<145) {
|
|
|
|
band = "2m"
|
|
|
|
}
|
|
|
|
if (freq>432 && freq<433){
|
|
|
|
band = "70cm"
|
|
|
|
}
|
|
|
|
return band
|
|
|
|
}
|
|
|
|
|
|
|
|
func ScanLine(line string) (Result, bool) {
|
|
|
|
var err error
|
|
|
|
var tmp string
|
|
|
|
element := new(Row)
|
|
|
|
result := new(Result)
|
|
|
|
found := false
|
|
|
|
|
|
|
|
// dont fail on too short lines
|
|
|
|
if len(line) < 16 {
|
|
|
|
log.WithFields(log.Fields{"line":line}).Error("line too short")
|
|
|
|
return *result, false
|
|
|
|
}
|
|
|
|
|
|
|
|
// parse only lines in new format, because old format misses band
|
|
|
|
if line[6] == '_' && line[15] != 'T' {
|
|
|
|
dataSlice := strings.Fields(line)
|
|
|
|
for i, v := range dataSlice {
|
|
|
|
switch i {
|
|
|
|
case 0:
|
|
|
|
element.Time, err = time.Parse("060102_150405",v)
|
|
|
|
if err != nil {
|
|
|
|
log.WithFields(log.Fields{"err":err}).Trace("something went wrong while parsing the timestamp: ",v)
|
|
|
|
return *result, false
|
|
|
|
}
|
|
|
|
case 1:
|
|
|
|
element.Bandf,_ = strconv.ParseFloat(v,10)
|
|
|
|
case 2:
|
|
|
|
element.Direction = v
|
|
|
|
case 3:
|
|
|
|
element.Mode = v
|
|
|
|
case 4:
|
|
|
|
element.Strength,_ = strconv.Atoi(v)
|
|
|
|
case 5:
|
|
|
|
element.Offset,_ = strconv.ParseFloat(v,10)
|
|
|
|
case 6:
|
|
|
|
element.Freq = v
|
|
|
|
case 7:
|
|
|
|
element.First= v
|
|
|
|
case 8:
|
|
|
|
element.Second= v
|
|
|
|
case 9:
|
|
|
|
element.Third = v
|
|
|
|
case 10:
|
|
|
|
element.Fourth = v
|
|
|
|
default:
|
|
|
|
log.WithFields(log.Fields{"line":line}).Trace("can't parse line..")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// check for 4 element sequence like 'CQ DX DL3SD JO31'
|
|
|
|
if element.Fourth != "" {
|
|
|
|
result.Call = element.Third
|
|
|
|
log.WithFields(log.Fields{"line":line,"callsign":result.Call}).Trace("parsed 4 element callsign")
|
|
|
|
} else {
|
|
|
|
result.Call = element.Second
|
|
|
|
}
|
|
|
|
|
|
|
|
// ignore 'TNX QSO GL 73' etc.
|
|
|
|
if blackList[result.Call] {
|
|
|
|
log.WithFields(log.Fields{"line":line,"callsign":result.Call}).Trace("skipping callsign")
|
|
|
|
return *result, false
|
|
|
|
}
|
|
|
|
|
|
|
|
// take care of Calls like <DL3SD> / FIXME does this actually work?
|
|
|
|
tmp = strings.Replace(result.Call, "<", "", -1)
|
|
|
|
result.Call = strings.Replace(tmp, ">", "", -1)
|
|
|
|
|
|
|
|
result.Band = GetBand(element.Bandf)
|
|
|
|
result.Ent, found = dxcc.Lookup(result.Call) // FIXME this is where the expensive stuff happens and should be cached..
|
|
|
|
// FIXME result.Grid = qrz.lookup(result.Call) ;) or track in ALL.txt ^^
|
|
|
|
if found && result.Band != "unknown" {
|
|
|
|
result.Signal = element.Strength
|
|
|
|
result.Mode = element.Mode
|
|
|
|
// FIXME
|
|
|
|
// * get better grid, see above
|
|
|
|
// * build geohash from better grid with gpsfromgrid(result.Grid)
|
|
|
|
result.GeoHash = geohash.Encode(result.Ent.Latitude, result.Ent.Longitude)
|
|
|
|
result.Timestamp = element.Time
|
|
|
|
if element.Direction[0] == 'R' {
|
|
|
|
result.Rx = 1
|
|
|
|
} else {
|
|
|
|
result.Rx = 0
|
|
|
|
result.Grid = element.Third
|
|
|
|
}
|
|
|
|
log.WithFields(log.Fields{ "call":result.Call,
|
|
|
|
"signal":result.Signal,
|
|
|
|
"dxcc":result.Ent.DXCC,
|
|
|
|
"continent":result.Ent.Continent,
|
|
|
|
"band":result.Band,
|
|
|
|
"sendtime":result.Timestamp,
|
|
|
|
"mode":result.Mode,
|
|
|
|
"geohash":result.GeoHash,
|
|
|
|
"rx":result.Rx,
|
|
|
|
}).Trace("successfully parsed line")
|
|
|
|
return *result, true
|
|
|
|
} else {
|
|
|
|
if !found {
|
|
|
|
log.WithFields(log.Fields{"line":line,"callsign":element.Second}).Trace("cant parse callsign")
|
|
|
|
} else if result.Band == "unknown" {
|
|
|
|
log.WithFields(log.Fields{"line":line,"band":result.Band}).Trace("cant parse band")
|
|
|
|
} else {
|
|
|
|
log.WithFields(log.Fields{"line":line}).Error("something really strange happened..")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
// log.WithFields(log.Fields{"line":line}).Info("found old formated line..")
|
|
|
|
}
|
|
|
|
return *result, false
|
|
|
|
}
|
|
|
|
|
|
|
|
|