support better wbo format (#4000)

* new format

* comment

* pr feedback

* heater duty

* extra

* comment

* test multi-accept logic

* s

* test new format

* this comment made no sense

Co-authored-by: Matthew Kennedy <makenne@microsoft.com>
This commit is contained in:
Matthew Kennedy 2022-05-06 23:19:51 -07:00 committed by GitHub
parent f2018dc5e3
commit e13dad3a2a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 190 additions and 8 deletions

View File

@ -3,21 +3,57 @@
#if EFI_CAN_SUPPORT || EFI_UNIT_TEST
#include "AemXSeriesLambda.h"
static constexpr uint32_t aem_base = 0x180;
static constexpr uint32_t rusefi_base = 0x190;
AemXSeriesWideband::AemXSeriesWideband(uint8_t sensorIndex, SensorType type)
: CanSensorBase(
0x180 + sensorIndex, // 0th sensor is 0x180, others sequential above that
0, // ID passed here doesn't matter since we override acceptFrame
type,
MS2NT(21) // sensor transmits at 100hz, allow a frame to be missed
)
, m_sensorIndex(sensorIndex)
{}
void AemXSeriesWideband::decodeFrame(const CANRxFrame& frame, efitick_t nowNt) {
bool AemXSeriesWideband::acceptFrame(const CANRxFrame& frame) const {
if (frame.DLC != 8) {
invalidate();
return;
return false;
}
uint32_t id = CAN_ID(frame);
// 0th sensor is 0x180, 1st sensor is 0x181, etc
uint32_t aemXSeriesId = aem_base + m_sensorIndex;
// 0th sensor is 0x190 and 0x191, 1st sensor is 0x192 and 0x193
uint32_t rusefiBaseId = rusefi_base + 2 * m_sensorIndex;
return
id == aemXSeriesId ||
id == rusefiBaseId ||
id == rusefiBaseId + 1;
}
void AemXSeriesWideband::decodeFrame(const CANRxFrame& frame, efitick_t nowNt) {
int32_t id = CAN_ID(frame);
// accept frame has already guaranteed that this message belongs to us
// We just have to check if it's AEM or rusEFI
if (id < rusefi_base) {
decodeAemXSeries(frame, nowNt);
} else {
// rusEFI custom format
if ((id & 0x1) != 0) {
// low bit is set, this is the "diag" frame
decodeRusefiDiag(frame);
} else {
// low bit not set, this is standard frame
decodeRusefiStandard(frame, nowNt);
}
}
}
void AemXSeriesWideband::decodeAemXSeries(const CANRxFrame& frame, efitick_t nowNt) {
// reports in 0.0001 lambda per LSB
uint16_t lambdaInt = SWAP_UINT16(frame.data16[0]);
float lambdaFloat = 0.0001f * lambdaInt;
@ -63,4 +99,59 @@ void AemXSeriesWideband::decodeFrame(const CANRxFrame& frame, efitick_t nowNt) {
setValidValue(lambdaFloat, nowNt);
}
// TODO: include rusEFI wideband file directly
namespace wbo
{
struct StandardData
{
uint8_t Version;
uint8_t Valid;
uint16_t Lambda;
uint16_t TemperatureC;
uint16_t pad;
};
struct DiagData
{
uint16_t Esr;
uint16_t NernstDc;
uint8_t PumpDuty;
uint8_t Status;
uint8_t HeaterDuty;
uint8_t pad;
};
} // namespace wbo
void AemXSeriesWideband::decodeRusefiStandard(const CANRxFrame& frame, efitick_t nowNt) {
auto data = reinterpret_cast<const wbo::StandardData*>(&frame.data8[0]);
// TODO: enforce version check
//bool versionValid = data->Version != RUSEFI_WIDEBAND_VERSION;
float lambda = 0.0001f * data->Lambda;
engine->outputChannels.wbTemperature[m_sensorIndex] = data->TemperatureC;
bool valid = data->Valid != 0;
if (valid) {
setValidValue(lambda, nowNt);
} else {
invalidate();
}
}
void AemXSeriesWideband::decodeRusefiDiag(const CANRxFrame& frame) {
auto data = reinterpret_cast<const wbo::DiagData*>(&frame.data8[0]);
engine->outputChannels.wbHeaterDuty[m_sensorIndex] = data->HeaterDuty / 255.0f;
if (m_sensorIndex == 0 || engineConfiguration->debugMode == DBG_RUSEFI_WIDEBAND) {
engine->outputChannels.debugFloatField1 = data->PumpDuty / 255.0f;
engine->outputChannels.debugFloatField3 = data->NernstDc / 1000.0f;
}
}
#endif

View File

@ -6,9 +6,19 @@ class AemXSeriesWideband final : public CanSensorBase {
public:
AemXSeriesWideband(uint8_t sensorIndex, SensorType type);
bool acceptFrame(const CANRxFrame& frame) const override;
protected:
// Dispatches to one of the three decoders below
void decodeFrame(const CANRxFrame& frame, efitick_t nowNt) override;
// Decode an actual AEM controller, or a rusEFI controller sending AEM format
void decodeAemXSeries(const CANRxFrame& frame, efitick_t nowNt);
// Decode rusEFI custom format
void decodeRusefiStandard(const CANRxFrame& frame, efitick_t nowNt);
void decodeRusefiDiag(const CANRxFrame& frame);
private:
const uint8_t m_sensorIndex;
};

View File

@ -2,12 +2,49 @@
#include "AemXSeriesLambda.h"
using ::testing::StrictMock;
using ::testing::_;
TEST(CanWideband, AcceptFrameId0) {
AemXSeriesWideband dut(0, SensorType::Lambda1);
TEST(CanWideband, DecodeValid) {
Sensor::resetRegistry();
CANRxFrame frame;
frame.IDE = false;
frame.DLC = 8;
// Check that the AEM format frame is accepted
frame.SID = 0x180;
EXPECT_TRUE(dut.acceptFrame(frame));
// Check that the rusEFI standard data is accepted
frame.SID = 0x190;
EXPECT_TRUE(dut.acceptFrame(frame));
// Check that the rusEFI extended data is accepted
frame.SID = 0x191;
EXPECT_TRUE(dut.acceptFrame(frame));
}
TEST(CanWideband, AcceptFrameId1) {
AemXSeriesWideband dut(1, SensorType::Lambda2);
CANRxFrame frame;
frame.IDE = false;
frame.DLC = 8;
// Check that the AEM format frame is accepted
frame.SID = 0x181;
EXPECT_TRUE(dut.acceptFrame(frame));
// Check that the rusEFI standard data is accepted
frame.SID = 0x192;
EXPECT_TRUE(dut.acceptFrame(frame));
// Check that the rusEFI extended data is accepted
frame.SID = 0x193;
EXPECT_TRUE(dut.acceptFrame(frame));
}
TEST(CanWideband, DecodeValidAemFormat) {
AemXSeriesWideband dut(0, SensorType::Lambda1);
dut.Register();
@ -54,4 +91,48 @@ TEST(CanWideband, DecodeValid) {
dut.processFrame(frame, getTimeNowNt());
EXPECT_FLOAT_EQ(-1, Sensor::get(SensorType::Lambda1).value_or(-1));
Sensor::resetRegistry();
}
TEST(CanWideband, DecodeRusefiStandard)
{
EngineTestHelper eth(TEST_ENGINE);
AemXSeriesWideband dut(0, SensorType::Lambda1);
dut.Register();
CANRxFrame frame;
frame.SID = 0x190;
frame.IDE = false;
frame.DLC = 8;
// version
frame.data8[0] = 0;
// valid
frame.data8[1] = 1;
// data = 0.7 lambda
*reinterpret_cast<uint16_t*>(&frame.data8[2]) = 7000;
// data = 1234 deg C
*reinterpret_cast<uint16_t*>(&frame.data8[4]) = 1234;
engine->outputChannels.wbTemperature[0] = 0;
// check not set
EXPECT_FLOAT_EQ(-1, Sensor::get(SensorType::Lambda1).value_or(-1));
// check that lambda updates
dut.processFrame(frame, getTimeNowNt());
EXPECT_FLOAT_EQ(0.7f, Sensor::get(SensorType::Lambda1).value_or(-1));
// Check that temperature updates
EXPECT_EQ(engine->outputChannels.wbTemperature[0], 1234);
// Check that valid bit is respected (should be invalid now)
frame.data8[1] = 0;
dut.processFrame(frame, getTimeNowNt());
EXPECT_FLOAT_EQ(-1, Sensor::get(SensorType::Lambda1).value_or(-1));
}