LCOV - code coverage report
Current view: top level - unit_tests/tests - test_hpfp.cpp (source / functions) Hit Total Coverage
Test: coverage.info Lines: 185 185 100.0 %
Date: 2024-04-22 23:59:02 Functions: 20 20 100.0 %

          Line data    Source code
       1             : #include "pch.h"
       2             : 
       3             : #include "high_pressure_fuel_pump.h"
       4             : #include "fuel_computer.h"
       5             : 
       6             : using ::testing::_;
       7             : using ::testing::StrictMock;
       8             : 
       9           4 : TEST(HPFP, Lobe) {
      10           1 :         EngineTestHelper eth(engine_type_e::TEST_ENGINE);
      11             : 
      12           1 :         engineConfiguration->hpfpCam = HPFP_CAM_NONE;
      13           1 :         engineConfiguration->hpfpPeakPos = 123;
      14           1 :         engineConfiguration->hpfpCamLobes = 3;
      15             : 
      16           1 :         engine->triggerCentral.vvtPosition[0][0] = 40; // Bank 0
      17           1 :         engine->triggerCentral.vvtPosition[0][1] = 80;
      18           1 :         engine->triggerCentral.vvtPosition[1][0] =120; // Bank 1
      19           1 :         engine->triggerCentral.vvtPosition[1][1] =160;
      20             : 
      21           1 :         HpfpLobe lobe;
      22             : 
      23             :         // Run through all five CAM modes
      24           6 :         for (int cam = 0; cam < 5; cam++) {
      25             :                 static hpfp_cam_e map[5] = { HPFP_CAM_NONE, HPFP_CAM_IN1, HPFP_CAM_EX1,
      26             :                                              HPFP_CAM_IN2, HPFP_CAM_EX2};
      27           5 :                 engineConfiguration->hpfpCam = map[cam];
      28             : 
      29             :                 // Run through several cycles of the engine to make sure we keep wrapping around
      30          25 :                 for (int i = 0; i < 4; i++) {
      31          20 :                         EXPECT_EQ(lobe.findNextLobe(), 240 + 123 + cam * 20);
      32          20 :                         EXPECT_EQ(lobe.findNextLobe(), 480 + 123 + cam * 20);
      33          20 :                         EXPECT_EQ(lobe.findNextLobe(),   0 + 123 + cam * 20);
      34             :                 }
      35             :         }
      36             : 
      37             :         // Can we change the number of lobes?
      38           1 :         engineConfiguration->hpfpCam = HPFP_CAM_NONE;
      39           1 :         engineConfiguration->hpfpCamLobes = 4;
      40           1 :         EXPECT_EQ(lobe.findNextLobe(), 180 + 123);
      41           1 :         EXPECT_EQ(lobe.findNextLobe(), 360 + 123);
      42           1 :         EXPECT_EQ(lobe.findNextLobe(), 540 + 123);
      43           1 :         EXPECT_EQ(lobe.findNextLobe(),   0 + 123);
      44             : 
      45             :         // Can we change the peak position?
      46           1 :         engineConfiguration->hpfpPeakPos = 95;
      47           1 :         EXPECT_EQ(lobe.findNextLobe(), 180 + 95);
      48           1 :         EXPECT_EQ(lobe.findNextLobe(), 360 + 95);
      49           1 :         EXPECT_EQ(lobe.findNextLobe(), 540 + 95);
      50           1 :         EXPECT_EQ(lobe.findNextLobe(),   0 + 95);
      51           2 : }
      52             : 
      53           4 : TEST(HPFP, InjectionReplacementFuel) {
      54           1 :         EngineTestHelper eth(engine_type_e::TEST_ENGINE);
      55             : 
      56           1 :         engineConfiguration->cylindersCount = 4;
      57           1 :         engineConfiguration->hpfpCamLobes = 4;
      58           1 :         engine->engineState.injectionMass[0] = 0.05 /* cc/cyl */ * fuelDensity;
      59           1 :         engineConfiguration->hpfpPumpVolume = 0.2; // cc/lobe
      60             : 
      61           1 :         HpfpQuantity math;
      62             : 
      63           1 :         EXPECT_FLOAT_EQ(math.calcFuelPercent(1000), 25);
      64             : 
      65             :         // What if we change the # of lobes?
      66           1 :         engineConfiguration->hpfpCamLobes = 3;
      67           1 :         EXPECT_FLOAT_EQ(math.calcFuelPercent(1000), 25 * 1.333333333f);
      68             : 
      69             :         // More fuel!
      70           1 :         engine->engineState.injectionMass[0] = 0.1 /* cc/cyl */ * fuelDensity;
      71           1 :         EXPECT_FLOAT_EQ(math.calcFuelPercent(1000), 50 * 1.333333333f);
      72             : 
      73             :         // More cylinders!
      74           1 :         engineConfiguration->cylindersCount = 6;
      75           1 :         EXPECT_FLOAT_EQ(math.calcFuelPercent(1000), 50 * 2.); // Ooops we maxed out
      76             : 
      77             :         // Compensation testing
      78           1 :         engineConfiguration->cylindersCount =
      79           1 :                 engineConfiguration->hpfpCamLobes; // Make math easier
      80          11 :         for (int i = 0; i < HPFP_COMPENSATION_SIZE; i++) {
      81             :                 // one bin every 1000 RPM
      82          10 :                 config->hpfpCompensationRpmBins[i] = std::min(i * 1000, 8000);
      83             :         }
      84          11 :         for (int i = 0; i < HPFP_COMPENSATION_SIZE; i++) {
      85             :                 // one bin every 0.05 cc/lobe
      86          10 :                 config->hpfpCompensationLoadBins[i] = std::min(i * 0.05, 60.);
      87             :         }
      88             : 
      89           1 :         config->hpfpCompensation[2][1] = -10;
      90           1 :         EXPECT_FLOAT_EQ(math.calcFuelPercent(1000), 40); // -10, in cell
      91           1 :         EXPECT_FLOAT_EQ(math.calcFuelPercent(1500), 45); // -5, half way
      92           1 :         EXPECT_FLOAT_EQ(math.calcFuelPercent(2000), 50); // -0, in next cell
      93             : 
      94           1 :         config->hpfpCompensation[2][1] = 20;
      95           1 :         EXPECT_FLOAT_EQ(math.calcFuelPercent(1000), 70); // +20, in cell
      96           1 :         EXPECT_FLOAT_EQ(math.calcFuelPercent(1500), 60); // +10, half way
      97           1 :         EXPECT_FLOAT_EQ(math.calcFuelPercent(2000), 50); // +0, in next cell
      98             : 
      99             :         // 25% more fuel (1.25*50=62.5 base), but half way between comp of 20 and 0 (so comp of 10)
     100           1 :         engine->engineState.injectionMass[0] = 0.125 /* cc/cyl */ * fuelDensity;
     101           1 :         EXPECT_FLOAT_EQ(math.calcFuelPercent(1000), 72.5); // +10 comp
     102           1 :         EXPECT_FLOAT_EQ(math.calcFuelPercent(1500), 67.5); // +5, half way
     103           1 :         EXPECT_FLOAT_EQ(math.calcFuelPercent(2000), 62.5); // +0 base
     104           2 : }
     105             : 
     106           4 : TEST(HPFP, PI) {
     107           1 :         EngineTestHelper eth(engine_type_e::TEST_ENGINE);
     108             : 
     109           1 :         engineConfiguration->hpfpTargetDecay = 0;
     110           1 :         engineConfiguration->hpfpPidP = 0;
     111           1 :         engineConfiguration->hpfpPidI = 0;
     112           1 :         engineConfiguration->hpfpPeakPos = 0;
     113             : 
     114           1 :         engineConfiguration->cylindersCount = 4;
     115           1 :         engineConfiguration->hpfpCamLobes = 4;
     116           1 :         engine->engineState.injectionMass[0] = 0.05 /* cc/cyl */ * fuelDensity;
     117           1 :         engineConfiguration->hpfpPumpVolume = 0.2; // cc/lobe
     118             : 
     119           1 :         HpfpQuantity math;
     120             : 
     121          11 :         for (int i = 0; i < HPFP_TARGET_SIZE; i++) {
     122             :                 // one bin every 1000 RPM
     123          10 :                 config->hpfpTargetRpmBins[i] = std::min(i * 1000, 8000);
     124             :         }
     125          11 :         for (int i = 0; i < HPFP_TARGET_SIZE; i++) {
     126             :                 // one bin every 20kPa
     127          10 :                 config->hpfpTargetLoadBins[i] = std::min(i * 20, 200);
     128             :         }
     129          11 :         for (int r = 0; r < HPFP_TARGET_SIZE; r++) {
     130         110 :                 for (int c = 0; c < HPFP_TARGET_SIZE; c++) {
     131         100 :                         config->hpfpTarget[r][c] = 1000 * r + 10 * c;
     132             :                 }
     133             :         }
     134             : 
     135           1 :         Sensor::setMockValue(SensorType::Map, 40);
     136           1 :         Sensor::setMockValue(SensorType::FuelPressureHigh, 1000);
     137           1 :         EXPECT_FLOAT_EQ(math.calcPI(1000, 120), -20); // Test integral clamp
     138           1 :         EXPECT_FLOAT_EQ(math.m_I_sum_percent, -20); // and again
     139           1 :         EXPECT_FLOAT_EQ(math.m_pressureTarget_kPa, 2010);
     140           1 :         EXPECT_FLOAT_EQ(math.calcPI(1000, -40), 40); // Test integral clamp
     141           1 :         EXPECT_FLOAT_EQ(math.m_I_sum_percent, 40); // and again
     142           1 :         EXPECT_FLOAT_EQ(math.m_pressureTarget_kPa, 2010);
     143             : 
     144             :         // Test pressure decay
     145           1 :         math.calcPI(4000, 0);
     146           1 :         EXPECT_FLOAT_EQ(math.m_pressureTarget_kPa, 2040);
     147           1 :         math.calcPI(1000, 0);
     148           1 :         EXPECT_FLOAT_EQ(math.m_pressureTarget_kPa, 2040);
     149           1 :         engineConfiguration->hpfpTargetDecay = 1000;
     150           1 :         math.calcPI(1000, 0);
     151           1 :         EXPECT_FLOAT_EQ(math.m_pressureTarget_kPa, 2035); // 5ms of decay
     152             : 
     153             :         // Proportional gain
     154           1 :         math.reset(); // Reset for ease of testing
     155           1 :         EXPECT_FLOAT_EQ(math.calcPI(1000, 0), 0); // Validate reset
     156           1 :         EXPECT_FLOAT_EQ(math.m_pressureTarget_kPa, 2010);
     157           1 :         engineConfiguration->hpfpPidP = 0.01;
     158           1 :         EXPECT_FLOAT_EQ(math.calcPI(1000, 0), 10.10);
     159           1 :         engineConfiguration->hpfpPidP = 0.02;
     160           1 :         EXPECT_FLOAT_EQ(math.calcPI(1000, 0), 20.20);
     161             : 
     162             :         // Integral gain
     163           1 :         engineConfiguration->hpfpPidI = 0.001;
     164           1 :         EXPECT_FLOAT_EQ(math.calcPI(1000, 0), 20.368334);
     165           1 :         EXPECT_FLOAT_EQ(math.m_I_sum_percent, 0.168333333);
     166           2 : }
     167             : 
     168           4 : TEST(HPFP, Angle) {
     169           1 :         EngineTestHelper eth(engine_type_e::TEST_ENGINE);
     170             : 
     171           1 :         engineConfiguration->hpfpTargetDecay = 0;
     172           1 :         engineConfiguration->hpfpPidP = 0;
     173           1 :         engineConfiguration->hpfpPidI = 0;
     174           1 :         engineConfiguration->hpfpPeakPos = 0;
     175             : 
     176           1 :         engineConfiguration->cylindersCount = 4;
     177           1 :         engineConfiguration->hpfpCamLobes = 4;
     178           1 :         engine->engineState.injectionMass[0] = 0.05 /* cc/cyl */ * fuelDensity;
     179           1 :         engineConfiguration->hpfpPumpVolume = 0.2; // cc/lobe
     180             : 
     181           1 :         HpfpQuantity math;
     182             : 
     183          11 :         for (int i = 0; i < HPFP_TARGET_SIZE; i++) {
     184             :                 // one bin every 1000 RPM
     185          10 :                 config->hpfpTargetRpmBins[i] = std::min(i * 1000, 8000);
     186             :         }
     187          11 :         for (int i = 0; i < HPFP_TARGET_SIZE; i++) {
     188             :                 // one bin every 20kPa
     189          10 :                 config->hpfpTargetLoadBins[i] = std::min(i * 20, 200);
     190             :         }
     191          11 :         for (int r = 0; r < HPFP_TARGET_SIZE; r++) {
     192         110 :                 for (int c = 0; c < HPFP_TARGET_SIZE; c++) {
     193         100 :                         config->hpfpTarget[r][c] = 1000 * r + 10 * c;
     194             :                 }
     195             :         }
     196          17 :         for (int i = 0; i < HPFP_LOBE_PROFILE_SIZE; i++) {
     197          16 :                 config->hpfpLobeProfileQuantityBins[i] = 100. * i / (HPFP_LOBE_PROFILE_SIZE - 1);
     198          16 :                 config->hpfpLobeProfileAngle[i] = 150. * i / (HPFP_LOBE_PROFILE_SIZE - 1);
     199             :         }
     200             : 
     201           1 :         HpfpController model;
     202             : 
     203           1 :         EXPECT_FLOAT_EQ(math.calcFuelPercent(1000), 25); // Double check baseline
     204           1 :         EXPECT_FLOAT_EQ(math.calcPI(1000, 10), 0); // Validate no PI
     205           1 :         EXPECT_NEAR(math.pumpAngleFuel(1000, &model), 37.5, 0.4); // Given the profile, should be 50% higher
     206             : 
     207           1 :         engine->engineState.injectionMass[0] = 0.08 /* cc/cyl */ * fuelDensity;
     208           1 :         EXPECT_FLOAT_EQ(math.calcFuelPercent(1000), 40); // Double check baseline
     209           1 :         EXPECT_FLOAT_EQ(math.calcPI(1000, 10), 0); // Validate no PI
     210           1 :         EXPECT_NEAR(math.pumpAngleFuel(1000, &model), 60, 0.4); // Given the profile, should be 50% higher
     211             : 
     212           1 :         engineConfiguration->hpfpPidP = 0.01;
     213           1 :         Sensor::setMockValue(SensorType::Map, 40);
     214           1 :         Sensor::setMockValue(SensorType::FuelPressureHigh, 1000);
     215           1 :         EXPECT_FLOAT_EQ(math.calcPI(1000, 10), 10.1);
     216           1 :         EXPECT_NEAR(math.pumpAngleFuel(1000, &model), 50.1 * 1.5, 0.4); // Given the profile, should be 50% higher
     217           2 : }
     218             : 
     219           4 : TEST(HPFP, Schedule) {
     220           1 :         EngineTestHelper eth(engine_type_e::TEST_ENGINE);
     221             : 
     222           1 :         engineConfiguration->cylindersCount = 4;
     223           1 :         engineConfiguration->hpfpCamLobes = 4;
     224           1 :         engineConfiguration->hpfpPumpVolume = 0.2; // cc/lobe
     225             : 
     226          11 :         for (int i = 0; i < HPFP_TARGET_SIZE; i++) {
     227             :                 // one bin every 1000 RPM
     228          10 :                 config->hpfpTargetRpmBins[i] = std::min(i * 1000, 8000);
     229             :         }
     230          11 :         for (int i = 0; i < HPFP_TARGET_SIZE; i++) {
     231             :                 // one bin every 20kPa
     232          10 :                 config->hpfpTargetLoadBins[i] = std::min(i * 20, 200);
     233             :         }
     234          11 :         for (int r = 0; r < HPFP_TARGET_SIZE; r++) {
     235         110 :                 for (int c = 0; c < HPFP_TARGET_SIZE; c++) {
     236         100 :                         config->hpfpTarget[r][c] = 1000 * r + 10 * c;
     237             :                 }
     238             :         }
     239          17 :         for (int i = 0; i < HPFP_LOBE_PROFILE_SIZE; i++) {
     240          16 :                 config->hpfpLobeProfileQuantityBins[i] = 100. * i / (HPFP_LOBE_PROFILE_SIZE - 1);
     241          16 :                 config->hpfpLobeProfileAngle[i] = 150. * i / (HPFP_LOBE_PROFILE_SIZE - 1);
     242             :         }
     243             : 
     244           1 :         auto & hpfp = *engine->module<HpfpController>();
     245             : 
     246           1 :         StrictMock<MockExecutor> mockExec;
     247           1 :         engine->executor.setMockExecutor(&mockExec);
     248           1 :         engineConfiguration->hpfpActivationAngle = 30;
     249             : 
     250           1 :         constexpr angle_t angle0 = 90;
     251           1 :         constexpr angle_t angle1 = 270 - 37.6923065;
     252           1 :         constexpr angle_t angle2 = angle1 + 30;
     253             : 
     254           1 :         constexpr float tick_per_deg = USF2NT(1000000.)*60/360/1000;
     255             : 
     256           1 :         constexpr efitick_t nt0 = tick_per_deg * angle0;
     257           1 :         constexpr efitick_t nt1 = tick_per_deg * angle1;
     258           1 :         constexpr efitick_t nt2 = tick_per_deg * angle2;
     259             : 
     260             :         {
     261           1 :                 testing::InSequence is;
     262             : 
     263             :                 // First call to setRpmValue will cause a dummy call to fast periodic timer.
     264             :                 // Injection Mass will be 0 so expect a no-op.
     265           1 :                 EXPECT_CALL(mockExec, scheduleByTimestampNt(testing::NotNull(), &hpfp.m_event.scheduling, nt0, action_s(HpfpController::pinTurnOff, &hpfp)));
     266             : 
     267             :                 // Second call will be the start of a real pump event.
     268           1 :                 EXPECT_CALL(mockExec, scheduleByTimestampNt(testing::NotNull(), &hpfp.m_event.scheduling, nt1, action_s(HpfpController::pinTurnOn, &hpfp)));
     269             : 
     270             :                 // Third call will be off event
     271           1 :                 EXPECT_CALL(mockExec, scheduleByTimestampNt(testing::NotNull(), &hpfp.m_event.scheduling, nt2, action_s(HpfpController::pinTurnOff, &hpfp)));
     272           1 :         }
     273           1 :         EXPECT_CALL(mockExec, cancel(_)).Times(2);
     274             : 
     275             :         // For HPFP to work, events need to be scheduled after the next tooth.  This makes sure the
     276             :         // peak pos occurs after the next tooth.
     277           1 :         engineConfiguration->hpfpPeakPos = 90;
     278             :         // This will call the fast callback routine
     279           1 :         engine->rpmCalculator.setRpmValue(1000);
     280           1 :         engine->engineState.injectionMass[0] = 0.05 /* cc/cyl */ * fuelDensity;
     281           1 :         engineConfiguration->hpfpValvePin = Gpio::A2; // arbitrary
     282             : 
     283           1 :         hpfp.onFastCallback();
     284             :         // First event was scheduled by setRpmValue with 0 injection mass.  So, it's off.
     285           1 :         eth.assertTriggerEvent("h0", 0, &hpfp.m_event, (void*)&HpfpController::pinTurnOff, 270);
     286             : 
     287             :         // Make the previous event happen, schedule the next.
     288           1 :         engine->module<TriggerScheduler>()->scheduleEventsUntilNextTriggerTooth(
     289             :                 1000, tick_per_deg * 0, 180, 360);
     290             :         // Mock executor doesn't run events, so we run it manually
     291           1 :         HpfpController::pinTurnOff(&hpfp);
     292             : 
     293             :         // Now we should have a regular on/off event.
     294           1 :         eth.assertTriggerEvent("h1", 0, &hpfp.m_event, (void*)&HpfpController::pinTurnOn, 450 - 37.6923065f);
     295             : 
     296             :         // Make it happen
     297           1 :         engine->module<TriggerScheduler>()->scheduleEventsUntilNextTriggerTooth(
     298             :                 1000, tick_per_deg * 180, 360, 540);
     299             : 
     300             :         // Since we have a mock scheduler, lets insert the correct timestamp in the scheduling
     301             :         // struct.
     302           1 :         hpfp.m_event.scheduling.momentX = nt1;
     303             : 
     304           1 :         HpfpController::pinTurnOn(&hpfp);
     305             : 
     306             :         // The off event goes directly to scheduleByAngle and is tested by the last EXPECT_CALL
     307             :         // above.
     308           2 : }

Generated by: LCOV version 1.14