Skip to content

Commit

Permalink
Add DTM, OSD, RSD, TLL, TTM, VBW sentences (#93)
Browse files Browse the repository at this point in the history
  • Loading branch information
aldas committed Feb 26, 2022
1 parent e6fa0a7 commit d882190
Show file tree
Hide file tree
Showing 15 changed files with 1,016 additions and 4 deletions.
6 changes: 6 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ At this moment, this library supports the following sentence types:
| [DPT](https://gpsd.gitlab.io/gpsd/NMEA.html#_dpt_depth_of_water) | Depth of Water |
| [DSC](./dsc.go) | Digital Selective Calling Information |
| [DSE](./dse.go) | Expanded digital selective calling |
| [DTM](https://gpsd.gitlab.io/gpsd/NMEA.html#_dtm_datum_reference) | Datum Reference |
| [EVE](./eve.go) | General Event Message |
| [FIR](./fir.go) | Fire Detection event with time and location |
| [GGA](http://aprs.gids.nl/nmea/#gga) | GPS Positioning System Fix Data |
Expand All @@ -62,14 +63,19 @@ At this moment, this library supports the following sentence types:
| [MTW](https://gpsd.gitlab.io/gpsd/NMEA.html#_mtw_mean_temperature_of_water) | Mean Temperature of Water |
| [MWD](https://www.tronico.fi/OH6NT/docs/NMEA0183.pdf) | Wind Direction and Speed |
| [MWV](https://gpsd.gitlab.io/gpsd/NMEA.html#_mwv_wind_speed_and_angle) | Wind Speed and Angle |
| [OSD](https://gpsd.gitlab.io/gpsd/NMEA.html#_osd_own_ship_data) | Own Ship Data |
| [RMB](https://gpsd.gitlab.io/gpsd/NMEA.html#_rmb_recommended_minimum_navigation_information) | Recommended Minimum Navigation Information |
| [RMC](http://aprs.gids.nl/nmea/#rmc) | Recommended Minimum Specific GPS/Transit data |
| [ROT](https://gpsd.gitlab.io/gpsd/NMEA.html#_rot_rate_of_turn) | Rate of turn |
| [RPM](https://gpsd.gitlab.io/gpsd/NMEA.html#_rpm_revolutions) | Engine or Shaft revolutions and pitch |
| [RSA](https://gpsd.gitlab.io/gpsd/NMEA.html#_rsa_rudder_sensor_angle) | Rudder Sensor Angle |
| [RSD](https://gpsd.gitlab.io/gpsd/NMEA.html#_rsd_radar_system_data) | RADAR System Data |
| [RTE](http://aprs.gids.nl/nmea/#rte) | Route |
| [THS](http://www.nuovamarea.net/pytheas_9.html) | Actual vessel heading in degrees True and status |
| [TLL](https://gpsd.gitlab.io/gpsd/NMEA.html#_tll_target_latitude_and_longitude) | Target latitude and longitude |
| [TTM](https://gpsd.gitlab.io/gpsd/NMEA.html#_ttm_tracked_target_message) | Tracked Target Message |
| [TXT](https://www.nmea.org/Assets/20160520%20txt%20amendment.pdf) | Sentence is for the transmission of text messages |
| [VBW](https://gpsd.gitlab.io/gpsd/NMEA.html#_vbw_dual_groundwater_speed) | Dual Ground/Water Speed |
| [VDM/VDO](https://gpsd.gitlab.io/gpsd/AIVDM.html) | Encapsulated binary payload (commonly used with AIS data) |
| [VDR](https://gpsd.gitlab.io/gpsd/NMEA.html#_vdr_set_and_drift) | Set and Drift |
| [VHW](https://www.tronico.fi/OH6NT/docs/NMEA0183.pdf) | Water Speed and Heading |
Expand Down
48 changes: 48 additions & 0 deletions dtm.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
package nmea

const (
// TypeDTM type of DTM sentence for Datum Reference
TypeDTM = "DTM"
)

// DTM - Datum Reference
// https://gpsd.gitlab.io/gpsd/NMEA.html#_dtm_datum_reference
//
// Format: $--DTM,ref,x,llll,c,llll,c,aaa,ref*hh<CR><LF>
// Example: $GPDTM,W84,,0.0,N,0.0,E,0.0,W84*6F
// Example: $GPDTM,W84,,00.0000,N,00.0000,W,,W84*53
type DTM struct {
BaseSentence
LocalDatumCode string // Local datum code (W84,W72,S85,P90,999)
LocalDatumSubcode string // Local datum subcode. May be blank.

LatitudeOffsetMinute float64 // Latitude offset (minutes) (negative if south)
LongitudeOffsetMinute float64 // Longitude offset (minutes) (negative if west)

AltitudeOffsetMeters float64 // Altitude offset in meters
DatumName string // Reference datum name. What’s usually seen here is "W84", the standard WGS84 datum used by GPS.
}

// newDTM constructor
func newDTM(s BaseSentence) (DTM, error) {
p := NewParser(s)
p.AssertType(TypeDTM)
m := DTM{
BaseSentence: s,
LocalDatumCode: p.String(0, "local datum code"),
LocalDatumSubcode: p.String(1, "local datum subcode"),

LatitudeOffsetMinute: p.Float64(2, "latitude offset minutes"),
LongitudeOffsetMinute: p.Float64(4, "longitude offset minutes"),

AltitudeOffsetMeters: p.Float64(6, "altitude offset offset"),
DatumName: p.String(7, "datum name"),
}
if p.String(3, "latitude offset direction") == South {
m.LatitudeOffsetMinute *= -1
}
if p.String(5, "longitude offset direction") == West {
m.LongitudeOffsetMinute *= -1
}
return m, p.Err()
}
71 changes: 71 additions & 0 deletions dtm_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
package nmea

import (
"github.com/stretchr/testify/assert"
"testing"
)

func TestDTM(t *testing.T) {
var tests = []struct {
name string
raw string
err string
msg DTM
}{
{
name: "good sentence 1",
raw: "$GPDTM,W84,,0.0,N,0.0,E,0.0,W84*6F",
msg: DTM{
BaseSentence: BaseSentence{},
LocalDatumCode: "W84",
LocalDatumSubcode: "",
LatitudeOffsetMinute: 0,
LongitudeOffsetMinute: 0,
AltitudeOffsetMeters: 0,
DatumName: "W84",
},
},
{
name: "good sentence 2",
raw: "$GPDTM,W84,X,00.1200,S,12.0000,W,100,W84*27",
msg: DTM{
BaseSentence: BaseSentence{},
LocalDatumCode: "W84",
LocalDatumSubcode: "X",
LatitudeOffsetMinute: -0.12,
LongitudeOffsetMinute: -12,
AltitudeOffsetMeters: 100,
DatumName: "W84",
},
},
{
name: "invalid nmea: LatitudeOffsetMinute",
raw: "$GPDTM,W84,,x,N,0.0,E,0.0,W84*39",
err: "nmea: GPDTM invalid latitude offset minutes: x",
},
{
name: "invalid nmea: LongitudeOffsetMinute",
raw: "$GPDTM,W84,,0.0,N,x,E,0.0,W84*39",
err: "nmea: GPDTM invalid longitude offset minutes: x",
},
{
name: "invalid nmea: AltitudeOffsetMeters",
raw: "$GPDTM,W84,,0.0,N,0.0,E,x,W84*39",
err: "nmea: GPDTM invalid altitude offset offset: x",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
m, err := Parse(tt.raw)
if tt.err != "" {
assert.Error(t, err)
assert.EqualError(t, err, tt.err)
} else {
assert.NoError(t, err)
mm := m.(DTM)
mm.BaseSentence = BaseSentence{}
assert.Equal(t, tt.msg, mm)
}
})
}
}
103 changes: 103 additions & 0 deletions osd.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
package nmea

const (
// TypeOSD type for OSD sentence for Own Ship Data
TypeOSD = "OSD"

// OSDReferenceBottomTrackingLog is reference for bottom tracking log
OSDReferenceBottomTrackingLog = "B"
// OSDReferenceManual is reference for manually entered
OSDReferenceManual = "M"
// OSDReferenceWaterReferenced is reference for water referenced
OSDReferenceWaterReferenced = "W"
// OSDReferenceRadarTracking is reference for radar tracking of fixed target
OSDReferenceRadarTracking = "R"
// OSDReferencePositioningSystemGroundReference is reference for positioning system ground reference
OSDReferencePositioningSystemGroundReference = "P"
)

// OSD - Own Ship Data
// https://gpsd.gitlab.io/gpsd/NMEA.html#_osd_own_ship_data
// https://github.com/nohal/OpenCPN/wiki/ARPA-targets-tracking-implementation#osd---own-ship-data
//
// Format: $--OSD,x.x,A,x.x,a,x.x,a,x.x,x.x,a*hh<CR><LF>
// Example: $RAOSD,179.0,A,179.0,M,00.0,M,,,N*76
type OSD struct {
BaseSentence
// Heading is Heading in degrees
Heading float64

// HeadingStatus is Heading status
// * A - data valid
// * V - data invalid
HeadingStatus string

// VesselTrueCourse is Vessel Course, degrees True
VesselTrueCourse float64

// CourseReference is Course Reference, B/M/W/R/P
// * B - bottom tracking log
// * M - manually entered
// * W - water referenced
// * R - radar tracking of fixed target
// * P - positioning system ground reference
CourseReference string

// VesselSpeed is Vessel Speed
VesselSpeed float64

// SpeedReference is Speed Reference, B/M/W/R/P
// * B - bottom tracking log
// * M - manually entered
// * W - water referenced
// * R - radar tracking of fixed target
// * P - positioning system ground reference.
SpeedReference string

// VesselSetTrue is Vessel Set, degrees True - Manually entered
VesselSetTrue float64

// VesselDrift is Vessel drift (speed) - Manually entered
VesselDrift float64

// SpeedUnits is Speed Units
// * K - km/h
// * N - Knots
// * S - statute miles/h
SpeedUnits string
}

// newOSD constructor
func newOSD(s BaseSentence) (OSD, error) {
p := NewParser(s)
p.AssertType(TypeOSD)
m := OSD{
BaseSentence: s,
Heading: p.Float64(0, "heading"),
HeadingStatus: p.EnumString(1, "heading status", StatusValid, StatusInvalid),
VesselTrueCourse: p.Float64(2, "vessel course true"),
CourseReference: p.EnumString(
3,
"course reference",
OSDReferenceBottomTrackingLog,
OSDReferenceManual,
OSDReferenceWaterReferenced,
OSDReferenceRadarTracking,
OSDReferencePositioningSystemGroundReference,
),
VesselSpeed: p.Float64(4, "vessel speed"),
SpeedReference: p.EnumString(
5,
"speed reference",
OSDReferenceBottomTrackingLog,
OSDReferenceManual,
OSDReferenceWaterReferenced,
OSDReferenceRadarTracking,
OSDReferencePositioningSystemGroundReference,
),
VesselSetTrue: p.Float64(6, "vessel set"),
VesselDrift: p.Float64(7, "vessel drift"),
SpeedUnits: p.EnumString(8, "speed units", DistanceUnitKilometre, DistanceUnitNauticalMile, DistanceUnitStatuteMile),
}
return m, p.Err()
}
91 changes: 91 additions & 0 deletions osd_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
package nmea

import (
"github.com/stretchr/testify/assert"
"testing"
)

func TestOSD(t *testing.T) {
var tests = []struct {
name string
raw string
err string
msg OSD
}{
{
name: "good sentence",
raw: "$RAOSD,179.0,A,179.0,M,00.0,M,,,N*76",
msg: OSD{
BaseSentence: BaseSentence{},
Heading: 179,
HeadingStatus: "A",
VesselTrueCourse: 179,
CourseReference: "M",
VesselSpeed: 0,
SpeedReference: "M",
VesselSetTrue: 0,
VesselDrift: 0,
SpeedUnits: "N",
},
},
{
name: "invalid nmea: Heading",
raw: "$RAOSD,x179.0,A,179.0,M,00.0,M,,,N*0e",
err: "nmea: RAOSD invalid heading: x179.0",
},
{
name: "invalid nmea: HeadingStatus",
raw: "$RAOSD,179.0,xA,179.0,M,00.0,M,,,N*0e",
err: "nmea: RAOSD invalid heading status: xA",
},
{
name: "invalid nmea: VesselTrueCourse",
raw: "$RAOSD,179.0,A,x179.0,M,00.0,M,,,N*0e",
err: "nmea: RAOSD invalid vessel course true: x179.0",
},
{
name: "invalid nmea: CourseReference",
raw: "$RAOSD,179.0,A,179.0,xM,00.0,M,,,N*0e",
err: "nmea: RAOSD invalid course reference: xM",
},
{
name: "invalid nmea: VesselSpeed",
raw: "$RAOSD,179.0,A,179.0,M,x00.0,M,,,N*0e",
err: "nmea: RAOSD invalid vessel speed: x00.0",
},
{
name: "invalid nmea: SpeedReference",
raw: "$RAOSD,179.0,A,179.0,M,00.0,xM,,,N*0e",
err: "nmea: RAOSD invalid speed reference: xM",
},
{
name: "invalid nmea: VesselSetTrue",
raw: "$RAOSD,179.0,A,179.0,M,00.0,M,x,,N*0e",
err: "nmea: RAOSD invalid vessel set: x",
},
{
name: "invalid nmea: VesselDrift",
raw: "$RAOSD,179.0,A,179.0,M,00.0,M,,x,N*0e",
err: "nmea: RAOSD invalid vessel drift: x",
},
{
name: "invalid nmea: SpeedUnits",
raw: "$RAOSD,179.0,A,179.0,M,00.0,M,,,xN*0e",
err: "nmea: RAOSD invalid speed units: xN",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
m, err := Parse(tt.raw)
if tt.err != "" {
assert.Error(t, err)
assert.EqualError(t, err, tt.err)
} else {
assert.NoError(t, err)
mm := m.(OSD)
mm.BaseSentence = BaseSentence{}
assert.Equal(t, tt.msg, mm)
}
})
}
}
Loading

0 comments on commit d882190

Please sign in to comment.