rusefi-1/firmware/controllers/math/engine_math.cpp

357 lines
11 KiB
C++
Raw Normal View History

2014-08-29 07:52:33 -07:00
/**
* @file engine_math.cpp
* @brief
*
* @date Jul 13, 2013
* @author Andrey Belomutskiy, (c) 2012-2014
*
* 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 "io_pins.h"
#include "OutputSignalList.h"
#include "trigger_decoder.h"
#include "event_registry.h"
2014-09-29 17:02:57 -07:00
#include "efiGpio.h"
2014-08-29 07:52:33 -07:00
2014-11-24 09:03:09 -08:00
EXTERN_ENGINE
;
2014-11-07 21:07:22 -08:00
2014-11-25 09:05:03 -08:00
extern OutputPin outputs[IO_PIN_COUNT];
2014-08-29 07:52:33 -07:00
/*
* default Volumetric Efficiency
*/
//float getDefaultVE(int rpm) {
// if (rpm > 5000)
// return interpolate(5000, 1.1, 8000, 1, rpm);
// return interpolate(500, 0.5, 5000, 1.1, rpm);
//}
/**
* @return number of milliseconds in one crankshaft revolution
*/
float getCrankshaftRevolutionTimeMs(int rpm) {
return 360 * getOneDegreeTimeMs(rpm);
}
/**
* @brief Returns engine load according to selected engine_load_mode
*
*/
2014-11-24 18:03:34 -08:00
float getEngineLoadT(DECLARE_ENGINE_PARAMETER_F) {
2014-08-29 07:52:33 -07:00
efiAssert(engine!=NULL, "engine 2NULL", NAN);
efiAssert(engineConfiguration!=NULL, "engineConfiguration 2NULL", NAN);
switch (engineConfiguration->algorithm) {
case LM_MAF:
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();
2014-09-28 13:05:28 -07:00
case LM_ALPHA_N:
2014-11-24 19:03:19 -08:00
return getTPS(PASS_ENGINE_PARAMETER_F);
2014-08-29 07:52:33 -07:00
default:
firmwareError("Unexpected engine load parameter: %d", engineConfiguration->algorithm);
return -1;
}
}
void setSingleCoilDwell(engine_configuration_s *engineConfiguration) {
for (int i = 0; i < DWELL_CURVE_SIZE; i++) {
engineConfiguration->sparkDwellBins[i] = 0;
engineConfiguration->sparkDwell[i] = -1;
}
engineConfiguration->sparkDwellBins[5] = 1;
engineConfiguration->sparkDwell[5] = 4;
engineConfiguration->sparkDwellBins[6] = 4500;
engineConfiguration->sparkDwell[6] = 4;
engineConfiguration->sparkDwellBins[7] = 12500;
engineConfiguration->sparkDwell[7] = 0;
}
2014-11-22 09:03:10 -08:00
OutputSignalList injectonSignals CCM_OPTIONAL;
2014-08-29 07:52:33 -07:00
2014-11-25 17:03:21 -08:00
void initializeIgnitionActions(float advance, float dwellAngle, IgnitionEventList *list DECLARE_ENGINE_PARAMETER_S) {
2014-08-29 07:52:33 -07:00
efiAssertVoid(engineConfiguration->cylindersCount > 0, "cylindersCount");
list->resetEventList();
2014-11-25 17:03:21 -08:00
for (int i = 0; i < CONFIG(cylindersCount); i++) {
float localAdvance = advance + ENGINE(angleExtra[i]);
io_pin_e pin = ENGINE(ignitionPin[i]);
2014-08-29 07:52:33 -07:00
2014-11-25 17:03:21 -08:00
// todo efiAssertVoid(list->size)
IgnitionEvent *event = &list->events[list->size++];
2014-08-29 07:52:33 -07:00
2014-11-25 17:03:21 -08:00
if (!isPinAssigned(pin)) {
// todo: extact method for this index math
warning(OBD_PCM_Processor_Fault, "no_pin_cl #%d", (int) pin - (int) SPARKOUT_1_OUTPUT + 1);
2014-08-29 07:52:33 -07:00
}
2014-11-25 17:03:21 -08:00
event->io_pin = pin;
event->advance = localAdvance;
2014-08-29 07:52:33 -07:00
2014-11-25 17:03:21 -08:00
findTriggerPosition(&event->dwellPosition, localAdvance - dwellAngle
PASS_ENGINE_PARAMETER);
2014-08-29 07:52:33 -07:00
}
}
2014-11-25 17:03:21 -08:00
void FuelSchedule::registerInjectionEvent(io_pin_e pin, float angle, bool_t isSimultanious DECLARE_ENGINE_PARAMETER_S) {
2014-11-07 09:03:10 -08:00
ActuatorEventList *list = &events;
2014-09-29 17:02:57 -07:00
2014-11-14 15:03:43 -08:00
if (!isSimultanious && !isPinAssigned(pin)) {
2014-10-03 13:04:07 -07:00
// todo: extact method for this index math
2014-11-10 07:03:20 -08:00
warning(OBD_PCM_Processor_Fault, "no_pin_inj #%d", (int) pin - (int) INJECTOR_1_OUTPUT + 1);
2014-09-29 17:02:57 -07:00
}
2014-11-12 06:03:14 -08:00
InjectionEvent *ev = list->getNextActuatorEvent();
2014-11-07 17:04:58 -08:00
OutputSignal *actuator = injectonSignals.add(pin);
2014-11-12 06:03:14 -08:00
ev->isSimultanious = isSimultanious;
2014-11-25 09:05:03 -08:00
efiAssertVoid(TRIGGER_SHAPE(getSize()) > 0, "uninitialized trigger_shape_s");
2014-11-07 17:04:58 -08:00
if (ev == NULL) {
// error already reported
return;
}
ev->actuator = actuator;
2014-11-25 09:05:03 -08:00
findTriggerPosition(&ev->position, angle PASS_ENGINE_PARAMETER);
2014-11-07 22:05:46 -08:00
hasEvents[ev->position.eventIndex] = true;
}
FuelSchedule::FuelSchedule() {
clear();
}
void FuelSchedule::clear() {
memset(hasEvents, 0, sizeof(hasEvents));
2014-08-29 07:52:33 -07:00
}
2014-11-25 10:03:01 -08:00
void FuelSchedule::addFuelEvents(injection_mode_e mode DECLARE_ENGINE_PARAMETER_S) {
2014-11-07 09:03:10 -08:00
ActuatorEventList *list = &events;
2014-11-24 09:03:09 -08:00
;
2014-08-29 07:52:33 -07:00
list->resetEventList();
2014-11-07 21:07:22 -08:00
float baseAngle = engineConfiguration->globalTriggerAngleOffset + engineConfiguration->injectionOffset;
2014-08-29 07:52:33 -07:00
switch (mode) {
case IM_SEQUENTIAL:
2014-11-07 21:07:22 -08:00
for (int i = 0; i < engineConfiguration->cylindersCount; i++) {
2014-11-12 06:03:14 -08:00
io_pin_e pin = INJECTOR_PIN_BY_INDEX(getCylinderId(engineConfiguration->firingOrder, i) - 1);
2014-11-24 09:03:09 -08:00
float angle = baseAngle
+ (float) engineConfiguration->engineCycle * i / engineConfiguration->cylindersCount;
2014-11-25 09:05:03 -08:00
registerInjectionEvent(pin, angle, false PASS_ENGINE_PARAMETER);
2014-08-29 07:52:33 -07:00
}
break;
case IM_SIMULTANEOUS:
2014-11-07 21:07:22 -08:00
for (int i = 0; i < engineConfiguration->cylindersCount; i++) {
2014-11-24 09:03:09 -08:00
float angle = baseAngle
+ (float) engineConfiguration->engineCycle * i / engineConfiguration->cylindersCount;
2014-08-29 07:52:33 -07:00
2014-11-12 06:03:14 -08:00
/**
* We do not need injector pin here because we will control all injectors
* simultaniously
*/
2014-11-25 09:05:03 -08:00
registerInjectionEvent(IO_INVALID, angle, true PASS_ENGINE_PARAMETER);
2014-08-29 07:52:33 -07:00
}
break;
case IM_BATCH:
2014-11-07 21:07:22 -08:00
for (int i = 0; i < engineConfiguration->cylindersCount; i++) {
int index = i % (engineConfiguration->cylindersCount / 2);
2014-11-12 06:03:14 -08:00
io_pin_e pin = INJECTOR_PIN_BY_INDEX(index);
2014-11-24 09:03:09 -08:00
float angle = baseAngle
+ i * (float) engineConfiguration->engineCycle / engineConfiguration->cylindersCount;
2014-11-25 09:05:03 -08:00
registerInjectionEvent(pin, angle, false PASS_ENGINE_PARAMETER);
2014-08-29 07:52:33 -07:00
/**
* also fire the 2nd half of the injectors so that we can implement a batch mode on individual wires
*/
2014-11-12 07:03:20 -08:00
pin = INJECTOR_PIN_BY_INDEX(index + (engineConfiguration->cylindersCount / 2));
2014-11-25 09:05:03 -08:00
registerInjectionEvent(pin, angle, false PASS_ENGINE_PARAMETER);
2014-08-29 07:52:33 -07:00
}
break;
default:
firmwareError("Unexpected injection mode %d", mode);
}
}
/**
* @return Spark dwell time, in milliseconds.
*/
2014-11-12 13:05:43 -08:00
float getSparkDwellMsT(int rpm DECLARE_ENGINE_PARAMETER_S) {
2014-08-29 07:52:33 -07:00
if (isCrankingR(rpm)) {
2014-11-24 09:03:09 -08:00
if (engineConfiguration->useConstantDwellDuringCranking) {
2014-11-06 07:06:08 -08:00
return engineConfiguration->ignitionDwellForCrankingMs;
} else {
2014-11-24 09:03:09 -08:00
// technically this could be implemented via interpolate2d
float angle = engineConfiguration->crankingChargeAngle;
return getOneDegreeTimeMs(rpm) * angle;
2014-11-06 07:06:08 -08:00
}
2014-08-29 07:52:33 -07:00
}
efiAssert(!cisnan(rpm), "invalid rpm", NAN);
return interpolate2d(rpm, engineConfiguration->sparkDwellBins, engineConfiguration->sparkDwell, DWELL_CURVE_SIZE);
}
2014-11-25 17:03:21 -08:00
static int findAngleIndex(float angleOffset DECLARE_ENGINE_PARAMETER_S) {
2014-11-25 09:05:03 -08:00
/**
* Here we rely on this to be pre-calculated, that's a performance optimization
*/
int engineCycleEventCount = engine->engineCycleEventCount;
2014-08-29 07:52:33 -07:00
2014-11-25 17:03:21 -08:00
efiAssert(engineCycleEventCount > 0, "engineCycleEventCount", 0);
2014-09-24 10:05:03 -07:00
2014-09-24 09:03:00 -07:00
uint32_t middle;
uint32_t left = 0;
uint32_t right = engineCycleEventCount - 1;
2014-08-29 07:52:33 -07:00
/**
* Let's find the last trigger angle which is less or equal to the desired angle
* todo: extract binary search as template method?
*/
while (true) {
middle = (left + right) / 2;
2014-11-25 17:03:21 -08:00
float eventAngle = TRIGGER_SHAPE(eventAngles[middle]);
2014-08-29 07:52:33 -07:00
if (middle == left) {
2014-11-25 17:03:21 -08:00
return middle;
2014-09-29 17:02:57 -07:00
}
2014-11-25 09:05:03 -08:00
if (angleOffset < eventAngle) {
2014-08-29 07:52:33 -07:00
right = middle;
2014-11-25 09:05:03 -08:00
} else if (angleOffset > eventAngle) {
2014-08-29 07:52:33 -07:00
left = middle;
} else {
2014-11-25 17:03:21 -08:00
return middle;
2014-08-29 07:52:33 -07:00
}
}
2014-11-25 17:03:21 -08:00
}
void findTriggerPosition(event_trigger_position_s *position, float angleOffset DECLARE_ENGINE_PARAMETER_S) {
angleOffset += CONFIG(globalTriggerAngleOffset);
fixAngle(angleOffset);
2014-08-29 07:52:33 -07:00
2014-11-25 17:03:21 -08:00
int index = findAngleIndex(angleOffset PASS_ENGINE_PARAMETER);
float eventAngle = TRIGGER_SHAPE(eventAngles[index]);
2014-08-29 07:52:33 -07:00
if (angleOffset < eventAngle) {
firmwareError("angle constraint violation in registerActuatorEventExt(): %f/%f", angleOffset, eventAngle);
return;
}
2014-11-25 17:03:21 -08:00
position->eventIndex = index;
2014-08-29 07:52:33 -07:00
position->eventAngle = eventAngle;
position->angleOffset = angleOffset - eventAngle;
}
static int order_1_THEN_3_THEN_4_THEN2[] = { 1, 3, 4, 2 };
static int order_1_THEN_5_THEN_3_THEN_6_THEN_2_THEN_4[] = { 1, 5, 3, 6, 2, 4 };
2014-09-29 17:02:57 -07:00
static int order_1_8_4_3_6_5_7_2[] = { 1, 8, 4, 3, 6, 5, 7, 2 };
2014-08-29 07:52:33 -07:00
/**
* @param index from zero to cylindersCount - 1
* @return cylinderId from one to cylindersCount
*/
int getCylinderId(firing_order_e firingOrder, int index) {
switch (firingOrder) {
case FO_ONE_CYLINDER:
return 1;
case FO_1_THEN_3_THEN_4_THEN2:
return order_1_THEN_3_THEN_4_THEN2[index];
case FO_1_THEN_5_THEN_3_THEN_6_THEN_2_THEN_4:
return order_1_THEN_5_THEN_3_THEN_6_THEN_2_THEN_4[index];
case FO_1_8_4_3_6_5_7_2:
return order_1_8_4_3_6_5_7_2[index];
default:
firmwareError("getCylinderId not supported for %d", firingOrder);
}
return -1;
}
2014-11-25 17:03:21 -08:00
io_pin_e getIgnitionPinForIndex(int i DECLARE_ENGINE_PARAMETER_S) {
switch (CONFIG(ignitionMode)) {
case IM_ONE_COIL:
return SPARKOUT_1_OUTPUT;
break;
case IM_WASTED_SPARK: {
int wastedIndex = i % (CONFIG(cylindersCount) / 2);
int id = getCylinderId(CONFIG(firingOrder), wastedIndex) - 1;
return (io_pin_e) (SPARKOUT_1_OUTPUT + id);
}
break;
case IM_INDIVIDUAL_COILS:
return (io_pin_e) ((int) SPARKOUT_1_OUTPUT + getCylinderId(CONFIG(firingOrder), i) - 1);
break;
default:
firmwareError("unsupported ignitionMode %d in initializeIgnitionActions()", engineConfiguration->ignitionMode);
return SPARKOUT_1_OUTPUT;
}
}
2014-11-25 10:03:01 -08:00
void prepareOutputSignals(DECLARE_ENGINE_PARAMETER_F) {
2014-10-31 13:03:07 -07:00
2014-11-24 09:03:09 -08:00
engine_configuration2_s *engineConfiguration2 = engine->engineConfiguration2;
2014-08-29 07:52:33 -07:00
// todo: move this reset into decoder
2014-11-24 13:03:32 -08:00
engine->triggerShape.calculateTriggerSynchPoint(engineConfiguration, engine);
2014-08-29 07:52:33 -07:00
2014-11-25 17:03:21 -08:00
for (int i = 0; i < CONFIG(cylindersCount); i++) {
ENGINE(angleExtra[i])= (float) CONFIG(engineCycle) * i / CONFIG(cylindersCount);
ENGINE(ignitionPin[i]) = getIgnitionPinForIndex(i PASS_ENGINE_PARAMETER);
}
2014-08-29 07:52:33 -07:00
injectonSignals.clear();
2014-11-25 17:03:21 -08:00
engineConfiguration2->crankingInjectionEvents.addFuelEvents(engineConfiguration->crankingInjectionMode
PASS_ENGINE_PARAMETER);
2014-11-25 10:03:01 -08:00
engineConfiguration2->injectionEvents.addFuelEvents(engineConfiguration->injectionMode PASS_ENGINE_PARAMETER);
2014-08-29 07:52:33 -07:00
}
void setFuelRpmBin(engine_configuration_s *engineConfiguration, float l, float r) {
setTableBin(engineConfiguration->fuelRpmBins, FUEL_RPM_COUNT, l, r);
}
void setFuelLoadBin(engine_configuration_s *engineConfiguration, float l, float r) {
setTableBin(engineConfiguration->fuelLoadBins, FUEL_LOAD_COUNT, l, r);
}
void setTimingRpmBin(engine_configuration_s *engineConfiguration, float l, float r) {
setTableBin(engineConfiguration->ignitionRpmBins, IGN_RPM_COUNT, l, r);
}
void setTimingLoadBin(engine_configuration_s *engineConfiguration, float l, float r) {
setTableBin(engineConfiguration->ignitionLoadBins, IGN_LOAD_COUNT, l, r);
}
int isInjectionEnabled(engine_configuration_s *engineConfiguration) {
// todo: is this worth a method? should this be inlined?
return engineConfiguration->isInjectionEnabled;
}