void SecondaryRPMISR(){
	/* Clear the interrupt flag for this input compare channel */
	TFLG = 0x02;
	DEBUG_TURN_PIN_ON(DECODER_BENCHMARKS, BIT1, PORTB);

	/* Save all relevant available data here */
	unsigned short edgeTimeStamp = TC1;    /* Save the timestamp */
	unsigned char PTITCurrentState = PTIT; /* Save the values on port T regardless of the state of DDRT */

	if(!(PTITCurrentState & 0x02)){ // TODO Remove this once the configuration can be adjusted to only fire on one edge!
		// Only count one edge, the other is irrelevant, and this comment will be two once the above todo is completed.
		KeyUserDebugs.secondaryTeethSeen++;

		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 long thisEventTimeStamp = timeStamp.timeLong;

		unsigned long thisInterEventPeriod = 0;
		if(KeyUserDebugs.decoderFlags & LAST_TIMESTAMP_VALID){
			thisInterEventPeriod = thisEventTimeStamp - lastSecondaryEventTimeStamp;
		}

		// This sets currentEvent to 255 such that when the primary ISR runs it is rolled over to zero!
		if(KeyUserDebugs.decoderFlags & CONFIGURED_SYNC){
			/* If the count is less than 23, then we know that the electrical pulse that triggered
			 * this ISR execution was almost certainly in error and it is NOT valid to stay in sync.
			 *
			 * If the count is greater than 24, then we know that an electrical noise pulse triggered
			 * the other interrupt in between and was missed by the time period checks (unlikely, but
			 * possible) and that, therefore, there could have been a noise pulse on this input too,
			 * and therefore we don't really know where we are.
			 *
			 * In the case where the count is exactly 24 we can only rely on the time period checks in
			 * the other ISR, which should be sufficient unless poorly setup by a user with too wide
			 * of a tolerance level.
			 *
			 * There is zero point adding relative timing checks to this ISR because by nature, the
			 * other N teeth have already checked out good timing wise and therefore the average also
			 * does. Thus if we did check, for it to ever fail it would need to be tighter, and in
			 * reality it must be more loose due to the larger possible variation over the much much
			 * larger time frame.
			 */
			if(KeyUserDebugs.currentEvent < (numberOfRealEvents - 1)){
				resetToNonRunningState(COUNT_OF_EVENTS_IMPOSSIBLY_LOW_NOISE);
			}else if(KeyUserDebugs.currentEvent > (numberOfRealEvents -1)){
				resetToNonRunningState(COUNT_OF_EVENTS_IMPOSSIBLY_HIGH_NOISE);
			} // ELSE do nothing, and be happy :-)
		}else{ // If not synced, sync, as this is our reference point.
			SET_SYNC_LEVEL_TO(CONFIGURED_SYNC);
		}
		KeyUserDebugs.currentEvent = 0xFF; // TODO reset always, and catch noise induced errors below, this behaviour (now some lines above) may be bad/not fussy enough, or could be good, depending upon determinate nature of the inter event timing between primary and secondary, or not, perhaps move "lose sync or correct sync" as a configuration variable
	}
	DEBUG_TURN_PIN_OFF(DECODER_BENCHMARKS, NBIT1, PORTB);
}
// CAM sensor doesn't use time stamps due to timing belt tension vibration, plus the crank has 6 times more events, so any discrepancy would be caught that way
void SecondaryRPMISR(){
	/* Clear the interrupt flag for this input compare channel */
	TFLG = 0x02;
	DEBUG_TURN_PIN_ON(DECODER_BENCHMARKS, BIT0, PORTB);
	secondaryPTITCurrentState = PTIT; /* Save the values on port T regardless of the state of DDRT */
	KeyUserDebugs.secondaryTeethSeen++;
	if(KeyUserDebugs.decoderFlags & CAM_SYNC){
		// check crankTeethSinceLastCamTooth to see if it's 4 or 12 and if not either of those, lose sync.
		crankTeethSinceLastCamTooth =  KeyUserDebugs.primaryTeethSeen - previousCrankTeethSeen;
		previousCrankTeethSeen =  KeyUserDebugs.primaryTeethSeen;
		if(crankTeethSinceLastCamTooth != 4 && crankTeethSinceLastCamTooth != 12){
			if(crankTeethSinceLastCamTooth < 4){
				resetToNonRunningState(COUNT_OF_EVENTS_IMPOSSIBLY_LOW_NOISE);
			}else{
				resetToNonRunningState(COUNT_OF_EVENTS_IMPOSSIBLY_HIGH_NOISE);
			}
		}
	}else{
		if(camTeethSeen == 0){
			camTeethSeen = 1;
			previousCrankTeethSeen =  KeyUserDebugs.primaryTeethSeen;
		}else if(camTeethSeen == 1){
			camTeethSeen = 2;
			crankTeethSinceLastCamTooth =  KeyUserDebugs.primaryTeethSeen - previousCrankTeethSeen;
			previousCrankTeethSeen =  KeyUserDebugs.primaryTeethSeen;
			if(crankTeethSinceLastCamTooth == 12){ // 12 Crank teeth in the 2 missing Cam teeth
				SET_SYNC_LEVEL_TO(CAM_SYNC);
				KeyUserDebugs.currentEvent = 0;
			}else if(crankTeethSinceLastCamTooth != 4){ // 4 Crank teeth in 1 Cam tooth
				if(crankTeethSinceLastCamTooth > 4){
					resetToNonRunningState(COUNT_OF_EVENTS_IMPOSSIBLY_HIGH_NOISE);
				}else{
					resetToNonRunningState(COUNT_OF_EVENTS_IMPOSSIBLY_LOW_NOISE);
				}
			}
		}else if(camTeethSeen == 2){
			camTeethSeen = 3;
			crankTeethSinceLastCamTooth =  KeyUserDebugs.primaryTeethSeen - previousCrankTeethSeen;
			previousCrankTeethSeen =  KeyUserDebugs.primaryTeethSeen;
			if(crankTeethSinceLastCamTooth == 12){
				SET_SYNC_LEVEL_TO(CAM_SYNC);
				KeyUserDebugs.currentEvent = 0;
			}else if(crankTeethSinceLastCamTooth != 4){
				if(crankTeethSinceLastCamTooth > 4){
					resetToNonRunningState(COUNT_OF_EVENTS_IMPOSSIBLY_HIGH_NOISE);
				}else{
					resetToNonRunningState(COUNT_OF_EVENTS_IMPOSSIBLY_LOW_NOISE);
				}
			}
		}else if(camTeethSeen == 3){
			camTeethSeen = 4;
			crankTeethSinceLastCamTooth =   KeyUserDebugs.primaryTeethSeen - previousCrankTeethSeen;
			previousCrankTeethSeen =  KeyUserDebugs.primaryTeethSeen;
			if(crankTeethSinceLastCamTooth == 12){
				SET_SYNC_LEVEL_TO(CAM_SYNC);
				KeyUserDebugs.currentEvent = 0;
			}else if(crankTeethSinceLastCamTooth != 4){
				if(crankTeethSinceLastCamTooth > 4){
					resetToNonRunningState(COUNT_OF_EVENTS_IMPOSSIBLY_HIGH_NOISE);
				}else{
					resetToNonRunningState(COUNT_OF_EVENTS_IMPOSSIBLY_LOW_NOISE);
				}
			}else{
				SET_SYNC_LEVEL_TO(CAM_SYNC); // Set the sync on the eventAngle[12] 360 degrees
				KeyUserDebugs.currentEvent = 12;
			}
		}else{
			resetToNonRunningState(BUG_REACHED_UNREACHABLE_CODE);
		}
	}
	DEBUG_TURN_PIN_OFF(DECODER_BENCHMARKS, NBIT0, PORTB);
}
void SecondaryRPMISR(){
	// Reads the inner slot on the disk.

	/* Clear the interrupt flag for this input compare channel */
	TFLG = 0x02;
	DEBUG_TURN_PIN_ON(DECODER_BENCHMARKS, BIT1, PORTB);

	/* Save all relevant available data here */
	edgeTimeStamp = TC1;				/* Save the timestamp */
	unsigned char PTITCurrentState = PTIT;	// TODO invert tests and other behaviour in this code base and remove this not.			/* Save the values on port T regardless of the state of DDRT */

	KeyUserDebugs.secondaryTeethSeen++;
	// remember that this is both edges, though... 8 per cycle, 4 per rev for the outter wheel, 2/1 for this wheel.

	/* 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 long thisEventTimeStamp = timeStamp.timeLong;

	unsigned long thisInterEventPeriod = 0;
	if(KeyUserDebugs.decoderFlags & LAST_TIMESTAMP_VALID){
		thisInterEventPeriod = thisEventTimeStamp - lastEventTimeStamp;
	}

	// Determine the correct event based on post transition state (and toggle debug pins)
	unsigned char correctEvent;
	if(PTITCurrentState & 0x02){
		//temp
		// Pins 0, 2, 4 and 7 - no need to check for numbers, just always do on rising edge and only in primary isr same for RPM above
		sampleEachADC(ADCBuffers);
		Counters.syncedADCreadings++;

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

		// Reset the clock for reading timeout
		Clocks.timeoutADCreadingClock = 0;

		correctEvent = 9;
	}else{
		//temp
		// Pins 0, 2, 4 and 7 - no need to check for numbers, just always do on rising edge and only in primary isr same for RPM above
		sampleEachADC(ADCBuffers);
		Counters.syncedADCreadings++;

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

		// Reset the clock for reading timeout
		Clocks.timeoutADCreadingClock = 0;

		correctEvent = 6;
	}

	unsigned char lastEvent = 0;
	if(KeyUserDebugs.decoderFlags & CAM_SYNC){
		lastEvent = KeyUserDebugs.currentEvent;
		KeyUserDebugs.currentEvent++;

		// ...and check that it's correct
		if(KeyUserDebugs.currentEvent != correctEvent){
			resetToNonRunningState(STATE_MISMATCH_IN_SECONDARY_RPM_ISR);
			return;
		}
	}else{	// If not synced, sync, as in this ISR we always know where we are.
		KeyUserDebugs.currentEvent = correctEvent;
		lastEvent = KeyUserDebugs.currentEvent - 1;
		SET_SYNC_LEVEL_TO(CAM_SYNC);
	}

	unsigned short thisTicksPerDegree = 0;
	if(KeyUserDebugs.decoderFlags & CAM_SYNC){
		unsigned short thisAngle = eventAngles[KeyUserDebugs.currentEvent] - eventAngles[lastEvent];

		thisTicksPerDegree = (unsigned short)((ticks_per_degree_multiplier * thisInterEventPeriod) / thisAngle); // with current scale range for 60/12000rpm is largest ticks per degree = 3472, smallest = 17 with largish error

		if(KeyUserDebugs.decoderFlags & LAST_PERIOD_VALID){
			unsigned short ratioBetweenThisAndLast = (unsigned short)(((unsigned long)lastTicksPerDegree * 1000) / thisTicksPerDegree);
			KeyUserDebugs.inputEventTimeTolerance = ratioBetweenThisAndLast;
			if(ratioBetweenThisAndLast > fixedConfigs2.decoderSettings.decelerationInputEventTimeTolerance){
				resetToNonRunningState(SECONDARY_EVENT_ARRIVED_TOO_LATE);
				return;
			}else if(ratioBetweenThisAndLast < fixedConfigs2.decoderSettings.accelerationInputEventTimeTolerance){
				resetToNonRunningState(SECONDARY_EVENT_ARRIVED_TOO_EARLY);
				return;
			}
		}/*else*/ if(KeyUserDebugs.decoderFlags & LAST_TIMESTAMP_VALID){
			*ticksPerDegreeRecord = thisTicksPerDegree;
		}
	}

	if(KeyUserDebugs.decoderFlags & CAM_SYNC){
		SCHEDULE_ECT_OUTPUTS();
	}

	OUTPUT_COARSE_BBS();

	if(KeyUserDebugs.decoderFlags & LAST_TIMESTAMP_VALID){
		lastTicksPerDegree = thisTicksPerDegree;
		KeyUserDebugs.decoderFlags |= LAST_PERIOD_VALID;
	}
	// Always
	lastEventTimeStamp = thisEventTimeStamp;
	KeyUserDebugs.decoderFlags |= LAST_TIMESTAMP_VALID;

	DEBUG_TURN_PIN_OFF(DECODER_BENCHMARKS, NBIT1, PORTB);
}
void PrimaryRPMISR(){
	/* Clear the interrupt flag for this input compare channel */
	TFLG = 0x01;
	DEBUG_TURN_PIN_ON(DECODER_BENCHMARKS, BIT0, PORTB);

	/* Save all relevant available data here */
	edgeTimeStamp = TC0;				/* Save the edge time stamp */
	unsigned char PTITCurrentState = PTIT;	// TODO invert tests and other behaviour in this code base and remove this not.		/* Save the values on port T regardless of the state of DDRT */

	KeyUserDebugs.primaryTeethSeen++;

	/* 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 long thisEventTimeStamp = timeStamp.timeLong;

	unsigned long thisInterEventPeriod = 0;
	if(KeyUserDebugs.decoderFlags & LAST_TIMESTAMP_VALID){
		thisInterEventPeriod = thisEventTimeStamp - lastEventTimeStamp;
	}

	// Determine the correct event based on post transition state (and toggle debug pins)
	unsigned char correctEvent = 0;
	if(PTITCurrentState & 0x01){
		// Pins 0, 2, 4 and 7 - no need to check for numbers, just always do on rising edge and only in primary isr same for RPM above
		sampleEachADC(ADCBuffers);
		Counters.syncedADCreadings++;

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

		// Reset the clock for reading timeout
		Clocks.timeoutADCreadingClock = 0;

		if(!(PTITCurrentState & 0x02)){
			correctEvent = 8;
		}
	}else{
		//temp
		// Pins 0, 2, 4 and 7 - no need to check for numbers, just always do on rising edge and only in primary isr same for RPM above
		sampleEachADC(ADCBuffers);
		Counters.syncedADCreadings++;

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

		// Reset the clock for reading timeout
		Clocks.timeoutADCreadingClock = 0;

		if(PTITCurrentState & 0x02){
			unknownEdges++;
			if(unknownEdges == 3){
				correctEvent = 4;
			}
		}else{
			correctEvent = 7;
			unknownEdges = 0;
		}
	}

	unsigned char lastEvent = 0;
	if(KeyUserDebugs.decoderFlags & CAM_SYNC){
		lastEvent = KeyUserDebugs.currentEvent;
		KeyUserDebugs.currentEvent++;
		if(KeyUserDebugs.currentEvent == numberOfRealEvents){
			KeyUserDebugs.currentEvent = 0;
		}

		// ...and check that it's correct
		if((correctEvent != 0) && (KeyUserDebugs.currentEvent != correctEvent)){
			resetToNonRunningState(STATE_MISMATCH_IN_PRIMARY_RPM_ISR);
			return;
		}
	}else if(correctEvent != 0){
		KeyUserDebugs.currentEvent = correctEvent;
		lastEvent = KeyUserDebugs.currentEvent - 1;
		SET_SYNC_LEVEL_TO(CAM_SYNC);
	}

	unsigned short thisTicksPerDegree = 0;
	if(KeyUserDebugs.decoderFlags & CAM_SYNC){
		unsigned short thisAngle = 0;
		if(KeyUserDebugs.currentEvent == 0){
			thisAngle = eventAngles[KeyUserDebugs.currentEvent] + totalEventAngleRange - eventAngles[lastEvent] ; // Optimisable... leave readable for now! :-p J/K learn from this...
		}else{
			thisAngle = eventAngles[KeyUserDebugs.currentEvent] - eventAngles[lastEvent];
		}

		thisTicksPerDegree = (unsigned short)((ticks_per_degree_multiplier * thisInterEventPeriod) / thisAngle); // with current scale range for 60/12000rpm is largest ticks per degree = 3472, smallest = 17 with largish error

		if(KeyUserDebugs.decoderFlags & LAST_PERIOD_VALID){
			unsigned short ratioBetweenThisAndLast = (unsigned short)(((unsigned long)lastTicksPerDegree * 1000) / thisTicksPerDegree);
			KeyUserDebugs.inputEventTimeTolerance = ratioBetweenThisAndLast;
			if(ratioBetweenThisAndLast > fixedConfigs2.decoderSettings.decelerationInputEventTimeTolerance){
				resetToNonRunningState(PRIMARY_EVENT_ARRIVED_TOO_LATE);
				return;
			}else if(ratioBetweenThisAndLast < fixedConfigs2.decoderSettings.accelerationInputEventTimeTolerance){
				resetToNonRunningState(PRIMARY_EVENT_ARRIVED_TOO_EARLY);
				return;
			}else{
				if(PTITCurrentState & 0x01){
					// TODO Calculate RPM from last primaryLeadingEdgeTimeStamp
				}else{
					// TODO Calculate RPM from last primaryTrailingEdgeTimeStamp
				}
			}
		}/*else*/ if(KeyUserDebugs.decoderFlags & LAST_TIMESTAMP_VALID){ // TODO temp for testing just do rpm this way, fill above out later.
			*ticksPerDegreeRecord = thisTicksPerDegree;
		}
	}

	if(KeyUserDebugs.decoderFlags & CAM_SYNC){
		SCHEDULE_ECT_OUTPUTS();
	}

	OUTPUT_COARSE_BBS();

	if(KeyUserDebugs.decoderFlags & LAST_TIMESTAMP_VALID){
		lastTicksPerDegree = thisTicksPerDegree;
		KeyUserDebugs.decoderFlags |= LAST_PERIOD_VALID;
	}
	// Always
	lastEventTimeStamp = thisEventTimeStamp;
	KeyUserDebugs.decoderFlags |= LAST_TIMESTAMP_VALID;

	DEBUG_TURN_PIN_OFF(DECODER_BENCHMARKS, NBIT0, PORTB);
}
/* Interrupt on rising and falling edges to count the number of teeth that have passed
 * in that window. 4 of the windows on the 8 tooth channel have a unique width. The pulse
 * accumulator will hold that count so there is no need to interrupt on the 360 tooth channel.
 *
 * Note: Primary LT1 Optispark Interrupt wired to the 8x channel.
 */
void PrimaryRPMISR(void) {
    /* Clear the interrupt flag for this input compare channel */
    TFLG = 0x01;
    // Grab this first as it is the most critical var in this decoder
    accumulatorRegisterCount = PACN1;/* save count before it changes */
    DEBUG_TURN_PIN_ON(DECODER_BENCHMARKS, BIT0, PORTB);

    /* Save all relevant available data here */
    unsigned char PTITCurrentState = PTIT; /* Save the values on port T regardless of the state of DDRT */
    unsigned short edgeTimeStamp = TC0; /* Save the edge time stamp */

    windowState = PTITCurrentState & 0x01; /* Save the high/low state of the port, HIGH PRIORITY some windows are only 2deg wide */
    unsigned char accumulatorCount = accumulatorRegisterCount - lastPARegisterReading;/* save count before it changes */
    lastPARegisterReading = accumulatorRegisterCount;
    unsigned char i; /* temp loop var */

    KeyUserDebugs.primaryTeethSeen++;
    KeyUserDebugs.secondaryTeethSeen += accumulatorCount;
//	DEBUG = accumulatorCount; // TODO remove DEBUG

    /* always make sure you have two good counts(there are a few windows that share counts) */
    if(!(KeyUserDebugs.decoderFlags & CAM_SYNC)) {
        // FRED do this on a per edge basis to lower chances of false match with +/- 1 counts
        if(accumulatorCount == AMBIGUOUS_COUNT) {
            return;
        } else {
            unsigned char lastEvent = 0xFF;
            for(i = 0; numberOfRealEvents > i; i++) {
                if(windowCounts[i] == accumulatorCount) {
                    if(i == 0) { /* keep our counter from going out of range */
                        KeyUserDebugs.currentEvent = 0xFF; // Will be rolled over to 0
                        lastEvent = NUMBER_OF_REAL_EVENTS - 1;
                    } else {
                        lastEvent = i - 1;
                        KeyUserDebugs.currentEvent = lastEvent; // Will be rolled up to current
                    }
                    break;
                }
            }

            if(lastEvent == 0xFF) { // Indicates that we didn't find a match, previously uncaught, would have occasionally matched last event with i = max and no match found on THIS event
                return;
            } else if(windowCounts[lastEvent] == lastAccumulatorCount) { /* if true we are in sync! */
                SET_SYNC_LEVEL_TO(CAM_SYNC);
            } else {
                // TODO missedsync opportunity ++ or something
            }

            lastAccumulatorCount = accumulatorCount;

            // TODO put fuzzy initial sync in place, maybe.
            //		// If still not synced, try to do fuzzy sync
            //		if(!(decoderFlags & CAM_SYNC)){
            //			// loop with +1 and -1
            //			// count fuzzy syncs, if genuine, should only be one + and one -
            //			// if not, give up and clear all state
            //		}
            //		return; // TODO remove and continue on down the thread
        }
    }

    // Not an else block because the if block above can change the state of its own condition
    if(KeyUserDebugs.decoderFlags & CAM_SYNC) {
        KeyUserDebugs.currentEvent++;
        if(KeyUserDebugs.currentEvent == numberOfRealEvents) { /* roll our event over if we are at the end */
            KeyUserDebugs.currentEvent = 0;
        }

        /*
         * bastardTeeth will be zero if things are going well, and a low number
         * if there is some latency, and a large number if totally wrong. This
         * will not catch sequences of same direction errors, though. We need
         * to keep a running track of past bastardTeeth too. TODO
         */

        signed char bastardTeeth = accumulatorCount - windowCounts[KeyUserDebugs.currentEvent];
        cumulativeBastardTeeth += bastardTeeth;

//		DEBUG = cumulativeBastardTeeth; // TODO remove DEBUG
//		DEBUG = bastardTeeth;
//		DEBUG = windowCounts[currentEvent]; // TODO remove DEBUG

        // Cumulative Tolerance Code TODO add counters to monitor aggressiveness of this
        if(windowsPerAllowedCumulativeBastardTooth) {
            cumulativeBastardTeethEroderCounter++;
            if(cumulativeBastardTeethEroderCounter == windowsPerAllowedCumulativeBastardTooth) {
                cumulativeBastardTeethEroderCounter = 0;
                if(cumulativeBastardTeeth > 0) {
                    cumulativeBastardTeeth--;
                    // DEBUG++;
                    // counter for decrement
                } else if(cumulativeBastardTeeth < 0) {
                    cumulativeBastardTeeth++;
                    // counter for increment
                } else {
                    // counter for does nothing
                }
            }
        }

        /* if we are in-sync continue checking and perform required decoder calcs */
        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;
        }

        if((bastardTeeth > MAX_BASTARD_TEETH) || (bastardTeeth < -MAX_BASTARD_TEETH)) {
            resetToNonRunningState(BASTARD_SYNC_LOSS_ID_BASE + bastardTeeth); // TODO move this to the syncLossIDs.h header
            return;
        } else if((cumulativeBastardTeeth > MAX_CUMULATIVE_BASTARD_TEETH) || (cumulativeBastardTeeth < -MAX_CUMULATIVE_BASTARD_TEETH)) {
            resetToNonRunningState(BASTARD_CUMULATIVE_SYNC_LOSS_ID_BASE + cumulativeBastardTeeth); // TODO move this to the syncLossIDs.h header
            return;
        } else { // Tooth count was within spec
            if(!(KeyUserDebugs.decoderFlags & OK_TO_SCHEDULE)) {
                SET_SYNC_LEVEL_TO(CAM_SYNC); // Add confirmation until it is
            }

            /* TODO all required calcs etc as shown in other working decoders */
            if((KeyUserDebugs.currentEvent % 2) == 1) { /* if we captured on a rising edge that is to say an evenly spaced edge perform the calcs */
                // temporary data from inputs
                unsigned long primaryLeadingEdgeTimeStamp = timeStamp.timeLong;
                unsigned long timeBetweenSuccessivePrimaryPulses = primaryLeadingEdgeTimeStamp - lastPrimaryEventTimeStamp;
                lastPrimaryEventTimeStamp = primaryLeadingEdgeTimeStamp;

                /* RPM CALC, KISS for now and only run this part of the ISR when the edge has gone high
                 * this way we have evenly spaced teeth
                 */
                *ticksPerDegreeRecord = (unsigned short)((ticks_per_degree_multiplier * timeBetweenSuccessivePrimaryPulses) / (90 * ANGLE_FACTOR));
                // instead of above:
                // save time difference
                // have angle of time difference as setting
                // do whole rpm calc in main loop to save ISR time, make more sense, and be more coherent to read
                // then it's possible to use different tpdm figures for different RPM levels, thereby allowing a large range AND fine granularity!
                // tpd would still need to be calculated for scheduling reasons, and the different scalings would need to be checked for overflow there.

                // TODO Once sampling/RPM is configurable, use this tooth for a lower MAP reading.
                sampleEachADC(ADCBuffers);
                Counters.syncedADCreadings++;
                /* Set flag to say calc required */
                coreStatusA |= CALC_FUEL_IGN;
                /* Reset the clock for reading timeout */
                Clocks.timeoutADCreadingClock = 0;
            }
        }
        SCHEDULE_ECT_OUTPUTS();
    }

    OUTPUT_COARSE_BBS();

    DEBUG_TURN_PIN_OFF(DECODER_BENCHMARKS, NBIT0, PORTB);
}
void PrimaryRPMISR(){
	/* Clear the interrupt flag for this input compare channel */
	TFLG = 0x01;
	DEBUG_TURN_PIN_ON(DECODER_BENCHMARKS, BIT0, PORTB);

	/* Save all relevant available data here */
	unsigned short edgeTimeStamp = TC0;				/* Save the edge time stamp */
	unsigned char PTITCurrentState = PTIT;			/* Save the values on port T regardless of the state of DDRT */

	KeyUserDebugs.primaryTeethSeen++;

	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 long thisEventTimeStamp = timeStamp.timeLong;

	unsigned char lastEvent = 0;
	unsigned short thisTicksPerDegree = 0;
	if(PTITCurrentState & 0x01){
		// temporary data from inputs
		unsigned long primaryLeadingEdgeTimeStamp = timeStamp.timeLong;
		unsigned long timeBetweenSuccessivePrimaryPulses = primaryLeadingEdgeTimeStamp - lastPrimaryEventTimeStamp;
		lastPrimaryEventTimeStamp = primaryLeadingEdgeTimeStamp;


		thisTicksPerDegree = (unsigned short)((ticks_per_degree_multiplier * timeBetweenSuccessivePrimaryPulses) / angleOfSingleIteration);
		*ticksPerDegreeRecord = thisTicksPerDegree;

		// TODO Once sampling/RPM is configurable, use this tooth for a lower MAP reading.
		sampleEachADC(ADCBuffers);
		Counters.syncedADCreadings++;

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

		/* Reset the clock for reading timeout */
		Clocks.timeoutADCreadingClock = 0;

		KeyUserDebugs.currentEvent = 1;
		lastEvent = 0;
	}else{
		// temporary data from inputs
		unsigned long secondaryLeadingEdgeTimeStamp = timeStamp.timeLong;
		unsigned long timeBetweenSuccessiveSecondaryPulses = secondaryLeadingEdgeTimeStamp - lastSecondaryEventTimeStamp;
		lastSecondaryEventTimeStamp = secondaryLeadingEdgeTimeStamp;


		thisTicksPerDegree = (unsigned short)((ticks_per_degree_multiplier * timeBetweenSuccessiveSecondaryPulses) / angleOfSingleIteration);
		*ticksPerDegreeRecord = thisTicksPerDegree;

		// TODO make this stuff behave correctly, this one will only run at startup, and the other will always run, but do it by generic config and split this stuff out into a shared function, soon.
		sampleEachADC(ADCBuffers);
		Counters.syncedADCreadings++;

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

		/* Reset the clock for reading timeout */
		Clocks.timeoutADCreadingClock = 0;

		KeyUserDebugs.currentEvent = 0;
		lastEvent = 1;
	}

	unsigned long thisInterEventPeriod = 0;
	if(KeyUserDebugs.decoderFlags & LAST_TIMESTAMP_VALID){
		thisInterEventPeriod = thisEventTimeStamp - lastEventTimeStamp;
	}

	// This should check during gain of sync too and prevent gaining sync if the numbers aren't right, however this is a step up for the Hotel at this time.
	if(KeyUserDebugs.decoderFlags & COMBUSTION_SYNC){
		unsigned short thisAngle = 0;
		if(KeyUserDebugs.currentEvent == 0){
			// Fix this to work for all:
//			thisAngle = eventAngles[KeyUserDebugs.currentEvent] + totalEventAngleRange - eventAngles[lastEvent] ; // Optimisable... leave readable for now! :-p J/K learn from this...
			thisAngle = eventAngles[KeyUserDebugs.currentEvent] + angleOfSingleIteration - eventAngles[lastEvent] ; // Optimisable... leave readable for now! :-p J/K learn from this...
		}else{
			thisAngle = eventAngles[KeyUserDebugs.currentEvent] - eventAngles[lastEvent];
		}

		thisTicksPerDegree = (unsigned short)((ticks_per_degree_multiplier * thisInterEventPeriod) / thisAngle); // with current scale range for 60/12000rpm is largest ticks per degree = 3472, smallest = 17 with largish error

		if(KeyUserDebugs.decoderFlags & LAST_PERIOD_VALID){
			unsigned short ratioBetweenThisAndLast = (unsigned short)(((unsigned long)lastTicksPerDegree * 1000) / thisTicksPerDegree);
			KeyUserDebugs.inputEventTimeTolerance = ratioBetweenThisAndLast;
			if(ratioBetweenThisAndLast > fixedConfigs2.decoderSettings.decelerationInputEventTimeTolerance){
				resetToNonRunningState(PRIMARY_EVENT_ARRIVED_TOO_LATE);
				return;
			}else if(ratioBetweenThisAndLast < fixedConfigs2.decoderSettings.accelerationInputEventTimeTolerance){
				resetToNonRunningState(PRIMARY_EVENT_ARRIVED_TOO_EARLY);
				return;
			}
		}
	}

	if(KeyUserDebugs.decoderFlags & COMBUSTION_SYNC){
		SCHEDULE_ECT_OUTPUTS();
	}

	OUTPUT_COARSE_BBS();

	/* TODO this delays outputs until the fourth ISR execution, but we could
	 * get them one execution or 1/8 of a rev sooner if we did a preliminary
	 * calc from the previous edge instead of the previous same edge now, and
	 *
	 * The proper way to do this is set sync when we have it and not set data
	 * as having been recorded until we know the data is good. That way the
	 * scheduler can keep things unscheduled until the time is right.
	 */
	if(KeyUserDebugs.decoderFlags & LAST_PERIOD_VALID){
		SET_SYNC_LEVEL_TO(COMBUSTION_SYNC);
	}

	if(KeyUserDebugs.decoderFlags & LAST_TIMESTAMP_VALID){
		lastTicksPerDegree = thisTicksPerDegree;
		KeyUserDebugs.decoderFlags |= LAST_PERIOD_VALID;
	}
	// Always
	lastEventTimeStamp = thisEventTimeStamp;
	KeyUserDebugs.decoderFlags |= LAST_TIMESTAMP_VALID;

	DEBUG_TURN_PIN_OFF(DECODER_BENCHMARKS, NBIT0, PORTB);
}
void PrimaryRPMISR(void) {
	/* Clear the interrupt flag for this input compare channel */
	TFLG = 0x01;
	DEBUG_TURN_PIN_ON(DECODER_BENCHMARKS, BIT0, PORTB);

	/* Save all relevant available data here */
	unsigned short edgeTimeStamp = TC0;    /* Save the edge time stamp */
	unsigned char PTITCurrentState = PTIT; /* Save the values on port T regardless of the state of DDRT */

	KeyUserDebugs.primaryTeethSeen++;

	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 long thisEventTimeStamp = timeStamp.timeLong;

	if(PTITCurrentState & 0x01){
		// Calc this period
		unsigned char lastEvent = 0;
		unsigned long thisInterEventPeriod = 0;
		if(KeyUserDebugs.decoderFlags & LAST_TIMESTAMP_VALID){
			thisInterEventPeriod = thisEventTimeStamp - lastEventTimeStamp;

			if(KeyUserDebugs.decoderFlags & LAST_PERIOD_VALID){
				unsigned long larger;
				unsigned long smaller;
				unsigned char thisLargerThanLast;
				if(thisInterEventPeriod > lastInterEventPeriod){
					larger = thisInterEventPeriod;
					smaller = lastInterEventPeriod;
					thisLargerThanLast = 1;
				}else{
					larger = lastInterEventPeriod;
					smaller = thisInterEventPeriod;
					thisLargerThanLast = 0;
				}

				// Calculate tolerance, then add and subtract it from whatever required
				unsigned long tolerance = (smaller * MISSING_TEETH * fixedConfigs2.decoderSettings.missingToothTolerance) / 4096;
				// div by 4k = fairly high minimum RPM for low teeth wheels
				// perhaps provide some options for different tolerance on different types of expected widths
				// the wide one on larger missing counts has more time to get to a higher RPM and needs a wider tolerance
				// possible options: different percent of smaller for each type, different percent and based on ideal w/b instead of smaller
				// Another option that keeps the 25% tolerance as the correct amount for any missing count is to simply take the percentage of
				// the smaller component and multiply by the number of missing teeth! This can be a flash config flag option or possibly rpm thresholded
				// it could be done on a per level basis too.
				unsigned long idealWide = 0;
				unsigned long idealBackward = 0;
				if(larger < (smaller + tolerance)){ // has to be first to be most efficient
					matches.pairs.thisPair = MatchedPair; // same period, roughly
				}else{
					idealWide = smaller * (MISSING_TEETH + 1); // has to be second to be most efficient
					if((larger < (idealWide + tolerance)) && (larger > (idealWide - tolerance))){
						if(thisLargerThanLast){
							matches.pairs.thisPair = NarrowWide;
						}else{
							matches.pairs.thisPair = WideNarrow;
						}
					}else{ // We're not in good shape...
						idealBackward = ((smaller * (MISSING_TEETH + 2)) / 2); // this leads to further code running later, so should come next
						if((larger <  (idealBackward + tolerance)) && (larger > (idealBackward - tolerance))){
							if(thisLargerThanLast){
								matches.pairs.thisPair = NarrowBackward;
							}else{
								matches.pairs.thisPair = BackwardNarrow;
							}
						}else if(larger > (idealWide + tolerance)){ // We're in very bad shape...
							if(thisLargerThanLast){
								resetToNonRunningState(yourVRSensorHasALoosePlugFixIt);
							}else{
								resetToNonRunningState(noiseAppearedWayTooEarlyAsIfItWasAVRToothButWasnt);
							}
						}else{ // Fell between the cracks, not matched, settings very tight, therefore was in two possible places on either side of (N+2)/2.
							resetToNonRunningState(yourSyncToleranceIsTighterThanAWellYouGetTheIdea);
						}
					}
				}

				// This all needs a little more complexity for cam only/crank only/crank + cam sync use, hard coded to crank only for now
				if(KeyUserDebugs.decoderFlags & LAST_MATCH_VALID){ // If we have enough data
					if(KeyUserDebugs.decoderFlags & CRANK_SYNC){
						lastEvent = KeyUserDebugs.currentEvent;
						KeyUserDebugs.currentEvent++;
						if(KeyUserDebugs.currentEvent == numberOfRealEvents){
							KeyUserDebugs.currentEvent = 0;
						}

						if((KeyUserDebugs.currentEvent == 0) && (matches.pattern != MatchedPairNarrowWide)){ // First event after gap
							resetToNonRunningState(matches.pattern + MaskBySumPattern);
						}else if((KeyUserDebugs.currentEvent == 1) && (matches.pattern != NarrowWideWideNarrow)){ // Second event after gap
							resetToNonRunningState(matches.pattern + MaskBySumPattern);
						}else if((KeyUserDebugs.currentEvent == 2) && (matches.pattern != WideNarrowMatchedPair)){ // Third event after gap
							resetToNonRunningState(matches.pattern + MaskBySumPattern);
						}else if((KeyUserDebugs.currentEvent > 2) && (matches.pattern != MatchedPairMatchedPair)){ // All other events should be preceeded by two matched pairs
							resetToNonRunningState(matches.pattern + MaskBySumPattern);
						} // else carry on happily as always
					}else{
						if(matches.pattern == MatchedPairMatchedPair){      //         | small | small | small | - All periods match, could be anywhere, unless...
							NumberOfTwinMatchedPairs++;
							// Because this method REQUIRES 4 evenly spaced teeth to work, it's only available to 5-1 or greater wheels.
							if((NUMBER_OF_WHEEL_EVENTS > 3) && (NumberOfTwinMatchedPairs == (NUMBER_OF_WHEEL_EVENTS - 3))){ // This can't find a match until it's on it's fourth execution
								// This will match repeatedly then un-sync on next cycle if tolerance is set too high
								KeyUserDebugs.currentEvent = NUMBER_OF_WHEEL_EVENTS - 1; // Zero indexed
								lastEvent = KeyUserDebugs.currentEvent - 1;
								SET_SYNC_LEVEL_TO(CRANK_SYNC); // Probability of this = (N + 1) / M
								// Sample RPM and ADCs here on the basis of cylinders and revolutions
								// IE, sample RPM once (total teeth (inc missing) per engine cycle / cyls) events have passed
								// And, do it from the last matching tooth, and do that on every tooth
								// So have a buffer of time stamps, which would take a LOT of RAM, hmmm, perhaps just wait.
								// Missing teeth users are clearly not fussed about fast starting anyway
								// And once sync is gained good readings can be taken without excess memory usage
							}else if((NUMBER_OF_WHEEL_EVENTS > 3) && (NumberOfTwinMatchedPairs > (NUMBER_OF_WHEEL_EVENTS - 3))){ // More matched pairs than possible with config
								resetToNonRunningState(yourSyncToleranceIsLooserThanAWellYouGetTheIdea);
							} // else fall through to wait.
						}else if(matches.pattern == MatchedPairNarrowWide){ // | small | small |      BIG      | Last tooth is first tooth after missing  - ((M-N)-3)/M = common
							KeyUserDebugs.currentEvent = 0;
							lastEvent = NUMBER_OF_WHEEL_EVENTS - 1; // Zero indexed
							SET_SYNC_LEVEL_TO(CRANK_SYNC);
						}else if(matches.pattern == NarrowWideWideNarrow){  // | small |      BIG      | small | Last tooth is second tooth after missing - 1/M
							KeyUserDebugs.currentEvent = 1;
							lastEvent = 0;
							SET_SYNC_LEVEL_TO(CRANK_SYNC);
						}else if(matches.pattern == WideNarrowMatchedPair){ // |      BIG      | small | small | Last tooth is third tooth after missing  - 1/M
							KeyUserDebugs.currentEvent = 2;
							lastEvent = 1;
							SET_SYNC_LEVEL_TO(CRANK_SYNC);
						}else{
							resetToNonRunningState(matches.pattern); // Where they are defined individually in the error file! Beautiful!!
						}
					}
				}
			}
		}

		unsigned short thisTicksPerDegree = 0;
		if(KeyUserDebugs.decoderFlags & CRANK_SYNC){
			unsigned short thisAngle = 0;
			if(KeyUserDebugs.currentEvent == 0){
				thisAngle = eventAngles[KeyUserDebugs.currentEvent] + angleOfSingleIteration - eventAngles[lastEvent] ; // Optimisable... leave readable for now! :-p J/K learn from this...
			}else{
				thisAngle = eventAngles[KeyUserDebugs.currentEvent] - eventAngles[lastEvent];
			}
			thisTicksPerDegree = (unsigned short)((ticks_per_degree_multiplier * thisInterEventPeriod) / thisAngle); // with current scale range for 60/12000rpm is largest ticks per degree = 3472, smallest = 17 with largish error

			if(KeyUserDebugs.decoderFlags & LAST_TPD_VALID){
				unsigned short ratioBetweenThisAndLast = (unsigned short)(((unsigned long)lastTicksPerDegree * 1000) / thisTicksPerDegree);
				KeyUserDebugs.inputEventTimeTolerance = ratioBetweenThisAndLast;
				if(ratioBetweenThisAndLast > fixedConfigs2.decoderSettings.decelerationInputEventTimeTolerance){
					resetToNonRunningState(PRIMARY_EVENT_ARRIVED_TOO_LATE);
					return;
				}else if(ratioBetweenThisAndLast < fixedConfigs2.decoderSettings.accelerationInputEventTimeTolerance){
					resetToNonRunningState(PRIMARY_EVENT_ARRIVED_TOO_EARLY);
					return;
				}
			}

			SCHEDULE_ECT_OUTPUTS();

			KeyUserDebugs.decoderFlags |= LAST_TPD_VALID;

			// sample adcs and record rpm here after scheduling
			*ticksPerDegreeRecord = thisTicksPerDegree;

			sampleEachADC(ADCBuffers);
			Counters.syncedADCreadings++;

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

			// Reset the clock for reading timeout
			Clocks.timeoutADCreadingClock = 0;
		}

		OUTPUT_COARSE_BBS();

		if(KeyUserDebugs.decoderFlags & LAST_TIMESTAMP_VALID){
			if(KeyUserDebugs.decoderFlags & LAST_PERIOD_VALID){
				KeyUserDebugs.decoderFlags |= LAST_MATCH_VALID;
			}
			matches.pairs.lastPair = matches.pairs.thisPair; // Stash var for next time
			lastInterEventPeriod = thisInterEventPeriod;
			lastTicksPerDegree = thisTicksPerDegree;
			KeyUserDebugs.decoderFlags |= LAST_PERIOD_VALID;
		}
		// Always
		lastEventTimeStamp = thisEventTimeStamp;
		KeyUserDebugs.decoderFlags |= LAST_TIMESTAMP_VALID;
	}else{
		// do checking for width variance too, perhaps optionally.
	}
	DEBUG_TURN_PIN_OFF(DECODER_BENCHMARKS, NBIT0, PORTB);
}