From 626fa9ed9420ae9e36352e92e1988dd4097b4021 Mon Sep 17 00:00:00 2001 From: Ahmed HASAN Date: Tue, 5 Sep 2023 09:28:25 +0300 Subject: [PATCH 1/2] README.md updated for AZT and MCP --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index a922ede..b60c18e 100644 --- a/README.md +++ b/README.md @@ -47,6 +47,7 @@ on [IEC 61162-1:2016 (Edition 5.0 2016-08)](https://webstore.iec.ch/publication/ | [ALR](./alr.go) | Set alarm state | | | [APB](./apb.go) | Heading/track controller (autopilot) sentence B | [gpsd](https://gpsd.gitlab.io/gpsd/NMEA.html#_apb_autopilot_sentence_b) | | [ARC](./arc.go) | Alert command refused | | +| [AZT](./azt.go) | Azimuth Thruster message | | | [BBM](./bbm.go) | AIS broadcast binary message | | | [BEC](./bec.go) | Bearing and distance to waypoint, Dead reckoning | [1](http://www.nmea.de/nmea0183datensaetze.html#bec) | | [BOD](./bod.go) | Bearing origin to destination | [gpsd](https://gpsd.gitlab.io/gpsd/NMEA.html#_bod_bearing_waypoint_to_waypoint) | @@ -95,6 +96,7 @@ on [IEC 61162-1:2016 (Edition 5.0 2016-08)](https://webstore.iec.ch/publication/ | LR3 | AIS long-range reply sentence 3 | | | LRF | AIS long-range function | | | LRI | AIS long-range interrogation | | +| [MCP](./mcp.go) | Micropilot Joystick controller message | | | [MDA](./mda.go) | Meteorological Composite | [gpsd](https://gpsd.gitlab.io/gpsd/NMEA.html#_mda_meteorological_composite) | | [MTA](./mta.go) | Air Temperature (obsolete, use XDR instead) | | | MOB | Man over board notification | | From 011d4ad8064ece1d39077481b1ca72c9fa2e74a1 Mon Sep 17 00:00:00 2001 From: Ahmed HASAN Date: Tue, 5 Sep 2023 09:29:30 +0300 Subject: [PATCH 2/2] AZT and MCP sentences added --- azt.go | 60 ++++++++++++++++++++++++++++++++++++++++++++++++++++ azt_test.go | 61 +++++++++++++++++++++++++++++++++++++++++++++++++++++ mcp.go | 38 +++++++++++++++++++++++++++++++++ mcp_test.go | 50 +++++++++++++++++++++++++++++++++++++++++++ sentence.go | 4 ++++ 5 files changed, 213 insertions(+) create mode 100644 azt.go create mode 100644 azt_test.go create mode 100644 mcp.go create mode 100644 mcp_test.go diff --git a/azt.go b/azt.go new file mode 100644 index 0000000..b8bcd15 --- /dev/null +++ b/azt.go @@ -0,0 +1,60 @@ +package nmea + +const ( + // TypeAZT type for AZT sentences + TypeAZT = "AZT" +) + +// AZT - Azimuth Thruster Message +// Format: $--AZT,x.x,x.x,x.x,x.x,x.x,x.x,x.x,x.x,x.x,x.x,x.x,x.x,x.x,x.x,x.x,x.x,x*hh +// Example: $IIAZT,1,45.5,90.0,50.0,60.0,75.0,30.0,40.0,80.0,70.0,3,85.0,0,0,0,12345,9876,5432*2B +type AZT struct { + BaseSentence + ThrusterNo int64 // 1...9 Azimuth thruster number 1..9. + SteeringCommandSetValue float64 // 0...359.9 ° Steering command set value. + SteeringMeasurementActualValue float64 // 0...359.9 ° Steering measurement actual value. + PrimeMoverCommandSetValue float64 // -100...+100 % Prime mover (P.M.) RPM command set value. +100% equals nominal RPM (negative optional). + PrimeMoverMeasurementActualValue float64 // -100...+100 % P.M. RPM measurement actual value. +100% equals nominal RPM (negative optional). + VariableSlippingClutchCommandSetValue float64 // 0...100 % Variable slipping clutch command set value. 100% equals fully engaged clutch (no slipping), 0% equals clutch disengaged. . + PitchCommandSetValue float64 // -100...+100 % Pitch command set value. -100% equals max. negative pitch, 0% equals zero pitch, +100% equals max. positive pitch. . + PitchMeasurementActualValue float64 // -100...+100 % Pitch measurement actual value. . + PMLoadLimitSetValue float64 // 0...120 % P.M. load limit set value (LLS). . + PMLoadLimitCurrentMaxValue float64 // 0...120 % P.M. load limit current max. allowed value. . + PMLoadMeasurementActualValue float64 // 0...120 % P.M. load measurement actual value (FPS). . + ActiveControlStationNumber int64 // 0...9 Active control station number in range of 1…9. Value is zero if no control station is selected. + PropellerRPMMeasurementActualValue float64 // 0...100 % Propeller RPM measurement actual value. +100% equals nominal RPM (used with slipping clutch). + Reserved1 float64 // Reserved for future use. . + Reserved2 float64 // Reserved for future use. . + Reserved3 float64 // Reserved for future use. . + ValueErrorStatusWord int64 // Value error status word for values 1...15, each bit indicates a single value error condition state either OK or ERROR. + ControlStateWord1 int64 // Control state word 1, each bit indicates a separate condition state either ON or OFF. + ControlStateWord2 int64 // Control state word 2 (pitch), each bit indicates a separate condition state either ON or OFF. +} + +// newAZT constructor +func newAZT(s BaseSentence) (Sentence, error) { + p := NewParser(s) + p.AssertType(TypeAZT) + return AZT{ + BaseSentence: s, + ThrusterNo: p.Int64(0, "x"), + SteeringCommandSetValue: p.Float64(1, "value1"), + SteeringMeasurementActualValue: p.Float64(2, "value2"), + PrimeMoverCommandSetValue: p.Float64(3, "value3"), + PrimeMoverMeasurementActualValue: p.Float64(4, "value4"), + VariableSlippingClutchCommandSetValue: p.Float64(5, "value5"), + PitchCommandSetValue: p.Float64(6, "value6"), + PitchMeasurementActualValue: p.Float64(7, "value7"), + PMLoadLimitSetValue: p.Float64(8, "value8"), + PMLoadLimitCurrentMaxValue: p.Float64(9, "value9"), + PMLoadMeasurementActualValue: p.Float64(10, "value10"), + ActiveControlStationNumber: p.Int64(11, "value11"), + PropellerRPMMeasurementActualValue: p.Float64(12, "value12"), + Reserved1: p.Float64(13, "value13"), + Reserved2: p.Float64(14, "value14"), + Reserved3: p.Float64(15, "value15"), + ValueErrorStatusWord: p.Int64(16, "vesw"), + ControlStateWord1: p.Int64(17, "csw1"), + ControlStateWord2: p.Int64(18, "csw2"), + }, p.Err() +} diff --git a/azt_test.go b/azt_test.go new file mode 100644 index 0000000..fc93241 --- /dev/null +++ b/azt_test.go @@ -0,0 +1,61 @@ +package nmea + +import ( + "github.com/stretchr/testify/assert" + "testing" +) + +var azttests = []struct { + name string + raw string + err string + msg AZT +}{ + { + name: "good sentence", + raw: "$IIAZT,1,45.5,90.0,50.0,60.0,75.0,30.0,40.0,80.0,70.0,70.0,3,85.0,0,0,0,12345,9876,5432*70", + msg: AZT{ + ThrusterNo: 1, + SteeringCommandSetValue: 45.5, + SteeringMeasurementActualValue: 90.0, + PrimeMoverCommandSetValue: 50.0, + PrimeMoverMeasurementActualValue: 60.0, + VariableSlippingClutchCommandSetValue: 75.0, + PitchCommandSetValue: 30.0, + PitchMeasurementActualValue: 40.0, + PMLoadLimitSetValue: 80.0, + PMLoadLimitCurrentMaxValue: 70.0, + PMLoadMeasurementActualValue: 70.0, + ActiveControlStationNumber: 3, + PropellerRPMMeasurementActualValue: 85.0, + Reserved1: 0, + Reserved2: 0, + Reserved3: 0, + ValueErrorStatusWord: 12345, + ControlStateWord1: 9876, + ControlStateWord2: 5432, + }, + }, + { + name: "bad validity", + raw: "$IIAZT,1,45.5,90.0,50.0,60.0,75.0,30.0,40.0,80.0,70.0,70.0,3,85.0,0,0,0,12345,9876,5432*74", + err: "nmea: sentence checksum mismatch [70 != 74]", + }, +} + +func TestAZT(t *testing.T) { + for _, tt := range azttests { + 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) + azt := m.(AZT) + azt.BaseSentence = BaseSentence{} + assert.Equal(t, tt.msg, azt) + } + }) + } +} diff --git a/mcp.go b/mcp.go new file mode 100644 index 0000000..1ae775a --- /dev/null +++ b/mcp.go @@ -0,0 +1,38 @@ +package nmea + +const ( + // TypeMCP type for MCP sentences + TypeMCP = "MCP" +) + +// MCP - Micropilot Joystick Controller Message +// Format: $--MCP,x.x,f,x.x,f,x.x,f,x.x,f,x.x,f,x,i,x,i,x,i*hh +// Example: $IIMCP,50.0,%f,30.0,%f,45.0,%f,0,0,0*hh +type MCP struct { + BaseSentence + JoystickSurgeAxisCommandSetValue float64 // Joystick surge axis (ahead - astern) command set value. + JoystickSwayAxisCommandSetValue float64 // Joystick sway axis (sideways port - sideways starboard) command set value. + JoystickYawAxisCommandSetValue float64 // Joystick yaw axis (rotation counter-clockwise - clockwise) command set value. + Reserved1 float64 // Reserved for future use. + Reserved2 float64 // Reserved for future use. + ValueErrorStatusWord int64 // Value error status word for values 1...5, each bit indicates a single value error condition state either OK or ERROR. + ControlStateWord1 int64 // Control state word 1, each bit indicates a separate condition state either ON or OFF. + ControlStateWord2 int64 // Control state word 2, each bit indicates a separate condition state either ON or OFF. +} + +// newMCP constructor +func newMCP(s BaseSentence) (Sentence, error) { + p := NewParser(s) + p.AssertType(TypeMCP) + return MCP{ + BaseSentence: s, + JoystickSurgeAxisCommandSetValue: p.Float64(0, "value1"), + JoystickSwayAxisCommandSetValue: p.Float64(1, "value2"), + JoystickYawAxisCommandSetValue: p.Float64(2, "value3"), + Reserved1: p.Float64(3, "value4"), + Reserved2: p.Float64(4, "value5"), + ValueErrorStatusWord: p.Int64(5, "vesw"), + ControlStateWord1: p.Int64(6, "csw1"), + ControlStateWord2: p.Int64(7, "csw2"), + }, p.Err() +} diff --git a/mcp_test.go b/mcp_test.go new file mode 100644 index 0000000..4df1679 --- /dev/null +++ b/mcp_test.go @@ -0,0 +1,50 @@ +package nmea + +import ( + "github.com/stretchr/testify/assert" + "testing" +) + +var mcptests = []struct { + name string + raw string + err string + msg MCP +}{ + { + name: "good sentence", + raw: "$IIMCP,50.0,25.0,10.0,0,0,12345,9876,5432*72", + msg: MCP{ + JoystickSurgeAxisCommandSetValue: 50.0, + JoystickSwayAxisCommandSetValue: 25.0, + JoystickYawAxisCommandSetValue: 10.0, + Reserved1: 0, + Reserved2: 0, + ValueErrorStatusWord: 12345, + ControlStateWord1: 9876, + ControlStateWord2: 5432, + }, + }, + { + name: "bad validity", + raw: "$IIMCP,50.0,25.0,10.0,0,0,12345,9876,5432*64", + err: "nmea: sentence checksum mismatch [72 != 64]", + }, +} + +func TestMCP(t *testing.T) { + for _, tt := range mcptests { + 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) + mcp := m.(MCP) + mcp.BaseSentence = BaseSentence{} + assert.Equal(t, tt.msg, mcp) + } + }) + } +} diff --git a/sentence.go b/sentence.go index 2619275..3444ea1 100644 --- a/sentence.go +++ b/sentence.go @@ -435,6 +435,10 @@ func (p *SentenceParser) Parse(raw string) (Sentence, error) { return newXDR(s) case TypeXTE: return newXTE(s) + case TypeMCP: + return newMCP(s) + case TypeAZT: + return newAZT(s) } } if s.Raw[0] == SentenceStartEncapsulated[0] {