Skip to content

Commit

Permalink
Fix engine flags (#2277)
Browse files Browse the repository at this point in the history
* Fix engine flags

The documentation is ambiguous, so now checked against SignalK

* Update unit tests
  • Loading branch information
pgrawehr authored Feb 15, 2024
1 parent 24d6b2d commit d4fea8a
Show file tree
Hide file tree
Showing 7 changed files with 83 additions and 29 deletions.
5 changes: 5 additions & 0 deletions src/devices/Nmea0183/NmeaParser.cs
Original file line number Diff line number Diff line change
Expand Up @@ -226,6 +226,11 @@ private void Sender()
}
}

if (sentenceToSend.Valid == false)
{
continue;
}

TalkerSentence ts = new TalkerSentence(sentenceToSend);
string dataToSend = ts.ToString() + "\r\n";
byte[] buffer = _encoding.GetBytes(dataToSend);
Expand Down
28 changes: 26 additions & 2 deletions src/devices/Nmea0183/Sentences/EngineRevolutions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,12 @@ public class EngineRevolutions : NmeaSentence
private static bool Matches(TalkerSentence sentence) => Matches(sentence.Id);

/// <summary>
/// Constructs a new MWV sentence
/// Constructs a new RPM sentence
/// </summary>
/// <param name="source">RPM source, typically <see cref="RotationSource.Engine"/></param>
/// <param name="speed">Engine rotational speed</param>
/// <param name="engineNumber">Engine Number, counting from 1</param>
/// <param name="pitch">Propeller pitch, if applicable</param>
public EngineRevolutions(RotationSource source, RotationalSpeed speed, int engineNumber, Ratio pitch)
: base(OwnTalkerId, Id, DateTimeOffset.UtcNow)
{
Expand All @@ -36,8 +40,13 @@ public EngineRevolutions(RotationSource source, RotationalSpeed speed, int engin
}

/// <summary>
/// Constructs a new MWV sentence
/// Constructs a new RPM sentence
/// </summary>
/// <param name="talker">Custom talker Id</param>
/// <param name="source">RPM source, typically <see cref="RotationSource.Engine"/></param>
/// <param name="speed">Engine rotational speed</param>
/// <param name="engineNumber">Engine Number, counting from 1</param>
/// <param name="pitch">Propeller pitch, if applicable</param>
public EngineRevolutions(TalkerId talker, RotationSource source, RotationalSpeed speed, int engineNumber, Ratio pitch)
: base(talker, Id, DateTimeOffset.UtcNow)
{
Expand All @@ -48,6 +57,21 @@ public EngineRevolutions(TalkerId talker, RotationSource source, RotationalSpeed
Valid = true;
}

/// <summary>
/// Constructs an RPM sentence from an <see cref="EngineData"/> instance
/// </summary>
/// <param name="talker">Custom talker Id</param>
/// <param name="engineData">Engine data set</param>
public EngineRevolutions(TalkerId talker, EngineData engineData)
: base(talker, Id, DateTimeOffset.UtcNow)
{
RotationSource = RotationSource.Engine;
RotationalSpeed = engineData.Revolutions;
EngineNumber = engineData.EngineNo + 1;
PropellerPitch = engineData.Pitch;
Valid = true;
}

/// <summary>
/// Internal constructor
/// </summary>
Expand Down
42 changes: 21 additions & 21 deletions src/devices/Nmea0183/Sentences/EngineStatus.cs
Original file line number Diff line number Diff line change
Expand Up @@ -26,85 +26,85 @@ public enum EngineStatus : UInt32
/// <summary>
/// Engine should be checked, or engine is performing self test.
/// </summary>
CheckEngine = 0b_0000_0000_0000_0001,
CheckEngine = 0b_0000_0001_0000_0000,

/// <summary>
/// Engine is too hot
/// </summary>
OverTemperature = 0b_0000_0000_0000_0010,
OverTemperature = 0b_0010_0000_0000_0000,

/// <summary>
/// Engine lubrication oil pressure is low
/// </summary>
LowOilPressure = 0b_0000_0000_0000_0100,
LowOilPressure = 0b_0000_0100_0000_0000,

/// <summary>
/// Not enough lubrication oil
/// </summary>
LowOilLevel = 0b_0000_0000_0000_1000,
LowOilLevel = 0b_0000_1000_0000_0000,

/// <summary>
/// Low fuel pressure
/// </summary>
LowFuelPressure = 0b_0000_0000_0001_0000,
LowFuelPressure = 0b_0001_0000_0000_0000,

/// <summary>
/// Low battery voltage
/// </summary>
LowSystemVoltage = 0b0000_0000_0010_0000,
LowSystemVoltage = 0b0010_0000_0000_0000,

/// <summary>
/// Not enough coolant water in the coolant loop
/// </summary>
LowCoolantLevel = 0b0000_0000_0100_0000,
LowCoolantLevel = 0b0100_0000_0000_0000,

/// <summary>
/// Low external cooling water flow. Check inlet valves.
/// </summary>
WaterFlow = 0b0000_0000_1000_0000,
WaterFlow = 0b1000_0000_0000_0000,

/// <summary>
/// There's water in the fuel.
/// </summary>
WaterInFuel = 0b0000_0001_0000_0000,
WaterInFuel = 0b0000_0000_0000_0001,

/// <summary>
/// The alternator is not charging. This is expected to be on when the engine main switch
/// is enabled but the engine is not (yet) running. If it is on when the engine is running,
/// the alternator is probably broken.
/// </summary>
ChargeIndicator = 0b0000_0010_0000_0000,
ChargeIndicator = 0b_0000_0000_0000_0010,

/// <summary>
/// The engine is pre-heating. One should wait until this signal goes off before attempting
/// to start the engine.
/// </summary>
PreheatIndicator = 0b0000_0100_0000_0000,
PreheatIndicator = 0b0000_0000_0000_0100,

/// <summary>
/// ???
/// </summary>
HighBoostPressure = 0b0000_1000_0000_0000,
HighBoostPressure = 0b0000_0000_0000_1000,

/// <summary>
/// The engine is running above it's designated safe RPM limit.
/// </summary>
RevLimitExceeded = 0b0001_0000_0000_0000,
RevLimitExceeded = 0b0000_0000_0001_0000,

/// <summary>
/// EGR System failure?
/// </summary>
EgrSystem = 0b0010_0000_0000_0000,
EgrSystem = 0b0000_0000_0010_0000,

/// <summary>
/// The throttle position sensor is broken
/// </summary>
ThrottlePositionSensor = 0b0100_0000_0000_0000,
ThrottlePositionSensor = 0b0000_0000_0100_0000,

/// <summary>
/// The engine entered emergency stop mode
/// </summary>
EngineEmergencyStopMode = 0b1000_0000_0000_0000,
EngineEmergencyStopMode = 0b0000_0000_1000_0000,

/// <summary>
/// This is a level 1 warning
Expand All @@ -124,27 +124,27 @@ public enum EngineStatus : UInt32
/// <summary>
/// Engine maintenance is needed
/// </summary>
MaintenanceNeeded = 0b1000_0000_0000_0000_0000,
MaintenanceNeeded = 0b1000_0000_0000_0000_0000_0000_0000,

/// <summary>
/// There was an error communicating with the engine controls
/// </summary>
EngineCommError = 0b0001_0000_0000_0000_0000_0000,
EngineCommError = 0b0001_0000_0000_0000_0000_0000_0000_0000,

/// <summary>
/// ???
/// </summary>
SecondaryThrottle = 0b0010_0000_0000_0000_0000_0000,
SecondaryThrottle = 0b0010_0000_0000_0000_0000_0000_0000_0000,

/// <summary>
/// The engine cannot be started because the gear is not in "neutral" position. Set gear and throttle
/// to neutral and try again.
/// </summary>
NeutralStartProtect = 0b0100_0000_0000_0000_0000_0000,
NeutralStartProtect = 0b0100_0000_0000_0000_0000_0000_0000_0000,

/// <summary>
/// The engine is shutting down.
/// </summary>
EngineShuttingDown = 0b1000_0000_0000_0000_0000_0000,
EngineShuttingDown = 0b1000_0000_0000_0000_0000_0000_0000_0000,
}
}
12 changes: 8 additions & 4 deletions src/devices/Nmea0183/Sentences/SeaSmartEngineDetail.cs
Original file line number Diff line number Diff line change
Expand Up @@ -203,9 +203,13 @@ public override string ToNmeaParameterList()
string swappedString = operatingTimeString.Substring(6, 2) + operatingTimeString.Substring(4, 2) +
operatingTimeString.Substring(2, 2) + operatingTimeString.Substring(0, 2);

// Status = 0 is ok, anything else seems to indicate a fault
uint status = (uint)Status;
string statusString = status.ToString("X4", CultureInfo.InvariantCulture);
// Lower 16 bits of status
uint status1 = ((uint)Status & 0xFFFF);
string status1String = status1.ToString("X4", CultureInfo.InvariantCulture);

uint status2 = ((uint)Status & 0xFFFF0000) >> 16;
string status2String = status2.ToString("X4", CultureInfo.InvariantCulture);

int engineTempKelvin;
if (Temperature.HasValue)
{
Expand All @@ -219,7 +223,7 @@ public override string ToNmeaParameterList()
string engineTempString = engineTempKelvin.ToString("X4", CultureInfo.InvariantCulture);
// Seems to require a little endian conversion as well
engineTempString = engineTempString.Substring(2, 2) + engineTempString.Substring(0, 2);
return "01F201," + timeStampText + ",02," + engineNoText + "0000FFFF" + engineTempString + "00050000" + swappedString + "FFFF000000" + statusString + "00007F7F";
return "01F201," + timeStampText + ",02," + engineNoText + "0000FFFF" + engineTempString + "00050000" + swappedString + "FFFF000000" + status1String + status2String + "7F7F";

}

Expand Down
2 changes: 1 addition & 1 deletion src/devices/Nmea0183/Sentences/SeaSmartEngineFast.cs
Original file line number Diff line number Diff line change
Expand Up @@ -129,7 +129,7 @@ public RotationalSpeed RotationalSpeed
}

/// <summary>
/// Number of the engine.
/// Number of the engine, 0-based.
/// </summary>
public int EngineNumber
{
Expand Down
8 changes: 8 additions & 0 deletions src/devices/Nmea0183/samples/NmeaSimulator/NmeaSimulator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -197,6 +197,14 @@ private void SendNewData()
WaterSpeedAndAngle vhw = new WaterSpeedAndAngle(null, null, data.SpeedTroughWater);
SendSentence(vhw);

var engineData = new EngineData(10000, EngineStatus.None | EngineStatus.MaintenanceNeeded | EngineStatus.LowCoolantLevel, 0, RotationalSpeed.FromRevolutionsPerMinute(2000), Ratio.FromPercent(20), TimeSpan.FromHours(1.5), Temperature.FromDegreesCelsius(60.6));
// EngineRevolutions rv = new EngineRevolutions(TalkerId.ElectronicChartDisplayAndInformationSystem, engineData);
// SendSentence(rv);
SeaSmartEngineFast fast = new SeaSmartEngineFast(engineData);
SendSentence(fast);
SeaSmartEngineDetail detail = new SeaSmartEngineDetail(engineData);
SendSentence(detail);

// Test Seatalk message (understood by some OpenCPN plugins)
////RawSentence sentence = new RawSentence(new TalkerId('S', 'T'), new SentenceId("ALK"), new string[]
////{
Expand Down
15 changes: 14 additions & 1 deletion src/devices/Nmea0183/tests/EngineSentencesTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ public void NewImplementation()

Assert.Equal(@"$ECRPM,E,1,0,0,A*50
$PCDIN,01F200,00002710,02,000000FFFF00FFFF*23
$PCDIN,01F201,00002710,02,000000FFFFE7720005000018150000FFFF000000000100007F7F*5C
$PCDIN,01F201,00002710,02,000000FFFFE7720005000018150000FFFF000000010000007F7F*5C
", _sb.ToString());

_sb.Clear();
Expand All @@ -70,6 +70,19 @@ public void NewImplementation()
", _sb.ToString());
}

[Fact]
public void DecodeEngineDetail()
{
string data = "$PCDIN,01F201,00002710,02,000000FFFFE7720005000018150000FFFF000000010000007F7F*5C";
var ts = TalkerSentence.FromSentenceString(data, TalkerId.Any, out _);
Assert.NotNull(ts);
DateTimeOffset time = DateTimeOffset.Now;
var decoded = ts.TryGetTypedValue(ref time) as SeaSmartEngineDetail;
Assert.NotNull(decoded);
Assert.Equal(0, decoded.EngineNumber);
Assert.Equal(EngineStatus.CheckEngine, decoded.Status);
}

private void NewImpl(EngineData engineData)
{
EngineRevolutions rv = new EngineRevolutions(TalkerId.ElectronicChartDisplayAndInformationSystem, RotationSource.Engine, engineData.Revolutions, engineData.EngineNo + 1, engineData.Pitch);
Expand Down

0 comments on commit d4fea8a

Please sign in to comment.