/** * Use core variables to lookup and calculate the derived variables. This * function uses the core variables to lookup and calculate further second * order variables such as load, VE, Lamdda, Transient fuel correction, engine * temperature enrichment, Injector dead time, etc. */ void generateDerivedVars(){ extern unsigned long RPMWeighted; extern KeyUserDebug KeyUserDebugs; extern const volatile fixedConfig1 fixedConfigs1; extern const volatile fixedConfig2 fixedConfigs2; /* Determine load based on options */ if(!(fixedConfigs2.algorithmSettings.loadType)){ /* Use MAP as load */ DerivedVars->LoadMain = CoreVars->MAP; }else if(fixedConfigs2.algorithmSettings.loadType == LOAD_TPS){ /* Use TPS as load */ DerivedVars->LoadMain = CoreVars->TPS; }else if(fixedConfigs2.algorithmSettings.loadType == LOAD_AAP){ /* Use AAP corrected MAP as load */ DerivedVars->LoadMain = ((unsigned long)CoreVars->MAP * CoreVars->AAP) / KPA(100); // TODO add maf calc load option here }else if(fixedConfigs2.algorithmSettings.algorithmType == ALGO_MAF){ //For now keep Load based on MAP for other uses, during the fuel calc algorithmType //will catch the MAF mode and use its reading DerivedVars->LoadMain = CoreVars->MAP; }else{ /* Default to MAP, but throw error */ DerivedVars->LoadMain = CoreVars->MAP; } // Back calculate KPA using a MAF sensor. Air Flow is in CC/min, looked up from a 2d table based on the signal from the MAF sensor. if(fixedConfigs2.algorithmSettings.algorithmType == ALGO_MAF){ unsigned long collectiveFlow = lookupTwoDTableUSV((twoDTableUSMAF*)&TablesC.SmallTablesC.MAFVersusVoltageTable, CoreVars->MAF, TWODTABLEUS_MAF_LENGTH); collectiveFlow <<= 16; unsigned long cylFill = ((collectiveFlow / fixedConfigs1.engineSettings.cylinderCount) / ((RPMWeighted / RPM_FACTOR) / (fixedConfigs1.engineSettings.strokesPerCycle / 2))); KeyUserDebugs.zsp9 = (cylFill * CYLINDER_FLOW_FACTOR) / fixedConfigs1.engineSettings.perCylinderVolume; //Calculated KPA //KeyUserDebugs.zsp8 = lookupTwoDTableUSV((twoDTableUSMAF*)&TablesC.SmallTablesC.MAFVersusVoltageTable, CoreVars->MAF, TWODTABLEUS_MAF_LENGTH); } /* Look up target Lambda with RPM and Load */ //#if CONFIG == SEANKR1_ID //FIXME temp hack until a real lambda table is built // DerivedVars->Lambda = LR(0.80); //From what I read E85 makes best power in the low .8s //#else DerivedVars->Lambda = lookupMainTable(CoreVars->RPM, DerivedVars->LoadMain, LAMBDA_TABLE_LOCATION_ID); //#endif /* Look up injector dead time with battery voltage */ DerivedVars->IDT = lookupTwoDTableUS((twoDTableUS*)&TablesA.SmallTablesA.injectorDeadTimeTable, CoreVars->BRV); if(!(fixedConfigs2.algorithmSettings.dwellType)){ DerivedVars->Dwell = lookupTwoDTableUS((twoDTableUS*)&TablesA.SmallTablesA.dwellDesiredVersusVoltageTable, CoreVars->BRV); }else if(fixedConfigs2.algorithmSettings.dwellType == DWELL_RPM){ DerivedVars->Dwell = lookupTwoDTableUS((twoDTableUS*)&TablesA.SmallTablesA.dwellVersusRPMTable, CoreVars->RPM); }else if(fixedConfigs2.algorithmSettings.dwellType == DWELL_FIXED){ DerivedVars->Dwell = fixedConfigs2.algorithmSettings.dwellFixedPeriod; }else{ DerivedVars->Dwell = 0; } unsigned long tempAdvance = ANGLE_FACTOR * (unsigned long)lookupMainTable(CoreVars->RPM, DerivedVars->LoadMain, IGNITION_ADVANCE_TABLE_MAIN_LOCATION_ID); DerivedVars->Advance = (unsigned short)(tempAdvance / 1024); // This calculation will change when the timing tables get shrunk to a more reasonable 8 bit size with appropriate scaling // Move this magic number to an appropriate place and/or refactor timing calcs/values/etc /// @todo TODO make generic!!!! // to go generic we need: // angle between ignition events (if have tpd) (or total angle and number of events) // max % dwell // minimum spark time // a setting to choose which behaviour (don't limit/% dwell limit/min spark time/other?) #if CONFIG == HOTEL_ID /// @bug hack for hyundai! 135 = 3/4 of 180 = one cycle... unsigned long threeQuartersOfAvailableTime = ((unsigned long)CoreVars->DRPM * 135 * ANGLE_FACTOR) / TICKS_PER_DEGREE_MULTIPLIER; if(DerivedVars->Dwell > threeQuartersOfAvailableTime){ DerivedVars->Dwell = threeQuartersOfAvailableTime; } #endif /* Look up the engine temperature enrichment percentage with temperature */ DerivedVars->ETE = lookupTwoDTableUS((twoDTableUS*)&TablesA.SmallTablesA.engineTempEnrichmentTablePercent, CoreVars->CHT); /* TODO The above needs some careful thought put into it around different loads and correction effects. */ /* Calculate the Transient Fuel Correction */ if(TRUE /*WWTFC*/){ /* Do ONLY WW correction if enabled */ // Do ww stuff, maybe pre done via RTC/RTI for consistent period? DerivedVars->TFCTotal = 0; /* TODO replace with real code */ }else if(FALSE /*STDTFC*/){ /* Do any combination of standard approximate methods */ /* Initialse the variable as a base */ DerivedVars->TFCTotal = 0; /* Based on the rate of change of MAP and some history/taper time */ if(FALSE /*MAPTFC*/){ // Do MAP based DerivedVars->TFCTotal += 0; } /* Based on the rate of change of TPS and some history/taper time */ if(FALSE /*TPSTFC*/){ // Do TPS based DerivedVars->TFCTotal += 0; } /* Based on the rate of change of RPM and some history/taper time */ if(FALSE /*RPMTFC*/){ // Do RPM based DerivedVars->TFCTotal += 0; } }else{ /* Default to no correction */ DerivedVars->TFCTotal = 0; /* Don't throw error as correction may not be required */ } }
/** @brief Generate the core variables and average them. * * Each raw ADC value is converted to a usable measurement via a variety of * methods chosen at runtime by configured settings. Once in their native units * and therefore closer to maximal use of the available data range they are * all averaged. * * @todo TODO incorporate averaging code, right now its a straight copy. * @todo TODO change the way configuration is done and make sure the most common options are after the first if(). * @todo TODO add actual configuration options to the fixed config blocks for these items. * * @author Fred Cooke */ void generateCoreVars(){ /*&&&&&&&& Calculate and obtain the basic variables with which we will perform the calculations &&&&&&&&*/ /* Pre calculate things used in multiple places */ /* Bound the TPS ADC reading and shift it to start at zero */ unsigned short unboundedTPSADC = ADCBuffers->TPS; if(unboundedTPSADC > fixedConfigs2.sensorRanges.TPSMaximumADC){ boundedTPSADC = TPSADCRange; }else if(unboundedTPSADC > fixedConfigs2.sensorRanges.TPSMinimumADC){ // force secondary config to be used... TODO remove this boundedTPSADC = unboundedTPSADC - fixedConfigs2.sensorRanges.TPSMinimumADC; }else{ boundedTPSADC = 0; } /* Get BRV from ADC using transfer variables (all installations need this) */ unsigned short localBRV; if(TRUE){ /* If BRV connected */ /// @todo TODO WARNING: HACK!!! Remove ASAP!!! IE, As Soon As Preston (get's a new cpu on the TA card!) #ifdef HOTEL localBRV = (((unsigned long)ADCBuffers->MAT * fixedConfigs2.sensorRanges.BRVRange) / ADC_DIVISIONS) + fixedConfigs2.sensorRanges.BRVMinimum; #else localBRV = (((unsigned long)ADCBuffers->BRV * fixedConfigs2.sensorRanges.BRVRange) / ADC_DIVISIONS) + fixedConfigs2.sensorRanges.BRVMinimum; #endif }else if(FALSE){ /* Configured to be fixed value */ /* Get the preferred BRV figure from configuration settings */ localBRV = fixedConfigs2.sensorPresets.presetBRV; }else{ /* Fail safe if config is broken */ /* Default to normal alternator charging voltage 14.4V */ localBRV = VOLTS(14.4); /* If anyone is listening, let them know something is wrong */ sendErrorIfClear(BRV_NOT_CONFIGURED_CODE); } unsigned short localCHT; /* Get CHT from ADC using the transfer table (all installations need this) */ if(TRUE){ /* If CHT connected */ localCHT = CHTTransferTable[ADCBuffers->CHT]; }else if(FALSE){ /* Configured to be read From ADC as dashpot */ /* Transfer the ADC reading to an engine temperature in a reasonable way */ localCHT = (ADCBuffers->CHT * 10) + DEGREES_C(0); /* 0 ADC = 0C = 273.15K = 27315, 1023 ADC = 102.3C = 375.45K = 37545 */ }else if(FALSE){ /* Configured to be fixed value */ /* Get the preferred CHT figure from configuration settings */ localCHT = fixedConfigs2.sensorPresets.presetCHT; }else{ /* Fail safe if config is broken */ /* Default to normal running temperature of 85C/358K */ localCHT = DEGREES_C(85); /* If anyone is listening, let them know something is wrong */ sendErrorIfClear(CHT_NOT_CONFIGURED_CODE); } unsigned short localIAT; /* Get IAT from ADC using the transfer table (all installations need this) */ if(TRUE){ /* If IAT connected */ /* using false here causes iat to default to room temp, useful with heatsoaked OEM sensors like the Volvo's... */ localIAT = IATTransferTable[ADCBuffers->IAT]; }else if(FALSE){ /* Configured to be read From ADC as dashpot */ /* Transfer the ADC reading to an air temperature in a reasonable way */ localIAT = (ADCBuffers->IAT * 10) + 27315; /* 0 ADC = 0C = 273.15K = 27315, 1023 ADC = 102.3C = 375.45K = 37545 */ }else if(FALSE){ /* Configured to be fixed value */ /* Get the preferred IAT figure from configuration settings */ localIAT = fixedConfigs2.sensorPresets.presetIAT; }else{ /* Fail safe if config is broken */ /* Default to room temperature (20C/293K) TODO poor choice, fix. */ localIAT = DEGREES_C(20); /* If anyone is listening, let them know something is wrong */ sendErrorIfClear(IAT_NOT_CONFIGURED_CODE); } unsigned short localMAT; /* Determine the MAT reading for future calculations */ if(TRUE){ /* If MAT sensor is connected */ /* Get MAT from ADC using same transfer table as IAT (too much space to waste on having two) */ localMAT = IATTransferTable[ADCBuffers->MAT]; }else if(FALSE){ /* Configured to be fixed value */ /* Get the preferred MAT figure from configuration settings */ localMAT = fixedConfigs2.sensorPresets.presetMAT; }else{ /* Fail safe if config is broken */ /* If not, default to same value as IAT */ localMAT = localIAT; /* If anyone is listening, let them know something is wrong */ sendErrorIfClear(MAT_NOT_CONFIGURED_CODE); } unsigned short localMAP; unsigned short localIAP; /* Determine the MAP pressure to use for future calculations */ if(TRUE){ /* If MAP sensor is connected */ /* get MAP from ADC using transfer variables */ localMAP = (((unsigned long)ADCBuffers->MAP * fixedConfigs2.sensorRanges.MAPRange) / ADC_DIVISIONS) + fixedConfigs2.sensorRanges.MAPMinimum; if(TRUE){ /* If Intercooler boost sensor connected */ /* Get IAP from ADC using the same transfer variables as they both need to read the same range */ localIAP = (((unsigned long)ADCBuffers->IAP * fixedConfigs2.sensorRanges.MAPRange) / ADC_DIVISIONS) + fixedConfigs2.sensorRanges.MAPMinimum; } }else if(FALSE){ /* Configured for MAP to imitate TPS signal */ /* Get MAP from TPS via conversion */ localMAP = (((unsigned long)boundedTPSADC * TPSMAPRange) / TPSADCRange) + fixedConfigs2.sensorRanges.TPSClosedMAP; }else if(FALSE){ /* Configured for dash potentiometer on ADC */ /* Get MAP from ADC via conversion to internal kPa figure where 1023ADC = 655kPa */ localMAP = ADCBuffers->MAP << 6; if(TRUE){ /* If Intercooler boost sensor enabled */ /* Get IAP from ADC via conversion to internal kPa figure where 1023ADC = 655kPa */ localIAP = ADCBuffers->IAP << 6; } }else if(FALSE){ /* Configured for fixed MAP from config */ /* Get the preferred MAP figure from configuration settings */ localMAP = fixedConfigs2.sensorPresets.presetMAP; }else{ /* Fail safe if config is broken */ /* Default to zero to nulify all other calcs and effectively cut fuel */ localMAP = 0; /* If anyone is listening, let them know something is wrong */ sendErrorIfClear(MAP_NOT_CONFIGURED_CODE); // or maybe queue it? } /* Determine MAF variable if required */ unsigned short localMAF = 0; // Default to zero as it is not required for anything except main PW calcs optionally if(TRUE){ localMAF = MAFTransferTable[ADCBuffers->MAF]; } unsigned short localAAP; /* Determine the Atmospheric pressure to use for future calculations */ if(TRUE){ /* Configured for second sensor to read AAP */ /* get AAP from ADC using separate vars to allow 115kPa sensor etc to be used */ localAAP = (((unsigned long)ADCBuffers->AAP * fixedConfigs2.sensorRanges.AAPRange) / ADC_DIVISIONS) + fixedConfigs2.sensorRanges.AAPMinimum; }else if(FALSE){ /* Configured for dash potentiometer on ADC */ /* Get AAP from ADC via conversion to internal kPa figure where 1023ADC = 102.3kPa */ localAAP = ADCBuffers->AAP * 10; }else if(FALSE){ /* Configured for fixed AAP from config */ /* Get the preferred AAP figure from configuration settings */ localAAP = fixedConfigs2.sensorPresets.presetAAP; }else{ /* Fail safe if config is broken */ /* Default to sea level */ localAAP = KPA(100); /* If anyone is listening, let them know something is wrong */ sendErrorIfClear(AAP_NOT_CONFIGURED_CODE); // or maybe queue it? } unsigned short localEGO; /* Get main Lambda reading */ if(TRUE){ /* If WBO2-1 is connected */ /* Get EGO from ADCs using transfer variables */ localEGO = (((unsigned long)ADCBuffers->EGO * fixedConfigs2.sensorRanges.EGORange) / ADC_DIVISIONS) + fixedConfigs2.sensorRanges.EGOMinimum; }else if(FALSE){ /* Configured for fixed EGO from config */ /* Get the preferred EGO figure from configuration settings */ localEGO = fixedConfigs2.sensorPresets.presetEGO; }else{ /* Default value if not connected incase other things are misconfigured */ /* Default to stoichiometric */ localEGO = LAMBDA(1.0); /* If anyone is listening, let them know something is wrong */ sendErrorIfClear(EGO_NOT_CONFIGURED_CODE); // or maybe queue it? } unsigned short localEGO2; /* Get second Lambda reading */ if(TRUE){ /* If WBO2-2 is connected */ /* Get EGO2 from ADCs using same transfer variables as EGO */ localEGO2 = (((unsigned long)ADCBuffers->EGO2 * fixedConfigs2.sensorRanges.EGORange) / ADC_DIVISIONS) + fixedConfigs2.sensorRanges.EGOMinimum; }else if(FALSE){ /* Configured for fixed EGO2 from config */ /* Get the preferred EGO2 figure from configuration settings */ localEGO2 = fixedConfigs2.sensorPresets.presetEGO2; }else{ /* Default value if not connected incase other things are misconfigured */ /* Default to stoichiometric */ localEGO2 = LAMBDA(1.0); /* If anyone is listening, let them know something is wrong */ sendErrorIfClear(EGO2_NOT_CONFIGURED_CODE); // or maybe queue it? } unsigned short localTPS; /* Get TPS percentage */ if(TRUE){ /* If TPS is connected */ /* Get TPS from ADC no need to add TPS min as we know it is zero by definition */ localTPS = ((unsigned long)boundedTPSADC * PERCENT(100)) / TPSADCRange; }else if(FALSE){ /* Configured for TPS to imitate MAP signal */ /* Get TPS from MAP via conversion */ /* Box MAP signal down */ if(localTPS > fixedConfigs2.sensorRanges.TPSOpenMAP){ /* Greater than ~95kPa */ localTPS = PERCENT(100); }else if(localTPS < fixedConfigs2.sensorRanges.TPSClosedMAP){ /* Less than ~30kPa */ localTPS = 0; }else{ /* Scale MAP range to TPS range */ localTPS = localMAP - fixedConfigs2.sensorRanges.TPSClosedMAP; } // get TPS from MAP no need to add TPS min as we know it is zero by definition localTPS = ((unsigned long)localTPS * PERCENT(100)) / (fixedConfigs2.sensorRanges.TPSOpenMAP - fixedConfigs2.sensorRanges.TPSClosedMAP); }else if(FALSE){ /* Configured for dash potentiometer on ADC */ /* Get TPS from ADC as shown : 1023 ADC = 100%, 0 ADC = 0% */ localTPS = ((unsigned long)ADCBuffers->TPS * PERCENT(100)) / ADC_DIVISIONS; }else if(FALSE){ /* Configured for fixed TPS from config */ /* Get the preferred TPS figure from configuration settings */ localTPS = fixedConfigs2.sensorPresets.presetTPS; }else{ /* Fail safe if config is broken */ /* Default to 50% to not trigger any WOT or CT conditions */ localTPS = PERCENT(50); // TODO YAGNI? /* If anyone is listening, let them know something is wrong */ sendErrorIfClear(TPS_NOT_CONFIGURED_CODE); // or maybe queue it? } /* Get RPM by locking out ISRs for a second and grabbing the Tooth logging data */ //atomic start // copy rpm data //atomic end // Calculate RPM and delta RPM and delta delta RPM from data recorded if(*ticksPerDegree != 0){ CoreVars->RPM = (unsigned short)(degreeTicksPerMinute / *ticksPerDegree); }else{ CoreVars->RPM = 0; } CoreVars->DRPM = *ticksPerDegree; // unsigned short localDRPM = 0; // unsigned short localDDRPM = 0; /*&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&*/ /*&&&&&&&&&&&&&&&&&&&&&&&&&&&& Average the variables as per the configuration &&&&&&&&&&&&&&&&&&&&&&&&&&*/ /* Strictly speaking only the primary variables need to be averaged. After that, the derived ones are */ /* already averaged in a way. However, there may be some advantage to some short term averaging on the */ /* derived ones also, so it is something to look into later. */ /// @todo TODO average the generated values here // newVal var word ' the value from the ADC // smoothed var word ' a nicely smoothed result // // if newval > smoothed then // smoothed = smoothed + (newval - smoothed)/alpha // else // smoothed = smoothed - (smoothed - newval)/alpha // endif // from : http://www.tigoe.net/pcomp/code/category/code/arduinowiring/41 // for now just copy them in. CoreVars->IAT = localIAT; CoreVars->CHT = localCHT; CoreVars->TPS = localTPS; CoreVars->EGO = localEGO; CoreVars->BRV = localBRV; CoreVars->MAP = localMAP; CoreVars->AAP = localAAP; CoreVars->MAT = localMAT; CoreVars->EGO2 = localEGO2; CoreVars->IAP = localIAP; CoreVars->MAF = localMAF; // CoreVars->DRPM = localDRPM; // CoreVars->DDRPM = localDDRPM; // CoreVars->DTPS = localDTPS; // later... unsigned short i; for(i=0;i<sizeof(CoreVar);i++){ // TODO /* Perform averaging on all primary variables as per the configuration array */ // get old value(s) // process new and old to produce result based on config array value */ // assign result to old value holder } // TODO /*&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&*/ }