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:
parent
f4ce428e7a
commit
46e38447e5
|
@ -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;
|
||||||
|
}
|
|
@ -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;
|
||||||
|
};
|
|
@ -2,6 +2,7 @@
|
||||||
#include "proxy_sensor.h"
|
#include "proxy_sensor.h"
|
||||||
#include "functional_sensor.h"
|
#include "functional_sensor.h"
|
||||||
#include "redundant_sensor.h"
|
#include "redundant_sensor.h"
|
||||||
|
#include "redundant_ford_tps.h"
|
||||||
#include "rpm_calculator.h"
|
#include "rpm_calculator.h"
|
||||||
#include "Lps25Sensor.h"
|
#include "Lps25Sensor.h"
|
||||||
#include "linear_func.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));
|
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 {
|
void RpmCalculator::showInfo(Logging* logger, const char* /*sensorName*/) const {
|
||||||
scheduleMsg(logger, "RPM sensor: stopped: %d spinning up: %d cranking: %d running: %d rpm: %f",
|
scheduleMsg(logger, "RPM sensor: stopped: %d spinning up: %d cranking: %d running: %d rpm: %f",
|
||||||
isStopped(),
|
isStopped(),
|
||||||
|
|
|
@ -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/sensor_info_printing.cpp \
|
||||||
$(PROJECT_DIR)/controllers/sensors/functional_sensor.cpp \
|
$(PROJECT_DIR)/controllers/sensors/functional_sensor.cpp \
|
||||||
$(PROJECT_DIR)/controllers/sensors/redundant_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)/controllers/sensors/AemXSeriesLambda.cpp \
|
||||||
$(PROJECT_DIR)/cotnrollers/sensors/flex_sensor.cpp \
|
$(PROJECT_DIR)/cotnrollers/sensors/flex_sensor.cpp \
|
||||||
$(PROJECT_DIR)/controllers/sensors/software_knock.cpp \
|
$(PROJECT_DIR)/controllers/sensors/software_knock.cpp \
|
||||||
|
|
|
@ -5,6 +5,7 @@
|
||||||
#include "error_handling.h"
|
#include "error_handling.h"
|
||||||
#include "functional_sensor.h"
|
#include "functional_sensor.h"
|
||||||
#include "redundant_sensor.h"
|
#include "redundant_sensor.h"
|
||||||
|
#include "redundant_ford_tps.h"
|
||||||
#include "proxy_sensor.h"
|
#include "proxy_sensor.h"
|
||||||
#include "linear_func.h"
|
#include "linear_func.h"
|
||||||
#include "tps.h"
|
#include "tps.h"
|
||||||
|
@ -21,9 +22,14 @@ FunctionalSensor tpsSens1s(SensorType::Tps1Secondary, MS2NT(10));
|
||||||
FunctionalSensor tpsSens2p(SensorType::Tps2Primary, MS2NT(10));
|
FunctionalSensor tpsSens2p(SensorType::Tps2Primary, MS2NT(10));
|
||||||
FunctionalSensor tpsSens2s(SensorType::Tps2Secondary, 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 tps1(SensorType::Tps1, SensorType::Tps1Primary, SensorType::Tps1Secondary);
|
||||||
RedundantSensor tps2(SensorType::Tps2, SensorType::Tps2Primary, SensorType::Tps2Secondary);
|
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 pedalFuncPrimary;
|
||||||
LinearFunc pedalFuncSecondary;
|
LinearFunc pedalFuncSecondary;
|
||||||
FunctionalSensor pedalSensorPrimary(SensorType::AcceleratorPedalPrimary, MS2NT(10));
|
FunctionalSensor pedalSensorPrimary(SensorType::AcceleratorPedalPrimary, MS2NT(10));
|
||||||
|
@ -81,25 +87,35 @@ static bool initTpsFunc(LinearFunc& func, FunctionalSensor& sensor, adc_channel_
|
||||||
return sensor.Register();
|
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);
|
bool hasSecond = initTpsFunc(func, sensor, channel, closed, open, min, max);
|
||||||
|
|
||||||
|
if (isFordTps && fordTps) {
|
||||||
|
fordTps->configure(5.0f, 52.6f);
|
||||||
|
fordTps->Register();
|
||||||
|
} else {
|
||||||
redund.configure(5.0f, !hasSecond);
|
redund.configure(5.0f, !hasSecond);
|
||||||
|
|
||||||
redund.Register();
|
redund.Register();
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void initTps(DECLARE_CONFIG_PARAMETER_SIGNATURE) {
|
void initTps(DECLARE_CONFIG_PARAMETER_SIGNATURE) {
|
||||||
percent_t min = CONFIG(tpsErrorDetectionTooLow);
|
percent_t min = CONFIG(tpsErrorDetectionTooLow);
|
||||||
percent_t max = CONFIG(tpsErrorDetectionTooHigh);
|
percent_t max = CONFIG(tpsErrorDetectionTooHigh);
|
||||||
|
|
||||||
if (!CONFIG(consumeObdSensors)) {
|
if (!CONFIG(consumeObdSensors)) {
|
||||||
|
// primary TPS sensors
|
||||||
initTpsFunc(tpsFunc1p, tpsSens1p, CONFIG(tps1_1AdcChannel), CONFIG(tpsMin), CONFIG(tpsMax), min, max);
|
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);
|
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);
|
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
|
// TPS-like stuff that isn't actually a TPS
|
||||||
initTpsFunc(wastegateFunc, wastegateSens, CONFIG(wastegatePositionSensor), CONFIG(wastegatePositionMin), CONFIG(wastegatePositionMax), min, max);
|
initTpsFunc(wastegateFunc, wastegateSens, CONFIG(wastegatePositionSensor), CONFIG(wastegatePositionMin), CONFIG(wastegatePositionMax), min, max);
|
||||||
|
|
|
@ -506,7 +506,7 @@ end_struct
|
||||||
injector_s injector
|
injector_s injector
|
||||||
|
|
||||||
bit isForcedInduction;
|
bit isForcedInduction;
|
||||||
bit unused_294_1;
|
bit useFordRedundantTps;
|
||||||
bit isVerboseAuxPid1;
|
bit isVerboseAuxPid1;
|
||||||
bit unused_294_3;
|
bit unused_294_3;
|
||||||
bit unused_294_4;
|
bit unused_294_4;
|
||||||
|
|
|
@ -3491,6 +3491,7 @@ cmd_set_engine_type_default = "@@TS_IO_TEST_COMMAND_char@@\x00\x31\x00\x00"
|
||||||
field = "Ignition Math Logic @", ignMathCalculateAtIndex
|
field = "Ignition Math Logic @", ignMathCalculateAtIndex
|
||||||
field = "MAP Averaging Logic @", mapAveragingSchedulingAtIndex
|
field = "MAP Averaging Logic @", mapAveragingSchedulingAtIndex
|
||||||
field = "showHumanReadableWarning (affects Burn)", showHumanReadableWarning
|
field = "showHumanReadableWarning (affects Burn)", showHumanReadableWarning
|
||||||
|
field = "Ford redundant TPS mode", useFordRedundantTps
|
||||||
|
|
||||||
|
|
||||||
help = helpGeneral, "rusEFI General Help"
|
help = helpGeneral, "rusEFI General Help"
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
#include "redundant_sensor.h"
|
#include "redundant_sensor.h"
|
||||||
|
#include "redundant_ford_tps.h"
|
||||||
|
|
||||||
#include <gtest/gtest.h>
|
#include <gtest/gtest.h>
|
||||||
|
|
||||||
|
@ -220,3 +221,143 @@ TEST_F(SensorRedundantIgnoreSecond, SetBothIgnoreSecond)
|
||||||
EXPECT_FLOAT_EQ(result.Value, 74.0f);
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue