calculate per-cylinder ignition timing (#3652)
* simplify cylinder phasing * per cylinder timing * s * s * s * why was there a divide by 2?
This commit is contained in:
parent
17debda6b6
commit
3a95e86112
|
@ -624,7 +624,7 @@ static void updateFuelInfo() {
|
|||
}
|
||||
|
||||
static void updateIgnition(int rpm) {
|
||||
float timing = engine->engineState.timingAdvance;
|
||||
float timing = engine->engineState.timingAdvance[0];
|
||||
// that's weird logic. also seems broken for two stroke?
|
||||
tsOutputChannels.ignitionAdvance = timing > FOUR_STROKE_CYCLE_DURATION / 2 ? timing - FOUR_STROKE_CYCLE_DURATION : timing;
|
||||
// 60
|
||||
|
|
|
@ -165,7 +165,11 @@ void EngineState::periodicFastCallback() {
|
|||
injectionOffset = getInjectionOffset(rpm, fuelLoad);
|
||||
|
||||
float ignitionLoad = getIgnitionLoad();
|
||||
timingAdvance = getAdvance(rpm, ignitionLoad) * luaAdjustments.ignitionTimingMult + luaAdjustments.ignitionTimingAdd;
|
||||
float advance = getAdvance(rpm, ignitionLoad) * luaAdjustments.ignitionTimingMult + luaAdjustments.ignitionTimingAdd;
|
||||
|
||||
for (size_t i = 0; i < engineConfiguration->specs.cylindersCount; i++) {
|
||||
timingAdvance[i] = advance;
|
||||
}
|
||||
|
||||
// TODO: calculate me from a table!
|
||||
trailingSparkAngle = engineConfiguration->trailingSparkAngle;
|
||||
|
|
|
@ -54,7 +54,7 @@ public:
|
|||
/**
|
||||
* timing advance is angle distance before Top Dead Center (TDP), i.e. "10 degree timing advance" means "happens 10 degrees before TDC"
|
||||
*/
|
||||
angle_t timingAdvance = 0;
|
||||
angle_t timingAdvance[MAX_CYLINDER_COUNT] = {0};
|
||||
|
||||
// Angle between firing the main (primary) spark and the secondary (trailing) spark
|
||||
angle_t trailingSparkAngle = 0;
|
||||
|
|
|
@ -600,7 +600,7 @@ void canDashboardHaltech(CanCycle cycle) {
|
|||
msg[2] = 0x00;
|
||||
msg[3] = 0x00;
|
||||
/* Ignition Angle (Leading) - y = x/10 */
|
||||
float timing = engine->engineState.timingAdvance;
|
||||
float timing = engine->engineState.timingAdvance[0];
|
||||
int16_t ignAngle = ((timing > 360 ? timing - 720 : timing) * 10);
|
||||
msg[4] = (ignAngle >> 8);
|
||||
msg[5] = (ignAngle & 0x00ff);
|
||||
|
|
|
@ -56,7 +56,7 @@ static void populateFrame(Speeds& msg) {
|
|||
auto rpm = GET_RPM();
|
||||
msg.rpm = rpm;
|
||||
|
||||
auto timing = engine->engineState.timingAdvance;
|
||||
auto timing = engine->engineState.timingAdvance[0];
|
||||
msg.timing = timing > 360 ? timing - 720 : timing;
|
||||
|
||||
msg.injDuty = getInjectorDutyCycle(rpm);
|
||||
|
|
|
@ -138,7 +138,7 @@ static void handleGetDataRequest(const CANRxFrame& rx) {
|
|||
obdSendValue(_1_MODE, pid, 1, Sensor::getOrZero(SensorType::VehicleSpeed));
|
||||
break;
|
||||
case PID_TIMING_ADVANCE: {
|
||||
float timing = engine->engineState.timingAdvance;
|
||||
float timing = engine->engineState.timingAdvance[0];
|
||||
timing = (timing > 360.0f) ? (timing - 720.0f) : timing;
|
||||
obdSendValue(_1_MODE, pid, 1, (timing + 64.0f) * 2.0f); // angle before TDC. (A/2)-64
|
||||
break;
|
||||
|
|
|
@ -11,9 +11,9 @@
|
|||
|
||||
#include "hip9011.h"
|
||||
|
||||
int getCylinderKnockBank(uint8_t cylinderIndex) {
|
||||
int getCylinderKnockBank(uint8_t cylinderNumber) {
|
||||
// C/C++ can't index in to bit fields, we have to provide lookup ourselves
|
||||
switch (cylinderIndex) {
|
||||
switch (cylinderNumber) {
|
||||
#if EFI_PROD_CODE
|
||||
case 0:
|
||||
return engineConfiguration->knockBankCyl1;
|
||||
|
@ -45,13 +45,13 @@ int getCylinderKnockBank(uint8_t cylinderIndex) {
|
|||
}
|
||||
}
|
||||
|
||||
bool KnockController::onKnockSenseCompleted(uint8_t cylinderIndex, float dbv, efitick_t lastKnockTime) {
|
||||
bool KnockController::onKnockSenseCompleted(uint8_t cylinderNumber, float dbv, efitick_t lastKnockTime) {
|
||||
bool isKnock = dbv > engine->engineState.knockThreshold;
|
||||
|
||||
#if EFI_TUNER_STUDIO
|
||||
// Pass through per-cylinder peak detector
|
||||
float cylPeak = peakDetectors[cylinderIndex].detect(dbv, lastKnockTime);
|
||||
tsOutputChannels.knock[cylinderIndex] = roundf(cylPeak);
|
||||
float cylPeak = peakDetectors[cylinderNumber].detect(dbv, lastKnockTime);
|
||||
tsOutputChannels.knock[cylinderNumber] = roundf(cylPeak);
|
||||
|
||||
// Pass through all-cylinders peak detector
|
||||
tsOutputChannels.knockLevel = allCylinderPeakDetector.detect(dbv, lastKnockTime);
|
||||
|
@ -64,7 +64,7 @@ bool KnockController::onKnockSenseCompleted(uint8_t cylinderIndex, float dbv, ef
|
|||
|
||||
// TODO: retard timing, then put it back!
|
||||
if (isKnock) {
|
||||
auto baseTiming = engine->engineState.timingAdvance;
|
||||
auto baseTiming = engine->engineState.timingAdvance[cylinderNumber];
|
||||
|
||||
// TODO: 20 configurable? Better explanation why 20?
|
||||
auto distToMinimum = baseTiming - (-20);
|
||||
|
@ -107,13 +107,13 @@ void KnockController::periodicFastCallback() {
|
|||
}
|
||||
|
||||
// This callback is to be implemented by the knock sense driver
|
||||
__attribute__((weak)) void onStartKnockSampling(uint8_t cylinderIndex, float samplingTimeSeconds, uint8_t channelIdx) {
|
||||
UNUSED(cylinderIndex);
|
||||
__attribute__((weak)) void onStartKnockSampling(uint8_t cylinderNumber, float samplingTimeSeconds, uint8_t channelIdx) {
|
||||
UNUSED(cylinderNumber);
|
||||
UNUSED(samplingTimeSeconds);
|
||||
UNUSED(channelIdx);
|
||||
}
|
||||
|
||||
static uint8_t cylinderIndexCopy;
|
||||
static uint8_t cylinderNumberCopy;
|
||||
|
||||
// Called when its time to start listening for knock
|
||||
// Does some math, then hands off to the driver to start any sampling hardware
|
||||
|
@ -126,16 +126,16 @@ static void startKnockSampling(Engine* engine) {
|
|||
float samplingSeconds = engine->rpmCalculator.oneDegreeUs * engineConfiguration->knockSamplingDuration / US_PER_SECOND_F;
|
||||
|
||||
// Look up which channel this cylinder uses
|
||||
auto channel = getCylinderKnockBank(cylinderIndexCopy);
|
||||
auto channel = getCylinderKnockBank(cylinderNumberCopy);
|
||||
|
||||
// Call the driver to begin sampling
|
||||
onStartKnockSampling(cylinderIndexCopy, samplingSeconds, channel);
|
||||
onStartKnockSampling(cylinderNumberCopy, samplingSeconds, channel);
|
||||
}
|
||||
|
||||
static scheduling_s startSampling;
|
||||
|
||||
void Engine::onSparkFireKnockSense(uint8_t cylinderIndex, efitick_t nowNt) {
|
||||
cylinderIndexCopy = cylinderIndex;
|
||||
void Engine::onSparkFireKnockSense(uint8_t cylinderNumber, efitick_t nowNt) {
|
||||
cylinderNumberCopy = cylinderNumber;
|
||||
|
||||
#if EFI_HIP_9011 || EFI_SOFTWARE_KNOCK
|
||||
scheduleByAngle(&startSampling, nowNt,
|
||||
|
@ -143,6 +143,6 @@ void Engine::onSparkFireKnockSense(uint8_t cylinderIndex, efitick_t nowNt) {
|
|||
#endif
|
||||
|
||||
#if EFI_HIP_9011
|
||||
hip9011_onFireEvent(cylinderIndex, nowNt);
|
||||
hip9011_onFireEvent(cylinderNumber, nowNt);
|
||||
#endif
|
||||
}
|
||||
|
|
|
@ -9,12 +9,12 @@
|
|||
|
||||
#include "peak_detect.h"
|
||||
|
||||
int getCylinderKnockBank(uint8_t cylinderIndex);
|
||||
int getCylinderKnockBank(uint8_t cylinderNumber);
|
||||
|
||||
class KnockController {
|
||||
public:
|
||||
// onKnockSenseCompleted is the callback from the knock sense driver to report a sensed knock level
|
||||
bool onKnockSenseCompleted(uint8_t cylinderIndex, float dbv, efitick_t lastKnockTime);
|
||||
bool onKnockSenseCompleted(uint8_t cylinderNumber, float dbv, efitick_t lastKnockTime);
|
||||
void periodicFastCallback();
|
||||
|
||||
float getKnockRetard() const;
|
||||
|
|
|
@ -503,7 +503,7 @@ static void showMainInfo(Engine *engine) {
|
|||
int rpm = GET_RPM();
|
||||
float el = getFuelingLoad();
|
||||
efiPrintf("rpm %d engine_load %.2f", rpm, el);
|
||||
efiPrintf("fuel %.2fms timing %.2f", engine->injectionDuration, engine->engineState.timingAdvance);
|
||||
efiPrintf("fuel %.2fms timing %.2f", engine->injectionDuration, engine->engineState.timingAdvance[0]);
|
||||
#endif /* EFI_PROD_CODE */
|
||||
}
|
||||
|
||||
|
|
|
@ -75,7 +75,7 @@ static void prepareCylinderIgnitionSchedule(angle_t dwellAngleDuration, floatms_
|
|||
|
||||
const angle_t sparkAngle =
|
||||
// Negate because timing *before* TDC, and we schedule *after* TDC
|
||||
- engine->engineState.timingAdvance
|
||||
- engine->engineState.timingAdvance[event->cylinderNumber]
|
||||
// Offset by this cylinder's position in the cycle
|
||||
+ getCylinderAngle(event->cylinderIndex, event->cylinderNumber)
|
||||
// Pull any extra timing for knock retard
|
||||
|
@ -373,7 +373,7 @@ void initializeIgnitionActions() {
|
|||
IgnitionEventList *list = &engine->ignitionEvents;
|
||||
angle_t dwellAngle = engine->engineState.dwellAngle;
|
||||
floatms_t sparkDwell = engine->engineState.sparkDwell;
|
||||
if (cisnan(engine->engineState.timingAdvance) || cisnan(dwellAngle)) {
|
||||
if (cisnan(engine->engineState.timingAdvance[0]) || cisnan(dwellAngle)) {
|
||||
// error should already be reported
|
||||
// need to invalidate previous ignition schedule
|
||||
list->isReady = false;
|
||||
|
|
|
@ -11,7 +11,7 @@
|
|||
#include "ch.hpp"
|
||||
|
||||
static NO_CACHE adcsample_t sampleBuffer[2000];
|
||||
static int8_t currentCylinderIndex = 0;
|
||||
static int8_t currentCylinderNumber = 0;
|
||||
static efitick_t lastKnockSampleTime = 0;
|
||||
static Biquad knockFilter;
|
||||
|
||||
|
@ -104,7 +104,7 @@ const ADCConversionGroup* getConversionGroup(uint8_t channelIdx) {
|
|||
return &adcConvGroupCh1;
|
||||
}
|
||||
|
||||
void onStartKnockSampling(uint8_t cylinderIndex, float samplingSeconds, uint8_t channelIdx) {
|
||||
void onStartKnockSampling(uint8_t cylinderNumber, float samplingSeconds, uint8_t channelIdx) {
|
||||
if (!engineConfiguration->enableSoftwareKnock) {
|
||||
return;
|
||||
}
|
||||
|
@ -128,8 +128,8 @@ void onStartKnockSampling(uint8_t cylinderIndex, float samplingSeconds, uint8_t
|
|||
// Select the appropriate conversion group - it will differ depending on which sensor this cylinder should listen on
|
||||
auto conversionGroup = getConversionGroup(channelIdx);
|
||||
|
||||
// Stash the current cylinder's index so we can store the result appropriately
|
||||
currentCylinderIndex = cylinderIndex;
|
||||
// Stash the current cylinder's number so we can store the result appropriately
|
||||
currentCylinderNumber = cylinderNumber;
|
||||
|
||||
adcStartConversionI(&KNOCK_ADC, conversionGroup, sampleBuffer, sampleCount);
|
||||
lastKnockSampleTime = getTimeNowNt();
|
||||
|
@ -201,7 +201,7 @@ void processLastKnockEvent() {
|
|||
// clamp to reasonable range
|
||||
db = clampF(-100, db, 100);
|
||||
|
||||
engine->knockController.onKnockSenseCompleted(currentCylinderIndex, db, lastKnockTime);
|
||||
engine->knockController.onKnockSenseCompleted(currentCylinderNumber, db, lastKnockTime);
|
||||
}
|
||||
|
||||
void KnockThread::ThreadTask() {
|
||||
|
|
|
@ -23,7 +23,7 @@ TEST(ignition, twoCoils) {
|
|||
ASSERT_EQ(engine->ignitionPin[ID2INDEX(12)], 1);
|
||||
|
||||
// let's recalculate with zero timing so that we can focus on relation advance between cylinders
|
||||
engine->engineState.timingAdvance = 0;
|
||||
setArrayValues(engine->engineState.timingAdvance, 0.0f);
|
||||
initializeIgnitionActions();
|
||||
|
||||
ASSERT_EQ(engine->ignitionEvents.elements[0].sparkAngle, 0);
|
||||
|
|
|
@ -345,7 +345,7 @@ TEST(misc, testRpmCalculator) {
|
|||
|
||||
eth.engine.periodicFastCallback();
|
||||
|
||||
ASSERT_NEAR(engine->engineState.timingAdvance, 707, 0.1f);
|
||||
ASSERT_NEAR(engine->engineState.timingAdvance[0], 707, 0.1f);
|
||||
|
||||
assertEqualsM("fuel #1", 4.5450, engine->injectionDuration);
|
||||
InjectionEvent *ie0 = &engine->injectionEvents.elements[0];
|
||||
|
|
Loading…
Reference in New Issue