From 50f7a653a712f8e6c49261918ce79d91b70a0a53 Mon Sep 17 00:00:00 2001 From: Nathan Schulte <8540239+nmschulte@users.noreply.github.com> Date: Tue, 16 Jul 2024 04:08:43 -0500 Subject: [PATCH] Toyota ETCS-i [02/02]: RedundantSensor: partial second (avoid average, etc.) (#431) --- .../controllers/sensors/redundant_sensor.cpp | 11 +- .../controllers/sensors/redundant_sensor.h | 2 +- firmware/integration/rusefi_config.txt | 8 +- firmware/tunerstudio/tunerstudio.template.ini | 11 +- unit_tests/tests/sensor/redundant.cpp | 112 ++++++++---------- 5 files changed, 68 insertions(+), 76 deletions(-) diff --git a/firmware/controllers/sensors/redundant_sensor.cpp b/firmware/controllers/sensors/redundant_sensor.cpp index e9ff540d06..ac659d3a49 100644 --- a/firmware/controllers/sensors/redundant_sensor.cpp +++ b/firmware/controllers/sensors/redundant_sensor.cpp @@ -48,27 +48,28 @@ SensorResult RedundantSensor::get() const { } else { // Sensor is partially redundant; useful for some sensors: e.g. Ford and Toyota ETCS-i - // The threshold at which to switch to partial redundancy, just below maximum to avoid misbehavior near 100% + // The partial redundancy threshold, slightly less than 100% to avoid issues near full-range float threshold = m_secondMaximum * 0.95f; - // The scaled second sensor, proportioning it to the first sensor + // Scale the second sensor value accordingly, proportioning to the first sensor float scaledSecond = sensor2.Value * m_secondMaximum / 100; // Check second sensor is below partial redundancy switch-over threshold if (scaledSecond <= threshold) { float delta = absF(sensor1.Value - scaledSecond); if (delta <= m_maxDifference) { - // All is well: sensors are valid and values check out, return the average value - return (sensor1.Value + scaledSecond) / 2; + // All is well: sensors are valid and values check out, return the primary value + return sensor1.Value; } } else { // Check first sensor is at or above partial redundancy switch-over threshold if (sensor1.Value >= m_secondMaximum - m_maxDifference) { + // All is well: sensors are valid and values check out, return the primary value return sensor1.Value; } } } - // Fall-through and any other condition indicates an unexpected discrepancy, return inconsistency error + // Any other condition indicates an unexpected discrepancy, return inconsistency error return UnexpectedCode::Inconsistent; } diff --git a/firmware/controllers/sensors/redundant_sensor.h b/firmware/controllers/sensors/redundant_sensor.h index 0842ff8610..a0b27b2bce 100644 --- a/firmware/controllers/sensors/redundant_sensor.h +++ b/firmware/controllers/sensors/redundant_sensor.h @@ -10,7 +10,7 @@ public: SensorType secondSensor ); - void configure(float maxDifference, bool ignoreSecondSensor, float secondaryMaximum); + void configure(float maxDifference, bool ignoreSecondSensor, float secondaryMaximum = 100); SensorResult get() const override; diff --git a/firmware/integration/rusefi_config.txt b/firmware/integration/rusefi_config.txt index 5b29da04cc..aba3a2c2ca 100644 --- a/firmware/integration/rusefi_config.txt +++ b/firmware/integration/rusefi_config.txt @@ -401,7 +401,7 @@ injector_s injector injector_s injectorSecondary bit isForcedInduction;Does the vehicle have a turbo or supercharger? -bit useFordRedundantTps;On some Ford and Toyota vehicles one of the throttle sensors is not linear on the full range, i.e. in the specific range of the positions we effectively have only one sensor. +bit unusedFordTps bit lambdaProtectionEnable bit overrideTriggerGaps bit enableFan1WithAc;Turn on this fan when AC is on. @@ -423,7 +423,7 @@ bit useTLE8888_stepper bit usescriptTableForCanSniffingFiltering bit verboseCan,"Print all","Do not print";Print incoming and outgoing first bus CAN messages in FOME console bit artificialTestMisfire,"Danger Mode","No thank you";Experimental setting that will cause a misfire\nDO NOT ENABLE. -bit useFordRedundantPps;On some Ford and Toyota vehicles one of the pedal sensors is not linear on the full range, i.e. in the specific range of the positions we effectively have only one sensor. +bit unusedFordPps bit cltSensorPulldown bit iatSensorPulldown bit allowIdenticalPps @@ -1413,8 +1413,8 @@ uint8_t alsEtbPosition;;"", 1, 0, 0, 20000, 0 uint8_t ALSMaxDriverThrottleIntent;;"%", 1, 0, 0, 10, 0 pin_input_mode_e ALSActivatePinMode; - uint8_t autoscale tpsSecondaryMaximum;For Ford TPS, use 53%. For Toyota ETCS-i, use ~65%;"%", 0.5, 0, 0, 100, 1 - uint8_t autoscale ppsSecondaryMaximum;For Toyota ETCS-i, use ~69%;"%", 0.5, 0, 0, 100, 1 + uint8_t autoscale tpsSecondaryMaximum;For Ford TPS, use 53%. For Toyota ETCS-i, use ~65%. 0 and 100 disable, <20 invalid, rest will avoid sensor averaging.;"%", 0.5, 0, 0, 100, 1 + uint8_t autoscale ppsSecondaryMaximum;For Toyota ETCS-i, use ~69%. 0 and 100 disable, <20 invalid, rest will avoid sensor averaging.;"%", 0.5, 0, 0, 100, 1 pin_input_mode_e[LUA_DIGITAL_INPUT_COUNT iterate] luaDigitalInputPinModes; uint8_t autoscale rpmHardLimitHyst;If the hard limit is 7200rpm and hysteresis is 200rpm, then when the ECU sees 7200rpm, fuel/ign will cut, and stay cut until 7000rpm (7200-200) is reached;"RPM", 10, 0, 0, 2500, 0 diff --git a/firmware/tunerstudio/tunerstudio.template.ini b/firmware/tunerstudio/tunerstudio.template.ini index dd9a52e65e..f901e7f0e9 100644 --- a/firmware/tunerstudio/tunerstudio.template.ini +++ b/firmware/tunerstudio/tunerstudio.template.ini @@ -4461,21 +4461,22 @@ dialog = tcuControls, "Transmission Settings" field = "Disable after revolutions", acrRevolutions field = "Disable after engine phase", acrDisablePhase + dialog = parkingLotRedundantSensors, "Sensor Redundancy" + field = "Partial Secondary TPS Maximum", tpsSecondaryMaximum + field = "Partial Secondary PPS Maximum", ppsSecondaryMaximum + field = "BRZ/FRS/GT86 pedal", allowIdenticalPps + dialog = parkingLot, "Experimental/Broken" field = "I understand ECU Locking", yesUnderstandLocking field = "Tune read/write password", tuneHidingKey, { yesUnderstandLocking == 1 } field = "#System hacks" field = "Global fuel correction", globalFuelCorrection field = "MAP Averaging Logic @", mapAveragingSchedulingAtIndex - field = "Ford redundant TPS mode", useFordRedundantTps - field = "Secondary TPS maximum", tpsSecondaryMaximum, {useFordRedundantTps} - field = "Ford redundant PPS mode", useFordRedundantPps - field = "Secondary PPS maximum", ppsSecondaryMaximum, {useFordRedundantPps} field = "ADC vRef voltage", adcVcc field = "CLT sensor is pulldown instead of pullup", cltSensorPulldown field = "IAT sensor is pulldown instead of pullup", iatSensorPulldown field = "Analog divider ratio", analogInputDividerCoefficient@@if_ts_show_analog_divider - field = "BRZ/FRS/GT86 pedal", allowIdenticalPps + panel = parkingLotRedundantSensors field = "Artificial Misfire", artificialTestMisfire field = "Instant Rpm Range", instantRpmRange field = "Always use instant RPM", alwaysInstantRpm diff --git a/unit_tests/tests/sensor/redundant.cpp b/unit_tests/tests/sensor/redundant.cpp index e6c83f04e0..dd7c80f07d 100644 --- a/unit_tests/tests/sensor/redundant.cpp +++ b/unit_tests/tests/sensor/redundant.cpp @@ -24,7 +24,7 @@ protected: ASSERT_TRUE(m1.Register()); ASSERT_TRUE(m2.Register()); - dut.configure(5.0f, false, 100); + dut.configure(5.0f, false); } void TearDown() override @@ -58,23 +58,11 @@ TEST_F(SensorRedundant, SetOnlyOneSensor) auto result = dut.get(); EXPECT_FALSE(result.Valid); } -} -TEST_F(SensorRedundant, SetTwoSensors) -{ - // Don't set any sensors - expect invalid - { - auto result = dut.get(); - EXPECT_FALSE(result.Valid); - EXPECT_EQ(result.Code, UnexpectedCode::Inconsistent); - } - - // Set one sensor - m1.set(24.0f); // Set the other sensor m2.set(26.0f); - // Should now be valid - and the average of the two input + // Should now be valid - and the average of both sensors { auto result = dut.get(); EXPECT_TRUE(result.Valid); @@ -82,6 +70,18 @@ TEST_F(SensorRedundant, SetTwoSensors) } } +TEST_F(SensorRedundant, CheckOnlySecondInvalid) +{ + // Set second sensor only + m2.set(66.0f); + + // Should be invalid - only one is set! + { + auto result = dut.get(); + EXPECT_FALSE(result.Valid); + } +} + TEST_F(SensorRedundant, DifferenceNone) { // Set both sensors to the same value @@ -136,7 +136,6 @@ TEST_F(SensorRedundant, DifferenceOverLimitSwapped) } } - class SensorRedundantIgnoreSecond : public ::testing::Test { protected: @@ -159,7 +158,7 @@ protected: ASSERT_TRUE(m1.Register()); ASSERT_TRUE(m2.Register()); - dut.configure(5.0f, true, 100); + dut.configure(5.0f, true); } void TearDown() override @@ -175,6 +174,7 @@ TEST_F(SensorRedundantIgnoreSecond, CheckIsRedundant) EXPECT_FALSE(dut.isRedundant()); } } + TEST_F(SensorRedundantIgnoreSecond, OnlyFirst) { // Don't set any sensors - expect invalid @@ -184,7 +184,7 @@ TEST_F(SensorRedundantIgnoreSecond, OnlyFirst) EXPECT_EQ(result.Code, UnexpectedCode::Unknown); } - // Set one sensor + // Set first sensor m1.set(44.0f); // Should be valid - we don't care about second sensor @@ -193,17 +193,20 @@ TEST_F(SensorRedundantIgnoreSecond, OnlyFirst) EXPECT_TRUE(result.Valid); EXPECT_FLOAT_EQ(result.Value, 44.0f); } -} -TEST_F(SensorRedundantIgnoreSecond, OnlySecond) -{ - // Don't set any sensors - expect invalid + // Set the second sensor too + m2.set(46.0f); + + // Should be valid, but only get the value from m1 { auto result = dut.get(); - EXPECT_FALSE(result.Valid); - EXPECT_EQ(result.Code, UnexpectedCode::Unknown); + EXPECT_TRUE(result.Valid); + EXPECT_FLOAT_EQ(result.Value, 44.0f); } +} +TEST_F(SensorRedundantIgnoreSecond, CheckOnlySecondInvalid) +{ // Set second sensor only m2.set(66.0f); @@ -214,27 +217,6 @@ TEST_F(SensorRedundantIgnoreSecond, OnlySecond) } } -TEST_F(SensorRedundantIgnoreSecond, SetBothIgnoreSecond) -{ - // Don't set any sensors - expect invalid - { - auto result = dut.get(); - EXPECT_FALSE(result.Valid); - EXPECT_EQ(result.Code, UnexpectedCode::Unknown); - } - - // Set both sensors - m1.set(74.0f); - m2.set(76.0f); - - // Should be valid, but only get the value from m1 - { - auto result = dut.get(); - EXPECT_TRUE(result.Valid); - EXPECT_FLOAT_EQ(result.Value, 74.0f); - } -} - class SensorRedundantPartialSecond : public ::testing::Test { protected: @@ -273,7 +255,7 @@ TEST_F(SensorRedundantPartialSecond, CheckIsRedundant) } } -TEST_F(SensorRedundantPartialSecond, SetOnlyOneSensor) +TEST_F(SensorRedundantPartialSecond, SetNone) { // Don't set any sensors - expect invalid { @@ -281,7 +263,10 @@ TEST_F(SensorRedundantPartialSecond, SetOnlyOneSensor) EXPECT_FALSE(result.Valid); EXPECT_EQ(result.Code, UnexpectedCode::Inconsistent); } +} +TEST_F(SensorRedundantPartialSecond, SetOnlyOneSensor) +{ // Set first sensor m1.set(24.0f); @@ -294,23 +279,28 @@ TEST_F(SensorRedundantPartialSecond, SetOnlyOneSensor) TEST_F(SensorRedundantPartialSecond, SetTwoSensors) { - // Don't set any sensors - expect invalid - { - auto result = dut.get(); - EXPECT_FALSE(result.Valid); - EXPECT_EQ(result.Code, UnexpectedCode::Inconsistent); - } - // Set first sensor - m1.set(12.0f); - // Set second sensor at double the first - m2.set(28.0f); + m1.set(0.0f); + // Set second sensor + m2.set(0.0f); - // Should now be valid - and the average of the two input + // Should now be valid, and output the primary { auto result = dut.get(); EXPECT_TRUE(result.Valid); - EXPECT_FLOAT_EQ(result.Value, 13.0f); + EXPECT_FLOAT_EQ(result.Value, 0.0f); + } +} + +TEST_F(SensorRedundantPartialSecond, CheckOnlySecondInvalid) +{ + // Set second sensor only + m2.set(66.0f); + + // Should be invalid - only one is set! + { + auto result = dut.get(); + EXPECT_FALSE(result.Valid); } } @@ -320,7 +310,7 @@ TEST_F(SensorRedundantPartialSecond, DifferenceNone) m1.set(10); m2.set(20); - // Expect valid, and 10 output + // Expect valid, and output the primary { auto result = dut.get(); EXPECT_TRUE(result.Valid); @@ -334,11 +324,11 @@ TEST_F(SensorRedundantPartialSecond, DifferenceNearLimit) m1.set(7.501f); m2.set(2 * 12.499f); - // Expect valid, and 10 output + // Expect valid, and output the primary { auto result = dut.get(); EXPECT_TRUE(result.Valid); - EXPECT_FLOAT_EQ(result.Value, 10.0f); + EXPECT_FLOAT_EQ(result.Value, 7.501f); } } @@ -374,7 +364,7 @@ TEST_F(SensorRedundantPartialSecond, PartialRedundancyRange) m1.set(75); m2.set(100); - // expect valid, at 75% + // Expect valid, and output the first { auto result = dut.get(); EXPECT_TRUE(result.Valid);