diff --git a/protocol/aprs/address.go b/protocol/aprs/address.go index f595505..99a288e 100644 --- a/protocol/aprs/address.go +++ b/protocol/aprs/address.go @@ -1,126 +1,82 @@ package aprs -import ( - "encoding/json" - "errors" - "fmt" - "strings" -) - -var ( - ErrAddressInvalid = errors.New(`aprs: invalid address`) -) +import "strings" type Address struct { Call string `json:"call"` SSID string `json:"ssid,omitempty"` - IsRepeated bool `json:"is_repeated,omitempty"` + IsRepeated bool `json:"is_repeated"` +} + +func ParseAddress(s string) Address { + r := strings.HasSuffix(s, "*") + if r { + s = strings.TrimSuffix(s, "*") + } + i := strings.IndexByte(s, '-') + if i < 1 { + return Address{ + Call: s, + IsRepeated: r, + } + } + return Address{ + Call: s[:i], + SSID: s[i+1:], + IsRepeated: r, + } } func (a Address) EqualTo(b Address) bool { - return a.Call == b.Call && a.SSID == b.SSID + return strings.EqualFold(a.Call, b.Call) && a.SSID == b.SSID +} + +func (a Address) IsQConstruct() bool { + return len(a.Call) == 3 && len(a.SSID) == 0 && a.Call[0] == 'q' +} + +func (a Address) Passcode() int16 { + v := int16(0x73e2) + for i, l := 0, len(a.Call); i < l; i = i + 2 { + v ^= int16(a.Call[i]) << 8 + if i+1 < len(a.Call) { + v ^= int16(a.Call[i+1]) + } + } + return v & 0x7fff } func (a Address) String() string { - var r = "" - + var b strings.Builder + b.WriteString(a.Call) + if a.SSID != "" { + b.WriteByte('-') + b.WriteString(a.SSID) + } if a.IsRepeated { - r = "*" + b.WriteByte('*') } - if a.SSID == "" { - return a.Call + r - } - return fmt.Sprintf("%s-%s%s", a.Call, a.SSID, r) -} - -func (a Address) Secret() int16 { - var h = int16(0x73e2) - var c = a.Call - - if len(c)%2 > 0 { - c += "\x00" - } - for i := 0; i < len(c); i += 2 { - h ^= int16(c[i]) << 8 - h ^= int16(c[i+1]) - } - return h & 0x7fff -} - -func (a Address) MarshalJSON() ([]byte, error) { - return json.Marshal(a.String()) -} - -func (a *Address) UnmarshalJSON(b []byte) error { - var s string - if err := json.Unmarshal(b, &s); err != nil { - return err - } - p, err := ParseAddress(s) - if err != nil { - return err - } - a.Call = p.Call - a.SSID = p.SSID - a.IsRepeated = p.IsRepeated - return nil -} - -func ParseAddress(s string) (Address, error) { - r := strings.HasSuffix(s, "*") - if r { - s = s[:len(s)-1] - } - p := strings.Split(s, "-") - if len(p) == 0 || len(p) > 2 { - return Address{}, ErrAddressInvalid - } - - a := Address{Call: p[0], IsRepeated: r} - if len(p) == 2 { - a.SSID = p[1] - } - - return a, nil -} - -func MustParseAddress(s string) Address { - a, err := ParseAddress(s) - if err != nil { - panic(err) - } - return a -} - -func IsQConstruct(call string) bool { - return len(call) == 3 && call[0] == 'q' + return b.String() } type Path []Address -func (p Path) String() string { - var s = make([]string, len(p)) - for i, a := range p { - s[i] = a.String() +func (path Path) EqualTo(other Path) bool { + if len(path) != len(other) { + return false } - return strings.Join(s, ",") -} - -func ParsePath(p string) (Path, error) { - ss := strings.Split(p, ",") - - if len(ss) == 0 { - return nil, nil - } - - var err error - as := make(Path, len(ss)) - for i, s := range ss { - as[i], err = ParseAddress(s) - if err != nil { - return nil, err + for i, a := range path { + if !a.EqualTo(other[i]) { + return false } } - - return as, nil + return true +} + +func (path Path) String() string { + part := make([]string, len(path)) + for i, a := range path { + part[i] = a.String() + } + return strings.Join(part, ",") } diff --git a/protocol/aprs/base91.go b/protocol/aprs/base91.go index 779a654..67869be 100644 --- a/protocol/aprs/base91.go +++ b/protocol/aprs/base91.go @@ -2,12 +2,14 @@ package aprs import ( "fmt" + "slices" ) func base91Decode(s string) (n int, err error) { for i, l := 0, len(s); i < l; i++ { c := s[i] if c < 33 || c > 122 { + // panic(fmt.Sprintf("aprs: invalid base-91 encoding char %q (%d)", c, c)) return 0, fmt.Errorf("aprs: invalid base-91 encoding char %q (%d)", c, c) } @@ -17,14 +19,13 @@ func base91Decode(s string) (n int, err error) { return } -/* -func base91Encode(n int) string { - var s []string - for n > 0 { - c := n % 91 +func base91Encode(b []byte, n int) { + i := 0 + for n > 1 { + x := n % 91 n /= 91 - s = append([]string{string(byte(c) + 33)}, s...) + b[i] = byte(x) + 33 + i++ } - return strings.Join(s, "") + slices.Reverse(b[:i]) } -*/ diff --git a/protocol/aprs/data.go b/protocol/aprs/data.go new file mode 100644 index 0000000..7c04d63 --- /dev/null +++ b/protocol/aprs/data.go @@ -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]) + } +} diff --git a/protocol/aprs/data_test.go b/protocol/aprs/data_test.go new file mode 100644 index 0000000..c70daea --- /dev/null +++ b/protocol/aprs/data_test.go @@ -0,0 +1,29 @@ +package aprs + +import ( + "reflect" + "testing" +) + +func testCompareData(t *testing.T, want, test Data) { + t.Helper() + + if want == nil && test != nil { + t.Errorf("expected no data, got %T", test) + return + } + if want != nil && test == nil { + t.Errorf("expected data %T, got nil", want) + return + } + + if reflect.TypeOf(want) != reflect.TypeOf(test) { + t.Errorf("expected data %T, got %T", want, test) + return + } + + switch want := want.(type) { + case *Position: + testComparePosition(t, want, test.(*Position)) + } +} diff --git a/protocol/aprs/datatype.go b/protocol/aprs/datatype.go deleted file mode 100644 index 05b4c1f..0000000 --- a/protocol/aprs/datatype.go +++ /dev/null @@ -1,41 +0,0 @@ -package aprs - -import "fmt" - -type DataType byte - -var ( - dataTypeName = map[DataType]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 DataType) String() string { - if s, ok := dataTypeName[t]; ok { - return s - } - return fmt.Sprintf("Unknown packet type %#02x", byte(t)) -} diff --git a/protocol/aprs/frame.go b/protocol/aprs/frame.go new file mode 100644 index 0000000..c00284a --- /dev/null +++ b/protocol/aprs/frame.go @@ -0,0 +1,58 @@ +package aprs + +import ( + "errors" + "strings" +) + +var ( + ErrPayloadMarker = errors.New("aprs: can't find payload marker") + ErrDestinationMarker = errors.New("aprs: can't find destination marker") + ErrPathLength = errors.New("aprs: invalid path length") +) + +// Frame represents a single APRS frame. +type Frame struct { + Source Address `json:"source"` + Destination Address `json:"destination"` + Path Path `json:"path"` + Raw Raw `json:"raw"` + Data Data `json:"data,omitempty"` +} + +func Parse(s string) (*Frame, error) { + i := strings.IndexByte(s, ':') + if i == -1 { + return nil, ErrPayloadMarker + } + + var ( + route = s[:i] + frame = &Frame{Raw: Raw(s[i+1:])} + ) + if i = strings.IndexByte(route, '>'); i == -1 { + return nil, ErrDestinationMarker + } + frame.Source, route = ParseAddress(route[:i]), route[i+1:] + + path := strings.Split(route, ",") + if len(path) == 0 || len(path) > 9 { + return nil, ErrPathLength + } + frame.Destination = ParseAddress(path[0]) + for i, l := 1, len(path); i < l; i++ { + addr := ParseAddress(path[i]) + frame.Path = append(frame.Path, addr) + } + + var err error + for _, d := range decoders { + if d.CanDecode(frame) { + if frame.Data, err = d.Decode(frame); err == nil { + break + } + } + } + + return frame, nil +} diff --git a/protocol/aprs/frame_test.go b/protocol/aprs/frame_test.go new file mode 100644 index 0000000..d41cdf0 --- /dev/null +++ b/protocol/aprs/frame_test.go @@ -0,0 +1,136 @@ +package aprs + +import ( + "bufio" + "io" + "math" + "os" + "path/filepath" + "strconv" + "strings" + "testing" +) + +func TestParse(t *testing.T) { + tests := []struct { + Line string + Want *Frame + }{ + { + `PD0MZ-10>APLRG1,WIDE1-1,qAC:=L4:I#P),la !GLoRa APRS iGate / 433.775MHz / 125kHz / SF12 / CR5 Batt=4.25V`, + &Frame{ + Source: Address{Call: "PD0MZ", SSID: "10"}, + Destination: Address{Call: "APLRG1"}, + Path: Path{{Call: "WIDE1", SSID: "1"}, {Call: "qAC"}}, + Data: &Position{ + Latitude: 51.860004, + Longitude: 6.309997, + HasMessaging: true, + Symbol: "La", + Comment: "LoRa APRS iGate / 433.775MHz / 125kHz / SF12 / CR5 Batt=4.25V", + }, + }, + }, + } + + for _, test := range tests { + t.Run(test.Line, func(t *testing.T) { + f, err := Parse(test.Line) + if err != nil { + t.Fatal(err) + } + //if !reflect.DeepEqual(f, test.Want) { + // t.Errorf("expected %+#v, got %#+v", test.Want, f) + //} + testCompareFrame(t, test.Want, f) + }) + } +} + +func TestParseSamples(t *testing.T) { + f, err := os.Open(filepath.Join("testdata", "packets.txt")) + if err != nil { + t.Skip(err) + return + } + defer func() { _ = f.Close() }() + + r := bufio.NewReader(f) + for { + l, err := r.ReadString('\n') + if err != nil { + if err == io.EOF { + return + } + t.Fatal(err) + } + + l = testUnescapeHex(strings.TrimSpace(l[25:])) + if _, err = Parse(l); err != nil { + t.Errorf("%s: %v", l, err) + } + } +} + +// testUnescapeHex replaces occurrences of <0xHH> (case-insensitive) +// with the corresponding single byte. +// Invalid sequences are left unchanged. +func testUnescapeHex(s string) string { + if !strings.Contains(s, "<0x") && !strings.Contains(s, "<0X") { + return s + } + + var b strings.Builder + b.Grow(len(s)) // upper bound; result will not exceed input length + + for i := 0; i < len(s); { + // Minimal length for "<0xHH>" is 6 + if i+6 <= len(s) && + s[i] == '<' && + (s[i+1] == '0') && + (s[i+2] == 'x' || s[i+2] == 'X') && + testIsHex(s[i+3]) && + testIsHex(s[i+4]) && + s[i+5] == '>' { + + // Parse the hex byte + v, err := strconv.ParseUint(s[i+3:i+5], 16, 8) + if err == nil { + b.WriteByte(byte(v)) + i += 6 + continue + } + } + + // Default: copy one byte and continue + b.WriteByte(s[i]) + i++ + } + + return b.String() +} + +func testIsHex(c byte) bool { + return ('0' <= c && c <= '9') || + ('a' <= c && c <= 'f') || + ('A' <= c && c <= 'F') +} + +func testAlmostEqual(a, b float64) bool { + return math.Abs(a-b) < 1e-5 +} + +func testCompareFrame(t *testing.T, want, test *Frame) { + t.Helper() + + if !test.Source.EqualTo(want.Source) { + t.Errorf("expected source %s, got %s", want.Source, test.Source) + } + if !test.Destination.EqualTo(want.Destination) { + t.Errorf("expected destination %s, got %s", want.Destination, test.Destination) + } + if !test.Path.EqualTo(want.Path) { + t.Errorf("expected path %q, got %q", want.Path, test.Path) + } + testCompareData(t, want.Data, test.Data) +} diff --git a/protocol/aprs/message.go b/protocol/aprs/message.go new file mode 100644 index 0000000..6e371e7 --- /dev/null +++ b/protocol/aprs/message.go @@ -0,0 +1,99 @@ +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 +} diff --git a/protocol/aprs/message_test.go b/protocol/aprs/message_test.go new file mode 100644 index 0000000..06a009b --- /dev/null +++ b/protocol/aprs/message_test.go @@ -0,0 +1,153 @@ +package aprs + +import ( + "testing" +) + +func TestParseMessage(t *testing.T) { + tests := []struct { + Name string + Raw Raw + Want *Message + }{ + { + "message, no ack expected", + ":WU2Z :Testing", + &Message{ + Recipient: "WU2Z", + Text: "Testing", + }, + }, + { + "message with sequence number, ack expected", + ":WU2Z :Testing{003", + &Message{ + ID: 3, + Recipient: "WU2Z", + Text: "Testing", + }, + }, + { + "an e-mail message", + ":EMAIL :msproul@ap.org Test email", + &Message{ + Recipient: "EMAIL", + Text: "msproul@ap.org Test email", + }, + }, + { + "message acknowledgement", + ":KB2ICI-14:ack003", + &Message{ + ID: 3, + IsAcknowledge: true, + Recipient: "KB2ICI-14", + }, + }, + { + "message rejection", + ":KB2ICI-14:rej003", + &Message{ + ID: 3, + IsRejection: true, + Recipient: "KB2ICI-14", + }, + }, + { + "bulletin", + ":BLN3 :Snow expected in Tampa RSN", + &Message{ + ID: 3, + IsBulletin: true, + Recipient: "BLN3", + Text: "Snow expected in Tampa RSN", + }, + }, + { + "annoucement", + ":BLNQ :Mt St Helen digi will be QRT this weekend", + &Message{ + IsBulletin: true, + Recipient: "BLNQ", + AnnoucementID: 'Q', + Text: "Mt St Helen digi will be QRT this weekend", + }, + }, + { + "group bulletin 4 to the WX group", + ":BLN4WX :Stand by your snowplows", + &Message{ + ID: 4, + IsBulletin: true, + Recipient: "BLN4WX", + Group: "WX", + Text: "Stand by your snowplows", + }, + }, + { + "national weather service alert", + ":NWS-WARN :092010z,THUNDER_STORM,AR_ASHLEY,{S9JbA", + &Message{ + IsBulletin: true, + Recipient: "NWS-WARN", + Severity: "WARN", + Text: "092010z,THUNDER_STORM,AR_ASHLEY,{S9JbA", + }, + }, + } + + var decoder messageDecoder + for _, test := range tests { + t.Run(test.Name, func(t *testing.T) { + frame := &Frame{Raw: test.Raw} + if !decoder.CanDecode(frame) { + t.Fatalf("%T can't decode %q", decoder, test.Raw) + } + + v, err := decoder.Decode(frame) + if err != nil { + t.Fatal(err) + } + + testCompareMessage(t, test.Want, v) + }) + } +} + +func testCompareMessage(t *testing.T, want *Message, value any) { + t.Helper() + + test, ok := value.(*Message) + if !ok { + t.Fatalf("expected data to be a %T, got %T", want, value) + return + } + + if test.ID != want.ID { + t.Errorf("expected id %d, got %d", want.ID, test.ID) + } + if test.IsAcknowledge != want.IsAcknowledge { + t.Errorf("expected is acknowledge %t, got %t", want.IsAcknowledge, test.IsAcknowledge) + } + if test.IsRejection != want.IsRejection { + t.Errorf("expected is rejection %t, got %t", want.IsRejection, test.IsRejection) + } + if test.IsBulletin != want.IsBulletin { + t.Errorf("expected is bulletin %t, got %t", want.IsBulletin, test.IsBulletin) + } + if test.Recipient != want.Recipient { + t.Errorf("expected recipient %q, got %q", want.Recipient, test.Recipient) + } + if test.AnnoucementID != want.AnnoucementID { + t.Errorf("expected annoucement id %q, got %q", want.AnnoucementID, test.AnnoucementID) + } + if test.Group != want.Group { + t.Errorf("expected group %q, got %q", want.Group, test.Group) + } + if test.Severity != want.Severity { + t.Errorf("expected severity %q, got %q", want.Severity, test.Severity) + } + if test.Text != want.Text { + t.Errorf("expected text %q, got %q", want.Text, test.Text) + } +} diff --git a/protocol/aprs/mice.go b/protocol/aprs/mice.go new file mode 100644 index 0000000..a536a6c --- /dev/null +++ b/protocol/aprs/mice.go @@ -0,0 +1,428 @@ +package aprs + +import ( + "bytes" + "errors" + "fmt" +) + +type MessageType int + +const ( + MsgEmergency MessageType = iota + MsgPriority + MsgSpecial + MsgCommitted + MsgReturning + MsgInService + MsgEnRoute + MsgOffDuty + MsgCustom0 + MsgCustom1 + MsgCustom2 + MsgCustom3 + MsgCustom4 + MsgCustom5 + MsgCustom6 +) + +type MicE struct { + // Core + Latitude float64 + Longitude float64 + Ambiguity int + Velocity *Velocity + Symbol string + Type MessageType + HasMessaging bool + + // Extensions + Altitude float64 // in meters + DAO *DAO + Telemetry *Telemetry + Comment string + + // Raw + RawDest string + RawInfo []byte +} + +func (report *MicE) String() string { + return report.Comment +} + +type DAO struct { + LatOffset float64 + LonOffset float64 +} + +type micEDecoder struct{} + +func (d micEDecoder) CanDecode(frame *Frame) bool { + if len(frame.Destination.Call) == 6 && len(frame.Raw) >= 8 { + for i := range 6 { + if _, _, _, _, ok := decodeDestChar(frame.Destination.Call[i]); !ok { + return false + } + } + return true + } + return false +} + +func (d micEDecoder) Decode(frame *Frame) (data Data, err error) { + if len(frame.Destination.Call) < 6 { + return nil, errors.New("destination too short for Mic-E") + } + if len(frame.Raw) < 9 { + return nil, errors.New("info field too short for Mic-E") + } + + var ( + r = new(MicE) + north, west bool + dest = frame.Destination.Call + info = []byte(frame.Raw[1:]) + ) + if r.Latitude, r.Type, r.Ambiguity, north, err = r.decodeLatitude(dest); err != nil { + return + } + if r.Longitude, west, r.Velocity, r.Symbol, err = r.decodeMicELongitudeAndMotion(dest, info); err != nil { + return + } + + if !north { + r.Latitude = -r.Latitude + } + if west { + r.Longitude = -r.Longitude + } + + r.parseExtensions(info[8:]) + + return r, nil +} + +func (r *MicE) decodeLatitude(dest string) ( + lat float64, + msg MessageType, + ambiguity int, + north bool, + err error, +) { + if len(dest) != 6 { + return 0, 0, 0, false, errors.New("aprs: MicE destination must be 6 chars") + } + + var digits [6]int + var msgBits int + + for i := range 6 { + c := dest[i] + + d, amb, nBit, mBit, ok := decodeDestChar(c) + if !ok { + return 0, 0, 0, false, fmt.Errorf("invalid dest char %q", c) + } + + digits[i] = d + + if amb { + ambiguity++ + } + + // Only first 3 chars contain message bits + if i < 3 && mBit { + msgBits |= 1 << (2 - i) + } + + // North bit defined by char 3 + if i == 3 { + north = nBit + } + } + + // Apply ambiguity masking per spec + maskAmbiguity(digits[:], ambiguity) + + deg := digits[0]*10 + digits[1] + min := digits[2]*10 + digits[3] + hun := digits[4]*10 + digits[5] + + if deg > 89 || min > 59 || hun > 99 { + return 0, 0, 0, false, errors.New("invalid latitude range") + } + + lat = float64(deg) + + float64(min)/60.0 + + float64(hun)/6000.0 + + if !north { + lat = -lat + } + + return lat, r.interpretMessage(msgBits), ambiguity, north, nil +} + +func decodeDestChar(c byte) ( + digit int, + ambiguity bool, + northBit bool, + msgBit bool, + ok bool, +) { + switch { + case c >= '0' && c <= '9': + return int(c - '0'), false, true, false, true + + case c >= 'A' && c <= 'J': + return int(c - 'A'), false, true, true, true + + case c >= 'P' && c <= 'Y': + return int(c - 'P'), false, false, true, true + + case c == 'K' || c == 'L' || c == 'Z': + return 0, true, true, false, true + + default: + return 0, false, false, false, false + } +} + +func maskAmbiguity(digits []int, ambiguity int) { + for i := 0; i < ambiguity && i < len(digits); i++ { + idx := len(digits) - 1 - i + digits[idx] = 0 + } +} + +func (r *MicE) decodeMicELongitudeAndMotion(dest string, info []byte) (lon float64, west bool, velocity *Velocity, symbol string, err error) { + if len(info) < 3 { + err = errors.New("info too short for longitude") + return + } + + d := int(info[0]) - 28 + m := int(info[1]) - 28 + h := int(info[2]) - 28 + if d < 0 || m < 0 || h < 0 { + err = errors.New("invalid longitude encoding") + return + } + + // 100° offset bit from dest[4] + if dest[4] >= 'P' { + d += 100 + } + + // Wrap correction + if d >= 180 { + d -= 80 + } + + if m >= 60 { + m -= 60 + d++ + } + + lon = float64(d) + + float64(m)/60.0 + + float64(h)/6000.0 + + // East/West from dest[5] + west = dest[5] >= 'P' + if west { + lon = -lon + } + + // Speed/course + if len(info) >= 6 { + s1 := int(info[3]) - 28 + s2 := int(info[4]) - 28 + s3 := int(info[5]) - 28 + + if !(s1 < 0 || s2 < 0 || s3 < 0) { + speed := s1*10 + s2/10 + course := (s2%10)*100 + s3 + + if speed >= 800 { + speed -= 800 + } + if course >= 400 { + course -= 400 + } + + if course >= 360 { + course %= 360 + } + + velocity = &Velocity{ + Speed: knotsToMetersPerSecond(float64(speed)), + Course: course, + } + } + } + + // Symbol + if len(info) >= 8 { + symbol = string([]byte{info[7], info[6]}) + } + + return +} + +func (report *MicE) interpretMessage(bits int) MessageType { + if bits == 0 { + return MsgEmergency + } + if bits <= 7 { + return MessageType(bits) + } + return MsgCustom0 +} + +func (report *MicE) parseExtensions(info []byte) { + info = report.parseOldTelemetry(info) + info = report.parseNewTelemetry(info) + info = report.parseAltitude(info) + info = report.parseDAO(info) + info = report.parseRadioPrefix(info) + + // Remainder is comment + if len(info) > 0 { + report.Comment = string(info) + } +} + +func (report *MicE) parseRadioPrefix(info []byte) []byte { + if len(info) == 0 { + return info + } + + switch info[0] { + case '>', ']': // Kenwood + return info[1:] + case '`': // Yaesu / Byonics / etc. + report.HasMessaging = true + return info[1:] + case '\'': // Yaesu / Byonics / etc. + return info[1:] + default: + return info + } +} + +func (report *MicE) parseOldTelemetry(info []byte) []byte { + if len(info) < 1 { + return info + } + + b := info[0] + if b != '`' && b != '\'' { + return info + } + + // Need 6 bytes total + if 6 > len(info) { + return info // not fatal, ignore malformed + } + + data := info[1:6] + values := make([]int, 5) + for i := 0; i < 5; i++ { + values[i] = int(data[i] - 33) + } + + report.Telemetry = &Telemetry{ + Analog: values, + } + + return info[6:] +} + +func (report *MicE) parseNewTelemetry(info []byte) []byte { + i := bytes.IndexByte(info, '|') + if i == -1 { + return info + } + + prefix, data := info[:i], info[i+1:] + if i = bytes.IndexByte(data, '|'); i == -1 { + return info + } + suffix, data := data[i+1:], data[:i] + if len(data) < 2 || len(data)%2 != 0 { + return info + } + + report.Telemetry = new(Telemetry) + var err error + if report.Telemetry.ID, err = base91Decode(string(data[:2])); err != nil { + return info + } + data = data[2:] + + for range 5 { + if len(data) == 0 { + break + } + var value int + if value, err = base91Decode(string(data[:2])); err != nil { + return info + } + report.Telemetry.Analog = append(report.Telemetry.Analog, value) + data = data[2:] + } + + if len(data) > 0 { + var digital int + if digital, err = base91Decode(string(data[:2])); err != nil { + return info + } + for range 8 { + report.Telemetry.Digital = append(report.Telemetry.Digital, (digital&1) == 1) + digital >>= 1 + } + } + + return append(prefix, suffix...) +} + +func (report *MicE) parseAltitude(info []byte) []byte { + if 4 > len(info) { + return info + } + + if info[3] != '}' { + return info + } + + alt, err := base91Decode(string(info[:3])) + if err != nil { + return info + } + value := alt - 10000 + report.Altitude = feetToMeters(float64(value)) + + return info[4:] +} + +func (report *MicE) parseDAO(info []byte) []byte { + if 6 > len(info) { + return info + } + + if info[0] != '!' || info[1] != 'W' { + return info + } + + latOff := float64(info[2]-33) / 10000.0 + lonOff := float64(info[3]-33) / 10000.0 + + report.DAO = &DAO{ + LatOffset: latOff, + LonOffset: lonOff, + } + + return info[6:] +} diff --git a/protocol/aprs/mice_test.go b/protocol/aprs/mice_test.go new file mode 100644 index 0000000..610a169 --- /dev/null +++ b/protocol/aprs/mice_test.go @@ -0,0 +1,130 @@ +package aprs + +import ( + "reflect" + "testing" +) + +func TestPositionMicE(t *testing.T) { + tests := []struct { + Name string + Frame string + Want *MicE + }{ + { + "position", + "N0CALL>S32U6T:`(_f \"Oj/", + &MicE{ + Latitude: 33.427333, + Longitude: 13.129000, + Symbol: "/j", + Velocity: &Velocity{Course: 251, Speed: 20.57777776}, + }, + }, + { + "position with altitude", + "N0CALL>S32U6T:`(_f \"Oj/\"4T}", + &MicE{ + Latitude: 33.427333, + Longitude: 13.129000, + Altitude: feetToMeters(61), + Symbol: "/j", + Velocity: &Velocity{Course: 251, Speed: 20.57777776}, + }, + }, + { + "position with telemetry", + "N0CALL>S32U6T:`(_f \"Oj/>|\\'s%0\\'c|", + &MicE{ + Latitude: 33.427333, + Longitude: 13.129000, + Symbol: "/j", + Velocity: &Velocity{Course: 251, Speed: 20.57777776}, + Telemetry: &Telemetry{ID: 5375, Analog: []int{7466, 1424, 612}}, + }, + }, + /* + { + "gridsquare position with comment", + "NOCALL>S32U6T:IO91SX/G Hello world", + &Position{ + Latitude: 33.427333, + Longitude: -12.129, + Symbol: "/G", + Comment: "Hello world", + }, + }, + */ + } + + var decoder micEDecoder + for _, test := range tests { + t.Run(test.Name, func(t *testing.T) { + frame, err := Parse(test.Frame) + if err != nil { + t.Fatalf("can't parse %q: %v", test.Frame, err) + } + if !decoder.CanDecode(frame) { + t.Fatalf("%T can't decode %q", decoder, test.Frame) + } + + v, err := decoder.Decode(frame) + if err != nil { + t.Fatalf("can't decode %q: %v", test.Frame, err) + } + + testCompareMicE(t, test.Want, v) + }) + } +} + +func testCompareMicE(t *testing.T, want *MicE, value any) { + t.Helper() + + p, ok := value.(*MicE) + if !ok { + t.Fatalf("expected data to be a %T, got %T", want, value) + return + } + + if p.HasMessaging != want.HasMessaging { + t.Errorf("expected to have messaging: %t, got %t", want.HasMessaging, p.HasMessaging) + } + + if !testAlmostEqual(p.Latitude, want.Latitude) { + t.Errorf("expected latitude %f, got %f", want.Latitude, p.Latitude) + } + if !testAlmostEqual(p.Longitude, want.Longitude) { + t.Errorf("expected longitude %f, got %f", want.Longitude, p.Longitude) + } + if !testAlmostEqual(p.Altitude, want.Altitude) { + t.Errorf("expected altitude %f, got %f", want.Altitude, p.Altitude) + } + + if p.Symbol != want.Symbol { + t.Errorf("expected symbol %q, got %q", want.Symbol, p.Symbol) + } + if p.Comment != want.Comment { + t.Errorf("expected comment %q, got %q", want.Comment, p.Comment) + } + + if want.Velocity != nil { + if p.Velocity == nil { + t.Errorf("expected velocity, got none") + } else if !reflect.DeepEqual(p.Velocity, want.Velocity) { + t.Errorf("expected velocity %#+v, got %#+v", want.Velocity, p.Velocity) + } + } else if p.Velocity != nil { + t.Errorf("expected no velocity, got %#+v", p.Velocity) + } + + if want.Telemetry != nil { + if p.Telemetry == nil { + t.Errorf("expected telemetry, got none") + } else if !reflect.DeepEqual(p.Telemetry, want.Telemetry) { + t.Errorf("expected telemetry %#+v, got %#+v", want.Telemetry, p.Telemetry) + } + } else if p.Telemetry != nil { + t.Errorf("expected no telemetry, got %#+v", p.Telemetry) + } +} diff --git a/protocol/aprs/packet.go b/protocol/aprs/packet.go deleted file mode 100644 index 8847823..0000000 --- a/protocol/aprs/packet.go +++ /dev/null @@ -1,481 +0,0 @@ -package aprs - -import ( - "errors" - "fmt" - "math" - "strconv" - "strings" - "time" -) - -var ( - // ErrInvalidPacket signals a corrupted/unknown APRS packet. - ErrInvalidPacket = errors.New("aprs: invalid packet") - - // ErrInvalidPosition signals a corrupted APRS position report. - ErrInvalidPosition = errors.New("aprs: invalid position") -) - -// Payload is the raw payload contained within an APRS packet. -type Payload string - -// Type of payload. -func (p Payload) Type() DataType { - var t DataType - - if len(p) > 0 { - t = DataType(p[0]) - } - - return t -} - -func isDigit(b byte) bool { - return b >= '0' && b <= '9' -} - -func (p Payload) Len() int { return len(p) } - -// Velocity details. -type Velocity struct { - Course float64 // Degrees - Speed float64 // Knots -} - -// Wind details. -type Wind struct { - Direction float64 // Degrees - Speed float64 // Knots -} - -// PowerHeightGain details. -type PowerHeightGain struct { - PowerCode byte - HeightCode byte - GainCode byte - DirectivityCode byte -} - -// Power level (in Watts). -func (p PowerHeightGain) Power() int { - w := int(p.PowerCode - '0') - if w <= 0 { - return 0 - } - return w * w -} - -// Height above ground (in meters). -func (p PowerHeightGain) Height() float64 { - h := float64(p.HeightCode - '0') - if h <= 0 { - return 10 - } - return math.Pow(2, h) * 10 -} - -// Gain level (in dBs). -func (p PowerHeightGain) Gain() int { - d := int(p.GainCode - '0') - if d <= 0 { - return 0 - } - return d -} - -// Directivity angle. -func (p PowerHeightGain) Directivity() float64 { - d := int(p.DirectivityCode - '0') - if d <= 0 { - return 0 - } - return float64(d%8) * 45.0 -} - -// OmniDFStrength contains the omni-directional direction finding signal strength (for fox hunting). -type OmniDFStrength struct { - StrengthCode byte - HeightCode byte - GainCode byte - DirectivityCode byte -} - -// Strength of the signal. -func (o OmniDFStrength) Strength() int { - w := int(o.StrengthCode - '0') - if w <= 0 { - return 0 - } - return w * w -} - -// Height above ground (in meters). -func (o OmniDFStrength) Height() float64 { - h := float64(o.HeightCode - '0') - if h <= 0 { - return 10 - } - return math.Pow(2, h) * 10 -} - -// Gain level (in dBs). -func (o OmniDFStrength) Gain() int { - d := int(o.GainCode - '0') - if d <= 0 { - return 0 - } - return d -} - -// Directivity angle. -func (o OmniDFStrength) Directivity() float64 { - d := int(o.DirectivityCode - '0') - if d <= 0 { - return 0 - } - return float64(d%8) * 45.0 -} - -// Packet contains an APRS packet. -type Packet struct { - // Raw packet (as captured from the air or APRS-IS). - Raw string `json:"-"` - - // Src is the source address. - Src Address `json:"src"` - - // Dst is the destination address. - Dst Address `json:"dst"` - - // Path contains the digipeater path. - Path Path `json:"path,omitempty"` - - // Payload is the raw payload. - Payload Payload `json:"payload"` - - // Position encoded in the payload. - Position *Position `json:"position,omitempty"` - - // Time encoded in the payload. - Time *time.Time `json:"time,omitempty"` - - // Altitude encoded in the payload (in feet). - Altitude float64 `json:"altitude,omitempty"` - - // Velocity encoded in the payload. - Velocity *Velocity `json:"velocity,omitempty"` - - // Wind details encoded in the payload. - Wind *Wind `json:"wind,omitempty"` - - // PHG are the power, height and gain details encoded in the payload. - PHG *PowerHeightGain `json:"phg,omitempty"` - - // DFS are the direction finder strength details encoded in the payload. - DFS *OmniDFStrength `json:"dfs,omitempty"` - - // Range encoded in the payload (in miles). - Range float64 `json:"range,omitempty"` - - // Symbol encoded in the payload. - Symbol Symbol `json:"symbol"` - - // Comment encoded in the payload. - Comment string `json:"comment,omitempty"` - - // Unparsed data. - data string -} - -// ParsePacket parses an APRS packet as captured from AX.25 or APRS-IS. -func ParsePacket(raw string) (Packet, error) { - p := Packet{Raw: raw} - - var i int - if i = strings.Index(raw, ":"); i < 0 { - return p, ErrInvalidPacket - } - p.Payload = Payload(raw[i+1:]) - - // Parse src, dst and path - var err error - var a = raw[:i] - if i = strings.Index(a, ">"); i < 0 { - return p, ErrInvalidPacket - } - if p.Src, err = ParseAddress(a[:i]); err != nil { - return p, err - } - var r = strings.Split(a[i+1:], ",") - if p.Dst, err = ParseAddress(r[0]); err != nil { - return p, err - } - if p.Path, err = ParsePath(strings.Join(r[1:], ",")); err != nil { - return p, err - } - - // Post processing of payload - err = p.parse() - return p, err -} - -func (p *Packet) parse() error { - s := string(p.Payload) - //log.Printf("parse %q [%c]\n", s, p.Payload.Type()) - - switch p.Payload.Type() { - case '!': // Lat/Long Position Report Format — without Timestamp - var o = strings.IndexByte(s, '!') - pos, txt, err := ParsePosition(s[o+1:], !isDigit(s[o+1])) - if err != nil { - return err - } - p.Position = &pos - p.data = txt - if len(s) >= 20 { - p.Symbol[0] = s[9] - p.Symbol[1] = s[19] - } - case '=': - compressed := IsValidCompressedSymTable(s[1]) - pos, txt, err := ParsePosition(s[1:], compressed) - if err != nil { - return err - } - p.Position = &pos - p.data = txt - if compressed { - p.Symbol[0] = s[1] - p.Symbol[1] = s[10] - } else { - p.Symbol[0] = s[9] - p.Symbol[1] = s[19] - } - case '/', '@': // Lat/Long Position Report Format — with Timestamp - if len(s) < 8 { - return ErrInvalidPosition - } - - var compressed bool - if s[7] == 'h' || s[7] == 'z' || s[7] == '/' { - if ts, err := ParseTime(s[1:]); err == nil { - p.Time = &ts - } - compressed = IsValidCompressedSymTable(s[8]) - pos, txt, err := ParsePosition(s[8:], compressed) - if err != nil { - return err - } - p.Position = &pos - p.data = txt - } else if s[7] >= '0' && s[7] <= '9' { - ts, err := ParseTime(s[1:]) - if err != nil { - return err - } - p.Time = &ts - compressed = IsValidCompressedSymTable(s[10]) - pos, txt, err := ParsePosition(s[10:], compressed) - if err != nil { - return err - } - p.Position = &pos - p.data = txt - } - if compressed { - p.Symbol[0] = s[8] - p.Symbol[1] = s[17] - } else { - p.Symbol[0] = s[16] - p.Symbol[1] = s[26] - } - case ';': - pos, txt, err := ParsePosition(s[18:], !isDigit(s[18])) - if err != nil { - return err - } - p.Position = &pos - p.data = txt - case '[': - pos, txt, err := ParsePositionGrid(s[1:]) - if err != nil { - return err - } - p.Position = &pos - p.data = txt - case '`', '\'': - pos, err := ParseMicE(s, p.Dst.Call) - if err != nil { - return err - } - p.Position = &pos - _ = p.parseMicEData() - - return nil // there is no additional data to parse - default: - pos, txt, err := ParsePositionBoth(s) - if err != nil { - if err != ErrInvalidPosition { - return err - } - p.Comment = s[1:] - } else { - p.Position = &pos - p.Comment = txt - } - } - - if p.Position != nil { - if p.Position.Compressed { - return p.parseCompressedData() - } - return p.parseData() - } - - return nil -} - -func (p *Packet) parseMicEData() error { - // APRS PROTOCOL REFERENCE 1.0.1 Chapter 10, page 42 in PDF - - s := string(p.Payload) - - // Mic-E Message Type - var mt []string - var t string - for i := 0; i < 3; i++ { - mc := miceCodes[rune(p.Dst.Call[i])][1] - if strings.HasSuffix(mc, "(Custom)") { - t = messageTypeCustom - } else if strings.HasSuffix(mc, "(Std)") { - t = messageTypeStd - } - mt = append(mt, string(mc[0])) - } - switch t { - case messageTypeStd: - mt = append(mt, " (Std)") - case messageTypeCustom: - mt = append(mt, " (Custom)") - } - p.Comment = miceMsgTypes[strings.Join(mt, "")] - - // Speed and Course. - speed := float64(int(s[4])-28) * 10 - dc := float64(int(s[5])-28) / 10 - unit := float64(int(dc)) - speed += unit - course := dc - unit - course += float64(int(s[6]) - 28) - if speed >= 800 { - speed -= 800 - } - speed = 1.852 * speed // convert speed from knots to km/h - if course >= 400 { - course -= 400 - } - - // Symbol - p.Symbol[0] = s[7] - p.Symbol[1] = s[8] - - p.Comment += fmt.Sprintf(" (%.fkm/h, %.f°)", speed, course) - - // Check whether there's additional Telemetry or Status Text data. - if len(s) == 9 { - return nil - } - - if s[9] == ',' || s[9] == '\x1d' { - // TODO: Parse telemetry data. - return nil - } - - // Parse MicE Status Text data. - // TODO: Parse additional data in the Status Text data: - // - Actual (custom) text message - // - Maidenhead locator - // - Altitude - - return nil -} - -func (p *Packet) parseCompressedData() error { - // Parse csT bytes - if len(p.data) >= 3 { - // Compression Type (T) Byte Format - // Bit: 7 | 6 | 5 | 4 3 | 2 1 0 | - // -------+--------+---------+-------------+------------------+ - // Unused | Unused | GPS Fix | NMEA Source | Origin | - // -------+--------+---------+-------------+------------------+ - // Val: 0 | 0 | 0 = old | 00 = other | 000 = Compressed | - // | | 1 = cur | 01 = GLL | 001 = TNC BTex | - // | | | 10 = CGA | 010 = Software | - // | | | 11 = RMC | 011 = [tbd] | - // | | | | 100 = KPC3 | - // | | | | 101 = Pico | - // | | | | 110 = Other | - // | | | | 111 = Digipeater | - cb := p.data[0] - 33 - sb := p.data[1] - 33 - Tb := p.data[2] - 33 - if p.data[0] != ' ' && ((Tb>>3)&3) == 2 { - // CGA sentence, NMEA Source = 0b10 - d, err := base91Decode(p.data[0:2]) - if err != nil { - return err - } - p.Altitude = math.Pow(1.002, float64(d)) - p.Comment = p.data[3:] - } else if cb <= 89 { // !..z - // Course/Speed - p.Velocity.Course = float64(cb) * 4.0 - p.Velocity.Speed = math.Pow(1.08, float64(sb)) - 1.0 - } else if cb == 90 { // { - // Pre-Calculated Radio Range - p.Range = 2 * math.Pow(1.08, float64(sb)) - } - } - return nil -} - -func (p *Packet) parseData() error { - switch { - case len(p.data) >= 1 && p.data[0] == ' ': - p.Comment = p.data[1:] - - case len(p.data) >= 7 && strings.HasPrefix(p.data, "PHG"): - p.PHG.PowerCode = p.data[3] - p.PHG.HeightCode = p.data[4] - p.PHG.GainCode = p.data[5] - p.PHG.DirectivityCode = p.data[6] - p.Range = math.Sqrt(2 * p.PHG.Height() * math.Sqrt((float64(p.PHG.Power())/10)*(float64(p.PHG.Gain())/2))) - p.Comment = p.data[7:] - - case len(p.data) >= 7 && strings.HasPrefix(p.data, "RNG"): - var err error - p.Range, err = strconv.ParseFloat(p.data[3:7], 64) - if err != nil { - return err - } - p.Comment = p.data[7:] - - case len(p.data) >= 7 && strings.HasPrefix(p.data, "DFS"): - p.DFS.StrengthCode = p.data[3] - p.DFS.HeightCode = p.data[4] - p.DFS.GainCode = p.data[5] - p.DFS.DirectivityCode = p.data[6] - p.Comment = p.data[7:] - } - return nil -} - -func (p Payload) Time() (time.Time, error) { - switch p.Type() { - case '/', '@': - return ParseTime(string(p)[1:]) - default: - return time.Time{}, nil - } -} diff --git a/protocol/aprs/packet_test.go b/protocol/aprs/packet_test.go deleted file mode 100644 index 9b0403a..0000000 --- a/protocol/aprs/packet_test.go +++ /dev/null @@ -1,329 +0,0 @@ -package aprs - -import ( - "math" - "testing" - "time" -) - -const earthRadius = float64(6378100) - -func testTime(day, hour, min, sec int) *time.Time { - t := time.Date(0, 0, day, hour, min, sec, 0, time.UTC) - return &t -} - -// haversin(θ) function -func testHaversin(theta float64) float64 { - return math.Pow(math.Sin(theta/2), 2) -} - -func testDistance(a *Position, b *Position) float64 { - var ( - lat1 = a.Latitude * math.Pi / 180 - lng1 = a.Longitude * math.Pi / 180 - lat2 = b.Latitude * math.Pi / 180 - lng2 = b.Longitude * math.Pi / 180 - h = testHaversin(lat2-lat1) + math.Cos(lat1)*math.Cos(lat2)*testHaversin(lng2-lng1) - ) - return 2 * earthRadius * math.Asin(math.Sqrt(h)) -} - -func TestPacket(t *testing.T) { - var tests = []struct { - Raw string - Src Address - Dst Address - PathLen int - Type DataType - Position *Position - Velocity *Velocity - PHG *PowerHeightGain - DFS *OmniDFStrength - Altitude float64 - Range float64 - Time *time.Time - }{ - { - Raw: "N0CALL>APRS,qAC:!4903.50N/07201.75W-Test 001234", - Src: MustParseAddress("N0CALL"), - Dst: MustParseAddress("APRS"), - PathLen: 1, - Type: DataType('!'), - Position: &Position{Latitude: 49.058333, Longitude: -72.029167}, - }, - { - Raw: "N0CALL>APRS,qAC:!4903.50N/07201.75W-Test /A=001234", - Src: MustParseAddress("N0CALL"), - Dst: MustParseAddress("APRS"), - PathLen: 1, - Type: DataType('!'), - Position: &Position{Latitude: 49.058333, Longitude: -72.029167}, - }, - { - Raw: "N0CALL>APRS,qAC:!49 . N/072 . W-", - Src: MustParseAddress("N0CALL"), - Dst: MustParseAddress("APRS"), - PathLen: 1, - Type: DataType('!'), - Position: &Position{Latitude: 49.0, Longitude: -72.000000}, - }, - /* - { - Raw: "N0CALL>APRS,qAC:TheNet X-1J4 (BFLD)!4903.50N/07201.75Wn", - Src: MustParseAddress("N0CALL"), - Dst: MustParseAddress("APRS"), - PathLen: 1, - Type: DataType('!'), - Position: &Position{Latitude: 49.058333, Longitude: -72.029167}, - }, - */ - { - Raw: "N0CALL>APRS,qAC:@092345/4903.50N/07201.75W>Test1234", - Src: MustParseAddress("N0CALL"), - Dst: MustParseAddress("APRS"), - PathLen: 1, - Type: DataType('@'), - Position: &Position{Latitude: 49.058333, Longitude: -72.029167}, - Time: testTime(9, 23, 45, 0), - }, - { - Raw: "N0CALL>APRS,qAC:=4903.50N/07201.75W#PHG5132", - Src: MustParseAddress("N0CALL"), - Dst: MustParseAddress("APRS"), - PathLen: 1, - Type: DataType('='), - Position: &Position{Latitude: 49.058333, Longitude: -72.029167}, - PHG: &PowerHeightGain{'5', '1', '3', '2'}, - }, - { - Raw: "N0CALL>APRS,qAC:=4903.50N/07201.75W 225/000g000t050r000p001...h00b10138dU2k", - Src: MustParseAddress("N0CALL"), - Dst: MustParseAddress("APRS"), - PathLen: 1, - Type: DataType('='), - Position: &Position{Latitude: 49.058333, Longitude: -72.029167}, - }, - { - Raw: "N0CALL>APRS,qAC:@092345/4903.50N/07201.75W>088/036", - Src: MustParseAddress("N0CALL"), - Dst: MustParseAddress("APRS"), - PathLen: 1, - Type: DataType('@'), - Position: &Position{Latitude: 49.058333, Longitude: -72.029167}, - Time: testTime(9, 23, 45, 0), - }, - { - Raw: "N0CALL>APRS,qAC:@234517h4903.50N/07201.75W>PHG5132", - Src: MustParseAddress("N0CALL"), - Dst: MustParseAddress("APRS"), - PathLen: 1, - Type: DataType('@'), - Position: &Position{Latitude: 49.058333, Longitude: -72.029167}, - Time: testTime(0, 23, 45, 17), - PHG: &PowerHeightGain{'5', '1', '3', '2'}, - }, - { - Raw: "N0CALL>APRS,qAC:@092345z4903.50N/07201.75W>RNG0050", - Src: MustParseAddress("N0CALL"), - Dst: MustParseAddress("APRS"), - PathLen: 1, - Type: DataType('@'), - Position: &Position{Latitude: 49.058333, Longitude: -72.029167}, - Time: testTime(9, 23, 45, 0), - Range: 50, - }, - { - Raw: "N0CALL>APRS,qAC:/234517h4903.50N/07201.75W>DFS2360", - Src: MustParseAddress("N0CALL"), - Dst: MustParseAddress("APRS"), - PathLen: 1, - Type: DataType('/'), - Position: &Position{Latitude: 49.058333, Longitude: -72.029167}, - Time: testTime(0, 23, 45, 17), - DFS: &OmniDFStrength{'2', '3', '6', '0'}, - }, - { - Raw: "N0CALL>APRS,qAC:@092345z4903.50N/07201.75W 090/000g000t066r000p000...dUII", - Src: MustParseAddress("N0CALL"), - Dst: MustParseAddress("APRS"), - PathLen: 1, - Type: DataType('@'), - Position: &Position{Latitude: 49.058333, Longitude: -72.029167}, - Time: testTime(9, 23, 45, 00), - }, - { - Raw: "N0CALL>APRS,qAC:[IO91SX] 35 miles NNW of London", - Src: MustParseAddress("N0CALL"), - Dst: MustParseAddress("APRS"), - PathLen: 1, - Type: DataType('['), - Position: &Position{Latitude: 51.958333, Longitude: -0.500000}, - }, - { - Raw: "N0CALL>APRS,qAC:[IO91]", - Src: MustParseAddress("N0CALL"), - Dst: MustParseAddress("APRS"), - PathLen: 1, - Type: DataType('['), - Position: &Position{Latitude: 51.0, Longitude: -2.0}, - }, - { - Raw: "WX4GSO-9>APN382,qAR,WD4LSS:!3545.18NL07957.08W#PHG5680/R,W,85NC,NCn Mount Shepherd Piedmont Triad NC", - Src: MustParseAddress("WX4GSO-9"), - Dst: MustParseAddress("APN382"), - PathLen: 2, - Type: DataType('!'), - Position: &Position{Latitude: 35.753000, Longitude: -79.951333}, - }, - { - Raw: "PA4TW>APRS,qAS,PA4TW-2:=5216.28N/00510.05Er Remco, DMR:2041014, Soms QRV op PI2NOS", - Src: MustParseAddress("PA4TW"), - Dst: MustParseAddress("APRS"), - PathLen: 2, - Type: DataType('='), - Position: &Position{Latitude: 52.271333, Longitude: 5.167500}, - }, - { - Raw: "PA4TW-10>APRS,TCPIP*,qAC,FOURTH:=5220.18N/00453.25EIhttp://aprs.pa4tw.nl:14501/", - Src: MustParseAddress("PA4TW-10"), - Dst: MustParseAddress("APRS"), - PathLen: 3, - Type: DataType('='), - Position: &Position{Latitude: 52.336333, Longitude: 4.887500}, - }, - { - Raw: "N0CALL-1>T3PY1Y,KQ1L-8*,WIDE1,WIDE2-1,qAR:=/5L!!<*e7> sTComment", - Src: MustParseAddress("N0CALL-1"), - Dst: MustParseAddress("T3PY1Y"), - PathLen: 4, - Type: DataType('='), - Position: &Position{Latitude: 49.5, Longitude: -72.75}, - }, - { - Raw: "N0CALL-1>T3PY1Y,KQ1L-8*,WIDE1,WIDE2-1,qAR:=/5L!!<*e7>7P[", - Src: MustParseAddress("N0CALL-1"), - Dst: MustParseAddress("T3PY1Y"), - PathLen: 4, - Type: DataType('='), - Position: &Position{Latitude: 49.5, Longitude: -72.75}, - Velocity: &Velocity{88.0, 36.2}, - }, - { - Raw: "N0CALL-1>T3PY1Y,KQ1L-8*,WIDE1,WIDE2-1,qAR:=/5L!!<*e7>{?!", - Src: MustParseAddress("N0CALL-1"), - Dst: MustParseAddress("T3PY1Y"), - PathLen: 4, - Type: DataType('='), - Position: &Position{Latitude: 49.5, Longitude: -72.75}, - Range: 20.13, - }, - { - Raw: "N0CALL-1>T3PY1Y,KQ1L-8*,WIDE1,WIDE2-1,qAR:=/5L!!<*e7OS]S", - Src: MustParseAddress("N0CALL-1"), - Dst: MustParseAddress("T3PY1Y"), - PathLen: 4, - Type: DataType('='), - Position: &Position{Latitude: 49.5, Longitude: -72.75}, - Altitude: 10004, - }, - { - Raw: "N0CALL-1>T3PY1Y,KQ1L-8*,WIDE1,WIDE2-1,qAR:@092345z/5L!!<*e7>{?!", - Src: MustParseAddress("N0CALL-1"), - Dst: MustParseAddress("T3PY1Y"), - PathLen: 4, - Type: '@', - Time: testTime(9, 23, 45, 0), - Position: &Position{Latitude: 49.5, Longitude: -72.75}, - Range: 20.13, - }, - { - Raw: "PE1ROG-8>APLC13,qAS,PE1ROG-2:!/49[8pQLoRa___MOBIL___TEST", - Src: MustParseAddress("PE1ROG-8"), - Dst: MustParseAddress("APLC13"), - PathLen: 2, - Type: '!', - }, - { - Raw: "PE1ROG-1>APPM13,TCPIP*,qAC,T2PRT:>Running on Raspberry Pi with RTL dongle", - Src: MustParseAddress("PE1ROG-1"), - Dst: MustParseAddress("APPM13"), - PathLen: 3, - Type: '>', - }, - { - Raw: "PD0MZ-4>APE32L,WIDE1-1,qAR,PD0MZ-10:>LoRa APRS Tracker", - Src: MustParseAddress("PD0MZ-4"), - Dst: MustParseAddress("APE32L"), - PathLen: 3, - Type: '>', - }, - } - - for _, test := range tests { - t.Run(test.Raw, func(t *testing.T) { - p, err := ParsePacket(test.Raw) - if err != nil { - t.Fatal(err) - } - if !p.Dst.EqualTo(test.Dst) { - t.Fatalf("expected dst %s, got %s", test.Dst, p.Dst) - } - if !p.Src.EqualTo(test.Src) { - t.Fatalf("expected src %s, got %s", test.Src, p.Src) - } - if len(p.Path) != test.PathLen { - t.Fatalf("expected path length %d, got %d: %v", test.PathLen, len(p.Path), p.Path) - } - if p.Payload.Type() != test.Type { - t.Fatalf("expected packet type %s [%c], got %s [%c]", - test.Type, test.Type, - p.Payload.Type(), p.Payload.Type()) - } - if test.Altitude != 0 { - if p.Altitude == 0 { - t.Fatalf("expected altitude %f, got none", test.Altitude) - } - if math.Abs(test.Altitude-p.Altitude) > 1.0 { - t.Fatalf("expected altitude %f, got %f", test.Altitude, p.Altitude) - } - } - if test.Velocity != nil { - if p.Velocity.Course == 0 { - t.Fatalf("expected velocity %v, got none", test.Velocity) - } - if math.Abs(test.Velocity.Course-p.Velocity.Course) > 1.0 { - t.Fatalf("expected course %f, got %f", test.Velocity.Course, p.Velocity.Course) - } - if math.Abs(test.Velocity.Speed-p.Velocity.Speed) > 1.0 { - t.Fatalf("expected speed %f, got %f", test.Velocity.Speed, p.Velocity.Speed) - } - } - if test.Range != 0 { - if p.Range == 0 { - t.Fatalf("expected range %f, got none", test.Range) - } - if math.Abs(test.Range-p.Range) > 0.1 { - t.Fatalf("expected range %f, got %f", test.Range, p.Range) - } - } - if test.Position != nil { - if p.Position == nil { - t.Fatalf("expected position %s, got none", test.Position) - } - if d := testDistance(test.Position, p.Position); d > 1.0 { - t.Fatalf("expected position %s, got %s with distance %f meter", test.Position, p.Position, d) - } - } - if test.Time != nil { - if p.Time == nil { - t.Fatalf("expected time %s", test.Time) - } - if test.Time.Sub(*p.Time) > time.Minute { - t.Fatalf("expected time %s, got %s", test.Time, p.Time) - } - } - //t.Logf("%#+v", p) - }) - } -} diff --git a/protocol/aprs/position.go b/protocol/aprs/position.go index ec0905c..94bf768 100644 --- a/protocol/aprs/position.go +++ b/protocol/aprs/position.go @@ -2,312 +2,498 @@ package aprs import ( "fmt" + "io" + "math" + "regexp" "strconv" "strings" - - "git.maze.io/go/ham/util/maidenhead" + "time" ) -var ( - // Position ambiguity replacement - disambiguation = []int{2, 3, 5, 6, 12, 13, 15, 16} - - miceCodes = map[rune]map[int]string{ - '0': {0: "0", 1: "0", 2: "S", 3: "0", 4: "E"}, - '1': {0: "1", 1: "0", 2: "S", 3: "0", 4: "E"}, - '2': {0: "2", 1: "0", 2: "S", 3: "0", 4: "E"}, - '3': {0: "3", 1: "0", 2: "S", 3: "0", 4: "E"}, - '4': {0: "4", 1: "0", 2: "S", 3: "0", 4: "E"}, - '5': {0: "5", 1: "0", 2: "S", 3: "0", 4: "E"}, - '6': {0: "6", 1: "0", 2: "S", 3: "0", 4: "E"}, - '7': {0: "7", 1: "0", 2: "S", 3: "0", 4: "E"}, - '8': {0: "8", 1: "0", 2: "S", 3: "0", 4: "E"}, - '9': {0: "9", 1: "0", 2: "S", 3: "0", 4: "E"}, - 'A': {0: "0", 1: "1 (Custom)"}, - 'B': {0: "1", 1: "1 (Custom)"}, - 'C': {0: "2", 1: "1 (Custom)"}, - 'D': {0: "3", 1: "1 (Custom)"}, - 'E': {0: "4", 1: "1 (Custom)"}, - 'F': {0: "5", 1: "1 (Custom)"}, - 'G': {0: "6", 1: "1 (Custom)"}, - 'H': {0: "7", 1: "1 (Custom)"}, - 'I': {0: "8", 1: "1 (Custom)"}, - 'J': {0: "9", 1: "1 (Custom)"}, - 'K': {0: " ", 1: "1 (Custom)"}, - 'L': {0: " ", 1: "0", 2: "S", 3: "0", 4: "E"}, - 'P': {0: "0", 1: "1 (Std)", 2: "N", 3: "100", 4: "W"}, - 'Q': {0: "1", 1: "1 (Std)", 2: "N", 3: "100", 4: "W"}, - 'R': {0: "2", 1: "1 (Std)", 2: "N", 3: "100", 4: "W"}, - 'S': {0: "3", 1: "1 (Std)", 2: "N", 3: "100", 4: "W"}, - 'T': {0: "4", 1: "1 (Std)", 2: "N", 3: "100", 4: "W"}, - 'U': {0: "5", 1: "1 (Std)", 2: "N", 3: "100", 4: "W"}, - 'V': {0: "6", 1: "1 (Std)", 2: "N", 3: "100", 4: "W"}, - 'W': {0: "7", 1: "1 (Std)", 2: "N", 3: "100", 4: "W"}, - 'X': {0: "8", 1: "1 (Std)", 2: "N", 3: "100", 4: "W"}, - 'Y': {0: "9", 1: "1 (Std)", 2: "N", 3: "100", 4: "W"}, - 'Z': {0: " ", 1: "1 (Std)", 2: "N", 3: "100", 4: "W"}, - } - - miceMsgTypes = map[string]string{ - "000": "Emergency", - "001 (Std)": "Priority", - "001 (Custom)": "Custom-6", - "010 (Std)": "Special", - "010 (Custom)": "Custom-5", - "011 (Std)": "Committed", - "011 (Custom)": "Custom-4", - "100 (Std)": "Returning", - "100 (Custom)": "Custom-3", - "101 (Std)": "In Service", - "101 (Custom)": "Custom-2", - "110 (Std)": "En Route", - "110 (Custom)": "Custom-1", - "111 (Std)": "Off Duty", - "111 (Custom)": "Custom-0", - } -) - -const ( - gridChars = "ABCDEFGHIJKLMNOPQRSTUVWX0123456789" - - messageTypeStd = "Std" - messageTypeCustom = "Custom" -) - -// Position contains GPS coordinates. type Position struct { - Latitude float64 `json:"latitude"` // Degrees - Longitude float64 `json:"longitude"` // Degrees - Ambiguity int `json:"ambiguity,omitempty"` - Symbol Symbol `json:"symbol"` - Compressed bool `json:"compressed,omitempty"` + HasMessaging bool `json:"has_messaging"` + Latitude float64 `json:"latitude"` + Longitude float64 `json:"longitude"` + Altitude float64 `json:"altitude,omitempty"` // Altitude (in meters) + Range float64 `json:"range,omitempty"` // Radio range (in meters) + IsCompressed bool `json:"is_compressed"` + CompressedGPSFix uint8 `json:"-"` + CompressedNMEASource uint8 `json:"-"` + CompressedOrigin uint8 `json:"-"` + Time time.Time `json:"time"` + Comment string `json:"comment"` + Symbol string `json:"symbol"` + Velocity *Velocity `json:"velocity,omitempty"` // Velocity encoded in the payload. + Wind *Wind `json:"wind,omitempty"` // Wind direction and speed. + PHG *PowerHeightGain `json:"phg,omitempty"` + DFS *OmniDFStrength `json:"dfs,omitempty"` + Weather *Weather `json:"weather,omitempty"` + Telemetry *Telemetry `json:"telemetry,omitempty"` // Telemetry data } -func (pos Position) String() string { - if pos.Ambiguity == 0 { - return fmt.Sprintf("{%f, %f}", pos.Latitude, pos.Longitude) - } - return fmt.Sprintf("{%f, %f}, ambiguity=%d", pos.Latitude, pos.Longitude, pos.Ambiguity) +func (pos *Position) String() string { + return "position" } -func ParseUncompressedPosition(s string) (Position, string, error) { - // APRS PROTOCOL REFERENCE 1.0.1 Chapter 8, page 32 (42 in PDF) +// Velocity details. +type Velocity struct { + Course int // degrees + Speed float64 // meters per second +} - pos := Position{} +// Wind details. +type Wind struct { + Direction float64 // degrees + Speed float64 // meters per second +} - if len(s) < 18 { - return pos, "", ErrInvalidPosition +// PowerHeightGain details. +type PowerHeightGain struct { + PowerCode byte + HeightCode byte + GainCode byte + DirectivityCode byte +} + +// OmniDFStrength contains the omni-directional direction finding signal strength (for fox hunting). +type OmniDFStrength struct { + StrengthCode byte + HeightCode byte + GainCode byte + DirectivityCode byte +} + +type positionDecoder struct{} + +func (d positionDecoder) CanDecode(frame *Frame) bool { + switch frame.Raw.Type() { + case '!', '=', '/', '@': // compressed/uncompressed with/without messaging + return true + default: + return false } +} - b := []byte(s) - for _, p := range disambiguation { - if b[p] == ' ' { - pos.Ambiguity++ - b[p] = '0' - } - } - s = string(b) - +func (d positionDecoder) Decode(frame *Frame) (data Data, err error) { var ( - err error - latDeg, latMin, latMinFrag uint64 - lngDeg, lngMin, lngMinFrag uint64 - latHemi, lngHemi byte - isSouth, isWest bool + raw = frame.Raw + kind = raw.Type() + hasTimestamp = kind == '@' || kind == '/' + hasMessaging = kind == '@' || kind == '=' + content = string(raw[1:]) + pos = &Position{ + HasMessaging: hasMessaging, + } ) - if latDeg, err = strconv.ParseUint(s[0:2], 10, 8); err != nil { - return pos, "", err - } - if latMin, err = strconv.ParseUint(s[2:4], 10, 8); err != nil { - return pos, "", err - } - if latMinFrag, err = strconv.ParseUint(s[5:7], 10, 8); err != nil { - return pos, "", err - } - latHemi = s[7] - pos.Symbol[0] = s[8] - if lngDeg, err = strconv.ParseUint(s[9:12], 10, 8); err != nil { - return pos, "", err - } - if lngMin, err = strconv.ParseUint(s[12:14], 10, 8); err != nil { - return pos, "", err - } - if lngMinFrag, err = strconv.ParseUint(s[15:17], 10, 8); err != nil { - return pos, "", err - } - lngHemi = s[17] - pos.Symbol[1] = s[18] - - if latHemi == 'S' || latHemi == 's' { - isSouth = true - } else if latHemi != 'N' && latHemi != 'n' { - return pos, "", ErrInvalidPosition - } - - if lngHemi == 'W' || lngHemi == 'w' { - isWest = true - } else if lngHemi != 'E' && lngHemi != 'e' { - return pos, "", ErrInvalidPosition - } - - if latDeg > 89 || lngDeg > 179 { - return pos, "", ErrInvalidPosition - } - - pos.Latitude = float64(latDeg) + float64(latMin)/60.0 + float64(latMinFrag)/6000.0 - pos.Longitude = float64(lngDeg) + float64(lngMin)/60.0 + float64(lngMinFrag)/6000.0 - - if isSouth { - pos.Latitude = 0.0 - pos.Latitude - } - if isWest { - pos.Longitude = 0.0 - pos.Longitude - } - - if pos.Symbol[1] >= 'a' || pos.Symbol[1] <= 'k' { - pos.Symbol[1] -= 32 - } - - if len(s) > 19 { - return pos, s[19:], nil - } - return pos, "", nil -} - -func ParseCompressedPosition(s string) (Position, string, error) { - // APRS PROTOCOL REFERENCE 1.0.1 Chapter 9, page 36 (46 in PDF) - - pos := Position{} - - if len(s) < 10 { - return pos, "", ErrInvalidPosition - } - - // Base-91 check - for _, c := range s[1:9] { - if c < 0x21 || c > 0x7b { - return pos, "", ErrInvalidPosition + if hasTimestamp { + if len(content) < 7 { + return nil, io.ErrUnexpectedEOF } + if pos.Time, _, err = parseTimestamp(content[:7]); err != nil { + return + } + content = content[7:] } - var err error - var lat, lng int - if lat, err = base91Decode(s[1:5]); err != nil { - return pos, "", err + if err = pos.parsePositionAndComment(content); err != nil { + return } - if lng, err = base91Decode(s[5:9]); err != nil { - return pos, "", err - } - - pos.Latitude = 90.0 - float64(lat)/380926.0 - pos.Longitude = -180.0 + float64(lng)/190463.0 - pos.Compressed = true - - return pos, s[10:], nil -} - -func ParseMicE(s, dest string) (Position, error) { - // APRS PROTOCOL REFERENCE 1.0.1 Chapter 10, page 42 in PDF - - pos := Position{} - - if len(s) < 9 || len(dest) != 6 { - return pos, ErrInvalidPosition - } - - ns := miceCodes[rune(dest[3])][2] - we := miceCodes[rune(dest[5])][4] - - latF := fmt.Sprintf("%s%s", miceCodes[rune(dest[0])][0], miceCodes[rune(dest[1])][0]) - latF = strings.Trim(latF, ". ") - latD, err := strconv.ParseFloat(latF, 64) - if err != nil { - return pos, ErrInvalidPosition - } - lonF := fmt.Sprintf("%s%s.%s%s", miceCodes[rune(dest[2])][0], miceCodes[rune(dest[3])][0], miceCodes[rune(dest[4])][0], miceCodes[rune(dest[5])][0]) - lonF = strings.Trim(lonF, ". ") - latM, err := strconv.ParseFloat(lonF, 64) - if err != nil { - return pos, ErrInvalidPosition - } - if latM != 0 { - latD += latM / 60 - } - if strings.ToUpper(ns) == "S" { - latD = -latD - } - - lonOff := miceCodes[rune(dest[4])][3] - lonD := float64(s[1]) - 28 - if lonOff == "100" { - lonD += 100 - } - if lonD >= 180 && lonD < 190 { - lonD -= 80 - } - if lonD >= 190 && lonD < 200 { - lonD -= 190 - } - - lonM := float64(s[2]) - 28 - if lonM >= 60 { - lonM -= 60 - } - // adding hundreth of minute then add minute as deg fraction - lonH := float64(s[3]) - 28 - if lonH != 0 { - lonM += lonH / 100 - } - if lonM != 0 { - lonD += lonM / 60 - } - if strings.ToUpper(we) == "W" { - lonD = -lonD - } - - pos.Latitude = latD - pos.Longitude = lonD return pos, nil } -func ParsePositionGrid(s string) (Position, string, error) { - var o int - for o = 0; o < len(s); o++ { - if strings.IndexByte(gridChars, s[o]) < 0 { +func (pos *Position) parsePositionAndComment(s string) (err error) { + var comment string + //log.Printf("parse position and comment %q: %t", s, isDigit(s[0])) + if isDigit(s[0]) { + // probably not compressed + if comment, err = pos.parseNotCompressedPosition(s); err != nil { + return + } + pos.IsCompressed = false + } else { + // probably compressed + if comment, err = pos.parseCompressedPosition(s); err != nil { + return + } + pos.IsCompressed = true + } + + if len(comment) > 0 { + if err = pos.parseAltitudeWeatherAndExtension(comment); err != nil { + return + } + } + + return +} + +func (pos *Position) parseCompressedPosition(raw string) (comment string, err error) { + if len(raw) < 10 { + return "", fmt.Errorf("aprs: invalid compressed position string of length %d", len(raw)) + } + + pos.Symbol = string([]byte{raw[0], raw[9]}) + + var lat, lng int + if lat, err = base91Decode(raw[1:5]); err != nil { + return + } + if lng, err = base91Decode(raw[5:9]); err != nil { + return + } + + pos.Latitude = 90.0 - float64(lat)/380926.0 + pos.Longitude = -180.0 + float64(lng)/190463.0 + pos.IsCompressed = true + + comment = raw[13:] + if len(comment) >= 3 { + // Compression Type (T) Byte Format + // + // Bit: 7 | 6 | 5 | 4 3 | 2 1 0 | + // -------+--------+---------+-------------+------------------+ + // Unused | Unused | GPS Fix | NMEA Source | Origin | + // -------+--------+---------+-------------+------------------+ + // Val: 0 | 0 | 0 = old | 00 = other | 000 = Compressed | + // | | 1 = cur | 01 = GLL | 001 = TNC BTex | + // | | | 10 = CGA | 010 = Software | + // | | | 11 = RMC | 011 = [tbd] | + // | | | | 100 = KPC3 | + // | | | | 101 = Pico | + // | | | | 110 = Other | + // | | | | 111 = Digipeater | + var ( + c = comment[0] - 33 + s = comment[1] - 33 + T = comment[2] - 33 + ) + if raw[10] == ' ' { + // Don't do any further processing + } else if ((T >> 3) & 3) == 2 { + // CGA sentence, NMEA Source = 0b10 + var altitudeFeet int + if altitudeFeet, err = base91Decode(comment[:2]); err != nil { + return + } + pos.Altitude = feetToMeters(float64(altitudeFeet)) + pos.CompressedGPSFix = (T >> 5) & 0x01 + pos.CompressedNMEASource = (T >> 3) & 0x03 + pos.CompressedOrigin = T & 0x07 + comment = comment[3:] + } else if comment[0] >= '!' && comment[0] <= 'z' { // !..z + // Course/speed + pos.Velocity = &Velocity{ + Course: int(c) * 4, + Speed: knotsToMetersPerSecond(math.Pow(1.08, float64(s))), + } + pos.CompressedGPSFix = (T >> 5) & 0x01 + pos.CompressedNMEASource = (T >> 3) & 0x03 + pos.CompressedOrigin = T & 0x07 + comment = comment[3:] + } else if comment[0] == '{' { // { + // Precalculated range + pos.Range = milesToMeters(2 * math.Pow(1.08, float64(s))) + comment = comment[3:] + } + } + + return +} + +func (pos *Position) parseNotCompressedPosition(s string) (comment string, err error) { + //log.Printf("parse not compressed: %q", s) + if len(s) < 19 { + return "", fmt.Errorf("aprs: invalid not compressed position string of length %d", len(s)) + } + + pos.Symbol = string([]byte{s[8], s[18]}) + if pos.Latitude, err = parseDMLatitude(s[:8]); err != nil { + return + } + if pos.Longitude, err = parseDMLongitude(s[9:18]); err != nil { + return + } + return s[19:], nil +} + +var ( + matchVelocity = regexp.MustCompile(`^[. 0-3][. 0-9]{2,2}/[. 0-9]{3}`) + matchPHG = regexp.MustCompile(`^PHG[0-9]{3,3}[0-8]`) + matchDFS = regexp.MustCompile(`^DFS[0-9]{3,3}[0-8]`) + matchRNG = regexp.MustCompile(`^RNG[0-9]{4}`) + matchAreaObject = regexp.MustCompile(``) +) + +func (pos *Position) parseAltitudeWeatherAndExtension(s string) (err error) { + //log.Printf("parse altitude, weather and extensions %q", s) + var comment string + + // Parse extensions + switch { + case matchVelocity.MatchString(s): + var course, speed int + if course, err = strconv.Atoi(fillZeros(s[:3])); err != nil { + return fmt.Errorf("invalid course: %v", err) + } + if speed, err = strconv.Atoi(fillZeros(s[4:7])); err != nil { + return fmt.Errorf("invalid speed: %v", err) + } + pos.Velocity = &Velocity{ + Course: int(course), + Speed: knotsToMetersPerSecond(float64(speed)), + } + comment = s[7:] + if len(comment) > 2 && comment[0] == '/' && isDigit(comment[1]) && isDigit(comment[2]) { + var dir int + if dir, err = strconv.Atoi(fillZeros(comment[1:4])); err != nil { + return fmt.Errorf("invalid wind direction: %v", err) + } + if speed, err = strconv.Atoi(fillZeros(comment[5:8])); err != nil { + return fmt.Errorf("invalid wind speed: %v", err) + } + pos.Wind = &Wind{ + Direction: float64(dir), + Speed: knotsToMetersPerSecond(float64(speed)), + } + comment = comment[8:] + } + + case matchPHG.MatchString(s): + comment = s[7:] + + case matchDFS.MatchString(s): + comment = s[7:] + + case matchRNG.MatchString(s): + comment = s[7:] + + default: + comment = s + } + + //log.Printf("after extensions: %q", comment) + + if pos.Altitude, comment, err = parseAltitude(comment); err != nil { + return + } + + //log.Printf("after altitude, before weather: %q", comment) + + if pos.Weather, comment, err = parseWeather(comment); err != nil { + return + } + + //log.Printf("after weather, before telemetry: %q", comment) + + if pos.Telemetry, comment, err = parseBase91Telemetry(comment); err != nil { + return + } + + pos.Comment = comment + + return +} + +func parseAltitude(s string) (altitude float64, comment string, err error) { + const altitudeMarker = "/A=" + if i := strings.Index(s, altitudeMarker); i >= 0 { + //log.Printf("parse altitude marker: %q", s[i+3:]) + var feet int + if feet, err = strconv.Atoi(strings.TrimSpace(s[i+3 : i+3+6])); err != nil { + return 0, "", fmt.Errorf("aprs: invalid altitude: %v", err) + } + return feetToMeters(float64(feet)), s[:i] + s[i+3+6:], nil + } + return 0, s, nil +} + +var weatherSymbolSize = map[byte]int{ + 'g': 3, // peak wind speed in the past 5 minutes, in mph + 't': 3, // temperature in degrees Fahrenheit + 'r': 3, // rainfall in hundredths of an inch + 'p': 3, // rainfall in hundredths of an inch + 'P': 3, // rainfall in hundredths of an inch + 'h': 2, // relative humidity in % + 'b': 5, // barometric pressure in tenths of millibars + 'l': 3, // luminosity (in Watts per square meter) 1000 and above + 'L': 3, // luminosity (in Watts per square meter) 999 and below + 's': 3, // snowfall in the last 24 hours in inches + '#': 3, // raw rain counter +} + +// Weather report. +type Weather struct { + WindGust float64 `json:"windGust"` // wind gust in m/s + Temperature float64 `json:"temperature"` // temperature (in degrees C) + Rain1h float64 `json:"rain1h"` // rain in the last hour (in mm) + Rain24h float64 `json:"rain24h"` // rain in the last day (in mm) + RainSinceMidnight float64 `json:"rainSinceMidnight"` // rain since midnight (in mm) + RainRaw int `json:"rainRaw"` // rain raw counter + Humidity float64 `json:"humidity"` // relative humidity (in %) + Pressure float64 `json:"pressure"` // pressure (in mBar) + Luminosity float64 `json:"luminosity"` // luminosity (in W/m^2) + Snowfall float64 `json:"snowfall"` // snowfall (in cm/day) +} + +func parseWeather(s string) (weather *Weather, comment string, err error) { + comment = s + for len(comment) > 0 { + if size, ok := weatherSymbolSize[comment[0]]; ok { + if len(comment[1:]) < size { + return nil, "", fmt.Errorf("aprs: not enough characters to encode weather symbol %c (%d < %d)", comment[0], len(comment[1:]), size) + } + + var value float64 + if value, err = strconv.ParseFloat(comment[1:size+1], 64); err != nil { + // Something else that started with a weather symbol, perhaps a comment, stop parsing + err = nil + break + } + + if weather == nil { + weather = new(Weather) + } + + switch comment[0] { + case 'g': + weather.WindGust = value * 0.4470 + case 't': + weather.Temperature = fahrenheitToCelcius(value) + case 'r': + weather.Rain1h = value * 0.254 + case 'p': + weather.Rain24h = value * 0.254 + case 'P': + weather.RainSinceMidnight = value * 0.254 + case 'h': + weather.Humidity = value + case 'b': + weather.Pressure = value / 10 + case 'l': + weather.Luminosity = value + 1000 + case 'L': + weather.Luminosity = value + case 's': + weather.Snowfall = value * 2.54 + case '#': + weather.RainRaw = int(value) + } + + comment = comment[1+size:] + } else { break } } - pos := Position{} - if o == 2 || o == 4 || o == 6 || o == 8 { - p, err := maidenhead.ParseLocator(s[:o]) - if err != nil { - return pos, "", err + return +} + +type Telemetry struct { + ID int + Analog []int + Digital []bool +} + +var matchTelemetry = regexp.MustCompile(`\|([!-{]{4,14})\|`) + +func parseBase91Telemetry(s string) (telemetry *Telemetry, comment string, err error) { + var i int + if i = strings.IndexByte(s, '|'); i == -1 { + return nil, s, nil + } + + var sequence string + comment, sequence = s[:i], s[i+1:] + if i = strings.IndexByte(sequence, '|'); i < 1 { + // no closing | found, return as comment + return nil, s, nil + } + + if sequence, comment = sequence[:i], comment+sequence[i+1:]; len(sequence)%2 != 0 { + // uneven number of sequence elements, + return nil, s, nil + } + + telemetry = new(Telemetry) + if telemetry.ID, err = base91Decode(sequence[:2]); err != nil { + // it wasn't base-91 encoded telemetry, return data as comment + return nil, s, nil + } + + var values []int + for i = 2; i < len(sequence); i += 2 { + var value int + if value, err = base91Decode(sequence[i : i+2]); err != nil { + // it wasn't base-91 encoded telemetry, return data as comment + return nil, s, nil } - pos.Latitude = p.Latitude - pos.Longitude = p.Longitude + values = append(values, value) } - - var txt string - if o < len(s) { - txt = s[o+1:] + if len(values) > 5 { + for i = 0; i < 8; i++ { + telemetry.Digital = append(telemetry.Digital, (values[5]&1) == 1) + values[5] >>= 1 + } + values = values[:5] } - return pos, txt, nil + telemetry.Analog = values + return } -func ParsePosition(s string, compressed bool) (Position, string, error) { - if compressed { - return ParseCompressedPosition(s) +func parseDMLatitude(s string) (v float64, err error) { + if len(s) != 8 || s[4] != '.' || !(s[7] == 'N' || s[7] == 'S') { + return 0, fmt.Errorf("aprs: invalid latitude %q", s) } - return ParseUncompressedPosition(s) + + s = strings.Replace(s, " ", "0", -1) // position ambiguity + + var ( + degs, mins, minFrags int + south = s[7] == 'S' + ) + if degs, err = strconv.Atoi(s[:2]); err != nil { + return + } + if mins, err = strconv.Atoi(s[2:4]); err != nil { + return + } + if minFrags, err = strconv.Atoi(s[5:7]); err != nil { + return + } + + v = float64(degs) + float64(mins)/60 + float64(minFrags)/6000 + if south { + return -v, nil + } + return v, nil } -func ParsePositionBoth(s string) (Position, string, error) { - pos, txt, err := ParseUncompressedPosition(s) - if err != nil { - return ParseCompressedPosition(s) +func parseDMLongitude(s string) (v float64, err error) { + if len(s) != 9 || s[5] != '.' || !(s[8] == 'W' || s[8] == 'E') { + return 0, fmt.Errorf("aprs: invalid longitude %q", s) } - return pos, txt, err + + s = strings.Replace(s, " ", "0", -1) // position ambiguity + + var ( + degs, mins, minFrags int + east = s[8] == 'E' + ) + if degs, err = strconv.Atoi(s[:3]); err != nil { + return + } + if mins, err = strconv.Atoi(s[3:5]); err != nil { + return + } + if minFrags, err = strconv.Atoi(s[6:8]); err != nil { + return + } + + v = float64(degs) + float64(mins)/60 + float64(minFrags)/6000 + if east { + return v, nil + } + return -v, nil } diff --git a/protocol/aprs/position_test.go b/protocol/aprs/position_test.go new file mode 100644 index 0000000..3ced958 --- /dev/null +++ b/protocol/aprs/position_test.go @@ -0,0 +1,433 @@ +package aprs + +import ( + "reflect" + "testing" + "time" +) + +func TestParseBase91Telemetry(t *testing.T) { + tests := []struct { + Test string + Telemetry *Telemetry + Comment string + }{ + { + "|!!!!|", + &Telemetry{Analog: []int{0}}, + "", + }, + { + "|ss11|", + &Telemetry{ID: 7544, Analog: []int{1472}}, + "", + }, + { + "|ss112233|", + &Telemetry{ID: 7544, Analog: []int{1472, 1564, 1656}}, + "", + }, + { + "|ss1122334455!\"|", + &Telemetry{ID: 7544, Analog: []int{1472, 1564, 1656, 1748, 1840}, Digital: []bool{true, false, false, false, false, false, false, false}}, + "", + }, + { + "|ss11|73's de N0CALL", + &Telemetry{ID: 7544, Analog: []int{1472}}, + "73's de N0CALL", + }, + { + "`pZ3l-B]/'\"6{}|!9'X$u|!wr8!|3", + &Telemetry{ID: 24, Analog: []int{601, 357}}, + "`pZ3l-B]/'\"6{}!wr8!|3", + }, + { + "!/0%3RTh<6>dS_http://aprs.fi/|\"p%T'.ag|", + &Telemetry{ID: 170, Analog: []int{415, 559, 5894}}, + "!/0%3RTh<6>dS_http://aprs.fi/", + }, + { + "!6304.03NN02739.63E#PHG26303/Siilinjarvi|\"p%T'.agff|", + &Telemetry{ID: 170, Analog: []int{415, 559, 5894, 6348}}, + "!6304.03NN02739.63E#PHG26303/Siilinjarvi", + }, + } + + for _, test := range tests { + t.Run(test.Test, func(t *testing.T) { + v, comment, err := parseBase91Telemetry(test.Test) + if err != nil { + t.Fatal(err) + } + if (v == nil) != (test.Telemetry == nil) { + t.Fatalf("expected telemetry %#+v, got %#+v", test.Telemetry, v) + } + if test.Telemetry != nil { + if v.ID != test.Telemetry.ID { + t.Errorf("expected id %d, got %d", test.Telemetry.ID, v.ID) + } + if !reflect.DeepEqual(v.Analog, test.Telemetry.Analog) { + t.Errorf("expected analog values %d, got %d", test.Telemetry.Analog, v.Analog) + } + if !reflect.DeepEqual(v.Digital, test.Telemetry.Digital) { + t.Errorf("expected digital values %t, got %t", test.Telemetry.Digital, v.Digital) + } + } + if comment != test.Comment { + t.Errorf("expected comment %q, got %q", test.Comment, comment) + } + }) + } +} + +func TestParsePosition(t *testing.T) { + localTime := time.Now() + tests := []struct { + Name string + Raw Raw + Want *Position + }{ + { + "no timestamp, no APRS messaging, with comment", + "!4903.50N/07201.75W-Test 001234", + &Position{ + Latitude: 49.058333, + Longitude: -72.029167, + Symbol: "/-", + Comment: "Test 001234", + }, + }, + { + "no timestamp, no APRS messaging, altitude = 1234 ft", + "!4903.50N/07201.75W-Test /A=001234", + &Position{ + Latitude: 49.058333, + Longitude: -72.029167, + Altitude: 376.1232, + Symbol: "/-", + Comment: "Test ", + }, + }, + { + "no timestamp, no APRS messaging, location to nearest degree", + "!49 . N/072 . W-", + &Position{ + Latitude: 49, + Longitude: -72, + Symbol: "/-", + }, + }, + { + "with timestamp, no APRS messaging, zulu time, with comment", + "/092345z4903.50N/07201.75W>Test1234", + &Position{ + Latitude: 49.058333, + Longitude: -72.029167, + Symbol: "/>", + Comment: "Test1234", + Time: time.Date(localTime.Year(), localTime.Month(), 9, 23, 45, 0, 0, time.UTC), + }, + }, + { + "with timestamp, with APRS messaging, local time, with comment", + "@092345/4903.50N/07201.75W>Test1234", + &Position{ + HasMessaging: true, + Latitude: 49.058333, + Longitude: -72.029167, + Symbol: "/>", + Comment: "Test1234", + Time: time.Date(localTime.Year(), localTime.Month(), 9, 23, 45, 0, 0, localTime.Location()), + }, + }, + { + "no timestamp, with APRS messaging, with PHG", + "=4903.50N/07201.75W#PHG5132", + &Position{ + HasMessaging: true, + Latitude: 49.058333, + Longitude: -72.029167, + Symbol: "/#", + }, + }, + { + "weather report", + "=4903.50N/07201.75W 225/000g000t050r000p001h00b10138dU2k", + &Position{ + HasMessaging: true, + Latitude: 49.058333, + Longitude: -72.029167, + Symbol: "/ ", + Comment: "dU2k", + Velocity: &Velocity{Course: 225}, + Weather: &Weather{Temperature: 10, Rain24h: 0.254, Pressure: 1013.8}, + }, + }, + { + "with timestamp, with APRS messaging, local time, course/speed", + "@092345/4903.50N/07201.75W>088/036", + &Position{ + HasMessaging: true, + Latitude: 49.058333, + Longitude: -72.029167, + Symbol: "/>", + Time: time.Date(localTime.Year(), localTime.Month(), 9, 23, 45, 0, 0, localTime.Location()), + Velocity: &Velocity{Course: 88, Speed: 18.519999984000002}, + }, + }, + { + "with timestamp, with APRS messaging, hours/mins/secs time, PHG", + "@234517h4903.50N/07201.75W>PHG5132", + &Position{ + HasMessaging: true, + Latitude: 49.058333, + Longitude: -72.029167, + Symbol: "/>", + Time: time.Date(0, 0, 0, 23, 45, 17, 0, time.UTC), + }, + }, + { + "with timestamp, with APRS messaging, zulu time, radio range", + "@092345z4903.50N/07201.75W>RNG0050", + &Position{ + HasMessaging: true, + Latitude: 49.058333, + Longitude: -72.029167, + Symbol: "/>", + Time: time.Date(localTime.Year(), localTime.Month(), 9, 23, 45, 0, 0, time.UTC), + }, + }, + { + "with timestamp, hours/mins/secs time, DF, no APRS messaging", + "/234517h4903.50N/07201.75W>DFS2360", + &Position{ + Latitude: 49.058333, + Longitude: -72.029167, + Symbol: "/>", + Time: time.Date(0, 0, 0, 23, 45, 17, 0, localTime.Location()), + }, + }, + { + "with timestamp, APRS messaging, zulu time, weather report", + "@092345z4903.50N/07201.75W 090/000g000t066r000p000dUII", + &Position{ + HasMessaging: true, + Latitude: 49.058333, + Longitude: -72.029167, + Symbol: "/ ", + Comment: "dUII", + Time: time.Date(localTime.Year(), localTime.Month(), 9, 23, 45, 0, 0, time.UTC), + Velocity: &Velocity{Course: 90}, + Weather: &Weather{Temperature: 18.88888888888889}, + }, + }, + { + "no timestamp, course/speed/bearing/NRQ, with APRS messaging, DF station moving", + "=4903.50N/07201.75W\\088/036/270/729", + &Position{ + HasMessaging: true, + Latitude: 49.058333, + Longitude: -72.029167, + Symbol: "/\\", + Velocity: &Velocity{Course: 88, Speed: knotsToMetersPerSecond(36)}, + Wind: &Wind{Direction: 270, Speed: knotsToMetersPerSecond(729)}, + }, + }, + { + "no timestamp, course/speed/bearing/NRQ, with APRS messaging, DF station fixed", + "=4903.50N/07201.75W\\000/036/270/729", + &Position{ + HasMessaging: true, + Latitude: 49.058333, + Longitude: -72.029167, + Symbol: "/\\", + Velocity: &Velocity{Course: 0, Speed: knotsToMetersPerSecond(36)}, + Wind: &Wind{Direction: 270, Speed: knotsToMetersPerSecond(729)}, + }, + }, + { + "with timestamp, course/speed/bearing/NRQ, with APRS messaging", + "@092345z4903.50N/07201.75W\\088/036/270/729", + &Position{ + HasMessaging: true, + Latitude: 49.058333, + Longitude: -72.029167, + Symbol: "/\\", + Time: time.Date(localTime.Year(), localTime.Month(), 9, 23, 45, 0, 0, time.UTC), + Velocity: &Velocity{Course: 88, Speed: knotsToMetersPerSecond(36)}, + Wind: &Wind{Direction: 270, Speed: knotsToMetersPerSecond(729)}, + }, + }, + { + "with timestamp, bearing/NRQ, no course/speed, no APRS messaging", + "/092345z4903.50N/07201.75W\\000/000/270/729", + &Position{ + Latitude: 49.058333, + Longitude: -72.029167, + Symbol: "/\\", + Time: time.Date(localTime.Year(), localTime.Month(), 9, 23, 45, 0, 0, time.UTC), + Velocity: &Velocity{}, + Wind: &Wind{Direction: 270, Speed: knotsToMetersPerSecond(729)}, + }, + }, + + // compressed positions: + + { + "compressed, with APRS messaging", + "=/5L!!<*e7> sTComment", + &Position{ + HasMessaging: true, + Latitude: 49.5, + Longitude: -72.750004, + Symbol: "/>", + Comment: "Comment", + }, + }, + { + "compressed, with APRS messaging, RMC sentence, with course/speed", + "=/5L!!<*e7>7P[", + &Position{ + HasMessaging: true, + Latitude: 49.5, + Longitude: -72.750004, + Symbol: "/>", + }, + }, + { + "compressed, with APRS messaging, with radio range", + "=/5L!!<*e7>{?!", + &Position{ + HasMessaging: true, + Latitude: 49.5, + Longitude: -72.750004, + Symbol: "/>", + }, + }, + { + "compressed, with APRS messaging, GGA sentence, altitude", + "=/5L!!<*e7OS]S", + &Position{ + HasMessaging: true, + Latitude: 49.5, + Longitude: -72.750004, + Symbol: "/O", + }, + }, + { + "compressed, with APRS messaging, timestamp, radio range", + "@092345z/5L!!<*e7>{?!", + &Position{ + HasMessaging: true, + Latitude: 49.5, + Longitude: -72.750004, + Symbol: "/>", + Time: time.Date(localTime.Year(), localTime.Month(), 9, 23, 45, 0, 0, time.UTC), + }, + }, + } + + var decoder positionDecoder + for _, test := range tests { + t.Run(test.Name, func(t *testing.T) { + frame := &Frame{Raw: test.Raw} + if !decoder.CanDecode(frame) { + t.Fatalf("%T can't decode %q", decoder, test.Raw) + } + + v, err := decoder.Decode(frame) + if err != nil { + t.Fatal(err) + } + + testComparePosition(t, test.Want, v) + }) + } + +} + +func testComparePosition(t *testing.T, want *Position, value any) { + t.Helper() + + p, ok := value.(*Position) + if !ok { + t.Fatalf("expected data to be a %T, got %T", want, value) + return + } + + if p.HasMessaging != want.HasMessaging { + t.Errorf("expected to have messaging: %t, got %t", want.HasMessaging, p.HasMessaging) + } + + if !testAlmostEqual(p.Latitude, want.Latitude) { + t.Errorf("expected latitude %f, got %f", want.Latitude, p.Latitude) + } + if !testAlmostEqual(p.Longitude, want.Longitude) { + t.Errorf("expected longitude %f, got %f", want.Longitude, p.Longitude) + } + if !testAlmostEqual(p.Altitude, want.Altitude) { + t.Errorf("expected altitude %f, got %f", want.Altitude, p.Altitude) + } + + if p.Symbol != want.Symbol { + t.Errorf("expected symbol %q, got %q", want.Symbol, p.Symbol) + } + if p.Comment != want.Comment { + t.Errorf("expected comment %q, got %q", want.Comment, p.Comment) + } + + if want.Time.Equal(time.Time{}) { + if !p.Time.Equal(time.Time{}) { + t.Errorf("expected no time stamp, got %s", p.Time) + } + } else if want.Time.Year() == -1 { + if p.Time.Hour() != want.Time.Hour() || + p.Time.Minute() != want.Time.Minute() || + p.Time.Second() != want.Time.Second() { + t.Errorf("expected time %s, got %s", want.Time.Format("15:04:05"), p.Time.Format("15:04:05")) + } + } else if !want.Time.Equal(p.Time) { + t.Errorf("expected time %s, got %s", want.Time, p.Time) + } + + if want.Velocity != nil { + if p.Velocity == nil { + t.Errorf("expected velocity, got none") + } else if !reflect.DeepEqual(p.Velocity, want.Velocity) { + t.Errorf("expected velocity %#+v, got %#+v", want.Velocity, p.Velocity) + } + } else if p.Velocity != nil { + t.Errorf("expected no velocity, got %#+v", p.Velocity) + } + + if want.Wind != nil { + if p.Wind == nil { + t.Errorf("expected wind, got none") + } else if !reflect.DeepEqual(p.Wind, want.Wind) { + t.Errorf("expected wind %#+v, got %#+v", want.Wind, p.Wind) + } + } else if p.Wind != nil { + t.Errorf("expected no wind, got %#+v", p.Wind) + } + + if want.Telemetry != nil { + if p.Telemetry == nil { + t.Errorf("expected telemetry, got none") + } else if !reflect.DeepEqual(p.Telemetry, want.Telemetry) { + t.Errorf("expected telemetry %#+v, got %#+v", want.Telemetry, p.Telemetry) + } + } else if p.Telemetry != nil { + t.Errorf("expected no telemetry, got %#+v", p.Telemetry) + } + + if want.Weather != nil { + if p.Weather == nil { + t.Errorf("expected weather, got none") + } else if !reflect.DeepEqual(p.Weather, want.Weather) { + t.Errorf("expected weather %#+v, got %#+v", want.Weather, p.Weather) + } + } else if p.Weather != nil { + t.Errorf("expected no weather, got %#+v", p.Weather) + } +} diff --git a/protocol/aprs/query.go b/protocol/aprs/query.go new file mode 100644 index 0000000..7f09240 --- /dev/null +++ b/protocol/aprs/query.go @@ -0,0 +1,50 @@ +package aprs + +import ( + "strconv" + "strings" +) + +type Query struct { + Type string `json:"type"` + Latitude float64 + Longitude float64 + Radius float64 // radius in meters +} + +func (q Query) String() string { + return q.Type +} + +type queryDecoder struct{} + +func (queryDecoder) CanDecode(frame *Frame) bool { + return len(frame.Raw) >= 3 && frame.Raw.Type() == '?' +} + +func (queryDecoder) Decode(frame *Frame) (data Data, err error) { + var ( + kind = string(frame.Raw[1:]) + args string + i int + ) + if i = strings.IndexByte(kind, '?'); i == -1 { + return &Query{ + Type: kind, + }, nil + } else { + kind, args = kind[:i], kind[i+1:] + } + + query := &Query{Type: kind} + + if part := strings.SplitN(strings.TrimSpace(args), ",", 3); len(part) == 3 { + var radius int + query.Latitude, _ = strconv.ParseFloat(part[0], 64) + query.Longitude, _ = strconv.ParseFloat(part[1], 64) + radius, _ = strconv.Atoi(part[2]) + query.Radius = milesToMeters(float64(radius)) + } + + return query, nil +} diff --git a/protocol/aprs/query_test.go b/protocol/aprs/query_test.go new file mode 100644 index 0000000..2ef9608 --- /dev/null +++ b/protocol/aprs/query_test.go @@ -0,0 +1,69 @@ +package aprs + +import "testing" + +func TestParseQuery(t *testing.T) { + tests := []struct { + Name string + Raw Raw + Want *Query + }{ + { + "general query", + "?APRS?", + &Query{ + Type: "APRS", + }, + }, + { + "general query for stations within a target footprint of radius 200 miles", + "?APRS? 34.02,-117.15,0200", + &Query{ + Type: "APRS", + Latitude: 34.02, + Longitude: -117.15, + Radius: milesToMeters(200), + }, + }, + } + + var decoder queryDecoder + for _, test := range tests { + t.Run(test.Name, func(t *testing.T) { + frame := &Frame{Raw: test.Raw} + if !decoder.CanDecode(frame) { + t.Fatalf("%T can't decode %q", decoder, test.Raw) + } + + v, err := decoder.Decode(frame) + if err != nil { + t.Fatal(err) + } + + testCompareQuery(t, test.Want, v) + }) + } +} + +func testCompareQuery(t *testing.T, want *Query, value any) { + t.Helper() + + test, ok := value.(*Query) + if !ok { + t.Fatalf("expected data to be a %T, got %T", want, value) + return + } + + if test.Type != want.Type { + t.Errorf("expected type %q, got %q", want.Type, test.Type) + } + if !testAlmostEqual(test.Latitude, want.Latitude) { + t.Errorf("expected latitude %f, got %f", want.Latitude, test.Latitude) + } + if !testAlmostEqual(test.Longitude, want.Longitude) { + t.Errorf("expected longitude %f, got %f", want.Longitude, test.Longitude) + } + if !testAlmostEqual(test.Radius, want.Radius) { + t.Errorf("expected radius %f, got %f", want.Radius, test.Radius) + } +} diff --git a/protocol/aprs/status.go b/protocol/aprs/status.go new file mode 100644 index 0000000..6124a1a --- /dev/null +++ b/protocol/aprs/status.go @@ -0,0 +1,92 @@ +package aprs + +import ( + "strings" + "time" + + "git.maze.io/go/ham/util/maidenhead" +) + +type Status struct { + Time time.Time + Latitude float64 + Longitude float64 + BeamHeading int + ERP int + Symbol string + Text string +} + +func (s Status) String() string { + return s.Text +} + +type statusReportDecoder struct{} + +func (statusReportDecoder) CanDecode(frame Frame) bool { + return frame.Raw.Type() == '>' +} + +func (statusReportDecoder) Decode(frame Frame) (data Data, err error) { + var ( + text = string(frame.Raw[1:]) + status = new(Status) + ) + if hasTimestamp(text) { + if status.Time, text, err = parseTimestamp(text); err != nil { + return + } + } + + if len(text) > 3 && text[len(text)-3] == '^' { + var ( + h = text[len(text)-2] + p = text[len(text)-1] + ) + if h >= '0' && h <= '9' { + status.BeamHeading = int(h-'0') * 10 + } else if h >= 'A' && h <= 'Z' { + status.BeamHeading = 100 + int(h-'A')*10 + } + if p >= '0' && p <= 'Z' { + status.ERP = int(p-'0') * int(p-'0') * 10 + } + text = text[:len(text)-3] + } + + here := text + if i := strings.IndexByte(here, ' '); i != -1 { + here = here[:i] + } + switch len(here) { + case 6: + var point maidenhead.Point + if point, err = maidenhead.ParseLocator(here[:4]); err != nil { + return + } + status.Latitude = point.Latitude + status.Longitude = point.Longitude + status.Symbol = here[4:] + if len(text) > 6 { + text = text[7:] + } else { + text = text[6:] + } + case 8: + var point maidenhead.Point + if point, err = maidenhead.ParseLocator(here[:6]); err != nil { + return + } + status.Latitude = point.Latitude + status.Longitude = point.Longitude + status.Symbol = here[6:] + if len(text) > 8 { + text = text[9:] + } else { + text = text[8:] + } + } + + status.Text = text + return status, nil +} diff --git a/protocol/aprs/status_test.go b/protocol/aprs/status_test.go new file mode 100644 index 0000000..97ea688 --- /dev/null +++ b/protocol/aprs/status_test.go @@ -0,0 +1,120 @@ +package aprs + +import "testing" + +func TestParseStatusReport(t *testing.T) { + tests := []struct { + Name string + Raw Raw + Want *Status + }{ + { + "without timestamp", + ">Net Control Center", + &Status{ + Text: "Net Control Center", + }, + }, + { + "with timestamp", + ">092345zNet Control Center", + &Status{ + Text: "Net Control Center", + }, + }, + { + "with beam heading and erp", + ">Test^B7", + &Status{ + Text: "Test", + BeamHeading: 110, + ERP: 490, + }, + }, + { + "with maidenhead locator", + ">IO91SX/G", + &Status{ + Latitude: 51.958333, + Longitude: -0.5, + Symbol: "/G", + }, + }, + { + "with short maidenhead locator", + ">IO91/G", + &Status{ + Latitude: 51, + Longitude: -2, + Symbol: "/G", + }, + }, + { + "with maidenhead locator and comment", + ">IO91SX/- My house", + &Status{ + Latitude: 51.958333, + Longitude: -0.5, + Symbol: "/-", + Text: "My house", + }, + }, + { + "with maidenhead locator and beam heading", + ">IO91SX/- ^B7", + &Status{ + Latitude: 51.958333, + Longitude: -0.5, + Symbol: "/-", + BeamHeading: 110, + ERP: 490, + }, + }, + } + + var decoder statusReportDecoder + for _, test := range tests { + t.Run(test.Name, func(t *testing.T) { + frame := Frame{Raw: test.Raw} + if !decoder.CanDecode(frame) { + t.Fatalf("%T can't decode %q", decoder, test.Raw) + } + + v, err := decoder.Decode(frame) + if err != nil { + t.Fatal(err) + } + + testCompareStatusReport(t, test.Want, v) + }) + } +} + +func testCompareStatusReport(t *testing.T, want *Status, value any) { + t.Helper() + + test, ok := value.(*Status) + if !ok { + t.Fatalf("expected data to be a %T, got %T", want, value) + return + } + + if !testAlmostEqual(test.Latitude, want.Latitude) { + t.Errorf("expected latitude %f, got %f", want.Latitude, test.Latitude) + } + if !testAlmostEqual(test.Longitude, want.Longitude) { + t.Errorf("expected longitude %f, got %f", want.Longitude, test.Longitude) + } + if test.BeamHeading != want.BeamHeading { + t.Errorf("expected beam heading %d, got %d", want.BeamHeading, test.BeamHeading) + } + if test.ERP != want.ERP { + t.Errorf("expected ERP %dW, got %dW", want.ERP, test.ERP) + } + if test.Symbol != want.Symbol { + t.Errorf("expected symbol %q, got %q", want.Symbol, test.Symbol) + } + if test.Text != want.Text { + t.Errorf("expected text %q, got %q", want.Text, test.Text) + } +} diff --git a/protocol/aprs/symbol.go b/protocol/aprs/symbol.go deleted file mode 100644 index abe9e9e..0000000 --- a/protocol/aprs/symbol.go +++ /dev/null @@ -1,266 +0,0 @@ -package aprs - -import ( - "encoding/json" - "fmt" -) - -var emptySymbol Symbol - -type Symbol [2]byte - -func (s Symbol) IsPrimaryTable() bool { return s[0] != '\\' } - -func (s Symbol) MarshalJSON() ([]byte, error) { - if s == emptySymbol { - return json.Marshal("") - } - return json.Marshal(string(s[:])) -} - -func (s Symbol) get(idx int) (string, error) { - var m map[byte]map[int]string - if s.IsPrimaryTable() { - m = primarySymbol - } else { - m = alternateSymbol - } - n, ok := m[s[1]] - if !ok { - return "", fmt.Errorf("unknown symbol %x", s[1]) - } - if i, ok := n[idx]; ok { - return i, nil - } - return "", fmt.Errorf("symbol doesn't have requested index: %v", n) -} - -func (s Symbol) String() string { - hr, err := s.get(1) - if err != nil { - return err.Error() - } - return hr -} - -func (s Symbol) SSID() (string, error) { - return s.get(2) -} - -func (s Symbol) Emoji() (string, error) { - return s.get(3) -} - -func IsValidCompressedSymTable(c byte) bool { - return c == '/' || - c == '\\' || - (c >= 0x41 && c <= 0x5a) || - (c >= 0x61 && c <= 0x6a) -} - -func IsValidUncompressedSymTable(c byte) bool { - return c == '/' || - c == '\\' || - (c >= 0x41 && c <= 0x5a) || - (c >= 0x30 && c <= 0x39) -} - -var ( - // Source: http://www.aprs.org/symbols/symbolsX.txt - // 0: XYZ code - // 1: Human readable - // 2: SSID - // 3: Emoji - primarySymbol = map[byte]map[int]string{ - '!': {0: "BB", 1: "Police, Sheriff", 3: ":cop:"}, - '"': {0: "BC", 1: "reserved"}, - '#': {0: "BD", 1: "Digi"}, - '$': {0: "BE", 1: "Phone", 3: ":phone:"}, - '%': {0: "BF", 1: "DX Cluster"}, - '&': {0: "BG", 1: "HF Gateway"}, - '\'': {0: "BH", 1: "Small Aircraft", 2: "11", 3: ":airplane:"}, - '(': {0: "BI", 1: "Mobile Satellite Station", 3: ":satellite:"}, - ')': {0: "BJ", 1: "Wheelchair", 3: ":wheelchair:"}, - '*': {0: "BK", 1: "Snowmobile"}, - '+': {0: "BL", 1: "Red Cross"}, - ',': {0: "BM", 1: "Boy Scout"}, - '-': {0: "BN", 1: "House QTH (VHF)"}, - '.': {0: "BO", 1: "X"}, - '/': {0: "BP", 1: "Red Dot"}, - '0': {0: "P0", 1: "Circle (0)"}, - '1': {0: "P1", 1: "Circle (1)"}, - '2': {0: "P2", 1: "Circle (2)"}, - '3': {0: "P3", 1: "Circle (3)"}, - '4': {0: "P4", 1: "Circle (4)"}, - '5': {0: "P5", 1: "Circle (5)"}, - '6': {0: "P6", 1: "Circle (6)"}, - '7': {0: "P7", 1: "Circle (7)"}, - '8': {0: "P8", 1: "Circle (8)"}, - '9': {0: "P9", 1: "Circle (9)"}, - ':': {0: "MR", 1: "Fire", 3: ":fire:"}, - ';': {0: "MS", 1: "Campground", 3: ":tent:"}, - '<': {0: "MT", 1: "Motorcycle", 2: "10", 3: ":bike:"}, - '=': {0: "MU", 1: "Railroad Engine", 3: ":train:"}, - '>': {0: "MV", 1: "Car", 2: "9", 3: ":car:"}, - '?': {0: "MW", 1: "File Server"}, - '@': {0: "MX", 1: "HC Future"}, - 'A': {0: "PA", 1: "Aid Station", 3: ":hospital:"}, - 'B': {0: "PB", 1: "BBS or PBBS"}, - 'C': {0: "PC", 1: "Canoe"}, - 'D': {0: "PD"}, - 'E': {0: "PE", 1: "Eyeball"}, - 'F': {0: "PF", 1: "Tractor", 3: ":tractor:"}, - 'G': {0: "PG", 1: "Grid Square"}, - 'H': {0: "PH", 1: "Hotel", 3: ":hotel:"}, - 'I': {0: "PI", 1: "TCP/IP"}, - 'J': {0: "PJ"}, - 'K': {0: "PK", 1: "School", 3: ":school:"}, - 'L': {0: "PL", 1: "PC User", 3: ":computer:"}, - 'M': {0: "PM", 1: "MacAPRS", 3: ":computer:"}, - 'N': {0: "PN", 1: "NTS Station"}, - 'O': {0: "PO", 1: "Balloon", 2: "11", 3: ":airplane:"}, - 'P': {0: "PP", 1: "Police", 3: ":police_car:"}, - 'Q': {0: "PQ"}, - 'R': {0: "PR", 1: "Recreational Vehicle", 2: "13", 3: ":car:"}, - 'S': {0: "PS", 1: "Shuttle"}, - 'T': {0: "PT", 1: "SSTV"}, - 'U': {0: "PU", 1: "Bus", 2: "2", 3: ":bus:"}, - 'V': {0: "PV", 1: "ATV"}, - 'W': {0: "PW", 1: "National WX Service Site"}, - 'X': {0: "PX", 1: "Helo", 2: "6"}, - 'Y': {0: "PY", 1: "Yacht", 2: "5", 3: ":sailboat:"}, - 'Z': {0: "PZ", 1: "WinAPRS", 3: ":computer:"}, - '[': {0: "HS", 1: "Human/Person", 2: "7", 3: ":running:"}, - '\\': {0: "HT", 1: "DF Station"}, - ']': {0: "HU", 1: "Post Office", 3: ":post_office:"}, - '^': {0: "HV", 1: "Large Aircraft", 3: ":airplane:"}, - '_': {0: "HW", 1: "Weather Station", 3: ":cloud:"}, - '`': {0: "HX", 1: "Dish Antenna", 3: ":satellite:"}, - 'a': {0: "LA", 1: "Ambulance", 2: "1", 3: ":ambulance:"}, - 'b': {0: "LB", 1: "Bike", 2: "4", 3: ":bike:"}, - 'c': {0: "LC", 1: "Incident Command Post"}, - 'd': {0: "LD", 1: "Fire Dept", 3: ":fire_engine:"}, - 'e': {0: "LE", 1: "Horse", 3: ":racehorse:"}, - 'f': {0: "LF", 1: "Fire Truck", 2: "3", 3: ":fire_engine:"}, - 'g': {0: "LG", 1: "Glider", 3: ":airplane:"}, - 'h': {0: "LH", 1: "Hospital", 3: ":hospital:"}, - 'i': {0: "LI", 1: "IOTA"}, - 'j': {0: "LJ", 1: "Jeep", 2: "12", 3: ":car:"}, - 'k': {0: "LK", 1: "Truck", 2: "14", 3: ":truck:"}, - 'l': {0: "LL", 1: "Laptop", 3: ":computer:"}, - 'm': {0: "LM", 1: "Mic-E Repeater"}, - 'n': {0: "LN", 1: "Node"}, - 'o': {0: "LO", 1: "EOC"}, - 'p': {0: "LP", 1: "Dog", 3: ":dog2:"}, - 'q': {0: "LQ", 1: "Grid SQ"}, - 'r': {0: "LR", 1: "Repeater"}, - 's': {0: "LS", 1: "Ship", 2: "8", 3: ":ship:"}, - 't': {0: "LT", 1: "Truck Stop"}, - 'u': {0: "LU", 1: "Truck (18 Wheeler)", 3: ":truck:"}, - 'v': {0: "LV", 1: "Van", 2: "15", 3: ":minibus:"}, - 'w': {0: "LW", 1: "Water Station"}, - 'x': {0: "LX", 1: "xAPRS", 3: ":computer:"}, - 'y': {0: "LY", 1: "Yagi @ QTH"}, - 'z': {0: "LZ"}, - '{': {0: "J1"}, - '|': {0: "J2", 1: "TNC Stream Switch"}, - '}': {0: "J3"}, - '~': {0: "J4", 1: "TNC Stream Switch"}, - } - alternateSymbol = map[byte]map[int]string{ - '!': {0: "OBO", 1: "Emergency"}, - '"': {0: "OC", 1: "Reserved"}, - '#': {0: "OD#", 1: "Overlay Digi"}, - '$': {0: "OEO", 1: "Bank/ATM", 3: ":atm:"}, - '%': {0: "OFO", 1: "Power Plant", 3: ":factory:"}, - '&': {0: "OG#", 1: "I=Igte R=RX T=1hopTX 2=2hopTX"}, - '\'': {0: "OHO", 1: "Crash Site"}, - '(': {0: "OIO", 1: "Cloudy", 3: ":cloud:"}, - ')': {0: "OJO", 1: "Firenet MEO"}, - '*': {0: "OK"}, - '+': {0: "OL", 1: "Church", 3: ":church:"}, - ',': {0: "OM", 1: "Girl Scouts", 3: ":tent:"}, - '-': {0: "ONO", 1: "House", 3: ":house:"}, - '.': {0: "OO", 1: "Ambiguous"}, - '/': {0: "OP", 1: "Waypoint Destination"}, - '0': {0: "A0#", 1: "Circle", 3: ":red_circle:"}, - '1': {0: "A1"}, - '2': {0: "A2"}, - '3': {0: "A3"}, - '4': {0: "A4"}, - '5': {0: "A5"}, - '6': {0: "A6"}, - '7': {0: "A7"}, - '8': {0: "A8O", 1: "WiFi Network"}, - '9': {0: "A9", 1: "Gas Station", 3: ":fuelpump:"}, - ':': {0: "NR"}, - ';': {0: "NSO", 1: "Park/Picnic"}, - '<': {0: "NTO", 1: "Advisory"}, - '=': {0: "NUO"}, - '>': {0: "NV#", 1: "Cars & Vehicles", 3: ":car:"}, - '?': {0: "NW", 1: "Info Kiosk"}, - '@': {0: "NX", 1: "Hurricane", 3: ":cyclone:"}, - 'A': {0: "AA#", 1: "Box DTMF & RFID"}, - 'B': {0: "AB"}, - 'C': {0: "AC", 1: "Coast Guard"}, - 'D': {0: "ADO", 1: "Depots"}, - 'E': {0: "AE", 1: "Smoke"}, - 'F': {0: "AF"}, - 'G': {0: "AG"}, - 'H': {0: "AHO", 1: "Haze"}, - 'I': {0: "AI", 1: "Rain Shower", 3: ":umbrella:"}, - 'J': {0: "AJ"}, - 'K': {0: "AK", 1: "Kenwood HT"}, - 'L': {0: "AL", 1: "Lighthouse"}, - 'M': {0: "AMO", 1: "MARS"}, - 'N': {0: "AN", 1: "Navigation Buoy"}, - 'O': {0: "AO", 1: "Rocket", 3: ":rocket:"}, - 'P': {0: "AP", 1: "Parking", 3: ":parking:"}, - 'Q': {0: "AQ", 1: "Quake"}, - 'R': {0: "ARO", 1: "Restaurant"}, - 'S': {0: "AS", 1: "Satellite/Pacsat", 3: ":rocket:"}, - 'T': {0: "AT", 1: "Thunderstorm"}, - 'U': {0: "AU", 1: "Sunny"}, - 'V': {0: "AV", 1: "VORTAC Nav Aid"}, - 'W': {0: "AW#", 1: "NWS Site"}, - 'X': {0: "AX", 1: "Pharmacy"}, - 'Y': {0: "AYO", 1: "Radios and devices"}, - 'Z': {0: "AZ"}, - '[': {0: "DSO", 1: "W. Cloud"}, - '\\': {0: "DTO", 1: "GPS"}, - ']': {0: "DU"}, - '^': {0: "DV#", 1: "Other Aircraft", 3: ":airplane:"}, - '_': {0: "DW#", 1: "WX Site"}, - '`': {0: "DX", 1: "Rain", 3: ":umbrella:"}, - 'a': {0: "SA#O"}, - 'b': {0: "SB"}, - 'c': {0: "SC#O", 1: "CD Triangle"}, - 'd': {0: "SD", 1: "DX Spot"}, - 'e': {0: "SE", 1: "Sleet"}, - 'f': {0: "SF", 1: "Funnel Cloud"}, - 'g': {0: "SG", 1: "Gale Flags"}, - 'h': {0: "SHO", 1: "Store or Hamfest"}, - 'i': {0: "SI#", 1: "Box / POI"}, - 'j': {0: "SJ", 1: "Work Zone"}, - 'k': {0: "SKO", 1: "Special Vehicle"}, - 'l': {0: "SL", 1: "Areas"}, - 'm': {0: "SM", 1: "Value Sign"}, - 'n': {0: "SN#", 1: "Triangle"}, - 'o': {0: "SO", 1: "Small Circle"}, - 'p': {0: "SP"}, - 'q': {0: "SQ"}, - 'r': {0: "SR", 1: "Restrooms"}, - 's': {0: "SS#", 1: "Ship/Boats", 3: ":speedboat:"}, - 't': {0: "ST", 1: "Tornado", 3: ":cyclone:"}, - 'u': {0: "SU#", 1: "Truck", 3: ":truck:"}, - 'v': {0: "SV#", 1: "Van", 3: ":minibus:"}, - 'w': {0: "SWO", 1: "Flooding"}, - 'x': {0: "SX", 1: "Wreck/Obstruction"}, - 'y': {0: "SY", 1: "Skywarn"}, - 'z': {0: "SZ#", 1: "Shelter"}, - '{': {0: "Q1"}, - '|': {0: "Q2", 1: "TNC Stream Switch"}, - '}': {0: "Q3"}, - '~': {0: "Q4", 1: "TNC Stream Switch"}, - } -) diff --git a/protocol/aprs/testdata/packets.txt b/protocol/aprs/testdata/packets.txt new file mode 100644 index 0000000..98e1550 --- /dev/null +++ b/protocol/aprs/testdata/packets.txt @@ -0,0 +1,996 @@ +2026-03-02 15:49:27 CET: GW1523>APN000,TCPXX*,qAX,CWOP-4:@021449z5052.20N/00331.42E_126/009g023t061r000p000P000b10185h58L207eMB63 +2026-03-02 15:49:27 CET: EW6603>APRS,TCPXX*,qAX,CWOP-4:@021449z3815.25N/07827.60W_161/000g003t037r000p000P000h57b10357.DsVP +2026-03-02 15:49:27 CET: IU6SSZ-15>APHBL3,TCPIP*,qAS,AD4NCO-10:@144927h4221.32N/01423.98E[000/000/D-APRS ADN Systems / DMR ID: 2226001 +2026-03-02 15:49:28 CET: G7UKK-10>APDW18,qAO,G7UKK-10:!5335.18N/00142.48W_202/007g011t054r000p000P000h79b10128144.800MHz Rx IGate and Weather Station near Huddersfield. +2026-03-02 15:49:28 CET: EA4KM-B>APDG02,TCPIP*,qAC,EA4KM-BS:!4013.22ND00351.04W&RNG0001/A=000010 70cm Voice (D-Star) 438.62500MHz -7.6000MHz +2026-03-02 15:49:28 CET: FW0217>APN000,TCPXX*,qAX,CWOP-5:@021449z4241.64N/07150.94W_.../...g...t017r000p000P000b10357h30eMB54 +2026-03-02 15:49:28 CET: W7DJK-5>APT314,WIDE1-1,WIDE2-1,qAR,N7XAO:/144930h4434.85N/12317.97W>071/000/A=000482/TinyTrak3|!.&D'R|!wa>! +2026-03-02 15:49:28 CET: HS6LU-11>AESPT4,TCPIP*,qAC,T2HAKATA:!1647.81NP10114.32E>176/000/A=000488 Vin:12.40V. SAT:22 Topspeed:7kmh DX:8km Uptime:206:36 +2026-03-02 15:49:28 CET: BD7KHW-10>APLG01,TCPIP*,qAC,T2FRANCE:!2440.26N/11335.23ErLORA APRS 433.775 & BD7KHW <0xe9><0x9f><0xb6><0xe5><0x85><0xb3><0xe6><0xac><0xa2><0xe8><0xbf><0x8e><0xe4><0xbd><0xa0> BAT: 3.3 +2026-03-02 15:49:28 CET: CQ0XSM-11>APOTW1,CQ0XBF-3*,WIDE3-2,qAR,CT2IRK-11:> 13.2V Meteo Semideiro - Amsat-Po - 144.900 Mhz +2026-03-02 15:49:27 CET: CR7BQX-8>APLRG1,TCPIP*,qAC,T2DENMARK:!L:0"[LAQ^a GLoRa APRS test|!.%_| +2026-03-02 15:49:27 CET: AD4FM-3>SUPV2U,qAR,AK4ZX-10:`q0Ynpx>/`"80}mon 144.700 winlink_4 +2026-03-02 15:49:27 CET: YG1BYD>AESPG4,TCPIP*,qAC,T2TOKYO:@021449z0636.83S/10648.69E_000/000g000t082r...p...P...h81b09800L000 Wx Station YG1BYD v1.0 T=27<0xc2><0xb0> H=81% P=980 +2026-03-02 15:49:27 CET: KE0CGR>APU25N,TCPIP*,qAC,T2MCI:=3853.73N\09442.56WL {UIV32N} +2026-03-02 15:49:27 CET: AD7MR-15>APDR17,WA7DRE-11,WIDE1*,WIDE2-1,qAR,WA7DRE:=4801.77N/11632.45WE APRSDroid, UV-PRO +2026-03-02 15:49:27 CET: HB9TMR-9>HB9TMR-9,WIDE1-1,WIDE2-1,qAR,OE9XVI-6:!4714.23N/00927.68E[285/000/A=001605HB9TMR-9 +2026-03-02 15:49:27 CET: BH6-1>APE32A,WIDE1-1,qAR,BH6MKM-11:>https://github.com/nakhonthai/ESP32APRS_Audio +2026-03-02 15:49:27 CET: WINLINK>APWL2K,TCPIP*,qAS,WLNK-1:;KD0IVV-10*020249z4103. NW09547. Wa145.090MHz Winlink Packet Gateway +2026-03-02 15:49:27 CET: 9W2KHE-3>APLRG1,TCPIP*,qAC,T2FINLAND:!LMa'hhar=_ !G.../...g...t085h..b10127Bandar Dato' Onn LoRa iGate 433.400MHz +2026-03-02 15:49:27 CET: N0KFB-11>T5PT3Q,WIDE1-1,qAR,W0ANA-15:`y*hm]R>/"6{}Out runnin' 'round / Mon 444.750,146.58,14].52 +2026-03-02 15:49:27 CET: KC7O-9>APT311,N6EX-4*,qAR,KELLER:!3409.44N/11809.07W>057/000/A=000931 +2026-03-02 15:49:27 CET: WH6CDU-N>APDG03,TCPIP*,qAC,WH6CDU-NS:!4015.00ND08604.20W&/A=0000002m MMDVM Voice (C4FM) 145.65000MHz +0.0000MHz, WH6CDU_Pi-Star_ND +2026-03-02 15:49:27 CET: VE7KGV-7>TYQTUS,FROMME,WIDE1*,WIDE2-1,qAR,AF7DX-1:`4[koq/[/`"4h}_0 +2026-03-02 15:49:27 CET: SA6BXE-2>APMI06,TCPIP*,qAC,T2SWEDEN:T#171,184,000,000,000,000,00000000 +2026-03-02 15:49:27 CET: VE1YAR>APNU19,WIDE3-3,qAR,VE1GU-2:!4356.67NS06601.32W#PHG5460/Yarmouth,NS MARCAN UIDIGI YARC +2026-03-02 15:49:28 CET: SR5SAN>APRX29,DL2-2,qAR,SR5ZOC-1:;438.837OC*111111z5211.14N/02246.90Er438.837MHz<0xc2><0xa0>-760<0xc2><0xa0>R60k<0xc2><0xa0>SR8DMR<0xc2><0xa0>Chotycze +2026-03-02 15:49:28 CET: BG6STN-7>APRS,TCPIP*,qAC,T2VAN:@021449z3026.72N/11131.94EOPHG5920 <0xe4><0xb8><0xbb><0xe9><0xa1><0xb5>:https://www.bg6stn.top , <0xe8><0xbf><0x90><0xe8><0xa1><0x8c><0xe6><0x97><0xb6><0xe9><0x97><0xb4>181<0xe5><0xa4><0xa9>23<0xe6><0x97><0xb6>0<0xe5><0x88><0x86> <0xe9><0x80><0x9f><0xe5><0xba><0xa6>133.0km/h <0xe6><0x80><0xbb><0xe9><0x87><0x8c><0xe7><0xa8><0x8b>355187.6km +2026-03-02 15:49:28 CET: OE3XVI-S>APDG01,TCPIP*,qAC,OE3XVI-GS:;OE3XVI B *021449z4756.28ND01549.12EaRNG0031 440 Voice 438.27500MHz -7.6000MHz +2026-03-02 15:49:28 CET: DG5GSA-B>APLRG1,TCPIP*,qAC,T2UKRAINE:=4817.28NL00749.18E&Lora I-Gate an X30 Antenne + VV + Bandpass, Solar Powerd +2026-03-02 15:49:28 CET: CW5988>APRS,TCPXX*,qAX,CWOP-5:@021449z3745.58N/12225.90W_276/004g009t056r000p007P007h87b10176L009.DsVP +2026-03-02 15:49:28 CET: EL-F5LCT>RXTLM-1,TCPIP,qAR,F5LCT::EL-F5LCT :UNIT.RX Erlang,TX Erlang,RXcount/10m,TXcount/10m,none1,STxxxxxx,logic +2026-03-02 15:49:28 CET: BD4KM-10>APET51,TCPIP*,qAC,T2FUKUOKA:!3730.66N/12203.05Erweihai igate 144.640MHz BR4IW 439.900 -5 88.5 8.5V +2026-03-02 15:49:28 CET: EL-F5LCT>RXTLM-1,TCPIP,qAR,F5LCT:T#232,0.00,0.00,0,1,0.0,00000000,SimplexLogic +2026-03-02 15:49:28 CET: EL-F5LCT>RXTLM-1,TCPIP,qAR,F5LCT:T#233,0.00,0.00,0,1,0.0,00000000,SimplexLogic2 +2026-03-02 15:49:28 CET: BI1NGG>APIN20,TCPIP*,qAC,T2YANTAI:!4024.48N/11728.79Er[QSO:439.600MHz -8.0MHz TSQ:100.0] +2026-03-02 15:49:28 CET: AMITY>APRS,TCPXX*,qAX,CWOP-5:@021449z3024.00N/09106.00W_030/001g004t064r000p000P000h87b10215.DsVP +2026-03-02 15:49:28 CET: EA1MNB-9>APLRT1,WIDE1-1,WIDE2-1,qAR,EC1AME-10:>https://github.com/richonguzman/LoRa_APRS_Tracker 2024.10.11 +2026-03-02 15:49:28 CET: JP1YJX-I>APIRP2,TCPIP*,qAC,JP1YJX-IS:!3524.64ND13924.17E&EBINA -> APRS +2026-03-02 15:49:28 CET: CE4TQY-7>APLRT1,WIDE1-1,qAR,CE4TQY-10:=/_g:JBEACON,qAR,VE7SST-2:>146.540s*111111x5602.75N/12207.88WrT100 VA7RHH +2026-03-02 15:49:26 CET: N0VYE-B>APDG02,TCPIP*,qAC,N0VYE-BS:!3910.85ND09154.61W&RNG0001/A=000010 70cm Voice (D-Star) 433.00000MHz +0.0000MHz +2026-03-02 15:49:26 CET: IW3BTS-2>APBM1D,IW3BTS,DMR*,qAR,IW3BTS:=4629.72N/01119.11Ef000/002/A=001012Paolo BTS VF BZ +2026-03-02 15:49:26 CET: W4VA-13>APMI06,KN4IJF-1,WC4J,WIDE2*,qAR,KG4GIY:T#003,170,095,030,057,25693,00000000 +2026-03-02 15:49:27 CET: K3TI-S>APJIO4,TCPIP*,qAC,K3TI-GS:;K3TI B *071248z4028.17ND07600.84WaRNG0040 440 Voice 445.550 -5.00 MHz +2026-03-02 15:49:27 CET: K3TI-S>APJIO4,TCPIP*,qAC,K3TI-GS:;K3TI C *071248z4028.17ND07600.84WaRNG0040 2m Voice 145.340 -0.600 MHz +2026-03-02 15:49:27 CET: E27ASY-1>APRX29,TCPIP*,qAC,T2OSAKA:!1445.26NR10043.99EaPHG3270 Rx-only iGate +2026-03-02 15:49:27 CET: YN1ZHG-10>APHPIB,TCPIP*,qAC,T2DENMARK:>QRG: 438.710MHz CTCSS: 123Hz @ 60W +2026-03-02 15:49:27 CET: YN1ZHG-10>APHPIB,TCPIP*,qAC,T2DENMARK:!1206.99N/08617.05WnUHF EchoLink Node YN1JLX-L | MGA +2026-03-02 15:49:27 CET: EB1GEM-3>APMI06,ESAS2-2,qAR,ED1ZAE-3:@021449z4330.01N/00536.07W-WX3in1Plus2.0 U=13.3V,T=??.?C/??.?F +2026-03-02 15:49:27 CET: 9A3HVZ>APDR16,TCPIP*,qAC,T2ERFURT:=4602.41N/01603.42EQ252/050/A=000643 MOBILE DMR TG:21966 +2026-03-02 15:49:27 CET: YB1HBO-5>APDR16,TCPIP*,qAC,T2SWEDEN:=0719.23S/11031.70Ew319/052/A=002060 https://aprsdroid.org/ +2026-03-02 15:49:27 CET: BD5DOY-12>APGPRS,TCPIP*,qAC,T2CS:@144909h1519.54S/02810.43Ef082/000/A=003768 YiWu CHINA TOYOTA 11.35V 31.0C +2026-03-02 15:49:27 CET: KE8MLF-S>APDG01,TCPIP*,qAC,KE8MLF-GS:;KE8MLF B *021449z4530.00ND08200.00WaRNG0001 440 Voice 438.80000MHz +0.0000MHz +2026-03-02 15:49:27 CET: KE9FZ-1>TQSP6X,qAR,KC9ZMY-2:`s5wl4%-/`"6@}_4 +2026-03-02 15:49:27 CET: VE3ZKA-B>APDG02,TCPIP*,qAC,VE3ZKA-BS:!4622.66ND08102.80W&RNG0001/A=000010 70cm Voice (D-Star) 445.60000MHz +0.0000MHz +2026-03-02 15:49:27 CET: SR2PP>APRS,TCPIP*,qAC,SIXTH:;ER-SR2PP *111111z5249.41NE01802.63E0145.700MHz T074 R22k **Doswietlenie sieci KPSR**_Kujawsko-Pomorska Siec Radiowa_w sieci SR2J,SR2JJ,SR2GG,SR2VV,SR2PP +2026-03-02 15:49:27 CET: K9RCZ-3>APLOX1,TCPIP*,qAC,T2TEXAS:!L9/3c80/1# XLora Digi/iGate <-> www.paaros.com/lora P=4.53V +2026-03-02 15:49:27 CET: JH4GQC>APU25N,TCPIP*,qAC,T2FUKUOKA:;EL-917416*311623z3415.46NE13231.55E0JH4GQC-L (Linked to this Wires-X) +2026-03-02 15:49:27 CET: YM9KE>AESPG4,TCPIP*,qAC,APRSTH:!3946.85N/04113.58E#145.700 88.5 TRAC ERZURUM +2026-03-02 15:49:27 CET: GW5031>APN000,TCPXX*,qAX,CWOP-7:@021449z3302.70N/09640.65W_052/000g003t065r000p000P000b10206h81L055eMB63 +2026-03-02 15:49:27 CET: K5MPH>APBK15,WIDE1-1,WIDE2-1,qAR,KC5MAH-3:!2555.09N/09730.00WIIgate/Digi +2026-03-02 15:49:27 CET: DW1973>APRS,TCPXX*,qAX,CWOP-6:@021449z4145.98N/08803.55W_017/007g013t031r000p000P000h51b10296.DsVP +2026-03-02 15:49:28 CET: SADDLE>APMI06,TCPIP*,qAS,K7RPT-10:@021449z4532.71NI12322.93W#PHG52604/Digipeater & iGate | K7RPT +2026-03-02 15:49:28 CET: VY1XY>APRS,TCPXX*,qAX,CWOP-7:@021449z6029.63N/13451.68W_054/000g001t-31r000p000P000h78b09982eCumulusDsVP +2026-03-02 15:49:28 CET: TA5ADB>APDR16,TCPIP*,qAC,T2KA:=3706.39N/03722.90Ee359/000/144.850MHz/A=003057 73!... +2026-03-02 15:49:28 CET: K0WXC-1>APRS,TCPIP*,qAC,T2SPAIN:@021445z4008.96N/09237.79W_c089s003g004t031r000P000h94b10123 +2026-03-02 15:49:28 CET: BR3QQ>APLM3D,TCPIP*,qAC,T2NANJING:!3954.10N/11934.59E&QHDRA 144.640MHz IGATE DIGI Voice BR3QQ 439.800-8 T88.5 14.2V +2026-03-02 15:49:28 CET: 9A3HVZ>APDR16,TCPIP*,qAC,T2ERFURT:=4602.41N/01603.40EQ252/051/A=000646 MOBILE DMR TG:21966 +2026-03-02 15:49:28 CET: BG4GNW>APN000,TCPIP*,qAC,T2HAKATA:=3043.54NW12119.71E_ +2026-03-02 15:49:28 CET: DL2AKT-20>APLRG1,TCPIP*,qAC,T2ERFURT:!L4gWgQAPNU3B,WIDE3-3,qAR,9A3XK:!4520.12N/01740.60E#PHG2230/APRS Pozega * JN85UI * UIDIGI1.93B (1) +2026-03-02 15:49:26 CET: KE7EX-12>KE7EX-10,WIDE2-2,qAR,KE7EX-10:@021443z3845.73N\07732.42WSHF A1A/F1D QRP Laser Keyed CW/WSPR (30m) U=13.5V +2026-03-02 15:49:27 CET: DW4212>APRS,TCPXX*,qAX,CWOP-5:@021449z4052.98N/00335.20W_173/000g006t055r000p000P000h63b10138L492.DsVP +2026-03-02 15:49:27 CET: YM3BUR>APMI06,LOCAL-1,qAR,YM1KTC-11:>TRAC WIDEn-N v1.13 U=13.3V,T=20.4C +2026-03-02 15:49:27 CET: W4IOS-10>APWW11,TCPIP*,qAC,T2DENMARK:>021449zAPRSISCE/32 FTM-300DR @ 33' +2026-03-02 15:49:27 CET: N9PNO-10>APNU19,qAR,KE0NHQ-3:!4231.08NS09037.10W#PHG6300/(N9PNO-10 W3,IAn DUBUQUE UIDIGI 1.9B3)1 +2026-03-02 15:49:27 CET: CE7DE-12>APLRT1,WIDE1-1,qAR,CE7DE-10:=/ctJE;q"`O;nQ +2026-03-02 15:49:27 CET: CW6404>APN000,TCPXX*,qAX,CWOP-5:@021446z3910.06N/07536.04W_111/002g007t032r000p000P000b10359h55L232eMB54 +2026-03-02 15:49:27 CET: IZ8FAV>APRS,TCPIP*,qAC,IQ2ARI-AS:;EL-IZ8FAV*111111z4048.83NE01421.46E0433.275MHz T071 R03m Svxlink Toni: 71.9-TG83 , 77.0-TG41100 , 88.5-TG222 , 123.0-TG32 , 141.0-TG41 , 67.0-TG61 , 131.8-TG21, 136.5-TG81, 107.2-TG82, 206.5-TG22290,167.9-TG97100 +2026-03-02 15:49:27 CET: KN4PLO>APDW17,WIDE1-1,qAR,WX4BK-13:!3334.17ND08504.49W#PHG5230146.640MHz T131 -060 /A=001280Tanner-Carrollton IG/Digi/WX +2026-03-02 15:49:27 CET: TB1DVM-7>APAT51,YM3BUR,WIDE1*,WIDE2-1,qAR,YM1KTC-11:!4057.94N/02839.61Ey000/000/A=000196OP.MURATHAN 0532 348 3764 +2026-03-02 15:49:27 CET: EL-F4ELA>RXTLM-1,TCPIP,qAR,F4ELA::EL-F4ELA :UNIT.RX Erlang,TX Erlang,RXcount/10m,TXcount/10m,none1,STxxxxxx,logic +2026-03-02 15:49:27 CET: DD7VM-7>APOSB4,TCPIP*,qAS,DD7VM:@021449z4919.56N/00720.23E[/A=000833SharkRF openSPOT4 +2026-03-02 15:49:27 CET: EL-F4ELA>RXTLM-1,TCPIP,qAR,F4ELA:T#124,0.00,0.02,0,3,0.0,00000000,SimplexLogic +2026-03-02 15:49:27 CET: WLNK-1>APWLK,TCPIP*,qAC,T2CAWEST::AD4FM-3 :You have 1 Winlink mail messages pending{7953 +2026-03-02 15:49:28 CET: EW9467>APRS,TCPXX*,qAX,CWOP-7:@021449z3322.83N/08427.83E_155/000g001t066r000p000P000b10237h56eMB36 +2026-03-02 15:49:28 CET: FW0403>APRS,TCPXX*,qAX,CWOP-4:@021449z2912.16N/09456.06W_098/008g012t068r000p002P002h89b10191L053WeatherCatV241B14H31 +2026-03-02 15:49:28 CET: NE3Z>APWW10,WIDE1-1,WIDE2-1,qAR,W4KEL-12:@144926h3816.80NR07732.62W&PHG3230Hello! +2026-03-02 15:49:28 CET: DO0RDG>APMI06,WIDE2-2,qAO,DG6HD-1:T#151,181,183,00000,00000,000,00000000 +2026-03-02 15:49:28 CET: KD3BSA-7>T0RU9W,KB3FCZ-2,WIDE1*,WIDE2-1,qAR,W5RTM-2:`kI*<0x1c>DmR/"7R}RADTEL950PRO +2026-03-02 15:49:28 CET: GW7041>APREST,TCPXX*,qAX,CWOP-3:@021449z3713.13N/09350.64W_069/003g004t038r000P019h97b10244L037eREST +2026-03-02 15:49:28 CET: HK4RAU-60>HK4DAP,TCPIP*,qAC,T2CAEAST:>Estacion Meterologica APRS con SCRIP Python, Raspberry Pi,ESP32 y Sensor BMP280 por HK4DAP +2026-03-02 15:49:28 CET: HK4RAU-60>HK4DAP,TCPIP*,qAC,T2CAEAST:@021449z0425.51N/07508.44W_000/004g000t069r000p000h69b10140ESTACION DEL CLIMA APRS ZONA 6 RADIOAFICIONADOS UNIDOS 146.520 MHz en Simplex Fonia - Reporte del clima en Ibagu<0xc3><0xa9>: nubes dispersas +2026-03-02 15:49:28 CET: DL5BTE-9>APRS,WIDE2-1,qAR,DB0PCH:!5335.88N/01126.02E>Speed=13km/h; VDD=8.57V; QRV: +2026-03-02 15:49:28 CET: EW2817>APN000,TCPXX*,qAX,CWOP-7:@021449z3559.64N/07853.94W_000/000g000t042r000p000P000b10087h71eMB63 +2026-03-02 15:49:28 CET: DO1BJ-14>APLC13,qAR,DB0BHV-L4:!/3J/TP^G'u1TQLoRa-System-Hannes QRL im Lkw +2026-03-02 15:49:28 CET: DM5HR-9>APLRT1,WIDE1-1,qAR,DL7JP-10:!/5w#mQppj>GUQ +2026-03-02 15:49:28 CET: DW5411>APRS,TCPXX*,qAX,CWOP-7:!3327.90N/08620.70W_135/002g007t063r000P000p000h71b10248.VWS-DavisVP2+ +2026-03-02 15:49:28 CET: 9M4GKJ-10>AESPG4,TCPIP*,qAC,DMRNET01:>V.4.4c build May 14 2023 20:28:54 Rx:107 Digi:60 Tx:56 UpTime:04.38 DX:02-03-2026 18:28Loc 9M4RJR-3 40km 6<0xc2><0xb0> +2026-03-02 15:49:28 CET: F1ZIB>APRS,TCPIP*,qAC,T2UK:;ER-F1ZIB *111111z4930.63NE00009.73E0432.800MHz T250 R20k 145.2375Mhz T:250.3Hz LH/F1ZIB N:385525 +2026-03-02 15:49:28 CET: BH4EAW-10>APN000,WIDE1-1,WIDE2-1,qAS,BG4GTL-10:=3116.97NZ12130.29E&000/000/A=000160BH4EAW-Digi 13.86V +2026-03-02 15:49:28 CET: WB5NFC-3>APBPQ1,TCPIP*,qAC,T2MCI:APDG02,TCPIP*,qAC,AA2QU-BS:!4436.06NW07332.29WiRNG0001/A=000010 70cm Voice (D-Star) 438.80000MHz +0.0000MHz, APRS for ircDDBGateway +2026-03-02 15:49:28 CET: AA2QU-B>APDG02,qAS,AA2QU:>Powered by WPSD (https://wpsd.radio) +2026-03-02 15:49:28 CET: BD4SDX-10>APET51,WIDE1-1,WIDE2-1,qAS,BY4SA-10:!3054.31N/12039.41Er WuJiang BD4SDX APRS iGate 144.640Mhz 13.7V +2026-03-02 15:49:28 CET: ER-SK7RGI>APSVX1,WIDE1-1,WIDE2-2,qAS,SK7RGI:T#602,000,000,000,000,000,00000000 +2026-03-02 15:49:28 CET: F1ZGQ-3>APFD29,F1ZBH-3*,qAR,F6ZAU-3:!4821.38N/00404.89W# DIGI F1ZGQ-3 ***ADRASEC-29*** +2026-03-02 15:49:28 CET: DH1SP-20>APRSGW,TCPIP*,qAR,DH1SP-20:T#075,999.1,27.8,37.1,1020.2,0.0,10000000 +2026-03-02 15:49:28 CET: DH1SP-20>APRSGW,TCPIP*,qAR,DH1SP-20:!4907.10N/00912.61E#/B=100/A=000656 +2026-03-02 15:49:28 CET: E27IHO-13>AESPG4,TCPIP*,qAC,CWOP-3:T#014,18,25,26,61,0.33,00000000,@ 14 49 27 2 3 2026 +2026-03-02 15:49:28 CET: ZR1RTG-8>APCLEY,TCPIP*,qAC,APRS-ZA:/021449z3356.20S/01853.41Ek000/000/A=000658 42C 1Mv 4.19V In 13.12V 9kmh +2026-03-02 15:49:28 CET: GW1972>APN000,TCPXX*,qAX,CWOP-3:@021449z4130.22N/08724.99W_296/005g011t030r000p000P000b10062h62eMB63 +2026-03-02 15:49:28 CET: PU4BNL-1>APE32I,TCPIP*,qAC,T2BRAZIL:!1944.80SI04758.44W&PHG5060/A=002656 IGate MODE - UBERABA-MG +2026-03-02 15:49:28 CET: M7OMH-15>APWW11,TCPIP*,qAC,T2SYDNEY:>IO93cr/#DX: G7UKK-10 11.0mi 167<0xb0> 14:49 5335.18N 00142.48W +2026-03-02 15:49:28 CET: EA1HMK-7>APLRT1,WIDE1-1,qAO,EA1HDC-10:=/9+f?L=*->;.Q +2026-03-02 15:49:28 CET: BD7IIT>APBM1D,DMR*,qAS,BM4602-10:=2301.73N/11307.26E[360/000/A=-00098Kayu Pan +2026-03-02 15:49:28 CET: HB9FM-4>APMI06,TCPIP*,qAC,T2SWEDEN:T#133,186,071,025,079,074,00000000 +2026-03-02 15:49:28 CET: OH7UDH>APAT51,WIDE2-1,qAR,OH5RBG:!6017.90N/02456.10E>192/000/A=000216Paranoidi Pessimisti +2026-03-02 15:49:28 CET: OH9RDA>APZMDR,TRACE1-1,qAR,SK2HG-10:!6541.26N/02442.00E# +2026-03-02 15:49:28 CET: VA2BBW-2>APDW19,qAR,VA2BBW-2:;VA2BBW-13*010000z4529.72N/07535.36W__87/001g002t007r000P000b10490h54 +2026-03-02 15:49:28 CET: LB1HJ>APBM1D,LD9HR,DMR*,qAR,LD9HR:@144926h6848.69N/01630.36E-010/000Trond +2026-03-02 15:49:28 CET: CX1AAO-Y>APDG03,TCPIP*,qAC,CX1AAO-YS:!3450.15SW05518.09Wi/A=00000070cm MMDVM Voice (C4FM) 434.60000MHz +5.0000MHz, APRS for YSFGateway +2026-03-02 15:49:28 CET: CX1AAO-Y>APDG03,qAS,CX1AAO:>Powered by WPSD (https://wpsd.radio) +2026-03-02 15:49:28 CET: BG3RQT-9>APTNC0,qAS,BG3RQT:=4000.77N/11621.83E[232/000/A=000304 ESP32 Tracker TEST <0xe5><0x8d><0xab><0xe6><0x98><0x9f><0xe6><0x95><0xb0><0xe9><0x87><0x8f>:10 | <0xe7><0xb4><0xaf><0xe8><0xae><0xa1><0xe8><0xb7><0x9d><0xe7><0xa6><0xbb>:0.72km +2026-03-02 15:49:28 CET: F5STO-S>APDG01,TCPIP*,qAC,F5STO-GS:;F5STO B *021449z4831.75ND00644.57EaRNG0025/A=000820 70cm Voice (D-Star) 433.48750MHz +0.0000MHz +2026-03-02 15:49:28 CET: NP4GT>APDW17,TCPIP*,qAC,T2PR:!1817.66N/06707.10W-/A=000395 NP4GT > http://www.np4gt.net > FK68kh | Anasco, PR 00610 +2026-03-02 15:49:28 CET: NP4GT>APDW17,TCPIP*,qAC,T2PR:;NP4GT-R *111111z1817.66N/06707.10WrPHG9390 449.975MHz Multimode DMR-RPT FK68kh | Anasco, PR 00610 > NP4GT > http://www.np4gt.net +2026-03-02 15:49:29 CET: IW6ATQ-8>BEACON,WIDE3-3,IQ6KX-10,qAR,IZ6DDQ-1:=4335.62N\01331.11ES - info www.iw6atq.net +2026-03-02 15:49:29 CET: PE2KMV-S>APDG01,TCPIP*,qAC,PE2KMV-GS:;PE2KMV B *021449z5052.06ND00550.43EaRNG0001/A=000010 70cm Voice (D-Star) 439.85000MHz -9.4000MHz +2026-03-02 15:49:29 CET: SP8MB-L>APLRFD,TCPIP*,qAC,T2PRT:!L4Wi3T6)F# GLora iGate/DIGI 434.855 @1k2 Batt=4.92V|'s%^!!!!!!!!| +2026-03-02 15:49:28 CET: JP1YJX-I>APIRP2,TCPIP*,qAC,JP1YJX-IS:APSVX1,WIDE1-1,qAS,W8PAR:T#103,012,000,001,000,000,00000000 +2026-03-02 15:49:28 CET: BA4IIK>APN000,TCPIP*,qAC,T2OSAKA:=3654.89N/12132.06E)4.0V +2026-03-02 15:49:28 CET: PK10XH-12>APN000,TCPIP*,qAC,T2JKTP:=1019.67N/12355.83Exradio fault +2026-03-02 15:49:28 CET: W5IC>APN000,TCPXX*,qAX,CWOP-3:@021444z3024.24N/09105.08W_338/000g003t063r000p000P000b10212h92L378eMB62 +2026-03-02 15:49:28 CET: AL7PB>APRS,TCPXX*,qAX,CWOP-6:@021449z3225.88N/09937.95W_000/000g000t050r000p000P000h78b10177eCumulusDsVP +2026-03-02 15:49:28 CET: HS7BHK-15>APAT81,WIDE1-1,WIDE2-1,qAR,HS7BHK-1:!1356.49N/10044.11E[077/000/A=000000HS7BHK D878UV +2026-03-02 15:49:28 CET: PC1RZ-7>APN000,WIDE2-1,qAR,PD5A-5:=5212.16N/00559.34E>055/000/A=000082 +2026-03-02 15:49:28 CET: N2ASU-N>APDG03,TCPIP*,qAC,N2ASU-NS:!3950.07ND07500.75W&/A=00000070cm MMDVM Voice (C4FM) 433.45000MHz +0.0000MHz, N2ASU_Pi-Star_ND +2026-03-02 15:49:28 CET: HS5SQI-4>AESPG4,TCPIP*,qAC,APRSTH::HS5SQI-4 :PARM.RxTraffic,TxTraffic,RxDrop,RSSI,Voltage +2026-03-02 15:49:28 CET: 9A0DVR>APBM1S,TCPIP*,qAS,BM2222:@021449z4522.88N/01500.87ErPHG0000Solarni DMR/FM repetitor 438.9875Mhz -7.6Mhz (173.8) i C4FM hotspot 433.650Mhz 438.9875/431.3875 CC1 +2026-03-02 15:49:28 CET: FW3211>APN000,TCPXX*,qAX,CWOP-6:@021449z4421.49N/00917.10E_264/001g003t054r000p006P006b10247h70eMB63 +2026-03-02 15:49:28 CET: ON6ZQ>APN100,TCPIP*,qAC,SIXTH:;I/VE-163 *021449z4635.72NS01236.82E;SOTA activ. planned on 20260302 1630z +2026-03-02 15:49:28 CET: DW8362>APRS,TCPXX*,qAX,CWOP-6:@021449z3914.33N/07730.43W_321/000g004t032r000p000P000b10353h62eMB35 +2026-03-02 15:49:28 CET: 9A0DVR>APBM1S,TCPIP*,qAS,BM2222:>https://brandmeister.network/?page=repeater&id=219015 +2026-03-02 15:49:29 CET: OK5TVR-8>APLRT1,OK1CTR-10*,qAS,OK1RPL-15:!/5?r.QoMC>G;Q DR:-119 DS:-0.50 +2026-03-02 15:49:29 CET: WINLINK>APWL2K,TCPIP*,qAS,WLNK-1:;KD0SFY *020249z3853. NW10447. Wa441.050MHz Winlink VARA FM Wide Gateway +2026-03-02 15:49:29 CET: BX2ADJ-2>APVRT7,TCPIP*,qAC,T2FUKUOKA:!2500.63N/12128.06Er 8.9V +31.6C 1012hPa +2026-03-02 15:49:29 CET: KF5JED-13>APRS,TCPIP*,qAC,AMBCWOP-2:@021449z3500.51N/10151.92W_047/004g005t042r000p000P000h62b10196L060AmbientCWOP.com +2026-03-02 15:49:29 CET: F1OLZ-7>APDR16,TCPIP*,qAC,T2SWEDEN:=4819.3 N/00340.8 W$161/002/145.625MHz/A=000466 https://aprsdro +2026-03-02 15:49:29 CET: VE2GWD-D>APDG03,TCPIP*,qAC,VE2GWD-DS:!4524.00ND07324.00W&/A=00000070cm MMDVM Voice (DMR) 446.50000MHz +0.0000MHz, APRS for DMRGateway +2026-03-02 15:49:29 CET: N7FAN-14>APDR17,TCPIP*,qAC,T2PANAMA:=3933.88N/10607.65Wu043/059/A=009176 n7fan@n7fan.com +2026-03-02 15:49:29 CET: FW9374>APRS,TCPXX*,qAX,AMBCWOP-1:@021449z3505.12N/08051.24W_055/000g000t048r000p000P000h70b10308L000AmbientCWOP.com +2026-03-02 15:49:29 CET: GW1603>APRS,TCPXX*,qAX,AMBCWOP-1:@021449z3656.69N/09044.93W_074/004g008t039r000p021P021h99b10229L083AmbientCWOP.com +2026-03-02 15:49:29 CET: DB0KLE-10>APLOX1,TCPIP*,qAC,T2ERFURT:!5138.59NL00618.96E&iGate Relais Uedemerbruch Batt=5.33V, +2026-03-02 15:49:29 CET: EL-E75BM>RXTLM-1,TCPIP,qAR,E75BM::EL-E75BM :UNIT.RX Erlang,TX Erlang,RXcount/10m,TXcount/10m,none1,STxxxxxx,logic +2026-03-02 15:49:29 CET: DL2NEQ-1>APRSGW,TCPIP*,qAR,DL2NEQ-1:!4932.21N/01046.39E#/B=100/A=001427 +2026-03-02 15:49:29 CET: AA6CD-15>APWW11,TCPIP*,qAC,T2DENMARK:@144926h3358.54N\11804.10W?Whittier Cam & Seismograph Station: http://whittiercam.ddns.net/ +2026-03-02 15:49:29 CET: KE4EST>APRS,TCPIP*,qAC,AMBCWOP-2:@021449z3506.61N/08406.13W_101/001g001t055r000p000P000h71b10256L088AmbientCWOP.com +2026-03-02 15:49:29 CET: XQ6AK-15>APRS,TCPIP*,qAC,AMBCWOP-2:@021449z4042.34S/07301.11W_006/000g002t058r000p001P001h91b10095L122AmbientCWOP.com +2026-03-02 15:49:28 CET: KC5EZZ-2>APTW14,WIDE1-1,WIDE2-2,qAR,KC5EZZ:_01051417c359s000g000t065r000p000P000h..b.....tU2k +2026-03-02 15:49:28 CET: KJ6EUC-B>APDG02,TCPIP*,qAC,KJ6EUC-BS:!3438.60ND11815.32W&RNG0001/A=000010 70cm Voice (D-Star) 440.64000MHz +5.0000MHz +2026-03-02 15:49:28 CET: ER-OE9XVD>RXTLM-1,TCPIP,qAR,OE9XVD::ER-OE9XVD:UNIT.RX Erlang,TX Erlang,RXcount/10m,TXcount/10m,none1,STxxxxxx,logic +2026-03-02 15:49:28 CET: ER-OE9XVD>RXTLM-1,TCPIP,qAR,OE9XVD:T#076,0.00,0.00,0,0,0.0,00000000,TetraLogic +2026-03-02 15:49:28 CET: N2KOJ-4>APBM1D,N2KOJ,DMR*,qAR,N2KOJ:@144926h4105.88N/07507.43W4144/000King of Jersey-2 +2026-03-02 15:49:28 CET: AJ4FJ-5>BEACON,qAO,K2ATL-1:;147.015PL*111111z3419.31N/08438.20Wr147.015MHz T100 + R45m Pine Log +2026-03-02 15:49:28 CET: SP5RC-9>URQW49,WIDE1-1,qAR,SR5NWA:`0W}oqr>/]"4l}145.500MHz , VA 136,5 Hz, op. PAWEL = +2026-03-02 15:49:28 CET: KM7CSF>SQSTRQ,WIDE1-1,qAR,W7BAN:`&/Pl@.>/`"CP}_5 +2026-03-02 15:49:28 CET: G6JEF-B>APDG02,TCPIP*,qAC,G6JEF-BS:!5405.24ND00012.07W&RNG0001/A=000010 70cm Voice (D-Star) 438.80000MHz +0.0000MHz +2026-03-02 15:49:28 CET: DB0WBD-10>APMI04,WIDE2-2,qAR,HB9FLD-11:T#014,179,044,002,057,000,00000000 +2026-03-02 15:49:29 CET: K6XLT-12>T3SVRR,SHAFER,WIDE1*,WIDE2-1,qAR,K7RYH-2:`,'5l <0x1c>v/'"='}|$g%j'M|!w^?!|3 +2026-03-02 15:49:29 CET: SR5SAN-2>APRX29,DL2-2,qAR,SQ5LS-2:;438.837OC*111111z5211.14N/02246.90Er438.837MHz<0xc2><0xa0>-760<0xc2><0xa0>R60k<0xc2><0xa0>SR8DMR<0xc2><0xa0>Chotycze +2026-03-02 15:49:29 CET: W9WRP>APRS,TCPIP*,qAC,AMBCWOP-2:@021449z3345.58N/11230.65W_292/002g002t060r000p000P000h34b10161L026AmbientCWOP.com +2026-03-02 15:49:29 CET: NP4KB>APRS,TCPIP*,qAC,AMBCWOP-2:@021449z1819.13N/06539.64W_117/000g001t...r000p000P000h...b10212L702AmbientCWOP.com +2026-03-02 15:49:29 CET: KN6RBP-Y>APDG03,TCPIP*,qAC,KN6RBP-YS:!3344.52N/11656.75WM/A=00000070cm MMDVM Voice (C4FM) 445.00000MHz -5.0000MHz, APRS for YSFGateway +2026-03-02 15:49:29 CET: KN6RBP-Y>APDG03,qAS,KN6RBP:>Powered by WPSD (https://wpsd.radio) +2026-03-02 15:49:29 CET: HS6TXN>AESPT4,TCPIP*,qAC,T2HK:!1554.85N/10101.56E-000/000/A=000334 Satellite 09 https://sites.google.com/view/pptxn +2026-03-02 15:49:29 CET: F5ZZ-10>APMI06,TCPIP*,qAC,T2FRANCE:@021449z4354.80N/00202.86E-WX3in1Plus2.0 ADRASEC 09 +2026-03-02 15:49:29 CET: IR3UFF-3>APDG03,TCPIP*,qAC,IR3UFF-3S:!4628.60ND01331.21E&/A=00196870cm MMDVM Voice (DMR) 431.41250MHz +1.6000MHz, Multimode Repeater +2026-03-02 15:49:29 CET: GW3738>APRS,TCPXX*,qAX,AMBCWOP-1:@021449z4309.21N/08948.32W_138/001g002t030r000p000P000h66b10281L206AmbientCWOP.com +2026-03-02 15:49:29 CET: IW2FCH-12>APLRT1,WIDE1-1,qAR,IW2FCH-10:=/7LZwQ(lPj@LG|(W%O| +2026-03-02 15:49:29 CET: GW3716>APRS,TCPXX*,qAX,AMBCWOP-1:@021449z3148.02N/09931.53W_141/006g008t061r000p000P000h85b10167L240AmbientCWOP.com +2026-03-02 15:49:29 CET: 9A3HVZ>APDR16,TCPIP*,qAC,T2ERFURT:=4602.40N/01603.38EQ252/051/A=000643 MOBILE DMR TG:21966 +2026-03-02 15:49:29 CET: HB9GYR-10>APMI06,TCPIP*,qAC,T2PRT::HB9GYR-10:PARM.V.in,Rx.1h,Dg.1h,Eff.1h,Temp,O1,O2,O3,O4,I1,I2,I3,I4 +2026-03-02 15:49:29 CET: KD4ROB-N>APDG03,TCPIP*,qAC,T2SPAIN:!3444.29ND08459.48W&/A=000000440 MMDVM Voice 446.30000MHz +0.0000MHz, KD4ROB_Pi-Star +2026-03-02 15:49:29 CET: HB9GYR-10>APMI06,TCPIP*,qAC,T2PRT::HB9GYR-10:UNIT.Volt,Pkt,Pkt,Pcnt,C,On,On,On,On,Hi,Hi,Hi,Hi +2026-03-02 15:49:29 CET: KC1NEZ-R>APDG03,TCPIP*,qAC,KC1NEZ-RS:!4148.01ND07227.32W&/A=00055870cm MMDVM Voice (C4FM) 442.55000MHz +5.0000MHz, KC1NEZ_Pi-Star_RPT +2026-03-02 15:49:29 CET: HB9GYR-10>APMI06,TCPIP*,qAC,T2PRT::HB9GYR-10:EQNS.0,0.075,0,0,10,0,0,10,0,0,1,0,0,0.5,-64 +2026-03-02 15:49:29 CET: KE0LUL-9>T3QP1W,WIDE1-1,qAR,N0JBE:`y_@l^ k/`"65}2012 HONDA RIDGELINE_% +2026-03-02 15:49:29 CET: KI7JYE>APRS,TCPIP*,qAC,AMBCWOP-2:@021449z4518.74N/12256.49W_263/000g000t043r000p000P000h98b10177L000AmbientCWOP.com +2026-03-02 15:49:28 CET: DL9TD-13>APLRT1,WIDE1-1,QA8M9,qAO,DB0LNR-10:=/4Kh7P/?F[90Q +2026-03-02 15:49:28 CET: MM7RRD-D>APDG03,TCPIP*,qAC,MM7RRD-DS:!5451.20N/00402.60Wr/A=00000070cm MMDVM Voice (DMR) 438.66250MHz +0.0000MHz, APRS for DMRGateway +2026-03-02 15:49:28 CET: MM7RRD-D>APDG03,qAS,MM7RRD:>Powered by WPSD (https://wpsd.radio) +2026-03-02 15:49:28 CET: DL2UL-4>APLC13,TCPIP*,qAC,T2CSNGRAD:!5118.51NL00630.86E&LoRa-System +2026-03-02 15:49:28 CET: TA1FL>APMI06,WIDE2-2,qAR,YM2VTC:T#108,170,044,000,078,000,00000000 +2026-03-02 15:49:28 CET: DK4WO-D>APDG03,TCPIP*,qAC,DK4WO-DS:!5159.54ND00738.62E&/A=00000070cm MMDVM Voice (DMR) 433.62500MHz +0.0000MHz, APRS for DMRGateway +2026-03-02 15:49:28 CET: E24XVG-1>APMI06,TCPIP*,qAS,E24XVG:@021449z1252.46N/10135.80E-WX3in1Plus2.0 U=12.2V,T=??.?C/??.?F +2026-03-02 15:49:28 CET: 9W2FRR-1>APLRG1,TCPIP*,qAC,T2TAIWAN:=LLo'th1f,a !GSelayang Utama LoRa APRS I-Gate Batt=3.95V +2026-03-02 15:49:28 CET: DH1LC-9>APOTC1,WIDE1-1,WIDE2-1,qAR,DB0KOE-1:/144927h/4d@JP7%G> st/A=000145 +2026-03-02 15:49:29 CET: DB0TT>APRS,TCPIP*,qAC,T2PRT:;ER-DB0TT *111111z5127.71NE00732.60E0438.650MHz T067 R57k DB0TT +2026-03-02 15:49:29 CET: DL4HAO-10>APMI06,TCPIP*,qAS,DL4HAO:@021449z5343.23N/01000.57E-WX3in1Plus2.0 U=13.1V,T=24.8C/testing digipeating + igate +2026-03-02 15:49:29 CET: JH7ZER-S>APDG01,TCPIP*,qAC,JH7ZER-GS:;JH7ZER B *021449z4047.97ND14048.63EaRNG0001/A=000010 70cm Voice (D-Star) 438.71000MHz +0.0000MHz +2026-03-02 15:49:29 CET: EL-F5KDB>RXTLM-1,TCPIP,qAR,F5KDB::EL-F5KDB :UNIT.RX Erlang,TX Erlang,RXcount/10m,TXcount/10m,none1,STxxxxxx,logic +2026-03-02 15:49:29 CET: SV1BLD-Y>APDG03,TCPIP*,qAC,SV1BLD-YS:!3757.56N/02311.95Er/A=00000070cm MMDVM Voice (C4FM) 433.80000MHz +0.0000MHz, APRS for YSFGateway +2026-03-02 15:49:29 CET: SV1BLD-Y>APDG03,qAS,SV1BLD:>Powered by WPSD (https://wpsd.radio) +2026-03-02 15:49:29 CET: DG7FDL>APSVX1,TCPIP*,qAC,T2TAS:;EL-DG7FDL*111111z5052.25NE00941.71E0PHG2250/145.525MHz toff R13k Bad Hersfeld 256m-NN - Node: 817631 +2026-03-02 15:49:29 CET: EL-F5KDB>RXTLM-1,TCPIP,qAR,F5KDB:T#027,0.00,0.02,0,3,0.0,00000000,SimplexLogic +2026-03-02 15:49:29 CET: GW3591>APRS,TCPXX*,qAX,AMBCWOP-1:@021449z3919.68N/07637.43W_072/002g003t032r000p000P000h47b10393L224AmbientCWOP.com +2026-03-02 15:49:29 CET: KA8ZGE-14>APDR16,TCPIP*,qAC,T2BIO:=4050.97N/08435.58Wu086/001/A=000705 No Ham Equipment +2026-03-02 15:49:29 CET: YM3KB-1>AESPG4,TCPIP*,qAC,T2ROMANIA:T#355,0,3,0,35,11.92,00000000,@ 13 37 35 2 3 2026 +2026-03-02 15:49:29 CET: N5LSN>APRS,TCPIP*,qAC,AMBCWOP-2:@021449z3430.27N/09229.89W_066/002g004t055r000p000P000h75b10213L154AmbientCWOP.com +2026-03-02 15:49:29 CET: KF7RZH-9>SRTUST,KE7JVX-3*,WIDE1,WIDE2-1,qAR,AK7Z-1:`'D6lrSk/`"8y}447.725MHz T100 -500_4 +2026-03-02 15:49:29 CET: VU3KEB-S>APDG01,TCPIP*,qAC,VU3KEB-GS:;VU3KEB B *021449z1253.43ND07732.31EaRNG0001/A=000010 70cm Voice (D-Star) 434.50000MHz +0.0000MHz +2026-03-02 15:49:29 CET: N1ORT>APRS,TCPIP*,qAC,AMBCWOP-2:@021449z4254.74N/07106.94W_094/003g005t017r000p000P000h31b10371L509AmbientCWOP.com +2026-03-02 15:49:29 CET: GW4196>APRS,TCPXX*,qAX,AMBCWOP-1:@021449z4035.23N/07648.99W_069/006g009t024r000p000P000h64b10376L231AmbientCWOP.com +2026-03-02 15:49:29 CET: GW6374>APRS,TCPXX*,qAX,AMBCWOP-1:@021449z4322.37N/07804.75W_040/006g007t019r000p000P000h64b10390L268AmbientCWOP.com +2026-03-02 15:49:29 CET: CW4226>APRS,TCPXX*,qAX,AMBCWOP-1:@021449z4132.24N/07557.31W_057/002g004t020r000p000P000h55b10337L218AmbientCWOP.com +2026-03-02 15:49:29 CET: KM4NDN>APRS,TCPIP*,qAC,AMBCWOP-2:@021449z4122.45N/08247.47W_067/003g008t024r000p000P000h57b10322L323AmbientCWOP.com +2026-03-02 15:49:29 CET: EL-EA3RKD>APSVX1,WIDE1-1,qAS,EA3RKD:T#076,000,017,000,001,000,00000000 +2026-03-02 15:49:29 CET: FW9130>APRS,TCPXX*,qAX,AMBCWOP-1:@021449z4104.49N/07523.34W_119/000g000t017r000p000P000h63b10360L166AmbientCWOP.com +2026-03-02 15:49:29 CET: HS6BUE-3>AESPG4,TCPIP*,qAC,T2TOKYO:=1656.25N/10009.33E_000/000g000t088r...p...P...h45b10030L000 T=31<0xc2><0xb0> H=45% P=1003 PM 1:[13] , 2.5:[22] , 10:[28] (<0xce><0xbc>g/m3) +2026-03-02 15:49:29 CET: GW3666>APRS,TCPXX*,qAX,AMBCWOP-1:@021449z4651.84N/12233.15W_028/000g000t029r000p000P000h99b10183L000AmbientCWOP.com +2026-03-02 15:49:29 CET: LA9TLA>APU25N,LD5AV*,WIDE3-2,qAR,LA8NRA:=5900.30N/00543.30E- {UIV32N} +2026-03-02 15:49:29 CET: 9A0URV>APBM1S,TCPIP*,qAS,BM2222:@021449z4504.08N/01338.40ErPHG0000DMR / C4FM / D-STAR / 438.7750/431.1750 CC1 +2026-03-02 15:49:29 CET: 9A0URV>APBM1S,TCPIP*,qAS,BM2222:>https://brandmeister.network/?page=repeater&id=219017 +2026-03-02 15:49:29 CET: KD5KJZ-9>APAT51,WIDE1-1,WIDE2-1,qAR,K5VZC-1:!3230.86N/09554.31Wk314/000/A=000478KD5KJZ VZCARES.ORG +2026-03-02 15:49:29 CET: KQ4JKJ>APRS,TCPIP*,qAC,AMBCWOP-2:@021449z2824.23N/08040.67W_038/002g003t076r000p000P000h67b10175L426AmbientCWOP.com +2026-03-02 15:49:29 CET: GW3677>APRS,TCPXX*,qAX,AMBCWOP-1:@021449z2913.72N/08233.34W_082/003g005t066r000p000P000h84b10231L428AmbientCWOP.com +2026-03-02 15:49:29 CET: GW3701>APRS,TCPXX*,qAX,AMBCWOP-1:@021449z3534.67N/09723.94W_248/002g002t039r000p000P000h90b10206L080AmbientCWOP.com +2026-03-02 15:49:29 CET: GW3710>APRS,TCPXX*,qAX,AMBCWOP-1:@021449z4422.64N/08925.94W_220/003g010t026r000p000P000h79b10279L124AmbientCWOP.com +2026-03-02 15:49:29 CET: FW3614>APRS,TCPXX*,qAX,AMBCWOP-1:@021449z3015.84N/08127.25W_092/005g006t065r000p000P000h89b10243L370AmbientCWOP.com +2026-03-02 15:49:29 CET: N7JJY-4>APN391,qAR,N0SZ-2:;449.300WY*111111z4103.32N/10447.03WrT131.8 -5.00 ANALOG +2026-03-02 15:49:29 CET: N2SKY-5>APRS,TCPIP*,qAC,T2SYDNEY:=4029.62N/10510.90WrPHG0330/448.400 PL 94.8 SkyHubLink.com NoCo +2026-03-02 15:49:29 CET: GB7BWR>HEARD,WIDE1-1,qAR,GB7RDG:GB7RDG GB7OXF GB7LOX GB7WEM GB7BPQ PD4R GB7BMY PI1LAP [Unsupported packet format] +2026-03-02 15:49:29 CET: TB8ATA-13>AESPG4,TCPIP*,qAC,T2BELGIUM::TB8ATA-13:EQNS.0,1,0,0,1,0,0,1,0,0,-1,0,0,1,0 +2026-03-02 15:49:29 CET: KO6GGW-Y>APDG03,TCPIP*,qAC,KO6GGW-YS:!3344.52NW11656.75Wi/A=00000070cm MMDVM Voice (C4FM) 445.35000MHz +0.0000MHz, APRS for YSFGateway +2026-03-02 15:49:29 CET: KO6GGW-Y>APDG03,qAS,KO6GGW:>Powered by WPSD (https://wpsd.radio) +2026-03-02 15:49:29 CET: W5ITZ>APRS,TCPIP*,qAC,AMBCWOP-2:@021449z2857.94N/09629.49W_114/002g002t064r000p000P000h98b10198L188AmbientCWOP.com +2026-03-02 15:49:29 CET: FW9191>APRS,TCPXX*,qAX,AMBCWOP-1:@021449z5113.44N/11442.41W_059/005g008t019r000p000P000h10b10079L016AmbientCWOP.com +2026-03-02 15:49:29 CET: WA8WDQ-D>APDG03,TCPIP*,qAC,WA8WDQ-DS:!4330.62ND07207.27W&/A=00000070cm MMDVM Voice (DMR) 444.36250MHz +5.0000MHz, APRS for DMRGateway +2026-03-02 15:49:29 CET: DL1MKJ-10>APLRG1,TCPIP*,qAC,T2CAEAST:!L4[*VP@%va GLoRa APRS iGate +2026-03-02 15:49:29 CET: GW5985>APRS,TCPXX*,qAX,AMBCWOP-1:@021449z1818.07N/06709.86W_157/001g003t087r000p000P000h64b10186L652AmbientCWOP.com +2026-03-02 15:49:29 CET: GW0191>APRS,TCPXX*,qAX,AMBCWOP-1:@021449z2959.52N/09531.26W_295/000g001t069r000p000P000h87b10232L099AmbientCWOP.com +2026-03-02 15:49:29 CET: GW0490>APRS,TCPXX*,qAX,AMBCWOP-1:@021449z4359.07N/07102.44W_005/000g002t014r000p000P000h40b10336L348AmbientCWOP.com +2026-03-02 15:49:29 CET: HK4AM-12>APLRG1,TCPIP*,qAC,T2BRAZIL:!LKCr^;D9^a GLoRa_iGate_HKNET_HK6RM Batt=5.16V +2026-03-02 15:49:29 CET: YM2KDB-3>APRS,WIDE2-2,qAR,YM2KY-1:>www.anarad.org.tr +2026-03-02 15:49:29 CET: DH0GSU-10>APLG01,TCPIP*,qAC,T2SWEDEN:=4859.10NL00823.52E&Uwe A03 - LoRa iGATE +2026-03-02 15:49:29 CET: G3RJI-9>UQ3TS4,WIDE1-1,WIDE2-1,qAR,MB7UUE:`v+Enqv>/`"49}145.525MHz alan mobile_% +2026-03-02 15:49:29 CET: K4FZN-10>APLRG1,TCPIP*,qAC,T2BC:=LAPLRG1,TCPIP*,qAC,T2PERTH:!L3GH3Pyo*& GLoRa APRS iGate LILYGO T3 ONLY RX|#6&8| +2026-03-02 15:49:29 CET: 9A0DRJ>APBM1S,TCPIP*,qAS,BM2222:@021449z4512.91N/01433.59ErPHG0000DMR / C4FM / D-STAR / 438.9875/431.3875 CC1 +2026-03-02 15:49:29 CET: EL-E75BM>RXTLM-1,TCPIP,qAR,E75BM:T#762,0.00,0.01,0,6,0.0,00000000,SimplexLogic +2026-03-02 15:49:29 CET: 9A0DRJ>APBM1S,TCPIP*,qAS,BM2222:>https://brandmeister.network/?page=repeater&id=219024 +2026-03-02 15:49:29 CET: GW6398>APRS,TCPXX*,qAX,AMBCWOP-1:@021449z3506.58N/10615.58W_220/008g011t052r000p000P000h35b10218L146AmbientCWOP.com +2026-03-02 15:49:29 CET: KD0RIH>APRS,TCPIP*,qAC,AMBCWOP-2:@021449z3947.75N/09332.65W_331/001g001t034r000p003P003h89b10258L095AmbientCWOP.com +2026-03-02 15:49:29 CET: N3WMC>APWW11,TCPIP*,qAC,T2PERTH:T#068,100,048,002,128,000,10000000 +2026-03-02 15:49:29 CET: KD4UFD>APRS,TCPIP*,qAC,AMBCWOP-2:@021449z3600.72N/08404.27W_039/003g003t049r000p004P004h90b10260L067AmbientCWOP.com +2026-03-02 15:49:29 CET: GW6839>APRS,TCPXX*,qAX,AMBCWOP-1:@021449z4302.48N/08245.74W_150/002g006t023r000p000P000h63b10347L285AmbientCWOP.com +2026-03-02 15:49:29 CET: FW8827>APRS,TCPXX*,qAX,AMBCWOP-1:@021449z4025.78N/07951.52W_273/002g002t026r000p000P000h64b10336L080AmbientCWOP.com +2026-03-02 15:49:29 CET: GW4455>APRS,TCPXX*,qAX,CWOP-6:/021449z4251.67N/10616.18W_269/003g007t045P000b10122h74cwMServer +2026-03-02 15:49:29 CET: FW9185>APRS,TCPXX*,qAX,AMBCWOP-1:@021449z3214.94N/11055.46W_151/000g000t060r000p000P000h40b10154L066AmbientCWOP.com +2026-03-02 15:49:29 CET: SP9INZ-6>UPQW49,SR9WXS*,WIDE1*,WIDE2-1,qAR,SR9KFZ:`/&n<0x1c><0x1c>v[/"7j}145.500 MHz //73! +2026-03-02 15:49:29 CET: VE3RLR-10>APDW17,TCPIP*,qAC,T2VAN:T#900,45.7,0.00,0.01,0.00 +2026-03-02 15:49:29 CET: KB0Y>APRS,TCPIP*,qAC,AMBCWOP-2:@021449z3044.58N/08323.70W_188/003g005t066r000p000P000h79b10289L147AmbientCWOP.com +2026-03-02 15:49:29 CET: 9A0DRR>APBM1S,TCPIP*,qAS,BM2222:@021449z4449.65N/01459.55ErPHG0000Radio klub Rijeka 9A1ARS 438.4375/430.8375 CC8 +2026-03-02 15:49:29 CET: K0AMC-5>APDW17,TCPIP*,qAC,T2MCI:!3818.68NT09026.18W& +2026-03-02 15:49:29 CET: HS5SQI-4>AESPG4,TCPIP*,qAC,APRSTH::HS5SQI-4 :UNIT.Pkt,Pkt,Pkt,dBm,V +2026-03-02 15:49:29 CET: HS5SQI-4>AESPG4,TCPIP*,qAC,APRSTH::HS5SQI-4 :EQNS.0,1,0,0,1,0,0,1,0,0,-1,0,0,1,0 +2026-03-02 15:49:29 CET: HS5SQI-4>AESPG4,TCPIP*,qAC,APRSTH:T#309,3,1,0,72,21.00,00000000 +2026-03-02 15:49:29 CET: BG3HPB-1>APRS4G,TCPIP*,qAC,T2NANJING:>https://aprs.tv 1.0.30 +2026-03-02 15:49:29 CET: F4JWQ-S>APDG01,TCPIP*,qAC,F4JWQ-GS:;F4JWQ B *021449z4843.58ND00122.13EaRNG0001/A=000010 70cm Voice (D-Star) 438.80000MHz -4.0000MHz +2026-03-02 15:49:29 CET: DH1GAP-13>APGE01,qAS,DH1GAP-10:!4750.27N/00855.56E_112/000g...t052r000p000h75b10304 +2026-03-02 15:49:29 CET: YO5CEA-10>TVQX91,YO8CLN-2,WIDE1*,WIDE2-1,qAR,YO8A-10:`3Gkl!MYY`"6Z}_) +2026-03-02 15:49:29 CET: F5RQX-9>TUQYVR,WIDE1-1,WIDE2-1,qAR,F6ZAF:`v)Xp,<0x1f>R/`"5<}_% +2026-03-02 15:49:29 CET: 9A0DRR>APBM1S,TCPIP*,qAS,BM2222:>https://brandmeister.network/?page=repeater&id=219029 +2026-03-02 15:49:30 CET: YO7IUM>APRS,TCPIP*,qAC,T2FRANCE:>Transmission ended +2026-03-02 15:49:30 CET: BD5DOY-8>AP51G2,TCPIP*,qAC,T2CS:!1524.90S/02816.88Ej349/018/A=004208<0xe6><0x99><0xae><0xe6><0x8b><0x89><0xe5><0xa4><0x9a> TEL:0573752436 1.996Km 148m 13.2V S08 +2026-03-02 15:49:30 CET: EL-DL0ABC>RXTLM-1,TCPIP,qAR,DL0ABC::EL-DL0ABC:UNIT.RX Erlang,TX Erlang,RXcount/10m,TXcount/10m,none1,STxxxxxx,logic +2026-03-02 15:49:30 CET: DJ0MDC>APREST,TCPXX*,qAX,CWOP-3:@021448z4851.51N/01034.23E_288/000g000t055r000P000h72b10210L162eREST +2026-03-02 15:49:30 CET: EL-DL0ABC>RXTLM-1,TCPIP,qAR,DL0ABC:T#076,5908208.00,0.01,0,1,0.0,00000000,TetraLogic +2026-03-02 15:49:29 CET: OE7CIT-1>APLG01,TCPIP*,qAC,T2LAUSITZ:=4715.87NL01119.79E&LoRa iGATE Info: github.com/lora-aprs/LoRa_APRS_iGate +2026-03-02 15:49:29 CET: W4MRL-N>APDG03,TCPIP*,qAC,W4MRL-NS:!3306.00ND08619.80W&/A=00000070cm MMDVM Voice (C4FM) 438.60000MHz +0.0000MHz, W4MRL_Pi-Star_ND +2026-03-02 15:49:29 CET: HB9GYR-10>APMI06,TCPIP*,qAC,T2PRT::HB9GYR-10:BITS.11111111,HB9GYR-10 Telemetry +2026-03-02 15:49:29 CET: CE3ABZ-6>APDR16,TCPIP*,qAC,T2SWEDEN:=3317.64S/07052.08We251/003/A=001626 https://aprsdroid.org/ +2026-03-02 15:49:29 CET: JJ2YAA-1>APTT4,WIDE1-1,WIDE2-1,qAO,JO2OKP-10:/000000h3446.10N113710.34E#000/000is 144.66MHz 1200bps 27C +2026-03-02 15:49:29 CET: GW0175>APRS,TCPXX*,qAX,AMBCWOP-1:@021449z3238.32N/10422.97W_000/011g012t050r000p000P000h54b10170AmbientCWOP.com +2026-03-02 15:49:29 CET: CN8VX>APSVX1,TCPIP*,qAS,CN8VX-16:T#034,000,015,000,001,000,00000000 +2026-03-02 15:49:29 CET: W7PLC>APRS,TCPIP*,qAC,AMBCWOP-2:@021449z4702.49N/12257.73W_358/000g000t031r000p000P000h99b10172L000AmbientCWOP.com +2026-03-02 15:49:29 CET: KK7DBC-D>APDG03,TCPIP*,qAC,KK7DBC-DS:!4810.78NW11702.60Wi/A=00000070cm MMDVM Voice (DMR) 439.00000MHz -5.0000MHz, APRS for DMRGateway +2026-03-02 15:49:29 CET: KK7DBC-D>APDG03,qAS,KK7DBC:>Powered by WPSD (https://wpsd.radio) +2026-03-02 15:49:29 CET: BJ8582>APN000,TCPIP*,qAC,T2TOKYO:=2929.20N/10626.65E$vlink.cc/bj8582 4.3V +2026-03-02 15:49:29 CET: GW0043>APRS,TCPXX*,qAX,AMBCWOP-1:@021449z3013.87N/08947.38W_350/002g004t061r000p000P000h99b10190L168AmbientCWOP.com +2026-03-02 15:49:29 CET: GW0469>APRS,TCPXX*,qAX,AMBCWOP-1:@021449z2635.65N/08136.78W_149/000g000t075r000p000P000h82b10189L500AmbientCWOP.com +2026-03-02 15:49:29 CET: GW0496>APRS,TCPXX*,qAX,AMBCWOP-1:@021449z4132.93N/07036.53W_342/000g001t023r000p000P000h47b10382L342AmbientCWOP.com +2026-03-02 15:49:29 CET: K2UWC-R>APDG03,TCPIP*,qAC,K2UWC-RS:!4029.38ND07516.55W&/A=00000070cm MMDVM Voice (C4FM) 441.10000MHz +5.0000MHz, K2UWC_Pi-Star_RPT +2026-03-02 15:49:29 CET: OE3OSB>APMI06,TCPIP*,qAC,T2AUSTRIA::OE3OSB :PARM.Vin,Rx1h,Dg1h,T@Dach,T@SDR,O1,O2,O3,O4,I1,I2,I3,I4 +2026-03-02 15:49:29 CET: KE7JVX-3>CQ,qAR,N7GEE-10:@021340z3316.93N/11049.27W_275/009g015t048r000p000P000h21b10138.DsVP +2026-03-02 15:49:29 CET: GW1992>APRS,TCPXX*,qAX,AMBCWOP-1:@021449z4008.65N/07527.53W_058/001g001t028r000p000P000h43b10411L174AmbientCWOP.com +2026-03-02 15:49:29 CET: OE3OSB>APMI06,TCPIP*,qAC,T2AUSTRIA::OE3OSB :UNIT.Volt,Pkt,Pkt,C,C,On,On,On,On,Hi,Hi,Hi,Hi +2026-03-02 15:49:30 CET: JH4GQC>APU25N,TCPIP*,qAC,T2FUKUOKA:;WIR-32062*290314z3415.48NW13231.55E0#42062(20:00-)#23496(00:00-)/144.60/88.5 +2026-03-02 15:49:30 CET: BM7IIG-1>APTMAG,TCPIP*,qAC,T2OSAKA:T#685,107,1,82,4,21,00000000 +2026-03-02 15:49:30 CET: OE3OSB>APMI06,TCPIP*,qAC,T2AUSTRIA::OE3OSB :EQNS.0,0.075,0,0,10,0,0,10,0,0,0.5,-64,0,0.5,-64 +2026-03-02 15:49:30 CET: OE3OSB>APMI06,TCPIP*,qAC,T2AUSTRIA::OE3OSB :BITS.11111111,WX3in1Plus20 Telemetry +2026-03-02 15:49:30 CET: IW0DSR>APBM1S,TCPIP*,qAS,BM2222:@021449z4203.62N/01248.09ErPHG0000Monte Gennaro (RM) - 430.6000/435.6000 CC1 +2026-03-02 15:49:30 CET: IW0DSR>APBM1S,TCPIP*,qAS,BM2222:>https://brandmeister.network/?page=repeater&id=222004 +2026-03-02 15:49:30 CET: VE6WDM-10>APFII0,TCPIP*,qAC,APRSFI:@144923h1018.97N\10727.04Es070/014/A=000013aprs.fi for iOS!wID! +2026-03-02 15:49:30 CET: KK5PP-3>APN383,qAR,W5NGU-3:!3255.88N/09627.61W#PHGD9560/DIGI Rockwall County, TX +2026-03-02 15:49:30 CET: KMASWX>APOT30,VE7RLO,VE7CHW,qAO,VA7AV-5:>www.kmasrc.ca WX Stn, ve7fsr@rac.ca +2026-03-02 15:49:30 CET: W4PVW-15>APMI06,WIDE2-2,qAR,KD4IEZ-1:@021352z3138.38N/08344.96W# CPARC Turner County GA +2026-03-02 15:49:29 CET: GW3580>APRS,TCPXX*,qAX,AMBCWOP-1:@021449z3650.51N/11951.85W_303/002g002t051r000p000P000h85b10167L010AmbientCWOP.com +2026-03-02 15:49:29 CET: W0AKO>APRS,TCPIP*,qAC,AMBCWOP-2:@021449z3700.20N/11330.96W_155/001g002t051r000p000P000h10b10143L039AmbientCWOP.com +2026-03-02 15:49:29 CET: HK2LS-1>APOT30,HK4LRM-14,WIDE2*,qAR,HK4D-3:!0607.00N/07529.62Wr 06.4V 55C +2026-03-02 15:49:29 CET: IV3DVE-8>APLRT1,WIDE1-1,qAU,IQ3AZ-11:=/7@eMQu]8>:eQ +2026-03-02 15:49:29 CET: EA5IHI-13>AESPG4,TCPIP*,qAC,T2BIO:@021449z3823.69N/00030.85W_Estacion Meteorologica Wx T=16<0xc2><0xb0> H=72% P=1003 +2026-03-02 15:49:29 CET: F5OPV-9T>APSFLT,WIDE1-1,qAR,F1ZVC-10:!/8@k=N=Q&>oM[ +2026-03-02 15:49:29 CET: FW8609>APRS,TCPXX*,qAX,AMBCWOP-1:@021449z4204.04N/12414.31W_177/010g011t048r000p000P000h99b10172AmbientCWOP.com +2026-03-02 15:49:29 CET: HP1UPR-1>APET51,TCPIP*,qAC,T2USANE:!0913.80N/07923.00W#UNI<0xd3>N PANAME<0xd1>A DE RADIO AFICIONADOS UPRA, DMR TG7146 12.3V +2026-03-02 15:49:29 CET: YB3RDW-13>APREST,TCPIP*,qAC,SEVENTH:@021448z0717.98S/11239.17E_125/002g002t076r000P179h97b10120L000eREST +2026-03-02 15:49:29 CET: GW0637>APRS,TCPXX*,qAX,AMBCWOP-1:@021449z4856.61N/11049.45W_333/000g000t030r000p000P000h61b10133L026AmbientCWOP.com +2026-03-02 15:49:30 CET: NF4J-9>S0SX1R,N4TAE-15*,WIDE2-1,qAR,KE4BFX-1:`t'I gkK\]"4O}= [Location changes too fast (adaptive limit)] +2026-03-02 15:49:30 CET: OE3HCH>APOSB4,TCPIP*,qAC,T2PERTH:@021449z4806.66N/01623.30Ey/A=000600SharkRF openSPOT4 +2026-03-02 15:49:30 CET: FW0443>APRS,TCPXX*,qAX,CWOP-7:/021449z3838.14N/12055.27W_128/002g005t050r000p000P000h00b11082RainwiseNet-MKIII +2026-03-02 15:49:30 CET: YG1CBG-13>AESPG4,TCPIP*,qAC,T2TAIWAN:@021449z0654.57S/10742.77E_WX Station T=32<0xc2><0xb0> H=0% P=931 +2026-03-02 15:49:30 CET: FW8421>APN000,TCPXX*,qAX,CWOP-4:@021449z3410.15S/05944.06W_045/014g022t080r000p000P000b10071h56eMB55 +2026-03-02 15:49:30 CET: PD5RM-9>UR2UR0,WIDE1-1,WIDE2-1,qAO,PB2N-10:`zRgms*>/`"3I}430.250MHz T088 +160 Robin QRV Mobiel_1 +2026-03-02 15:49:30 CET: EW4437>APRS,TCPXX*,qAX,CWOP-7:@021447z3800.75N/08539.78W_091/001g005t033r004p020P020h89b10277eCumulusDsVP +2026-03-02 15:49:30 CET: N0EO-C>APDG02,TCPIP*,qAC,N0EO-CS:!4647.29ND09206.86W&RNG0001/A=000010 2m Voice (D-Star) 147.97500MHz -0.6000MHz +2026-03-02 15:49:30 CET: JR4WE-I>APIRP2,TCPIP*,qAC,JR4WE-IS:!3522.02ND13245.28E&D-STAR -> APRS +2026-03-02 15:49:30 CET: JR4WE-I>APIRP2,TCPIP*,qAC,JR4WE-IS:APRS,TCPIP*,qAC,T2NUERNBG:!5118.39N/00621.83Eb327/007 +2026-03-02 15:49:30 CET: KK6MIG-13>APN000,TCPIP*,qAC,T2DENMARK:@021449z3744.64N/12228.95W_225/003g005t054r000p005P005b10161h01L007eMB57 +2026-03-02 15:49:30 CET: ED1ZAW-3>APMI06,WIDE3-3,qAR,ED1ZAD-3:@021407z4251.23N/00511.57W#PHG4160/Radioaficionados de Leon URLE Volt.=13.9V,Temp=7.9C 1605m. +2026-03-02 15:49:30 CET: NI2O>APWEE5,TCPIP*,qAC,CWOP-6:@021445z4002.78N/07524.25W_004/004g006t026r000p000P000b10371h48L189.weewx-5.2.0-ambientweatherapi +2026-03-02 15:49:30 CET: YF9UAG-17>AESPG4,TCPIP*,qAC,T2TAIWAN:T#017,0,0,0,78,0,00000000,@ 14 49 28 2 3 2026 +2026-03-02 15:49:30 CET: K4SIP-N>APDG04,TCPIP*,qAC,K4SIP-NS:!3507.96ND08506.61W&/A=00000070cm MMDVM Voice (NXDN) 446.90000MHz +0.0000MHz, K4SIP_Pi-Star_ND +2026-03-02 15:49:30 CET: DG5MLA-9>TX2P6L,DB0TC-10*,qAU,DO7HS-10:''G<0x1c> ,_>/>"8#}Gerald on Tour-DOK T08= +2026-03-02 15:49:30 CET: IR0UFZ>APBM1S,TCPIP*,qAS,BM2222:@021449z4112.43N/01334.57ErPHG0000MONTE ORLANDO - GAETA (LT) 430.1000/435.1000 CC1 +2026-03-02 15:49:30 CET: IR0UFZ>APBM1S,TCPIP*,qAS,BM2222:>https://brandmeister.network/?page=repeater&id=222012 +2026-03-02 15:49:30 CET: LU2DKV-10>AESPG4,TCPIP*,qAC,T2SYDNEY::LU2DKV-10:UNIT.Pkt,Pkt,Pkt,dBm,V +2026-03-02 15:49:29 CET: KC3ROS>APRS,TCPIP*,qAC,AMBCWOP-2:@021449z4038.66N/08010.23W_077/005g007t023r000p000P000h69b10315L147AmbientCWOP.com +2026-03-02 15:49:29 CET: F6BAZ-10>APLRG1,WIDE1-1,qAO,DB9VE-10:=L5W_9P+WD& !GLoRa APRS Batt=4.24V +2026-03-02 15:49:29 CET: GW1967>APRS,TCPXX*,qAX,AMBCWOP-1:@021449z4405.25N/07319.31W_059/000g003t014r000p000P000h50b10416L430AmbientCWOP.com +2026-03-02 15:49:29 CET: GW1985>APRS,TCPXX*,qAX,AMBCWOP-1:@021449z4502.32N/09325.63W_200/000g000t029r000p000P000h68b10265L130AmbientCWOP.com +2026-03-02 15:49:29 CET: SQ5H-3>APRS,TCPIP*,qAC,T2FINLAND:=5218.91N/02053.31E<039/025/GPRS Tracker // Sat: 8 // Bat: 4.13V /A=000306 +2026-03-02 15:49:29 CET: SQ5H-3>APRS,TCPIP*,qAC,T2FINLAND:> Honda NX500 +2026-03-02 15:49:29 CET: LU4AE-18>APDPRS,TCPIP*,qAS,LU4AE-24:!3441.13S/05828.51WrDMR (TX:434000/RX:439000) Tmp: 51.5 C, PWS: Normal, Usage: 5%, Disk: 41% TLM by arSpot v1.7.1 20230510 +2026-03-02 15:49:29 CET: SR6NWS>APMI06,TCPIP*,qAC,T2POLAND:@021449z5040.82NI01629.14E# W1 SP1 Wielka Sowa 12.1V, 7.0C +2026-03-02 15:49:30 CET: N7JJY-4>APN391,qAO,WB7GR-10:;462.600WY*111111z4101.32N/10447.03WrT141.3 +5.00 GMRS RPTR +2026-03-02 15:49:30 CET: BM4LQC-1>APTMAG,TCPIP*,qAC,T2HK:!2504.75N/12128.42EI<0xe5><0x99><0x93><0xe5><0xaf><0x92><0xe5><0x95><0x8f><0xe6><0x9a><0x96><0xe7><0x85><0xa7><0xe4><0xb8><0x89><0xe9><0xa4><0x90> <0xe6><0xa6><0xa8><0xe4><0xb9><0xbe><0xe8><0x8d><0xb7><0xe5><0x8c><0x85><0xe4><0xba><0xba><0xe8><0x92><0xb8><0xe7><0x99><0xbc> +2026-03-02 15:49:30 CET: N3OQM-N>APDG03,TCPIP*,qAC,N3OQM-NS:!4100.00ND08000.00E&/A=00000070cm MMDVM Voice (C4FM) 446.50000MHz +0.0000MHz, N3OQM_Pi-Star_ND +2026-03-02 15:49:30 CET: VK4MJM-3>APMI04,WIDE2-2,qAR,VK4GDW-3:>Join us for the John Moyle Memorial Weekend, 18/19 March 2023 +2026-03-02 15:49:30 CET: EW4319>APRS,TCPXX*,qAX,CWOP-5:@021449z3840.55N/09000.47W_088/001g006t035r000p015P004h90b10237L042eCumulusDsVP +2026-03-02 15:49:30 CET: OK2ZIL-10>APDR16,TCPIP*,qAC,T2LAUSITZ:=4907.95N/01726.35E>203/001/A=000761 Z<0xc3><0xa1>silkovna - on the route +2026-03-02 15:49:30 CET: VK4YHT-8>RSR2W7,WIDE1-1,WIDE2-1,qAR,VK4RAR-3:`N;$<0x1c><0x1e>YY/`"3x}X1C3 4.8V S22 +2026-03-02 15:49:30 CET: KF8EXA>APN000,TCPIP*,qAC,T2TEXAS:=4902.85N/12245.68W[I don't hoard radios, I curate redundancy +2026-03-02 15:49:30 CET: EW3105>APRS,TCPXX*,qAX,CWOP-5:@021448z3014.30N/09755.18W_180/008g014t066r000p000P000h83b10182.DsVP +2026-03-02 15:49:30 CET: K9FB-N>APDG03,TCPIP*,qAC,K9FB-NS:!4026.68ND08606.97W&/A=00000070cm MMDVM Voice (C4FM) 434.93750MHz +0.0000MHz, K9FB_Pi-Star_ND +2026-03-02 15:49:30 CET: MACPAS>APN382,WIDE1-1,qAR,K7MT:!4635.67ND11217.92W#PHG5490/W2,MTn-N,WR7HLN MACPASS +2026-03-02 15:49:30 CET: VK6MRG-7>APOTC1,WIDE1-1,WIDE2-2,qAR,VK6MRG-1:/144928h3333.90S/11726.07Ej044/000/A=001020 13.8V 28C HDOP01.2 SATS08Hello from Matty G. Prado +2026-03-02 15:49:30 CET: W4PL-C>APJI43,TCPIP*,qAC,W4PL-GS:!3509.30ND08518.33W&RNG0055 2m Voice 145.290 -0.600 Mhz +2026-03-02 15:49:30 CET: N1DTA-2>APMI06,TCPIP*,qAS,N1DTA:@021404z3729.21NI08441.83W# 13.9V, ??.?F, NEW REPEATER 444.9125 + CC1 +2026-03-02 15:49:30 CET: K4DFQ-D>APDG03,TCPIP*,qAC,K4DFQ-DS:!4100.00ND07300.00W&/A=00000070cm MMDVM Voice (DMR) 432.10000MHz +0.0000MHz, APRS for DMRGateway +2026-03-02 15:49:30 CET: EA3GKP-7>APRS,TCPIP*,qAC,T2CSNGRAD:=4123.71N/00207.56E&MiniWX Station Barcelona (v1.1f) +2026-03-02 15:49:30 CET: EA3GKP-7>APRS,TCPIP*,qAC,T2CSNGRAD:=4123.71N/00207.56E_.../...g...t062r...p...P...h100b10242 +2026-03-02 15:49:30 CET: EA3GKP-7>APRS,TCPIP*,qAC,T2CSNGRAD:T#496,084,294,000,000,000,00000000 +2026-03-02 15:49:30 CET: EA3GKP-7>APRS,TCPIP*,qAC,T2CSNGRAD::EA3GKP-7 :PARM.RSSI,VBAT +2026-03-02 15:49:30 CET: EA3GKP-7>APRS,TCPIP*,qAC,T2CSNGRAD::EA3GKP-7 :UNIT.dbm,V +2026-03-02 15:49:30 CET: EA3GKP-7>APRS,TCPIP*,qAC,T2CSNGRAD::EA3GKP-7 :EQNS.0,-1,0,0,0.01,0,0,0,0,0,0,0,0,0,0 +2026-03-02 15:49:30 CET: EA3GKP-7>APRS,TCPIP*,qAC,T2CSNGRAD::EA3GKP-7 :BITS.00000000,MiniWX Station Barcelon +2026-03-02 15:49:30 CET: 9A3HVZ>APDR16,TCPIP*,qAC,T2ERFURT:=4602.40N/01603.36EQ253/050/A=000643 MOBILE DMR TG:21966 +2026-03-02 15:49:30 CET: LA9SHA>APGLR1,WIDE1-1,qAR,LB1R-10:!5940.35N/00940.88E-Board B (FW v0.61d) +2026-03-02 15:49:30 CET: F5UJA-7>APRSGW,TCPIP*,qAR,F5UJA-7:!4829.65N/00727.42E-Michel JN38RL#F5UJA/B=099/A=000620 +2026-03-02 15:49:30 CET: FW0574>APRS,TCPXX*,qAX,CWOP-7:@021449z3716.10N/12158.30W_270/000g000t051r000p000P000b10128h98eMB39 +2026-03-02 15:49:30 CET: I0KTE-10>I0KTE,TCPIP*,qAC,IZ3MEZ-G::I0KTE :Test-A0,0000,1095,1100 +2026-03-02 15:49:30 CET: W5ANR-9>APN391,DECATU*,WIDE2-1,qAO,DODD:!3504.34NS09440.55W#PHG5800/ Cavanal Hill; Poteau, OK +2026-03-02 15:49:30 CET: N2PYI-2>APRX26,TCPIP*,qAC,T2USANW:T#241,9.0,7.7,39.0,5.0,32.0,00000000 +2026-03-02 15:49:30 CET: BH2TQT-29>NRLBOX,TCPIP*,qAC,T2TAIWAN:>E1[NRL_2100A_D4_260205 12.4V RX:438.6250:100.0 TX:438.6250:100.0] HELLO +2026-03-02 15:49:30 CET: W4PL-A>APJI43,TCPIP*,qAC,W4PL-GS:!3509.30ND08518.33W&RNG0035 1.2 Voice RNG0035 1.2 Voice 1291.0000 -20.00 Mhz +2026-03-02 15:49:30 CET: PU2OMB-Y>APDG03,TCPIP*,qAC,PU2OMB-YS:!2400.74S/04625.44W&/A=00000070cm MMDVM Voice (C4FM) 439.06500MHz +0.0000MHz, APRS for YSFGateway +2026-03-02 15:49:30 CET: PU2OMB-Y>APDG03,qAS,PU2OMB:>Powered by WPSD (https://wpsd.radio) +2026-03-02 15:49:30 CET: KMASWX>APOT30,VE7RLO,VE7CHW,qAR,VE7TPK:!5050.42N/12016.12W_ 13.7V +2026-03-02 15:49:30 CET: EA7EGJ>APSVX1,TCPIP*,qAC,T2AUSTRIA:T#076,000,017,000,008,000,00000000 +2026-03-02 15:49:30 CET: YM3KRM>APBM1S,TCPIP*,qAS,BM2841:@021401z3941.02N/02759.88ErPHG0000AKRAD - Anadolu Kesintisiz Iletisim Radyo Amat<0xc3><0xb6>rleri Dernegi 439.1625/431.5625 CC1 +2026-03-02 15:49:30 CET: W4PL-A>APJI43,TCPIP*,qAC,W4PL-GS:APDG02,TCPIP*,qAC,ZL2TFG-CS:!4019.83SD17536.48E&RNG0001 2m Voice 144.55000MHz +0.0000MHz +2026-03-02 15:49:30 CET: IR0UGR>APBM1S,TCPIP*,qAS,BM2222:@021449z4145.71N/01242.80ErPHG0000IR0UGR - Loc. JN61GV 430.5500/435.5500 CC1 +2026-03-02 15:49:30 CET: DW5818>APRS,TCPXX*,qAX,CWOP-3:@021449z4741.75N/00315.28W_199/004g008t060r000p004P001h76b10138.DsVP +2026-03-02 15:49:31 CET: DC1MBB-9>APRSWX,WIDE2-2,qAR,DG4MNU-10:!4811.64N/01137.14E_000/000g000t070h37b09640a413m 12.44V_WX-Munich +2026-03-02 15:49:31 CET: HB9AK-7>APMI06,qAR,HB9BB:@021449z4700.75N/00935.30E7PHG2930 WX3in1 Malanser Aelpli GR +2026-03-02 15:49:31 CET: FW2888>APRS,TCPXX*,qAX,CWOP-6:@021449z3819.62N/07724.10W_109/001g004t035r000p000P000b10287h60L183eMB50 +2026-03-02 15:49:31 CET: IR0UGR>APBM1S,TCPIP*,qAS,BM2222:>https://brandmeister.network/?page=repeater&id=222026 +2026-03-02 15:49:31 CET: BD6JDU-9>APDR16,TCPIP*,qAC,T2DENMARK::BH1KZQ-15:73{3 +2026-03-02 15:49:31 CET: DO8PGG-5>APDR16,TCPIP*,qAC,T2LAUSITZ:=5119.33N/00720.00E$277/002/A=000759 DMR+ Refl. 4671 +2026-03-02 15:49:31 CET: KM4LOD-S>APJIO4,TCPIP*,qAC,KM4LOD-GS:;KM4LOD B *201519z3324.31ND08150.25WaRNG0040 440 Voice 440.550 +5.00 MHz +2026-03-02 15:49:31 CET: KM4LOD-S>APJIO4,TCPIP*,qAC,KM4LOD-GS:;KM4LOD C *201508z3324.31ND08150.25WaRNG0040 2m Voice 145.260 -0.600 MHz +2026-03-02 15:49:31 CET: K4OBI>APN000,TCPIP*,qAC,T2TAS:@021449z3622.08N/08011.12W_085/003g006t042r000p000P000b10355h77eMB63 +2026-03-02 15:49:31 CET: GW5521>APRS,TCPXX*,qAX,CWOP-6:@021448z4617.85N/12216.00W_310/003g003t036r000p000P000h89b10188.DsVP +2026-03-02 15:49:31 CET: KMASWX>APOT30,VE7RLO,VE7CHW,qAO,VA7AV-5:!5050.42N/12016.12W_160/000g000t030p000h92T2WX +2026-03-02 15:49:30 CET: K4LLL-2>APDR16,TCPIP*,qAC,T2KOBLENZ:=3030.94N\08746.52Wo030/002/A=000102 https://aprsdroid.org/ +2026-03-02 15:49:30 CET: LA3BTA-8>VVQY19,WIDE1-1,WIDE2-2,qAR,LA3M-2:`*afl4Q[/`"45}Ivan la3bta@la3bta.no QRV on LA7MR or 145.500 MHz CTCSS 88.5_# +2026-03-02 15:49:30 CET: IR0EF>APBM1S,TCPIP*,qAS,BM2222:@021449z4306.52N/01223.32ErPHG0000Associazione Nazionale Radioamatori CISAR - IQ0SQ Sezione di PERUGIA - Ripetitore PERUGIA CENTRO 430.7625/435.7625 CC1 +2026-03-02 15:49:30 CET: IR0EF>APBM1S,TCPIP*,qAS,BM2222:>https://brandmeister.network/?page=repeater&id=222011 +2026-03-02 15:49:30 CET: WINLINK>APWL2K,TCPIP*,qAS,WLNK-1:;KD0TKR *020249z3856. NW10437. Wa441.075MHz Winlink VARA FM Wide Gateway +2026-03-02 15:49:30 CET: ER-EA4HCQ>APSVX1,WIDE1-1,qAS,EA4HCQ:T#076,000,020,000,004,000,00000000 +2026-03-02 15:49:30 CET: KJ4SNT>APMI04,TCPIP*,qAC,T2CAEAST:@021449z3657.76N108022.46W#PHG7060/A=02315 Floyd VA finchm@swva.net +2026-03-02 15:49:30 CET: HARIN>APN383,qAO,WEBER:!4219.60NN11339.28W#PHG5940 KC7SNN Mt Harrison APRS Digi +2026-03-02 15:49:30 CET: VE2UMS-3>APN391,VE2RAW-3*,qAR,VA2HMC-1:!4538.42NS07329.44W#PHG6440/W3 APRS Digi VE2UMS-3 +2026-03-02 15:49:30 CET: KC8SWY-15>APMI06,TCPIP*,qAS,KC8SWY:T#076,168,000,000,021,152,00000000 +2026-03-02 15:49:30 CET: FW9712>APRS,TCPXX*,qAX,CWOP-7:@021449z5058.21N/00635.52E_147/002g008t062r000p000P000h48b10207WeatherCatV331B8H31 +2026-03-02 15:49:30 CET: DF8BP-10>APGE01,TCPIP*,qAC,T2CZECH:!5209.43N/01031.10E&APRS iGate Wolfenbuettel 144,800 (RX only) +2026-03-02 15:49:30 CET: FW6434>APN000,TCPXX*,qAX,CWOP-5:@021449z5019.88N/01853.08E_193/000g003t052r000p000P000b10251h61L027eMB54 +2026-03-02 15:49:30 CET: DM5SR-15>APRS,TCPIP*,qAC,T2SPAIN:!5002.99N/00810.39E>079/060 Teslalogger MQTT>APRS-IS +2026-03-02 15:49:30 CET: TOQUER>APDW17,WIDE1-1,WIDE2-2,qAR,WEBB:!3717.36NT11316.60W&PHG5140Toquerville, 5,239 feet, W7ZDL, WWW.HAMCONZION.COM +2026-03-02 15:49:30 CET: BA3ME-10>APVRT7,TCPIP*,qAC,T2CS:!3705.54N/11428.52E_000/000g...t093r...p...h00b10204 12.0V +2026-03-02 15:49:30 CET: SQ7SCC-S>APDG01,TCPIP*,qAC,SQ7SCC-GS:;SQ7SCC C *021449z5143.04ND01929.38EaRNG0001/A=000722 2m Voice (D-Star) 144.92500MHz +0.0000MHz +2026-03-02 15:49:31 CET: WW5CRT>S2SU0U,WIDE1-1,WIDE2-1,qAR,W5DCR-3:`|M=r><0x1c>>/`"5u}_4 +2026-03-02 15:49:31 CET: W4PL-B>APJI43,TCPIP*,qAC,W4PL-GS:APJI43,TCPIP*,qAC,W4PL-GS:!3509.30ND08518.33W&RNG0040 440 Voice 443.150 +5.00 Mhz +2026-03-02 15:49:31 CET: ODOT>APODOT,TCPIP*,qAC,WA7BF:!4610.29N\12334.75WjODOT Traffic Alerts - NA7Q +2026-03-02 15:49:31 CET: WD7LEE>APWW11,WIDE1-1,WIDE2-1,qAR,ELMWOD:=4459.70N/08541.22WoLeelanau County EOC +2026-03-02 15:49:31 CET: IZ0ZIP>APBM1S,TCPIP*,qAS,BM2222:@021449z4126.80N/01349.57ErPHG0000IZ0ZIP - Cassino (FR) 430.9625/435.9625 CC1 +2026-03-02 15:49:31 CET: IZ0ZIP>APBM1S,TCPIP*,qAS,BM2222:>https://brandmeister.network/?page=repeater&id=222030 +2026-03-02 15:49:31 CET: PD2TX-7>APDR16,TCPIP*,qAC,T2GYOR:=5305.08N/00452.59E<051/001/145.300MHz pd2tx.com +2026-03-02 15:49:31 CET: OE9FWV-10>APLRG1,TCPIP*,qAC,T2CHILE:!L6Wt8Ptis_ G.../...g...t082h24b10246LoRa APRS Gisingen +2026-03-02 15:49:31 CET: E23JWE-1>AESPG4,TCPIP*,qAC,T2INDY::E23JWE-1 :PARM.PM1.0,PM2.5,PM10,RSSI,Voltage +2026-03-02 15:49:31 CET: DD5AT-10>APLRG1,TCPIP*,qAC,T2CHILE:!L4g'4P?P,& GLoRa iGATE Lohmar-Hoeffen 433.775Mhz +2026-03-02 15:49:31 CET: SNTIAM>APTT4,WIDE1-1,WIDE2-2,qAR,BRIVER:=4424.77N/12151.85W#>K7ZQU-11 digi /A=004738 +2026-03-02 15:49:31 CET: S51JY-9>APBM1D,S55DCM,DMR*,qAR,S55DCM:@144927h4616.80N/01518.08E>104/000ANDREJ +2026-03-02 15:49:30 CET: N2PYI-3>APRX26,TCPIP*,qAS,N2PYI-2:T#241,0.3,0.1,1.0,0.0,1.0,00000000 +2026-03-02 15:49:30 CET: EW8444>APRS,TCPXX*,qAX,CWOP-3:@021449z3541.80N/08238.05W_193/002g005t048r000p001h75b10274.wview_5_21_7 +2026-03-02 15:49:30 CET: OE3BCB-14>APRS,qAO,OE3XOC-11:!4812.01N/01553.80E_090/001g009t032r...p...P...h00b0300L026LoRa WX Neulengbach +2026-03-02 15:49:30 CET: F5ADL-10>APLRG1,TCPIP*,qAC,T2MEXICO:!L8I'!P9zza !GLoRa APRS Batt=4.13V +2026-03-02 15:49:30 CET: GW1618>APN000,TCPXX*,qAX,CWOP-5:@021446z4702.72N/12254.42W_184/002g003t033r000p000P000b10183h92eMB62 +2026-03-02 15:49:30 CET: ke5jzm-1>APN000,TCPIP*,qAC,T2DENMARK:@021449z3002.62N/09041.96W_105/003g007t074r000p000P000b10216h53L445eMB63 +2026-03-02 15:49:31 CET: LY1LB-1>APMI06,WIDE2-1,qAO,LY1RF:;LY0RKU *021449z5453.77N/02357.46Er438.850MHz Toff -760 R50k Repeater +2026-03-02 15:49:31 CET: KC3RVI>APOSB,TCPIP*,qAC,T2PANAMA:@021449z4012.88N\07501.13W&/A=000262SharkRF openSPOT3 +2026-03-02 15:49:31 CET: IT9JXC-2>APLRG1,WIDE1-1,qAO,IT9DBI-9:=L;5Y`RAPSVX1,WIDE1-1,qAS,EA4URC:T#076,000,080,000,009,000,00000000 +2026-03-02 15:49:31 CET: DW3695>APN000,TCPXX*,qAX,CWOP-7:@021449z4645.24N/00737.13E_180/004g005t061r000p001P001b10173h57L315eMB63 +2026-03-02 15:49:31 CET: D7126>APN000,TCPXX*,qAX,CWOP-4:@021449z3912.00N/00918.00W_157/003g010t060r000p001P001b10013h81L063eMB63 +2026-03-02 15:49:31 CET: 2E0RPZ-D>APDG03,TCPIP*,qAC,2E0RPZ-DS:!5021.93N\00405.07W&/A=00000070cm MMDVM Voice (DMR) 438.80000MHz +0.0000MHz, APRS for DMRGateway +2026-03-02 15:49:31 CET: 2E0RPZ-D>APDG03,qAS,2E0RPZ:>Powered by WPSD (https://wpsd.radio) +2026-03-02 15:49:31 CET: KO4QAH-S>APDG01,TCPIP*,qAC,KO4QAH-GS:;KO4QAH B *021449z3110.50ND08129.10WaRNG0001/A=000010 70cm Voice (D-Star) 445.60000MHz +0.0000MHz +2026-03-02 15:49:31 CET: EW5085>APN000,TCPXX*,qAX,CWOP-6:@021449z3219.80N/08028.68W_.../...g...t...r...p...P000b.....h..eMB61 +2026-03-02 15:49:31 CET: 9A7KXP-12>APMI03,WIDE2-2,qAR,9A4QV-2:@021541z4459.58N/01515.94E2Solarni APRS digi Lika-Brinje U=14.8V. +2026-03-02 15:49:31 CET: EL-YO6KXQ>RXTLM-1,TCPIP,qAR,YO6KXQ::EL-YO6KXQ:UNIT.RX Erlang,TX Erlang,RXcount/10m,TXcount/10m,none1,STxxxxxx,logic +2026-03-02 15:49:31 CET: AI5BH-D>APDG03,TCPIP*,qAC,AI5BH-DS:!3033.71NW09748.69Wi/A=00000070cm MMDVM Voice (DMR) 446.30000MHz -5.0000MHz, APRS for DMRGateway +2026-03-02 15:49:31 CET: AI5BH-D>APDG03,qAS,AI5BH:>Powered by WPSD (https://wpsd.radio) +2026-03-02 15:49:31 CET: DD3UTS-13>APRSWX,TCPIP*,qAC,T2ERFURT:>WX JO60UX Temp: 12.9C Niederschlag erkannt: nein Luftfeuchte: 36.0% +2026-03-02 15:49:31 CET: EL-YO6KXQ>RXTLM-1,TCPIP,qAR,YO6KXQ:T#182,0.00,0.01,0,1,0.0,00000000,TetraLogic +2026-03-02 15:49:31 CET: HK5FCI-10>APLRG1,TCPIP*,qAC,T2PANAMA:=0327.73NL07631.70WaHKNET-LoRa_APRS_IGATE +2026-03-02 15:49:31 CET: N4SRN-13>APN000,TCPXX*,qAX,CWOP-6:@021447z4258.53N/07130.36W_060/001g005t016r000p000P000b10366h34eMB62 +2026-03-02 15:49:31 CET: OE3EMC-9>APLC13,qAO,OK1LPD-1:!/5t9TRJTQLoRa-System +2026-03-02 15:49:31 CET: KP4LAC-R>APDG03,TCPIP*,qAC,KP4LAC-RS:!1759.80N/06706.02Wr/A=00000070cm MMDVM Voice (DMR) 447.97500MHz -5.0000MHz, https://www.qrz.com/db/KP4NET KP4NET/RPT TG 330050 BM TS1 CC1 +2026-03-02 15:49:31 CET: IK7WPI-15>APLRG1,TCPIP*,qAC,T2PANAMA:!L9XXYRXa=# !GLoRa APRS DIGIPEATER 433.775 MHhz Batt=4.34V +2026-03-02 15:49:31 CET: KD4VIF>APW249,K4CCC-9*,WIDE2-1,qAR,W4DEX:_03021439c005s002g006t046r000p000P000h00b10303wU2K +2026-03-02 15:49:31 CET: FW3866>APRS,TCPXX*,qAX,CWOP-5:@021449z4431.47N/09935.58E_.../...g010t...r000p000P000b10186h..eMB43 +2026-03-02 15:49:31 CET: DL6BZ-10>APLRG1,TCPIP*,qAC,T2CHILE:=5229.46NL00652.52E&LoRa APRS iGate LILYGo TTGo T-Beam v1.2 +2026-03-02 15:49:30 CET: DW8879>APRS,TCPXX*,qAX,CWOP-4:@021449z3456.57N/08952.44E_143/000g000t056r000p003P003h95b10226WeatherCatV331B8H31 +2026-03-02 15:49:30 CET: PD0PYL-5>5R0RZL,WIDE1-1,WIDE2-1,qAR,PI1GDA:`z8<0x1c>l-F-\`"3e}Test Yaesu FTM 510_5 +2026-03-02 15:49:30 CET: CW6791>APRS,TCPXX*,qAX,CWOP-5:@021449z4434.83N/08805.55W_154/004g006t030r000p000P000h73b10302.DsVP +2026-03-02 15:49:30 CET: KG5RGN-13>APMI06,TCPIP*,qAC,T2BC:@021449z3645.22N/10806.74W_043/002g007t047r000p000P000h00b00000WX3in1 weather +2026-03-02 15:49:30 CET: PAMPA>APOT30,WIDE3-3,qAR,K5NOC:!3530.48N/10101.59W# 13.5V 101F PHG5560/*PCG* DIGI PAMPA, TX W5GAF +2026-03-02 15:49:31 CET: KG5RGN-13>APMI06,TCPIP*,qAC,T2BC:@021449z3645.22N/10806.74W# www.NWNMEMCOMM.com U=14.2V,T=86.0F +2026-03-02 15:49:31 CET: K6SNY-5>ID,qAO,AI6SL-5:Network Node (SNY) +2026-03-02 15:49:31 CET: GW5927>APREST,TCPXX*,qAX,CWOP-3:@021448z4728.47N/12239.21W_086/002g002t027r000P000h97b10180L004eREST +2026-03-02 15:49:31 CET: W4PL-C>APJI43,TCPIP*,qAC,W4PL-GS:APMI06,TCPIP*,qAS,YO4FZV-10:@021449z4458.63N/02853.38E_022/003g004t044r000p000P...h79b10220WX3in1 weather +2026-03-02 15:49:31 CET: 9W2HFO-10>APDR16,TCPIP*,qAC,T2F5KAV:=0259.05N/10148.74Ef145.500MHz/A=000291 V40-V66 & APM.net +2026-03-02 15:49:31 CET: DO1FMI-15>APDW16,TCPIP*,qAC,T2CSNGRAD:!4737.65NR01259.70E&APRS Igate Berchtesgaden 570m NHN +2026-03-02 15:49:31 CET: AB5KT-1>APRS,TCPXX*,qAX,CWOP-3:@021449z2916.90N/09826.72W_286/002g007t068r000p000P000b10194h82eMB32 +2026-03-02 15:49:31 CET: YO4FZV-11>APMI06,TCPIP*,qAS,YO4FZV-10:@021449z4458.63N/02853.38E-WX3in1Plus2.0 U=12.3V,T=21.0C/69.8F +2026-03-02 15:49:31 CET: IR3UBZ>APBM1S,TCPIP*,qAS,BM2222:@021449z4603.46N/01314.91ErPHG0000ARI RCE FVG 431.4250/433.0250 CC1 +2026-03-02 15:49:31 CET: SNOW>APN383,qAR,WASHOE:!3909.25N111952.99W#PHG2830/NVn,NCAn,TEMPn/WG6D/Snow Valley Peak, NV/A=009214 +2026-03-02 15:49:31 CET: KI4WXS-R>APDG03,TCPIP*,qAC,KI4WXS-RS:!3517.84N\08106.92Wo/A=00077770cm MMDVM Voice (C4FM) 444.02500MHz +5.0000MHz, Dstar/Fusion/P25/NXDN +2026-03-02 15:49:31 CET: KI4WXS-R>APDG03,qAS,KI4WXS:>Powered by WPSD (https://wpsd.radio) +2026-03-02 15:49:31 CET: W7JLW-10>APSK21,TCPIP*,qAC,T2PERTH:=3240.83N/11425.43Wk246/004/A=000292!SN! +2026-03-02 15:49:31 CET: DJ1JAY-14>UR1VW6,WIDE1-1,WIDE2-2,qAO,DM0SZ-1:`<0x7f>RSr#?P/`"4s}Jens https://fm-funknetz.de :-)_4 +2026-03-02 15:49:31 CET: IR3UBZ>APBM1S,TCPIP*,qAS,BM2222:>https://brandmeister.network/?page=repeater&id=222032 +2026-03-02 15:49:31 CET: FW5122>APRS,TCPXX*,qAX,CWOP-5:@144830z3248.53N/09645.83W_013/000g006t064P000h78b10196.ewund +2026-03-02 15:49:31 CET: EL-EC4DA2>RXTLM-1,TCPIP,qAR,EC4DA2::EL-EC4DA2:UNIT.RX Erlang,TX Erlang,RXcount/10m,TXcount/10m,none1,STxxxxxx,logic +2026-03-02 15:49:31 CET: SP6EU-10>APLRG1,WIDE1-1,qAR,SP6AW-10:=L4XYdRU[fa !GLoRa APRS +2026-03-02 15:49:31 CET: EL-EC4DA2>RXTLM-1,TCPIP,qAR,EC4DA2:T#070,8862313.00,0.04,0,5,0.0,00000000,TetraLogic +2026-03-02 15:49:31 CET: 9A3BMY-10>APBM1D,DMR*,qAS,BM2341-10:=4616.84N/01623.48Eb360/000/A=000707Drazen +2026-03-02 15:49:31 CET: KC5DAQ-27>APN000,TCPIP*,qAC,T2TEXAS:@021449z3002.03N/09551.93W_145/009g014t067r000p000P000b10110h94L208eMB60 +2026-03-02 15:49:31 CET: ED3YBB-S>APDG01,TCPIP*,qAC,ED3YBB-GS:;ED3YBB A *021449z4215.02ND00311.37EaRNG0019 1.2 Voice 1298.57500MHz -28.0000MHz +2026-03-02 15:49:31 CET: DW0479>APN000,TCPXX*,qAX,CWOP-6:@021449z2801.57N/08132.28W_107/005g013t076r000p000P000b10231h72L554eMB63 +2026-03-02 15:49:31 CET: ER-EA2BKH>APSVX1,WIDE1-1,qAS,EA2BKH:T#076,000,061,000,007,000,00000000 +2026-03-02 15:49:31 CET: ZS0HTN>APDW17,WIDE1-1,qAO,ZS6VOL-10:APVRT7,TCPIP*,qAC,T2YANTAI:!3020.94N/12045.72Er +13.6C 11.9V +2026-03-02 15:49:31 CET: DF2TH-B>APDG02,TCPIP*,qAC,DF2TH-BS:!5312.11ND00837.89E&RNG0001/A=000010 70cm Voice (D-Star) 439.95000MHz -9.4000MHz +2026-03-02 15:49:31 CET: HB0PJ-13>APRS,TCPIP*,qAS,HB0PJ:@021449z4709.86N/00930.73E_.../...t058h48b09649L062KNX / aprs-weather-submit/1.6 +2026-03-02 15:49:31 CET: CW7077>APRS,TCPXX*,qAX,CWOP-6:@021449z3524.00N/08030.00W_086/000g000t046r000p000P000h69b10330.DsVP +2026-03-02 15:49:31 CET: GW3617>APREST,TCPXX*,qAX,CWOP-3:@021447z2833.83N/08120.89W_127/005g007t071r000P000h73b10240L562eREST +2026-03-02 15:49:31 CET: BG2GWN-7>APID98,WIDE1-1,qAO,BG2EFX-10:!4635.65N/12505.21E!000/000/A=000626144.640 8.5V S14 +2026-03-02 15:49:31 CET: 9A3HVZ>APDR16,TCPIP*,qAC,T2ERFURT:=4602.40N/01603.34EQ254/050/A=000639 MOBILE DMR TG:21966 +2026-03-02 15:49:31 CET: KO4QAH-B>APDG02,TCPIP*,qAC,KO4QAH-BS:!3110.50ND08129.10W&RNG0001/A=000010 70cm Voice (D-Star) 445.60000MHz +0.0000MHz +2026-03-02 15:49:31 CET: F1SFY>APZ900,WIDE1-1,WIDE2-1,qAR,F5ZFY-3:=4917.11N/00212.99E-En test +2026-03-02 15:49:31 CET: LD4SH>APOT30,WIDE2-1,qAO,LA3YNA-15:;LA6SR *111111z5846.05N/00906.35Er145.775MHz t107 -0600kHz +2026-03-02 15:49:31 CET: KC9YJD-D>APDG03,TCPIP*,qAC,KC9YJD-DS:!3807.13ND08959.98W&/A=00000070cm MMDVM Voice (DMR) 439.00000MHz -5.0000MHz, APRS for DMRGateway +2026-03-02 15:49:31 CET: OE7XXR-10>APMI06,TCPIP*,qAC,OE2XZR-10:;OE7XXR-B *181524z4727.99N/01149.59Er 438,200 -7,6MHz DSTAR DCS009A U=13.7V,T=3.8C +2026-03-02 15:49:31 CET: YM9KE>AESPG4,TCPIP*,qAC,APRSTH:>V.4.4b Rx:2058 Digi:792 Tx:4166 UpTime:74.49 DX:27-02-2026 15:55Loc TA9WDX-7 29km 320<0xc2><0xb0> +2026-03-02 15:49:31 CET: DB0SPB>APOT21,WIDE1-1,WIDE3-3,qAR,DB0NLS-10:!5057.62N/01441.40E#12.2V 29C APRS-Digi Spitzberg +2026-03-02 15:49:31 CET: JA0ZJX-10>APU25N,TCPIP*,qAC,T2FUKUOKA:=I<33(qB3h& BIgate Matsumoto 144.66MHz/1200bps {UIV32N} +2026-03-02 15:49:31 CET: IR3UJ>APBM1S,TCPIP*,qAS,BM2222:@021449z4548.67N/01332.00ErPHG0000ARI RCE FVG 430.0750/431.6750 CC1 +2026-03-02 15:49:31 CET: IR3UJ>APBM1S,TCPIP*,qAS,BM2222:>https://brandmeister.network/?page=repeater&id=222033 +2026-03-02 15:49:31 CET: SQ8MAE-1>APLRFD,WIDE1-1,WIDE2-1,QB3N6,qAR,SR8RK:!L5<&{T"Pt# GLoRa APRS Digi Godowa|$Q!q"V!a%V"X| +2026-03-02 15:49:31 CET: YF9UAG-16>AESPG4,TCPIP*,qAC,T2TAIPEI::YF9UAG-16:PARM.RxTraffic,TxTraffic,RxDrop,RSSI,SUN +2026-03-02 15:49:31 CET: JH4GQC>APU25N,TCPIP*,qAC,T2FUKUOKA:;WX RADAR *162315z3416.22N/13235.59E`Mt.HAIGAMINE Weather radar station +2026-03-02 15:49:32 CET: GOLDWX>APOTW1,PMETTO,WIDE1*,WIDE2-2,qAR,KB7REO-10:> 13.2V (Solar) N6EID / Gold Point Ghost Town WX Station +2026-03-02 15:49:32 CET: PY3JRT-15>APNU3B,WIDE1-1,WIDE2-1,qAR,PY3NZ-10:!3023.42S/05144.39W#PHG6000/REDE SUL APRS BARAO DO TRIUNFO RS +2026-03-02 15:49:32 CET: KB1YJ-1>APOT40,TCPIP*,qAC,T2CHILE:> +2026-03-02 15:49:32 CET: KD8IAY-11>APLRG1,TCPIP*,qAC,T2TEXAS:!L9q5b8TNFa GLoRa APRS +2026-03-02 15:49:32 CET: EW3240>APN000,TCPXX*,qAX,CWOP-5:@021449z4347.07N/08745.98W_159/006g012t031r000p000P000b10301h72L283eMB58 +2026-03-02 15:49:32 CET: 9A3HVZ>APDR16,TCPIP*,qAC,T2ERFURT:=4602.39N/01603.32EQ255/051/A=000639 MOBILE DMR TG:21966 +2026-03-02 15:49:32 CET: DB0BIN>APGE01,TCPIP*,qAC,T2AUSTRIA:>021449zat Home +2026-03-02 15:49:32 CET: ED7ZAD-3>APMI01,WIDE2-2,qAO,EA7KAV-15:;ED7YAC *021449z3756.87N/00448.58Wr145.675(-0.6) Sub77 & C4FM +2026-03-02 15:49:32 CET: CW9372>APRS,TCPXX*,qAX,CWOP-7:@021449z4248.95N/08607.20W_112/001g005t027r000p002P000h69b10332.DsVP +2026-03-02 15:49:31 CET: EI3JE-9>APAT51,EI2FHP-2*,WIDE2-1,qAR,EI2WCP-1:!5153.46N/00800.20Wj229/000/A=000393 Listening SIRN +2026-03-02 15:49:31 CET: KD4FJ-R>APDG03,TCPIP*,qAC,KD4FJ-RS:!2610.89ND09745.57W&/A=00000070cm MMDVM Voice (C4FM) 443.87500MHz +5.0000MHz, KD4FJ_Pi-Star_RPT +2026-03-02 15:49:31 CET: ONIONM>APLRG1,WIDE1-1,WIDE2-2,qAR,KD7YAT-44:=L=#X>228"# !GLoRa APRS Digi Onion Mtn 433.775 MHz BW125 SF7 CR4:6 DBUF+T de N7UV-61|$N%Z$$| +2026-03-02 15:49:31 CET: KQ9A-3>BEACON,qAR,W9LRT-1:KQ9A-3 APRS Digipeater Bourbon, IN +2026-03-02 15:49:31 CET: HS8KAY>AESPG4,TCPIP*,qAC,T2TAIPEI:T#3076,0,0,0,43,0,00000000 +2026-03-02 15:49:31 CET: HS8KAY>AESPG4,TCPIP*,qAC,T2TAIPEI:>V.4.2a SSID:HamShack-2.4G Rx:0 Digi:0 Tx:0 UpTime:23.00 +2026-03-02 15:49:31 CET: WINLINK>APWL2K,TCPIP*,qAS,WLNK-1:;KD0ZEA-10*020249z3823. NW09042. Wa145.070MHz Winlink Packet Gateway +2026-03-02 15:49:31 CET: DK5EW-9>APLRT1,WIDE1-1,qAO,DA0Y-10:!/60{>Pm:>>JdQ +2026-03-02 15:49:31 CET: YB3JF-1>APK5C1,YH3NPX-4*,qAR,YH3NPX-2:!0726.64S/11243.18ED +2026-03-02 15:49:31 CET: DG3NZ-10>APLRG1,TCPIP*,qAC,FIRST:!L5E+EQ>i<& GLoRa APRS, oberes Schwabachtal +2026-03-02 15:49:31 CET: KN4JRZ-N>APDG03,TCPIP*,qAC,KN4JRZ-NS:!5000.00ND00300.00W&/A=00000070cm MMDVM Voice (C4FM) 432.80000MHz +0.0000MHz, KN4JRZ_Pi-Star_ND +2026-03-02 15:49:32 CET: DB0FRB>APRX29,TCPIP*,qAC,T2GREECE:;DB0FIS *111111z4802.51N/00810.17ErC4FM-Relais 438.7375MHz -7.6; WIRES-X Raum DL-Suedwest +2026-03-02 15:49:32 CET: CA7TRF-4>APFII0,TCPIP*,qAC,APRSFI:@144929h4107.75S/07122.16W$166/006/A=002714QAP 146.520!wb1! +2026-03-02 15:49:32 CET: VE1BOF-D>APDG03,TCPIP*,qAC,VE1BOF-DS:!4500.89ND06453.47W&/A=00000070cm MMDVM Voice (DMR) 446.28700MHz +0.0000MHz, APRS for DMRGateway +2026-03-02 15:49:32 CET: W6JKL-9>S6PS0X,WIDE1-1,WIDE2-1,qAR,KI5ZMB:`{KPl]Bk/`"6D}_% +2026-03-02 15:49:32 CET: KQ9A-3>APRS,WIDE1-1,qAR,W9LRT-1:!4117.33NS08606.89W#PHG2390/A=000866 Bourbon IN +2026-03-02 15:49:32 CET: DO7DH-5>APRS,TCPIP*,qAC,T2LAUSITZ:@021449z4809.75N/01237.07E_.../...g...t056r...p...P...h61b10238InnSalzachWetter +2026-03-02 15:49:32 CET: KN4PAH-1>APDW18,WIDE1-1,WIDE2-1,qAR,N4VGW-1:!S;YNo:Dtm# ! Town of Bedford-Bedford VA DigiPi +2026-03-02 15:49:32 CET: 9M2EDK>APRX29,9M2EDK-1*,qAR,9M2OCX-1:!0257.18N\10134.70EhHAM RADIO STORE - QUEBEC VENTURES, (Facebook)-https://www.facebook.com/quebecventures | (WEBSITE)-https://www.quebecventures.com.my +2026-03-02 15:49:32 CET: KF0FBG-7>SYQWLZ,STLSMI,WIDE1*,WIDE2-1,qAO,KI0AU-2:`z=8l <0x1c>[/`monitoring w0te & 146.52_3 +2026-03-02 15:49:32 CET: ER-OE7XWT>RXTLM-1,TCPIP,qAR,OE7XWT::ER-OE7XWT:UNIT.RX Erlang,TX Erlang,RXcount/10m,TXcount/10m,none1,STxxxxxx,logic +2026-03-02 15:49:32 CET: ER-OE7XWT>RXTLM-1,TCPIP,qAR,OE7XWT:T#297,0.00,0.05,0,4,0.0,01000000,RepeaterLogic +2026-03-02 15:49:32 CET: KN4IJF-1>APMI04,WIDE2-2,qAR,W4AD-10:@021331z3846.88N/07725.93W#WX3in1Mini U=13.8V. +2026-03-02 15:49:32 CET: DB0ZD-10>APMI01,TCPIP*,qAS,DB0ZD:!4732.21N/01012.98ErPHG3200 W3 (R84)439,000MHz -7,6MHz +2026-03-02 15:49:32 CET: DB9VL-22>APLT00-1,DJ0QO-10*,qAO,DB0IKS-10:=/5R63P1{8< P[Meshcom4 Z19<<0xff><0x01>F +2026-03-02 15:49:32 CET: AE4OK-10>APRX29,TCPIP*,qAC,T2USANE:!3735.36NI07727.09W#PHG6200 Digi & TX-iGate in Ginter Park Subdivision, Richmond, VA +2026-03-02 15:49:32 CET: IR7UCN>APBM1S,TCPIP*,qAS,BM2222:@021449z4007.13N/01818.23ErPHG0000Repeater UHF 430.4500/435.4500 CC1 +2026-03-02 15:49:32 CET: IR7UCN>APBM1S,TCPIP*,qAS,BM2222:>https://brandmeister.network/?page=repeater&id=222045 +2026-03-02 15:49:32 CET: NG7I-B>APDG02,TCPIP*,qAC,NG7I-BS:!4707.32ND11822.38E&RNG0001/A=000010 440 Voice 440.30000MHz +5.0000MHz +2026-03-02 15:49:32 CET: AF4IG-8>EAAQXP,WIDE1-1,qAR,W0NED:`pVL<0x1c><0x1d>0R/4100FF006A667B08 +2026-03-02 15:49:31 CET: KD2RCQ>APOSB,TCPIP*,qAC,T2CAEAST:@021449z4044.88N\07348.07W&/A=000000SharkRF openSPOT3 +2026-03-02 15:49:31 CET: LA5PPA-1>APMI01,TCPIP*,qAC,SEVENTH:@021449z6033.70N/00915.37E_198/000g006t031r000p000P000h92b10186Liemarka, Nesbyen +2026-03-02 15:49:31 CET: PU5DSM-B>APDG02,TCPIP*,qAC,PU5DSM-BS:!2734.44SD04831.83W&RNG0001 440 Voice 433.09000MHz +0.0000MHz +2026-03-02 15:49:32 CET: IR0UIZ>APBM1S,TCPIP*,qAS,BM2222:@021449z4233.24N/01239.99ErPHG0000IR0UIZ - Terni 430.9375/435.9375 CC1 +2026-03-02 15:49:32 CET: IR0UIZ>APBM1S,TCPIP*,qAS,BM2222:>https://brandmeister.network/?page=repeater&id=222038 +2026-03-02 15:49:32 CET: SNOBNK>APN391,qAR,K7RYH-2:!4426.57NS11608.17W#PHG7230 W2,IDn-N,SnowbankMt W7VOI +2026-03-02 15:49:32 CET: BG3OTT>APBM1D,BG3OTT,DMR*,qAR,BG3OTT:@144925h3222.68N/11557.77E8000/000Hytera PNC380 Pro +2026-03-02 15:49:32 CET: EA1BQO>APU25N,TCPIP*,qAC,T2SPAIN:>261849zUI-View32 V2.03 +2026-03-02 15:49:32 CET: 9M2PHQ-7>P4TRW7,WIDE1-1,qAR,9M2SGN-1:`lTkq7$>/"4i} +2026-03-02 15:49:32 CET: IR1DO>APBM1S,TCPIP*,qAS,BM2222:@021449z4407.70N/00951.25EQPHG0000Simplex VHF Hotspot 145.4250/145.4250 CC1 +2026-03-02 15:49:32 CET: IR1DO>APBM1S,TCPIP*,qAS,BM2222:>https://brandmeister.network/?page=repeater&id=222041 +2026-03-02 15:49:32 CET: BH2UMM>APET51,TCPIP*,qAC,T2YANTAI:!3827.78N/10615.92EyBR9OO <0xe9><0x93><0xb6><0xe5><0xb7><0x9d><0xe4><0xb8><0x9a><0xe4><0xbd><0x99><0xe6><0x97><0xa0><0xe7><0xba><0xbf><0xe7><0x94><0xb5><0xe6><0x95><0xb0><0xe6><0xa8><0xa1><0xe4><0xb8><0xad><0xe7><0xbb><0xa7> 439.550 -8 T88.5 / S1~ +2026-03-02 15:49:32 CET: F6GVH-1>APNW01,TCPIP*,qAS,F6GVH::F6GVH-1 :PARM.I1,I2,U1,U2,Temp +2026-03-02 15:49:32 CET: F6GVH-1>APNW01,TCPIP*,qAS,F6GVH::F6GVH-1 :UNIT.mA,mA,Volt,Volt,C +2026-03-02 15:49:32 CET: F6GVH-1>APNW01,TCPIP*,qAS,F6GVH::F6GVH-1 :EQNS.0,19.5,0,0,19.5,0,0,0.1,0,0,0.1,0,0,0.5,-64 +2026-03-02 15:49:32 CET: WG6G-5>APDR16,TCPIP*,qAC,T2SWEDEN:=3347.41N\11805.06W>019/025/A=-00109 https://aprsdroid.org/ +2026-03-02 15:49:32 CET: LZ0SUN-B>APDG02,TCPIP*,qAC,LZ0SUN-BS:!4233.78ND02317.06E&RNG0062 440 Voice 438.42500MHz -7.6000MHz +2026-03-02 15:49:32 CET: CW7929>APRS,TCPXX*,qAX,CWOP-4:@021449z3406.02N/11623.30W_259/000g000t059r000p000P000h46b10143.DsVP +2026-03-02 15:49:32 CET: DL1NBU-11>APLG01,TCPIP*,qAC,T2SWEDEN:=5020.62NL01135.66E&LoRa_APRS_iGate DL1NBU-11 Frankenwald +2026-03-02 15:49:32 CET: DL4MGR-8>APRS,qAO,DL4MGR-10:T#084,45,48,31 +2026-03-02 15:49:32 CET: DL9CK>APMI0A,WIDE1-1,WIDE2-1,qAR,DO7AD:!5202.29N\01127.02E-Fill-in-Digi, 3W, X200, T=13.0C, U=12.2V +2026-03-02 15:49:32 CET: K7DCJ>T7TTQQ,WIDE1-1,WIDE2-1,qAR,K7NWS-2:'2PJl <0x1c>-/]445.950MHz-500 T110 V-Alert +2026-03-02 15:49:32 CET: W4IFI>APRS,TCPIP*,qAC,CWOP-3:@021449z3711.65N/10802.20W_039/003g006t040r000p000P000h62b10187eCumulusDsVP +2026-03-02 15:49:32 CET: VE9GM-15>TVUP4T,VE9GFL-3*,WIDE2-1,qAR,VE9GM-10:`_Frr6 k/`"6(}147.195MHz Toff +060 Gino On IRG_% +2026-03-02 15:49:32 CET: N4WDH-9>RXQQ2W,qAR,N4WDH-7:`n11p!lj/`"4-}_% +2026-03-02 15:49:32 CET: W0NE-4>BEACON,WIDE1-1,WIDE2-2,qAR,W0NE-3:;146.835-R*111111z4404.61N/09136.25Wr146.835MHz C131 -060 R45m NET:Sun@830p +2026-03-02 15:49:32 CET: DW2589>APN000,TCPXX*,qAX,CWOP-3:@021449z4210.96N/07046.26W_037/001g009t020r000p002P000b10387h41eMB63 +2026-03-02 15:49:32 CET: DC1CHS-2>APLT00-1,WIDE1-1,qAO,DE0KFD-1:=/6+;NQ`_Gb P[ +2026-03-02 15:49:32 CET: N7HRZ-9>SPRS8W,W4NN-1*,WIDE1*,KJ4YNR-1*,WIDE2*,qAR,WA4USN-5:`r?tlr<0x1f>>/`"3n}_% +2026-03-02 15:49:32 CET: EW6940>APRS,TCPXX*,qAX,CWOP-3:@021449z4140.10N/07234.40W_084/000g000t020r000p000P000b10379h35eMB42 +2026-03-02 15:49:31 CET: ZS4PR-4>APCLEY,TCPIP*,qAC,APRS-ZA:/021449z2610.99S/02756.38E>131/005/A=005555 53C 1Mv 4.16V In 13.06V 9kmh +2026-03-02 15:49:31 CET: BI9BBL-10>APE32A,TCPIP*,qAC,T2TOKYO:>https://github.com/nakhonthai/ESP32APRS_Audio +2026-03-02 15:49:31 CET: EW2561>APN000,TCPXX*,qAX,CWOP-6:@021449z5056.52N/00553.25E_259/001g004t064r000p000P000b10163h49L313eMB63 +2026-03-02 15:49:32 CET: K5CS-1>APX01,qAO,K5FRP-10:!3513.50NS09315.26W#PHG6760/ W2, ARn-N RUSSnet APRS Digi +2026-03-02 15:49:32 CET: KC3PWY-13>APRS,TCPXX*,qAX,CWOP-3:@021449z4032.09N/07955.84W_337/001g005t024r000p000P000h64b10349 +2026-03-02 15:49:32 CET: HB9TQL-10>APMI06,TCPIP*,qAS,HB9TQL:@021449z4731.36NI00742.50E&iGate Pratteln (RX only) http://hb9tql.ch +2026-03-02 15:49:32 CET: DN9HAN-7>APDR17,TCPIP*,qAC,T2LAUSITZ:=5308.37N/01115.81EU034/027/A=000190 Andy@Work +2026-03-02 15:49:32 CET: ON7DS-B>APDG02,TCPIP*,qAC,ON7DS-BS:!5107.66ND00428.33E&RNG0001/A=000010 70cm Voice (D-Star) 433.45000MHz +0.0000MHz +2026-03-02 15:49:32 CET: HAMLTN>APMI06,TCPIP*,qAC,W8FSM-MI:!4242.32NS08600.33W#W2,HamiltonMI Digi/Igate W8FSM U=14.2V +2026-03-02 15:49:32 CET: NJ3T-4>APN382,WIDE2-2,qAR,AC3DB-10:;443.725PA*111111z3948.43N/07910.31Wr443.725MHz T127.3 Allmode +2026-03-02 15:49:32 CET: SM5YNB-9>UX2U01,qAR,SM0RGQ-11:`+C&l>Uj/`"44}434.600MHz 145.400 alt 434.825Mhz_% +2026-03-02 15:49:32 CET: E27AC-1>APRX20,TCPIP*,qAC,T2INDY:!1329.18NI10115.85E&FRIEND DEPTH OF NIGHT 144.6625MHz +2026-03-02 15:49:32 CET: GW0NEN-2>APDW18,TCPIP*,qAC,T2BC:!R3uhBMKG@& ! DigiPi http://digipi.org/ +2026-03-02 15:49:32 CET: OE3MZC-2>APMI01-2,TCPIP*,qAS,KW4XC:@021449z4812.06N/01555.87E& Igate in Neulengbach 144.800/432.500Mhz +2026-03-02 15:49:32 CET: IR4UCO>APBM1S,TCPIP*,qAS,BM2222:@021449z4434.47N/01122.11ErPHG0000IR4UCO - Bologna 430.6125/435.6125 CC1 +2026-03-02 15:49:32 CET: IR4UCO>APBM1S,TCPIP*,qAS,BM2222:>https://brandmeister.network/?page=repeater&id=222046 +2026-03-02 15:49:32 CET: SP8NFE-1>APMI01,TCPIP*,qAS,K2RPF:@021449z4951.44N/02221.40E_180/000g000t046r000p000P000h81b00000WX3in1 weather +2026-03-02 15:49:32 CET: IR3AS>APMI06,TCPIP*,qAC,T2POLAND:@021449z4603.03N/01120.08E_315/000g000t031r000p000P...h86b08575Meteo Cima Panarotta 2000 mt +2026-03-02 15:49:32 CET: SP8NFE-1>APMI01,TCPIP*,qAS,K2RPF:@021449z4951.44N/02221.40E-WX3in1Plus +2026-03-02 15:49:32 CET: VK4CWL-13>APOTW1,WIDE1-1,WIDE2-1,qAR,VK4GDW-3:!2338.72S/14805.91E_090/001g002t073P000h83b10122V124OTW1 +2026-03-02 15:49:32 CET: EW5498>APN000,TCPXX*,qAX,CWOP-4:@021449z4648.14N/00709.08E_.../...g...t053r000p000P000b10217h65eMB63 +2026-03-02 15:49:33 CET: VE1PRB-1>APWW11,TCPIP*,qAC,T2PERTH:) +2026-03-02 15:49:33 CET: HS3LSE-10>APINDY,WIDE1-1,APRS,qAR,HS3LSE-1:)SR-BANRAE!1458.68N/10336.44Ea090/011 +2026-03-02 15:49:33 CET: SQ2MSL-10>APLRG1,TCPIP*,qAC,T2POLAND:!L3puuS>rC# !GLoRa APRS W<0xc5><0x82>oc<0xc5><0x82>awek Wsch<0xc3><0xb3>d - Zapora Batt=4.20V Ext=0.55V +2026-03-02 15:49:33 CET: 2E0SWI-1>APMI04,TCPIP*,qAS,2E0SWI-13:@021449z5047.47NR00109.96W&WX3in1Mini RX iGate U=12.3V. +2026-03-02 15:49:33 CET: DH7SW-D>APDG03,TCPIP*,qAC,DH7SW-DS:!4841.12ND00955.83E&/A=00000070cm MMDVM Voice (DMR) 430.03750MHz +9.8000MHz, APRS for DMRGateway +2026-03-02 15:49:33 CET: DB3BEF-S>APDG01,TCPIP*,qAC,DB3BEF-GS:;DB3BEF B *021449z5018.60ND00736.60EaRNG0001/A=000010 70cm Voice (D-Star) 433.67500MHz +0.0000MHz +2026-03-02 15:49:33 CET: ER-DB0SRS>RXTLM-1,TCPIP,qAR,DB0SRS::ER-DB0SRS:UNIT.RX Erlang,TX Erlang,RXcount/10m,TXcount/10m,none1,STxxxxxx,logic +2026-03-02 15:49:33 CET: ER-DB0SRS>RXTLM-1,TCPIP,qAR,DB0SRS:T#362,0.00,0.02,0,1,0.0,00000000,RepeaterLogic +2026-03-02 15:49:33 CET: BG6KQI-7>APN000,TCPIP*,qAC,T2OSAKA:=2505.81N\11701.60Ev<0xe6><0xac><0xa2><0xe8><0xbf><0x8e>QSO438.500 +2026-03-02 15:49:32 CET: HEAT-60>APRPJH,TCPIP*,qAC,APRPJU:=0417.28N/10325.72E:High heat signature detected by 9M2PJU APRS Bot. For more info, visit https://hamradio.my +2026-03-02 15:49:32 CET: N1TKS>APU25N,WB2ZII,KB2EAR-1,WIDE2,qAR,N2ARC:@021445z4100.51N/07339.16W_067/002g005t025r000p000P000h46b10394Davis Pro Wx station/ 50 watts out {UIV32N} +2026-03-02 15:49:32 CET: SR9LC-1>APRX29,TCPIP*,qAC,T2CSNGRAD:;438.700LC*161707z5041.95N/01838.54Er438.700MHz c071 -760 R60k SR9LC Przemiennik Lubliniecki +2026-03-02 15:49:32 CET: EI6HKB-10>APWW11,TCPIP*,qAC,T2CSNGRAD:>Baywatch Station +2026-03-02 15:49:32 CET: EL-EA3GXK>APSVX1,WIDE1-1,qAS,EA3GXK:T#076,000,000,000,000,000,00000000 +2026-03-02 15:49:32 CET: DO7RMO-2>APRSGW,TCPIP*,qAR,DO7RMO-2:!4853.90N/01256.44E>gerade da#Magnus/B=100/A=001601 +2026-03-02 15:49:32 CET: FW1210>APRS,TCPXX*,qAX,CWOP-5:!4258.16N/08804.13W_194/001g004t029r000P000p000h67b10300.VWS-DavisVP2 +2026-03-02 15:49:32 CET: VA3WWD-2>APTW14,VE3ZAP,qAR,VE3GSJ:_03051029c180s000g000t010r000p000P000h..b.....tU2k +2026-03-02 15:49:32 CET: F4IRV-1>APRS,TCPIP*,qAS,F4GOH-10:!4749.57N\00016.98E-Home +2026-03-02 15:49:32 CET: IW9HRH-12>APLRG1,WIDE1-1,qAO,IW9HRH-10:!L;9w-Qf@(# GLoRa APRS +2026-03-02 15:49:32 CET: DL9MBI-10>APLG01,TCPIP*,qAC,T2UK:=4806.70NL01244.35E&LoRa iGATE DL9MBI Plattenberg/Burgkirchen +2026-03-02 15:49:32 CET: 8P6PE-1>APMI04,TCPIP*,qAC,T2SYDNEY:@021449z1309.05N/05933.36W-WX3in1Mini Hello from BARBADOS +2026-03-02 15:49:32 CET: GW1754>APRS,TCPXX*,qAX,CWOP-6:@021445z5209.39N/00709.49W_216/008g015t051r000p022P006b10118h78.weewx-4.5.1-MetpakPro +2026-03-02 15:49:32 CET: AC0ID-10>APRS,TCPIP*,qAC,T2USANW:>MyPos v2.1.2 for Android +2026-03-02 15:49:33 CET: YB0SPS-14>APE32L,TCPIP*,qAC,T2TAS:>Lora iGate APRS with ESP32 Lora +2026-03-02 15:49:33 CET: IR0ZXU-B>APDG02,TCPIP*,qAC,IR0ZXU-BS:!4147.67ND01245.80E&RNG0037/A=000082 70cm Voice (D-Star) 430.21250MHz +1.6000MHz +2026-03-02 15:49:33 CET: BI9BZG-3>APFMO1,TCPIP*,qAC,T2HK:=3421.72NF10844.41EiFMO-STATION,CN,485,<0xe9><0x99><0x95>D-FMO<0xe4><0xb8><0xad><0xe7><0xbb><0xa7>,8.137.152.203,P1883,F5000KM,U5/25,PASS:yW5IhrxSGDXAD0ZLG2LiVQ==,SIGN:xMAP0ZYmQ1jdUcGSbQ5/JA== +2026-03-02 15:49:33 CET: BI9BZG-3>APFMO1,TCPIP*,qAC,T2HK:><0xe6><0xad><0xa3><0xe5><0xb8><0xb8>,<0xe5><0x9c><0xa8><0xe7><0xba><0xbf>/<0xe5><0xb3><0xb0><0xe5><0x80><0xbc>:5/25,<0xe5><0x92><0xb8><0xe9><0x98><0xb3><0xe5><0x85><0xac><0xe5><0xae><0x89><0xe6><0xb8><0xa9><0xe9><0xa6><0xa8><0xe6><0x8f><0x90><0xe9><0x86><0x92> <0xe6><0x8b><0x92><0xe7><0xbb><0x9d><0xe7><0x87><0x83><0xe6><0x94><0xbe><0xe7><0x83><0x9f><0xe8><0x8a><0xb1><0xe7><0x88><0x86><0xe7><0xab><0xb9> +2026-03-02 15:49:33 CET: HP1MRA-1>APET51,WIDE1-1,WIDE2-1,qAS,HP1UPR-1:!0904.25N/07925.95W#UNI<0xd3>N PANAME<0xd1>A DE RADIOAFICIONADOS TG7146 DMR UPRA 11.9V +2026-03-02 15:49:33 CET: VA7MLZ-9>TY0PPU,WIDE1-1,WIDE2-1,qAO,MSPK39:`3M;l"-k/`"3x}_" +2026-03-02 15:49:33 CET: KC5DAQ-25>APN000,TCPIP*,qAC,T2CAEAST:@021449z2946.99N/09540.93W_189/001g004t068r000p000P000b10114h85L188eMB63 +2026-03-02 15:49:33 CET: IW0EFJ-9>APN000,WIDE1-1,WIDE2-1,qAR,IZ0ANE-2:=4127.62N/01348.69E>360/000/A=000314APRS 432ARICASSINO 12.80V +2026-03-02 15:49:33 CET: TA5ADB>APDR16,TCPIP*,qAC,T2KA:=3706.39N/03722.90Ee144.850MHz/A=003061 73!... +2026-03-02 15:49:33 CET: N7QIN-1>EGFYPR,WIDE1-1,WIDE2-1,qAR,UCAPK:'3=hl <0x1c>K\]147.160MHzHome Station= +2026-03-02 15:49:33 CET: SUNLGT>APMI04,TCPIP*,qAC,T2TEXAS:T#107,131,135,003,069,166,00000000 +2026-03-02 15:49:33 CET: WA1EM>APDR16,TCPIP*,qAC,T2SWEDEN:=3852.49N/07708.65W$147.300MHz/A=000226 Skywarn <0xe2><0x80><0xa2> CERT +2026-03-02 15:49:33 CET: FW5399>APN000,TCPXX*,qAX,CWOP-7:@021445z3805.82S/17641.26E_264/000g002t048r000p000P000b10099h70eMB63 +2026-03-02 15:49:33 CET: K3ORB-10>APMI06,TCPIP*,qAS,K3ORB:@021449z3825.22N/07503.31W#Worcester Radio Grp U=13.1V,T=63.1F +2026-03-02 15:49:33 CET: G0NMP-N>APDG03,TCPIP*,qAC,T2SPAIN:!5234.22ND00051.84E&/A=000000440 MMDVM Voice 438.80000MHz +0.0000MHz, G0NMP_Pi-Star +2026-03-02 15:49:33 CET: DB0BIN>APGE01,TCPIP*,qAC,T2AUSTRIA:!4818.27N/00845.69E_180/000g...t056r...p000h..b10199 +2026-03-02 15:49:32 CET: KAZTUBAC1>APRS,TCPXX*,qAX,CWOP-4:@021449z3137.36N/11104.12E_000/000g003t048r000p000P000h40b10127WeatherCatV320B364H31 +2026-03-02 15:49:32 CET: K4GVE-N>APDG03,TCPIP*,qAC,K4GVE-NS:!3809.36ND07809.95W&/A=00000070cm MMDVM Voice (C4FM) 446.52500MHz +0.0000MHz, K4GVE_Pi-Star_ND +2026-03-02 15:49:32 CET: 9W2ICC-4>P3PYW0,WIDE1-1,qAO,9W4GHT-1:`mC%l#X>/"5F}Mobile Batt=4.26V100% Sats:6 Dist:0.0km MaxSpd:0.0kmh Time:0h5m AvgSpd:0.0kmh|(@%_| +2026-03-02 15:49:32 CET: DB0OE>APMI06,TCPIP*,qAC,T2POLAND::DB0OE :PARM.Vin,Rx1h,Dg1h,Eff1h,Eff,O1,O2,O3,O4,I1,I2,I3,I4 +2026-03-02 15:49:32 CET: BG5DZF-9>APRS4G,TCPIP*,qAC,T2FUKUOKA:/224932h2859.00N/12024.93Eb319/003/A=001268 A:-48 B:20/31 C:3.8V D:43<0xc2><0xb0>C +2026-03-02 15:49:32 CET: N0KFB-11>T5PT3P,WIDE1-1,WIDE2-1,qAR,KD0TLS-4:`y*hl|B>/"6{}Out runnin' 'round / Mon 444.750,146.58,14].52 +2026-03-02 15:49:32 CET: DB0OE>APMI06,TCPIP*,qAC,T2POLAND::DB0OE :UNIT.Volt,Pkt,Pkt,Pcnt,Pcnt,On,On,On,On,Hi,Hi,Hi,Hi +2026-03-02 15:49:32 CET: DB0TN-10>AP4R10,TCPIP*,qAC,HAMCLOUD3:!4820.32NI00809.24E&APRS4R IGATE Brandenkopf +2026-03-02 15:49:32 CET: FK4RG-9>TW5SQR,WIDE1-1,WIDE2-2,qAR,F5ILB:`w1-m"ov/`"55}_# +2026-03-02 15:49:32 CET: DB0OE>APMI06,TCPIP*,qAC,T2POLAND::DB0OE :EQNS.0,0.075,0,0,10,0,0,10,0,0,1,0,0,1,0 +2026-03-02 15:49:32 CET: DB0OE>APMI06,TCPIP*,qAC,T2POLAND::DB0OE :BITS.11111111,WX3in1Plus20 Telemetry +2026-03-02 15:49:32 CET: W4GCW>APWW11,N1KSC-1*,WIDE2-1,qAR,KM4ZYG-10:@144928h/@4Vs:*)2- sT(Time 0:00:00) +2026-03-02 15:49:32 CET: DO1NIC-S>APDG01,TCPIP*,qAC,DO1NIC-GS:;DO1NIC B *021449z5234.00ND01320.00EaRNG0001/A=000010 70cm Voice (D-Star) 435.00000MHz +0.0000MHz +2026-03-02 15:49:32 CET: WB6YNM-4>ID,qAO,KK6SEN-2:WB6YNM-4/R DISABL/D BBOX/B KBERR/N +2026-03-02 15:49:33 CET: HB9EKJ-B>APDG02,TCPIP*,qAC,HB9EKJ-BS:!4702.40ND00904.80E&RNG0001/A=000010 70cm Voice (D-Star) 438.57500MHz -7.6000MHz +2026-03-02 15:49:33 CET: SR8MBR-1>APRX29,WIDE2-1,TCPIP*,qAS,SR8MBR-1:!4956.40N/02202.89E#W2,SPn Tyczyn Digi/iGate cross LoRa SR8MBR-2 +2026-03-02 15:49:33 CET: J73Z-10>APMI06,TCPIP*,qAC,T2POLAND:@021449z1519.92NI06123.22W-WX3in1Plus2.0 I-GATE , 145.05MHz R15m +2026-03-02 15:49:33 CET: WINLINK>APWL2K,TCPIP*,qAS,WLNK-1:;KD0ZEA-10*020249z3823. NW09042. Wa145.070MHz Winlink VARA FM Wide Gateway +2026-03-02 15:49:33 CET: OE3RPU-7>APTETR,OE3XWW-T,TCPIP*,qAU,OE3XHQ:!4730.62N/01602.25E[000/000 via OE3XWW-T (TETRA-Austria) +2026-03-02 15:49:33 CET: WB2BWU-2>APK102,WIDE1-1,WIDE2-2,qAR,KC2MDN-2::WLNK-1 :rej7953 +2026-03-02 15:49:33 CET: KD4BNQ-3>APDW14,TCPIP*,qAC,T2USANW:!3721.00NG08032.23W# +2026-03-02 15:49:33 CET: PY5KT-13>APRS,TCPIP*,qAC,T2BRAZIL:T#2808,1249,507,446,240,06642,11111000 +2026-03-02 15:49:33 CET: KD7MPG-3>APRS,TCPIP*,qAC,WG3K-CA:@021449z4456.91N/12259.69W_065/000g001t048r000p000P000b10272h93eMB42 +2026-03-02 15:49:33 CET: DL1FOX-9>APBM1D,DMR*,qAS,DL3OCK-12:=4823.53N/01144.47E[360/000/A=000000Robert HyTalk +2026-03-02 15:49:33 CET: VA3NB-10>KITTY,qAr,VE9QLE-2:=4332.82N/08014.99Wy[https://cats.radio] Guelph CATS igate, 1W with whip antenna on roof. /A=000033 +2026-03-02 15:49:33 CET: BD4TUC-1>AP51G2,TCPIP*,qAC,T2CS:!3137.26N/12021.96E>011/003/A=000055<0xe6><0x97><0xa0><0xe9><0x94><0xa1><0xe5><0xb8><0x82><0xe6><0x97><0xa0><0xe7><0xba><0xbf><0xe7><0x94><0xb5><0xe5><0x8d><0x8f><0xe4><0xbc><0x9a> <0xe5><0x9c><0xb0><0xe5><0x9d><0x80> 4668.113Km 021m 4.2V S10 +2026-03-02 15:49:33 CET: EL-CN8EAA>RXTLM-1,TCPIP,qAR,CN8EAA::EL-CN8EAA:UNIT.RX Erlang,TX Erlang,RXcount/10m,TXcount/10m,none1,STxxxxxx,logic +2026-03-02 15:49:33 CET: NM2V-N>APDG03,TCPIP*,qAC,NM2V-NS:!2814.93ND08116.88W&/A=00000070cm MMDVM Voice (C4FM) 446.52500MHz +0.0000MHz, NM2V_Pi-Star_ND +2026-03-02 15:49:33 CET: HS8KGG>AESPG4,TCPIP*,qAC,T2CS:T#24606,0,0,0,32,0,00000000 +2026-03-02 15:49:33 CET: HS8KGG>AESPG4,TCPIP*,qAC,T2CS:>V.4.2a SSID:HamShack-2.4G Rx:0 Digi:0 Tx:0 UpTime:184.22 +2026-03-02 15:49:33 CET: F4BKE-10>APLRG1,TCPIP*,qAC,T2PANAMA:!L6.j=P,EBa GLoRa APRS +2026-03-02 15:49:33 CET: LA3RIA-7>APU25N,LD1BK,WIDE2*,qAR,LD1FE:=5956.86N/01039.09E_Current forcast ... {UIV32N} +2026-03-02 15:49:33 CET: SZ3P>APRX28,TCPIP*,qAC,T2GREECE:!3813.24N\02143.28E-DMR Rpt 439.075 -7.6 MHz CC1 TS1:202 TS2:2023 | Tx27.1C Pi41.9C +2026-03-02 15:49:33 CET: DL6NEC-10>APLRG1,TCPIP*,qAC,T2ROMANIA:!L53wjQ$9M& GLoRa APRS iGate Test +2026-03-02 15:49:33 CET: UR0DVC-10>APMI04,WIDE3-3,qAR,UR4SWG-14:;UR0DVC *041021z4806.51N/02412.53Er145.625 TSQ 173.8 +2026-03-02 15:49:33 CET: IZ8EQA>APDR16,TCPIP*,qAC,T2UK:=3805.0 N/01538.0 Ey Op Antonio 145.425 R.C +2026-03-02 15:49:33 CET: SV5DDT-12>APE32L,TCPIP*,qAC,T2SPAIN:!3625.80NL02814.07EzPHG1060/A=000016 +2026-03-02 15:49:33 CET: LU8EB-1>APNU19,WIDE2-1,qAR,LW7EFM-1:!3515.14S/05840.86W#PHG5250 W2, APRS-DIGI Udaondo Bs As +2026-03-02 15:49:33 CET: IR1UDQ>APBM1S,TCPIP*,qAS,BM2222:@021449z4454.56N/00902.04ErPHG0000MMDVM 431.3000/432.9000 CC1 +2026-03-02 15:49:33 CET: DG2KKR-4>APLT00-1,qAR,DB0NOR:!5049.91N/00648.09E>107/000/A=000293LoRa Tracker !wU$! +2026-03-02 15:49:33 CET: 2E0KIO-10>APLRG1,TCPIP*,qAC,T2RDU:!L3aSLMuX[& GLoRa APRS +2026-03-02 15:49:33 CET: W5RKJ-N>APDG03,TCPIP*,qAC,W5RKJ-NS:!2953.73ND09540.10W&/A=0000002m MMDVM Voice (C4FM) 144.95000MHz +0.0000MHz, W5RKJ_Pi-Star_ND +2026-03-02 15:49:33 CET: BD4TSK-10>AP51D8,WIDE1-1,qAS,BI4VNM-10:!3149.46N/12026.60ErJYRSA APRS DIGI Huaxi China 13.5V +2026-03-02 15:49:33 CET: FUQING>APE32I,TCPIP*,qAC,T2TAIPEI:/021449z2542.69N/11917.27E&PHG1020 E2[Device status]system self checking OK! +2026-03-02 15:49:33 CET: VK2FS-5>APMI06,TCPIP*,qAC,T2SYDNEY:@021449z3347.82S\15110.27E&145.175MHz iGate, Chatswood U=13.8V,T=27.7C +2026-03-02 15:49:33 CET: OK0HCS-S>APDG01,TCPIP*,qAC,OK0HCS-GS:;OK0HCS B *021449z5004.93ND01423.36EaRNG0012 440 Voice 439.45000MHz -7.6000MHz +2026-03-02 15:49:33 CET: 9W2DCM-1>AESPG4,TCPIP*,qAC,T2SYDNEY::9W2DCM-1 :PARM.RxTraffic,TxTraffic,RxDrop,RSSI,Voltage +2026-03-02 15:49:33 CET: 9W2DCM-1>AESPG4,TCPIP*,qAC,T2SYDNEY::9W2DCM-1 :UNIT.Pkt,Pkt,Pkt,dBm,V +2026-03-02 15:49:33 CET: 9W2DCM-1>AESPG4,TCPIP*,qAC,T2SYDNEY::9W2DCM-1 :EQNS.0,1,0,0,1,0,0,1,0,0,-1,0,0,1,0 +2026-03-02 15:49:33 CET: 9W2DCM-1>AESPG4,TCPIP*,qAC,T2SYDNEY:T#235,1,4,1,32,5.25,00000000 +2026-03-02 15:49:33 CET: IR1UDQ>APBM1S,TCPIP*,qAS,BM2222:>https://brandmeister.network/?page=repeater&id=222052 +2026-03-02 15:49:33 CET: BG5AAB-9>R80TQ1,TCPIP*,qAC,T2FUKUOKA:`0V><0x1c><0x1d>EP\`"4$}51G3 A2117.0Km 13.6V 25.8C S08 51G3 +2026-03-02 15:49:33 CET: N0GIO-15>APRX29,TCPIP*,qAC,T2USANW:!3700.96NI10724.97W#N0GIO-15 I-Gate/Digi Arboles, CO +2026-03-02 15:49:33 CET: EW1335>APRS,TCPXX*,qAX,CWOP-4:!4416.41N/07512.34W_286/001g002t016r000P000p000h40b10366.VWS-DavisVVue +2026-03-02 15:49:33 CET: F4FEB-L>APLRG1,TCPIP*,qAC,T2CSNGRAD:!L6`;?O{M5a GLoRa APRS Igate Batt=4.25V +2026-03-02 15:49:34 CET: SR3NPB>APMI06,TCPIP*,qAC,T2CSNGRAD:T#219,174,034,013,067,000,00000000 +2026-03-02 15:49:34 CET: PA3FNT-1>APDW17,qAS,PA3FNT:!5243.76NR00544.62E&PA3FNT-1 iGate RX-only | DireWolf 1.6 | RPi+RTL-SDR +2026-03-02 15:49:34 CET: BG5DVF-7>APFMO2,TCPIP*,qAC,T2YANTAI:=3026.12NF12016.83Ei<0xe4><0xb8><0xb4><0xe5><0xb9><0xb3><0xe6><0xac><0xa2><0xe8><0xbf><0x8e><0xe6><0x82><0xa8><0xef><0xbc><0x81><0xef><0xbc><0x81> +2026-03-02 15:49:34 CET: BG5DVF-7>APFMO2,TCPIP*,qAC,T2YANTAI:><0xe6><0xad><0xa3><0xe5><0x9c><0xa8> <0xe7><0x94><0x98><0xe8><0x82><0x83><0xe9><0x9b><0x86><0xe7><0xbe><0xa4><0xe4><0xb8><0xad><0xe7><0xbb><0xa7>(BG9JYT) <0xe4><0xb8><0x8a><0xe5><0xae><0x88><0xe5><0x90><0xac> +2026-03-02 15:49:34 CET: IR2UDV>APBM1S,TCPIP*,qAS,BM2222:@021449z4550.30N/00924.31ErPHG0000IR2UDV Ari Sez Lecco 430.1125/431.7125 CC1 +2026-03-02 15:49:33 CET: YC1JEA-12>AESPG4,TCPIP*,qAC,T2FUKUOKA:@021449z0651.51S\10734.68E_000/000g000t077r...p...P...h96b09120L000 YC1JEA Bandung : [[Sunny]] SUN T=25<0xc2><0xb0> H=96% P=912 +2026-03-02 15:49:33 CET: K4IKR>APWW10,TCPIP*,qAC,T2PANAMA:T#210,100,048,002,500,000,10000000 +2026-03-02 15:49:33 CET: F1DUI-9>F1DUI-9,F5ZHR-4*,WIDE2-1,qAR,F4FEB-13:!4718.52N/00600.63E>010/000/A=001378F1DUI ANYTONE AT-D878UV-1.26 +2026-03-02 15:49:33 CET: OK1FET-4>APRS,qAR,OK1SE-12:!4956.28N/01420.12E__RESETi<0x00> +2026-03-02 15:49:33 CET: OK1FET-4>APRS,qAO,OK2JIB-10:!4956.28N/01420.12E__RESETi +2026-03-02 15:49:33 CET: F4JBM>APOSW,TCPIP*,qAC,T2DENMARK:@021449z4850.32N\00215.10E&/A=000000SharkRF openSPOT2 +2026-03-02 15:49:33 CET: IZ3GTH>APRS,TCPIP*,qAC,T2BIO:!4529.48N/01207.21EEEmgNet - https://www.cleverguy.it/emgnet OK-00-S +2026-03-02 15:49:33 CET: SQ1OHF-4>APLRG1,TCPIP*,qAC,T2POLAND:!L3DmzR7Si& GLoRa APRS iGate - RX 434.855 MHz (SF9/CR7)|%#%G| +2026-03-02 15:49:33 CET: SUNLGT>APMI04,WIDE2-1,qAR,ANVIL:T#106,131,135,003,069,166,00000000 +2026-03-02 15:49:33 CET: 9A3HVZ>APDR16,TCPIP*,qAC,T2ERFURT:=4602.39N/01603.30EQ255/051/A=000643 MOBILE DMR TG:21966 +2026-03-02 15:49:33 CET: DD3QQ-10>APLRG1,TCPIP*,qAC,T2CZECH:!L46ZDPFyp& GLoRa-Digi Muenster +2026-03-02 15:49:34 CET: OK1FET-4>APRS,qAS,OK1TPG-27:!4956.28N/01420.12E__RESETi SNR=-9dB RSSI=-78db +2026-03-02 15:49:34 CET: OK1FET-4>APRS,qAS,OK1KZE-10:!4956.28N/01420.12E__RESETi SNR=+6dB RSSI=-71db +2026-03-02 15:49:34 CET: JH4GQC>APU25N,TCPIP*,qAC,T2FUKUOKA:;XLX904 *170650z3415.44ND13231.55EaREFLECTOR XLX904H & XLX380Q 430.95MHz +2026-03-02 15:49:34 CET: IR4UBS>APBM1S,TCPIP*,qAS,BM2222:@021449z4413.63N/01154.91ErPHG0000RU101 - RIPETITORE IR4UBS Oriolo Dei Fichi (Ra) Altitudine 141 mt S.l.m - Info: ir4ubs@gmail.com 431.2625/432.8625 CC1 +2026-03-02 15:49:34 CET: IR4UBS>APBM1S,TCPIP*,qAS,BM2222:>https://brandmeister.network/?page=repeater&id=222054 +2026-03-02 15:49:34 CET: VE7SML-4>APMI01,TCPIP*,qAC,T2BC:T#186,124,128,130,000,000,00000000 +2026-03-02 15:49:34 CET: VE3CGR-4>APRX29,TCPIP*,qAC,T2CAEAST:T#973,0.0,0.9,0.0,0.0,3.0,00000000 +2026-03-02 15:49:34 CET: EB7DFA-5>APDR16,TCPIP*,qAC,T2BELGIUM:=3644.83N/00410.32W>060/060/A=000602 canal 3 pmr plan 333 +2026-03-02 15:49:34 CET: LD9TB>APBPQ1,TCPIP*,qAC,LB5ZH-14:!6938.03NB01850.86Eaax.25 1200 baud @ 144.925 og 70.3125MHz. -1 for BBS. +2026-03-02 15:49:34 CET: GJ8RRP-3>APTT4,qAR,GJ7DNI-13:T#593,123,096,255,094,088,00000011 +2026-03-02 15:49:34 CET: YU1TTA-13>APFTIS,TCPIP*,qAO,AE5PL-JF:!4404.76N/02213.16E_.../...g...t054r...p...P...h100b09931QFE 993 mbar, QNH 1023 mbar +2026-03-02 15:49:34 CET: EL-EC4DA>APSVX1,WIDE1-1,qAS,EC4DA:T#076,000,025,000,004,000,00000000 +2026-03-02 15:49:34 CET: SR9WXP>AKLPRZ,qAR,SR7NST:!4932.18N/01919.18E_034/001g002t035r...p...P...b08618h88 +2026-03-02 15:49:34 CET: 9A3HVZ>APDR16,TCPIP*,qAC,T2ERFURT:=4602.38N/01603.28EQ256/051/A=000643 MOBILE DMR TG:21966 +2026-03-02 15:49:34 CET: HS1AH-10>AESPG4,qAR,E22LPB:@021449z1453.85N/10023.88E#HS1AH 145.700 T=36<0xc2><0xb0> H=40% P=1005 PM 1:[21] , 2.5:[31] , 10:[33] (<0xce><0xbc>g/m3) +2026-03-02 15:49:34 CET: HS1AH-10>AESPG4,TCPIP*,qAC,T2HAKATA:@021449z1453.85N/10023.88E#HS1AH 145.700 T=36<0xc2><0xb0> H=40% P=1005 PM 1:[21] , 2.5:[31] , 10:[33] (<0xce><0xbc>g/m3) +2026-03-02 15:49:34 CET: N7XAO>APMI04,TCPIP*,qAC,FIRST:@021449z4434.90N/12318.05W-WX3in1Mini U=10.0V +2026-03-02 15:49:34 CET: VK7KT-7>APDR16,TCPIP*,qAO,AE5PL-JF:=4104.46S\14553.63Ek146.500MHz/A=000517 https://aprsdroid.org/q +2026-03-02 15:49:34 CET: HK4DAP-19>HK4DAP,TCPIP*,qAC,T2PANAMA:>Estacion Meterologica APRS con SCRIP Python, Raspberry Pi,ESP32 y Sensor BMP280 por HK4DAP +2026-03-02 15:49:32 CET: OE7MZH-B>APDG02,TCPIP*,qAC,OE7MZH-BS:!4716.80ND01127.00E&RNG0001/A=000010 70cm Voice (D-Star) 430.10000MHz +0.0000MHz +2026-03-02 15:49:33 CET: LZ0VRC-10>APE32L,WIDE1-1,qAR,YO7MGH-7:!4313.30NL02332.68E#PHG1060/A=001147Vratsa,Bulgaria +2026-03-02 15:49:33 CET: IR5UEC>APBM1S,TCPIP*,qAS,BM2222:@021449z4349.62N/01027.59ErPHG0000MMDVM 430.0875/435.0875 CC1 +2026-03-02 15:49:33 CET: HB9EKJ-S>APDG01,TCPIP*,qAC,HB9EKJ-GS:;HB9EKJ B *021449z4702.40ND00904.80EaRNG0001/A=000010 70cm Voice (D-Star) 438.57500MHz -7.6000MHz +2026-03-02 15:49:33 CET: IW0RED-10>APMI06,TCPIP*,qAC,IW0RED-IT:!4251.95NI01248.01E#PHG3800 IGate Monte Serano CRLNet - http://crlnet.it +2026-03-02 15:49:33 CET: SM5XDH-3>APDR3,WIDE1-1,WIDE2-2,qAR,SM0RGQ-11:!5854.38N/01612.15E3000/000/A=000351 +2026-03-02 15:49:33 CET: IR5UEC>APBM1S,TCPIP*,qAS,BM2222:>https://brandmeister.network/?page=repeater&id=222048 +2026-03-02 15:49:33 CET: LTROCK>APN391,qAR,K3KIN-1:!3447.95NS09229.95W#PHG7660 W3,ARn-N LTROCK W5DI Little Rock, AR +2026-03-02 15:49:33 CET: IR0UJM>APBM1S,TCPIP*,qAS,BM2222:@021449z4129.72N/01303.93ErPHG0000Sul Ripetitore <0xc3><0xa8> attivo il TG222059 Roma/Latina del 773 Radio Group (ex TG 222773) SLOT2 mail: radioteam@773radiogroup.it 430.5250/435.5250 CC1 +2026-03-02 15:49:33 CET: IR0UJM>APBM1S,TCPIP*,qAS,BM2222:>https://brandmeister.network/?page=repeater&id=222049 +2026-03-02 15:49:33 CET: W2TTT-5>APDR16,TCPIP*,qAC,T2BIO:=3040.25N/08140.08W$342/054/146.520MHz/A=-00235 https://aprsdro +2026-03-02 15:49:33 CET: K0JDD-1>APRX28,TCPIP*,qAC,T2PERTH:T#943,30.2,0.0,138.0,17.0,0.0,00000000 +2026-03-02 15:49:33 CET: XE2DAK-9>SRSQSW,WIDE1-1,WIDE2-1,qAR,XE2SI-10:`,U<0x1f>l zv/"5D}438.075MHz / Escape / pateando el botecito +2026-03-02 15:49:33 CET: F1FOO-10>APLRG1,TCPIP*,qAC,T2CAEAST:!L7YycN]ota GLoRa Aprs 433,775 MHz +2026-03-02 15:49:33 CET: TB8ATA-13>AESPG4,TCPIP*,qAC,T2BELGIUM:T#1837,0,0,0,25,85,00000000 +2026-03-02 15:49:33 CET: KB3AWQ>APREST,TCPIP*,qAC,THIRD:@021448z4114.63N/07702.04W_343/001g002t023r000P000h71b10369L256eREST +2026-03-02 15:49:33 CET: ZL2MTB-3>APTT4,WIDE1-1,WIDE2-1,qAR,ZL2ARG-1:>GOLDEN BAY DIGI 26C 13.6V +2026-03-02 15:49:34 CET: HS6BUE-3>AESPG4,TCPIP*,qAC,T2TOKYO:=1656.25N/10009.33E_ T=31<0xc2><0xb0> H=45% P=1003 PM 1:[13] , 2.5:[22] , 10:[28] (<0xce><0xbc>g/m3) +2026-03-02 15:49:34 CET: PG1ADW-4>APNL51,TCPIP*,qAI,PG1ADW-2,PG1ADW-4,T2AUSTRIA,T2HUB1,APRSFI-C1:!4819.54N/01425.57E`iGate PG1ADW RX@OCI +2026-03-02 15:49:34 CET: IK1WGX-7>APOSB,IK1WGX-1,C4FM*,qAS,IK1WGX:@021449z4440.62N/00802.29E[SharkRF openSPOT3 +2026-03-02 15:49:34 CET: CW5738>APRS,TCPXX*,qAX,CWOP-7:@021449z3256.45N/10550.52W_182/008g012t055r000p000P000h33b10219L000.DsVP +2026-03-02 15:49:34 CET: MB7UKC>APDW18,WIDE1-1,WIDE2-1,qAO,M0ZZX-5:!5157.73N/00116.07E#PHG4020Digipeater & iGate - Shotley Gate JO01PX - Keeper M0HKC +2026-03-02 15:49:34 CET: W7SWT-11>APNU19,WIDE,qAR,VE6AAH-10:!4851.28NS11108.41W#PHG5660 W3 SWEETGRASS W7SWT-11 B1 +2026-03-02 15:49:34 CET: IU2HUQ-B>APDG02,TCPIP*,qAC,IU2HUQ-BS:!4626.20ND01107.40E&RNG0001/A=000010 70cm Voice (D-Star) 433.65000MHz +0.0000MHz +2026-03-02 15:49:34 CET: FW5467>APRS,TCPXX*,qAX,CWOP-7:@021449z4139.46N/07444.33W_192/000g000t019r000p001P000b10366h64L290eMB50 +2026-03-02 15:49:34 CET: OZ8BLR-9>APDR16,TCPIP*,qAC,T2ROMANIA:=5449.63N/01144.19E>260/054/A=000127 on the road <0xf0><0x9f><0x98><0x80> +2026-03-02 15:49:34 CET: LU7HCN-12>APBM1D,LU7HCN,DMR*,qAR,LU7HCN:@144931h3121.90S/06408.61W[121/000Fede en movimiento!! +2026-03-02 15:49:34 CET: MB7UVB>APDW14,qAO,2E1HJE-10:;GB7TH *111111z5122.86N/00124.29Er430.2875MHz DMR: www.trig.org.uk/gb7th +2026-03-02 15:49:34 CET: N9ICV-C>APDG02,TCPIP*,qAC,N9ICV-CS:!3847.60ND08545.49W&RNG0001/A=000010 2m Voice (D-Star) 145.51250MHz +0.0000MHz +2026-03-02 15:49:34 CET: KI4YTE-N>APDG03,TCPIP*,qAC,KI4YTE-NS:!4515.78ND09338.24W&/A=00000070cm MMDVM Voice (C4FM) 446.52500MHz +0.0000MHz, KI4YTE_Pi-Star_ND +2026-03-02 15:49:33 CET: GM7GRH-10>APLRG1,TCPIP*,qAC,T2UKRAINE:!L2FykMgoYa GSelkirk District LoRa APRS iGate Batt=4.21V +2026-03-02 15:49:33 CET: WA6LCN-8>SXPQWX,WIDE1-1,WIDE2-1,qAO,WA6LCN-10:`2RXTLM-1,TCPIP,qAR,CN8EAA:T#541,0.00,0.02,0,1,0.0,00000000,RepeaterLogic +2026-03-02 15:49:33 CET: EW4266>APRS,TCPXX*,qAX,CWOP-4:@021449z4415.33N/07305.98W_202/003g008t010r000p000P000h51b10384L499.DsVP +2026-03-02 15:49:33 CET: NX2I-11>APRX29,TCPIP*,qAC,T2RDU::NX2I-11 :PARM.Avg 10m,Avg 10m,RxPkts,IGateDropRx,TxPkts +2026-03-02 15:49:34 CET: W1HS-11>APMI06,TCPIP*,qAC,T2NALA:@021449z4315.93NT07221.29W&PHG7480/RX/TX iGate Perry Mountain, Charlestown, NH (14.2V 25.4F) +2026-03-02 15:49:34 CET: IW4DGS-S>APDG01,TCPIP*,qAC,IW4DGS-GS:;IW4DGS B *021449z4412.29NW01203.57EiRNG0001/A=000010 70cm Voice (D-Star) 430.00000MHz +0.0000MHz, APRS for ircDDBGateway +2026-03-02 15:49:34 CET: IW4DGS-S>APDG01,qAS,IW4DGS:>Powered by WPSD (https://wpsd.radio) +2026-03-02 15:49:34 CET: DW9097>APN000,TCPXX*,qAX,CWOP-5:@021449z3737.47N/01502.74E_272/002g007t056r000p000P000b10231h74L348eMB63 +2026-03-02 15:49:34 CET: IU2HUQ-S>APDG01,TCPIP*,qAC,IU2HUQ-GS:;IU2HUQ B *021449z4626.20ND01107.40EaRNG0001/A=000010 70cm Voice (D-Star) 433.65000MHz +0.0000MHz +2026-03-02 15:49:34 CET: LW2HAH>APBM1D,LW2HAH,DMR*,qAR,LW2HAH:@144931h3237.96S/06241.52W[116/001APRS_DMR_LW2HAH-7_DV +2026-03-02 15:49:34 CET: EI7JQ-14>APDR16,TCPIP*,qAC,T2CSNGRAD:=5323.87N/00802.87Wu020/050/A=000444 I monitor 145.500 and DMR +2026-03-02 15:49:34 CET: PY2NET-5>APDR16,TCPIP*,qAC,T2LAUSITZ:=2258.77S/04704.69W(/A=002158 Robson 439.725 DStar Valinhos SP +2026-03-02 15:49:34 CET: W4GCW-9>APT314,N1KSC-1*,WIDE1*,KM4ZYG-10*,WIDE2*,qAR,W4KBW:>Voice on 146.520 +2026-03-02 15:49:34 CET: KD1KE>APU25N,TCPIP*,qAC,T2PERTH:@021449z4428.85N/06920.58W_178/001g005t009r000p000P000h52b10347WX Station of KD1KE {UIV32N} +2026-03-02 15:49:34 CET: EL-E71ACU>RXTLM-1,TCPIP,qAR,E71ACU::EL-E71ACU:UNIT.RX Erlang,TX Erlang,RXcount/10m,TXcount/10m,none1,STxxxxxx,logic +2026-03-02 15:49:34 CET: EL-E71ACU>RXTLM-1,TCPIP,qAR,E71ACU:T#010,0.00,0.67,0,21,0.0,00000000,SimplexLogic +2026-03-02 15:49:34 CET: FW3802>APN000,TCPXX*,qAX,CWOP-4:@021449z4732.40N/11710.20W_302/000g001t046r000p000P000b10322h70eMB63 +2026-03-02 15:49:34 CET: KG7JQP>APWW11,TCPIP*,qAC,T2PANAMA:>021449zDX: W7SWT-11 38.5mi 78<0xb0> 14:49 4851.28N 11108.41W +2026-03-02 15:49:34 CET: HK4RAU-40>HK4DAP,TCPIP*,qAC,T2CAEAST:>Estacion Meterologica APRS con SCRIP Python, Raspberry Pi,ESP32 y Sensor BMP280 por HK4DAP +2026-03-02 15:49:34 CET: HK4RAU-40>HK4DAP,TCPIP*,qAC,T2CAEAST:@021449z0728.01N/07651.04W_010/010g000t082r000p000h78b10100ESTACION DEL CLIMA APRS ZONA 4 RADIOAFICIONADOS UNIDOS 146.520 MHz en Simplex Fonia - Reporte del clima en Apartad<0xc3><0xb3>: nubes dispersas +2026-03-02 15:49:34 CET: VA6AEA>APDW16,TCPIP*,qAC,T2CAEAST:!5304.71NR11408.68W&VA6AEA iGate +2026-03-02 15:49:34 CET: EA5JMY-13>APRS,TCPIP*,qAC,T2UK:=3929.52N/00022.44W_.../...g...t065r...p...P...h54b10187 +2026-03-02 15:49:34 CET: EA5JMY-13>APRS,TCPIP*,qAC,T2UK:T#070,050,304,000,000,000,00000000 +2026-03-02 15:49:34 CET: WINLINK>APWL2K,TCPIP*,qAS,WLNK-1:;KD2DO-10 *020249z4306. NW07737. Wa145.030MHz Winlink Packet Gateway +2026-03-02 15:49:34 CET: SM0TCZ-13>APMI01,TCPIP*,qAS,SM0TCZ:@021449z5914.43N/01757.80EI#WX3in1Plus +2026-03-02 15:49:34 CET: DB0DB>APU25N,TCPIP*,qAC,T2POLNW:=4746.60N/00742.07EIigate Blauen {UIV32N} +2026-03-02 15:49:34 CET: K4KJQ-13>APMI06,TCPIP*,qAS,K4KJQ:;146.76-KY*111111z3802.38N/08424.18Wr146.760MHz T67 -060 R12m +2026-03-02 15:49:34 CET: YD3BHZ-7>APBTUV,YH3NPX-4*,WIDE2-1,qAR,YH3NPX-2:!0745.47S/11312.58E>208/001/A=0001295RH PRO JATIM-CLUB +2026-03-02 15:49:34 CET: OH3KUN-9>APLT00,qAO,OH3ERV-L1:!6128.15N/02346.49E>174/019/A=000341 +2026-03-02 15:49:34 CET: IR2UDV>APBM1S,TCPIP*,qAS,BM2222:>https://brandmeister.network/?page=repeater&id=222055 +2026-03-02 15:49:34 CET: W9MID-10>GPS,qAR,W9RCA-10:MARC MEETING 8am 3rd Satur<0x7f><0x7f> at REMC Hq. Club Net Sun 7pm. [Unsupported packet format] +2026-03-02 15:49:34 CET: WV8AR-10>APRX29,qAR,KG4DVE-10:;WV8AR-10 *111111z3824.36N/08154.06W#APRS Digi - Scott Depot, WV -WV8AR- +2026-03-02 15:49:34 CET: ED1ZAK-3>UIDIGI,qAR,ED1ZBA-3:UIDIGI 1.9 +2026-03-02 15:49:34 CET: TB3DEU-13>APLRT1,WIDE1-1,qAR,TB3DEU-16:=/:1(lUmLgbD@Q +2026-03-02 15:49:34 CET: G8DHE-1>APDR16,TCPIP*,qAC,T2KA:=5049.68N/00022.95W$097/000/A=000196 GP9 +2026-03-02 15:49:34 CET: EA5IHI-13>AESPG4,TCPIP*,qAC,T2BIO:>V.4.4b Rx:0 Digi:0 Tx:32 UpTime:00.28 +2026-03-02 15:49:34 CET: E21ZSS-S>APDG01,TCPIP*,qAC,E21ZSS-GS:;E21ZSS C *021449z1240.13ND10116.52EaRNG0001/A=000010 2m Voice (D-Star) 145.56250MHz +0.0000MHz +2026-03-02 15:49:34 CET: EA3EW-N>APDG03,TCPIP*,qAC,EA3EW-NS:!4115.00ND00110.20E&/A=00000070cm MMDVM Voice (C4FM) 430.01250MHz +0.0000MHz, EA3EW_Pi-Star_ND +2026-03-02 15:49:34 CET: KB8SCS-1>APMI06,WIDE2-2,qAR,KD9MAB-15:@021355z4024.51N/08448.21W#W9JCA Coffee and Donuts SAT'S 8am JAY CNTY FAIRGROUNDS SCOUT CABIN +2026-03-02 15:49:34 CET: IR3UIB>APBM1S,TCPIP*,qAS,BM2222:@021449z4630.42N/01255.97ErPHG0000ARI RCE FVG 433.1250/431.5250 CC1 +2026-03-02 15:49:34 CET: DL8FMA-10>APLG01,TCPIP*,qAC,T2UKRAINE:=4759.79NL01200.42E&LoRa iGATE APRS 70cm 433.775 MHz, Info: github.com/lora-aprs/LoRa_APRS_iGate +2026-03-02 15:49:34 CET: IZ1ZCT-15>APLRT1,WIDE2-2,qAO,IZ1RWC-15:=/82Q?PyYC>=1QARI La Spezia - sysop: Roberto - iz1zct@gmail.com|(%%;| +2026-03-02 15:49:34 CET: IR3UIB>APBM1S,TCPIP*,qAS,BM2222:>https://brandmeister.network/?page=repeater&id=222056 +2026-03-02 15:49:34 CET: GW3184>APRS,TCPXX*,qAX,CWOP-4:@021450z4024.40N/08005.77W_085/000g004t025r000p000P000h62b10326.WFL +2026-03-02 15:49:34 CET: LU2DKV-10>AESPG4,TCPIP*,qAC,T2SYDNEY::LU2DKV-10:EQNS.0,1,0,0,1,0,0,1,0,0,-1,0,0,1,0 +2026-03-02 15:49:34 CET: GW2602>APN000,TCPXX*,qAX,CWOP-7:@021449z3821.45N/12200.01W_186/002g005t056r000p000P000b10171h81eMB60 +2026-03-02 15:49:34 CET: DO0KL-S>APDG01,TCPIP*,qAC,DO0KL-GS:;DO0KL B *021449z5129.70NW00632.85EiRNG0019/A=000262 70cm Voice (D-Star) 439.13750MHz -7.6000MHz, APRS for ircDDBGateway +2026-03-02 15:49:34 CET: DO0KL-S>APDG01,qAS,DO0KL:>Powered by WPSD (https://wpsd.radio) +2026-03-02 15:49:34 CET: GW0DQW-10>APLRG1,TCPIP*,qAC,T2PRT:!L4D:7Mczua GLoRa APRS +2026-03-02 15:49:34 CET: WA8LMF-42>APU25N,TCPIP*,qAC,WG3K-CA:>251749zAPRS-over-VARA Webservers http://WA8LMF.net/map +2026-03-02 15:49:34 CET: DL1NUX>APFII0,TCPIP*,qAC,APRSFI:@144934h5014.07N/01058.85Eb086/006/A=000998Attila B37!w>o! +2026-03-02 15:49:34 CET: TI0ARC-13>APRS,TCPIP*,qAC,FIRST:=0949.91N/08352.86W_.../...g...t070r...p...P...h69b10122 +2026-03-02 15:49:34 CET: TI0ARC-13>APRS,TCPIP*,qAC,FIRST:T#239,080,306,000,000,000,00000000 +2026-03-02 15:49:34 CET: IR0UJN>APBM1S,TCPIP*,qAS,BM2222:@021449z4144.79N/01239.02ErPHG0000Sono attivi i seguenti TG: 222059 773 Radio Group Roma/Latina (ex TG222773) SLOT2 e 222094 R.N.R.E. Protezione Civile (ex TG22211) SLOT1 430.5375/435.5375 CC1 +2026-03-02 15:49:34 CET: IR0UJN>APBM1S,TCPIP*,qAS,BM2222:>https://brandmeister.network/?page=repeater&id=222059 +2026-03-02 15:49:35 CET: GW5315>APRS,TCPXX*,qAX,AMBCWOP-1:@021449z3523.12N/08054.20W_005/005g010t048r000p000P000h64b10333L305AmbientCWOP.com +2026-03-02 15:49:35 CET: F4HQI-10>APLRG1,TCPIP*,qAC,T2FRANCE:=L5zz_P2v`a !GLoRa APRS 73' +2026-03-02 15:49:35 CET: IR0UEI>APBM1S,TCPIP*,qAS,BM2222:@021449z4226.04N/01235.84ErPHG0000IR0UEI - S. Pancrazio TR 431.4625/433.0625 CC1 +2026-03-02 15:49:35 CET: IR0UEI>APBM1S,TCPIP*,qAS,BM2222:>https://brandmeister.network/?page=repeater&id=222060 +2026-03-02 15:49:34 CET: ON0ABT-2>APMI03,WIDE2-2,qAR,ON1TG-10:=5106.91N200318.93E# DIGI Beernem - WOC vzw +2026-03-02 15:49:34 CET: DO7AD>APU25N,TCPIP*,qAC,T2FINLAND:S8RT3W,WIDE1-1,qAR,KN4FM-8:`jA-l!sk/`"7-}_% +2026-03-02 15:49:34 CET: WX9EMS>APU25N,TCPIP*,qAC,T2USANW:=4233.11NI08824.12W&PHG5260/Lake Geneva IGate {UIV32N} +2026-03-02 15:49:34 CET: KJ7OGG>APN000,TCPXX*,qAX,CWOP-3:@021449z3328.39N/11204.47W_330/001g003t067r000p000P000b10099h38eMB63 +2026-03-02 15:49:34 CET: G0HWW-3>UR2RRR-2,WIDE1-1,WIDE2-2,qAR,MB7URG:`w9Ml<0x1d>H#/"4f}PicoAPRS by DB1NTO +2026-03-02 15:49:34 CET: YO6IOG-10>APMI04,WIDE2-2,qAO,YO6GZJ-10:@021449z4539.48N/02537.22E& I-Gate Brasov +2026-03-02 15:49:34 CET: WA8LMF-10>APU25N,qAo,WA8LMF-42:>251749zAPRS-over-VARA Webservers http://WA8LMF.net/map +2026-03-02 15:49:34 CET: GW4151>APRS,TCPXX*,qAX,AMBCWOP-1:@021449z4248.99N/08601.12W_339/002g004t026r000p000P000h75b10133L086AmbientCWOP.com +2026-03-02 15:49:34 CET: G7PAZ-S>APDG01,TCPIP*,qAC,G7PAZ-GS:;G7PAZ B *021449z5257.98ND00038.25WaRNG0001/A=000010 70cm Voice (D-Star) 438.80000MHz +0.0000MHz +2026-03-02 15:49:34 CET: VK6JI-11>APN000,TCPIP*,qAC,T2TAS:@021449z3149.22S/11548.55E_210/000g001t067r000p000P000b10123h75L000eMB59 +2026-03-02 15:49:34 CET: IU0PNB-D>APDG03,TCPIP*,qAC,IU0PNB-DS:!4127.97N\01239.23E-/A=00000070cm MMDVM Voice (DMR) 431.60000MHz +0.0000MHz, APRS for DMRGateway +2026-03-02 15:49:34 CET: IU0PNB-D>APDG03,qAS,IU0PNB:>Powered by WPSD (https://wpsd.radio) +2026-03-02 15:49:34 CET: SR6NWS>APMI06,TCPIP*,qAC,T2POLAND:;145.7875S*021449z5040.82N/01629.14Er145.7875MHz c094 -060 R60k SR6S Wielka Sowa 7.1C +2026-03-02 15:49:34 CET: OE6CFD-1>APRSGW,TCPIP*,qAR,OE6CFD-1:!4703.96N\01540.02ELEuropaberg#GAFF/B=100/A=001637 +2026-03-02 15:49:34 CET: KG5APU>APRS,TCPIP*,qAC,AMBCWOP-2:@021449z3310.81N/09629.88W_171/001g001t063r000p000P000h87b10201L063AmbientCWOP.com +2026-03-02 15:49:35 CET: WB2UTI-S>APDG01,TCPIP*,qAC,WB2UTI-GS:;WB2UTI B *021449z2852.89ND08200.11WaRNG0001/A=000010 70cm Voice (D-Star) 433.50000MHz +0.0000MHz +2026-03-02 15:49:35 CET: K1LVA>APRS,TCPIP*,qAC,AMBCWOP-2:@021449z3334.73N/08133.90W_013/003g005t059r000p000P000h86b10284L567AmbientCWOP.com +2026-03-02 15:49:35 CET: FW9876>APRS,TCPXX*,qAX,AMBCWOP-1:@021449z3320.33N/11713.29W_175/000g000t053r000p000P000h96b10136L014AmbientCWOP.com +2026-03-02 15:49:35 CET: JP3PYY-10>APU25N,TCPIP*,qAC,T2HAKATA:>192248zI-GATE 144.66MHz 1200bps +2026-03-02 15:49:35 CET: KE5FEN-N>APDG03,TCPIP*,qAC,KE5FEN-NS:!2924.28ND09843.12W&/A=00000070cm MMDVM Voice (C4FM) 438.80000MHz +0.0000MHz, KE5FEN_Pi-Star_ND +2026-03-02 15:49:35 CET: N6LKA-R>APDG03,TCPIP*,qAC,N6LKA-RS:!3359.88ND11702.91W&/A=00000070cm MMDVM Voice (C4FM) 449.64000MHz -5.0000MHz, N6LKA_Pi-Star_RPT +2026-03-02 15:49:35 CET: K1RFS>APRS,TCPIP*,qAC,AMBCWOP-2:@021449z4032.46N/11202.08W_186/000g000t043r000p001P001h94b09988L018AmbientCWOP.com +2026-03-02 15:49:35 CET: M0NOM-10>APLRG1,TCPIP*,qAC,T2CZECH:!L3!v7Mf'Ma GWindermere LoRa APRS iGATE +2026-03-02 15:49:35 CET: GW5686>APRS,TCPXX*,qAX,AMBCWOP-1:@021449z4443.54N/06723.77W_329/004g004t013r000p000P000h45b10336AmbientCWOP.com +2026-03-02 15:49:35 CET: GW5761>APRS,TCPXX*,qAX,AMBCWOP-1:@021449z3232.66N/09421.96W_225/001g006t064r000p000P000h81b10216L395AmbientCWOP.com +2026-03-02 15:49:35 CET: VK2RTZ-S>APDG01,TCPIP*,qAC,VK2RTZ-GS:;VK2RTZ B *021449z3234.20SD15115.60EaRNG0001 440 Voice 438.15000MHz -7.0000MHz +2026-03-02 15:49:35 CET: SR8NZ-10>APLRFD,TCPIP*,qAC,EIGHTH:!L5FswSv7?# GLoRa APRS Digi 70cm Batt=4.05V|'l#.!!!!!!!!| +2026-03-02 15:49:35 CET: F4ETJ-1>APX210,TCPIP*,qAC,T2POLAND:;F1ZAG/R *021449z4532.10N/00021.57WrEntre 1270.025_Sortie 1298.025 Shift-28Mhz +2026-03-02 15:49:35 CET: ER-DB0NLS>APSVX1,TCPIP*,qAS,DB0NLS:T#012,000,006,000,001,000,00000000 +2026-03-02 15:49:34 CET: HK4DAP-19>HK4DAP,TCPIP*,qAC,T2PANAMA:@021449z0613.13N/07514.61W_270/003g000t069r000p000h68b10120ESTACION DEL CLIMA APRS - Reporte del clima en El Pe<0xc3><0xb1>ol: muy nuboso +2026-03-02 15:49:34 CET: XE3RFM-9>R0UY7V,WIDE1-1,WIDE2-1,qAR,XE3EA:`uAPl{>>/GRUPO APRS MERIDA +2026-03-02 15:49:34 CET: DB0ZOD>APRS,TCPIP*,qAC,T2GREECE:;EL-DB0ZOD*111111z5231.43NE01325.90E0145.662MHz T000 R31k 145.6625 MHz -600 kHz / 1750Hz / FM-Repeater / Echolink Node: 969258 +2026-03-02 15:49:34 CET: KK7LLM-D>APDG03,TCPIP*,qAC,KK7LLM-DS:!4011.19N/11136.52Wx/A=00000070cm MMDVM Voice (DMR) 438.20000MHz -5.0000MHz, APRS for DMRGateway +2026-03-02 15:49:34 CET: KK7LLM-D>APDG03,qAS,KK7LLM:>Powered by WPSD (https://wpsd.radio) +2026-03-02 15:49:34 CET: CE4PLU-10>APLRG1,TCPIP*,qAC,T2CHILE:!L_pu8<;vya GAPRS RC PICHILEMU Batt=4.25V +2026-03-02 15:49:34 CET: DL7ATR-2>APWW11,WIDE1-1,WIDE2-1,qAR,DB0SDA:@144840h5036.70N/00618.14E-(Time 0:00:00)PHG4100 Hans-Juergen in Simmerath, DOK G26, dl7atr.darc.de +2026-03-02 15:49:34 CET: JA9VEZ-1>APNU19,WIDE1-1,qAR,JA9UZF-10:!3650.16N113659.47E#144.66 1k2 Himi 1 +2026-03-02 15:49:34 CET: N4YH-10>APWW11,TCPIP*,qAC,SECOND:@144934h3520.29N/08510.28W#John 3:16 +2026-03-02 15:49:34 CET: HB9RMW-41>APRSMC,TCPIP*,qAR,HB9RMW-40:!4714.53N/00827.91E>/B=100/A=001608 +2026-03-02 15:49:34 CET: DO0KL-B>APDG02,TCPIP*,qAC,DO0KL-BS:!5129.70NW00632.85EiRNG0019/A=000262 70cm Voice (D-Star) 439.13750MHz -7.6000MHz, APRS for ircDDBGateway +2026-03-02 15:49:34 CET: DO0KL-B>APDG02,qAS,DO0KL:>Powered by WPSD (https://wpsd.radio) +2026-03-02 15:49:34 CET: PD7EDI-14>UQ5WP8,qAO,PI1ZLD-2:`zZ9l ok/`"3m}_4 +2026-03-02 15:49:35 CET: FW4237>APRS,TCPXX*,qAX,AMBCWOP-1:@021449z3724.20N/07935.89W_081/005g006t038r000p000P000h65b10338L061AmbientCWOP.com +2026-03-02 15:49:35 CET: CW7220>APRS,TCPXX*,qAX,CWOP-7:@021449z3854.00N/00124.00E_043/010g...t059L091P000h86b10184.DsVP +2026-03-02 15:49:35 CET: DO8PGG-5>APDR16,TCPIP*,qAC,T2LAUSITZ:=5119.34N/00719.99E$277/002/A=000763 DMR+ Refl. 4671 +2026-03-02 15:49:35 CET: N8HON>APRS,TCPIP*,qAC,AMBCWOP-2:@021449z3838.08N/07920.77W_171/000g000t035r000p000P000h54b10293L188AmbientCWOP.com +2026-03-02 15:49:35 CET: CA3RJR-10>APLRG1,TCPIP*,qAC,T2MEXICO:!L_CEq<[$2a GLoRa APRS Batt=4.19V Ext=0.71V +2026-03-02 15:49:35 CET: EW8955>APREST,TCPXX*,qAX,CWOP-6:@021448z4009.03N/07947.55W_078/004g004t025r000P000h66b10320L058eREST +2026-03-02 15:49:35 CET: GW5727>APRS,TCPXX*,qAX,AMBCWOP-1:@021449z3845.13N/07900.35W_112/001g002t033r000p000P000h69b10343L128AmbientCWOP.com +2026-03-02 15:49:35 CET: DB0NLS>APSVX1,TCPIP*,qAC,T2LAUSITZ::ER-DB0NLS:PARM.RX Avg 15m,TX Avg 15m,RX Count 15m,TX Count 15m,,RX,TX +2026-03-02 15:49:35 CET: DB0NLS>APSVX1,TCPIP*,qAC,T2LAUSITZ::ER-DB0NLS:UNIT.erlang,erlang,receptions,transmissions +2026-03-02 15:49:35 CET: DB0NLS>APSVX1,TCPIP*,qAC,T2LAUSITZ::ER-DB0NLS:BITS.11111111,SvxLink RepeaterLogic +2026-03-02 15:49:35 CET: DB0NLS>APSVX1,TCPIP*,qAC,T2LAUSITZ::ER-DB0NLS:EQNS.0,0.00100,0,0,0.00100,0 +2026-03-02 15:49:35 CET: 9A3HVZ>APDR16,TCPIP*,qAC,T2ERFURT:=4602.38N/01603.26EQ257/051/A=000643 MOBILE DMR TG:21966 +2026-03-02 15:49:35 CET: F4JIJ-10>APLRG1,TCPIP*,qAC,T2UK:!L5g>MO$AUa GLoRa APRS http://www.f6kbf.fr/ +2026-03-02 15:49:35 CET: BR4IN>APDR16,TCPIP*,qAC,T2UKRAINE:=3638.3 N/11702.2 Er439.110MHz -5 88.5 <0xe6><0xb5><0x8e><0xe5><0x8d><0x97><0xe9><0xbb><0x84><0xe6><0xb2><0xb3><0xe4><0xb8><0x9a><0xe4><0xbd><0x99><0xe6><0x97><0xa0><0xe7><0xba><0xbf><0xe7><0x94><0xb5> <0xe7><0xa4><0xba><0xe4><0xbd><0x8d><0xe7><0x82><0xb9> +2026-03-02 15:49:35 CET: S57DV-9>APLRT1,WIDE1-1,qAR,S55DLM-10:=\74@zR/pl>9HG +2026-03-02 15:49:34 CET: K7SIE-N>APDG03,TCPIP*,qAC,K7SIE-NS:!5000.00ND00300.00W&/A=00000070cm MMDVM Voice (C4FM) 438.80000MHz +0.0000MHz, K7SIE_Pi-Star_ND +2026-03-02 15:49:34 CET: N2RWE>APMI06,TCPIP*,qAC,T2RDU:T#018,189,007,000,077,141,00000000 +2026-03-02 15:49:34 CET: UN7GWP-1>APLOX1,WIDE1-1,qAO,UN9GIE-1:!L8Z-{aw'R# XLORA DIGI i-GATE 434,855 @300 bps P=5.27V +2026-03-02 15:49:35 CET: KC5TXI>APRS,TCPIP*,qAC,AMBCWOP-2:@021449z3243.04N/09708.37W_194/001g001t068r000p000P000h79b10204L086AmbientCWOP.com +2026-03-02 15:49:35 CET: GW3614>APRS,TCPXX*,qAX,AMBCWOP-1:@021449z3849.17N/12115.33W_214/000g000t051r000p000P000h90b10179L006AmbientCWOP.com +2026-03-02 15:49:35 CET: GW3348>APRS,TCPXX*,qAX,AMBCWOP-1:@021449z4804.41N/12207.71W_093/004g005t032r000p000P000h95b10173L004AmbientCWOP.com +2026-03-02 15:49:35 CET: OK9DOB-9>APRS,TCPIP*,qAC,T2CZECH:T#158,0000,422,00,000000,211,01000000 +2026-03-02 15:49:35 CET: SR9NDJ>APRX28,TCPIP*,qAC,T2RADOM:!5033.45NI01927.02E&PHG3280 Digi & IGate Jurajski W2,SPn by SQ9NFI on Linux operator SP9JKL ==> http://sq9nfi.pzk.pl +2026-03-02 15:49:35 CET: SR9NDJ>APRX28,TCPIP*,qAC,T2RADOM:;SR9DJK *000000h5033.45N/01927.01E&DMR Repeater Static TG260/TS1 2609/TS2 operator SP9JKL +2026-03-02 15:49:35 CET: WB2UTI-B>APDG02,TCPIP*,qAC,WB2UTI-BS:!2852.89ND08200.11W&RNG0001/A=000010 70cm Voice (D-Star) 433.50000MHz +0.0000MHz +2026-03-02 15:49:35 CET: EW2895>APRS,TCPXX*,qAX,CWOP-3:@021449z3953.17N/08609.68W_043/001g005t029r000p000P000b10282h81eMB32 +2026-03-02 15:49:35 CET: GW5656>APRS,TCPXX*,qAX,AMBCWOP-1:@021449z3836.38N/07914.81W_315/002g003t034r000p000P000h66b10352L214AmbientCWOP.com +2026-03-02 15:49:35 CET: GW5669>APRS,TCPXX*,qAX,AMBCWOP-1:@021449z3830.60N/07857.88W_123/002g003t033r000p000P000h53b10347L174AmbientCWOP.com +2026-03-02 15:49:35 CET: GW5845>APRS,TCPXX*,qAX,AMBCWOP-1:@021449z3855.60N/07828.06W_056/004g006t031r000p000P000h61b10345L046AmbientCWOP.com +2026-03-02 15:49:35 CET: GW5801>APRS,TCPXX*,qAX,AMBCWOP-1:@021449z2935.78N/09507.33W_020/000g001t068r000p000P000h97b10203L060AmbientCWOP.com +2026-03-02 15:49:35 CET: HB9DM-3>APWW10,TCPIP*,qAC,T2PERTH:>021449zSwissDMR 438.325 MHz (-7.6 MHz) CC2 +2026-03-02 15:49:35 CET: PD0JOY-9>APDR15,TCPIP*,qAC,T2LAUSITZ:=5154.25N/00420.21E>295/001/144.725MHz/A=000219 bicycle +2026-03-02 15:49:35 CET: DL1GHN-9>TX2QX5,WIDE1-1,WIDE2-1,qAS,DB0LHR-10:`}QumR">/]"5p}= +2026-03-02 15:49:35 CET: PA1ZF-9>UR1UU7,WIDE1-1,WIDE2-1,qAO,3UTR1-10:`{8>o-Gv/`"3k}430.125MHz T077 +160 Bartel Gytsjerk FRYSLAN_% +2026-03-02 15:49:35 CET: E23JWE-1>AESPG4,TCPIP*,qAC,T2INDY::E23JWE-1 :UNIT.ug/m3,ug/m3,ug/m3,dBm,V +2026-03-02 15:49:35 CET: KD7LBV-9>S3RVQW,WIDE1-1,WIDE2-1,qAO,W9EN-10:`'QBmU7k/`"7k}Monitoring 440.375 MHz T100 -5.0_% +2026-03-02 15:49:35 CET: CE7DE-5>APLRG1,WIDE1-1,qAR,CE7DE-4:!Lcy1:;oVh# xG147.180 +600 T107.2 - 146.730 -600 T107.2 http://trauko.org/radioclub +2026-03-02 15:49:35 CET: IR4ZZW>APBM1S,TCPIP*,qAS,BM2222:@021449z4454.40N/00932.38ErPHG0000Pigazzano - Piacenza 431.4375/433.0375 CC1 +2026-03-02 15:49:35 CET: FW9998>APRS,TCPXX*,qAX,AMBCWOP-1:@021449z4223.78N/07108.04W_140/000g000t019r000p000P000h34b10392L372AmbientCWOP.com +2026-03-02 15:49:35 CET: GW0220>APRS,TCPXX*,qAX,AMBCWOP-1:@021449z3549.89N/09438.07W_308/000g001t048r000p000P000h99b10200L036AmbientCWOP.com +2026-03-02 15:49:35 CET: GW0383>APRS,TCPXX*,qAX,AMBCWOP-1:@021449z3835.81N/07809.11W_119/000g000t033r000p000P000h46b10326L136AmbientCWOP.com +2026-03-02 15:49:35 CET: GW0415>APRS,TCPXX*,qAX,AMBCWOP-1:@021449z4454.11N/09316.74W_135/002g005t031r000p000P000h61b10237L095AmbientCWOP.com +2026-03-02 15:49:35 CET: GW0484>APRS,TCPXX*,qAX,AMBCWOP-1:@021449z4824.86N/11058.25W_099/000g000t018r000p000P000h78b10090L027AmbientCWOP.com +2026-03-02 15:49:35 CET: GW0554>APRS,TCPXX*,qAX,AMBCWOP-1:@021449z3044.31N/08907.69W_299/001g001t061r000p000P000h99b10239L246AmbientCWOP.com +2026-03-02 15:49:35 CET: FW3002>APRS,TCPXX*,qAX,AMBCWOP-1:@021449z2708.62N/08010.72W_152/003g009t077r000p000P000h72b10234L551AmbientCWOP.com +2026-03-02 15:49:35 CET: PA3FAS>APDR16,TCPIP*,qAC,T2ROMANIA:=5209.62N/00624.58E//A=000223 TYT TH-9800D +2026-03-02 15:49:35 CET: SP1TRX-7>APLRT1,WIDE1-1,qAR,SP1TRX-10:!/38!=R.$S>5SQ +2026-03-02 15:49:35 CET: DW8BRQ-A>APSTAR,TCPIP*,qAC,T2PERTH:!0830.61N/12317.04EaPHG1210/A=000033AllStarLink 3 MicroNode 524255 Dodoy Mobile @ 432.700MHz +2026-03-02 15:49:35 CET: PI1PTN>APSVX1,TCPIP*,qAC,T2PRT:;EL-PI1PTN*111111z5215.46NE00536.60E0PHG3360/438.450MHz Toff R21k SvxLink by SM0SVX (www.svxlink.org) +2026-03-02 15:49:35 CET: GW5579>APRS,TCPXX*,qAX,AMBCWOP-1:@021449z5359.64N/02354.97E_253/001g002t041r000p000P000h78b10246L016AmbientCWOP.com +2026-03-02 15:49:35 CET: GW5755>APRS,TCPXX*,qAX,AMBCWOP-1:@021449z2801.78N/03437.47E_344/016g017t072r000p000P000h30b10141L030AmbientCWOP.com +2026-03-02 15:49:35 CET: GRYLCP-11>APDW17,HOP7-7,HOP7-7,qAR,AB1PH-15:!4208.85N/07113.60We212/001/A=000264AB1PH ATGP +2026-03-02 15:49:35 CET: OE1XZS>APRS,TCPIP*,qAC,T2AUSTRIA:;EL-OE1XZS*111111z4811.46NE01619.25E0430.562MHz T000 R21k OE1XZS-R TETRA-DMO-RPT OE:VIENNA/AUSTRIA +2026-03-02 15:49:35 CET: SV9DRS-N>APDG03,TCPIP*,qAC,SV9DRS-NS:!3520.11ND02503.77E&/A=00000070cm MMDVM Voice (C4FM) 438.80000MHz +0.0000MHz, SV9DRS_Pi-Star_ND +2026-03-02 15:49:35 CET: GW5795>APRS,TCPXX*,qAX,AMBCWOP-1:@021449z4203.25N/09725.76W_154/004g007t032r000p000P000h65b10243L065AmbientCWOP.com +2026-03-02 15:49:35 CET: GW5939>APRS,TCPXX*,qAX,AMBCWOP-1:@021449z0657.61N/15812.61E_109/000g000t078r000p000P000h91b10089AmbientCWOP.com +2026-03-02 15:49:35 CET: GW6033>APRS,TCPXX*,qAX,AMBCWOP-1:@021449z3939.04N/10446.70W_106/004g005t048r000p000P000h42b10148L049AmbientCWOP.com +2026-03-02 15:49:35 CET: FW5255>APRS,TCPXX*,qAX,AMBCWOP-1:@021449z3953.76N/07457.51W_150/000g001t028r000p000P000h53b10391L110AmbientCWOP.com +2026-03-02 15:49:35 CET: GW6055>APRS,TCPXX*,qAX,AMBCWOP-1:@021449z3531.55N/08433.09W_026/002g003t055r000p000P000h73b10262L170AmbientCWOP.com +2026-03-02 15:49:35 CET: GW0552>APRS,TCPXX*,qAX,AMBCWOP-1:@021449z3825.13N/08212.90W_093/003g005t035r000p000P000h59b10257L073AmbientCWOP.com +2026-03-02 15:49:35 CET: FW1299>APN000,TCPXX*,qAX,CWOP-3:@021449z4028.63N/07434.87W_065/000g001t027r000p000P000b10391h51eMB55 +2026-03-02 15:49:35 CET: F6KLI-10>APDW16,WIDE1-1,WIDE2-1,qAR,F6KLI-10:!4453.90NI00014.55W&Igate Radio club F6KLI +2026-03-02 15:49:35 CET: KG5JPJ-R>APDG03,TCPIP*,qAC,KG5JPJ-RS:!3612.26ND09406.12W&/A=00155570cm MMDVM Voice (C4FM) 442.52500MHz +5.0000MHz, KG5JPJ_Pi-Star_RPT +2026-03-02 15:49:35 CET: WX4ET>APRS,TCPIP*,qAC,AMBCWOP-2:@021449z3633.45N/08233.72W_081/002g002t048r000p004P004h92b10235L063AmbientCWOP.com +2026-03-02 15:49:35 CET: JK2QUE-10>APU25N,TCPIP*,qAC,T2ROMANIA:=3441.31N913724.06E&PHG3230/9600bps 144.64MHz Toyohashi-City +2026-03-02 15:49:35 CET: HZ1AX-10>APSN01,TCPIP*,qAC,T2TAIWAN:=2642.24N/05003.06E#/A=000094CSN iGate +2026-03-02 15:49:35 CET: DW2447>APRS,TCPXX*,qAX,AMBCWOP-1:@021449z3258.95N/08011.00W_096/002g003t057r000p000P000h81b10285L239AmbientCWOP.com +2026-03-02 15:49:35 CET: N3TTT-13>APRS,TCPIP*,qAC,AMBCWOP-2:@021449z3908.90N/07535.36W_026/006g008t031r000p000P000h60b10380L166AmbientCWOP.com +2026-03-02 15:49:35 CET: DL7TA-1>APRS,TCPIP*,qAS,DB0ZOD:@021449z5232.80NT01321.80EATetra TMO (2635176) +2026-03-02 15:49:35 CET: YG1CBG-13>AESPG4,TCPIP*,qAC,T2TAIWAN:>V.4.4c Rx:0 Digi:0 Tx:933 UpTime:12.33 +2026-03-02 15:49:35 CET: CQ0EPTM>APRS,TCPIP*,qAC,T2CSNGRAD:;ER-CQ0EPT*111111z3709.73NE00832.11W0433.800MHz T000 R23k [Tetra-DMO] 433.800 +2026-03-02 15:49:35 CET: ER-DB0DMO>RXTLM-1,TCPIP,qAR,DB0DMO::ER-DB0DMO:UNIT.RX Erlang,TX Erlang,RXcount/10m,TXcount/10m,none1,STxxxxxx,logic +2026-03-02 15:49:36 CET: DW3024>APRS,TCPXX*,qAX,CWOP-4:@021449z3259.05N/07947.98W_033/003g010t053L162r000P000p000h90b10279VL1252 +2026-03-02 15:49:36 CET: ER-DB0DMO>RXTLM-1,TCPIP,qAR,DB0DMO:T#076,0.00,0.01,0,1,0.0,00000000,TetraLogic +2026-03-02 15:49:36 CET: YM2KG>BEACON,WIDE2-2,qAR,YM1KE-1:;YM1KWB *021410z4056.71N/02911.47E# ANARAD 145.775 67TON KURBAN BAYRAMINIZI KUTLARIZ +2026-03-02 15:49:35 CET: KB1RMA-13>APRS,TCPIP*,qAC,AMBCWOP-2:@021449z4247.75N/07050.40W_320/006g009t019r000p000P000h35b10405L519AmbientCWOP.com +2026-03-02 15:49:35 CET: GW5769>APRS,TCPXX*,qAX,AMBCWOP-1:@021449z4321.43N/07329.99W_031/000g000t020r000p000P000h44b10415L411AmbientCWOP.com +2026-03-02 15:49:35 CET: GW5833>APRS,TCPXX*,qAX,AMBCWOP-1:@021449z3507.33N/10629.37W_023/002g002t053r000p000P000h36b10221L038AmbientCWOP.com +2026-03-02 15:49:35 CET: FW0062>APRS,TCPXX*,qAX,CWOP-6:@021449z3223.12N/11101.45W_108/001g002t054r000p000P000b10167h38L049eMB51 +2026-03-02 15:49:35 CET: Z32CPU>APDW16,qAO,Z32CPU:!4159.92NR02123.79E& Weather in Skopje => 15 <0xc2><0xb0>C - Wind => 1.03 m/s N - Humidity => 47 % - Pressure => 1023 hPa +2026-03-02 15:49:35 CET: G7TKI-13>APWEE5,TCPIP*,qAC,CWOP-7:@021445z5152.50N/00022.30E_249/002g004t063r000p001P000b10183h63L197.weewx-5.1.0-Interceptor +2026-03-02 15:49:35 CET: AB5KT-1>APRS,TCPXX*,qAX,CWOP-3:@021444z2916.90N/09826.72W_111/003g007t067r000p000P000b10192h82eMB32 +2026-03-02 15:49:35 CET: FW6592>APRS,TCPXX*,qAX,AMBCWOP-1:@021449z3500.14N/07905.04W_134/002g008t044r000p000P000h85b10314L073AmbientCWOP.com +2026-03-02 15:49:35 CET: 2E0UCD-10>APLRG1,TCPIP*,qAC,T2TAS:!L4b:FMHNGa GLandcross LoRa APRS Igate +2026-03-02 15:49:35 CET: GW0634>APRS,TCPXX*,qAX,AMBCWOP-1:@021449z3626.88N/10310.39W_237/005g006t045r000p000P000h46b08902L066AmbientCWOP.com +2026-03-02 15:49:35 CET: HB4LO-1>APLRG1,TCPIP*,qAC,T2CZECH:=4649.70NL00656.38E&HB4LO-P - IGATE ELO +2026-03-02 15:49:35 CET: EL-EA3URE>RXTLM-1,TCPIP,qAR,EA3URE::EL-EA3URE:UNIT.RX Erlang,TX Erlang,RXcount/10m,TXcount/10m,none1,STxxxxxx,logic +2026-03-02 15:49:35 CET: EL-EA3URE>RXTLM-1,TCPIP,qAR,EA3URE:T#017,0.00,0.01,0,5,0.0,00000000,SimplexLogic +2026-03-02 15:49:35 CET: GW2294>APN000,TCPXX*,qAX,CWOP-5:@021442z4724.67N/00732.70E_053/002g006t061r000p000P000b10198h48L230eMB58 +2026-03-02 15:49:35 CET: GW1778>APRS,TCPXX*,qAX,AMBCWOP-1:@021449z2525.55S/04918.22W_316/001g001t071r000p000P000h71b10213L262AmbientCWOP.com +2026-03-02 15:49:35 CET: IQ9TP-10>APWW11,TCPIP*,qAC,T2IRELAND:>021449zAPRSISCE/32 +2026-03-02 15:49:35 CET: OH6DL-10>APWW11,TCPIP*,qAC,T2CHILE:) +2026-03-02 15:49:35 CET: N4GQY-10>APWW11,TCPIP*,qAC,T2SYDNEY:@144935h3045.70N/08607.35W#APRS-IS +2026-03-02 15:49:35 CET: IQ9TP-10>APWW11,TCPIP*,qAC,T2IRELAND:)IQ9TP!3800.34N\01232.07E-Sez. ARI Trapani - dom h9:30 mer h20:30 +2026-03-02 15:49:35 CET: IQ9TP-10>APWW11,TCPIP*,qAC,T2IRELAND:)IR9D!3802.30N/01235.22ErPHG3840RV62/R7 Mt. Erice ARI TP 145.775MHz T094 -060 +2026-03-02 15:49:35 CET: IQ9TP-10>APWW11,TCPIP*,qAC,T2IRELAND:)IR9UAC!3802.30N/01235.22ErPHG3860RU014/RU7 Mt. Erice ARI TP 430.175MHz T094 +5000 C4FM +2026-03-02 15:49:35 CET: IQ9TP-10>APWW11,TCPIP*,qAC,T2IRELAND:)IW9FRA-6!3800.53N/01235.83E%DX Cluster ARI TP dxpisder.iw9fra.com:7300 +2026-03-02 15:49:35 CET: IQ9TP-10>APWW11,TCPIP*,qAC,T2IRELAND:)IW9FRA-ND!3800.71NW01232.69E&144.900MHz ARI TP C4FM #27644 ROOM ITALY +2026-03-02 15:49:36 CET: SQ6CZH-1>APRX29,TCPIP*,qAC,T2BIO:!5109.07N/01651.85E_(untested) Weather station +2026-03-02 15:49:36 CET: YO8SDE-8>APMI06,TCPIP*,qAS,YO8SDE:@021449z4742.73NI02639.03E# WX3in1Plus2.0 U=12.3V,T=2.5C/36.5F at Curtesti, BT +2026-03-02 15:49:36 CET: IR8ZZZ>APBM1S,TCPIP*,qAS,BM2222:@021449z4049.73N/01407.36ErPHG0000IR8ZZZ Pozzuoli ( Na ) 431.3750/432.9750 CC1 +2026-03-02 15:49:36 CET: AB6MB-6>APOSB4,TCPIP*,qAS,AB6MB:@021449z3731.49N/12202.78Wv/A=000000Jeff's openSPOT4 +2026-03-02 15:49:36 CET: IW4DST-10>APLRG1,TCPIP*,qAC,T2PERTH:!L7uF'Q(jR& GLoRa_APRS_iGate +2026-03-02 15:49:36 CET: IU5LRH-S>APDG01,TCPIP*,qAC,IU5LRH-GS:;IU5LRH B *021449z4321.09ND01117.50EaRNG0001/A=000010 70cm Voice (D-Star) 431.15000MHz +0.0000MHz +2026-03-02 15:49:36 CET: VE7MYA-10>APJYC1,TCPIP*,qAC,T2PR:;RCMP *111111z5848.58N/12241.61W! +2026-03-02 15:49:35 CET: FW7629>APRS,TCPXX*,qAX,AMBCWOP-1:@021449z3515.80N/08034.96W_057/000g000t045r000p000P000h71b10318L143AmbientCWOP.com +2026-03-02 15:49:35 CET: FW8057>APRS,TCPXX*,qAX,AMBCWOP-1:@021449z3537.27N/07847.04W_032/003g003t043r000p000P000h72b10311L079AmbientCWOP.com +2026-03-02 15:49:35 CET: CW0333>APRS,TCPXX*,qAX,AMBCWOP-1:@021449z4304.92N/07042.82W_263/003g004t022r000p000P000h35b10324L437AmbientCWOP.com +2026-03-02 15:49:35 CET: GW0586>APRS,TCPXX*,qAX,AMBCWOP-1:@021449z3509.05N/10621.05W_122/001g006t053r000p000P000h35b10208L041AmbientCWOP.com +2026-03-02 15:49:35 CET: OM5KV-15>APNL51,TCPIP*,qAI,OM5KV-15,T2CSNGRAD,T2HUB1,T2HUB3,APRSFI-C1:!4746.48N/01805.55E`Radiosonde Tracker - Based on kxyTrack 1.8-dxl_smod2501 +2026-03-02 15:49:35 CET: KN4CI-1>APTW01,WIDE1-1,WIDE2-1,qAR,K4HNH-10:_03060812c140s004g007t063r000p000P000h18b10236tRSW +2026-03-02 15:49:35 CET: SP6CRO-1>TU4WT3,F4KIT-5*,WIDE2-1,qAR,F4GMA:`{-~<0x1c><0x1d>Sb/"8^}Hello world! +2026-03-02 15:49:35 CET: YM3KBU>APBM1S,TCPIP*,qAS,BM2841:@021401z4020.31N/02901.14ErPHG0000AKRAD - Anadolu Kesintisiz Iletisim Radyo Amat<0xc3><0xb6>rleri Dernegi 439.1625/431.5625 CC1 +2026-03-02 15:49:35 CET: IK3XJT>APRS,TCPXX*,qAX,CWOP-3:@021448z4533.83N/01213.67E_146/000g000t058r000p000P000h77b10230L114.DsVP +2026-03-02 15:49:35 CET: IR3ZZW>APBM1S,TCPIP*,qAS,BM2222:@021449z4629.85N/01306.05ErPHG0000IR3ZZW Trelli di Paularo (UD) 430.8375/435.8375 CC2 +2026-03-02 15:49:35 CET: IR3ZZW>APBM1S,TCPIP*,qAS,BM2222:>https://brandmeister.network/?page=repeater&id=222073 +2026-03-02 15:49:35 CET: YF9UAG-16>AESPG4,TCPIP*,qAC,T2TAIPEI::YF9UAG-16:UNIT.Pkt,Pkt,Pkt,dBm,% +2026-03-02 15:49:35 CET: OK2KQM-7>T9TW46,WIDE1-1,WIDE2-1,qAR,OK0DSK-1:`.6Dl^a>/"71} +2026-03-02 15:49:36 CET: YD1ION-13>AESPG4,TCPIP*,qAC,T2OSAKA::YD1ION-13:PARM.RxTraffic,TxTraffic,RxDrop,RSSI,Voltage +2026-03-02 15:49:36 CET: JH4GQC>APU25N,TCPIP*,qAC,T2FUKUOKA:;ONDO SETO*100541z3411.77N\13232.59EdDX SPOT for Matsuyama & KITA-Hiroshima +2026-03-02 15:49:36 CET: HB9ZF-10>APRS,qAR,DJ8KL-2:!4717.27N/00852.88E&G/D -I-R-R H24 +2026-03-02 15:49:36 CET: HUMANITY>APRS,TCPIP*,qAC,NINTH:=3130.87N/03401.12EsSTOP TERRORISM, SAVE BABIES AND CATS +2026-03-02 15:49:36 CET: CW1648>APN000,TCPXX*,qAX,CWOP-5:@021449z4008.61N/07522.67W_048/000g000t026r000p000P000b10354h49L227eMB63 +2026-03-02 15:49:36 CET: KB8BMY-10>APDR16,TCPIP*,qAC,T2SWEDEN:=4658.94N/12255.55W[209/054/A=000118 Mike - Pacific Northwest +2026-03-02 15:49:36 CET: OE5XIM-10>APMI06,TCPIP*,qAC,OE5XOL::OE5XIM-10:PARM.RX 1h,RX 10m,EF 1h,Total,DC,O1,O2,O3,O4,I1,I2,I3,I4 +2026-03-02 15:49:36 CET: OE5XIM-10>APMI06,TCPIP*,qAC,OE5XOL::OE5XIM-10:UNIT.Pkt,Pkt,Pcnt,Pcnt,Volt,On,On,On,On,Hi,Hi,Hi,Hi +2026-03-02 15:49:36 CET: OE5XIM-10>APMI06,TCPIP*,qAC,OE5XOL::OE5XIM-10:EQNS.0,10,0,0,1,0,0,1,0,0,1,0,0,0.075,0 +2026-03-02 15:49:36 CET: OE5XIM-10>APMI06,TCPIP*,qAC,OE5XOL::OE5XIM-10:BITS.11111111,Telemetry test +2026-03-02 15:49:36 CET: BG7RAV-5>APN000,TCPIP*,qAC,T2HK:=2128.13N/10907.42E5072/012<0xe5><0x8c><0x97><0xe6><0xb5><0xb7><0xe4><0xb8><0xad><0xe7><0xbb><0xa7>439.350-8-88.5 3.8V +2026-03-02 15:49:36 CET: F4DVI-7>APDR16,TCPIP*,qAC,T2PRT:=4355.45N/00454.72E$162/008/A=000189 https://aprsdroid.org/ +2026-03-02 15:49:36 CET: 9A3HVZ>APDR16,TCPIP*,qAC,T2ERFURT:=4602.38N/01603.24EQ259/051/A=000646 MOBILE DMR TG:21966 +2026-03-02 15:49:36 CET: KK7YCF-9>TW3YWS,WIDE1-1,WIDE2-1,qAO,NT7U-10:`21?n](>/`"4q}_5 +2026-03-02 15:49:36 CET: JIGEN>WIDE1-1,TCPIP*,qAC,T2CSNGRAD:!4056.23N/01451.23EI jCLNT V 5.03.3, PC Only, Descr:"Sysop Voip Italia" - FRN System DE - FRN Server server.voip-italia.net:10024, Net:Nazionale +2026-03-02 15:49:36 CET: YO3IXW-14>APRX29,TCPIP*,qAC,T2CSNGRAD:;YO3RAM *111111z4425.52N/02608.92EyOp. Edi | http://www.yo3ram.ro +2026-03-02 15:49:36 CET: BX1AAK-B>APDG02,TCPIP*,qAC,BX1AAK-BS:!2509.59ND12146.03E&RNG0001/A=000010 70cm Voice (D-Star) 431.64000MHz +0.0000MHz diff --git a/protocol/aprs/time.go b/protocol/aprs/time.go deleted file mode 100644 index 3495c90..0000000 --- a/protocol/aprs/time.go +++ /dev/null @@ -1,33 +0,0 @@ -package aprs - -import ( - "fmt" - "time" -) - -type TimeFormatError struct { - Time string -} - -func (err TimeFormatError) Error() string { - return fmt.Sprintf("aprs: unknown time stamp %q", err.Time) -} - -func ParseTime(s string) (time.Time, error) { - if len(s) < 7 { - return time.Time{}, TimeFormatError{s} - } - - switch { - case s[6] == 'z': // Day/Hours/Minutes (DHM) format - return time.Parse("021504", s[:6]) - case s[6] == '/': // Day/Hours/Minutes (DHM) format - return time.Parse("021504", s[:6]) - case s[6] == 'h': // Hours/Minutes/Seconds (HMS) format - return time.Parse("150405", s[:6]) - case len(s) >= 8: // Month/Day/Hours/Minutes (MDHM) format - return time.Parse("01021504", s[:8]) - default: - return time.Time{}, TimeFormatError{s} - } -} diff --git a/protocol/aprs/util.go b/protocol/aprs/util.go new file mode 100644 index 0000000..5459528 --- /dev/null +++ b/protocol/aprs/util.go @@ -0,0 +1,56 @@ +package aprs + +import ( + "strconv" + "strings" +) + +func isDigit(b byte) bool { + return b >= '0' && b <= '9' +} + +func feetToMeters(v float64) float64 { + return v * 0.3048 + +} + +func knotsToMetersPerSecond(v float64) float64 { + return v * 0.514444444 +} + +func milesToMeters(v float64) float64 { + return v * 1609.344 +} + +func fahrenheitToCelcius(v float64) float64 { + return (v - 32) / 1.8 +} + +func fillZeros(s string) string { + s = strings.Replace(s, " ", "0", -1) + s = strings.Replace(s, ".", "0", -1) + return s +} + +func pow(n, exp int) int { + if n <= 0 { + return 1 + } + + result := 1 + for i := 0; i < exp; i++ { + result *= n + } + return result +} + +func parseBytesWithSpaces(b []byte) (int, error) { + for i := len(b) - 1; i >= 0; i-- { + if b[i] == ' ' { + b[i] = '0' + } else { + break + } + } + return strconv.Atoi(string(b)) +} diff --git a/protocol/aprs/weather.go b/protocol/aprs/weather.go new file mode 100644 index 0000000..7ee3293 --- /dev/null +++ b/protocol/aprs/weather.go @@ -0,0 +1,12 @@ +package aprs + +type weatherDecoder struct{} + +func (weatherDecoder) CanDecode(frame Frame) bool { + switch frame.Raw.Type() { + case '!', '#', '$', '*': + return true + default: + return false + } +}