implement "ford mode" injector correction (#4686)

* implement "ford mode"

* missed the enum value
This commit is contained in:
Matthew Kennedy 2022-10-20 19:25:39 -07:00 committed by GitHub
parent c51e54ee2b
commit 890a8e0fed
6 changed files with 160 additions and 64 deletions

View File

@ -10,14 +10,46 @@
#include "fuel_computer.h" #include "fuel_computer.h"
void InjectorModelBase::prepare() { void InjectorModelBase::prepare() {
m_massFlowRate = getInjectorMassFlowRate(); float flowRatio = getInjectorFlowRatio();
// "large pulse" flow rate
m_massFlowRate = flowRatio * getBaseFlowRate();
m_deadtime = getDeadtime(); m_deadtime = getDeadtime();
if (getNonlinearMode() == INJ_FordModel) {
m_smallPulseFlowRate = flowRatio * getSmallPulseFlowRate();
m_smallPulseBreakPoint = getSmallPulseBreakPoint();
// amount added to small pulses to correct for the "kink" from low flow region
m_smallPulseOffset = 1000 * ((m_smallPulseBreakPoint / m_massFlowRate) - (m_smallPulseBreakPoint / m_smallPulseFlowRate));
}
} }
constexpr float convertToGramsPerSecond(float ccPerMinute) { constexpr float convertToGramsPerSecond(float ccPerMinute) {
return ccPerMinute * (fuelDensity / 60.f); return ccPerMinute * (fuelDensity / 60.f);
} }
float InjectorModel::getBaseFlowRate() const {
if (engineConfiguration->injectorFlowAsMassFlow) {
return engineConfiguration->injector.flow;
} else {
return convertToGramsPerSecond(engineConfiguration->injector.flow);
}
}
float InjectorModel::getSmallPulseFlowRate() const {
return engineConfiguration->fordInjectorSmallPulseSlope;
}
float InjectorModel::getSmallPulseBreakPoint() const {
// convert milligrams -> grams
return 0.001f * engineConfiguration->fordInjectorSmallPulseBreakPoint;
}
InjectorNonlinearMode InjectorModel::getNonlinearMode() const {
return engineConfiguration->injectorNonlinearMode;
}
expected<float> InjectorModel::getAbsoluteRailPressure() const { expected<float> InjectorModel::getAbsoluteRailPressure() const {
switch (engineConfiguration->injectorCompensationMode) { switch (engineConfiguration->injectorCompensationMode) {
case ICM_FixedRailPressure: case ICM_FixedRailPressure:
@ -79,15 +111,6 @@ float InjectorModel::getInjectorFlowRatio() {
return flowRatio; return flowRatio;
} }
float InjectorModel::getInjectorMassFlowRate() {
// TODO: injector flow dependent upon temperature/ethanol content?
auto injectorVolumeFlow = engineConfiguration->injector.flow;
float flowRatio = getInjectorFlowRatio();
return flowRatio * convertToGramsPerSecond(injectorVolumeFlow);
}
float InjectorModel::getDeadtime() const { float InjectorModel::getDeadtime() const {
return interpolate2d( return interpolate2d(
Sensor::get(SensorType::BatteryVoltage).value_or(VBAT_FALLBACK_VALUE), Sensor::get(SensorType::BatteryVoltage).value_or(VBAT_FALLBACK_VALUE),
@ -97,18 +120,15 @@ float InjectorModel::getDeadtime() const {
} }
float InjectorModelBase::getInjectionDuration(float fuelMassGram) const { float InjectorModelBase::getInjectionDuration(float fuelMassGram) const {
// TODO: support injector nonlinearity correction if (fuelMassGram <= 0) {
// If 0 mass, don't do any math, just skip the injection.
floatms_t baseDuration = fuelMassGram / m_massFlowRate * 1000;
if (baseDuration <= 0) {
// If 0 duration, don't correct or add deadtime, just skip the injection.
return 0.0f; return 0.0f;
} }
// Correct short pulses (if enabled) // Get the no-offset duration
baseDuration = correctShortPulse(baseDuration); float baseDuration = getBaseDurationImpl(fuelMassGram);
// Add deadtime offset
return baseDuration + m_deadtime; return baseDuration + m_deadtime;
} }
@ -117,8 +137,18 @@ float InjectorModelBase::getFuelMassForDuration(floatms_t duration) const {
return duration * m_massFlowRate * 0.001f; return duration * m_massFlowRate * 0.001f;
} }
float InjectorModel::correctShortPulse(float baseDuration) const { float InjectorModelBase::getBaseDurationImpl(float fuelMassGram) const {
switch (engineConfiguration->injectorNonlinearMode) { floatms_t baseDuration = fuelMassGram / m_massFlowRate * 1000;
switch (getNonlinearMode()) {
case INJ_FordModel:
if (fuelMassGram < m_smallPulseBreakPoint) {
// Small pulse uses a different slope, and adds the "zero fuel pulse" offset
return (fuelMassGram / m_smallPulseFlowRate * 1000) + m_smallPulseOffset;
} else {
// Large pulse
return baseDuration;
}
case INJ_PolynomialAdder: case INJ_PolynomialAdder:
return correctInjectionPolynomial(baseDuration); return correctInjectionPolynomial(baseDuration);
case INJ_None: case INJ_None:
@ -127,7 +157,7 @@ float InjectorModel::correctShortPulse(float baseDuration) const {
} }
} }
float InjectorModel::correctInjectionPolynomial(float baseDuration) const { float InjectorModelBase::correctInjectionPolynomial(float baseDuration) const {
if (baseDuration > engineConfiguration->applyNonlinearBelowPulse) { if (baseDuration > engineConfiguration->applyNonlinearBelowPulse) {
// Large pulse, skip correction. // Large pulse, skip correction.
return baseDuration; return baseDuration;

View File

@ -17,26 +17,48 @@ public:
floatms_t getInjectionDuration(float fuelMassGram) const override; floatms_t getInjectionDuration(float fuelMassGram) const override;
float getFuelMassForDuration(floatms_t duration) const override; float getFuelMassForDuration(floatms_t duration) const override;
virtual float getInjectorMassFlowRate() = 0;
virtual float getInjectorFlowRatio() = 0; virtual float getInjectorFlowRatio() = 0;
virtual expected<float> getAbsoluteRailPressure() const = 0; virtual expected<float> getAbsoluteRailPressure() const = 0;
virtual float correctShortPulse(float baseDuration) const = 0;
virtual float getBaseFlowRate() const = 0;
virtual InjectorNonlinearMode getNonlinearMode() const = 0;
float getBaseDurationImpl(float baseDuration) const;
virtual float correctInjectionPolynomial(float baseDuration) const;
// Ford small pulse model
virtual float getSmallPulseFlowRate() const = 0;
virtual float getSmallPulseBreakPoint() const = 0;
private: private:
// Mass flow rate for large-pulse flow, g/s
float m_massFlowRate = 0; float m_massFlowRate = 0;
// Break point below which the "small pulse" slope is used, grams
float m_smallPulseBreakPoint = 0;
// Flow rate for small pulses, g/s
float m_smallPulseFlowRate = 0;
// Correction adder for small pulses to correct for small/large pulse kink, ms
float m_smallPulseOffset = 0;
}; };
class InjectorModel : public InjectorModelBase { class InjectorModel : public InjectorModelBase {
public: public:
floatms_t getDeadtime() const override; floatms_t getDeadtime() const override;
float getInjectorMassFlowRate() override; float getBaseFlowRate() const override;
float getInjectorFlowRatio() override; float getInjectorFlowRatio() override;
expected<float> getAbsoluteRailPressure() const override; expected<float> getAbsoluteRailPressure() const override;
InjectorNonlinearMode getNonlinearMode() const override;
// Ford small pulse model
float getSmallPulseFlowRate() const override;
float getSmallPulseBreakPoint() const override;
// Small pulse correction logic // Small pulse correction logic
float correctShortPulse(float baseDuration) const override;
virtual float correctInjectionPolynomial(float baseDuration) const;
using interface_t = IInjectorModel; // Mock interface using interface_t = IInjectorModel; // Mock interface
}; };

View File

@ -620,6 +620,7 @@ typedef enum __attribute__ ((__packed__)) {
typedef enum __attribute__ ((__packed__)) { typedef enum __attribute__ ((__packed__)) {
INJ_None = 0, INJ_None = 0,
INJ_PolynomialAdder = 1, INJ_PolynomialAdder = 1,
INJ_FordModel = 2,
} InjectorNonlinearMode; } InjectorNonlinearMode;
typedef enum __attribute__ ((__packed__)) { typedef enum __attribute__ ((__packed__)) {

View File

@ -742,7 +742,9 @@ pin_input_mode_e throttlePedalUpPinMode;
float compressionRatio;Just for reference really, not taken into account by any logic at this point;"CR", 1, 0, 0, 300, 1 float compressionRatio;Just for reference really, not taken into account by any logic at this point;"CR", 1, 0, 0, 300, 1
Gpio[TRIGGER_SIMULATOR_PIN_COUNT iterate] triggerSimulatorPins;Each rusEFI piece can provide synthetic trigger signal for external ECU. Sometimes these wires are routed back into trigger inputs of the same rusEFI board.\nSee also directSelfStimulation which is different. Gpio[TRIGGER_SIMULATOR_PIN_COUNT iterate] triggerSimulatorPins;Each rusEFI piece can provide synthetic trigger signal for external ECU. Sometimes these wires are routed back into trigger inputs of the same rusEFI board.\nSee also directSelfStimulation which is different.
uint16_t unusedTrig
uint16_t autoscale fordInjectorSmallPulseSlope;;"g/s", 0.001, 0, 0, 65, 3
pin_output_mode_e[TRIGGER_SIMULATOR_PIN_COUNT iterate] triggerSimulatorPinModes; pin_output_mode_e[TRIGGER_SIMULATOR_PIN_COUNT iterate] triggerSimulatorPinModes;
uint8_t unusedTrigMode uint8_t unusedTrigMode
output_pin_e o2heaterPin;Narrow band o2 heater, not used for CJ125. 'ON' if engine is running, 'OFF' if stopped or cranking. See wboHeaterPin output_pin_e o2heaterPin;Narrow band o2 heater, not used for CJ125. 'ON' if engine is running, 'OFF' if stopped or cranking. See wboHeaterPin
@ -817,7 +819,7 @@ output_pin_e acFanPin;Optional Radiator Fan used with A/C
brain_input_pin_e vehicleSpeedSensorInputPin; brain_input_pin_e vehicleSpeedSensorInputPin;
switch_input_pin_e clutchUpPin;Some vehicles have a switch to indicate that clutch pedal is all the way up switch_input_pin_e clutchUpPin;Some vehicles have a switch to indicate that clutch pedal is all the way up
custom InjectorNonlinearMode 1 bits, U08, @OFFSET@, [0:0], "None", "Polynomial" custom InjectorNonlinearMode 1 bits, U08, @OFFSET@, [0:1], "None", "Polynomial", "Ford (dual slope)"
InjectorNonlinearMode injectorNonlinearMode InjectorNonlinearMode injectorNonlinearMode
pin_input_mode_e clutchUpPinMode; pin_input_mode_e clutchUpPinMode;
@ -1212,7 +1214,7 @@ int16_t tps2Max;Full throttle#2. tpsMax value as 10 bit ADC value. Not Voltage!\
bit stepperDcInvertedPins;Enable if DC-motor driver (H-bridge) inverts the signals (eg. RZ7899 on Hellen boards) bit stepperDcInvertedPins;Enable if DC-motor driver (H-bridge) inverts the signals (eg. RZ7899 on Hellen boards)
bit canOpenBLT; Allow OpenBLT on Primary CAN bit canOpenBLT; Allow OpenBLT on Primary CAN
bit can2OpenBLT; Allow OpenBLT on Secondary CAN bit can2OpenBLT; Allow OpenBLT on Secondary CAN
bit unused1740b2 bit injectorFlowAsMassFlow,"mass flow","volumetric flow";Select whether to configure injector flow in volumetric flow (defualt, cc/min) or mass flow (g/s).
bit unused1127 bit unused1127
bit unused1128 bit unused1128
bit unused1129 bit unused1129
@ -1444,7 +1446,9 @@ tChargeMode_e tChargeMode;
uint8_t autoscale dfcoDelay;Delay before cutting fuel. Set to 0 to cut immediately with no delay. May cause rumbles and pops out of your exhaust...;"sec", 0.1, 0, 0, 10, 1 uint8_t autoscale dfcoDelay;Delay before cutting fuel. Set to 0 to cut immediately with no delay. May cause rumbles and pops out of your exhaust...;"sec", 0.1, 0, 0, 10, 1
uint8_t autoscale acDelay;Delay before engaging the AC compressor. Set to 0 to engage immediately with no delay. Use this to prevent bogging at idle when AC engages.;"sec", 0.1, 0, 0, 10, 1 uint8_t autoscale acDelay;Delay before engaging the AC compressor. Set to 0 to engage immediately with no delay. Use this to prevent bogging at idle when AC engages.;"sec", 0.1, 0, 0, 10, 1
int8_t[9] unused4080;;"", 1, 0, 0, 0, 0 uint16_t autoscale fordInjectorSmallPulseBreakPoint;;"mg", 0.001, 0, 0, 65, 3
int8_t[5] unused4080;;"", 1, 0, 0, 0, 0
! Someday there will be a 6th option for BMW S55 that uses a separate shaft just for HPFP ! Someday there will be a 6th option for BMW S55 that uses a separate shaft just for HPFP
#define hpfp_cam_e_enum "NONE", "Intake 1", "Exhaust 1", "Intake 2", "Exhaust 2" #define hpfp_cam_e_enum "NONE", "Intake 1", "Exhaust 1", "Intake 2", "Exhaust 2"

View File

@ -2222,7 +2222,8 @@ cmd_set_engine_type_default = "@@TS_IO_TEST_COMMAND_char@@@@ts_command_e_TS_
; Engine->Injection Settings ; Engine->Injection Settings
dialog = injChars, "Injector Settings", yAxis dialog = injChars, "Injector Settings", yAxis
field = "Injector Flow", injector_flow, {isInjectionEnabled == 1} field = "Injector flow", injector_flow, {isInjectionEnabled == 1}
field = "Injector flow units", injectorFlowAsMassFlow, {isInjectionEnabled == 1}
field = "Fuel rail pressure sensor", injectorPressureType, { isInjectionEnabled && (highPressureFuel_hwChannel || lowPressureFuel_hwChannel) } field = "Fuel rail pressure sensor", injectorPressureType, { isInjectionEnabled && (highPressureFuel_hwChannel || lowPressureFuel_hwChannel) }
field = "Injector flow compensation mode", injectorCompensationMode, { isInjectionEnabled } field = "Injector flow compensation mode", injectorCompensationMode, { isInjectionEnabled }
field = "Injector reference pressure", fuelReferencePressure, { isInjectionEnabled && injectorCompensationMode != 0 } field = "Injector reference pressure", fuelReferencePressure, { isInjectionEnabled && injectorCompensationMode != 0 }
@ -2273,9 +2274,16 @@ cmd_set_engine_type_default = "@@TS_IO_TEST_COMMAND_char@@@@ts_command_e_TS_
field = "x^6", injectorCorrectionPolynomial7 field = "x^6", injectorCorrectionPolynomial7
field = "x^7", injectorCorrectionPolynomial8 field = "x^7", injectorCorrectionPolynomial8
dialog = injectorNonlinearFord, "Ford-model Small Pulse Correction", yAxis
field = "Small pulse slope (ALOSL)", fordInjectorSmallPulseSlope
field = "Set this to 'mass flow'", injectorFlowAsMassFlow
field = "Large pulse slope (AHISL)", injector_flow
field = "Small pulse breakpoint (FUEL_BKPT)", fordInjectorSmallPulseBreakPoint
dialog = injectorNonlinear dialog = injectorNonlinear
field = "Small pulse correction mode", injectorNonlinearMode field = "Small pulse correction mode", injectorNonlinearMode
panel = injectorNonlinearPolynomial, {1}, { injectorNonlinearMode != 0 } panel = injectorNonlinearPolynomial, {1}, { injectorNonlinearMode == 1 }
panel = injectorNonlinearFord, {1}, { injectorNonlinearMode == 2 }
dialog = testLuaOut, "Lua Out Test" dialog = testLuaOut, "Lua Out Test"
commandButton = "Lua Out #1", cmd_test_lua1 commandButton = "Lua Out #1", cmd_test_lua1

View File

@ -7,17 +7,21 @@ using ::testing::StrictMock;
class MockInjectorModel : public InjectorModelBase { class MockInjectorModel : public InjectorModelBase {
public: public:
MOCK_METHOD(floatms_t, getDeadtime, (), (const, override)); MOCK_METHOD(floatms_t, getDeadtime, (), (const, override));
MOCK_METHOD(float, getInjectorMassFlowRate, (), (override)); MOCK_METHOD(float, getBaseFlowRate, (), (const, override));
MOCK_METHOD(float, getInjectorFlowRatio, (), (override)); MOCK_METHOD(float, getInjectorFlowRatio, (), (override));
MOCK_METHOD(expected<float>, getAbsoluteRailPressure, (), (const, override)); MOCK_METHOD(expected<float>, getAbsoluteRailPressure, (), (const, override));
MOCK_METHOD(float, correctShortPulse, (float baseDuration), (const, override)); MOCK_METHOD(float, getSmallPulseFlowRate, (), (const, override));
MOCK_METHOD(float, getSmallPulseBreakPoint, (), (const, override));
MOCK_METHOD(InjectorNonlinearMode, getNonlinearMode, (), (const, override));
}; };
TEST(InjectorModel, Prepare) { TEST(InjectorModel, Prepare) {
StrictMock<MockInjectorModel> dut; StrictMock<MockInjectorModel> dut;
EXPECT_CALL(dut, getDeadtime()); EXPECT_CALL(dut, getDeadtime());
EXPECT_CALL(dut, getInjectorMassFlowRate()); EXPECT_CALL(dut, getBaseFlowRate());
EXPECT_CALL(dut, getNonlinearMode());
EXPECT_CALL(dut, getInjectorFlowRatio());
dut.prepare(); dut.prepare();
} }
@ -27,11 +31,12 @@ TEST(InjectorModel, getInjectionDuration) {
EXPECT_CALL(dut, getDeadtime()) EXPECT_CALL(dut, getDeadtime())
.WillOnce(Return(2.0f)); .WillOnce(Return(2.0f));
EXPECT_CALL(dut, getInjectorMassFlowRate()) EXPECT_CALL(dut, getInjectorFlowRatio())
.WillOnce(Return(1.0f));
EXPECT_CALL(dut, getBaseFlowRate())
.WillOnce(Return(4.8f)); // 400cc/min .WillOnce(Return(4.8f)); // 400cc/min
EXPECT_CALL(dut, correctShortPulse(_)) EXPECT_CALL(dut, getNonlinearMode())
.Times(2) .WillRepeatedly(Return(INJ_None));
.WillRepeatedly([](float b) { return b; });
dut.prepare(); dut.prepare();
@ -39,23 +44,65 @@ TEST(InjectorModel, getInjectionDuration) {
EXPECT_NEAR(dut.getInjectionDuration(0.02f), 20 / 4.8f + 2.0f, EPS4D); EXPECT_NEAR(dut.getInjectionDuration(0.02f), 20 / 4.8f + 2.0f, EPS4D);
} }
TEST(InjectorModel, getInjectionDurationNonlinear) { TEST(InjectorModel, getInjectionDurationWithFlowRatio) {
StrictMock<MockInjectorModel> dut; StrictMock<MockInjectorModel> dut;
EXPECT_CALL(dut, getDeadtime()) EXPECT_CALL(dut, getDeadtime())
.WillOnce(Return(2.0f)); .WillOnce(Return(2.0f));
EXPECT_CALL(dut, getInjectorMassFlowRate()) EXPECT_CALL(dut, getInjectorFlowRatio())
.WillOnce(Return(1.1f));
EXPECT_CALL(dut, getBaseFlowRate())
.WillOnce(Return(4.8f)); // 400cc/min .WillOnce(Return(4.8f)); // 400cc/min
EXPECT_CALL(dut, getNonlinearMode())
// Dummy nonlinearity correction: just doubles the pulse .WillRepeatedly(Return(INJ_None));
EXPECT_CALL(dut, correctShortPulse(_))
.Times(2)
.WillRepeatedly([](float b) { return 2 * b; });
dut.prepare(); dut.prepare();
EXPECT_NEAR(dut.getInjectionDuration(0.01f), 2 * 10 / 4.8f + 2.0f, EPS4D); EXPECT_NEAR(dut.getInjectionDuration(0.01f), 10 / (4.8f * 1.1f) + 2.0f, EPS4D);
EXPECT_NEAR(dut.getInjectionDuration(0.02f), 2 * 20 / 4.8f + 2.0f, EPS4D); EXPECT_NEAR(dut.getInjectionDuration(0.02f), 20 / (4.8f * 1.1f) + 2.0f, EPS4D);
}
TEST(InjectorModel, nonLinearFordMode) {
StrictMock<MockInjectorModel> dut;
EXPECT_CALL(dut, getDeadtime())
.WillOnce(Return(0));
EXPECT_CALL(dut, getInjectorFlowRatio())
.WillOnce(Return(1.0f));
// 2005 F150 injectors
EXPECT_CALL(dut, getBaseFlowRate())
.WillRepeatedly(Return(2.979f));
EXPECT_CALL(dut, getSmallPulseFlowRate())
.WillRepeatedly(Return(3.562f));
EXPECT_CALL(dut, getSmallPulseBreakPoint())
.WillRepeatedly(Return(0.00627f));
EXPECT_CALL(dut, getNonlinearMode())
.WillRepeatedly(Return(INJ_FordModel));
dut.prepare();
EXPECT_NEAR(dut.getBaseDurationImpl(0.000f), 0.344f, 1e-3);
EXPECT_NEAR(dut.getBaseDurationImpl(0.001f), 0.625f, 1e-3);
EXPECT_NEAR(dut.getBaseDurationImpl(0.002f), 0.906f, 1e-3);
EXPECT_NEAR(dut.getBaseDurationImpl(0.003f), 1.187f, 1e-3);
EXPECT_NEAR(dut.getBaseDurationImpl(0.004f), 1.467f, 1e-3);
EXPECT_NEAR(dut.getBaseDurationImpl(0.005f), 1.748f, 1e-3);
EXPECT_NEAR(dut.getBaseDurationImpl(0.006f), 2.029f, 1e-3);
EXPECT_NEAR(dut.getBaseDurationImpl(0.007f), 2.350f, 1e-3);
EXPECT_NEAR(dut.getBaseDurationImpl(0.008f), 2.685f, 1e-3);
EXPECT_NEAR(dut.getBaseDurationImpl(0.009f), 3.021f, 1e-3);
EXPECT_NEAR(dut.getBaseDurationImpl(0.010f), 3.357f, 1e-3);
EXPECT_NEAR(dut.getBaseDurationImpl(0.011f), 3.693f, 1e-3);
EXPECT_NEAR(dut.getBaseDurationImpl(0.012f), 4.028f, 1e-3);
EXPECT_NEAR(dut.getBaseDurationImpl(0.013f), 4.364f, 1e-3);
EXPECT_NEAR(dut.getBaseDurationImpl(0.014f), 4.700f, 1e-3);
EXPECT_NEAR(dut.getBaseDurationImpl(0.015f), 5.035f, 1e-3);
EXPECT_NEAR(dut.getBaseDurationImpl(0.016f), 5.371f, 1e-3);
EXPECT_NEAR(dut.getBaseDurationImpl(0.017f), 5.707f, 1e-3);
EXPECT_NEAR(dut.getBaseDurationImpl(0.018f), 6.042f, 1e-3);
EXPECT_NEAR(dut.getBaseDurationImpl(0.019f), 6.378f, 1e-3);
EXPECT_NEAR(dut.getBaseDurationImpl(0.020f), 6.714f, 1e-3);
} }
TEST(InjectorModel, nonlinearPolynomial) { TEST(InjectorModel, nonlinearPolynomial) {
@ -117,22 +164,6 @@ INSTANTIATE_TEST_SUITE_P(
::testing::Values(0.1f, 0.5f, 1.0f, 2.0f, 10.0f) ::testing::Values(0.1f, 0.5f, 1.0f, 2.0f, 10.0f)
); );
TEST_P(FlowRateFixture, FlowRateRatio) {
float flowRatio = GetParam();
StrictMock<TesterGetFlowRate> dut;
EXPECT_CALL(dut, getInjectorFlowRatio()).WillOnce(Return(flowRatio));
EngineTestHelper eth(TEST_ENGINE);
engineConfiguration->injector.flow = 500;
// 500 cc/min = 6g/s
float expectedFlow = flowRatio * 6.0f;
// Check that flow is adjusted correctly
EXPECT_FLOAT_EQ(expectedFlow, dut.getInjectorMassFlowRate());
}
TEST_P(FlowRateFixture, PressureRatio) { TEST_P(FlowRateFixture, PressureRatio) {
float pressureRatio = GetParam(); float pressureRatio = GetParam();
// Flow ratio should be the sqrt of pressure ratio // Flow ratio should be the sqrt of pressure ratio