Ford redundant ETB tps (#2519)

* ford tps

* configurable maximum

* initialization

* check consistency when in the low range

* print info

* default

* config field

* config parameter

* test

Co-authored-by: Matthew Kennedy <makenne@microsoft.com>
This commit is contained in:
Matthew Kennedy 2021-04-05 12:57:03 -07:00 committed by GitHub
parent f4ce428e7a
commit 46e38447e5
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 259 additions and 8 deletions

View File

@ -0,0 +1,53 @@
#include "redundant_ford_tps.h"
#include "efilib.h"
RedundantFordTps::RedundantFordTps(SensorType outputType, SensorType first, SensorType second)
: Sensor(outputType)
, m_first(first)
, m_second(second)
{
}
void RedundantFordTps::configure(float maxDifference, float secondaryMaximum) {
m_maxDifference = maxDifference;
m_secondaryMaximum = secondaryMaximum;
}
SensorResult RedundantFordTps::get() const {
// Sensor 1 is "plain linear" and has the full range
auto tps1 = Sensor::get(m_first);
// Sensor 2 is compressed in to 0 -> 50%
auto tps2 = Sensor::get(m_second);
// If either result is invalid, return invalid.
if (!tps1 || !tps2) {
return unexpected;
}
// The "actual" position resolved by the second throttle - this tops out at m_secondaryMaximum instead of 100%
float tps2Actual = tps2.Value * m_secondaryMaximum / 100;
// Switch modes slightly below the maximum of the secondary, to avoid misbehavior near 100%
float avgThreshold = m_secondaryMaximum * 0.9f;
// Case 1: both sensors show low position, average result
if (tps1.Value < avgThreshold && tps2Actual < avgThreshold) {
// Check that the resolved positions are close
float delta = absF(tps1.Value - tps2Actual);
if (delta > m_maxDifference) {
return unexpected;
}
return (tps1.Value + tps2Actual) / 2;
}
// Case 2: both sensors show high position, return "full scale" sensor's position
if (tps1.Value > avgThreshold && tps2Actual > (avgThreshold - 3)) {
return tps1;
}
// Any other condition indicates an mismatch, and therefore an error
return unexpected;
}

View File

@ -0,0 +1,34 @@
#pragma once
#include "sensor.h"
class RedundantFordTps final : public Sensor
{
public:
RedundantFordTps(
SensorType outputType,
SensorType firstSensor,
SensorType secondSensor
);
void configure(float maxDifference, float secondaryMaximum);
SensorResult get() const override;
bool isRedundant() const override {
return true;
}
void showInfo(Logging* logger, const char* sensorName) const override;
private:
// The two sensors we interpret to form one redundant sensor
const SensorType m_first;
const SensorType m_second;
// How far apart do we allow the sensors to be before reporting an issue?
float m_maxDifference = 0;
// At what primary % does the secondary hit 100%?
float m_secondaryMaximum = 0;
};

View File

@ -2,6 +2,7 @@
#include "proxy_sensor.h"
#include "functional_sensor.h"
#include "redundant_sensor.h"
#include "redundant_ford_tps.h"
#include "rpm_calculator.h"
#include "Lps25Sensor.h"
#include "linear_func.h"
@ -37,6 +38,10 @@ void RedundantSensor::showInfo(Logging* logger, const char* sensorName) const {
scheduleMsg(logger, "Sensor \"%s\" is redundant combining \"%s\" and \"%s\"", sensorName, getSensorName(m_first), getSensorName(m_second));
}
void RedundantFordTps::showInfo(Logging* logger, const char* sensorName) const {
scheduleMsg(logger, "Sensor \"%s\" is Ford-type redundant TPS combining \"%s\" and \"%s\"", sensorName, getSensorName(m_first), getSensorName(m_second));
}
void RpmCalculator::showInfo(Logging* logger, const char* /*sensorName*/) const {
scheduleMsg(logger, "RPM sensor: stopped: %d spinning up: %d cranking: %d running: %d rpm: %f",
isStopped(),

View File

@ -11,6 +11,7 @@ CONTROLLERS_SENSORS_SRC_CPP = $(PROJECT_DIR)/controllers/sensors/thermistors.cp
$(PROJECT_DIR)/controllers/sensors/sensor_info_printing.cpp \
$(PROJECT_DIR)/controllers/sensors/functional_sensor.cpp \
$(PROJECT_DIR)/controllers/sensors/redundant_sensor.cpp \
$(PROJECT_DIR)/controllers/sensors/redundant_ford_tps.cpp \
$(PROJECT_DIR)/controllers/sensors/AemXSeriesLambda.cpp \
$(PROJECT_DIR)/cotnrollers/sensors/flex_sensor.cpp \
$(PROJECT_DIR)/controllers/sensors/software_knock.cpp \

View File

@ -5,6 +5,7 @@
#include "error_handling.h"
#include "functional_sensor.h"
#include "redundant_sensor.h"
#include "redundant_ford_tps.h"
#include "proxy_sensor.h"
#include "linear_func.h"
#include "tps.h"
@ -21,9 +22,14 @@ FunctionalSensor tpsSens1s(SensorType::Tps1Secondary, MS2NT(10));
FunctionalSensor tpsSens2p(SensorType::Tps2Primary, MS2NT(10));
FunctionalSensor tpsSens2s(SensorType::Tps2Secondary, MS2NT(10));
// Used in case of "normal", non-Ford ETB TPS
RedundantSensor tps1(SensorType::Tps1, SensorType::Tps1Primary, SensorType::Tps1Secondary);
RedundantSensor tps2(SensorType::Tps2, SensorType::Tps2Primary, SensorType::Tps2Secondary);
// Used only in case of weird Ford-style ETB TPS
RedundantFordTps fordTps1(SensorType::Tps1, SensorType::Tps1Primary, SensorType::Tps1Secondary);
RedundantFordTps fordTps2(SensorType::Tps2, SensorType::Tps2Primary, SensorType::Tps2Secondary);
LinearFunc pedalFuncPrimary;
LinearFunc pedalFuncSecondary;
FunctionalSensor pedalSensorPrimary(SensorType::AcceleratorPedalPrimary, MS2NT(10));
@ -81,12 +87,16 @@ static bool initTpsFunc(LinearFunc& func, FunctionalSensor& sensor, adc_channel_
return sensor.Register();
}
static void initTpsFuncAndRedund(RedundantSensor& redund, LinearFunc& func, FunctionalSensor& sensor, adc_channel_e channel, float closed, float open, float min, float max) {
static void initTpsFuncAndRedund(RedundantSensor& redund, RedundantFordTps* fordTps, bool isFordTps, LinearFunc& func, FunctionalSensor& sensor, adc_channel_e channel, float closed, float open, float min, float max) {
bool hasSecond = initTpsFunc(func, sensor, channel, closed, open, min, max);
redund.configure(5.0f, !hasSecond);
redund.Register();
if (isFordTps && fordTps) {
fordTps->configure(5.0f, 52.6f);
fordTps->Register();
} else {
redund.configure(5.0f, !hasSecond);
redund.Register();
}
}
void initTps(DECLARE_CONFIG_PARAMETER_SIGNATURE) {
@ -94,12 +104,18 @@ void initTps(DECLARE_CONFIG_PARAMETER_SIGNATURE) {
percent_t max = CONFIG(tpsErrorDetectionTooHigh);
if (!CONFIG(consumeObdSensors)) {
// primary TPS sensors
initTpsFunc(tpsFunc1p, tpsSens1p, CONFIG(tps1_1AdcChannel), CONFIG(tpsMin), CONFIG(tpsMax), min, max);
initTpsFuncAndRedund(tps1, tpsFunc1s, tpsSens1s, CONFIG(tps1_2AdcChannel), CONFIG(tps1SecondaryMin), CONFIG(tps1SecondaryMax), min, max);
initTpsFunc(tpsFunc2p, tpsSens2p, CONFIG(tps2_1AdcChannel), CONFIG(tps2Min), CONFIG(tps2Max), min, max);
initTpsFuncAndRedund(tps2, tpsFunc2s, tpsSens2s, CONFIG(tps2_2AdcChannel), CONFIG(tps2SecondaryMin), CONFIG(tps2SecondaryMax), min, max);
// Secondary TPS sensors (and redundant combining)
bool isFordTps = CONFIG(useFordRedundantTps);
initTpsFuncAndRedund(tps1, &fordTps1, isFordTps, tpsFunc1s, tpsSens1s, CONFIG(tps1_2AdcChannel), CONFIG(tps1SecondaryMin), CONFIG(tps1SecondaryMax), min, max);
initTpsFuncAndRedund(tps2, &fordTps2, isFordTps, tpsFunc2s, tpsSens2s, CONFIG(tps2_2AdcChannel), CONFIG(tps2SecondaryMin), CONFIG(tps2SecondaryMax), min, max);
// Pedal sensors
initTpsFunc(pedalFuncPrimary, pedalSensorPrimary, CONFIG(throttlePedalPositionAdcChannel), CONFIG(throttlePedalUpVoltage), CONFIG(throttlePedalWOTVoltage), min, max);
initTpsFuncAndRedund(pedal, pedalFuncSecondary, pedalSensorSecondary, CONFIG(throttlePedalPositionSecondAdcChannel), CONFIG(throttlePedalSecondaryUpVoltage), CONFIG(throttlePedalSecondaryWOTVoltage), min, max);
initTpsFuncAndRedund(pedal, nullptr, false, pedalFuncSecondary, pedalSensorSecondary, CONFIG(throttlePedalPositionSecondAdcChannel), CONFIG(throttlePedalSecondaryUpVoltage), CONFIG(throttlePedalSecondaryWOTVoltage), min, max);
// TPS-like stuff that isn't actually a TPS
initTpsFunc(wastegateFunc, wastegateSens, CONFIG(wastegatePositionSensor), CONFIG(wastegatePositionMin), CONFIG(wastegatePositionMax), min, max);

View File

@ -506,7 +506,7 @@ end_struct
injector_s injector
bit isForcedInduction;
bit unused_294_1;
bit useFordRedundantTps;
bit isVerboseAuxPid1;
bit unused_294_3;
bit unused_294_4;

View File

@ -3491,6 +3491,7 @@ cmd_set_engine_type_default = "@@TS_IO_TEST_COMMAND_char@@\x00\x31\x00\x00"
field = "Ignition Math Logic @", ignMathCalculateAtIndex
field = "MAP Averaging Logic @", mapAveragingSchedulingAtIndex
field = "showHumanReadableWarning (affects Burn)", showHumanReadableWarning
field = "Ford redundant TPS mode", useFordRedundantTps
help = helpGeneral, "rusEFI General Help"

View File

@ -1,4 +1,5 @@
#include "redundant_sensor.h"
#include "redundant_ford_tps.h"
#include <gtest/gtest.h>
@ -220,3 +221,143 @@ TEST_F(SensorRedundantIgnoreSecond, SetBothIgnoreSecond)
EXPECT_FLOAT_EQ(result.Value, 74.0f);
}
}
class SensorFordRedundantTps : public ::testing::Test
{
protected:
RedundantFordTps dut;
MockSensor m1, m2;
SensorFordRedundantTps()
: dut(SensorType::Tps1, SensorType::Tps1Primary, SensorType::Tps1Secondary)
, m1(SensorType::Tps1Primary)
, m2(SensorType::Tps1Secondary)
{
}
void SetUp() override
{
Sensor::resetRegistry();
// Other tests verify registry function - don't re-test it here
ASSERT_TRUE(dut.Register());
ASSERT_TRUE(m1.Register());
ASSERT_TRUE(m2.Register());
dut.configure(5.0f, 50);
}
void TearDown() override
{
Sensor::resetRegistry();
}
};
TEST_F(SensorFordRedundantTps, SetOnlyOneSensor)
{
// Don't set any sensors - expect invalid
{
auto result = dut.get();
EXPECT_FALSE(result.Valid);
}
// Set one sensor
m1.set(24.0f);
// Should still be invalid - only one is set!
{
auto result = dut.get();
EXPECT_FALSE(result.Valid);
}
}
TEST_F(SensorFordRedundantTps, SetTwoSensors)
{
// Don't set any sensors - expect invalid
{
auto result = dut.get();
EXPECT_FALSE(result.Valid);
}
// Set one sensor
m1.set(12.0f);
// Set the other sensor at double the first
m2.set(28.0f);
// Should now be valid - and the average of the two input
{
auto result = dut.get();
EXPECT_TRUE(result.Valid);
EXPECT_FLOAT_EQ(result.Value, 13.0f);
EXPECT_TRUE(dut.isRedundant());
}
}
TEST_F(SensorFordRedundantTps, DifferenceNone)
{
// Set both sensors to the same value
m1.set(10);
m2.set(20);
// Expect valid, and 10 output
{
auto result = dut.get();
EXPECT_TRUE(result.Valid);
EXPECT_FLOAT_EQ(result.Value, 10.0f);
}
}
TEST_F(SensorFordRedundantTps, DifferenceNearLimit)
{
// Set both sensors to nearly the limit (4.998 apart)
m1.set(7.501f);
m2.set(2 * 12.499f);
// Expect valid, and 10 output
{
auto result = dut.get();
EXPECT_TRUE(result.Valid);
EXPECT_FLOAT_EQ(result.Value, 10.0f);
}
}
TEST_F(SensorFordRedundantTps, DifferenceOverLimit)
{
// Set both sensors barely over the limit (5.002 apart)
m1.set(7.499f);
m2.set(2 * 12.501f);
// Expect invalid
{
auto result = dut.get();
EXPECT_FALSE(result.Valid);
}
}
TEST_F(SensorFordRedundantTps, DifferenceOverLimitSwapped)
{
// Now try it the other way (m1 > m2)
m1.set(12.501f);
m2.set(2 * 7.499f);
// Expect invalid
{
auto result = dut.get();
EXPECT_FALSE(result.Valid);
}
}
TEST_F(SensorFordRedundantTps, HighRange)
{
// Set the throttle like it's at 75%
m1.set(75);
m2.set(100);
// expect valid, at 75%
{
auto result = dut.get();
EXPECT_TRUE(result.Valid);
EXPECT_FLOAT_EQ(result.Value, 75.0f);
}
}