示例#1
0
static char *boldTerm(char *target, char *term, int offset, enum dbDbMatchType type)
/* Return a string with <b>term</b> swapped in for term at offset.
 * If offset is negative and type is ddmtSciName, treat term as an abbreviated species
 * name (term = "G. species" vs. target = "Genus species"): bold the first letter of the
 * genus and the matching portion of the species. */
{
int termLen = strlen(term);
int targetLen = strlen(target);
if (offset + termLen > targetLen)
    errAbort("boldTerm: invalid offset (%d) for term '%s' (length %d) in target '%s' (length %d)",
             offset, term, termLen, target, targetLen);
else if (offset < 0 && type != ddmtSciName)
    errAbort("boldTerm: negative offset (%d) given for type %d", offset, type);
// Allocate enough to have two bolded chunks:
int resultSize = targetLen + 2*strlen("<b></b>") + 1;
char result[resultSize];
char *p = result;
int size = sizeof(result);
if (offset >= 0)
    {
    // The part of target before the term:
    safeAddN(&p, &size, target, offset);
    // The bolded term:
    safeAdd(&p, &size, "<b>");
    safeAddN(&p, &size, target+offset, termLen);
    safeAdd(&p, &size, "</b>");
    // The rest of the target after the term:
    safeAdd(&p, &size, target+offset+termLen);
    // Accounting tweak -- we allocate enough for two bolded chunks, but use only one here:
    size -= strlen("<b></b>");
    }
else
    {
    // Term is abbreviated scientific name -- bold the first letter of the genus:
    safeAdd(&p, &size, "<b>");
    safeAddN(&p, &size, target, 1);
    safeAdd(&p, &size, "</b>");
    // add the rest of the genus:
    char *targetSpecies = skipLeadingSpaces(skipToSpaces(target));
    int targetOffset = targetSpecies - target;
    safeAddN(&p, &size, target+1, targetOffset-1);
    // bold the matching portion of the species:
    char *termSpecies = skipLeadingSpaces(skipToSpaces(term));
    termLen = strlen(termSpecies);
    safeAdd(&p, &size, "<b>");
    safeAddN(&p, &size, targetSpecies, termLen);
    safeAdd(&p, &size, "</b>");
    // add the rest of the species:
    safeAdd(&p, &size, targetSpecies+termLen); 
    }
if (*p != '\0' || size != 1)
    errAbort("boldTerm: bad arithmetic (size is %d, *p is '%c')", size, *p);
return cloneStringZ(result, resultSize);
}
示例#2
0
MediaTime MediaTime::operator+(const MediaTime& rhs) const
{
    if (rhs.isInvalid() || isInvalid())
        return invalidTime();

    if (rhs.isIndefinite() || isIndefinite())
        return indefiniteTime();

    if (isPositiveInfinite() && rhs.isNegativeInfinite())
        return invalidTime();

    if (isNegativeInfinite() && rhs.isPositiveInfinite())
        return invalidTime();

    if (isPositiveInfinite() || rhs.isPositiveInfinite())
        return positiveInfiniteTime();

    if (isNegativeInfinite() || rhs.isNegativeInfinite())
        return negativeInfiniteTime();

    if (hasDoubleValue() && rhs.hasDoubleValue())
        return MediaTime::createWithDouble(m_timeValueAsDouble + rhs.m_timeValueAsDouble);

    MediaTime a = *this;
    MediaTime b = rhs;

    if (a.hasDoubleValue())
        a.setTimeScale(DefaultTimeScale);
    else if (b.hasDoubleValue())
        b.setTimeScale(DefaultTimeScale);

    int32_t commonTimeScale;
    if (!leastCommonMultiple(a.m_timeScale, b.m_timeScale, commonTimeScale) || commonTimeScale > MaximumTimeScale)
        commonTimeScale = MaximumTimeScale;
    a.setTimeScale(commonTimeScale);
    b.setTimeScale(commonTimeScale);
    while (!safeAdd(a.m_timeValue, b.m_timeValue, a.m_timeValue)) {
        if (commonTimeScale == 1)
            return a.m_timeValue > 0 ? positiveInfiniteTime() : negativeInfiniteTime();
        commonTimeScale /= 2;
        a.setTimeScale(commonTimeScale);
        b.setTimeScale(commonTimeScale);
    }
    return a;
}
示例#3
0
MediaTime MediaTime::operator+(const MediaTime& rhs) const
{
    if (rhs.isInvalid() || isInvalid())
        return invalidTime();

    if (rhs.isIndefinite() || isIndefinite())
        return indefiniteTime();

    if (isPositiveInfinite()) {
        if (rhs.isNegativeInfinite())
            return invalidTime();
        return positiveInfiniteTime();
    }

    if (isNegativeInfinite()) {
        if (rhs.isPositiveInfinite())
            return invalidTime();
        return negativeInfiniteTime();
    }

    int32_t commonTimeScale;
    if (!leastCommonMultiple(this->m_timeScale, rhs.m_timeScale, commonTimeScale) || commonTimeScale > MaximumTimeScale)
        commonTimeScale = MaximumTimeScale;
    MediaTime a = *this;
    MediaTime b = rhs;
    a.setTimeScale(commonTimeScale);
    b.setTimeScale(commonTimeScale);
    while (!safeAdd(a.m_timeValue, b.m_timeValue, a.m_timeValue)) {
        if (commonTimeScale == 1)
            return a.m_timeValue > 0 ? positiveInfiniteTime() : negativeInfiniteTime();
        commonTimeScale /= 2;
        a.setTimeScale(commonTimeScale);
        b.setTimeScale(commonTimeScale);
    }
    return a;
}
/** @brief Fuel and ignition calculations
 *
 * Using a variety of primary algorithms calculate a base pulsewidth and then
 * apply various corrections to it such as injector dead time, transient fuel
 * correction, engine temperature enrichment and per cylinder trims. The fuel
 * injection timing is also determined here.
 *
 * Calculate the ignition timing and dwell here too. Several corrections are
 * applied to these as well.
 *
 * @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.
 */
void calculateFuelAndIgnition(){
	/*&&&&&&&&&&&&& Perform the basic calculations one step at a time to get a final pulsewidth &&&&&&&&&&&&*/

	if(TRUE /* Genuine method */){
		unsigned short airInletTemp = CoreVars->IAT; /* All except MAF use this. */
		/* Determine the type of air flow data */
		if(TRUE /* SpeedDensity */){
			/* This won't overflow until 512kPa or about 60psi of boost with 128% VE. */
			DerivedVars->AirFlow = ((unsigned long)CoreVars->MAP * DerivedVars->VEMain) / oneHundredPercentVE;
			/* Result is 450 - 65535 always. */
		}else if(FALSE /*AlphaN*/){
			DerivedVars->AirFlow = DerivedVars->VEMain; /* Not actually VE, but rather tuned air flow without density information */
		}else if(FALSE /*MAF*/){
			DerivedVars->AirFlow = CoreVars->MAF; /* Just fix temperature at appropriate level to provide correct Lambda */
			/// @todo TODO figure out what the correct "temperature" is to make MAF work correctly!
			airInletTemp = roomTemperature; // 293.15k is 20c * 100 to get value, so divide by 100 to get real number
		}else if(FALSE /*FixedAF*/){ /* Fixed air flow from config */
			DerivedVars->AirFlow = fixedConfigs2.sensorPresets.presetAF;
		}else{ /* Default to no fuel delivery and error */
			DerivedVars->AirFlow = 0;
			/* If anyone is listening, let them know something is wrong */
//			sendError(AIRFLOW_NOT_CONFIGURED_CODE); // or maybe queue it?
		}


		/* This won't overflow until well past 125C inlet, 1.5 Lambda and fuel as dense as water */
		DerivedVars->densityAndFuel = (((unsigned long)((unsigned long)airInletTemp * DerivedVars->Lambda) / stoichiometricLambda) * fixedConfigs1.engineSettings.densityOfFuelAtSTP) / densityOfFuelTotalDivisor;
		/* Result is 7500 - 60000 always. */

		/* Divisors for air inlet temp and pressure :
		 * #define airInletTempDivisor 100
		 * #define airPressureDivisor 100
		 * cancel each other out! all others are used. */


		DerivedVars->BasePW = (bootFuelConst * DerivedVars->AirFlow) / DerivedVars->densityAndFuel;
	}else if(FALSE /*configured*/){ /* Fixed PW from config */
		DerivedVars->BasePW = fixedConfigs2.sensorPresets.presetBPW;
	}else{ /* Default to no fuel delivery and error */
		DerivedVars->BasePW = 0;
		/* If anyone is listening, let them know something is wrong */
//		sendError(BPW_NOT_CONFIGURED_CODE); // or maybe queue it?
	}

	/*&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&*/




	/*&&&&&&&&&&&&&&&&&&&&&&&&&&&& Apply All Corrections PCFC, ETE, IDT, TFC etc &&&&&&&&&&&&&&&&&&&&&&&&&&&*/

	/* Apply the corrections after calculating */
	DerivedVars->EffectivePW = safeTrim(DerivedVars->BasePW, DerivedVars->TFCTotal);
	DerivedVars->EffectivePW = safeScale(DerivedVars->EffectivePW, DerivedVars->ETE, SHORT4TH);


//	unsigned char channel; // the declaration of this variable is used in multiple loops below.
//
//	/* "Calculate" the individual fuel pulse widths */
//	for(channel = 0; channel < INJECTION_CHANNELS; channel++){ /// @todo TODO make injector channels come from config, not defines.
//		/* Add or subtract the per cylinder fuel trims */
//		unsigned short channelPW;
//		channelPW = safeScale(DerivedVars->EffectivePW, TablesB.SmallTablesB.perCylinderFuelTrims[channel]);
//
//		/* Add on the IDT to get the final value and put it into the array */
//		//outputEventPulseWidthsMath[channel] = safeAdd(channelPW, DerivedVars->IDT); do not re-enable this without fixing it properly...
//	}

	// Make sure we don't have a PW if PW is supposed to be zero, ie, zero the IDT as well.
	if(!(DerivedVars->EffectivePW)){
		DerivedVars->IDT = 0; // This also makes fuel and electrical duty work consistently in external apps.
	}

	/* Reference PW for comparisons etc */
	DerivedVars->RefPW = safeAdd(DerivedVars->EffectivePW, DerivedVars->IDT);
	/*&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&*/

/// @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

/// TODO @todo FIXME part of to schedule or not to schedule should be : (masterPulseWidth > injectorMinimumPulseWidth)
// IE, NOT in the decoders... KISS in the decoders. This is a hangover from (very) early decoder dev

//	for(channel = 0;channel < INJECTION_CHANNELS;channel++){ /// @todo TODO make injector channels come from config, not defines.
		//injectorMainAdvances[channel] = IDT blah blah.
//	}

	/* "Calculate" the nominal total pulse width before per channel corrections */
	masterPulseWidth = safeAdd((DerivedVars->EffectivePW / fixedConfigs1.schedulingSettings.numberOfInjectionsPerEngineCycle), DerivedVars->IDT); // div by number of injections per cycle, configured above
	// but requires to know how big a cycle is, 1/4 1, 1/2, etc
}
示例#5
0
/** @brief The main function!
 *
 * The centre of the application is here. From here all non-ISR code is called
 * directly or indirectly. The two coarse blocks are init and the main loop.
 * Init is called first to set everything up and then the main loop is entered
 * where the flow of control continues until the device is switched off or
 * reset (excluding asynchronous ISR code). Currently the main loop only runs
 * the fuel, ignition and scheduling calculation code, and the communications
 * code and only when actually required. The intention is to maintain a very
 * low latency for calculations such that the behaviour of the device more
 * closely reflects the attached engines rapidly changing requirements. When
 * accessory code is added a new scheduling algorithm will be required to keep
 * the latency low without starving any particular blocks of CPU time.
 */
int  main(){ /// @todo TODO maybe move this to paged flash ?
	// Set everything up.
	init();

	/// @todo TODO Add verification reporting code here that disables the timer interrupts such that no events ever get scheduled, and then sits looping sending error packets out about what is wrong. set a whole bunch of flags and check them here sending a packet for each with a unique errorID for each and thus a unique easy to understand message for each on the PC side. BEFORE the priming code such that no fuel gets injected. Will need to modularise the comms stuff to process packets based on calls from this section too, avoid excess duplication if possible.

	// TODO move this to a function so that it can be called on a hot restart post being asleep.
	#define NUMBER_OF_OUTPUT_PINS 6
	unsigned char outputEvent;
	unsigned char activeFuelChannels[NUMBER_OF_OUTPUT_PINS] = {0xFF,0xFF,0xFF,0xFF,0xFF,0xFF};
	for(outputEvent = 0;outputEvent < fixedConfigs1.schedulingSettings.numberOfConfiguredOutputEvents;outputEvent++){
		if(fixedConfigs1.schedulingSettings.schedulingConfigurationBits[outputEvent] == 1 && fixedConfigs1.schedulingSettings.outputEventPinNumbers[outputEvent] < NUMBER_OF_OUTPUT_PINS) { // todo remove second condition?
			activeFuelChannels[fixedConfigs1.schedulingSettings.outputEventPinNumbers[outputEvent]] = outputEvent;
		}
	}
	sampleEachADC(ADCBuffers);       // Read sensors - from utils.h/.c
	generateCoreVars();              // Calculate BRV - from coreVarsGenerator.h/.c
	generateDerivedVars();           // Calculate IDT - from derivedVarsGenerator.h/.c
	// lookupTwoDTanlesUS is in tableLookup.h/.c
	unsigned short primingPulseWidth = lookupTwoDTableUS((twoDTableUS*)&TablesA.SmallTablesA.primingVolumeTable, CoreVars->CHT);
	primingPulseWidth = safeAdd(primingPulseWidth, DerivedVars->IDT);
	unsigned short edgeTimeStamp = TCNT;
	// call sched output with args
	LongTime timeStamp;
	/* Install the low word */
	timeStamp.timeShorts[1] = edgeTimeStamp;
	/* Find out what our timer value means and put it in the high word */
	if(TFLGOF && !(edgeTimeStamp & 0x8000)){ /* see 10.3.5 paragraph 4 of 68hc11 ref manual for details */
		timeStamp.timeShorts[0] = timerExtensionClock + 1;
	}else{
		timeStamp.timeShorts[0] = timerExtensionClock;
	}
	unsigned char outputPin;
	for(outputPin = 0; outputPin< NUMBER_OF_OUTPUT_PINS; outputPin++){
		if(activeFuelChannels[outputPin] < MAX_NUMBER_OF_OUTPUT_EVENTS){
			outputEventPulseWidthsMath[activeFuelChannels[outputPin]] = primingPulseWidth;
			outputEventDelayFinalPeriod[activeFuelChannels[outputPin]] = SHORTHALF;
			schedulePortTPin(activeFuelChannels[outputPin], timeStamp); // from 
		}
	}

	// Run forever repeating.
	while(TRUE){
		//unsigned short start = realTimeClockMillis;
		/* If ADCs require forced sampling, sample now */
		if(coreStatusA & FORCE_READING){
			ATOMIC_START(); /*&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&*/
			/* Atomic block to ensure a full set of readings are taken together */

			/* Check to ensure that a reading wasn't take before we entered a non interruptable state */
			if(coreStatusA & FORCE_READING){ // do we still need to do this TODO ?

				sampleEachADC(ADCBuffersRecord); // TODO still need to do a pair of loops and clock these two functions for performance.
				//sampleLoopADC(&ADCBuffers);
				resetToNonRunningState(EVENT_ARRIVAL_TIMEOUT);
				Counters.timeoutADCreadings++;

				/* Set flag to say calc required */
				coreStatusA |= CALC_FUEL_IGN;

				/* Clear force reading flag */
				coreStatusA &= CLEAR_FORCE_READING;
			}

			ATOMIC_END(); /*&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&*/
		}

		/* If required, do main fuel and ignition calcs first */
		if(coreStatusA & CALC_FUEL_IGN){
			ATOMIC_START(); /*&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&*/
			/* Atomic block to ensure that we don't clear the flag for the next data set when things are tight */

			/* Switch input bank so that we have a stable set of the latest data */
			if(ADCBuffers == &ADCBuffers1){
				ticksPerDegree = &ticksPerDegree0; // TODO temp, remove, maybe
				ticksPerDegreeRecord = &ticksPerDegree1; // TODO temp, remove, maybe
				ADCBuffers = &ADCBuffers0;
				ADCBuffersRecord = &ADCBuffers1;
			}else{
				ticksPerDegree = &ticksPerDegree1; // TODO temp, remove, maybe
				ticksPerDegreeRecord = &ticksPerDegree0; // TODO temp, remove, maybe
				ADCBuffers = &ADCBuffers1;
				ADCBuffersRecord = &ADCBuffers0;
			}

			/* Clear the calc required flag */
			coreStatusA &= CLEAR_CALC_FUEL_IGN;

			ATOMIC_END(); /*&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&*/

			// TODO DEBUG/TUNING MACRO HERE!
			/* Keep track of how many calcs we are managing per second... */
			Counters.calculationsPerformed++;

			/* Generate the core variables from sensor input and recorded tooth timings */
			generateCoreVars();
			// TODO DEBUG/TUNING MACRO HERE!

			/* Generate the derived variables from the core variables based on settings */
			generateDerivedVars();
			// TODO DEBUG/TUNING MACRO HERE!

			/* Perform the calculations TODO possibly move this to the software interrupt if it makes sense to do so */
			calculateFuelAndIgnition();
			// TODO DEBUG/TUNING MACRO HERE!

			/* Calculate the scheduling based on configuration and previously calculated variables */
			scheduleOutputs();
			// TODO DEBUG/TUNING MACRO HERE!
		}else{
			/* In the event that no calcs are required, sleep a little before returning to retry. */
			sleepMicro(3000); // TODO tune this, and then replace it completely. not doing this will cause the ISR lockouts to run for too high a proportion of the time
			/* Using 0.8 ticks as micros so it will run for a little longer than the math did */
		}


		if(!(TXBufferInUseFlags)){
			/* If the flag for com packet processing is set and the TX buffer is available process the data! */
			if(RXStateFlags & RX_READY_TO_PROCESS){
				/* Clear the flag */
				RXStateFlags &= RX_CLEAR_READY_TO_PROCESS;

				/* Handle the incoming packet */
				decodePacketAndRespond();
			}else{// if(lastCalcCount != Counters.calculationsPerformed){ // substitute true for full speed continuous stream test...

				/* send asynchronous data log if required */
				switch (TablesB.SmallTablesB.loggingSettings.datalogStreamType) {
					case asyncDatalogOff:
					{
						break;
					}
					case asyncDatalogBasic:
					{
						/* Flag that we are transmitting! */
						TXBufferInUseFlags |= COM_SET_SCI0_INTERFACE_ID;
						// SCI0 only for now...

						// headers including length...                 *length = configuredBasicDatalogLength;
						TXBufferCurrentPositionHandler = (unsigned char*)&TXBuffer;

						/* Initialised here such that override is possible */
						TXBufferCurrentPositionSCI0 = (unsigned char*)&TXBuffer;
						TXBufferCurrentPositionCAN0 = (unsigned char*)&TXBuffer;

						/* Set the flags : firmware, no ack, no addrs, has length */
						*TXBufferCurrentPositionHandler = HEADER_HAS_LENGTH;
						TXBufferCurrentPositionHandler++;

						/* Set the payload ID */
						*((unsigned short*)TXBufferCurrentPositionHandler) = responseBasicDatalog;
						TXBufferCurrentPositionHandler += 2;

						/* Set the length */
						unsigned short* localLength = (unsigned short*)TXBufferCurrentPositionHandler;
						TXBufferCurrentPositionHandler += 2;

						/* populate data log */
						*localLength = populateBasicDatalog();
						finaliseAndSend(0);
						break;
					}
					case asyncDatalogScratchPad:
					{
						break;
					}
					case asyncDatalogStructs:
					{
						break;
					}
					case asyncDatalogPosition:
					{
						break;
					}
					case asyncDatalogBlockBytes:
					{
						break;
					}
					case asyncDatalogBlockWords:
					{
						break;
					}
					case asyncDatalogBlockLongs:
					{
						break;
					}
					case asyncDatalogStreamByte:
					{
						/* Flag that we are transmitting! */
						TXBufferInUseFlags |= COM_SET_SCI0_INTERFACE_ID;
						// SCI0 only for now...

						// headers including length...                 *length = configuredBasicDatalogLength;
						TXBufferCurrentPositionHandler = (unsigned char*)&TXBuffer;

						/* Initialised here such that override is possible */
						TXBufferCurrentPositionSCI0 = (unsigned char*)&TXBuffer;
						TXBufferCurrentPositionCAN0 = (unsigned char*)&TXBuffer;

						/* Set the flags all zeros */
						*TXBufferCurrentPositionHandler = 0;
						TXBufferCurrentPositionHandler++;

						/* Set the payload ID */
						*((unsigned short*)TXBufferCurrentPositionHandler) = responseByteLADatalog;
						TXBufferCurrentPositionHandler += 2;

						/** Store PTIT for now, later make address of byte configurable TODO @todo */
						*((unsigned char*)TXBufferCurrentPositionHandler) = PTIT;
						TXBufferCurrentPositionHandler++;

						finaliseAndSend(0);
						break;
					}
					case asyncDatalogStreamWord:
					{
						break;
					}
					case asyncDatalogStreamLong:
					{
						break;
					}
				}
				// mechanism to ensure we only send something if the data has been updated
				lastCalcCount = Counters.calculationsPerformed;
			}
		}

		performSimpleGPIO();

		// PWM experimentation
		adjustPWM();
	}
}