protocol/aprs: refactored
This commit is contained in:
433
protocol/aprs/position_test.go
Normal file
433
protocol/aprs/position_test.go
Normal file
@@ -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)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user