2015-07-10 06:01:56 -07:00
|
|
|
/**
|
|
|
|
* @file engine_math.cpp
|
|
|
|
* @brief
|
|
|
|
*
|
|
|
|
* @date Jul 13, 2013
|
2017-01-03 03:05:22 -08:00
|
|
|
* @author Andrey Belomutskiy, (c) 2012-2017
|
2015-07-10 06:01:56 -07:00
|
|
|
*
|
|
|
|
* This file is part of rusEfi - see http://rusefi.com
|
|
|
|
*
|
|
|
|
* rusEfi is free software; you can redistribute it and/or modify it under the terms of
|
|
|
|
* the GNU General Public License as published by the Free Software Foundation; either
|
|
|
|
* version 3 of the License, or (at your option) any later version.
|
|
|
|
*
|
|
|
|
* rusEfi is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without
|
|
|
|
* even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
* GNU General Public License for more details.
|
|
|
|
*
|
|
|
|
* You should have received a copy of the GNU General Public License along with this program.
|
|
|
|
* If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include "main.h"
|
|
|
|
#include "engine_math.h"
|
|
|
|
#include "engine_configuration.h"
|
|
|
|
#include "interpolation.h"
|
|
|
|
#include "allsensors.h"
|
|
|
|
#include "trigger_decoder.h"
|
|
|
|
#include "event_registry.h"
|
|
|
|
#include "efiGpio.h"
|
|
|
|
#include "fuel_math.h"
|
2016-01-08 12:01:38 -08:00
|
|
|
#include "advance_map.h"
|
2015-07-10 06:01:56 -07:00
|
|
|
|
|
|
|
EXTERN_ENGINE
|
|
|
|
;
|
|
|
|
|
2016-11-03 20:02:58 -07:00
|
|
|
extern EnginePins enginePins;
|
2015-07-10 06:01:56 -07:00
|
|
|
|
2017-05-15 20:28:49 -07:00
|
|
|
floatms_t getEngineCycleDuration(int rpm DECLARE_ENGINE_PARAMETER_SUFFIX) {
|
2017-03-08 21:51:27 -08:00
|
|
|
return getCrankshaftRevolutionTimeMs(rpm) * (engineConfiguration->operationMode == TWO_STROKE ? 1 : 2);
|
|
|
|
}
|
|
|
|
|
2015-07-10 06:01:56 -07:00
|
|
|
/**
|
2016-01-08 12:01:38 -08:00
|
|
|
* @return number of milliseconds in one crank shaft revolution
|
2015-07-10 06:01:56 -07:00
|
|
|
*/
|
2015-09-07 06:03:24 -07:00
|
|
|
floatms_t getCrankshaftRevolutionTimeMs(int rpm) {
|
2016-08-30 19:02:21 -07:00
|
|
|
if (rpm == 0) {
|
|
|
|
return NAN;
|
|
|
|
}
|
2015-07-10 06:01:56 -07:00
|
|
|
return 360 * getOneDegreeTimeMs(rpm);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @brief Returns engine load according to selected engine_load_mode
|
|
|
|
*
|
|
|
|
*/
|
2017-05-15 20:28:49 -07:00
|
|
|
float getEngineLoadT(DECLARE_ENGINE_PARAMETER_SIGNATURE) {
|
2015-07-10 06:01:56 -07:00
|
|
|
efiAssert(engine!=NULL, "engine 2NULL", NAN);
|
|
|
|
efiAssert(engineConfiguration!=NULL, "engineConfiguration 2NULL", NAN);
|
2016-08-28 13:02:34 -07:00
|
|
|
switch (engineConfiguration->fuelAlgorithm) {
|
2015-07-10 06:01:56 -07:00
|
|
|
case LM_PLAIN_MAF:
|
2017-05-15 20:28:49 -07:00
|
|
|
if (!hasMafSensor(PASS_ENGINE_PARAMETER_SIGNATURE)) {
|
2017-04-19 19:03:14 -07:00
|
|
|
warning(CUSTOM_MAF_NEEDED, "MAF sensor needed for current fuel algorithm");
|
2015-07-10 06:01:56 -07:00
|
|
|
return NAN;
|
|
|
|
}
|
|
|
|
return getMafT(engineConfiguration);
|
|
|
|
case LM_SPEED_DENSITY:
|
|
|
|
// SD engine load is used for timing lookup but not for fuel calculation
|
|
|
|
case LM_MAP:
|
|
|
|
return getMap();
|
|
|
|
case LM_ALPHA_N:
|
2017-05-15 20:28:49 -07:00
|
|
|
return getTPS(PASS_ENGINE_PARAMETER_SIGNATURE);
|
2015-07-10 06:01:56 -07:00
|
|
|
case LM_REAL_MAF: {
|
2017-05-15 20:28:49 -07:00
|
|
|
return getRealMaf(PASS_ENGINE_PARAMETER_SIGNATURE);
|
2015-07-10 06:01:56 -07:00
|
|
|
}
|
|
|
|
default:
|
2017-01-22 06:03:08 -08:00
|
|
|
warning(CUSTOM_UNKNOWN_ALGORITHM, "Unexpected engine load parameter: %d", engineConfiguration->fuelAlgorithm);
|
2015-07-10 06:01:56 -07:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void setSingleCoilDwell(engine_configuration_s *engineConfiguration) {
|
|
|
|
for (int i = 0; i < DWELL_CURVE_SIZE; i++) {
|
2017-02-18 12:01:47 -08:00
|
|
|
engineConfiguration->sparkDwellRpmBins[i] = i + 1;
|
|
|
|
engineConfiguration->sparkDwellValues[i] = 4;
|
2015-07-10 06:01:56 -07:00
|
|
|
}
|
|
|
|
|
2017-02-18 12:01:47 -08:00
|
|
|
engineConfiguration->sparkDwellRpmBins[5] = 10;
|
|
|
|
engineConfiguration->sparkDwellValues[5] = 4;
|
2015-07-10 06:01:56 -07:00
|
|
|
|
2017-02-18 12:01:47 -08:00
|
|
|
engineConfiguration->sparkDwellRpmBins[6] = 4500;
|
|
|
|
engineConfiguration->sparkDwellValues[6] = 4;
|
2015-07-10 06:01:56 -07:00
|
|
|
|
2017-02-18 12:01:47 -08:00
|
|
|
engineConfiguration->sparkDwellRpmBins[7] = 12500;
|
|
|
|
engineConfiguration->sparkDwellValues[7] = 0;
|
2015-07-10 06:01:56 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
#if EFI_ENGINE_CONTROL || defined(__DOXYGEN__)
|
|
|
|
|
2016-11-30 17:02:41 -08:00
|
|
|
FuelSchedule::FuelSchedule() {
|
|
|
|
clear();
|
|
|
|
}
|
|
|
|
|
|
|
|
void FuelSchedule::clear() {
|
2016-11-30 19:06:43 -08:00
|
|
|
isReady = false;
|
2016-11-30 17:02:41 -08:00
|
|
|
}
|
|
|
|
|
2017-03-12 12:57:33 -07:00
|
|
|
/**
|
|
|
|
* @returns false in case of error, true if success
|
|
|
|
*/
|
2017-05-15 20:28:49 -07:00
|
|
|
bool FuelSchedule::addFuelEventsForCylinder(int i DECLARE_ENGINE_PARAMETER_SUFFIX) {
|
2016-12-25 19:02:40 -08:00
|
|
|
efiAssert(engine!=NULL, "engine is NULL", false);
|
2016-11-30 17:02:41 -08:00
|
|
|
|
2017-04-12 14:51:35 -07:00
|
|
|
floatus_t oneDegreeUs = ENGINE(rpmCalculator.oneDegreeUs); // local copy
|
2017-04-12 12:44:24 -07:00
|
|
|
if (cisnan(oneDegreeUs)) {
|
2016-11-30 17:02:41 -08:00
|
|
|
// in order to have fuel schedule we need to have current RPM
|
|
|
|
// wonder if this line slows engine startup?
|
2016-12-25 19:02:40 -08:00
|
|
|
return false;
|
2016-11-30 17:02:41 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* injection phase is scheduled by injection end, so we need to step the angle back
|
|
|
|
* for the duration of the injection
|
|
|
|
*
|
|
|
|
* todo: since this method is not invoked within trigger event handler and
|
|
|
|
* engineState.injectionOffset is calculated from the same utility timer should we more that logic here?
|
|
|
|
*/
|
2017-11-06 19:29:39 -08:00
|
|
|
floatms_t fuelMs = ENGINE(injectionDuration);
|
2017-04-12 14:51:35 -07:00
|
|
|
efiAssert(!cisnan(fuelMs), "NaN fuelMs", false);
|
|
|
|
angle_t injectionDuration = MS2US(fuelMs) / oneDegreeUs;
|
2017-10-29 13:42:36 -07:00
|
|
|
efiAssert(!cisnan(injectionDuration), "NaN injectionDuration", false);
|
|
|
|
assertAngleRange(injectionDuration, "injectionDuration_r");
|
2017-04-12 14:51:35 -07:00
|
|
|
floatus_t injectionOffset = ENGINE(engineState.injectionOffset);
|
2017-04-13 08:19:36 -07:00
|
|
|
if (cisnan(injectionOffset)) {
|
|
|
|
// injection offset map not ready - we are not ready to schedule fuel events
|
|
|
|
return false;
|
|
|
|
}
|
2017-08-26 17:10:50 -07:00
|
|
|
angle_t baseAngle = injectionOffset - injectionDuration;
|
2017-04-12 12:44:24 -07:00
|
|
|
efiAssert(!cisnan(baseAngle), "NaN baseAngle", false);
|
2017-08-26 16:54:06 -07:00
|
|
|
assertAngleRange(baseAngle, "baseAngle_r");
|
2016-11-30 17:02:41 -08:00
|
|
|
|
2017-11-06 18:30:39 -08:00
|
|
|
int injectorIndex;
|
2016-11-30 17:02:41 -08:00
|
|
|
|
2017-05-15 20:28:49 -07:00
|
|
|
injection_mode_e mode = engine->getCurrentInjectionMode(PASS_ENGINE_PARAMETER_SIGNATURE);
|
2016-11-30 19:06:43 -08:00
|
|
|
|
2017-11-06 16:00:30 -08:00
|
|
|
if (mode == IM_SIMULTANEOUS || mode == IM_SINGLE_POINT) {
|
2017-11-06 18:30:39 -08:00
|
|
|
injectorIndex = 0;
|
2016-11-30 17:02:41 -08:00
|
|
|
} else if (mode == IM_SEQUENTIAL) {
|
2017-11-06 18:30:39 -08:00
|
|
|
injectorIndex = getCylinderId(i PASS_ENGINE_PARAMETER_SUFFIX) - 1;
|
2016-11-30 17:02:41 -08:00
|
|
|
} else if (mode == IM_BATCH) {
|
|
|
|
// does not look exactly right, not too consistent with IM_SEQUENTIAL
|
2017-11-06 18:30:39 -08:00
|
|
|
injectorIndex = i % (engineConfiguration->specs.cylindersCount / 2);
|
2016-11-30 17:02:41 -08:00
|
|
|
} else {
|
2016-12-18 16:02:00 -08:00
|
|
|
warning(CUSTOM_OBD_UNEXPECTED_INJECTION_MODE, "Unexpected injection mode %d", mode);
|
2017-11-06 18:30:39 -08:00
|
|
|
injectorIndex = 0;
|
2016-11-30 17:02:41 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
bool isSimultanious = mode == IM_SIMULTANEOUS;
|
|
|
|
|
2016-12-06 19:03:16 -08:00
|
|
|
assertAngleRange(baseAngle, "addFbaseAngle");
|
|
|
|
|
2016-12-20 20:02:52 -08:00
|
|
|
int cylindersCount = CONFIG(specs.cylindersCount);
|
2016-12-20 21:02:53 -08:00
|
|
|
if (cylindersCount < 1) {
|
2016-12-25 19:02:40 -08:00
|
|
|
warning(CUSTOM_OBD_ZERO_CYLINDER_COUNT, "temp cylindersCount %d", cylindersCount);
|
|
|
|
return false;
|
2016-12-20 21:02:53 -08:00
|
|
|
}
|
2016-12-20 20:02:52 -08:00
|
|
|
|
2016-11-30 17:02:41 -08:00
|
|
|
float angle = baseAngle
|
2016-12-20 20:02:52 -08:00
|
|
|
+ i * ENGINE(engineCycle) / cylindersCount;
|
2016-11-30 17:02:41 -08:00
|
|
|
|
|
|
|
InjectorOutputPin *secondOutput;
|
|
|
|
if (mode == IM_BATCH && CONFIG(twoWireBatchInjection)) {
|
|
|
|
/**
|
|
|
|
* also fire the 2nd half of the injectors so that we can implement a batch mode on individual wires
|
|
|
|
*/
|
2017-11-06 18:30:39 -08:00
|
|
|
int secondIndex = injectorIndex + (CONFIG(specs.cylindersCount) / 2);
|
2016-11-30 17:02:41 -08:00
|
|
|
secondOutput = &enginePins.injectors[secondIndex];
|
|
|
|
} else {
|
|
|
|
secondOutput = NULL;
|
|
|
|
}
|
2015-08-23 19:01:55 -07:00
|
|
|
|
2017-11-06 18:30:39 -08:00
|
|
|
InjectorOutputPin *output = &enginePins.injectors[injectorIndex];
|
2015-08-23 19:01:55 -07:00
|
|
|
|
2017-07-10 18:47:51 -07:00
|
|
|
if (!isSimultanious && !output->isInitialized()) {
|
2016-08-28 13:02:34 -07:00
|
|
|
// todo: extract method for this index math
|
2016-12-18 16:02:00 -08:00
|
|
|
warning(CUSTOM_OBD_INJECTION_NO_PIN_ASSIGNED, "no_pin_inj #%s", output->name);
|
2015-07-10 06:01:56 -07:00
|
|
|
}
|
|
|
|
|
2016-11-30 19:06:43 -08:00
|
|
|
InjectionEvent *ev = &elements[i];
|
|
|
|
ev->ownIndex = i;
|
2017-03-05 18:09:31 -08:00
|
|
|
#if EFI_UNIT_TEST || defined(__DOXYGEN__)
|
2016-11-30 19:06:43 -08:00
|
|
|
ev->engine = engine;
|
|
|
|
#endif
|
2016-12-05 19:01:54 -08:00
|
|
|
fixAngle(angle, "addFuel#1");
|
2015-07-10 06:01:56 -07:00
|
|
|
|
2016-11-30 16:01:43 -08:00
|
|
|
ev->outputs[0] = output;
|
2016-11-30 17:02:41 -08:00
|
|
|
ev->outputs[1] = secondOutput;
|
2015-07-10 06:01:56 -07:00
|
|
|
|
|
|
|
ev->isSimultanious = isSimultanious;
|
|
|
|
|
2017-03-12 12:57:33 -07:00
|
|
|
if (TRIGGER_SHAPE(getSize()) < 1) {
|
2017-04-12 06:26:22 -07:00
|
|
|
warning(CUSTOM_ERR_NOT_INITIALIZED_TRIGGER, "uninitialized TriggerShape");
|
2017-03-12 12:57:33 -07:00
|
|
|
return false;
|
|
|
|
}
|
2015-07-10 06:01:56 -07:00
|
|
|
|
2017-12-02 19:29:50 -08:00
|
|
|
efiAssert(!cisnan(angle), "findAngle#3", false);
|
2017-05-15 20:28:49 -07:00
|
|
|
TRIGGER_SHAPE(findTriggerPosition(&ev->injectionStart, angle PASS_ENGINE_PARAMETER_SUFFIX));
|
2017-03-05 18:09:31 -08:00
|
|
|
#if EFI_UNIT_TEST || defined(__DOXYGEN__)
|
2017-11-06 18:37:21 -08:00
|
|
|
printf("registerInjectionEvent angle=%f trgIndex=%d inj %d\r\n", angle, ev->injectionStart.eventIndex, injectorIndex);
|
2016-01-30 19:03:36 -08:00
|
|
|
#endif
|
2016-12-25 19:02:40 -08:00
|
|
|
return true;
|
2015-07-10 06:01:56 -07:00
|
|
|
}
|
|
|
|
|
2017-05-15 20:28:49 -07:00
|
|
|
void FuelSchedule::addFuelEvents(DECLARE_ENGINE_PARAMETER_SIGNATURE) {
|
2016-11-30 18:06:24 -08:00
|
|
|
clear();
|
2015-07-10 06:01:56 -07:00
|
|
|
|
2016-11-30 17:02:41 -08:00
|
|
|
for (int i = 0; i < CONFIG(specs.cylindersCount); i++) {
|
2016-11-30 19:06:43 -08:00
|
|
|
InjectionEvent *ev = &elements[i];
|
|
|
|
ev->ownIndex = i;
|
2017-05-15 20:28:49 -07:00
|
|
|
bool result = addFuelEventsForCylinder(i PASS_ENGINE_PARAMETER_SUFFIX);
|
2016-12-25 19:02:40 -08:00
|
|
|
if (!result)
|
|
|
|
return;
|
2015-07-10 06:01:56 -07:00
|
|
|
}
|
2016-11-30 19:06:43 -08:00
|
|
|
isReady = true;
|
2015-07-10 06:01:56 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
#endif
|
|
|
|
|
2017-07-06 08:19:02 -07:00
|
|
|
static floatms_t getCrankingSparkDwell(DECLARE_ENGINE_PARAMETER_SIGNATURE) {
|
2016-11-11 16:02:29 -08:00
|
|
|
if (engineConfiguration->useConstantDwellDuringCranking) {
|
|
|
|
return engineConfiguration->ignitionDwellForCrankingMs;
|
|
|
|
} else {
|
|
|
|
// technically this could be implemented via interpolate2d
|
|
|
|
float angle = engineConfiguration->crankingChargeAngle;
|
2017-07-08 04:19:26 -07:00
|
|
|
return getOneDegreeTimeMs(GET_RPM()) * angle;
|
2016-11-11 16:02:29 -08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-07-10 06:01:56 -07:00
|
|
|
/**
|
2017-04-18 05:16:53 -07:00
|
|
|
* @return Spark dwell time, in milliseconds. 0 if tables are not ready.
|
2015-07-10 06:01:56 -07:00
|
|
|
*/
|
2017-05-15 20:28:49 -07:00
|
|
|
floatms_t getSparkDwell(int rpm DECLARE_ENGINE_PARAMETER_SUFFIX) {
|
2016-11-30 12:02:43 -08:00
|
|
|
float dwellMs;
|
2017-07-06 05:49:55 -07:00
|
|
|
if (ENGINE(rpmCalculator).isCranking(PASS_ENGINE_PARAMETER_SIGNATURE)) {
|
2017-07-06 08:19:02 -07:00
|
|
|
dwellMs = getCrankingSparkDwell(PASS_ENGINE_PARAMETER_SIGNATURE);
|
2016-11-30 12:02:43 -08:00
|
|
|
} else {
|
|
|
|
efiAssert(!cisnan(rpm), "invalid rpm", NAN);
|
|
|
|
|
2017-05-29 08:31:07 -07:00
|
|
|
dwellMs = interpolate2d("dwell", rpm, engineConfiguration->sparkDwellRpmBins, engineConfiguration->sparkDwellValues, DWELL_CURVE_SIZE);
|
2015-07-10 06:01:56 -07:00
|
|
|
}
|
|
|
|
|
2017-04-18 05:16:53 -07:00
|
|
|
if (cisnan(dwellMs) || dwellMs <= 0) {
|
|
|
|
// this could happen during engine configuration reset
|
|
|
|
warning(CUSTOM_ERR_DWELL_DURATION, "invalid dwell: %f at rpm=%d", dwellMs, rpm);
|
|
|
|
return 0;
|
2016-11-30 12:02:43 -08:00
|
|
|
}
|
|
|
|
return dwellMs;
|
2015-07-10 06:01:56 -07:00
|
|
|
}
|
|
|
|
|
2016-12-18 20:01:40 -08:00
|
|
|
/**
|
|
|
|
* this method is only used on initialization
|
|
|
|
*/
|
2017-05-15 20:28:49 -07:00
|
|
|
int TriggerShape::findAngleIndex(float target DECLARE_ENGINE_PARAMETER_SUFFIX) {
|
2017-03-01 19:01:08 -08:00
|
|
|
int engineCycleEventCount = TRIGGER_SHAPE(getLength());
|
2015-07-10 06:01:56 -07:00
|
|
|
|
|
|
|
efiAssert(engineCycleEventCount > 0, "engineCycleEventCount", 0);
|
|
|
|
|
|
|
|
uint32_t left = 0;
|
|
|
|
uint32_t right = engineCycleEventCount - 1;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Let's find the last trigger angle which is less or equal to the desired angle
|
|
|
|
* todo: extract binary search as template method?
|
|
|
|
*/
|
2015-09-13 11:02:32 -07:00
|
|
|
while (left <= right) {
|
|
|
|
int middle = (left + right) / 2;
|
2015-09-12 15:01:13 -07:00
|
|
|
angle_t eventAngle = TRIGGER_SHAPE(eventAngles[middle]);
|
2015-07-10 06:01:56 -07:00
|
|
|
|
2015-09-13 11:02:32 -07:00
|
|
|
if (eventAngle < target) {
|
|
|
|
left = middle + 1;
|
|
|
|
} else if (eventAngle > target) {
|
|
|
|
right = middle - 1;
|
|
|
|
} else {
|
2016-01-14 20:03:17 -08:00
|
|
|
// Values are equal
|
|
|
|
return middle; // Key found
|
2015-09-13 11:02:32 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
return left - 1;
|
2015-07-10 06:01:56 -07:00
|
|
|
}
|
|
|
|
|
2017-05-15 20:28:49 -07:00
|
|
|
void TriggerShape::findTriggerPosition(event_trigger_position_s *position, angle_t angleOffset DECLARE_ENGINE_PARAMETER_SUFFIX) {
|
2017-12-02 15:58:27 -08:00
|
|
|
efiAssertVoid(!cisnan(angleOffset), "findAngle#1");
|
2018-01-13 18:47:34 -08:00
|
|
|
efiAssertVoid(!cisnan(ENGINE(triggerCentral.triggerShape.tdcPosition)), "tdcPos#1")
|
|
|
|
efiAssertVoid(!cisnan(CONFIG(globalTriggerAngleOffset)), "tdcPos#2")
|
2016-01-14 20:03:17 -08:00
|
|
|
// convert engine cycle angle into trigger cycle angle
|
2015-07-10 06:01:56 -07:00
|
|
|
angleOffset += tdcPosition();
|
2017-12-02 15:58:27 -08:00
|
|
|
efiAssertVoid(!cisnan(angleOffset), "findAngle#2");
|
2016-12-05 19:01:54 -08:00
|
|
|
fixAngle(angleOffset, "addFuel#2");
|
2015-07-10 06:01:56 -07:00
|
|
|
|
2016-12-18 19:03:00 -08:00
|
|
|
int index = triggerIndexByAngle[(int)angleOffset];
|
|
|
|
angle_t eventAngle = eventAngles[index];
|
2015-07-10 06:01:56 -07:00
|
|
|
if (angleOffset < eventAngle) {
|
2016-12-18 16:02:00 -08:00
|
|
|
warning(CUSTOM_OBD_ANGLE_CONSTRAINT_VIOLATION, "angle constraint violation in findTriggerPosition(): %f/%f", angleOffset, eventAngle);
|
2015-07-10 06:01:56 -07:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
position->eventIndex = index;
|
|
|
|
position->eventAngle = eventAngle;
|
|
|
|
position->angleOffset = angleOffset - eventAngle;
|
|
|
|
}
|
|
|
|
|
2017-02-07 22:03:36 -08:00
|
|
|
static int order_1_2[] = {1, 2};
|
|
|
|
|
|
|
|
static int order_1_2_3[] = {1, 2, 3};
|
|
|
|
// 4 cylinder
|
|
|
|
|
2015-07-10 06:01:56 -07:00
|
|
|
static int order_1_THEN_3_THEN_4_THEN2[] = { 1, 3, 4, 2 };
|
|
|
|
static int order_1_THEN_2_THEN_4_THEN3[] = { 1, 2, 4, 3 };
|
|
|
|
static int order_1_THEN_3_THEN_2_THEN4[] = { 1, 3, 2, 4 };
|
|
|
|
|
2017-02-07 22:03:36 -08:00
|
|
|
// 5 cylinder
|
2015-07-10 06:01:56 -07:00
|
|
|
static int order_1_2_4_5_3[] = {1, 2, 4, 5, 3};
|
|
|
|
|
2017-02-07 22:03:36 -08:00
|
|
|
// 6 cylinder
|
2015-07-10 06:01:56 -07:00
|
|
|
static int order_1_THEN_5_THEN_3_THEN_6_THEN_2_THEN_4[] = { 1, 5, 3, 6, 2, 4 };
|
|
|
|
static int order_1_THEN_4_THEN_2_THEN_5_THEN_3_THEN_6[] = { 1, 4, 2, 5, 3, 6 };
|
2015-09-05 20:02:46 -07:00
|
|
|
static int order_1_THEN_2_THEN_3_THEN_4_THEN_5_THEN_6[] = { 1, 2, 3, 4, 5, 6 };
|
2017-01-12 21:03:30 -08:00
|
|
|
static int order_1_6_3_2_5_4[] = {1, 6, 3, 2, 5, 4};
|
2015-07-10 06:01:56 -07:00
|
|
|
|
2017-02-07 22:03:36 -08:00
|
|
|
// 8 cylinder
|
2015-07-10 06:01:56 -07:00
|
|
|
static int order_1_8_4_3_6_5_7_2[] = { 1, 8, 4, 3, 6, 5, 7, 2 };
|
|
|
|
|
2016-05-22 10:07:12 -07:00
|
|
|
static int order_1_8_7_2_6_5_4_3[] = { 1, 8, 7, 2, 6, 5, 4, 3 };
|
2016-06-05 17:03:16 -07:00
|
|
|
static int order_1_5_4_2_6_3_7_8[] = { 1, 5, 4, 2, 6, 3, 7, 8 };
|
2016-05-22 10:07:12 -07:00
|
|
|
|
2017-02-07 22:03:36 -08:00
|
|
|
// 10 cylinder
|
|
|
|
static int order_1_10_9_4_3_6_5_8_7_2[] = {1, 10, 9, 4, 3, 6, 5, 8, 7, 2};
|
2015-07-10 06:01:56 -07:00
|
|
|
|
2017-02-07 22:03:36 -08:00
|
|
|
// 12 cyliner
|
|
|
|
static int order_1_7_5_11_3_9_6_12_2_8_4_10[] = {1, 7, 5, 11, 3, 9, 6, 12, 2, 8, 4, 10};
|
2017-06-08 17:35:42 -07:00
|
|
|
static int order_1_7_4_10_2_8_6_12_3_9_5_11[] = {1, 7, 4, 10, 2, 8, 6, 12, 3, 9, 5, 11};
|
2015-09-10 16:01:32 -07:00
|
|
|
|
2017-05-15 20:28:49 -07:00
|
|
|
static int getFiringOrderLength(DECLARE_ENGINE_PARAMETER_SIGNATURE) {
|
2017-03-23 19:01:10 -07:00
|
|
|
|
|
|
|
switch (CONFIG(specs.firingOrder)) {
|
|
|
|
case FO_1:
|
|
|
|
return 1;
|
|
|
|
// 2 cylinder
|
|
|
|
case FO_1_2:
|
|
|
|
return 2;
|
|
|
|
// 3 cylinder
|
|
|
|
case FO_1_2_3:
|
|
|
|
return 3;
|
|
|
|
// 4 cylinder
|
|
|
|
case FO_1_3_4_2:
|
|
|
|
case FO_1_2_4_3:
|
|
|
|
case FO_1_3_2_4:
|
|
|
|
return 4;
|
|
|
|
// 5 cylinder
|
|
|
|
case FO_1_2_4_5_3:
|
|
|
|
return 5;
|
|
|
|
|
|
|
|
// 6 cylinder
|
|
|
|
case FO_1_5_3_6_2_4:
|
|
|
|
case FO_1_4_2_5_3_6:
|
|
|
|
case FO_1_2_3_4_5_6:
|
|
|
|
case FO_1_6_3_2_5_4:
|
|
|
|
return 6;
|
|
|
|
|
|
|
|
// 8 cylinder
|
|
|
|
case FO_1_8_4_3_6_5_7_2:
|
|
|
|
case FO_1_8_7_2_6_5_4_3:
|
|
|
|
case FO_1_5_4_2_6_3_7_8:
|
|
|
|
return 8;
|
|
|
|
|
|
|
|
// 10 cylinder
|
|
|
|
case FO_1_10_9_4_3_6_5_8_7_2:
|
|
|
|
return 10;
|
|
|
|
|
|
|
|
// 12 cylinder
|
|
|
|
case FO_1_7_5_11_3_9_6_12_2_8_4_10:
|
2017-06-08 17:35:42 -07:00
|
|
|
case FO_1_7_4_10_2_8_6_12_3_9_5_11:
|
2017-03-23 19:01:10 -07:00
|
|
|
return 12;
|
|
|
|
|
|
|
|
default:
|
|
|
|
warning(CUSTOM_OBD_UNKNOWN_FIRING_ORDER, "getCylinderId not supported for %d", CONFIG(specs.firingOrder));
|
|
|
|
}
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2015-07-10 06:01:56 -07:00
|
|
|
/**
|
|
|
|
* @param index from zero to cylindersCount - 1
|
|
|
|
* @return cylinderId from one to cylindersCount
|
|
|
|
*/
|
2017-05-15 20:28:49 -07:00
|
|
|
int getCylinderId(int index DECLARE_ENGINE_PARAMETER_SUFFIX) {
|
2017-03-23 19:01:10 -07:00
|
|
|
|
2017-05-15 20:28:49 -07:00
|
|
|
const int foLength = getFiringOrderLength(PASS_ENGINE_PARAMETER_SIGNATURE);
|
2017-03-23 19:01:10 -07:00
|
|
|
if (engineConfiguration->specs.cylindersCount != foLength) {
|
|
|
|
warning(CUSTOM_OBD_WRONG_FIRING_ORDER, "Wrong firing order %d/%d", engineConfiguration->specs.cylindersCount, foLength);
|
|
|
|
return 1;
|
|
|
|
}
|
2015-07-10 06:01:56 -07:00
|
|
|
|
2017-03-23 19:01:10 -07:00
|
|
|
switch (CONFIG(specs.firingOrder)) {
|
2016-07-20 16:04:27 -07:00
|
|
|
case FO_1:
|
2015-07-10 06:01:56 -07:00
|
|
|
return 1;
|
2015-09-10 16:01:32 -07:00
|
|
|
// 2 cylinder
|
2016-07-20 16:04:27 -07:00
|
|
|
case FO_1_2:
|
2015-07-10 06:01:56 -07:00
|
|
|
return order_1_2[index];
|
2015-09-10 16:01:32 -07:00
|
|
|
// 3 cylinder
|
|
|
|
case FO_1_2_3:
|
|
|
|
return order_1_2_3[index];
|
2015-07-10 06:01:56 -07:00
|
|
|
// 4 cylinder
|
2016-07-20 16:04:27 -07:00
|
|
|
case FO_1_3_4_2:
|
2015-07-10 06:01:56 -07:00
|
|
|
return order_1_THEN_3_THEN_4_THEN2[index];
|
2016-07-20 16:04:27 -07:00
|
|
|
case FO_1_2_4_3:
|
2015-07-10 06:01:56 -07:00
|
|
|
return order_1_THEN_2_THEN_4_THEN3[index];
|
2016-07-20 16:04:27 -07:00
|
|
|
case FO_1_3_2_4:
|
2015-07-10 06:01:56 -07:00
|
|
|
return order_1_THEN_3_THEN_2_THEN4[index];
|
|
|
|
// 5 cylinder
|
|
|
|
case FO_1_2_4_5_3:
|
|
|
|
return order_1_2_4_5_3[index];
|
|
|
|
|
|
|
|
// 6 cylinder
|
2016-07-20 16:04:27 -07:00
|
|
|
case FO_1_5_3_6_2_4:
|
2015-07-10 06:01:56 -07:00
|
|
|
return order_1_THEN_5_THEN_3_THEN_6_THEN_2_THEN_4[index];
|
2016-07-20 16:04:27 -07:00
|
|
|
case FO_1_4_2_5_3_6:
|
2015-07-10 06:01:56 -07:00
|
|
|
return order_1_THEN_4_THEN_2_THEN_5_THEN_3_THEN_6[index];
|
2016-07-20 16:04:27 -07:00
|
|
|
case FO_1_2_3_4_5_6:
|
2015-09-05 20:02:46 -07:00
|
|
|
return order_1_THEN_2_THEN_3_THEN_4_THEN_5_THEN_6[index];
|
2017-01-12 21:03:30 -08:00
|
|
|
case FO_1_6_3_2_5_4:
|
|
|
|
return order_1_6_3_2_5_4[index];
|
2015-09-05 20:02:46 -07:00
|
|
|
|
2015-07-10 06:01:56 -07:00
|
|
|
// 8 cylinder
|
|
|
|
case FO_1_8_4_3_6_5_7_2:
|
|
|
|
return order_1_8_4_3_6_5_7_2[index];
|
2016-05-22 10:07:12 -07:00
|
|
|
case FO_1_8_7_2_6_5_4_3:
|
|
|
|
return order_1_8_7_2_6_5_4_3[index];
|
2016-06-05 17:03:16 -07:00
|
|
|
case FO_1_5_4_2_6_3_7_8:
|
|
|
|
return order_1_5_4_2_6_3_7_8[index];
|
2015-07-10 06:01:56 -07:00
|
|
|
|
2017-02-07 22:03:36 -08:00
|
|
|
// 10 cylinder
|
|
|
|
case FO_1_10_9_4_3_6_5_8_7_2:
|
|
|
|
return order_1_10_9_4_3_6_5_8_7_2[index];
|
|
|
|
|
|
|
|
// 12 cylinder
|
|
|
|
case FO_1_7_5_11_3_9_6_12_2_8_4_10:
|
|
|
|
return order_1_7_5_11_3_9_6_12_2_8_4_10[index];
|
2017-06-08 17:35:42 -07:00
|
|
|
case FO_1_7_4_10_2_8_6_12_3_9_5_11:
|
|
|
|
return order_1_7_4_10_2_8_6_12_3_9_5_11[index];
|
2017-02-07 22:03:36 -08:00
|
|
|
|
2015-07-10 06:01:56 -07:00
|
|
|
default:
|
2017-03-23 19:01:10 -07:00
|
|
|
warning(CUSTOM_OBD_UNKNOWN_FIRING_ORDER, "getCylinderId not supported for %d", CONFIG(specs.firingOrder));
|
2015-07-10 06:01:56 -07:00
|
|
|
}
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
2017-05-15 20:28:49 -07:00
|
|
|
static int getIgnitionPinForIndex(int i DECLARE_ENGINE_PARAMETER_SUFFIX) {
|
2015-07-10 06:01:56 -07:00
|
|
|
switch (CONFIG(ignitionMode)) {
|
|
|
|
case IM_ONE_COIL:
|
2016-01-16 14:02:38 -08:00
|
|
|
return 0;
|
2015-07-10 06:01:56 -07:00
|
|
|
break;
|
|
|
|
case IM_WASTED_SPARK: {
|
2016-01-16 14:02:38 -08:00
|
|
|
return i % (CONFIG(specs.cylindersCount) / 2);
|
2015-07-10 06:01:56 -07:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
case IM_INDIVIDUAL_COILS:
|
2016-01-16 14:02:38 -08:00
|
|
|
return i;
|
2015-07-10 06:01:56 -07:00
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
2017-04-19 19:03:14 -07:00
|
|
|
warning(CUSTOM_OBD_IGNITION_MODE, "unsupported ignitionMode %d in initializeIgnitionActions()", engineConfiguration->ignitionMode);
|
2016-01-16 14:02:38 -08:00
|
|
|
return 0;
|
2015-07-10 06:01:56 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-05-15 20:28:49 -07:00
|
|
|
void TriggerShape::prepareShape(DECLARE_ENGINE_PARAMETER_SIGNATURE) {
|
2016-12-18 20:01:40 -08:00
|
|
|
int engineCycleInt = (int) getEngineCycle(CONFIG(operationMode));
|
|
|
|
for (int angle = 0; angle < engineCycleInt; angle++) {
|
2017-05-15 20:28:49 -07:00
|
|
|
int triggerShapeIndex = findAngleIndex(angle PASS_ENGINE_PARAMETER_SUFFIX);
|
2017-03-03 20:49:05 -08:00
|
|
|
if (engineConfiguration->useOnlyRisingEdgeForTrigger) {
|
|
|
|
// we need even index for front_only mode - so if odd indexes are rounded down
|
|
|
|
triggerShapeIndex = triggerShapeIndex & 0xFFFFFFFE;
|
|
|
|
}
|
2016-12-18 20:01:40 -08:00
|
|
|
triggerIndexByAngle[angle] = triggerShapeIndex;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-07-10 06:01:56 -07:00
|
|
|
#if EFI_ENGINE_CONTROL || defined(__DOXYGEN__)
|
|
|
|
|
2016-01-14 21:01:42 -08:00
|
|
|
/**
|
|
|
|
* This heavy method is only invoked in case of a configuration change or initialization.
|
|
|
|
*/
|
2017-05-15 20:28:49 -07:00
|
|
|
void prepareOutputSignals(DECLARE_ENGINE_PARAMETER_SIGNATURE) {
|
2016-01-14 21:01:42 -08:00
|
|
|
ENGINE(engineCycle) = getEngineCycle(CONFIG(operationMode));
|
2015-07-10 06:01:56 -07:00
|
|
|
|
2016-11-11 16:02:29 -08:00
|
|
|
angle_t maxTimingCorrMap = -720.0f;
|
|
|
|
angle_t maxTimingMap = -720.0f;
|
|
|
|
for (int rpmIndex = 0;rpmIndex<IGN_RPM_COUNT;rpmIndex++) {
|
|
|
|
for (int l = 0;l<IGN_LOAD_COUNT;l++) {
|
|
|
|
maxTimingCorrMap = maxF(maxTimingCorrMap, config->ignitionIatCorrTable[l][rpmIndex]);
|
|
|
|
maxTimingMap = maxF(maxTimingMap, config->ignitionTable[l][rpmIndex]);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-10-31 19:02:12 -07:00
|
|
|
#if EFI_UNIT_TEST
|
2016-11-07 20:01:47 -08:00
|
|
|
printf("prepareOutputSignals %d onlyEdge=%s %s\r\n", engineConfiguration->trigger.type, boolToString(engineConfiguration->useOnlyRisingEdgeForTrigger),
|
|
|
|
getIgnition_mode_e(engineConfiguration->ignitionMode));
|
2016-10-31 19:02:12 -07:00
|
|
|
#endif
|
|
|
|
|
2015-07-10 06:01:56 -07:00
|
|
|
for (int i = 0; i < CONFIG(specs.cylindersCount); i++) {
|
2016-01-14 21:01:42 -08:00
|
|
|
ENGINE(angleExtra[i])= ENGINE(engineCycle) * i / CONFIG(specs.cylindersCount);
|
2017-05-15 20:28:49 -07:00
|
|
|
ENGINE(ignitionPin[i]) = getIgnitionPinForIndex(i PASS_ENGINE_PARAMETER_SUFFIX);
|
2015-07-10 06:01:56 -07:00
|
|
|
}
|
|
|
|
|
2017-05-15 20:28:49 -07:00
|
|
|
TRIGGER_SHAPE(prepareShape(PASS_ENGINE_PARAMETER_SIGNATURE));
|
2015-07-10 06:01:56 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
#endif
|
|
|
|
|
2017-05-15 20:28:49 -07:00
|
|
|
void setFuelRpmBin(float from, float to DECLARE_ENGINE_PARAMETER_SUFFIX) {
|
2015-07-10 06:01:56 -07:00
|
|
|
setTableBin(config->fuelRpmBins, FUEL_RPM_COUNT, from, to);
|
|
|
|
}
|
|
|
|
|
2017-05-15 20:28:49 -07:00
|
|
|
void setFuelLoadBin(float from, float to DECLARE_ENGINE_PARAMETER_SUFFIX) {
|
2015-07-10 06:01:56 -07:00
|
|
|
setTableBin(config->fuelLoadBins, FUEL_LOAD_COUNT, from, to);
|
|
|
|
}
|
|
|
|
|
2017-05-15 20:28:49 -07:00
|
|
|
void setTimingRpmBin(float from, float to DECLARE_ENGINE_PARAMETER_SUFFIX) {
|
2016-01-21 19:01:31 -08:00
|
|
|
setRpmBin(config->ignitionRpmBins, IGN_RPM_COUNT, from, to);
|
2015-07-10 06:01:56 -07:00
|
|
|
}
|
|
|
|
|
2017-05-15 20:28:49 -07:00
|
|
|
void setTimingLoadBin(float from, float to DECLARE_ENGINE_PARAMETER_SUFFIX) {
|
2015-07-10 06:01:56 -07:00
|
|
|
setTableBin(config->ignitionLoadBins, IGN_LOAD_COUNT, from, to);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* this method sets algorithm and ignition table scale
|
|
|
|
*/
|
2017-05-15 20:28:49 -07:00
|
|
|
void setAlgorithm(engine_load_mode_e algo DECLARE_ENGINE_PARAMETER_SUFFIX) {
|
2016-08-28 13:02:34 -07:00
|
|
|
engineConfiguration->fuelAlgorithm = algo;
|
2015-07-10 06:01:56 -07:00
|
|
|
if (algo == LM_ALPHA_N) {
|
2017-05-15 20:28:49 -07:00
|
|
|
setTimingLoadBin(20, 120 PASS_ENGINE_PARAMETER_SUFFIX);
|
2015-07-10 06:01:56 -07:00
|
|
|
} else if (algo == LM_SPEED_DENSITY) {
|
2018-01-07 08:17:49 -08:00
|
|
|
setLinearCurve(config->ignitionLoadBins, IGN_LOAD_COUNT, 20, 120, 3);
|
2017-05-15 20:28:49 -07:00
|
|
|
buildTimingMap(35 PASS_ENGINE_PARAMETER_SUFFIX);
|
2015-07-10 06:01:56 -07:00
|
|
|
}
|
|
|
|
}
|
2016-07-24 20:02:52 -07:00
|
|
|
|
2017-06-11 11:40:29 -07:00
|
|
|
void setFlatInjectorLag(float value DECLARE_ENGINE_PARAMETER_SUFFIX) {
|
2016-08-28 13:02:34 -07:00
|
|
|
setArrayValues(engineConfiguration->injector.battLagCorr, VBAT_INJECTOR_CURVE_SIZE, value);
|
2016-07-24 20:02:52 -07:00
|
|
|
}
|
2017-03-19 14:54:03 -07:00
|
|
|
|
2017-06-11 11:59:03 -07:00
|
|
|
// todo: make this a macro
|
2017-05-15 20:28:49 -07:00
|
|
|
void assertEngineReference(DECLARE_ENGINE_PARAMETER_SIGNATURE) {
|
2017-03-19 14:54:03 -07:00
|
|
|
efiAssertVoid(engine != NULL, "engine is NULL");
|
|
|
|
}
|