protocol/aprs: refactored
Some checks failed
Run tests / test (1.25) (push) Failing after 1m37s
Run tests / test (stable) (push) Failing after 1m37s

This commit is contained in:
2026-03-02 22:28:17 +01:00
parent 452f521866
commit 63040a44b3
24 changed files with 3506 additions and 1533 deletions

119
protocol/aprs/data.go Normal file
View File

@@ -0,0 +1,119 @@
package aprs
import (
"fmt"
"regexp"
"strconv"
"time"
)
type decoder interface {
CanDecode(*Frame) bool
Decode(*Frame) (Data, error)
}
var decoders []decoder
func init() {
decoders = []decoder{
positionDecoder{},
micEDecoder{},
messageDecoder{},
queryDecoder{},
}
}
// Raw is the string encoded raw frame payload.
type Raw string
func (p Raw) Type() Type {
var t Type
if len(p) > 0 {
t = Type(p[0])
}
return t
}
// Data represents a decoded payload data.
type Data interface {
String() string
}
// Type of payload.
type Type byte
var typeName = map[Type]string{
0x1c: "Current Mic-E Data (Rev 0 beta)",
0x1d: "Old Mic-E Data (Rev 0 beta)",
'!': "Position without timestamp (no APRS messaging), or Ultimeter 2000 WX Station",
'#': "Peet Bros U-II Weather Station",
'$': "Raw GPS data or Ultimeter 2000",
'%': "Agrelo DFJr / MicroFinder",
'"': "Old Mic-E Data (but Current data for TM-D700)",
')': "Item",
'*': "Peet Bros U-II Weather Station",
',': "Invalid data or test data",
'/': "Position with timestamp (no APRS messaging)",
':': "Message",
';': "Object",
'<': "Station Capabilities",
'=': "Position without timestamp (with APRS messaging)",
'>': "Status",
'?': "Query",
'@': "Position with timestamp (with APRS messaging)",
'T': "Telemetry data",
'[': "Maidenhead grid locator beacon (obsolete)",
'_': "Weather Report (without position)",
'`': "Current Mic-E Data (not used in TM-D700)",
'{': "User-Defined APRS packet format",
'}': "Third-party traffic",
}
func (t Type) String() string {
if s, ok := typeName[t]; ok {
return s
}
return fmt.Sprintf("unknown %02x", byte(t))
}
func (t Type) IsMessage() bool {
return t == ':'
}
func (t Type) IsThirdParty() bool {
return t == '}'
}
var matchTimestamp = regexp.MustCompile(`^[0-9]{6}[zh/]`)
func hasTimestamp(s string) bool {
return matchTimestamp.MatchString(s)
}
func parseTimestamp(s string) (t time.Time, comment string, err error) {
// log.Printf("parse timestamp %q", s)
var hh, mm, ss int
if hh, err = strconv.Atoi(s[0:2]); err != nil {
return
}
if mm, err = strconv.Atoi(s[2:4]); err != nil {
return
}
if ss, err = strconv.Atoi(s[4:6]); err != nil {
return
}
now := time.Now()
switch s[6] {
case 'z': // DDHHMMz zulu time
now = now.UTC()
return time.Date(now.Year(), now.Month(), hh, mm, ss, 0, 0, time.UTC), s[7:], nil
case '/': // DDHHMM/ local time
return time.Date(now.Year(), now.Month(), hh, mm, ss, 0, 0, now.Location()), s[7:], nil
case 'h': // HHMMSSh zulu time
now = now.UTC()
return time.Date(now.Year(), now.Month(), now.Day(), hh, mm, ss, 0, time.UTC), s[7:], nil
default:
return time.Time{}, "", fmt.Errorf("aprs: invalid/unknown timestamp marker %q", s[6])
}
}