Compare commits

..

No commits in common. "51a23364e20d2584bb30194b8c13b622014b898f" and "7593cd61d4787a441d6364f721fd67d3587b6f44" have entirely different histories.

6 changed files with 25 additions and 126 deletions

View File

@ -43,7 +43,6 @@ or
- TunerStudio UI improvements (#436, etc)
- Dropdown selector for popular gearbox ratios (#358, thank you @alrijleh and @nmschulte!)
- Add two more aux linear sensors #476
- Support wasted spark on odd cylinder count 4-stroke engines. Improves startup and allows running without a cam sensor!
### Fixed
- Improve performance with Lua CAN reception of a high volume of frames

View File

@ -24,8 +24,6 @@ public:
*/
angle_t engineCycle;
bool useOddFireWastedSpark = false;
/**
* this is based on sensorChartMode and sensorSnifferRpmThreshold settings
*/

View File

@ -104,14 +104,6 @@ static void prepareCylinderIgnitionSchedule(angle_t dwellAngleDuration, floatms_
event->sparkAngle = sparkAngle;
auto ignitionMode = getCurrentIgnitionMode();
// On an odd cylinder (or odd fire) wasted spark engine, map outputs as if in sequential.
// During actual scheduling, the events just get scheduled every 360 deg instead
// of every 720 deg.
if (ignitionMode == IM_WASTED_SPARK && engine->engineState.useOddFireWastedSpark) {
ignitionMode = IM_INDIVIDUAL_COILS;
}
engine->outputChannels.currentIgnitionMode = static_cast<uint8_t>(ignitionMode);
const int index = getIgnitionPinForIndex(event->cylinderIndex, ignitionMode);
@ -317,9 +309,20 @@ void turnSparkPinHigh(IgnitionEvent *event) {
}
static void scheduleSparkEvent(bool limitedSpark, IgnitionEvent *event,
int rpm, float dwellMs, float dwellAngle, float sparkAngle, efitick_t edgeTimestamp, float currentPhase, float nextPhase) {
int rpm, efitick_t edgeTimestamp, float currentPhase, float nextPhase) {
float angleOffset = dwellAngle - currentPhase;
angle_t sparkAngle = event->sparkAngle;
const floatms_t dwellMs = engine->ignitionState.sparkDwell;
if (std::isnan(dwellMs) || dwellMs <= 0) {
warning(ObdCode::CUSTOM_DWELL, "invalid dwell to handle: %.2f at %d", dwellMs, rpm);
return;
}
if (std::isnan(sparkAngle)) {
warning(ObdCode::CUSTOM_ADVANCE_SPARK, "NaN advance");
return;
}
float angleOffset = event->dwellAngle - currentPhase;
if (angleOffset < 0) {
angleOffset += engine->engineState.engineCycle;
}
@ -454,12 +457,6 @@ void onTriggerEventSparkLogic(int rpm, efitick_t edgeTimestamp, float currentPha
engine->outputChannels.sparkCutReason = (int8_t)limitedSparkState.reason;
bool limitedSpark = !limitedSparkState.value;
const floatms_t dwellMs = engine->ignitionState.sparkDwell;
if (std::isnan(dwellMs) || dwellMs <= 0) {
warning(ObdCode::CUSTOM_DWELL, "invalid dwell to handle: %.2f at %d", dwellMs, rpm);
return;
}
if (!engine->ignitionEvents.isReady) {
prepareIgnitionSchedule();
}
@ -470,48 +467,13 @@ void onTriggerEventSparkLogic(int rpm, efitick_t edgeTimestamp, float currentPha
* See initializeIgnitionActions()
*/
// Only apply odd cylinder count wasted logic if:
// - odd cyl count
// - current mode is wasted spark
// - four stroke
bool enableOddCylinderWastedSpark =
engine->engineState.useOddFireWastedSpark
&& getCurrentIgnitionMode() == IM_WASTED_SPARK;
// scheduleSimpleMsg(&logger, "eventId spark ", eventIndex);
if (engine->ignitionEvents.isReady) {
for (size_t i = 0; i < engineConfiguration->cylindersCount; i++) {
IgnitionEvent *event = &engine->ignitionEvents.elements[i];
angle_t dwellAngle = event->dwellAngle;
angle_t sparkAngle = event->sparkAngle;
if (std::isnan(sparkAngle)) {
warning(ObdCode::CUSTOM_ADVANCE_SPARK, "NaN advance");
continue;
}
bool isOddCylWastedEvent = false;
if (enableOddCylinderWastedSpark) {
auto dwellAngleWastedEvent = dwellAngle + 360;
if (dwellAngleWastedEvent > 720) {
dwellAngleWastedEvent -= 720;
}
// Check whether this event hits 360 degrees out from now (ie, wasted spark),
// and if so, twiddle the dwell and spark angles so it happens now instead
isOddCylWastedEvent = isPhaseInRange(dwellAngleWastedEvent, currentPhase, nextPhase);
if (isOddCylWastedEvent) {
dwellAngle = dwellAngleWastedEvent;
sparkAngle += 360;
if (sparkAngle > 720) {
sparkAngle -= 720;
}
}
}
if (!isOddCylWastedEvent && !isPhaseInRange(dwellAngle, currentPhase, nextPhase)) {
if (!isPhaseInRange(event->dwellAngle, currentPhase, nextPhase)) {
continue;
}
@ -536,7 +498,7 @@ void onTriggerEventSparkLogic(int rpm, efitick_t edgeTimestamp, float currentPha
engine->ALSsoftSparkLimiter.setTargetSkipRatio(ALSSkipRatio);
#endif // EFI_ANTILAG_SYSTEM
scheduleSparkEvent(limitedSpark, event, rpm, dwellMs, dwellAngle, sparkAngle, edgeTimestamp, currentPhase, nextPhase);
scheduleSparkEvent(limitedSpark, event, rpm, edgeTimestamp, currentPhase, nextPhase);
}
}
}

View File

@ -19,6 +19,12 @@ static bool noFiringUntilVvtSync(vvt_mode_e vvtMode) {
return true;
}
// Odd cylinder count engines don't work properly with wasted spark, so wait for full sync (so that sequential works)
// See https://github.com/rusefi/rusefi/issues/4195 for the issue to properly support this case
if (engineConfiguration->cylindersCount > 1 && engineConfiguration->cylindersCount % 2 == 1) {
return true;
}
// Symmetrical crank modes require cam sync before firing
// non-symmetrical cranks can use faster spin-up mode (firing in wasted/batch before VVT sync)
// Examples include Nissan MR/VQ, Miata NB, etc

View File

@ -386,7 +386,8 @@ ignition_mode_e getCurrentIgnitionMode() {
ignition_mode_e ignitionMode = engineConfiguration->ignitionMode;
#if EFI_SHAFT_POSITION_INPUT
// In spin-up cranking mode we don't have full phase sync info yet, so wasted spark mode is better
if (ignitionMode == IM_INDIVIDUAL_COILS) {
// However, only do this on even cylinder count engines: odd cyl count doesn't fire at all
if (ignitionMode == IM_INDIVIDUAL_COILS && (engineConfiguration->cylindersCount % 2 == 0)) {
bool missingPhaseInfoForSequential =
!engine->triggerCentral.triggerState.hasSynchronizedPhase();
@ -404,20 +405,7 @@ ignition_mode_e getCurrentIgnitionMode() {
* This heavy method is only invoked in case of a configuration change or initialization.
*/
void prepareOutputSignals() {
auto operationMode = getEngineRotationState()->getOperationMode();
getEngineState()->engineCycle = getEngineCycle(operationMode);
bool isOddFire = false;
for (size_t i = 0; i < engineConfiguration->cylindersCount; i++) {
if (engineConfiguration->timing_offset_cylinder[i] != 0) {
isOddFire = true;
break;
}
}
// Use odd fire wasted spark logic if not two stroke, and an odd fire or odd cylinder # engine
getEngineState()->useOddFireWastedSpark = operationMode != TWO_STROKE
&& (isOddFire | (engineConfiguration->cylindersCount % 2 == 1));
getEngineState()->engineCycle = getEngineCycle(getEngineRotationState()->getOperationMode());
#if EFI_UNIT_TEST
if (verboseMode) {

View File

@ -9,8 +9,6 @@
#include "spark_logic.h"
using ::testing::_;
using ::testing::InSequence;
using ::testing::StrictMock;
TEST(ignition, twoCoils) {
EngineTestHelper eth(engine_type_e::FRANKENSO_BMW_M73_F);
@ -150,55 +148,3 @@ TEST(ignition, CylinderTimingTrim) {
EXPECT_NEAR(engine->engineState.timingAdvance[2], unadjusted + 2, EPS4D);
EXPECT_NEAR(engine->engineState.timingAdvance[3], unadjusted + 4, EPS4D);
}
TEST(ignition, oddCylinderWastedSpark) {
StrictMock<MockExecutor> mockExec;
EngineTestHelper eth(engine_type_e::TEST_ENGINE);
engine->scheduler.setMockExecutor(&mockExec);
engineConfiguration->cylindersCount = 1;
engineConfiguration->firingOrder = FO_1;
engineConfiguration->ignitionMode = IM_WASTED_SPARK;
efitick_t nowNt1 = 1000000;
efitick_t nowNt2 = 2222222;
engine->rpmCalculator.oneDegreeUs = 100;
{
InSequence is;
// Should schedule one dwell+fire pair:
// Dwell 5 deg from now
float nt1deg = USF2NT(engine->rpmCalculator.oneDegreeUs);
efitick_t startTime = nowNt1 + nt1deg * 5;
EXPECT_CALL(mockExec, schedule(testing::NotNull(), _, startTime, _));
// Spark 15 deg from now
efitick_t endTime = startTime + nt1deg * 10;
EXPECT_CALL(mockExec, schedule(testing::NotNull(), _, endTime, _));
// Should schedule second dwell+fire pair, the out of phase copy
// Dwell 5 deg from now
startTime = nowNt2 + nt1deg * 5;
EXPECT_CALL(mockExec, schedule(testing::NotNull(), _, startTime, _));
// Spark 15 deg from now
endTime = startTime + nt1deg * 10;
EXPECT_CALL(mockExec, schedule(testing::NotNull(), _, endTime, _));
}
engine->ignitionState.sparkDwell = 1;
// dwell should start at 15 degrees ATDC and firing at 25 deg ATDC
engine->ignitionState.dwellAngle = 10;
engine->engineState.timingAdvance[0] = -25;
engine->engineState.useOddFireWastedSpark = true;
engineConfiguration->minimumIgnitionTiming = -25;
// expect to schedule the on-phase dwell and spark (not the wasted spark copy)
onTriggerEventSparkLogic(1200, nowNt1, 10, 30);
// expect to schedule second events, the out-of-phase dwell and spark (the wasted spark copy)
onTriggerEventSparkLogic(1200, nowNt2, 360 + 10, 360 + 30);
}