void NamedOutputPin::setHigh() { #if EFI_DEFAILED_LOGGING || defined(__DOXYGEN__) // signal->hi_time = hTimeNow(); #endif /* EFI_DEFAILED_LOGGING */ // turn the output level ACTIVE setValue(true); // sleep for the needed duration #if EFI_ENGINE_SNIFFER || defined(__DOXYGEN__) // explicit check here is a performance optimization to speed up no-chart mode if (ENGINE(isEngineChartEnabled)) { // this is a performance optimization - array index is cheaper then invoking a method with 'switch' const char *pinName = name; // dbgDurr = hal_lld_get_counter_value() - dbgStart; addEngineSniffferEvent(pinName, WC_UP); } #endif /* EFI_ENGINE_SNIFFER */ // dbgDurr = hal_lld_get_counter_value() - dbgStart; }
void turnPinLow(NamedOutputPin *output) { efiAssertVoid(output!=NULL, "NULL turnPinLow"); #if EFI_GPIO || defined(__DOXYGEN__) // turn off the output doSetOutputPinValue2(output, false); #endif /* EFI_GPIO */ #if EFI_DEFAILED_LOGGING || defined(__DOXYGEN__) systime_t after = hTimeNow(); debugInt(&signal->logging, "a_time", after - signal->hi_time); scheduleLogging(&signal->logging); #endif /* EFI_DEFAILED_LOGGING */ #if EFI_ENGINE_SNIFFER || defined(__DOXYGEN__) if (ENGINE(isEngineChartEnabled)) { // this is a performance optimization - array index is cheaper then invoking a method with 'switch' const char *pinName = output->name; addEngineSniffferEvent(pinName, WC_DOWN); } #endif /* EFI_ENGINE_SNIFFER */ }
void turnPinHigh(NamedOutputPin *output) { efiAssertVoid(output!=NULL, "NULL @ turnPinHigh"); #if EFI_DEFAILED_LOGGING || defined(__DOXYGEN__) // signal->hi_time = hTimeNow(); #endif /* EFI_DEFAILED_LOGGING */ // turn the output level ACTIVE // todo: this XOR should go inside the setOutputPinValue method doSetOutputPinValue2(output, true); // sleep for the needed duration #if EFI_ENGINE_SNIFFER || defined(__DOXYGEN__) // explicit check here is a performance optimization to speed up no-chart mode if (ENGINE(isEngineChartEnabled)) { // this is a performance optimization - array index is cheaper then invoking a method with 'switch' const char *pinName = output->name; // dbgDurr = hal_lld_get_counter_value() - dbgStart; addEngineSniffferEvent(pinName, WC_UP); } #endif /* EFI_ENGINE_SNIFFER */ // dbgDurr = hal_lld_get_counter_value() - dbgStart; }
/* Gateway of LEMIRE_ENGINE */ void mexFunction(int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[]) { mxClassID ClassID; /* Data pointers, which one are used depends on the class of A */ double *adouble, *minvaldouble, *maxvaldouble; float *asingle, *minvalsingle, *maxvalsingle; int64 *aint64, *minvalint64, *maxvalint64; int32 *aint32, *minvalint32, *maxvalint32; int16 *aint16, *minvalint16, *maxvalint16; int08 *aint08, *minvalint08, *maxvalint08; uint64 *auint64, *minvaluint64, *maxvaluint64; uint32 *auint32, *minvaluint32, *maxvaluint32; uint16 *auint16, *minvaluint16, *maxvaluint16; uint08 *auint08, *minvaluint08, *maxvaluint08; mwSize i, n, window; int left, size; mwSize *U, *L; /* wedge */ int nU, nL; /* wedge number of elements (0 is empty wedge) */ int Ufirst, Lfirst, Ulast, Llast; /* Indices of two ends of the wedge */ /* Check number of arguments */ if (nrhs!=2) mexErrMsgTxt("LEMIRE_ENGINE: two arguments are required."); /* Get class of input matrix A */ ClassID = mxGetClassID(A); /* Do not support on sparse */ if (mxIsSparse(A)) mexErrMsgTxt("LEMIRE_ENGINE: First input A must be full."); /* Get the number of elements of A */ n = mxGetM(A)*mxGetN(A); /* Window input must be double */ if (mxGetClassID(WINDOW)!=mxDOUBLE_CLASS) mexErrMsgTxt("LEMIRE_ENGINE: Second input WINDOW must be double."); /* Get the window size, cast it in mwSize */ window = (mwSize)(*mxGetPr(WINDOW)); if (window<1) /* Check if it's valid */ mexErrMsgTxt("LEMIRE_ENGINE: windows must be 1 or greater."); if (window>n || window>MAXINT) mexErrMsgTxt("LEMIRE_ENGINE: windows larger than data length."); /* Allocate wedges buffers for L and U, each is size (window+1) */ size = (int)(window+1); L = mxMalloc((2*size)*sizeof(mwSize)); if (L==NULL) mexErrMsgTxt("LEMIRE_ENGINE: out of memory."); U = L + size; /* Create output arrays */ if (mxGetM(A)==1) /* row */ { MINVAL = mxCreateNumericMatrix(1, n-window+1, ClassID, mxREAL); MAXVAL = mxCreateNumericMatrix(1, n-window+1, ClassID, mxREAL); } else if (mxGetN(A)==1) /* column */ { MINVAL = mxCreateNumericMatrix(n-window+1, 1, ClassID, mxREAL); MAXVAL = mxCreateNumericMatrix(n-window+1, 1, ClassID, mxREAL); } else mexErrMsgTxt("LEMIRE_ENGINE: First input A must be a vector."); /* Check if allocation is succeeded */ if (MINVAL==NULL || MAXVAL==NULL) mexErrMsgTxt("LEMIRE_ENGINE: out of memory."); /* Initialize empty wedges L and U */ nU = nL = 0; Lfirst = Ufirst = 0; Llast = Ulast = -1; /* Call the engine depending on ClassID */ switch (ClassID) { case mxDOUBLE_CLASS: ENGINE(adouble, minvaldouble, maxvaldouble, double); break; case mxSINGLE_CLASS: ENGINE(asingle, minvalsingle, maxvalsingle, float); break; case mxINT64_CLASS: ENGINE(aint64, minvalint64, maxvalint64, int64); break; case mxUINT64_CLASS: ENGINE(auint64, minvaluint64, maxvaluint64, uint64); break; case mxINT32_CLASS: ENGINE(aint32, minvalint32, maxvalint32, int32); break; case mxUINT32_CLASS: ENGINE(auint32, minvaluint32, maxvaluint32, uint32); break; case mxCHAR_CLASS: ENGINE(auint16, minvaluint16, maxvaluint16, uint16); break; case mxINT16_CLASS: ENGINE(aint16, minvalint16, maxvalint16, int16); break; case mxUINT16_CLASS: ENGINE(auint16, minvaluint16, maxvaluint16, uint16); break; case mxLOGICAL_CLASS: ENGINE(auint08, minvaluint08, maxvaluint08, uint08); break; case mxINT8_CLASS: ENGINE(aint08, minvalint08, maxvalint08, int08); break; case mxUINT8_CLASS: ENGINE(auint08, minvaluint08, maxvaluint08, uint08); break; default: mexErrMsgTxt("LEMIRE_ENGINE: Class not supported."); } /* switch */ /* Free the buffer */ mxFree(L); return; } /* Gateway LEMIRE_ENGINE */
/** * @brief Register an event for digital sniffer */ void WaveChart::addEvent3(const char *name, const char * msg) { #if EFI_TEXT_LOGGING if (!ENGINE(isEngineChartEnabled)) { return; } if (skipUntilEngineCycle != 0 && ENGINE(rpmCalculator.getRevolutionCounter()) < skipUntilEngineCycle) return; #if EFI_SIMULATOR // todo: add UI control to enable this for firmware if desired // CONFIG(alignEngineSnifferAtTDC) && if (!collectingData) { return; } #endif efiAssertVoid(CUSTOM_ERR_6651, name!=NULL, "WC: NULL name"); #if EFI_PROD_CODE efiAssertVoid(CUSTOM_ERR_6652, getCurrentRemainingStack() > 32, "lowstck#2c"); #endif /* EFI_PROD_CODE */ efiAssertVoid(CUSTOM_ERR_6653, isInitialized, "chart not initialized"); #if DEBUG_WAVE scheduleSimpleMsg(&debugLogging, "current", chart->counter); #endif /* DEBUG_WAVE */ if (isFull()) { return; } #if EFI_HISTOGRAMS && EFI_PROD_CODE int beforeCallback = hal_lld_get_counter_value(); #endif efitick_t nowNt = getTimeNowNt(); bool alreadyLocked = lockOutputBuffer(); // we have multiple threads writing to the same output buffer if (counter == 0) { startTimeNt = nowNt; } counter++; /** * We want smaller times within a chart in order to reduce packet size. */ /** * todo: migrate to binary fractions in order to eliminate * this division? I do not like division * * at least that's 32 bit division now */ uint32_t diffNt = nowNt - startTimeNt; uint32_t time100 = NT2US(diffNt / 10); if (remainingSize(&logging) > 35) { /** * printf is a heavy method, append is used here as a performance optimization */ appendFast(&logging, name); appendChar(&logging, CHART_DELIMETER); appendFast(&logging, msg); appendChar(&logging, CHART_DELIMETER); // time100 -= startTime100; itoa10(timeBuffer, time100); appendFast(&logging, timeBuffer); appendChar(&logging, CHART_DELIMETER); logging.linePointer[0] = 0; } if (!alreadyLocked) { unlockOutputBuffer(); } #if EFI_HISTOGRAMS && EFI_PROD_CODE int64_t diff = hal_lld_get_counter_value() - beforeCallback; if (diff > 0) { hsAdd(&engineSnifferHisto, diff); } #endif /* EFI_HISTOGRAMS */ #endif /* EFI_TEXT_LOGGING */ }
int main(int argc, char** argv) { try { std::map<std::string, std::string> engines; ObjectFactory<std::string, Engine> engineFactory; ENGINE("groovie", "Groovie", Groovie::GroovieEngine); ENGINE("kyra2", "Legend of Kyrandia: Hand of Fate", Kyra::Kyra2Engine); ENGINE("scummv6", "SCUMM v6", Scumm::v6::Scummv6Engine); po::options_description visible("Options"); visible.add_options() ("help,h", "Produce this help message.") ("engine,e", po::value<std::string>(), "Engine the script originates from.") ("list,l", "List the supported engines.") ("dump-disassembly,d", po::value<std::string>()->implicit_value(""), "Dump the disassembly to a file. Leave out filename to output to stdout.") ("dump-graph,g", po::value<std::string>()->implicit_value(""), "Output the control flow graph in dot format to a file. Leave out filename to output to stdout.") ("only-disassembly,D", "Stops after disassembly. Implies -d.") ("only-graph,G", "Stops after control flow graph has been generated. Implies -g.") ("show-unreachable,u", "Show the address and contents of unreachable groups in the script.") ("variant,v", po::value<std::string>()->default_value(""), "Tell the engine that the script is from a specific variant. To see a list of variants supported by a specific engine, use the -h option and the -e option together.") ("no-stack-effect,s", "Leave out the stack effect when printing raw instructions."); po::options_description args(""); args.add(visible).add_options() ("input-file", po::value<std::string>(), "Input file"); po::positional_options_description fileArg; fileArg.add("input-file", -1); po::variables_map vm; try { // FIXME: If specified as the last parameter before the input file name, -d currently requires a filename to specified. -d must be specified earlier than that if outputting to stdout. po::store(po::command_line_parser(argc, argv).options(args).positional(fileArg).run(), vm); po::notify(vm); } catch (std::exception& e) { std::cout << e.what() << std::endl; } if (vm.count("list")) { std::cout << "Available engines:" << "\n"; std::map<std::string, std::string>::iterator it; for (it = engines.begin(); it != engines.end(); it++) std::cout << (*it).first << " " << (*it).second << "\n"; return 0; } if (vm.count("help") || !vm.count("input-file")) { std::cout << "Usage: " << argv[0] << " [option...] file" << "\n"; std::cout << visible << "\n"; if (vm.count("engine") && engines.find(vm["engine"].as<std::string>()) != engines.end()) { Engine *engine = engineFactory.create(vm["engine"].as<std::string>()); std::vector<std::string> variants; engine->getVariants(variants); if (variants.empty()) { std::cout << engines[vm["engine"].as<std::string>()] << " does not use variants.\n"; } else { std::cout << "Supported variants for " << engines[vm["engine"].as<std::string>()] << ":\n"; for (std::vector<std::string>::iterator i = variants.begin(); i != variants.end(); ++i) { std::cout << " " << *i << "\n"; } } delete engine; std::cout << "\n"; } std::cout << "Note: If outputting to stdout, -d or -g must NOT be specified immediately before the input file.\n"; return 1; } if (!vm.count("engine")) { std::cout << "Engine must be specified.\n"; return 2; } else if (engines.find(vm["engine"].as<std::string>()) == engines.end()) { std::cout << "Unknown engine.\n"; return 2; } if (vm.count("no-stack-effect")) { setOutputStackEffect(false); } Engine *engine = engineFactory.create(vm["engine"].as<std::string>()); engine->_variant = vm["variant"].as<std::string>(); std::string inputFile = vm["input-file"].as<std::string>(); // Disassembly InstVec insts; Disassembler *disassembler = engine->getDisassembler(insts); disassembler->open(inputFile.c_str()); disassembler->disassemble(); if (vm.count("dump-disassembly")) { std::streambuf *buf; std::ofstream of; if (vm["dump-disassembly"].as<std::string>() != "") { of.open(vm["dump-disassembly"].as<std::string>().c_str()); buf = of.rdbuf(); } else { buf = std::cout.rdbuf(); } std::ostream out(buf); disassembler->dumpDisassembly(out); } if (!engine->supportsCodeFlow() || vm.count("only-disassembly") || insts.empty()) { if (!vm.count("dump-disassembly")) { disassembler->dumpDisassembly(std::cout); } delete disassembler; delete engine; return 0; } delete disassembler; // Control flow analysis ControlFlow *cf = new ControlFlow(insts, engine); cf->createGroups(); Graph g = cf->analyze(); if (vm.count("dump-graph")) { std::streambuf *buf; std::ofstream of; if (vm["dump-graph"].as<std::string>() != "") { of.open(vm["dump-graph"].as<std::string>().c_str()); buf = of.rdbuf(); } else { buf = std::cout.rdbuf(); } std::ostream out(buf); boost::write_graphviz(out, g, boost::make_label_writer(get(boost::vertex_name, g)), boost::makeArrowheadWriter(get(boost::edge_attribute, g)), GraphProperties(engine, g)); } if (!engine->supportsCodeGen() || vm.count("only-graph")) { if (!vm.count("dump-graph")) { boost::write_graphviz(std::cout, g, boost::make_label_writer(get(boost::vertex_name, g)), boost::makeArrowheadWriter(get(boost::edge_attribute, g)), GraphProperties(engine, g)); } delete cf; delete engine; return 0; } // Post-processing of CFG engine->postCFG(insts, g); // Code generation CodeGenerator *cg = engine->getCodeGenerator(std::cout); cg->generate(g); if (vm.count("show-unreachable")) { std::vector<GroupPtr> unreachable; VertexRange vr = boost::vertices(g); for (VertexIterator v = vr.first; v != vr.second; ++v) { GroupPtr gr = boost::get(boost::vertex_name, g, *v); if (gr->_stackLevel == -1) unreachable.push_back(gr); } if (!unreachable.empty()) { for (size_t i = 0; i < unreachable.size(); i++) { if (i == 0) { if (unreachable.size() == 1) std::cout << boost::format("\n%d unreachable group detected.\n") % unreachable.size(); else std::cout << boost::format("\n%d unreachable groups detected.\n") % unreachable.size(); } std::cout << "Group " << (i + 1) << ":\n"; ConstInstIterator inst = unreachable[i]->_start; do { std::cout << *inst; } while (inst++ != unreachable[i]->_end); std::cout << "----------\n"; } } } // Free memory delete cf; delete cg; delete engine; } catch (UnknownOpcodeException &e) { std::cerr << "ERROR: " << e.what() << "\n"; return 3; } catch (std::exception &e) { std::cerr << "ERROR: " << e.what() << "\n"; return 4; } return 0; }
void EngineState::periodicFastCallback(DECLARE_ENGINE_PARAMETER_SIGNATURE) { efitick_t nowNt = getTimeNowNt(); if (ENGINE(rpmCalculator).isCranking(PASS_ENGINE_PARAMETER_SIGNATURE)) { crankingTime = nowNt; timeSinceCranking = 0.0f; } else { timeSinceCranking = nowNt - crankingTime; } updateAuxValves(PASS_ENGINE_PARAMETER_SIGNATURE); int rpm = ENGINE(rpmCalculator).getRpm(PASS_ENGINE_PARAMETER_SIGNATURE); sparkDwell = getSparkDwell(rpm PASS_ENGINE_PARAMETER_SUFFIX); dwellAngle = sparkDwell / getOneDegreeTimeMs(rpm); if (hasAfrSensor(PASS_ENGINE_PARAMETER_SIGNATURE)) { engine->sensors.currentAfr = getAfr(PASS_ENGINE_PARAMETER_SIGNATURE); } // todo: move this into slow callback, no reason for IAT corr to be here iatFuelCorrection = getIatFuelCorrection(engine->sensors.iat PASS_ENGINE_PARAMETER_SUFFIX); // todo: move this into slow callback, no reason for CLT corr to be here if (boardConfiguration->useWarmupPidAfr && engine->sensors.clt < engineConfiguration->warmupAfrThreshold) { if (rpm < 200) { cltFuelCorrection = 1; warmupAfrPid.reset(); } else { cltFuelCorrection = warmupAfrPid.getValue(warmupTargetAfr, engine->sensors.currentAfr, 1); } #if ! EFI_UNIT_TEST || defined(__DOXYGEN__) if (engineConfiguration->debugMode == DBG_WARMUP_ENRICH) { tsOutputChannels.debugFloatField1 = warmupTargetAfr; warmupAfrPid.postState(&tsOutputChannels); } #endif } else { cltFuelCorrection = getCltFuelCorrection(PASS_ENGINE_PARAMETER_SIGNATURE); } // update fuel consumption states fuelConsumption.update(nowNt PASS_ENGINE_PARAMETER_SUFFIX); // Fuel cut-off isn't just 0 or 1, it can be tapered fuelCutoffCorrection = getFuelCutOffCorrection(nowNt, rpm PASS_ENGINE_PARAMETER_SUFFIX); // post-cranking fuel enrichment. // for compatibility reasons, apply only if the factor is greater than zero (0.01 margin used) if (engineConfiguration->postCrankingFactor > 0.01f) { // convert to microsecs and then to seconds float timeSinceCrankingInSecs = NT2US(timeSinceCranking) / 1000000.0f; // use interpolation for correction taper postCrankingFuelCorrection = interpolateClamped(0.0f, engineConfiguration->postCrankingFactor, engineConfiguration->postCrankingDurationSec, 1.0f, timeSinceCrankingInSecs); } else { postCrankingFuelCorrection = 1.0f; } cltTimingCorrection = getCltTimingCorrection(PASS_ENGINE_PARAMETER_SIGNATURE); engineNoiseHipLevel = interpolate2d("knock", rpm, engineConfiguration->knockNoiseRpmBins, engineConfiguration->knockNoise, ENGINE_NOISE_CURVE_SIZE); baroCorrection = getBaroCorrection(PASS_ENGINE_PARAMETER_SIGNATURE); injectionOffset = getinjectionOffset(rpm PASS_ENGINE_PARAMETER_SUFFIX); float engineLoad = getEngineLoadT(PASS_ENGINE_PARAMETER_SIGNATURE); timingAdvance = getAdvance(rpm, engineLoad PASS_ENGINE_PARAMETER_SUFFIX); if (engineConfiguration->fuelAlgorithm == LM_SPEED_DENSITY) { float coolantC = ENGINE(sensors.clt); float intakeC = ENGINE(sensors.iat); float tps = getTPS(PASS_ENGINE_PARAMETER_SIGNATURE); tChargeK = convertCelsiusToKelvin(getTCharge(rpm, tps, coolantC, intakeC PASS_ENGINE_PARAMETER_SUFFIX)); float map = getMap(); /** * *0.01 because of https://sourceforge.net/p/rusefi/tickets/153/ */ float rawVe = veMap.getValue(rpm, map); // get VE from the separate table for Idle if (CONFIG(useSeparateVeForIdle)) { float idleVe = interpolate2d("idleVe", rpm, config->idleVeBins, config->idleVe, IDLE_VE_CURVE_SIZE); // interpolate between idle table and normal (running) table using TPS threshold rawVe = interpolateClamped(0.0f, idleVe, boardConfiguration->idlePidDeactivationTpsThreshold, rawVe, tps); } currentVE = baroCorrection * rawVe * 0.01; targetAFR = afrMap.getValue(rpm, map); } else { baseTableFuel = getBaseTableFuel(rpm, engineLoad); } }
void EngineState::periodicFastCallback(DECLARE_ENGINE_PARAMETER_F) { int rpm = ENGINE(rpmCalculator.rpmValue); efitick_t nowNt = getTimeNowNt(); if (isCrankingR(rpm)) { crankingTime = nowNt; } else { timeSinceCranking = nowNt - crankingTime; } sparkDwell = getSparkDwell(rpm PASS_ENGINE_PARAMETER); dwellAngle = sparkDwell / getOneDegreeTimeMs(rpm); // todo: move this into slow callback, no reason for IAT corr to be here iatFuelCorrection = getIatCorrection(iat PASS_ENGINE_PARAMETER); // todo: move this into slow callback, no reason for CLT corr to be here if (boardConfiguration->useWarmupPidAfr && clt < engineConfiguration->warmupAfrThreshold) { if (rpm < 200) { cltFuelCorrection = 1; warmupAfrPid.reset(); } else { cltFuelCorrection = warmupAfrPid.getValue(warmupTargetAfr, getAfr(PASS_ENGINE_PARAMETER_F), 1); } #if ! EFI_UNIT_TEST || defined(__DOXYGEN__) if (engineConfiguration->debugMode == WARMUP_ENRICH) { tsOutputChannels.debugFloatField1 = warmupTargetAfr; warmupAfrPid.postState(&tsOutputChannels); } #endif } else { cltFuelCorrection = getCltFuelCorrection(clt PASS_ENGINE_PARAMETER); } cltTimingCorrection = getCltTimingCorrection(clt PASS_ENGINE_PARAMETER); engineNoiseHipLevel = interpolate2d(rpm, engineConfiguration->knockNoiseRpmBins, engineConfiguration->knockNoise, ENGINE_NOISE_CURVE_SIZE); baroCorrection = getBaroCorrection(PASS_ENGINE_PARAMETER_F); injectionOffset = getinjectionOffset(rpm PASS_ENGINE_PARAMETER); float engineLoad = getEngineLoadT(PASS_ENGINE_PARAMETER_F); timingAdvance = getAdvance(rpm, engineLoad PASS_ENGINE_PARAMETER); if (engineConfiguration->fuelAlgorithm == LM_SPEED_DENSITY) { float coolantC = ENGINE(engineState.clt); float intakeC = ENGINE(engineState.iat); float tps = getTPS(PASS_ENGINE_PARAMETER_F); tChargeK = convertCelsiusToKelvin(getTCharge(rpm, tps, coolantC, intakeC PASS_ENGINE_PARAMETER)); float map = getMap(); /** * *0.01 because of https://sourceforge.net/p/rusefi/tickets/153/ */ currentVE = baroCorrection * veMap.getValue(rpm, map) * 0.01; targetAFR = afrMap.getValue(rpm, map); } else { baseTableFuel = getBaseTableFuel(engineConfiguration, rpm, engineLoad); } }
static ALWAYS_INLINE void handleSparkEvent(bool limitedSpark, uint32_t eventIndex, IgnitionEvent *iEvent, int rpm DECLARE_ENGINE_PARAMETER_S) { float dwellMs = ENGINE(engineState.sparkDwell); if (cisnan(dwellMs) || dwellMs < 0) { firmwareError("invalid dwell: %f at %d", dwellMs, rpm); return; } floatus_t chargeDelayUs = ENGINE(rpmCalculator.oneDegreeUs) * iEvent->dwellPosition.angleOffset; int isIgnitionError = chargeDelayUs < 0; ignitionErrorDetection.add(isIgnitionError); if (isIgnitionError) { #if EFI_PROD_CODE || defined(__DOXYGEN__) scheduleMsg(logger, "Negative spark delay=%f", chargeDelayUs); #endif chargeDelayUs = 0; return; } if (cisnan(dwellMs)) { firmwareError("NaN in scheduleOutput", dwellMs); return; } /** * We are alternating two event lists in order to avoid a potential issue around revolution boundary * when an event is scheduled within the next revolution. */ scheduling_s * sUp = &iEvent->signalTimerUp; scheduling_s * sDown = &iEvent->signalTimerDown; /** * The start of charge is always within the current trigger event range, so just plain time-based scheduling */ if (!limitedSpark) { #if EFI_UNIT_TEST || defined(__DOXYGEN__) printf("spark charge delay=%f\r\n", chargeDelayUs); #endif /** * Note how we do not check if spark is limited or not while scheduling 'spark down' * This way we make sure that coil dwell started while spark was enabled would fire and not burn * the coil. */ scheduleTask("spark up", sUp, chargeDelayUs, (schfunc_t) &turnPinHigh, iEvent->output); } /** * Spark event is often happening during a later trigger event timeframe * TODO: improve precision */ findTriggerPosition(&iEvent->sparkPosition, iEvent->advance PASS_ENGINE_PARAMETER); if (iEvent->sparkPosition.eventIndex == eventIndex) { /** * Spark should be fired before the next trigger event - time-based delay is best precision possible */ float timeTillIgnitionUs = ENGINE(rpmCalculator.oneDegreeUs) * iEvent->sparkPosition.angleOffset; #if EFI_UNIT_TEST || defined(__DOXYGEN__) printf("spark delay=%f angle=%f\r\n", timeTillIgnitionUs, iEvent->sparkPosition.angleOffset); #endif scheduleTask("spark1 down", sDown, (int) timeTillIgnitionUs, (schfunc_t) &turnPinLow, iEvent->output); } else { /** * Spark should be scheduled in relation to some future trigger event, this way we get better firing precision */ bool isPending = assertNotInList<IgnitionEvent>(ENGINE(iHead), iEvent); if (isPending) return; LL_APPEND(ENGINE(iHead), iEvent); } }
/** * @brief Trigger decoding happens here * This method is invoked every time we have a fall or rise on one of the trigger sensors. * This method changes the state of trigger_state_s data structure according to the trigger event * @param signal type of event which just happened * @param nowNt current time */ void TriggerState::decodeTriggerEvent(trigger_event_e const signal, efitime_t nowNt DECLARE_ENGINE_PARAMETER_S) { efiAssertVoid(signal <= SHAFT_3RD_UP, "unexpected signal"); trigger_wheel_e triggerWheel = eventIndex[signal]; if (!engineConfiguration->useOnlyFrontForTrigger && curSignal == prevSignal) { orderingErrorCounter++; } prevSignal = curSignal; curSignal = signal; currentCycle.eventCount[triggerWheel]++; efitime_t currentDurationLong = getCurrentGapDuration(nowNt); /** * For performance reasons, we want to work with 32 bit values. If there has been more then * 10 seconds since previous trigger event we do not really care. */ currentDuration = currentDurationLong > 10 * US2NT(US_PER_SECOND_LL) ? 10 * US2NT(US_PER_SECOND_LL) : currentDurationLong; bool isPrimary = triggerWheel == T_PRIMARY; if (isLessImportant(signal)) { #if EFI_UNIT_TEST || defined(__DOXYGEN__) if (printTriggerDebug) { printf("%s isLessImportant %s %d\r\n", getTrigger_type_e(engineConfiguration->trigger.type), getTrigger_event_e(signal), nowNt); } #endif /** * For less important events we simply increment the index. */ nextTriggerEvent() ; if (TRIGGER_SHAPE(gapBothDirections) && considerEventForGap()) { isFirstEvent = false; thirdPreviousDuration = durationBeforePrevious; durationBeforePrevious = toothed_previous_duration; toothed_previous_duration = currentDuration; toothed_previous_time = nowNt; } } else { #if EFI_UNIT_TEST || defined(__DOXYGEN__) if (printTriggerDebug) { printf("%s event %s %d\r\n", getTrigger_type_e(engineConfiguration->trigger.type), getTrigger_event_e(signal), nowNt); } #endif isFirstEvent = false; // todo: skip a number of signal from the beginning #if EFI_PROD_CODE || defined(__DOXYGEN__) // scheduleMsg(&logger, "from %f to %f %d %d", triggerConfig->syncRatioFrom, triggerConfig->syncRatioTo, currentDuration, shaftPositionState->toothed_previous_duration); // scheduleMsg(&logger, "ratio %f", 1.0 * currentDuration/ shaftPositionState->toothed_previous_duration); #else if (toothed_previous_duration != 0) { // printf("ratio %f: cur=%d pref=%d\r\n", 1.0 * currentDuration / shaftPositionState->toothed_previous_duration, // currentDuration, shaftPositionState->toothed_previous_duration); } #endif bool isSynchronizationPoint; if (TRIGGER_SHAPE(isSynchronizationNeeded)) { /** * Here I prefer to have two multiplications instead of one division, that's a micro-optimization */ isSynchronizationPoint = currentDuration > toothed_previous_duration * TRIGGER_SHAPE(syncRatioFrom) && currentDuration < toothed_previous_duration * TRIGGER_SHAPE(syncRatioTo) && toothed_previous_duration > durationBeforePrevious * TRIGGER_SHAPE(secondSyncRatioFrom) && toothed_previous_duration < durationBeforePrevious * TRIGGER_SHAPE(secondSyncRatioTo) // this is getting a little out of hand, any ideas? && durationBeforePrevious > thirdPreviousDuration * TRIGGER_SHAPE(thirdSyncRatioFrom) && durationBeforePrevious < thirdPreviousDuration * TRIGGER_SHAPE(thirdSyncRatioTo) ; #if EFI_PROD_CODE || defined(__DOXYGEN__) if (engineConfiguration->isPrintTriggerSynchDetails || someSortOfTriggerError) { #else if (printTriggerDebug) { #endif /* EFI_PROD_CODE */ float gap = 1.0 * currentDuration / toothed_previous_duration; float prevGap = 1.0 * toothed_previous_duration / durationBeforePrevious; float gap3 = 1.0 * durationBeforePrevious / thirdPreviousDuration; #if EFI_PROD_CODE || defined(__DOXYGEN__) scheduleMsg(logger, "gap=%f/%f/%f @ %d while expected %f/%f and %f/%f error=%d", gap, prevGap, gap3, currentCycle.current_index, TRIGGER_SHAPE(syncRatioFrom), TRIGGER_SHAPE(syncRatioTo), TRIGGER_SHAPE(secondSyncRatioFrom), TRIGGER_SHAPE(secondSyncRatioTo), someSortOfTriggerError); #else actualSynchGap = gap; print("current gap %f/%f/%f c=%d prev=%d\r\n", gap, prevGap, gap3, currentDuration, toothed_previous_duration); #endif /* EFI_PROD_CODE */ } } else { /** * in case of noise the counter could be above the expected number of events */ int d = engineConfiguration->useOnlyFrontForTrigger ? 2 : 1; isSynchronizationPoint = !shaft_is_synchronized || (currentCycle.current_index >= TRIGGER_SHAPE(size) - d); } #if EFI_UNIT_TEST || defined(__DOXYGEN__) if (printTriggerDebug) { printf("%s isSynchronizationPoint=%d index=%d %s\r\n", getTrigger_type_e(engineConfiguration->trigger.type), isSynchronizationPoint, currentCycle.current_index, getTrigger_event_e(signal)); } #endif if (isSynchronizationPoint) { /** * We can check if things are fine by comparing the number of events in a cycle with the expected number of event. */ bool isDecodingError = currentCycle.eventCount[0] != TRIGGER_SHAPE(expectedEventCount[0]) || currentCycle.eventCount[1] != TRIGGER_SHAPE(expectedEventCount[1]) || currentCycle.eventCount[2] != TRIGGER_SHAPE(expectedEventCount[2]); triggerDecoderErrorPin.setValue(isDecodingError); if (isDecodingError) { lastDecodingErrorTime = getTimeNowNt(); someSortOfTriggerError = true; totalTriggerErrorCounter++; if (engineConfiguration->isPrintTriggerSynchDetails || someSortOfTriggerError) { #if EFI_PROD_CODE || defined(__DOXYGEN__) scheduleMsg(logger, "error: synchronizationPoint @ index %d expected %d/%d/%d got %d/%d/%d", currentCycle.current_index, TRIGGER_SHAPE(expectedEventCount[0]), TRIGGER_SHAPE(expectedEventCount[1]), TRIGGER_SHAPE(expectedEventCount[2]), currentCycle.eventCount[0], currentCycle.eventCount[1], currentCycle.eventCount[2]); #endif /* EFI_PROD_CODE */ } } errorDetection.add(isDecodingError); if (isTriggerDecoderError()) { warning(OBD_PCM_Processor_Fault, "trigger decoding issue. expected %d/%d/%d got %d/%d/%d", TRIGGER_SHAPE(expectedEventCount[0]), TRIGGER_SHAPE(expectedEventCount[1]), TRIGGER_SHAPE(expectedEventCount[2]), currentCycle.eventCount[0], currentCycle.eventCount[1], currentCycle.eventCount[2]); } shaft_is_synchronized = true; // this call would update duty cycle values nextTriggerEvent() ; nextRevolution(); } else { nextTriggerEvent() ; } thirdPreviousDuration = durationBeforePrevious; durationBeforePrevious = toothed_previous_duration; toothed_previous_duration = currentDuration; toothed_previous_time = nowNt; } if (!isValidIndex(PASS_ENGINE_PARAMETER_F)) { warning(OBD_PCM_Processor_Fault, "unexpected eventIndex=%d while size %d", currentCycle.current_index, TRIGGER_SHAPE(size)); lastDecodingErrorTime = getTimeNowNt(); someSortOfTriggerError = true; } if (someSortOfTriggerError) { if (getTimeNowNt() - lastDecodingErrorTime > US2NT(US_PER_SECOND_LL)) { someSortOfTriggerError = false; } } if (ENGINE(sensorChartMode) == SC_RPM_ACCEL || ENGINE(sensorChartMode) == SC_DETAILED_RPM) { angle_t currentAngle = TRIGGER_SHAPE(eventAngles[currentCycle.current_index]); // todo: make this '90' depend on cylinder count? angle_t prevAngle = currentAngle - 90; fixAngle(prevAngle); // todo: prevIndex should be pre-calculated int prevIndex = TRIGGER_SHAPE(triggerIndexByAngle[(int)prevAngle]); // now let's get precise angle for that event prevAngle = TRIGGER_SHAPE(eventAngles[prevIndex]); // todo: re-implement this as a subclass. we need two instances of // uint32_t time = nowNt - timeOfLastEvent[prevIndex]; angle_t angleDiff = currentAngle - prevAngle; // todo: angle diff should be pre-calculated fixAngle(angleDiff); // float r = (60000000.0 / 360 * US_TO_NT_MULTIPLIER) * angleDiff / time; #if EFI_SENSOR_CHART || defined(__DOXYGEN__) if (boardConfiguration->sensorChartMode == SC_DETAILED_RPM) { // scAddData(currentAngle, r); } else { // scAddData(currentAngle, r / instantRpmValue[prevIndex]); } #endif // instantRpmValue[currentCycle.current_index] = r; // timeOfLastEvent[currentCycle.current_index] = nowNt; } } angle_t getEngineCycle(operation_mode_e operationMode) { return operationMode == TWO_STROKE ? 360 : 720; } void addSkippedToothTriggerEvents(trigger_wheel_e wheel, TriggerShape *s, int totalTeethCount, int skippedCount, float toothWidth, float offset, float engineCycle, float filterLeft, float filterRight) { efiAssertVoid(totalTeethCount > 0, "total count"); efiAssertVoid(skippedCount >= 0, "skipped count"); for (int i = 0; i < totalTeethCount - skippedCount - 1; i++) { float angleDown = engineCycle / totalTeethCount * (i + (1 - toothWidth)); float angleUp = engineCycle / totalTeethCount * (i + 1); s->addEvent(offset + angleDown, wheel, TV_RISE, filterLeft, filterRight); s->addEvent(offset + angleUp, wheel, TV_FALL, filterLeft, filterRight); } float angleDown = engineCycle / totalTeethCount * (totalTeethCount - skippedCount - 1 + (1 - toothWidth)); s->addEvent(offset + angleDown, wheel, TV_RISE, filterLeft, filterRight); s->addEvent(offset + engineCycle, wheel, TV_FALL, filterLeft, filterRight); }
static void printSensors(Logging *log, bool fileFormat) { // current time, in milliseconds int nowMs = currentTimeMillis(); float sec = ((float) nowMs) / 1000; reportSensorF(log, fileFormat, "time", "", sec, 3); // log column 1 int rpm = 0; #if EFI_SHAFT_POSITION_INPUT || defined(__DOXYGEN__) rpm = getRpmE(engine); reportSensorI(log, fileFormat, "rpm", "RPM", rpm); // log column 2 // reportSensorF(log, fileFormat, "TRG_0_DUTY", "%", getTriggerDutyCycle(0), 2); // reportSensorF(log, fileFormat, "TRG_1_DUTY", "%", getTriggerDutyCycle(1), 2); #endif #if EFI_PROD_CODE || defined(__DOXYGEN__) reportSensorF(log, fileFormat, "int_temp", "C", getMCUInternalTemperature(), 2); // log column #3 #endif reportSensorI(log, fileFormat, "mode", "v", packEngineMode(PASS_ENGINE_PARAMETER_F)); // log column #3 if (hasCltSensor()) { reportSensorF(log, fileFormat, "CLT", "C", getCoolantTemperature(PASS_ENGINE_PARAMETER_F), 2); // log column #4 } if (hasTpsSensor()) { reportSensorF(log, fileFormat, "TPS", "%", getTPS(PASS_ENGINE_PARAMETER_F), 2); // log column #5 } if (hasVBatt(PASS_ENGINE_PARAMETER_F)) { reportSensorF(log, fileFormat, "vbatt", "V", getVBatt(PASS_ENGINE_PARAMETER_F), 2); // log column #6 } if (hasIatSensor()) { reportSensorF(log, fileFormat, "IAT", "C", getIntakeAirTemperature(PASS_ENGINE_PARAMETER_F), 2); // log column #7 } if (hasMafSensor()) { reportSensorF(log, fileFormat, "maf", "V", getMaf(PASS_ENGINE_PARAMETER_F), 2); reportSensorF(log, fileFormat, "mafr", "kg/hr", getRealMaf(PASS_ENGINE_PARAMETER_F), 2); } #if EFI_ANALOG_SENSORS || defined(__DOXYGEN__) if (engineConfiguration->map.sensor.hwChannel != EFI_ADC_NONE) { reportSensorF(log, fileFormat, "MAP", "kPa", getMap(), 2); // reportSensorF(log, fileFormat, "map_r", "V", getRawMap(), 2); } #endif /* EFI_ANALOG_SENSORS */ #if EFI_ANALOG_SENSORS || defined(__DOXYGEN__) if (hasBaroSensor()) { reportSensorF(log, fileFormat, "baro", "kPa", getBaroPressure(), 2); } #endif /* EFI_ANALOG_SENSORS */ if (hasAfrSensor(PASS_ENGINE_PARAMETER_F)) { reportSensorF(log, fileFormat, "afr", "AFR", getAfr(PASS_ENGINE_PARAMETER_F), 2); } #if EFI_IDLE_CONTROL || defined(__DOXYGEN__) if (fileFormat) { reportSensorF(log, fileFormat, "idle", "%", getIdlePosition(), 2); } #endif /* EFI_IDLE_CONTROL */ #if EFI_ANALOG_SENSORS || defined(__DOXYGEN__) reportSensorF(log, fileFormat, "target", "AFR", engine->engineState.targetAFR, 2); #endif /* EFI_ANALOG_SENSORS */ if (fileFormat) { reportSensorF(log, fileFormat, "tCharge", "K", engine->engineState.tChargeK, 2); // log column #8 reportSensorF(log, fileFormat, "curVE", "%", veMap.getValue(rpm, getMap()), 2); } float engineLoad = getEngineLoadT(PASS_ENGINE_PARAMETER_F); reportSensorF(log, fileFormat, "ENGINE_LOAD", "x", engineLoad, 2); reportSensorF(log, fileFormat, "dwell", "ms", ENGINE(engineState.sparkDwell), 2); if (fileFormat) { reportSensorF(log, fileFormat, "timing", "deg", engine->engineState.timingAdvance, 2); } if (fileFormat) { floatms_t fuelBase = getBaseFuel(rpm PASS_ENGINE_PARAMETER); reportSensorF(log, fileFormat, "f: base", "ms", fuelBase, 2); reportSensorF(log, fileFormat, "f: actual", "ms", ENGINE(actualLastInjection), 2); reportSensorF(log, fileFormat, "f: lag", "ms", engine->engineState.injectorLag, 2); reportSensorF(log, fileFormat, "f: running", "ms", ENGINE(engineState.runningFuel), 2); reportSensorF(log, fileFormat, "f: wall amt", "v", ENGINE(wallFuel).getWallFuel(0), 2); reportSensorF(log, fileFormat, "f: wall crr", "v", ENGINE(wallFuelCorrection), 2); reportSensorI(log, fileFormat, "version", "#", getRusEfiVersion()); } if (engineConfiguration->hasVehicleSpeedSensor) { #if EFI_VEHICLE_SPEED || defined(__DOXYGEN__) float vehicleSpeed = getVehicleSpeed(); #else float vehicleSpeed = 0; #endif /* EFI_PROD_CODE */ reportSensorF(log, fileFormat, "vss", "kph", vehicleSpeed, 2); float sp2rpm = rpm == 0 ? 0 : vehicleSpeed / rpm; reportSensorF(log, fileFormat, "sp2rpm", "x", sp2rpm, 2); } reportSensorF(log, fileFormat, "knck_c", "count", engine->knockCount, 0); reportSensorF(log, fileFormat, "knck_v", "v", engine->knockVolts, 2); // reportSensorF(log, fileFormat, "vref", "V", getVRef(engineConfiguration), 2); if (fileFormat) { reportSensorF(log, fileFormat, "f: tps delta", "v", engine->tpsAccelEnrichment.getMaxDelta(), 2); reportSensorF(log, fileFormat, "f: tps fuel", "ms", engine->engineState.tpsAccelEnrich, 2); reportSensorF(log, fileFormat, "f: el delta", "v", engine->engineLoadAccelEnrichment.getMaxDelta(), 2); reportSensorF(log, fileFormat, "f: el fuel", "v", engine->engineLoadAccelEnrichment.getEngineLoadEnrichment(PASS_ENGINE_PARAMETER_F) * 100 / getMap(), 2); reportSensorF(log, fileFormat, "f: duty", "%", getInjectorDutyCycle(rpm PASS_ENGINE_PARAMETER), 2); } // debugFloat(&logger, "tch", getTCharge1(tps), 2); for (int i = 0;i<FSIO_ADC_COUNT;i++) { if (engineConfiguration->fsioAdc[i] != EFI_ADC_NONE) { strcpy(buf, "adcX"); buf[3] = '0' + i; reportSensorF(log, fileFormat, buf, "", getVoltage("fsio", engineConfiguration->fsioAdc[i]), 2); } } reportSensorI(log, fileFormat, "warn", "count", engine->engineState.warningCounter); reportSensorI(log, fileFormat, "error", "code", engine->engineState.lastErrorCode); }