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]) } }