R18A1 decoder as tested on the WikiSpeed car in Seattle.
[freeems-vanilla.git] / src / main / fuelAndIgnitionCalcs.c
blobac39660a5e458dc702476773d723a99779cef8ef
1 /* FreeEMS - the open source engine management system
3 * Copyright 2008-2013 Fred Cooke
5 * This file is part of the FreeEMS project.
7 * FreeEMS software is free software: you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation, either version 3 of the License, or
10 * (at your option) any later version.
12 * FreeEMS software is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
17 * You should have received a copy of the GNU General Public License
18 * along with any FreeEMS software. If not, see http://www.gnu.org/licenses/
20 * We ask that if you make any changes to this file you email them upstream to
21 * us at admin(at)diyefi(dot)org or, even better, fork the code on github.com!
23 * Thank you for choosing FreeEMS to run your engine!
27 /** @file
29 * @ingroup measurementsAndCalculations
31 * @brief Fuel and ignition calculations.
33 * This file contains all of the main fuel and ignition calculations based
34 * upon the variables that we have already determined in previous stages.
38 #define FUELANDIGNITIONCALCS_C
39 #include "inc/freeEMS.h"
40 #include "inc/utils.h"
41 #include "inc/locationIDs.h"
42 #include "inc/tableLookup.h"
43 #include "inc/fuelAndIgnitionCalcs.h"
46 /**
47 * Final fuel and ignition calculations. Using a variety of primary algorithms
48 * calculate a base pulsewidth and then apply various corrections to it such as
49 * injector dead time, transient fuel correction, engine temperature enrichment
50 * and per cylinder trims. The ignition timing and fuel injection timing are
51 * also determined here, as are the various limiters and cuts.
53 void calculateFuelAndIgnition(){
54 unsigned short airInletTemp = CoreVars->IAT; /* All except MAF use this. */
55 /* Determine the type of air flow data */
56 if(!(fixedConfigs2.algorithmSettings.algorithmType)){
57 /* Look up VE with RPM and MAP */
58 DerivedVars->VEMain = lookupMainTable(CoreVars->RPM, CoreVars->MAP, VETableMainLocationID);
59 /* This won't overflow until 512kPa or about 60psi of boost with 128% VE. */
60 DerivedVars->AirFlow = ((unsigned long)CoreVars->MAP * DerivedVars->VEMain) / VE(100);
61 /* Result is 450 - 65535 always. */
62 }else if(fixedConfigs2.algorithmSettings.algorithmType == ALGO_ALPHA_N){
63 /* Look up Airflow with RPM and TPS */
64 DerivedVars->AirFlow = lookupMainTable(CoreVars->RPM, CoreVars->TPS, AirflowTableLocationID); /* Tuned air flow without density information */
65 }else if(fixedConfigs2.algorithmSettings.algorithmType == ALGO_MAF){
66 DerivedVars->AirFlow = CoreVars->MAF; /* Just fix temperature at appropriate level to provide correct Lambda */
67 /// @todo TODO figure out what the correct "temperature" is to make MAF work correctly!
68 airInletTemp = DEGREES_C(20); // Room temperature?
69 }else if(fixedConfigs2.algorithmSettings.algorithmType == ALGO_SD_AN_BLEND){
70 /* Look up VE with RPM and MAP */
71 DerivedVars->VEMain = lookupMainTable(CoreVars->RPM, CoreVars->MAP, VETableMainLocationID);
72 /* This won't overflow until 512kPa or about 60psi of boost with 128% VE. */
73 KeyUserDebugs.speedDensityAirFlow = ((unsigned long)CoreVars->MAP * DerivedVars->VEMain) / VE(100);
75 /* Look up Airflow with RPM and TPS */
76 KeyUserDebugs.alphaNAirFlow = lookupMainTable(CoreVars->RPM, CoreVars->TPS, AirflowTableLocationID); /* Tuned air flow without density information */
78 KeyUserDebugs.blendAlphaNPercent = lookupTwoDTableUS((twoDTableUS*)&TablesA.SmallTablesA.blendVersusRPMTable, CoreVars->RPM);
80 unsigned short airflowSD = safeScale(KeyUserDebugs.speedDensityAirFlow, SHORTMAX - KeyUserDebugs.blendAlphaNPercent, SHORTMAX);
81 unsigned short airflowAN = safeScale(KeyUserDebugs.alphaNAirFlow, KeyUserDebugs.blendAlphaNPercent, SHORTMAX);
83 DerivedVars->AirFlow = safeAdd(airflowSD, airflowAN);
84 }else{ /* Default to no fuel delivery and error */
85 DerivedVars->AirFlow = 0;
89 /* This won't overflow until well past 125C inlet, 1.5 Lambda and fuel as dense as water */
90 DerivedVars->densityAndFuel = (((unsigned long)((unsigned long)airInletTemp * DerivedVars->Lambda) / LAMBDA(1.0)) * fixedConfigs1.engineSettings.densityOfFuelAtSTP) / FUEL_DENSITY(FUEL_DENSITY_UNIT_FACTOR);
91 /* Result is 7500 - 60000 always. TODO clean up the last item on the above line */
93 /* Divisors for air inlet temp and pressure :
94 * #define airInletTempDivisor 100
95 * #define airPressureDivisor 100
96 * cancel each other out! all others are used. */
98 DerivedVars->BasePW = (bootFuelConst * DerivedVars->AirFlow) / DerivedVars->densityAndFuel;
100 /*&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&*/
105 /*&&&&&&&&&&&&&&&&&&&&&&&&&&&& Apply All Corrections PCFC, ETE, IDT, TFC etc &&&&&&&&&&&&&&&&&&&&&&&&&&&*/
107 /* Apply the corrections after calculating */
108 DerivedVars->EffectivePW = safeTrim(DerivedVars->BasePW, DerivedVars->TFCTotal);
109 DerivedVars->EffectivePW = safeScale(DerivedVars->EffectivePW, DerivedVars->ETE, SHORT4TH);
112 // unsigned char channel; // the declaration of this variable is used in multiple loops below.
114 // /* "Calculate" the individual fuel pulse widths */
115 // for(channel = 0; channel < INJECTION_CHANNELS; channel++){ /// @todo TODO make injector channels come from config, not defines.
116 // /* Add or subtract the per cylinder fuel trims */
117 // unsigned short channelPW;
118 // channelPW = safeScale(DerivedVars->EffectivePW, TablesB.SmallTablesB.perCylinderFuelTrims[channel]);
120 // /* Add on the IDT to get the final value and put it into the array */
121 // //outputEventPulseWidthsMath[channel] = safeAdd(channelPW, DerivedVars->IDT); do not re-enable this without fixing it properly...
122 // }
124 // Make sure we don't have a PW if PW is supposed to be zero, ie, zero the IDT as well.
125 if(!(DerivedVars->EffectivePW)){
126 DerivedVars->IDT = 0; // This also makes fuel and electrical duty work consistently in external apps.
129 /* Reference PW for comparisons etc */
130 DerivedVars->RefPW = safeAdd(DerivedVars->EffectivePW, DerivedVars->IDT);
131 /*&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&*/
133 /// @todo accumulate errors such that we know what sort of PW WOULD have been requested and enable a "over duty cut" to protect boosted users with insufficient injector size on cold nights
135 /// TODO @todo FIXME part of to schedule or not to schedule should be : (masterPulseWidth > injectorMinimumPulseWidth)
136 // IE, NOT in the decoders... KISS in the decoders. This is a hangover from (very) early decoder dev
138 // for(channel = 0;channel < INJECTION_CHANNELS;channel++){ /// @todo TODO make injector channels come from config, not defines.
139 //injectorMainAdvances[channel] = IDT blah blah.
140 // }
142 /* "Calculate" the nominal total pulse width before per channel corrections */
143 masterPulseWidth = safeAdd((DerivedVars->EffectivePW / fixedConfigs1.schedulingSettings.numberOfInjectionsPerEngineCycle), DerivedVars->IDT); // div by number of injections per cycle, configured above
144 // but requires to know how big a cycle is, 1/4 1, 1/2, etc
146 // Note, conversions to address and then pointer are necessary to avoid error on direct cast
147 // Cuts and limiters TODO move these to their own special place?
148 // TODO Make source of threshold either struct or temp based curve for these
150 if(fixedConfigs1.cutAndLimiterSettings.cutsEnabled.IgnitionRPM){
151 unsigned short confirmedReenableThreshold = fixedConfigs1.cutAndLimiterSettings.IgnitionRPM.reenableThreshold;
152 if(confirmedReenableThreshold >= fixedConfigs1.cutAndLimiterSettings.IgnitionRPM.disableThreshold){
153 confirmedReenableThreshold = fixedConfigs1.cutAndLimiterSettings.IgnitionRPM.disableThreshold / 2;
155 if(CoreVars->RPM > fixedConfigs1.cutAndLimiterSettings.IgnitionRPM.disableThreshold){
156 ((ignitionCutFlags *)&KeyUserDebugs.ignitionCuts)->IgnitionRPM = 1;
157 }else if(CoreVars->RPM < confirmedReenableThreshold){
158 ((ignitionCutFlags *)&KeyUserDebugs.ignitionCuts)->IgnitionRPM = 0;
162 if(fixedConfigs1.cutAndLimiterSettings.cutsEnabled.InjectionRPM){
163 unsigned short confirmedReenableThreshold = fixedConfigs1.cutAndLimiterSettings.InjectionRPM.reenableThreshold;
164 if(confirmedReenableThreshold >= fixedConfigs1.cutAndLimiterSettings.InjectionRPM.disableThreshold){
165 confirmedReenableThreshold = fixedConfigs1.cutAndLimiterSettings.InjectionRPM.disableThreshold / 2;
167 if(CoreVars->RPM > fixedConfigs1.cutAndLimiterSettings.InjectionRPM.disableThreshold){
168 ((injectionCutFlags *)&KeyUserDebugs.injectionCuts)->InjectionRPM = 1;
169 }else if(CoreVars->RPM < confirmedReenableThreshold){
170 ((injectionCutFlags *)&KeyUserDebugs.injectionCuts)->InjectionRPM = 0;
174 // TODO add time based lock out as well as threshold based as threshold could re-enable too quickly
175 if(fixedConfigs1.cutAndLimiterSettings.cutsEnabled.InjOverBoost || fixedConfigs1.cutAndLimiterSettings.cutsEnabled.IgnOverBoost){
176 unsigned short confirmedReenableThreshold = fixedConfigs1.cutAndLimiterSettings.OverBoost.reenableThreshold;
177 if(confirmedReenableThreshold >= fixedConfigs1.cutAndLimiterSettings.OverBoost.disableThreshold){
178 confirmedReenableThreshold = fixedConfigs1.cutAndLimiterSettings.OverBoost.disableThreshold / 2;
180 if(CoreVars->MAP > fixedConfigs1.cutAndLimiterSettings.OverBoost.disableThreshold){
181 ((injectionCutFlags *)&KeyUserDebugs.injectionCuts)->InjOverBoost = fixedConfigs1.cutAndLimiterSettings.cutsEnabled.InjOverBoost;
182 ((ignitionCutFlags *)&KeyUserDebugs.ignitionCuts)->IgnOverBoost = fixedConfigs1.cutAndLimiterSettings.cutsEnabled.IgnOverBoost;
183 }else if(CoreVars->MAP < confirmedReenableThreshold){
184 ((injectionCutFlags *)&KeyUserDebugs.injectionCuts)->InjOverBoost = 0;
185 ((ignitionCutFlags *)&KeyUserDebugs.ignitionCuts)->IgnOverBoost = 0;