Files
ham/protocol/aprs/message.go
maze 8f8a97300f
Some checks failed
Run tests / test (1.25) (push) Failing after 1m36s
Run tests / test (stable) (push) Failing after 1m36s
protocol/aprs: extract comment to frame
2026-03-02 22:37:39 +01:00

100 lines
2.5 KiB
Go

package aprs
import (
"fmt"
"strconv"
"strings"
)
type Message struct {
ID int `json:"id"`
IsAcknowledge bool `json:"is_ack"`
IsRejection bool `json:"is_rejection"`
IsBulletin bool `json:"is_bulletin"`
Recipient string `json:"recipient"`
AnnoucementID byte `json:"announcement_id,omitempty"`
Group string `json:"group,omitempty"`
Severity string `json:"severity,omitempty"`
Text string `json:"text"`
}
func (msg *Message) String() string {
return fmt.Sprintf("%s: %q", msg.Recipient, msg.Text)
}
type messageDecoder struct{}
func (messageDecoder) CanDecode(frame *Frame) bool {
var (
maybeMessage = len(frame.Raw) >= 11 && frame.Raw[10] == ':'
maybeBulletin = len(frame.Raw) >= 10 && frame.Raw[9] == ':'
)
return frame.Raw.Type() == ':' && (maybeMessage || maybeBulletin)
}
func (messageDecoder) Decode(frame *Frame) (data Data, err error) {
var (
msg = new(Message)
text string
size = len(text)
)
if len(frame.Raw) >= 10 && frame.Raw[9] == ':' {
msg.Recipient, text = strings.TrimSpace(string(frame.Raw[1:9])), string(frame.Raw[9:])
} else {
msg.Recipient, text = strings.TrimSpace(string(frame.Raw[1:10])), string(frame.Raw[10:])
}
switch {
case strings.HasPrefix(msg.Recipient, "BLN"):
// Bulletin
kind := msg.Recipient[3:]
if id, err := strconv.Atoi(kind); err == nil {
// General bulletin
msg.IsBulletin = true
msg.ID = id
} else if len(kind) >= 2 && isDigit(kind[0]) {
// Group Bulletin Format
msg.IsBulletin = true
msg.ID = int(kind[0] - '0')
msg.Group = kind[1:]
} else if len(kind) == 1 {
// Announcement
msg.IsBulletin = true
msg.AnnoucementID = kind[0]
}
case strings.HasPrefix(msg.Recipient, "NWS-"):
// National Weather Service Bulletins
msg.IsBulletin = true
msg.Severity = msg.Recipient[4:]
default:
if i := strings.LastIndexByte(text, '{'); i != -1 && i > (size-6) {
// Plain message ID: {XXXXX (where there are 1-5 X)
msg.ID, _ = strconv.Atoi(text[i+1:])
text = text[:i]
}
if i := strings.LastIndex(text, ":ack"); i != -1 && i > (size-9) {
// Message acknowledgement: :ackXXXXX (where there are 1-5 X)
msg.IsAcknowledge = true
msg.ID, _ = strconv.Atoi(text[i+4:])
text = text[:i]
}
if i := strings.LastIndex(text, ":rej"); i != -1 && i > (size-9) {
// Message acknowledgement: :rejXXXXX (where there are 1-5 X)
msg.IsRejection = true
msg.ID, _ = strconv.Atoi(text[i+4:])
text = text[:i]
}
}
if len(text) > 0 && text[0] == ':' {
text = text[1:]
}
msg.Text = text
return msg, nil
}