2015-07-10 06:01:56 -07:00
/**
* @ file trigger_structure . cpp
*
* @ date Jan 20 , 2014
2020-01-14 00:54:46 -08:00
* @ author Andrey Belomutskiy , ( c ) 2012 - 2020
2015-07-10 06:01:56 -07:00
*
* This file is part of rusEfi - see http : //rusefi.com
*
* rusEfi is free software ; you can redistribute it and / or modify it under the terms of
* the GNU General Public License as published by the Free Software Foundation ; either
* version 3 of the License , or ( at your option ) any later version .
*
* rusEfi is distributed in the hope that it will be useful , but WITHOUT ANY WARRANTY ; without
* even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
* GNU General Public License for more details .
*
* You should have received a copy of the GNU General Public License along with this program .
* If not , see < http : //www.gnu.org/licenses/>.
*/
2018-12-08 13:31:46 -08:00
2021-07-25 22:05:17 -07:00
# include "pch.h"
2022-09-07 12:56:45 -07:00
2018-12-25 19:47:29 -08:00
# include "trigger_chrysler.h"
# include "trigger_gm.h"
# include "trigger_nissan.h"
# include "trigger_mazda.h"
# include "trigger_misc.h"
# include "trigger_mitsubishi.h"
# include "trigger_subaru.h"
2021-10-04 16:28:45 -07:00
# include "trigger_suzuki.h"
2015-07-10 06:01:56 -07:00
# include "trigger_structure.h"
2018-12-25 19:47:29 -08:00
# include "trigger_toyota.h"
2020-04-18 19:20:17 -07:00
# include "trigger_renix.h"
2018-12-25 19:47:29 -08:00
# include "trigger_rover.h"
# include "trigger_honda.h"
# include "trigger_vw.h"
2017-01-03 13:02:43 -08:00
# include "trigger_universal.h"
2021-11-13 18:50:04 -08:00
# include "trigger_mercedes.h"
2018-12-25 19:47:29 -08:00
2019-04-12 19:07:03 -07:00
# if EFI_SENSOR_CHART
2017-05-18 13:39:04 -07:00
# include "sensor_chart.h"
2017-05-19 10:04:16 -07:00
# endif /* EFI_SENSOR_CHART */
2015-07-10 06:01:56 -07:00
2021-11-16 01:15:29 -08:00
void event_trigger_position_s : : setAngle ( angle_t angle ) {
2021-11-17 00:54:21 -08:00
findTriggerPosition ( & engine - > triggerCentral . triggerShape ,
& engine - > triggerCentral . triggerFormDetails ,
2021-11-16 01:15:29 -08:00
this , angle ) ;
2019-11-23 21:15:44 -08:00
}
2015-07-10 06:01:56 -07:00
2021-11-21 01:56:07 -08:00
TriggerWaveform : : TriggerWaveform ( ) {
2020-01-12 07:43:02 -08:00
initialize ( OM_NONE ) ;
2020-08-24 21:59:07 -07:00
}
2016-08-18 21:02:50 -07:00
2020-01-12 07:43:02 -08:00
void TriggerWaveform : : initialize ( operation_mode_e operationMode ) {
2016-08-17 21:02:22 -07:00
isSynchronizationNeeded = true ; // that's default value
2019-08-08 19:57:22 -07:00
bothFrontsRequired = false ;
2022-04-01 18:10:08 -07:00
isSecondWheelCam = false ;
2020-01-12 07:43:02 -08:00
needSecondTriggerInput = false ;
2020-01-14 00:54:46 -08:00
shapeWithoutTdc = false ;
2016-08-18 19:01:57 -07:00
2018-10-21 10:58:54 -07:00
setTriggerSynchronizationGap ( 2 ) ;
2018-12-25 07:20:13 -08:00
for ( int gapIndex = 1 ; gapIndex < GAP_TRACKING_LENGTH ; gapIndex + + ) {
2018-10-21 10:58:54 -07:00
// NaN means do not use this gap ratio
2018-12-25 07:20:13 -08:00
setTriggerSynchronizationGap3 ( gapIndex , NAN , 100000 ) ;
2018-10-21 10:58:54 -07:00
}
2020-10-04 10:05:49 -07:00
gapTrackingLength = 1 ;
2016-08-18 19:01:57 -07:00
2015-07-10 06:01:56 -07:00
tdcPosition = 0 ;
2017-02-23 10:53:27 -08:00
shapeDefinitionError = useOnlyPrimaryForSync = false ;
2015-07-10 06:01:56 -07:00
useRiseEdge = true ;
2017-03-18 17:59:52 -07:00
gapBothDirections = false ;
2015-07-10 06:01:56 -07:00
this - > operationMode = operationMode ;
triggerShapeSynchPointIndex = 0 ;
memset ( initialState , 0 , sizeof ( initialState ) ) ;
memset ( expectedEventCount , 0 , sizeof ( expectedEventCount ) ) ;
2021-11-21 01:56:07 -08:00
wave . reset ( ) ;
2022-06-01 18:24:20 -07:00
wave . waveCount = TRIGGER_INPUT_PIN_COUNT ;
2021-11-21 01:56:07 -08:00
wave . phaseCount = 0 ;
2015-07-10 06:01:56 -07:00
previousAngle = 0 ;
2019-02-02 22:49:41 -08:00
memset ( isRiseEvent , 0 , sizeof ( isRiseEvent ) ) ;
2019-04-12 19:07:03 -07:00
# if EFI_UNIT_TEST
2020-12-02 19:17:38 -08:00
memset ( & triggerSignalIndeces , 0 , sizeof ( triggerSignalIndeces ) ) ;
memset ( & triggerSignalStates , 0 , sizeof ( triggerSignalStates ) ) ;
2022-04-03 09:22:47 -07:00
knownOperationMode = true ;
# endif // EFI_UNIT_TEST
2015-07-10 06:01:56 -07:00
}
2020-03-03 05:37:02 -08:00
size_t TriggerWaveform : : getSize ( ) const {
2021-11-21 01:56:07 -08:00
return wave . phaseCount ;
2016-08-20 06:02:06 -07:00
}
2019-12-07 22:09:39 -08:00
int TriggerWaveform : : getTriggerWaveformSynchPointIndex ( ) const {
2016-08-20 06:02:06 -07:00
return triggerShapeSynchPointIndex ;
}
2015-07-10 06:01:56 -07:00
/**
2016-11-14 12:04:44 -08:00
* physical primary trigger duration
2020-08-08 14:00:14 -07:00
* @ see getEngineCycle
2021-07-01 19:10:37 -07:00
* @ see getCrankDivider
2015-07-10 06:01:56 -07:00
*/
2019-12-07 22:09:39 -08:00
angle_t TriggerWaveform : : getCycleDuration ( ) const {
2016-11-13 21:02:13 -08:00
switch ( operationMode ) {
2020-04-19 06:59:55 -07:00
case FOUR_STROKE_THREE_TIMES_CRANK_SENSOR :
2021-11-06 21:03:16 -07:00
return FOUR_STROKE_CYCLE_DURATION / SYMMETRICAL_THREE_TIMES_CRANK_SENSOR_DIVIDER ;
2016-11-13 21:02:13 -08:00
case FOUR_STROKE_SYMMETRICAL_CRANK_SENSOR :
2021-11-06 21:03:16 -07:00
return FOUR_STROKE_CYCLE_DURATION / SYMMETRICAL_CRANK_SENSOR_DIVIDER ;
2022-05-17 18:38:24 -07:00
case FOUR_STROKE_TWELVE_TIMES_CRANK_SENSOR :
return FOUR_STROKE_CYCLE_DURATION / SYMMETRICAL_TWELVE_TIMES_CRANK_SENSOR_DIVIDER ;
2016-11-13 21:02:13 -08:00
case FOUR_STROKE_CRANK_SENSOR :
2016-11-14 12:04:44 -08:00
case TWO_STROKE :
2021-11-06 21:03:16 -07:00
return TWO_STROKE_CYCLE_DURATION ;
2016-11-13 21:02:13 -08:00
default :
2021-11-06 21:03:16 -07:00
return FOUR_STROKE_CYCLE_DURATION ;
2016-11-13 21:02:13 -08:00
}
2015-07-10 06:01:56 -07:00
}
2022-06-09 14:21:22 -07:00
bool TriggerWaveform : : needsDisambiguation ( ) const {
2022-09-04 22:16:24 -07:00
switch ( getWheelOperationMode ( ) ) {
2022-06-09 14:21:22 -07:00
case FOUR_STROKE_CRANK_SENSOR :
case FOUR_STROKE_SYMMETRICAL_CRANK_SENSOR :
case FOUR_STROKE_THREE_TIMES_CRANK_SENSOR :
case FOUR_STROKE_TWELVE_TIMES_CRANK_SENSOR :
return true ;
case FOUR_STROKE_CAM_SENSOR :
case TWO_STROKE :
return false ;
default :
firmwareError ( OBD_PCM_Processor_Fault , " bad operationMode() in needsDisambiguation " ) ;
return true ;
}
}
2016-11-14 12:04:44 -08:00
/**
* Trigger event count equals engine cycle event count if we have a cam sensor .
* Two trigger cycles make one engine cycle in case of a four stroke engine If we only have a cranksensor .
2020-08-26 20:35:11 -07:00
*
* ' engine - > engineCycleEventCount ' hold a pre - calculated copy of this value as a performance optimization
2016-11-14 12:04:44 -08:00
*/
2020-03-03 05:37:02 -08:00
size_t TriggerWaveform : : getLength ( ) const {
2016-11-14 12:04:44 -08:00
/**
2022-05-17 18:38:24 -07:00
* 24 for FOUR_STROKE_TWELVE_TIMES_CRANK_SENSOR
2021-07-05 20:07:36 -07:00
* 6 for FOUR_STROKE_THREE_TIMES_CRANK_SENSOR
2016-11-14 12:04:44 -08:00
* 4 for FOUR_STROKE_SYMMETRICAL_CRANK_SENSOR
* 2 for FOUR_STROKE_CRANK_SENSOR
* 1 otherwise
*/
int multiplier = getEngineCycle ( operationMode ) / getCycleDuration ( ) ;
return multiplier * getSize ( ) ;
}
2019-12-07 22:09:39 -08:00
angle_t TriggerWaveform : : getAngle ( int index ) const {
2015-07-10 06:01:56 -07:00
/**
* FOUR_STROKE_CRANK_SENSOR magic :
* We have two crank shaft revolutions for each engine cycle
* See also trigger_central . cpp
* See also getEngineCycleEventCount ( )
*/
2021-11-21 01:56:07 -08:00
efiAssert ( CUSTOM_ERR_ASSERT , wave . phaseCount ! = 0 , " shapeSize=0 " , NAN ) ;
int crankCycle = index / wave . phaseCount ;
int remainder = index % wave . phaseCount ;
2016-11-13 21:02:13 -08:00
2021-07-17 20:27:20 -07:00
auto cycleStartAngle = getCycleDuration ( ) * crankCycle ;
auto positionWithinCycle = getSwitchAngle ( remainder ) ;
return cycleStartAngle + positionWithinCycle ;
2015-07-10 06:01:56 -07:00
}
2022-09-11 00:46:50 -07:00
void TriggerWaveform : : addEventClamped ( angle_t angle , TriggerWheel const channelIndex , TriggerValue const stateParam , float filterLeft , float filterRight ) {
2019-07-23 16:22:40 -07:00
if ( angle > filterLeft & & angle < filterRight ) {
# if EFI_UNIT_TEST
// printf("addEventClamped %f %s\r\n", angle, getTrigger_value_e(stateParam));
# endif /* EFI_UNIT_TEST */
2019-02-02 22:34:20 -08:00
addEvent ( angle / getEngineCycle ( operationMode ) , channelIndex , stateParam ) ;
2019-07-23 16:22:40 -07:00
}
2015-07-10 06:01:56 -07:00
}
2022-09-04 22:16:24 -07:00
/**
* See also Engine # getOperationMode which accounts for additional settings which are
* needed to resolve precise mode for vague wheels
*/
operation_mode_e TriggerWaveform : : getWheelOperationMode ( ) const {
2015-07-10 06:01:56 -07:00
return operationMode ;
}
2019-04-12 19:07:03 -07:00
# if EFI_UNIT_TEST
2015-09-10 20:01:32 -07:00
extern bool printTriggerDebug ;
# endif
2022-09-11 00:46:50 -07:00
size_t TriggerWaveform : : getExpectedEventCount ( TriggerWheel channelIndex ) const {
return expectedEventCount [ ( int ) channelIndex ] ;
2021-06-26 19:07:26 -07:00
}
2019-12-07 22:09:39 -08:00
void TriggerWaveform : : calculateExpectedEventCounts ( bool useOnlyRisingEdgeForTrigger ) {
2021-01-04 20:34:19 -08:00
if ( ! useOnlyRisingEdgeForTrigger ) {
2021-03-20 05:40:36 -07:00
for ( size_t i = 0 ; i < efi : : size ( expectedEventCount ) ; i + + ) {
2022-09-11 00:46:50 -07:00
if ( getExpectedEventCount ( ( TriggerWheel ) i ) % 2 ! = 0 ) {
firmwareError ( ERROR_TRIGGER_DRAMA , " Trigger: should be even number of events index=%d count=%d " , i , getExpectedEventCount ( ( TriggerWheel ) i ) ) ;
2021-01-04 20:34:19 -08:00
}
}
}
2022-09-11 00:46:50 -07:00
bool isSingleToothOnPrimaryChannel = useOnlyRisingEdgeForTrigger ? getExpectedEventCount ( TriggerWheel : : T_PRIMARY ) = = 1 : getExpectedEventCount ( TriggerWheel : : T_PRIMARY ) = = 2 ;
2020-01-14 00:54:46 -08:00
// todo: next step would be to set 'isSynchronizationNeeded' automatically based on the logic we have here
if ( ! shapeWithoutTdc & & isSingleToothOnPrimaryChannel ! = ! isSynchronizationNeeded ) {
2022-03-19 14:51:47 -07:00
firmwareError ( ERROR_TRIGGER_DRAMA , " shapeWithoutTdc isSynchronizationNeeded isSingleToothOnPrimaryChannel constraint violation " ) ;
2020-01-14 00:54:46 -08:00
}
2021-03-07 21:00:48 -08:00
if ( isSingleToothOnPrimaryChannel ) {
useOnlyPrimaryForSync = true ;
}
2020-01-14 00:54:46 -08:00
2018-12-24 20:16:33 -08:00
// todo: move the following logic from below here
2022-09-10 23:57:35 -07:00
// if (!useOnlyRisingEdgeForTrigger || stateParam == TriggerValue::RISE) {
2018-12-25 07:13:00 -08:00
// expectedEventCount[channelIndex]++;
2018-12-24 20:16:33 -08:00
// }
}
2021-11-14 12:35:11 -08:00
/**
* Deprecated ! many usages should be replaced by addEvent360
*/
2022-09-11 00:46:50 -07:00
void TriggerWaveform : : addEvent720 ( angle_t angle , TriggerWheel const channelIndex , TriggerValue const state ) {
2021-11-14 12:35:11 -08:00
addEvent ( angle / FOUR_STROKE_CYCLE_DURATION , channelIndex , state ) ;
2020-04-18 17:28:03 -07:00
}
2022-09-11 00:46:50 -07:00
void TriggerWaveform : : addEvent360 ( angle_t angle , TriggerWheel const channelIndex , TriggerValue const state ) {
2021-11-14 12:35:11 -08:00
efiAssertVoid ( CUSTOM_OMODE_UNDEF , operationMode = = FOUR_STROKE_CAM_SENSOR | | operationMode = = FOUR_STROKE_CRANK_SENSOR , " Not a mode for 360 " ) ;
addEvent ( CRANK_MODE_MULTIPLIER * angle / FOUR_STROKE_CYCLE_DURATION , channelIndex , state ) ;
2021-07-16 21:45:47 -07:00
}
2022-09-11 00:46:50 -07:00
void TriggerWaveform : : addEventAngle ( angle_t angle , TriggerWheel const channelIndex , TriggerValue const state ) {
2020-04-18 17:28:03 -07:00
addEvent ( angle / getCycleDuration ( ) , channelIndex , state ) ;
2018-12-25 07:20:13 -08:00
}
2022-09-11 00:46:50 -07:00
void TriggerWaveform : : addEvent ( angle_t angle , TriggerWheel const channelIndex , TriggerValue const state ) {
2018-09-10 19:10:55 -07:00
efiAssertVoid ( CUSTOM_OMODE_UNDEF , operationMode ! = OM_NONE , " operationMode not set " ) ;
2015-07-10 06:01:56 -07:00
2022-09-11 00:46:50 -07:00
if ( channelIndex = = TriggerWheel : : T_SECONDARY ) {
2020-01-12 07:43:02 -08:00
needSecondTriggerInput = true ;
}
2015-07-10 06:01:56 -07:00
2019-04-12 19:07:03 -07:00
# if EFI_UNIT_TEST
2015-09-10 20:01:32 -07:00
if ( printTriggerDebug ) {
2021-11-06 23:57:32 -07:00
printf ( " addEvent2 %.2f i=%d r/f=%d \r \n " , angle , channelIndex , state ) ;
2015-09-10 20:01:32 -07:00
}
# endif
2019-04-12 19:07:03 -07:00
# if EFI_UNIT_TEST
2021-11-21 01:56:07 -08:00
assertIsInBounds ( wave . phaseCount , triggerSignalIndeces , " trigger shape overflow " ) ;
triggerSignalIndeces [ wave . phaseCount ] = channelIndex ;
triggerSignalStates [ wave . phaseCount ] = state ;
2021-01-04 18:05:16 -08:00
# endif // EFI_UNIT_TEST
2015-07-10 06:01:56 -07:00
2019-02-02 22:34:20 -08:00
// todo: the whole 'useOnlyRisingEdgeForTrigger' parameter and logic should not be here
// todo: see calculateExpectedEventCounts
// related calculation should be done once trigger is initialized outside of trigger shape scope
2022-09-10 23:57:35 -07:00
if ( ! useOnlyRisingEdgeForTriggerTemp | | state = = TriggerValue : : RISE ) {
2022-09-11 00:46:50 -07:00
expectedEventCount [ ( int ) channelIndex ] + + ;
2015-07-10 06:01:56 -07:00
}
2022-03-19 14:51:47 -07:00
if ( angle < = 0 | | angle > 1 ) {
firmwareError ( CUSTOM_ERR_6599 , " angle should be positive not above 1: index=%d angle %f " , channelIndex , angle ) ;
return ;
}
2021-11-21 01:56:07 -08:00
if ( wave . phaseCount > 0 ) {
2015-07-10 06:01:56 -07:00
if ( angle < = previousAngle ) {
2022-09-10 23:16:47 -07:00
warning ( CUSTOM_ERR_TRG_ANGLE_ORDER , " invalid angle order %s %s: new=%.2f/%f and prev=%.2f/%f, size=%d " ,
2022-09-11 09:40:24 -07:00
getTriggerWheel ( channelIndex ) ,
2022-09-10 23:16:47 -07:00
getTrigger_value_e ( state ) ,
2020-05-08 22:49:17 -07:00
angle , angle * getCycleDuration ( ) ,
previousAngle , previousAngle * getCycleDuration ( ) ,
2021-11-21 01:56:07 -08:00
wave . phaseCount ) ;
2019-08-07 19:02:08 -07:00
setShapeDefinitionError ( true ) ;
2015-07-10 06:01:56 -07:00
return ;
}
}
previousAngle = angle ;
2021-11-21 01:56:07 -08:00
if ( wave . phaseCount = = 0 ) {
wave . phaseCount = 1 ;
2015-07-10 06:01:56 -07:00
for ( int i = 0 ; i < PWM_PHASE_MAX_WAVE_PER_PWM ; i + + ) {
2021-11-21 01:56:07 -08:00
wave . setChannelState ( i , /* switchIndex */ 0 , /* value */ initialState [ i ] ) ;
2015-07-10 06:01:56 -07:00
}
2022-09-10 23:57:35 -07:00
isRiseEvent [ 0 ] = TriggerValue : : RISE = = state ;
2021-11-21 01:56:07 -08:00
wave . setSwitchTime ( 0 , angle ) ;
2022-09-11 00:46:50 -07:00
wave . setChannelState ( ( int ) channelIndex , /* channelIndex */ 0 , /* value */ state ) ;
2015-07-10 06:01:56 -07:00
return ;
}
2022-08-28 06:43:21 -07:00
if ( wave . findAngleMatch ( angle ) ) {
2017-04-19 19:03:14 -07:00
warning ( CUSTOM_ERR_SAME_ANGLE , " same angle: not supported " ) ;
2019-08-07 19:02:08 -07:00
setShapeDefinitionError ( true ) ;
2015-07-10 06:01:56 -07:00
return ;
}
2021-11-21 01:56:07 -08:00
int index = wave . findInsertionAngle ( angle ) ;
2015-07-10 06:01:56 -07:00
2018-04-29 23:22:54 -07:00
/**
* todo : it would be nice to be able to provide trigger angles without sorting them externally
* The idea here is to shift existing data - including handling high vs low state of the signals
*/
2018-02-05 14:16:34 -08:00
// todo: does this logic actually work? I think it does not! due to broken state handling
2018-04-29 23:22:54 -07:00
/*
2015-07-10 06:01:56 -07:00
for ( int i = size - 1 ; i > = index ; i - - ) {
for ( int j = 0 ; j < PWM_PHASE_MAX_WAVE_PER_PWM ; j + + ) {
2021-11-21 01:56:07 -08:00
wave . waves [ j ] . pinStates [ i + 1 ] = wave . getChannelState ( j , index ) ;
2015-07-10 06:01:56 -07:00
}
2021-11-21 01:56:07 -08:00
wave . setSwitchTime ( i + 1 , wave . getSwitchTime ( i ) ) ;
2015-07-10 06:01:56 -07:00
}
2018-04-29 23:22:54 -07:00
*/
2022-09-10 23:57:35 -07:00
isRiseEvent [ index ] = TriggerValue : : RISE = = state ;
2015-07-10 06:01:56 -07:00
2021-11-21 01:56:07 -08:00
if ( ( unsigned ) index ! = wave . phaseCount ) {
2017-05-29 16:23:15 -07:00
firmwareError ( ERROR_TRIGGER_DRAMA , " are we ever here? " ) ;
2015-07-10 06:01:56 -07:00
}
2021-11-21 01:56:07 -08:00
wave . phaseCount + + ;
2015-07-10 06:01:56 -07:00
for ( int i = 0 ; i < PWM_PHASE_MAX_WAVE_PER_PWM ; i + + ) {
2021-11-21 01:56:07 -08:00
pin_state_t value = wave . getChannelState ( /* channelIndex */ i , index - 1 ) ;
wave . setChannelState ( i , index , value ) ;
2015-07-10 06:01:56 -07:00
}
2021-11-21 01:56:07 -08:00
wave . setSwitchTime ( index , angle ) ;
2022-09-11 00:46:50 -07:00
wave . setChannelState ( ( int ) channelIndex , index , state ) ;
2015-07-10 06:01:56 -07:00
}
2019-12-07 22:09:39 -08:00
angle_t TriggerWaveform : : getSwitchAngle ( int index ) const {
2021-11-21 01:56:07 -08:00
return getCycleDuration ( ) * wave . getSwitchTime ( index ) ;
2015-07-10 06:01:56 -07:00
}
2019-12-07 22:09:39 -08:00
void setToothedWheelConfiguration ( TriggerWaveform * s , int total , int skipped ,
2018-12-25 17:09:35 -08:00
operation_mode_e operationMode ) {
2019-04-12 19:07:03 -07:00
# if EFI_ENGINE_CONTROL
2015-07-10 06:01:56 -07:00
s - > useRiseEdge = true ;
2019-12-07 22:09:39 -08:00
initializeSkippedToothTriggerWaveformExt ( s , total , skipped ,
2018-12-25 17:09:35 -08:00
operationMode ) ;
2015-07-10 06:01:56 -07:00
# endif
}
2019-12-07 22:09:39 -08:00
void TriggerWaveform : : setTriggerSynchronizationGap2 ( float syncRatioFrom , float syncRatioTo ) {
2018-12-25 07:20:13 -08:00
setTriggerSynchronizationGap3 ( /*gapIndex*/ 0 , syncRatioFrom , syncRatioTo ) ;
2018-10-21 10:41:01 -07:00
}
2019-12-07 22:09:39 -08:00
void TriggerWaveform : : setTriggerSynchronizationGap3 ( int gapIndex , float syncRatioFrom , float syncRatioTo ) {
2015-07-10 06:01:56 -07:00
isSynchronizationNeeded = true ;
2020-12-04 12:22:53 -08:00
efiAssertVoid ( OBD_PCM_Processor_Fault , gapIndex > = 0 & & gapIndex < GAP_TRACKING_LENGTH , " gapIndex out of range " ) ;
2021-05-25 14:15:48 -07:00
syncronizationRatioFrom [ gapIndex ] = syncRatioFrom ;
syncronizationRatioTo [ gapIndex ] = syncRatioTo ;
2018-12-25 07:20:13 -08:00
if ( gapIndex = = 0 ) {
2018-10-21 10:41:01 -07:00
// we have a special case here - only sync with one gap has this feature
this - > syncRatioAvg = ( int ) efiRound ( ( syncRatioFrom + syncRatioTo ) * 0.5f , 1.0f ) ;
}
2020-10-04 10:05:49 -07:00
gapTrackingLength = maxI ( 1 + gapIndex , gapTrackingLength ) ;
2018-10-21 10:41:01 -07:00
2019-04-12 19:07:03 -07:00
# if EFI_UNIT_TEST
2017-03-04 16:47:15 -08:00
if ( printTriggerDebug ) {
2018-12-25 07:20:13 -08:00
printf ( " setTriggerSynchronizationGap3 %d %.2f %.2f \r \n " , gapIndex , syncRatioFrom , syncRatioTo ) ;
2017-03-04 16:47:15 -08:00
}
# endif /* EFI_UNIT_TEST */
2018-10-21 10:41:01 -07:00
2015-07-10 06:01:56 -07:00
}
2021-07-17 14:47:13 -07:00
uint16_t TriggerWaveform : : findAngleIndex ( TriggerFormDetails * details , angle_t targetAngle ) const {
2021-01-16 18:22:30 -08:00
size_t engineCycleEventCount = getLength ( ) ;
2018-12-25 09:27:34 -08:00
2021-11-09 04:03:27 -08:00
efiAssert ( CUSTOM_ERR_ASSERT , engineCycleEventCount ! = 0 & & engineCycleEventCount < = 0xFFFF ,
" engineCycleEventCount " , 0 ) ;
2018-12-25 09:27:34 -08:00
uint32_t left = 0 ;
uint32_t right = engineCycleEventCount - 1 ;
/**
* Let ' s find the last trigger angle which is less or equal to the desired angle
* todo : extract binary search as template method ?
*/
2021-11-09 04:03:27 -08:00
do {
uint32_t middle = ( left + right ) / 2 ;
if ( details - > eventAngles [ middle ] < = targetAngle ) {
left = middle + 1 ;
} else {
right = middle - 1 ;
}
} while ( left < = right ) ;
left - = 1 ;
if ( useOnlyRisingEdgeForTriggerTemp ) {
left & = ~ 1U ;
}
return left ;
2018-12-25 09:27:34 -08:00
}
2019-12-07 22:09:39 -08:00
void TriggerWaveform : : setShapeDefinitionError ( bool value ) {
2019-08-07 19:02:08 -07:00
shapeDefinitionError = value ;
}
2020-08-24 21:59:07 -07:00
void findTriggerPosition ( TriggerWaveform * triggerShape ,
TriggerFormDetails * details ,
event_trigger_position_s * position ,
2021-11-16 01:15:29 -08:00
angle_t angle ) {
2019-10-07 21:30:20 -07:00
efiAssertVoid ( CUSTOM_ERR_6574 , ! cisnan ( angle ) , " findAngle#1 " ) ;
assertAngleRange ( angle , " findAngle#a1 " , CUSTOM_ERR_6545 ) ;
2018-12-25 09:27:34 -08:00
2020-08-24 21:59:07 -07:00
efiAssertVoid ( CUSTOM_ERR_6575 , ! cisnan ( triggerShape - > tdcPosition ) , " tdcPos#1 " )
assertAngleRange ( triggerShape - > tdcPosition , " tdcPos#a1 " , CUSTOM_UNEXPECTED_TDC_ANGLE ) ;
2018-12-25 09:27:34 -08:00
2021-11-17 00:54:21 -08:00
efiAssertVoid ( CUSTOM_ERR_6576 , ! cisnan ( engineConfiguration - > globalTriggerAngleOffset ) , " tdcPos#2 " )
assertAngleRange ( engineConfiguration - > globalTriggerAngleOffset , " tdcPos#a2 " , CUSTOM_INVALID_GLOBAL_OFFSET ) ;
2018-12-25 09:27:34 -08:00
// convert engine cycle angle into trigger cycle angle
2021-11-17 00:54:21 -08:00
angle + = triggerShape - > tdcPosition + engineConfiguration - > globalTriggerAngleOffset ;
2019-10-07 21:30:20 -07:00
efiAssertVoid ( CUSTOM_ERR_6577 , ! cisnan ( angle ) , " findAngle#2 " ) ;
2022-09-04 22:16:24 -07:00
wrapAngle2 ( angle , " addFuel#2 " , CUSTOM_ERR_6555 , getEngineCycle ( triggerShape - > getWheelOperationMode ( ) ) ) ;
2018-12-25 09:27:34 -08:00
2021-11-09 04:03:27 -08:00
int triggerEventIndex = triggerShape - > findAngleIndex ( details , angle ) ;
2020-08-24 21:59:07 -07:00
angle_t triggerEventAngle = details - > eventAngles [ triggerEventIndex ] ;
2021-07-17 20:27:20 -07:00
angle_t offsetFromTriggerEvent = angle - triggerEventAngle ;
// Guarantee that we aren't going to try and schedule an event prior to the tooth
if ( offsetFromTriggerEvent < 0 ) {
2019-10-07 21:42:47 -07:00
warning ( CUSTOM_OBD_ANGLE_CONSTRAINT_VIOLATION , " angle constraint violation in findTriggerPosition(): %.2f/%.2f " , angle , triggerEventAngle ) ;
2018-12-25 09:27:34 -08:00
return ;
}
2021-01-11 05:01:54 -08:00
{
// This must happen under lock so that the tooth and offset don't get partially read and mismatched
chibios_rt : : CriticalSectionLocker csl ;
position - > triggerEventIndex = triggerEventIndex ;
2021-07-17 20:27:20 -07:00
position - > angleOffsetFromTriggerEvent = offsetFromTriggerEvent ;
2021-01-11 05:01:54 -08:00
}
2018-12-25 09:27:34 -08:00
}
2022-05-29 10:49:00 -07:00
void TriggerWaveform : : prepareShape ( TriggerFormDetails & details ) {
2019-04-12 19:07:03 -07:00
# if EFI_ENGINE_CONTROL && EFI_SHAFT_POSITION_INPUT
2021-09-05 02:56:59 -07:00
if ( shapeDefinitionError ) {
// Nothing to do here if there's a problem with the trigger shape
return ;
}
2022-05-29 10:49:00 -07:00
details . prepareEventAngles ( this ) ;
2019-01-31 15:30:40 -08:00
# endif
2018-12-25 19:47:29 -08:00
}
2019-12-07 22:09:39 -08:00
void TriggerWaveform : : setTriggerSynchronizationGap ( float syncRatio ) {
2020-12-04 13:24:19 -08:00
setTriggerSynchronizationGap3 ( /*gapIndex*/ 0 , syncRatio * TRIGGER_GAP_DEVIATION_LOW , syncRatio * TRIGGER_GAP_DEVIATION_HIGH ) ;
2015-09-12 12:02:40 -07:00
}
2021-10-24 14:12:19 -07:00
void TriggerWaveform : : setSecondTriggerSynchronizationGap ( float syncRatio ) {
setTriggerSynchronizationGap3 ( /*gapIndex*/ 1 , syncRatio * TRIGGER_GAP_DEVIATION_LOW , syncRatio * TRIGGER_GAP_DEVIATION_HIGH ) ;
}
2019-12-07 22:09:39 -08:00
void TriggerWaveform : : setSecondTriggerSynchronizationGap2 ( float syncRatioFrom , float syncRatioTo ) {
2018-12-25 07:20:13 -08:00
setTriggerSynchronizationGap3 ( /*gapIndex*/ 1 , syncRatioFrom , syncRatioTo ) ;
2015-09-12 12:02:40 -07:00
}
2019-12-07 22:09:39 -08:00
void TriggerWaveform : : setThirdTriggerSynchronizationGap ( float syncRatio ) {
2020-12-04 13:24:19 -08:00
setTriggerSynchronizationGap3 ( /*gapIndex*/ 2 , syncRatio * TRIGGER_GAP_DEVIATION_LOW , syncRatio * TRIGGER_GAP_DEVIATION_HIGH ) ;
2015-12-27 13:02:44 -08:00
}
2018-12-25 19:47:29 -08:00
/**
* External logger is needed because at this point our logger is not yet initialized
*/
2022-05-31 21:55:34 -07:00
void TriggerWaveform : : initializeTriggerWaveform ( operation_mode_e triggerOperationMode , const TriggerConfiguration & triggerConfig ) {
2018-12-25 19:47:29 -08:00
2019-04-12 19:07:03 -07:00
# if EFI_PROD_CODE
2019-12-13 15:02:24 -08:00
efiAssertVoid ( CUSTOM_ERR_6641 , getCurrentRemainingStack ( ) > EXPECTED_REMAINING_STACK , " init t " ) ;
2022-05-30 16:36:47 -07:00
efiPrintf ( " initializeTriggerWaveform(%s/%d) " , getTrigger_type_e ( triggerConfig . TriggerType . type ) , ( int ) triggerConfig . TriggerType . type ) ;
2018-12-25 19:47:29 -08:00
# endif
shapeDefinitionError = false ;
2022-05-31 21:55:34 -07:00
bool useOnlyRisingEdgeForTrigger = triggerConfig . UseOnlyRisingEdgeForTrigger ;
2018-12-25 19:47:29 -08:00
this - > useOnlyRisingEdgeForTriggerTemp = useOnlyRisingEdgeForTrigger ;
2022-05-30 16:36:47 -07:00
switch ( triggerConfig . TriggerType . type ) {
2018-12-25 19:47:29 -08:00
case TT_TOOTHED_WHEEL :
2022-04-02 16:31:31 -07:00
/**
* huh ? why all know skipped wheel shapes use ' setToothedWheelConfiguration ' method
* which touches ' useRiseEdge ' flag while here we do not touch it ? !
*/
2022-05-30 16:36:47 -07:00
initializeSkippedToothTriggerWaveformExt ( this , triggerConfig . TriggerType . customTotalToothCount ,
triggerConfig . TriggerType . customSkippedToothCount , triggerOperationMode ) ;
2018-12-25 19:47:29 -08:00
break ;
case TT_MAZDA_MIATA_NA :
2019-02-02 21:40:25 -08:00
initializeMazdaMiataNaShape ( this ) ;
2018-12-25 19:47:29 -08:00
break ;
case TT_MAZDA_MIATA_NB1 :
2022-01-31 15:20:43 -08:00
# if EFI_PROD_CODE
// todo: remove this fatal and remove 'TT_MAZDA_MIATA_NB1' in May of 2022
firmwareError ( CUSTOM_ERR_TEST_ERROR , " Miata NB1 needs to adjust trigger configuration " ) ;
# endif
initializeMazdaMiataVVtTestShape ( this ) ;
2018-12-25 19:47:29 -08:00
break ;
case TT_MAZDA_MIATA_VVT_TEST :
initializeMazdaMiataVVtTestShape ( this ) ;
break ;
2021-10-04 08:25:27 -07:00
case TT_SUZUKI_G13B :
2021-10-04 16:28:45 -07:00
initializeSuzukiG13B ( this ) ;
break ;
2021-09-11 20:42:05 -07:00
case TT_FORD_TFI_PIP :
2021-09-12 08:56:12 -07:00
configureFordPip ( this ) ;
break ;
2020-12-03 20:54:08 -08:00
case TT_FORD_ST170 :
configureFordST170 ( this ) ;
break ;
2022-01-31 15:20:43 -08:00
case TT_VVT_MIATA_NB :
2020-02-04 22:49:33 -08:00
initializeMazdaMiataVVtCamShape ( this ) ;
break ;
2020-04-19 06:59:55 -07:00
case TT_RENIX_66_2_2_2 :
2020-04-19 11:54:02 -07:00
initializeRenix66_2_2 ( this ) ;
break ;
2020-04-18 19:20:17 -07:00
case TT_RENIX_44_2_2 :
2020-04-19 06:59:55 -07:00
initializeRenix44_2_2 ( this ) ;
2020-04-18 19:20:17 -07:00
break ;
2018-12-25 19:47:29 -08:00
case TT_MIATA_VVT :
initializeMazdaMiataNb2Crank ( this ) ;
break ;
case TT_DODGE_NEON_1995 :
2019-12-07 22:09:39 -08:00
configureNeon1995TriggerWaveform ( this ) ;
2018-12-25 19:47:29 -08:00
break ;
case TT_DODGE_NEON_1995_ONLY_CRANK :
2019-12-07 22:09:39 -08:00
configureNeon1995TriggerWaveformOnlyCrank ( this ) ;
2018-12-25 19:47:29 -08:00
break ;
case TT_DODGE_STRATUS :
2019-12-07 22:09:39 -08:00
configureDodgeStratusTriggerWaveform ( this ) ;
2018-12-25 19:47:29 -08:00
break ;
case TT_DODGE_NEON_2003_CAM :
2019-12-07 22:09:39 -08:00
configureNeon2003TriggerWaveformCam ( this ) ;
2018-12-25 19:47:29 -08:00
break ;
case TT_DODGE_NEON_2003_CRANK :
2019-12-07 22:09:39 -08:00
configureNeon2003TriggerWaveformCam ( this ) ;
// configureNeon2003TriggerWaveformCrank(triggerShape);
2018-12-25 19:47:29 -08:00
break ;
case TT_FORD_ASPIRE :
2019-12-07 22:09:39 -08:00
configureFordAspireTriggerWaveform ( this ) ;
2018-12-25 19:47:29 -08:00
break ;
2021-07-16 21:27:56 -07:00
case TT_VVT_NISSAN_VQ35 :
2021-07-01 18:48:14 -07:00
initializeNissanVQvvt ( this ) ;
break ;
2022-03-19 13:28:06 -07:00
case TT_VVT_MITSUBISHI_3A92 :
2022-03-19 19:06:19 -07:00
initializeVvt3A92 ( this ) ;
break ;
2022-03-22 03:34:22 -07:00
case TT_VVT_TOYOTA_4_1 :
2022-03-23 16:25:13 -07:00
initializeToyota4_1 ( this ) ;
break ;
2022-03-19 19:06:19 -07:00
case TT_VVT_MITSUBISHI_6G75 :
2021-08-08 01:45:40 -07:00
case TT_NISSAN_QR25 :
2021-08-08 01:56:56 -07:00
initializeNissanQR25crank ( this ) ;
break ;
2021-07-16 21:27:56 -07:00
case TT_NISSAN_VQ30 :
2021-07-16 22:10:13 -07:00
initializeNissanVQ30cam ( this ) ;
break ;
2021-07-16 21:27:56 -07:00
case TT_NISSAN_VQ35 :
2021-07-16 21:45:47 -07:00
initializeNissanVQ35crank ( this ) ;
2021-07-01 19:10:37 -07:00
break ;
2021-11-05 04:15:03 -07:00
case TT_NISSAN_MR18_CRANK :
initializeNissanMR18crank ( this ) ;
break ;
2021-11-05 12:33:22 -07:00
case TT_NISSAN_MR18_CAM_VVT :
initializeNissanMRvvt ( this ) ;
break ;
2021-04-08 19:16:14 -07:00
case TT_KAWA_KX450F :
2021-04-08 20:23:01 -07:00
configureKawaKX450F ( this ) ;
break ;
2021-02-06 17:12:58 -08:00
case TT_SKODA_FAVORIT :
2021-02-06 17:53:30 -08:00
setSkodaFavorit ( this ) ;
break ;
2021-02-01 20:18:11 -08:00
case TT_GM_60_2_2_2 :
2021-02-02 06:50:20 -08:00
configureGm60_2_2_2 ( this ) ;
break ;
2018-12-25 19:47:29 -08:00
case TT_GM_7X :
2019-12-07 22:09:39 -08:00
configureGmTriggerWaveform ( this ) ;
2018-12-25 19:47:29 -08:00
break ;
case TT_MAZDA_DOHC_1_4 :
configureMazdaProtegeLx ( this ) ;
break ;
case TT_ONE_PLUS_ONE :
2020-04-25 19:23:53 -07:00
configureOnePlusOne ( this ) ;
2018-12-25 19:47:29 -08:00
break ;
case TT_3_1_CAM :
2020-04-25 19:23:53 -07:00
configure3_1_cam ( this ) ;
2018-12-25 19:47:29 -08:00
break ;
2021-11-13 18:50:04 -08:00
case TT_MERCEDES_2_SEGMENT :
setMercedesTwoSegment ( this ) ;
break ;
2018-12-25 19:47:29 -08:00
case TT_ONE :
2022-04-02 22:11:53 -07:00
setToothedWheelConfiguration ( this , 1 , 0 , triggerOperationMode ) ;
2018-12-25 19:47:29 -08:00
break ;
case TT_MAZDA_SOHC_4 :
configureMazdaProtegeSOHC ( this ) ;
break ;
2021-10-25 08:05:45 -07:00
case TT_DAIHATSU :
2020-12-03 20:12:30 -08:00
configureDaihatsu4 ( this ) ;
break ;
2020-08-25 13:19:23 -07:00
case TT_VVT_JZ :
2022-04-02 22:11:53 -07:00
setToothedWheelConfiguration ( this , 3 , 0 , triggerOperationMode ) ;
2020-08-25 13:19:23 -07:00
break ;
2022-03-19 12:54:43 -07:00
case TT_36_2_1_1 :
2022-03-19 14:51:47 -07:00
initialize36_2_1_1 ( this ) ;
break ;
case TT_36_2_1 :
2022-03-20 08:24:00 -07:00
initialize36_2_1 ( this ) ;
break ;
2021-11-25 17:45:58 -08:00
case TT_TOOTHED_WHEEL_32_2 :
2022-04-02 22:11:53 -07:00
setToothedWheelConfiguration ( this , 32 , 2 , triggerOperationMode ) ;
2022-07-23 07:00:55 -07:00
// todo: why is this 32/2 asking for third gap while 60/2 is happy with just two gaps?
// method above sets second gap, here we add third
// this third gap is not required to sync on perfect signal but is needed to handle to reject cranking transition noise
2021-11-25 17:45:58 -08:00
setThirdTriggerSynchronizationGap ( 1 ) ;
break ;
2018-12-25 19:47:29 -08:00
case TT_TOOTHED_WHEEL_60_2 :
2022-04-02 22:11:53 -07:00
setToothedWheelConfiguration ( this , 60 , 2 , triggerOperationMode ) ;
2018-12-25 19:47:29 -08:00
break ;
2020-05-09 20:47:51 -07:00
case TT_TOOTHED_WHEEL_36_2 :
2022-04-02 22:11:53 -07:00
setToothedWheelConfiguration ( this , 36 , 2 , triggerOperationMode ) ;
2022-05-04 21:41:26 -07:00
setTriggerSynchronizationGap3 ( /*gapIndex*/ 0 , /*from*/ 1.6 , 3.5 ) ;
setTriggerSynchronizationGap3 ( /*gapIndex*/ 1 , /*from*/ 0.7 , 1.3 ) ; // second gap is not required to synch on perfect signal but is needed to handle to reject cranking transition noise
2020-05-09 20:47:51 -07:00
break ;
2018-12-25 19:47:29 -08:00
case TT_60_2_VW :
setVwConfiguration ( this ) ;
break ;
case TT_TOOTHED_WHEEL_36_1 :
2022-04-02 22:11:53 -07:00
setToothedWheelConfiguration ( this , 36 , 1 , triggerOperationMode ) ;
2018-12-25 19:47:29 -08:00
break ;
2020-08-24 22:24:15 -07:00
case TT_VVT_BOSCH_QUICK_START :
2020-04-25 19:23:53 -07:00
configureQuickStartSenderWheel ( this ) ;
break ;
2021-03-22 04:48:29 -07:00
case TT_VVT_BARRA_3_PLUS_1 :
configureBarra3plus1cam ( this ) ;
break ;
2021-10-24 14:12:19 -07:00
case TT_HONDA_K_4_1 :
configureHondaK_4_1 ( this ) ;
break ;
2020-04-19 11:23:01 -07:00
case TT_HONDA_K_12_1 :
configureHondaK_12_1 ( this ) ;
break ;
2022-06-20 08:41:33 -07:00
case TT_SUBARU_EZ30 :
initializeSubaruEZ30 ( this ) ;
break ;
2022-08-31 19:12:45 -07:00
case TT_VVT_MAZDA_SKYACTIV :
initializeMazdaSkyactivCam ( this ) ;
2022-08-28 19:42:53 -07:00
break ;
2022-06-01 18:24:20 -07:00
case UNUSED_21 :
case UNUSED_34 :
2020-09-28 18:18:47 -07:00
case TT_1_16 :
configureOnePlus16 ( this ) ;
break ;
2018-12-25 19:47:29 -08:00
case TT_HONDA_CBR_600 :
configureHondaCbr600 ( this ) ;
break ;
2020-12-03 21:30:22 -08:00
case TT_CHRYSLER_NGC_36_2_2 :
configureChryslerNGC_36_2_2 ( this ) ;
break ;
2018-12-25 19:47:29 -08:00
case TT_MITSUBISHI :
initializeMitsubishi4g18 ( this ) ;
break ;
case TT_DODGE_RAM :
initDodgeRam ( this ) ;
break ;
case TT_JEEP_4_CYL :
initJeep_XJ_4cyl_2500 ( this ) ;
break ;
case TT_JEEP_18_2_2_2 :
initJeep18_2_2_2 ( this ) ;
break ;
case TT_SUBARU_7_6 :
initializeSubaru7_6 ( this ) ;
break ;
case TT_36_2_2_2 :
initialize36_2_2_2 ( this ) ;
break ;
case TT_2JZ_3_34 :
2020-05-08 22:15:55 -07:00
initialize2jzGE3_34_simulation_shape ( this ) ;
2018-12-25 19:47:29 -08:00
break ;
case TT_2JZ_1_12 :
initialize2jzGE1_12 ( this ) ;
break ;
2022-05-17 18:38:24 -07:00
case TT_12_TOOTH_CRANK :
configure12ToothCrank ( this ) ;
break ;
2018-12-25 19:47:29 -08:00
case TT_NISSAN_SR20VE :
initializeNissanSR20VE_4 ( this ) ;
break ;
case TT_ROVER_K :
initializeRoverK ( this ) ;
break ;
case TT_FIAT_IAW_P8 :
configureFiatIAQ_P8 ( this ) ;
break ;
2021-01-03 10:52:53 -08:00
case TT_TRI_TACH :
2021-01-03 21:26:05 -08:00
configureTriTach ( this ) ;
break ;
2022-05-06 05:25:44 -07:00
case TT_GM_24x :
initGmLS24_5deg ( this ) ;
break ;
case TT_GM_24x_2 :
initGmLS24_3deg ( this ) ;
2018-12-25 19:47:29 -08:00
break ;
2020-12-13 14:46:48 -08:00
case TT_SUBARU_7_WITHOUT_6 :
2020-12-13 13:22:02 -08:00
initializeSubaruOnly7 ( this ) ;
break ;
2020-05-14 17:40:47 -07:00
case TT_SUBARU_SVX :
initializeSubaru_SVX ( this ) ;
break ;
2021-08-31 02:54:04 -07:00
case TT_SUBARU_SVX_CRANK_1 :
2021-08-31 03:05:57 -07:00
initializeSubaru_SVX ( this ) ;
break ;
2021-08-31 02:54:04 -07:00
case TT_SUBARU_SVX_CAM_VVT :
2021-08-31 03:05:57 -07:00
initializeSubaru_SVX ( this ) ;
break ;
2021-08-31 02:54:04 -07:00
2018-12-25 19:47:29 -08:00
default :
2019-08-07 19:02:08 -07:00
setShapeDefinitionError ( true ) ;
2022-05-30 16:36:47 -07:00
warning ( CUSTOM_ERR_NO_SHAPE , " initializeTriggerWaveform() not implemented: %d " , triggerConfig . TriggerType . type ) ;
2018-12-25 19:47:29 -08:00
}
2019-02-02 21:40:25 -08:00
/**
* Feb 2019 suggestion : it would be an improvement to remove ' expectedEventCount ' logic from ' addEvent '
* and move it here , after all events were added .
*/
2018-12-25 19:47:29 -08:00
calculateExpectedEventCounts ( useOnlyRisingEdgeForTrigger ) ;
version + + ;
if ( ! shapeDefinitionError ) {
2021-11-21 01:56:07 -08:00
wave . checkSwitchTimes ( getCycleDuration ( ) ) ;
2018-12-25 19:47:29 -08:00
}
2020-08-26 20:35:11 -07:00
if ( bothFrontsRequired & & useOnlyRisingEdgeForTrigger ) {
# if EFI_PROD_CODE || EFI_SIMULATOR
firmwareError ( CUSTOM_ERR_BOTH_FRONTS_REQUIRED , " trigger: both fronts required " ) ;
# else
warning ( CUSTOM_ERR_BOTH_FRONTS_REQUIRED , " trigger: both fronts required " ) ;
# endif
}
2022-09-11 02:41:01 -07:00
}