├── tune.bin ├── .gitattributes ├── .vscode ├── extensions.json └── settings.json ├── envinit.sh ├── src ├── Operations │ ├── Operation_EngineParameters.cpp │ ├── Operation_InjectorDeadTime.cpp │ ├── Operation_CylinderAirMass_SD.cpp │ ├── Operation_EngineInjectorPrime.cpp │ ├── EngineOperationFactoryRegister.cpp │ ├── Operation_EnginePosition.cpp │ ├── Operation_EngineScheduleInjection.cpp │ └── Operation_EngineScheduleIgnition.cpp ├── ExpanderMain.cpp ├── EngineMain.cpp └── CommunicationHandlers │ ├── CommunicationHandler_OBD2.cpp │ └── CommunicationHandler_EFIGenie.cpp ├── .gitignore ├── include ├── Operations │ ├── EngineOperationFactoryRegister.h │ ├── Operation_EngineParameters.h │ ├── Operation_CylinderAirMass_SD.h │ ├── Operation_InjectorDeadTime.h │ ├── Operation_EnginePosition.h │ ├── Operation_EngineInjectorPrime.h │ ├── Operation_EngineScheduleIgnition.h │ └── Operation_EngineScheduleInjection.h ├── ExpanderMain.h ├── CommunicationHandlers │ ├── CommunicationHandler_OBD2.h │ └── CommunicationHandler_EFIGenie.h └── EngineMain.h ├── CMakePresets.json ├── test ├── CMakeLists.txt └── src │ ├── Operation_EngineScheduleIgnitionTests.cpp │ ├── IgnitionConfig_StaticTests.cpp │ ├── AcceptanceTest.cpp │ ├── InjectorGramServiceWrapper_DFCOTests.cpp │ ├── InjectorGramServiceTests.cpp │ ├── PrimeService_StaticPulseWidthTests.cpp │ ├── CylinderAirmassService_SDTests.cpp │ ├── CylinderAirTemperatureService_IAT_ECT_BiasTests.cpp │ ├── InjectorTimingServiceTests.cpp │ ├── IgnitionSchedulingServiceTests.cpp │ ├── FuelPumpServiceTests.cpp │ └── AfrService_Map_EthanolTests.cpp ├── README.md ├── CMakeLists.txt └── tune.json /tune.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FL0WL0W/EFIGenie/HEAD/tune.bin -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | -------------------------------------------------------------------------------- /.vscode/extensions.json: -------------------------------------------------------------------------------- 1 | { 2 | "recommendations": [ 3 | "ms-vscode.cpptools", 4 | "twxs.cmake", 5 | "ms-vscode.cmake-tools", 6 | "matepek.vscode-catch2-test-adapter" 7 | ] 8 | } 9 | -------------------------------------------------------------------------------- /envinit.sh: -------------------------------------------------------------------------------- 1 | git submodule init 2 | git submodule update --progress 3 | sudo apt install cmake 4 | sudo apt install g++ 5 | sudo wget -qO /usr/local/bin/ninja.gz https://github.com/ninja-build/ninja/releases/latest/download/ninja-linux.zip 6 | sudo gunzip /usr/local/bin/ninja.gz 7 | sudo chmod a+x /usr/local/bin/ninja -------------------------------------------------------------------------------- /src/Operations/Operation_EngineParameters.cpp: -------------------------------------------------------------------------------- 1 | #include "Operations/Operation_EngineParameters.h" 2 | #include "Config.h" 3 | 4 | #ifdef OPERATION_ENGINEPARAMETERS_H 5 | namespace EFIGenie 6 | { 7 | std::tuple Operation_EngineParameters::Execute(EnginePosition enginePosition) 8 | { 9 | _sequential = enginePosition.Sequential; 10 | _synced = enginePosition.Synced; 11 | return std::tuple(enginePosition.GetRPM(), _sequential, _synced); 12 | } 13 | } 14 | #endif -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Prerequisites 2 | *.d 3 | 4 | # Compiled Object files 5 | *.slo 6 | *.lo 7 | *.o 8 | *.obj 9 | 10 | # Precompiled Headers 11 | *.gch 12 | *.pch 13 | 14 | # Compiled Dynamic libraries 15 | *.so 16 | *.dylib 17 | *.dll 18 | 19 | # Fortran module files 20 | *.mod 21 | *.smod 22 | 23 | # Compiled Static libraries 24 | *.lai 25 | *.la 26 | *.a 27 | *.lib 28 | 29 | # Executables 30 | *.exe 31 | *.out 32 | *.app 33 | 34 | build/* 35 | libs/* 36 | .vscode/* 37 | !.vscode/extensions.json 38 | !.vscode/settings.json 39 | .vs/* 40 | -------------------------------------------------------------------------------- /include/Operations/EngineOperationFactoryRegister.h: -------------------------------------------------------------------------------- 1 | #include 2 | #include "Operations/OperationFactory.h" 3 | #include "EmbeddedIOServiceCollection.h" 4 | 5 | #ifndef ENGINEOPERATIONFACTORYREGISTER_H 6 | #define ENGINEOPERATIONFACTORYREGISTER_H 7 | namespace EFIGenie 8 | { 9 | class EngineOperationFactoryRegister 10 | { 11 | public: 12 | static void Register(uint32_t idOffset, OperationArchitecture::OperationFactory *factory, const EmbeddedIOOperations::EmbeddedIOServiceCollection *embeddedIOServiceCollection); 13 | }; 14 | } 15 | #endif -------------------------------------------------------------------------------- /include/Operations/Operation_EngineParameters.h: -------------------------------------------------------------------------------- 1 | #include "Operations/AbstractOperation.h" 2 | #include "Operations/Operation_EnginePosition.h" 3 | #include 4 | 5 | #ifndef OPERATION_ENGINEPARAMETERS_H 6 | #define OPERATION_ENGINEPARAMETERS_H 7 | namespace EFIGenie 8 | { 9 | class Operation_EngineParameters : public OperationArchitecture::Operation, EnginePosition> 10 | { 11 | protected: 12 | volatile bool _sequential; 13 | volatile bool _synced; 14 | public: 15 | std::tuple Execute(EnginePosition enginePosition) override; 16 | }; 17 | } 18 | #endif -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "testMate.cpp.test.advancedExecutables": [ 3 | "${command:cmake.buildDirectory}/test/EFIGenieUnitTests", 4 | "${command:cmake.buildDirectory}/_deps/reluctoroperations-build/test/ReluctorOperationsUnitTests", 5 | "${command:cmake.buildDirectory}/_deps/embeddediooperations-build/test/EmbeddedIOOperationsUnitTests", 6 | "${command:cmake.buildDirectory}/_deps/embeddedioservices-build/test/EmbeddedIOServicesUnitTests", 7 | "${command:cmake.buildDirectory}/_deps/operationarchitecture-build/test/OperationArchitectureUnitTests" 8 | ], 9 | "cmake.ctest.testExplorerIntegrationEnabled": false 10 | } -------------------------------------------------------------------------------- /include/Operations/Operation_CylinderAirMass_SD.h: -------------------------------------------------------------------------------- 1 | #include "Operations/Operation.h" 2 | 3 | #ifndef OPERATION_CYLINDERAIRMASS_SD_H 4 | #define OPERATION_CYLINDERAIRMASS_SD_H 5 | namespace EFIGenie 6 | { 7 | class Operation_CylinderAirMass_SD : public OperationArchitecture::Operation 8 | { 9 | protected: 10 | float _cylinderVolume; 11 | public: 12 | Operation_CylinderAirMass_SD(const float cylinderVolume); 13 | 14 | float Execute(float cylinderAirTemperature, float manifoldAbsolutePressure, float VolumetricEfficiency) override; 15 | 16 | static OperationArchitecture::AbstractOperation *Create(const void *config, size_t &sizeOut); 17 | }; 18 | } 19 | #endif -------------------------------------------------------------------------------- /include/Operations/Operation_InjectorDeadTime.h: -------------------------------------------------------------------------------- 1 | #include "Operations/Operation.h" 2 | 3 | #ifndef OPERATION_INJECTORDEADTIME_H 4 | #define OPERATION_INJECTORDEADTIME_H 5 | namespace EFIGenie 6 | { 7 | class Operation_InjectorDeadTime : public OperationArchitecture::Operation 8 | { 9 | protected: 10 | float _minInjectorFuelMass; 11 | public: 12 | Operation_InjectorDeadTime(const float minInjectorFuelMass); 13 | 14 | float Execute(uint8_t squirtsPerCycle, float fuelMass, float injectorFlowRate, float injectorDeadTime) override; 15 | 16 | static OperationArchitecture::AbstractOperation *Create(const void *config, size_t &sizeOut); 17 | }; 18 | } 19 | #endif -------------------------------------------------------------------------------- /include/ExpanderMain.h: -------------------------------------------------------------------------------- 1 | #include "EmbeddedIOServiceCollection.h" 2 | #include "Operations/Operation_Package.h" 3 | 4 | #ifndef EXPANDERMAIN_H 5 | #define EXPANDERMAIN_H 6 | namespace EFIGenie 7 | { 8 | class ExpanderMain 9 | { 10 | protected: 11 | OperationArchitecture::AbstractOperation *_mainLoopExecute = 0; 12 | OperationArchitecture::OperationFactory *_operationFactory = 0; 13 | OperationArchitecture::GeneratorMap *_variableMap; 14 | 15 | public: 16 | ExpanderMain(const void *config, size_t &sizeOut, const EmbeddedIOOperations::EmbeddedIOServiceCollection *embeddedIOServiceCollection, OperationArchitecture::GeneratorMap *variableMap); 17 | ~ExpanderMain(); 18 | 19 | void Setup(); 20 | void Loop(); 21 | }; 22 | } 23 | #endif -------------------------------------------------------------------------------- /include/Operations/Operation_EnginePosition.h: -------------------------------------------------------------------------------- 1 | #include "Operations/Operation.h" 2 | #include "ReluctorResult.h" 3 | 4 | #ifndef OPERATION_ENGINEPOSITION_H 5 | #define OPERATION_ENGINEPOSITION_H 6 | namespace EFIGenie 7 | { 8 | struct EnginePosition : public ReluctorOperations::ReluctorResult 9 | { 10 | bool Sequential : 1; 11 | 12 | EnginePosition() : Sequential(false) {} 13 | }; 14 | 15 | class Operation_EnginePosition : public OperationArchitecture::Operation 16 | { 17 | protected: 18 | EnginePosition _previousPreviousReluctorResult; 19 | EnginePosition _previousReluctorResult; 20 | bool _crankPriority; 21 | public: 22 | Operation_EnginePosition(bool crankPriority); 23 | 24 | EnginePosition Execute(ReluctorOperations::ReluctorResult crankPosition, ReluctorOperations::ReluctorResult camPosition) override; 25 | }; 26 | } 27 | #endif -------------------------------------------------------------------------------- /src/Operations/Operation_InjectorDeadTime.cpp: -------------------------------------------------------------------------------- 1 | #include "Operations/Operation_InjectorDeadTime.h" 2 | #include "Config.h" 3 | 4 | using namespace OperationArchitecture; 5 | 6 | #ifdef OPERATION_INJECTORDEADTIME_H 7 | namespace EFIGenie 8 | { 9 | Operation_InjectorDeadTime::Operation_InjectorDeadTime(const float minInjectorFuelMass) : _minInjectorFuelMass(minInjectorFuelMass) { } 10 | 11 | float Operation_InjectorDeadTime::Execute(uint8_t squirtsPerCycle, float fuelMass, float injectorFlowRate, float injectorDeadTime) 12 | { 13 | float injectorFuelMass = fuelMass / squirtsPerCycle; 14 | 15 | if(injectorFuelMass < _minInjectorFuelMass) 16 | injectorFuelMass = _minInjectorFuelMass; 17 | 18 | return injectorFuelMass / injectorFlowRate + injectorDeadTime; 19 | } 20 | 21 | AbstractOperation *Operation_InjectorDeadTime::Create(const void *config, size_t &sizeOut) 22 | { 23 | return new Operation_InjectorDeadTime(Config::CastAndOffset(config, sizeOut)); 24 | } 25 | } 26 | #endif -------------------------------------------------------------------------------- /include/CommunicationHandlers/CommunicationHandler_OBD2.h: -------------------------------------------------------------------------------- 1 | #include "Variable.h" 2 | #include "ICommunicationService.h" 3 | #include "Operations/Operation_Package.h" 4 | 5 | #ifndef COMMUNICATIONHANDLER_OBD2_H 6 | #define COMMUNICATIONHANDLER_OBD2_H 7 | namespace EFIGenie 8 | { 9 | struct OBD2VariableMap { 10 | uint32_t CalculatedEngineLoadID; 11 | uint32_t EngineCoolantTempID; 12 | uint32_t FuelTrimID[4]; 13 | uint32_t FuelPressureID; 14 | uint32_t IntakeManifoldPressureID; 15 | }; 16 | 17 | class CommunicationHandler_OBD2 18 | { 19 | protected: 20 | OperationArchitecture::GeneratorMap *_variableMap; 21 | const OBD2VariableMap *_obd2VariableMap; 22 | public: 23 | CommunicationHandler_OBD2(OperationArchitecture::GeneratorMap *variableMap, const OBD2VariableMap *obd2VariableMap); 24 | size_t Receive(EmbeddedIOServices::communication_send_callback_t sendCallBack, const void* buf, size_t length); 25 | }; 26 | } 27 | #endif -------------------------------------------------------------------------------- /include/EngineMain.h: -------------------------------------------------------------------------------- 1 | #include "EmbeddedIOServiceCollection.h" 2 | #include "Operations/Operation_Package.h" 3 | 4 | #ifndef ENGINEMAIN_H 5 | #define ENGINEMAIN_H 6 | namespace EFIGenie 7 | { 8 | class EngineMain 9 | { 10 | protected: 11 | OperationArchitecture::AbstractOperation *_inputsExecute = 0; 12 | OperationArchitecture::AbstractOperation *_preSyncExecute = 0; 13 | OperationArchitecture::AbstractOperation *_syncCondition = 0; 14 | OperationArchitecture::AbstractOperation *_mainLoopExecute = 0; 15 | bool _syncedOnce = false; 16 | OperationArchitecture::OperationFactory *_operationFactory = 0; 17 | OperationArchitecture::GeneratorMap *_variableMap; 18 | 19 | public: 20 | EngineMain(const void *config, size_t &sizeOut, const EmbeddedIOOperations::EmbeddedIOServiceCollection *embeddedIOServiceCollection, OperationArchitecture::GeneratorMap *variableMap); 21 | ~EngineMain(); 22 | 23 | void Setup(); 24 | void Loop(); 25 | }; 26 | } 27 | #endif -------------------------------------------------------------------------------- /src/Operations/Operation_CylinderAirMass_SD.cpp: -------------------------------------------------------------------------------- 1 | #include "Operations/Operation_CylinderAirMass_SD.h" 2 | #include "Config.h" 3 | 4 | using namespace OperationArchitecture; 5 | 6 | #ifdef OPERATION_CYLINDERAIRMASS_SD_H 7 | namespace EFIGenie 8 | { 9 | Operation_CylinderAirMass_SD::Operation_CylinderAirMass_SD(const float cylinderVolume) : _cylinderVolume(cylinderVolume) { } 10 | 11 | float Operation_CylinderAirMass_SD::Execute(float cylinderAirTemperature, float manifoldAbsolutePresssure, float volumetricEfficieny) 12 | { 13 | //PV=nRT => n = PV/RT 14 | //R = 0.083144621 L bar/K mol 15 | 16 | const float n = (manifoldAbsolutePresssure * _cylinderVolume * volumetricEfficieny) / (0.083144621f * (cylinderAirTemperature + 273.15f)); 17 | 18 | //the molecular weight of dry air is 28.9647 grams per mole. 19 | 20 | return n * 28.9647f; 21 | } 22 | 23 | AbstractOperation *Operation_CylinderAirMass_SD::Create(const void *config, size_t &sizeOut) 24 | { 25 | return new Operation_CylinderAirMass_SD(Config::CastAndOffset(config, sizeOut)); 26 | } 27 | } 28 | #endif -------------------------------------------------------------------------------- /include/Operations/Operation_EngineInjectorPrime.h: -------------------------------------------------------------------------------- 1 | #include "Operations/Operation.h" 2 | #include "EmbeddedIOServiceCollection.h" 3 | #include "Operations/OperationFactory.h" 4 | 5 | #ifndef OPERATION_ENGINEINJECTORPRIME_H 6 | #define OPERATION_ENGINEINJECTORPRIME_H 7 | namespace EFIGenie 8 | { 9 | class Operation_EngineInjectorPrime : public OperationArchitecture::Operation 10 | { 11 | protected: 12 | EmbeddedIOServices::ITimerService *_timerService; 13 | const EmbeddedIOServices::callback_t _openCallBack; 14 | EmbeddedIOServices::Task *_closeTask; 15 | public: 16 | Operation_EngineInjectorPrime(EmbeddedIOServices::ITimerService *timerService, const EmbeddedIOServices::callback_t openCallBack, const EmbeddedIOServices::callback_t closeCallBack); 17 | ~Operation_EngineInjectorPrime(); 18 | 19 | void Execute(float time) override; 20 | 21 | static OperationArchitecture::AbstractOperation *Create(const void *config, size_t &sizeOut, const EmbeddedIOOperations::EmbeddedIOServiceCollection *embeddedIOServiceCollection, OperationArchitecture::OperationFactory *factory); 22 | }; 23 | } 24 | #endif -------------------------------------------------------------------------------- /CMakePresets.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": 2, 3 | "configurePresets": [ 4 | { 5 | "name": "GCC 11.3.0", 6 | "displayName": "GCC 11.3.0 x86_64-linux-gnu", 7 | "description": "Using compilers: C = /usr/bin/gcc, CXX = /usr/bin/g++", 8 | "binaryDir": "${sourceDir}/build/${presetName}", 9 | "generator": "Ninja", 10 | "cacheVariables": { 11 | "CMAKE_INSTALL_PREFIX": "${sourceDir}/out/install/${presetName}", 12 | "CMAKE_C_COMPILER": "/usr/bin/gcc", 13 | "CMAKE_CXX_COMPILER": "/usr/bin/g++", 14 | "CMAKE_BUILD_TYPE": "Debug", 15 | "CMAKE_C_FLAGS": "-g -Wall", 16 | "CMAKE_CXX_FLAGS": "-g -Wall", 17 | "CMAKE_ASM_FLAGS": "-g -Wall", 18 | "EFIGenie_build_tests": true, 19 | "ReluctorOperations_build_tests": true, 20 | "EmbeddedIOOperations_build_tests": true, 21 | "EmbeddedIOServices_build_tests": true, 22 | "OperationArchitecture_build_tests": true 23 | } 24 | } 25 | ] 26 | } -------------------------------------------------------------------------------- /include/CommunicationHandlers/CommunicationHandler_EFIGenie.h: -------------------------------------------------------------------------------- 1 | #include "Variable.h" 2 | #include "GeneratorMap.h" 3 | #include "ICommunicationService.h" 4 | #include "CommunicationHandlers/CommunicationHandler_GetVariable.h" 5 | #include "CommunicationHandler_Prefix.h" 6 | 7 | #ifndef COMMUNICATIONHANDLER_EFIGENIE_H 8 | #define COMMUNICATIONHANDLER_EFIGENIE_H 9 | #define METADATA_VARIABLEID 4294967295 10 | namespace EFIGenie 11 | { 12 | typedef std::function communicationhandler_efigenie_write_t; 13 | typedef std::function communicationhandler_quit_t; 14 | typedef std::function communicationhandler_start_t; 15 | 16 | class CommunicationHandler_EFIGenie: public EmbeddedIOServices::CommunicationHandler_Prefix 17 | { 18 | protected: 19 | EmbeddedIOOperations::CommunicationHandler_GetVariable *_getVariableHandler; 20 | public: 21 | CommunicationHandler_EFIGenie(OperationArchitecture::GeneratorMap *variableMap, 22 | communicationhandler_efigenie_write_t writeCallback, 23 | communicationhandler_quit_t quitCallback, 24 | communicationhandler_start_t startCallback, 25 | const void *config); 26 | ~CommunicationHandler_EFIGenie(); 27 | }; 28 | } 29 | #endif -------------------------------------------------------------------------------- /test/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # CMakeList.txt : CMake project for EmbeddedIOServicesUnitTests, include source and define 2 | # project specific logic here. 3 | # 4 | cmake_minimum_required (VERSION 3.30[3.30]) 5 | 6 | set(TARGET EFIGenieUnitTests) 7 | set(HDR_DIR include) 8 | set(SRCS_DIR src) 9 | 10 | project(${TARGET} CXX) 11 | 12 | # headers are always in include/ 13 | file(GLOB_RECURSE HDRS CONFIGURE_DEPENDS ${HDR_DIR}/*.h) 14 | include_directories(${HDR_DIR}) 15 | # sources are always in src/ 16 | file(GLOB_RECURSE SRCS CONFIGURE_DEPENDS ${SRCS_DIR}/*.cpp) 17 | 18 | # create executable 19 | add_executable(${TARGET} ${SRCS}) 20 | 21 | # link static libraries 22 | target_link_libraries(${TARGET} OperationArchitecture) 23 | target_link_libraries(${TARGET} EmbeddedIOServices) 24 | target_link_libraries(${TARGET} EmbeddedIOServicesMockedObjects) 25 | target_link_libraries(${TARGET} EmbeddedIOOperations) 26 | target_link_libraries(${TARGET} ReluctorOperations) 27 | target_link_libraries(${TARGET} EFIGenie) 28 | Set(FETCHCONTENT_QUIET FALSE) 29 | include(FetchContent) 30 | FetchContent_Declare( 31 | gtest 32 | GIT_REPOSITORY https://github.com/google/googletest 33 | GIT_TAG main 34 | GIT_SHALLOW TRUE 35 | GIT_PROGRESS TRUE 36 | ) 37 | FetchContent_MakeAvailable(gtest) 38 | target_link_libraries(${TARGET} gtest gtest_main gmock) 39 | 40 | add_test(ALLTESTS ${TARGET}) -------------------------------------------------------------------------------- /test/src/Operation_EngineScheduleIgnitionTests.cpp: -------------------------------------------------------------------------------- 1 | #include "gmock/gmock.h" 2 | #include "gtest/gtest.h" 3 | #include "Operations/Operation_EngineScheduleIgnition.h" 4 | #include "MockTimerService.h" 5 | using namespace testing; 6 | 7 | using namespace EmbeddedIOServices; 8 | using namespace OperationArchitecture; 9 | using namespace EFIGenie; 10 | 11 | namespace UnitTests 12 | { 13 | class Operation_EngineScheduleIgnitionTests : public ::testing::Test 14 | { 15 | protected: 16 | MockTimerService _timerService; 17 | callback_t _dwell; 18 | callback_t _ignite; 19 | Operation_EngineScheduleIgnition *_operation; 20 | EnginePosition _enginePosition; 21 | 22 | Operation_EngineScheduleIgnitionTests() 23 | { 24 | EXPECT_CALL(_timerService, GetTicksPerSecond()).WillRepeatedly(Return(5000)); 25 | 26 | _operation = new Operation_EngineScheduleIgnition(&_timerService, 0, _dwell, _ignite); 27 | } 28 | }; 29 | 30 | TEST_F(Operation_EngineScheduleIgnitionTests, WhenSchedulingIgnition_ThenIgnitionScheduledCorrectly) 31 | { 32 | _enginePosition.CalculatedTick = 1000; 33 | _enginePosition.Position = 101; 34 | _enginePosition.PositionDot = 1; 35 | _enginePosition.Sequential = true; 36 | _enginePosition.Synced = true; 37 | _operation->Execute(_enginePosition, true, 0.003f, 10, 0.0005f); 38 | } 39 | } -------------------------------------------------------------------------------- /test/src/IgnitionConfig_StaticTests.cpp: -------------------------------------------------------------------------------- 1 | // #include "gmock/gmock.h" 2 | // #include "gtest/gtest.h" 3 | // #include "Service/EngineControlServiceBuilder.h" 4 | // using ::testing::AtLeast; 5 | // using ::testing::Return; 6 | 7 | // using namespace HardwareAbstraction; 8 | // using namespace EngineControlServices; 9 | // using namespace IOServices; 10 | // using namespace Service; 11 | 12 | // namespace UnitTests 13 | // { 14 | // TEST(IgnitionConfig_StaticTests, WhenGettingIgnitionConfig) 15 | // { 16 | // IgnitionConfig_StaticConfig *ignitionConfigConfig = (IgnitionConfig_StaticConfig *)malloc(sizeof(IgnitionConfig_StaticConfig)); 17 | // ServiceLocator *serviceLocator = new ServiceLocator(); 18 | 19 | // ignitionConfigConfig->IgnitionAdvance64thDegree = 10 * 64; 20 | // ignitionConfigConfig->IgnitionDwellTime = 4*0.001f; 21 | 22 | // void *config = malloc(ignitionConfigConfig->Size() + 1); 23 | // *(unsigned char *)config = 1; 24 | // memcpy(((unsigned char *)config + 1), ignitionConfigConfig, ignitionConfigConfig->Size()); 25 | 26 | // unsigned int size = 0; 27 | // IIgnitionConfig *ignitionConfig = EngineControlServiceBuilder::CreateIgnitionConfig(serviceLocator, config, &size); 28 | 29 | // IgnitionTiming timing = ignitionConfig->GetIgnitionTiming(); 30 | 31 | // ASSERT_EQ((short)(10 * 64), timing.IgnitionAdvance64thDegree); 32 | // ASSERT_NEAR(4*0.001f, timing.IgnitionDwellTime, 0.0001f); 33 | // ASSERT_TRUE(timing.IgnitionEnable); 34 | // } 35 | // } -------------------------------------------------------------------------------- /include/Operations/Operation_EngineScheduleIgnition.h: -------------------------------------------------------------------------------- 1 | #include "Operations/Operation.h" 2 | #include "Operations/OperationFactory.h" 3 | #include "Operation_EnginePosition.h" 4 | #include "EmbeddedIOServiceCollection.h" 5 | #include 6 | 7 | #ifndef OPERATION_ENGINESCHEDULEIGNITION_H 8 | #define OPERATION_ENGINESCHEDULEIGNITION_H 9 | namespace EFIGenie 10 | { 11 | class Operation_EngineScheduleIgnition : public OperationArchitecture::Operation, EnginePosition, bool, float, float, float> 12 | { 13 | protected: 14 | EmbeddedIOServices::ITimerService * const _timerService; 15 | const float _tdc; 16 | const EmbeddedIOServices::callback_t _dwellCallBack; 17 | const EmbeddedIOServices::callback_t _igniteCallBack; 18 | 19 | EmbeddedIOServices::Task *_dwellTask; 20 | EmbeddedIOServices::Task *_igniteTask; 21 | volatile EmbeddedIOServices::tick_t _lastDwellTick = 0; 22 | bool _dwelling = false; 23 | bool _previousSequential = false; 24 | public: 25 | Operation_EngineScheduleIgnition(EmbeddedIOServices::ITimerService * const timerService, const float tdc, const EmbeddedIOServices::callback_t dwellCallBack, const EmbeddedIOServices::callback_t igniteCallBack); 26 | ~Operation_EngineScheduleIgnition(); 27 | 28 | std::tuple Execute(EnginePosition enginePosition, bool enable, float ignitionDwell, float ignitionAdvance, float ignitionDwellMaxDeviation) override; 29 | void Dwell(); 30 | void Ignite(); 31 | 32 | static OperationArchitecture::AbstractOperation *Create(const void *config, size_t &sizeOut, const EmbeddedIOOperations::EmbeddedIOServiceCollection *embeddedIOServiceCollection, OperationArchitecture::OperationFactory *packager); 33 | }; 34 | } 35 | #endif -------------------------------------------------------------------------------- /include/Operations/Operation_EngineScheduleInjection.h: -------------------------------------------------------------------------------- 1 | #include "Operations/Operation.h" 2 | #include "Operation_EnginePosition.h" 3 | #include "EmbeddedIOServiceCollection.h" 4 | #include "Operations/OperationFactory.h" 5 | #include 6 | 7 | #ifndef OPERATION_ENGINESCHEDULEINJECTION_H 8 | #define OPERATION_ENGINESCHEDULEINJECTION_H 9 | namespace EFIGenie 10 | { 11 | //// Used to set the direction of a pin during initialization 12 | enum Operation_EngineScheduleInjection_InjectAt : uint8_t 13 | { 14 | Begin = 0, 15 | Middle = 1, 16 | End = 2 17 | }; 18 | 19 | class Operation_EngineScheduleInjection : public OperationArchitecture::Operation, EnginePosition, bool, float, float> 20 | { 21 | protected: 22 | EmbeddedIOServices::ITimerService * const _timerService; 23 | const float _tdc; 24 | const Operation_EngineScheduleInjection_InjectAt _injectAt; 25 | const EmbeddedIOServices::callback_t _openCallBack; 26 | const EmbeddedIOServices::callback_t _closeCallBack; 27 | 28 | EmbeddedIOServices::Task *_openTask; 29 | EmbeddedIOServices::Task *_closeTask; 30 | volatile EmbeddedIOServices::tick_t _lastOpenTick = 0; 31 | bool _open = false; 32 | public: 33 | Operation_EngineScheduleInjection(EmbeddedIOServices::ITimerService * const timerService, const float _tdc, const Operation_EngineScheduleInjection_InjectAt injectAt, const EmbeddedIOServices::callback_t openCallBack, const EmbeddedIOServices::callback_t closeCallBack); 34 | ~Operation_EngineScheduleInjection(); 35 | 36 | std::tuple Execute(EnginePosition enginePosition, bool enable, float injectionPulseWidth, float injectionPosition) override; 37 | void Open(); 38 | void Close(); 39 | 40 | static OperationArchitecture::AbstractOperation *Create(const void *config, size_t &sizeOut, const EmbeddedIOOperations::EmbeddedIOServiceCollection *embeddedIOServiceCollection, OperationArchitecture::OperationFactory *factory); 41 | }; 42 | } 43 | #endif -------------------------------------------------------------------------------- /src/Operations/Operation_EngineInjectorPrime.cpp: -------------------------------------------------------------------------------- 1 | #include "Operations/Operation_EngineInjectorPrime.h" 2 | #include "Config.h" 3 | 4 | using namespace EmbeddedIOServices; 5 | using namespace OperationArchitecture; 6 | using namespace EmbeddedIOOperations; 7 | 8 | #ifdef OPERATION_ENGINEINJECTORPRIME_H 9 | namespace EFIGenie 10 | { 11 | Operation_EngineInjectorPrime::Operation_EngineInjectorPrime(ITimerService *timerService, callback_t openCallBack, callback_t closeCallBack) : 12 | _timerService(timerService), 13 | _openCallBack(openCallBack), 14 | _closeTask(new Task(closeCallBack)) 15 | { } 16 | 17 | Operation_EngineInjectorPrime::~Operation_EngineInjectorPrime() 18 | { 19 | _timerService->UnScheduleTask(_closeTask); 20 | delete _closeTask; 21 | } 22 | 23 | void Operation_EngineInjectorPrime::Execute(float time) 24 | { 25 | _openCallBack(); 26 | _timerService->ScheduleTask(_closeTask, _timerService->GetTick() + static_cast(time * _timerService->GetTicksPerSecond())); 27 | } 28 | 29 | AbstractOperation *Operation_EngineInjectorPrime::Create(const void *config, size_t &sizeOut, const EmbeddedIOServiceCollection *embeddedIOServiceCollection, OperationFactory *factory) 30 | { 31 | callback_t openCallBack = 0; 32 | callback_t closeCallBack = 0; 33 | 34 | size_t size = 0; 35 | AbstractOperation *operation = factory->Create(config, size); 36 | Config::OffsetConfig(config, sizeOut, size); 37 | if(operation->NumberOfParameters == 1) 38 | { 39 | openCallBack = [operation]() { operation->Execute(true); }; 40 | closeCallBack = [operation]() { operation->Execute(false); }; 41 | } 42 | else 43 | { 44 | openCallBack = [operation]() { operation->Execute(); }; 45 | 46 | size = 0; 47 | AbstractOperation *operationClose = factory->Create(config, size); 48 | Config::OffsetConfig(config, sizeOut, size); 49 | closeCallBack = [operationClose]() { operationClose->Execute(); }; 50 | } 51 | 52 | return new Operation_EngineInjectorPrime(embeddedIOServiceCollection->TimerService, openCallBack, closeCallBack); 53 | } 54 | } 55 | #endif -------------------------------------------------------------------------------- /src/Operations/EngineOperationFactoryRegister.cpp: -------------------------------------------------------------------------------- 1 | #include "Operations/EngineOperationFactoryRegister.h" 2 | #include "Operations/Operation_CylinderAirMass_SD.h" 3 | #include "Operations/Operation_EngineInjectorPrime.h" 4 | #include "Operations/Operation_EnginePosition.h" 5 | #include "Operations/Operation_EngineParameters.h" 6 | #include "Operations/Operation_EngineScheduleIgnition.h" 7 | #include "Operations/Operation_EngineScheduleInjection.h" 8 | #include "Operations/Operation_InjectorDeadTime.h" 9 | 10 | using namespace OperationArchitecture; 11 | using namespace EmbeddedIOOperations; 12 | 13 | #ifdef ENGINEOPERATIONFACTORYREGISTER_H 14 | namespace EFIGenie 15 | { 16 | Operation_EnginePosition *Operation_EnginePositionInstanceCrankPriority = 0; 17 | Operation_EnginePosition *Operation_EnginePositionInstanceCamPriority = 0; 18 | Operation_EngineParameters * Operation_EngineParametersInstance = 0; 19 | 20 | void EngineOperationFactoryRegister::Register(uint32_t idOffset, OperationFactory *factory, const EmbeddedIOServiceCollection *embeddedIOServiceCollection) 21 | { 22 | factory->Register(idOffset + 1, Operation_CylinderAirMass_SD::Create); 23 | factory->Register(idOffset + 2, [embeddedIOServiceCollection, factory](const void *config, size_t &sizeOut) { return Operation_EngineInjectorPrime::Create(config, sizeOut, embeddedIOServiceCollection, factory); }); 24 | factory->Register(idOffset + 3, Operation_EnginePositionInstanceCrankPriority == 0 ? Operation_EnginePositionInstanceCrankPriority = new Operation_EnginePosition(true) : Operation_EnginePositionInstanceCrankPriority); 25 | factory->Register(idOffset + 4, Operation_EnginePositionInstanceCamPriority == 0 ? Operation_EnginePositionInstanceCamPriority = new Operation_EnginePosition(false) : Operation_EnginePositionInstanceCamPriority); 26 | factory->Register(idOffset + 5, Operation_EngineParametersInstance == 0 ? Operation_EngineParametersInstance = new Operation_EngineParameters() : Operation_EngineParametersInstance); 27 | factory->Register(idOffset + 6, [embeddedIOServiceCollection, factory](const void *config, size_t &sizeOut) { return Operation_EngineScheduleIgnition::Create(config, sizeOut, embeddedIOServiceCollection, factory); }); 28 | factory->Register(idOffset + 7, [embeddedIOServiceCollection, factory](const void *config, size_t &sizeOut) { return Operation_EngineScheduleInjection::Create(config, sizeOut, embeddedIOServiceCollection, factory); }); 29 | factory->Register(idOffset + 8, Operation_InjectorDeadTime::Create); 30 | } 31 | } 32 | #endif -------------------------------------------------------------------------------- /test/src/AcceptanceTest.cpp: -------------------------------------------------------------------------------- 1 | #include "gmock/gmock.h" 2 | #include "gtest/gtest.h" 3 | #include "EngineMain.h" 4 | #include "MockAnalogService.h" 5 | #include "MockDigitalService.h" 6 | #include "MockPwmService.h" 7 | #include "MockTimerService.h" 8 | #include "MockCANService.h" 9 | #include 10 | using namespace testing; 11 | 12 | using namespace OperationArchitecture; 13 | using namespace EmbeddedIOServices; 14 | using namespace EmbeddedIOOperations; 15 | using namespace EFIGenie; 16 | 17 | namespace UnitTests 18 | { 19 | class AcceptanceTest : public ::testing::Test 20 | { 21 | protected: 22 | MockAnalogService _analogService; 23 | MockDigitalService _digitalService; 24 | MockPwmService _pwmService; 25 | MockTimerService _timerService; 26 | MockCANService _canService; 27 | EmbeddedIOServiceCollection _embeddedIOServiceCollection; 28 | void *_config; 29 | size_t _sizeOut = 0; 30 | EngineMain *_engineMain; 31 | callback_t _crankTriggerCallback; 32 | callback_t _camTriggerCallback; 33 | 34 | AcceptanceTest() 35 | { 36 | // size_t uint8_align = alignof(uint8_t); 37 | // size_t uint16_align = alignof(uint16_t); 38 | // size_t uint32_align = alignof(uint32_t); 39 | // size_t uint64_align = alignof(uint64_t); 40 | // size_t int8_align = alignof(int8_t); 41 | // size_t int16_align = alignof(int16_t); 42 | // size_t int32_align = alignof(int32_t); 43 | // size_t int64_align = alignof(int64_t); 44 | // size_t bool_align = alignof(bool); 45 | // size_t float_align = alignof(float); 46 | // size_t double_align = alignof(double); 47 | 48 | 49 | _embeddedIOServiceCollection.AnalogService = &_analogService; 50 | _embeddedIOServiceCollection.DigitalService = &_digitalService; 51 | _embeddedIOServiceCollection.PwmService = &_pwmService; 52 | _embeddedIOServiceCollection.TimerService = &_timerService; 53 | _embeddedIOServiceCollection.CANService = &_canService; 54 | 55 | EXPECT_CALL(_timerService, GetTicksPerSecond()).WillRepeatedly(Return(5000)); 56 | EXPECT_CALL(_digitalService, AttachInterrupt(_, _)).WillOnce(SaveArg<1>(&_crankTriggerCallback)).WillOnce(SaveArg<1>(&_camTriggerCallback)); 57 | 58 | std::ifstream file("tune.bin", std::ios::binary | std::ios::ate); 59 | std::streamsize size = file.tellg(); 60 | file.seekg(0, std::ios::beg); 61 | 62 | _config = malloc(size); 63 | if (file.read(reinterpret_cast(_config), size)) 64 | { 65 | _engineMain = new EngineMain(_config, _sizeOut, &_embeddedIOServiceCollection, new GeneratorMap()); 66 | } 67 | } 68 | }; 69 | 70 | TEST_F(AcceptanceTest, ParsingConfigDidNotBreak) 71 | { 72 | } 73 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # EFIGenie 2 | 3 | An open source engine management. It uses a taskmanager and is coded in a 4 | operation oriented architecture. 5 | 6 | The target microcontroller at the moment are the cheap but powerful STM32 line of processors 7 | but any microcontroller can be ported by creating just 4 services, 8 | AnalogService, DigitalService, PwmService, TimerService that can be found in the https://github.com/FL0WL0W/EmbeddedIOServices 9 | repository. A large benefit to this architecture is that new sensors/abilities/features/etc. 10 | can be added very easily and can be chosen between the old and new through the configs. 11 | which means decoders can be changed, sensors added or removed, features added or removed. 12 | all without having to recompile. There are also some wrapper classes where things like 13 | two step rev limiters can be implemented or tri-step or even quad-step. still have a list of 14 | things to complete but i am at a state where i am testing the ecu. At this point it has run an engine https://www.youtube.com/watch?v=jJOaTFBkKEE 15 | 16 | ## Features 17 | * Customizable(size, limits, and axis) 3D Fuel and Ignition Maps (Speed Density, Alpha N) 18 | * Customizable formulas for all engine parameters 19 | * Cusotmizable inputs and outputs 20 | * Unlimited cylinder sequential injection and ignition (Depends on hardware) 21 | * Stm32Devboard - 8 cylinder sequential fuel and ignition (10, 12, 14, 16 wasted spark/banked injection) 22 | * Flexible Crank Cam Decoder 23 | * GM 24x (Cam + Crank, Crank Only, or Cam only) 24 | * Universal 1X decoder 25 | * Configurable Missing Tooth Decoder 26 | * --More to come 27 | * Firmware features 28 | * Unit Tests to ensure everything works 29 | * Operation Architecture 30 | * Easily add new sensors 31 | 32 | ## Building 33 | After trying with a few different toolchains the tool chain that seemed to work the best is GCC/G++9. 34 | 35 | * Ubuntu 20.04 36 | * GCC/G++9 37 | * CMake 38 | 39 | VSCode Extensions: 40 | * C/C++ 41 | * CMake 42 | * CMake Tools 43 | * Cortex-Debug 44 | * GoogleTest Adapter 45 | 46 | ``` 47 | sudo apt-get install cmake gcc g++ gdb 48 | ``` 49 | 50 | ### Building the Tests 51 | Make 52 | 53 | ## TODO List 54 | * *Idle Control 55 | * After Start Enrichment (Add Engine Time Since Started, AE can be configured in GUI with that variable available) 56 | * Customizable(size and seperations) FuelTrim Predictive Multi Channel Historic Table 57 | * Fuel Trim (Wideband/Pid and Narrowband | predictive based on TPS or MAP) 58 | * Transmission Control (Paddle shift transmission solenoid) 59 | * Transmission Control (line pressure control) 60 | * Transmission Control (automatic gear shifts) 61 | * OBD2 62 | 63 | > *High Priority 64 | -------------------------------------------------------------------------------- /src/ExpanderMain.cpp: -------------------------------------------------------------------------------- 1 | #include "ExpanderMain.h" 2 | #include "Operations/OperationFactoryRegister.h" 3 | #include "Operations/EmbeddedIOOperationFactoryRegister.h" 4 | #include "Operations/ReluctorOperationFactoryRegister.h" 5 | #include "Operations/EngineOperationFactoryRegister.h" 6 | #include "Operations/Operation_ReluctorGM24x.h" 7 | #include "Config.h" 8 | #include "CRC.h" 9 | 10 | using namespace OperationArchitecture; 11 | using namespace EmbeddedIOServices; 12 | using namespace EmbeddedIOOperations; 13 | using namespace ReluctorOperations; 14 | 15 | namespace EFIGenie 16 | { 17 | ExpanderMain::ExpanderMain(const void *config, size_t &sizeOut, const EmbeddedIOServiceCollection *embeddedIOServiceCollection, GeneratorMap *variableMap) 18 | { 19 | const uint32_t configSize = *reinterpret_cast(config) + sizeof(uint32_t); 20 | if(configSize == 0 || configSize > 100000) 21 | return; 22 | 23 | const uint32_t configCRC = CRC::CRC32(config, configSize); 24 | if(*reinterpret_cast(reinterpret_cast(config) + configSize) != configCRC) 25 | { 26 | return; 27 | } 28 | Config::OffsetConfig(config, sizeOut, sizeof(uint32_t)); 29 | 30 | OperationFactory * operationFactory = new OperationFactory(); 31 | 32 | OperationFactoryRegister::Register(10000, operationFactory, variableMap); 33 | EmbeddedIOOperationFactoryRegister::Register(20000, operationFactory, embeddedIOServiceCollection); 34 | ReluctorOperationFactoryRegister::Register(30000, operationFactory); 35 | EngineOperationFactoryRegister::Register(40000, operationFactory, embeddedIOServiceCollection); 36 | 37 | size_t size = 0; 38 | do 39 | { 40 | size = 0; 41 | const uint32_t operationId = Config::CastAndOffset(config, sizeOut); 42 | if(operationId == 0) 43 | break; 44 | AbstractOperation *operation = operationFactory->Create(config, size); 45 | operationFactory->Register(operationId, operation); 46 | Config::OffsetConfig(config, sizeOut, size); 47 | } 48 | while(size > 0); 49 | 50 | size = 0; 51 | _mainLoopExecute = operationFactory->Create(config, size); 52 | Config::OffsetConfig(config, sizeOut, size); 53 | 54 | operationFactory->Clear();; 55 | delete _operationFactory; 56 | Config::OffsetConfig(config, sizeOut, sizeof(uint32_t));//CRC 57 | } 58 | 59 | ExpanderMain::~ExpanderMain() 60 | { 61 | } 62 | 63 | void ExpanderMain::Setup() 64 | { 65 | } 66 | 67 | void ExpanderMain::Loop() 68 | { 69 | _mainLoopExecute->Execute(); 70 | } 71 | } -------------------------------------------------------------------------------- /test/src/InjectorGramServiceWrapper_DFCOTests.cpp: -------------------------------------------------------------------------------- 1 | // #include "gmock/gmock.h" 2 | // #include "gtest/gtest.h" 3 | // #include "EngineControlServices/InjectorGramService/InjectorGramServiceWrapper_DFCO.h" 4 | // #include "MockTimerService.h" 5 | // #include "MockAfrService.h" 6 | // #include "MockInjectorGramService.h" 7 | // #include "MockFloatInputService.h" 8 | // #include "EngineControlServices/RpmService/RpmService.h" 9 | // #include "Service/EngineControlServicesServiceBuilderRegister.h" 10 | // using ::testing::AtLeast; 11 | // using ::testing::Return; 12 | 13 | // using namespace Reluctor; 14 | // using namespace HardwareAbstraction; 15 | // using namespace IOServices; 16 | // using namespace Service; 17 | 18 | // namespace UnitTests 19 | // { 20 | // class InjectorGramServiceWrapper_DFCOTests : public ::testing::Test 21 | // { 22 | // protected: 23 | // IInjectorGramService *_injectorGramService; 24 | // MockFloatInputService _tpsService; 25 | // RpmService *_rpmService; 26 | 27 | // MockInjectorGramService _child; 28 | 29 | // InjectorGramServiceWrapper_DFCOTests() 30 | // { 31 | // _rpmService = new RpmService(0, 0); 32 | 33 | // InjectorGramServiceWrapper_DFCOConfig *config = new InjectorGramServiceWrapper_DFCOConfig(); 34 | 35 | // config->TpsThreshold = 0.1; 36 | // config->RpmEnable = 1200; 37 | // config->RpmDisable = 1300; 38 | 39 | // _child.InjectorGrams = reinterpret_cast(calloc(sizeof(float) * 8, sizeof(float) * 8)); 40 | 41 | // _injectorGramService = new InjectorGramServiceWrapper_DFCO(config, &_tpsService, _rpmService, &_child); 42 | // } 43 | 44 | // ~InjectorGramServiceWrapper_DFCOTests() override 45 | // { 46 | // free(_rpmService); 47 | // free(_injectorGramService); 48 | // } 49 | // }; 50 | 51 | // TEST_F(InjectorGramServiceWrapper_DFCOTests, WhenGettingInjectorGramsThenCorrectInjectorGramsIsReturned) 52 | // { 53 | // _rpmService->Rpm = 1000; 54 | // _tpsService.Value = 0.2f; 55 | // _child.InjectorGrams[0] = 10; 56 | // _injectorGramService->CalculateInjectorGrams(); 57 | // ASSERT_NEAR(10, _injectorGramService->InjectorGrams[0], 0.0001f); 58 | 59 | // _rpmService->Rpm = 2000; 60 | // _injectorGramService->CalculateInjectorGrams(); 61 | // ASSERT_NEAR(10, _injectorGramService->InjectorGrams[0], 0.0001f); 62 | 63 | // _rpmService->Rpm = 2000; 64 | // _tpsService.Value = 0.05f; 65 | // _injectorGramService->CalculateInjectorGrams(); 66 | // ASSERT_EQ(0, _injectorGramService->InjectorGrams); 67 | 68 | // _rpmService->Rpm = 1350; 69 | // _injectorGramService->CalculateInjectorGrams(); 70 | // ASSERT_EQ(0, _injectorGramService->InjectorGrams); 71 | 72 | // _rpmService->Rpm = 1250; 73 | // _injectorGramService->CalculateInjectorGrams(); 74 | // ASSERT_NEAR(10, _injectorGramService->InjectorGrams[0], 0.0001f); 75 | // } 76 | // } -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # CMakeList.txt : CMake project for Reluctor, include source and define 2 | # project specific logic here. 3 | # 4 | cmake_minimum_required (VERSION 3.30[3.30]) 5 | option(EFIGenie_build_tests "Build all of EFIGenie tests" OFF) 6 | 7 | set(TARGET EFIGenie) 8 | set(HDR_DIR include) 9 | set(SRCS_DIR src) 10 | set(LIBRARIES_DIR ${CMAKE_SOURCE_DIR}/libs) 11 | 12 | project(${TARGET} CXX) 13 | 14 | Set(FETCHCONTENT_QUIET FALSE) 15 | include(FetchContent) 16 | 17 | FetchContent_Declare( 18 | OperationArchitecture 19 | GIT_REPOSITORY https://github.com/FL0WL0W/OperationArchitecture 20 | GIT_TAG main 21 | GIT_PROGRESS TRUE 22 | GIT_SHALLOW TRUE 23 | SOURCE_DIR ${LIBRARIES_DIR}/OperationArchitecture 24 | ) 25 | FetchContent_MakeAvailable(OperationArchitecture) 26 | execute_process(COMMAND git pull WORKING_DIRECTORY ${LIBRARIES_DIR}/OperationArchitecture) 27 | FetchContent_Declare( 28 | EmbeddedIOServices 29 | GIT_REPOSITORY https://github.com/FL0WL0W/EmbeddedIOServices 30 | GIT_TAG main 31 | GIT_PROGRESS TRUE 32 | GIT_SHALLOW TRUE 33 | SOURCE_DIR ${LIBRARIES_DIR}/EmbeddedIOServices 34 | ) 35 | FetchContent_MakeAvailable(EmbeddedIOServices) 36 | execute_process(COMMAND git pull WORKING_DIRECTORY ${LIBRARIES_DIR}/EmbeddedIOServices) 37 | FetchContent_Declare( 38 | EmbeddedIOOperations 39 | GIT_REPOSITORY https://github.com/FL0WL0W/EmbeddedIOOperations 40 | GIT_TAG main 41 | GIT_PROGRESS TRUE 42 | GIT_SHALLOW TRUE 43 | SOURCE_DIR ${LIBRARIES_DIR}/EmbeddedIOOperations 44 | ) 45 | FetchContent_MakeAvailable(EmbeddedIOOperations) 46 | execute_process(COMMAND git pull WORKING_DIRECTORY ${LIBRARIES_DIR}/EmbeddedIOOperations) 47 | FetchContent_Declare( 48 | ReluctorOperations 49 | GIT_REPOSITORY https://github.com/FL0WL0W/ReluctorOperations 50 | GIT_TAG main 51 | GIT_PROGRESS TRUE 52 | GIT_SHALLOW TRUE 53 | SOURCE_DIR ${LIBRARIES_DIR}/ReluctorOperations 54 | ) 55 | FetchContent_MakeAvailable(ReluctorOperations) 56 | execute_process(COMMAND git pull WORKING_DIRECTORY ${LIBRARIES_DIR}/ReluctorOperations) 57 | 58 | # headers are always in include/ 59 | file(GLOB_RECURSE HDRS CONFIGURE_DEPENDS ${HDR_DIR}/*.h) 60 | include_directories(${HDR_DIR}) 61 | # sources are always in src/ 62 | file(GLOB_RECURSE SRCS CONFIGURE_DEPENDS ${SRCS_DIR}/*.cpp) 63 | 64 | # create library 65 | add_library(${TARGET} STATIC ${HDRS} ${SRCS}) 66 | target_include_directories(${TARGET} PUBLIC ${HDR_DIR}) 67 | target_link_libraries(${TARGET} PUBLIC OperationArchitecture) 68 | target_link_libraries(${TARGET} PUBLIC EmbeddedIOServices) 69 | target_link_libraries(${TARGET} PUBLIC EmbeddedIOOperations) 70 | target_link_libraries(${TARGET} PUBLIC ReluctorOperations) 71 | 72 | if(EFIGenie_build_tests) 73 | #include test 74 | add_subdirectory(test) 75 | endif() 76 | 77 | # install library 78 | install(TARGETS ${TARGET} LIBRARY DESTINATION lib) 79 | install(DIRECTORY "${HDR_DIR}/" DESTINATION include) -------------------------------------------------------------------------------- /src/Operations/Operation_EnginePosition.cpp: -------------------------------------------------------------------------------- 1 | #include "Operations/Operation_EnginePosition.h" 2 | #include "Config.h" 3 | 4 | using namespace OperationArchitecture; 5 | using namespace ReluctorOperations; 6 | 7 | #ifdef OPERATION_ENGINEPOSITION_H 8 | namespace EFIGenie 9 | { 10 | Operation_EnginePosition::Operation_EnginePosition(bool crankPriority) 11 | { 12 | _crankPriority = crankPriority; 13 | } 14 | 15 | EnginePosition Operation_EnginePosition::Execute(ReluctorResult crankPosition, ReluctorResult camPosition) 16 | { 17 | EnginePosition ret; 18 | ret.Synced = false; 19 | if(crankPosition.Synced) 20 | { 21 | bool camSynced = true; 22 | if(!camPosition.Synced) 23 | camSynced = false; 24 | //check for eroneous cam speed when crank has priority 25 | if(_crankPriority) 26 | { 27 | const float crankPositionDotOneThird = crankPosition.PositionDot / 3; 28 | const float crankPositionDotTwoThirds = crankPositionDotOneThird * 2; 29 | if(camPosition.PositionDot < crankPositionDotOneThird || camPosition.PositionDot > crankPositionDotTwoThirds) 30 | camSynced = false; 31 | } 32 | 33 | if(camSynced) 34 | { 35 | //we have both crank and cam 36 | //decide which one to use for scheduling. 37 | if(_crankPriority) 38 | { 39 | ret.Synced = true; 40 | ret.Position = crankPosition.Position; 41 | ret.PositionDot = crankPosition.PositionDot; 42 | float decision = camPosition.Position * 2 - crankPosition.Position; 43 | if(decision > 180 || decision < -180) 44 | { 45 | //we are on the second half of the cam 46 | ret.Position += 360; 47 | } 48 | ret.Sequential = true; 49 | ret.CalculatedTick = crankPosition.CalculatedTick; 50 | } 51 | else 52 | { 53 | //the crank reluctor is essential useless unless the cam sensor goes out of sync 54 | ret.Synced = true; 55 | ret.Position = camPosition.Position * 2; 56 | ret.PositionDot = camPosition.PositionDot * 2; 57 | ret.Sequential = true; 58 | ret.CalculatedTick = camPosition.CalculatedTick; 59 | } 60 | } 61 | else 62 | { 63 | //we only have the crank sensor 64 | ret.Synced = true; 65 | ret.Position = crankPosition.Position; 66 | ret.PositionDot = crankPosition.PositionDot; 67 | ret.Sequential = false; 68 | ret.CalculatedTick = crankPosition.CalculatedTick; 69 | } 70 | } 71 | else if(camPosition.Synced) 72 | { 73 | //we only have the cam sensor 74 | ret.Synced = true; 75 | ret.Position = camPosition.Position * 2; 76 | ret.PositionDot = camPosition.PositionDot * 2; 77 | ret.Sequential = true; 78 | ret.CalculatedTick = camPosition.CalculatedTick; 79 | } 80 | 81 | const rpm_t rpm = ret.GetRPM(); 82 | if(rpm > 20000 || rpm < 2) 83 | { 84 | ret.Synced = false; 85 | ret.Position = 0; 86 | ret.PositionDot = 0; 87 | ret.Sequential = false; 88 | } 89 | _previousPreviousReluctorResult = _previousReluctorResult; 90 | _previousReluctorResult = ret; 91 | return ret; 92 | } 93 | } 94 | #endif -------------------------------------------------------------------------------- /test/src/InjectorGramServiceTests.cpp: -------------------------------------------------------------------------------- 1 | // #include "gmock/gmock.h" 2 | // #include "gtest/gtest.h" 3 | // #include "EngineControlServices/InjectorGramService/InjectorGramService.h" 4 | // #include "MockTimerService.h" 5 | // #include "MockAfrService.h" 6 | // #include "MockCylinderAirTemperatureService.h" 7 | // #include "MockCylinderAirMassService.h" 8 | // #include "EngineControlServices/RpmService/RpmService.h" 9 | // #include "Service/EngineControlServicesServiceBuilderRegister.h" 10 | // #include "Service/HardwareAbstractionServiceBuilder.h" 11 | // using ::testing::AtLeast; 12 | // using ::testing::Return; 13 | 14 | // using namespace Reluctor; 15 | // using namespace HardwareAbstraction; 16 | // using namespace IOServices; 17 | // using namespace Service; 18 | 19 | // namespace UnitTests 20 | // { 21 | // class InjectorGramServiceTests : public ::testing::Test 22 | // { 23 | // protected: 24 | // IInjectorGramService *_injectorGramService; 25 | // ServiceLocator *_serviceLocator; 26 | // MockAfrService _afrService; 27 | // MockCylinderAirmassService _cylinderAirmassService; 28 | // CallBackGroup *_tickCallBackGroup; 29 | 30 | // InjectorGramServiceTests() 31 | // { 32 | // _serviceLocator = new ServiceLocator(); 33 | 34 | // _serviceLocator->Register(BUILDER_IAFRSERVICE, 0, &_afrService); 35 | // _serviceLocator->Register(BUILDER_ICYLINDERAIRMASSSERVICE, 0, &_cylinderAirmassService); 36 | 37 | // _tickCallBackGroup = new CallBackGroup(); 38 | // _serviceLocator->Register(TICK_CALL_BACK_GROUP, (void *)_tickCallBackGroup); 39 | 40 | // InjectorGramServiceConfig *injectorGramConfig = reinterpret_cast(malloc(sizeof(InjectorGramServiceConfig))); 41 | 42 | // uint8_t injectorToCylinder[8] = {0, 1, 2, 3, 4, 5, 6, 7}; 43 | 44 | // injectorGramConfig->Injectors = 8; 45 | 46 | // void *config = malloc(injectorGramConfig->Size()+1); 47 | // void *buildConfig = config; 48 | 49 | // *((uint8_t *)buildConfig) = 1; 50 | // buildConfig = (void *)(((uint8_t *)buildConfig) + 1); 51 | 52 | // memcpy(buildConfig, injectorGramConfig, sizeof(InjectorGramServiceConfig)); 53 | // buildConfig = (void *)((unsigned char *)buildConfig + sizeof(InjectorGramServiceConfig)); 54 | 55 | // memcpy(buildConfig, injectorToCylinder, sizeof(injectorToCylinder)); 56 | // buildConfig = (void*)((unsigned char *)buildConfig + sizeof(injectorToCylinder)); 57 | 58 | // _cylinderAirmassService.CylinderAirmass = reinterpret_cast(calloc(sizeof(float) * injectorGramConfig->Injectors, sizeof(float) * injectorGramConfig->Injectors)); 59 | 60 | // unsigned int size = 0; 61 | // _injectorGramService = reinterpret_cast(InjectorGramService::CreateInjectorGramService(_serviceLocator, config, size)); 62 | // } 63 | 64 | // ~InjectorGramServiceTests() override 65 | // { 66 | // free(_injectorGramService); 67 | // free(_tickCallBackGroup); 68 | // free(_serviceLocator); 69 | // } 70 | // }; 71 | 72 | // TEST_F(InjectorGramServiceTests, WhenGettingInjectorGramsThenCorrectInjectorGramsIsReturned) 73 | // { 74 | // _cylinderAirmassService.CylinderAirmass[0] = 1.2; 75 | // _afrService.Afr = 14.7f; 76 | // _injectorGramService->CalculateInjectorGrams(); 77 | // ASSERT_NEAR(0.0816326530612245f, _injectorGramService->InjectorGrams[0], 0.0001f); 78 | // } 79 | // } -------------------------------------------------------------------------------- /test/src/PrimeService_StaticPulseWidthTests.cpp: -------------------------------------------------------------------------------- 1 | // #include "gmock/gmock.h" 2 | // #include "gtest/gtest.h" 3 | // #include "MockBooleanOutputService.h" 4 | // #include "MockTimerService.h" 5 | // #include "Service/EngineControlServicesServiceBuilderRegister.h" 6 | // #include "Service/HardwareAbstractionServiceBuilder.h" 7 | // using ::testing::AtLeast; 8 | // using ::testing::Return; 9 | 10 | // using namespace HardwareAbstraction; 11 | // using namespace EngineControlServices; 12 | // using namespace Service; 13 | 14 | // namespace UnitTests 15 | // { 16 | // class PrimeService_StaticPulseWidthTests : public ::testing::Test 17 | // { 18 | // protected: 19 | // MockTimerService _timerService; 20 | // IPrimeService *_primeService; 21 | // ServiceLocator *_serviceLocator; 22 | // MockBooleanOutputService _ignitorOutputService0; 23 | // MockBooleanOutputService _ignitorOutputService1; 24 | // MockBooleanOutputService _ignitorOutputService2; 25 | // MockBooleanOutputService _ignitorOutputService3; 26 | // CallBackGroup *_tickCallBackGroup; 27 | // CallBackGroup *_postReluctorCallBackGroup; 28 | 29 | // PrimeService_StaticPulseWidthTests() 30 | // { 31 | // _serviceLocator = new ServiceLocator(); 32 | // _tickCallBackGroup = new CallBackGroup(); 33 | // _serviceLocator->Register(TICK_CALL_BACK_GROUP, (void *)_tickCallBackGroup); 34 | // _postReluctorCallBackGroup = new CallBackGroup(); 35 | // _serviceLocator->Register(POST_RELUCTOR_SYNC_CALL_BACK_GROUP, (void *)_postReluctorCallBackGroup); 36 | 37 | // IBooleanOutputService **ignitorOutputServices = (IBooleanOutputService **)malloc(sizeof(IBooleanOutputService **) * 5); 38 | // ignitorOutputServices[0] = (IBooleanOutputService *)(&_ignitorOutputService0); 39 | // ignitorOutputServices[1] = (IBooleanOutputService *)(&_ignitorOutputService1); 40 | // ignitorOutputServices[2] = (IBooleanOutputService *)(&_ignitorOutputService2); 41 | // ignitorOutputServices[3] = (IBooleanOutputService *)(&_ignitorOutputService3); 42 | // ignitorOutputServices[4] = 0; 43 | 44 | // _serviceLocator->Register(BUILDER_IBOOLEANOUTPUTSERVICEARRAY, INSTANCE_INJECTORS, ignitorOutputServices); 45 | // _serviceLocator->Register(TIMER_SERVICE_ID, &_timerService); 46 | 47 | // void *config = malloc(5); 48 | // void *buildConfig = config; 49 | 50 | // //fuel prime service id 51 | // *((unsigned char *)buildConfig) = 1; 52 | // buildConfig = (void *)(((unsigned char *)buildConfig) + 1); 53 | 54 | // //Prime Time 55 | // *((float *)buildConfig) = 1; 56 | // buildConfig = (void*)((float *)buildConfig + 1); 57 | 58 | // EXPECT_CALL(_timerService, GetTicksPerSecond()) 59 | // .WillRepeatedly(Return(5000)); 60 | // unsigned int sizeOut = 0; 61 | // _primeService = reinterpret_cast(IPrimeService::CreatePrimeService(_serviceLocator, config, sizeOut)); 62 | // } 63 | // }; 64 | 65 | // TEST_F(PrimeService_StaticPulseWidthTests, WhenUsingFuelPrimeThenFuelPrimeIsUsed) 66 | // { 67 | // EXPECT_CALL(_ignitorOutputService0, OutputSet()).Times(1); 68 | // EXPECT_CALL(_ignitorOutputService1, OutputSet()).Times(1); 69 | // EXPECT_CALL(_ignitorOutputService2, OutputSet()).Times(1); 70 | // EXPECT_CALL(_ignitorOutputService3, OutputSet()).Times(1); 71 | // EXPECT_CALL(_timerService, GetTick()).Times(5).WillRepeatedly(Return(0)); 72 | // EXPECT_CALL(_timerService, ScheduleCallBack(5000)).Times(4); 73 | // _postReluctorCallBackGroup->Execute(); 74 | 75 | // EXPECT_CALL(_ignitorOutputService0, OutputReset()).Times(1); 76 | // EXPECT_CALL(_ignitorOutputService1, OutputReset()).Times(1); 77 | // EXPECT_CALL(_ignitorOutputService2, OutputReset()).Times(1); 78 | // EXPECT_CALL(_ignitorOutputService3, OutputReset()).Times(1); 79 | // EXPECT_CALL(_timerService, GetTick()).Times(8).WillRepeatedly(Return(5000)); 80 | // _timerService.ReturnCallBack(); 81 | // } 82 | // } -------------------------------------------------------------------------------- /src/EngineMain.cpp: -------------------------------------------------------------------------------- 1 | #include "EngineMain.h" 2 | #include "Operations/OperationFactoryRegister.h" 3 | #include "Operations/EmbeddedIOOperationFactoryRegister.h" 4 | #include "Operations/ReluctorOperationFactoryRegister.h" 5 | #include "Operations/EngineOperationFactoryRegister.h" 6 | #include "Operations/Operation_ReluctorGM24x.h" 7 | #include "Config.h" 8 | #include "CRC.h" 9 | 10 | using namespace OperationArchitecture; 11 | using namespace EmbeddedIOServices; 12 | using namespace EmbeddedIOOperations; 13 | using namespace ReluctorOperations; 14 | 15 | namespace EFIGenie 16 | { 17 | EngineMain::EngineMain(const void *config, size_t &sizeOut, const EmbeddedIOServiceCollection *embeddedIOServiceCollection, GeneratorMap *variableMap) 18 | { 19 | const uint32_t configSize = *reinterpret_cast(config) + sizeof(uint32_t); 20 | if(configSize == 0 || configSize > 100000) 21 | return; 22 | 23 | const uint32_t configCRC = CRC::CRC32(config, configSize); 24 | if(*reinterpret_cast(reinterpret_cast(config) + configSize) != configCRC) 25 | { 26 | return; 27 | } 28 | sizeOut = 0; 29 | Config::OffsetConfig(config, sizeOut, sizeof(uint32_t)); 30 | 31 | _operationFactory = new OperationFactory(); 32 | 33 | OperationFactoryRegister::Register(10000, _operationFactory, variableMap); 34 | EmbeddedIOOperationFactoryRegister::Register(20000, _operationFactory, embeddedIOServiceCollection); 35 | ReluctorOperationFactoryRegister::Register(30000, _operationFactory); 36 | EngineOperationFactoryRegister::Register(40000, _operationFactory, embeddedIOServiceCollection); 37 | 38 | size_t size = 0; 39 | do 40 | { 41 | size = 0; 42 | const uint32_t operationId = Config::CastAndOffset(config, sizeOut); 43 | if(operationId == 0) 44 | break; 45 | AbstractOperation *operation = _operationFactory->Create(config, size); 46 | _operationFactory->Register(operationId, operation); 47 | Config::OffsetConfig(config, sizeOut, size); 48 | if(sizeOut > configSize) 49 | return; 50 | } 51 | while(size > 0); 52 | 53 | size = 0; 54 | _inputsExecute = _operationFactory->Create(config, size); 55 | Config::OffsetConfig(config, sizeOut, size); 56 | if(sizeOut > configSize) 57 | return; 58 | 59 | size = 0; 60 | _preSyncExecute = _operationFactory->Create(config, size); 61 | Config::OffsetConfig(config, sizeOut, size); 62 | if(sizeOut > configSize) 63 | return; 64 | 65 | size = 0; 66 | _syncCondition = _operationFactory->Create(config, size); 67 | Config::OffsetConfig(config, sizeOut, size); 68 | if(sizeOut > configSize) 69 | return; 70 | 71 | size = 0; 72 | _mainLoopExecute = _operationFactory->Create(config, size); 73 | Config::OffsetConfig(config, sizeOut, size); 74 | if(sizeOut > configSize) 75 | return; 76 | 77 | _operationFactory->Clear();; 78 | Config::OffsetConfig(config, sizeOut, sizeof(uint32_t));//CRC 79 | } 80 | 81 | EngineMain::~EngineMain() 82 | { 83 | if(_operationFactory != 0) 84 | delete _operationFactory; 85 | } 86 | 87 | void EngineMain::Setup() 88 | { 89 | if(_inputsExecute != 0) _inputsExecute->Execute(); 90 | if(_preSyncExecute != 0) _preSyncExecute->Execute(); 91 | } 92 | 93 | void EngineMain::Loop() 94 | { 95 | if(_inputsExecute != 0) _inputsExecute->Execute(); 96 | if(!_syncedOnce && _syncCondition != 0) 97 | _syncedOnce = _syncCondition->Execute(); 98 | if(_syncedOnce && _mainLoopExecute != 0) 99 | { 100 | _mainLoopExecute->Execute(); 101 | } 102 | } 103 | } -------------------------------------------------------------------------------- /src/CommunicationHandlers/CommunicationHandler_OBD2.cpp: -------------------------------------------------------------------------------- 1 | #include "CommunicationHandlers/CommunicationHandler_OBD2.h" 2 | 3 | using namespace EmbeddedIOServices; 4 | using namespace OperationArchitecture; 5 | 6 | #ifdef COMMUNICATIONHANDLER_OBD2_H 7 | namespace EFIGenie 8 | { 9 | CommunicationHandler_OBD2::CommunicationHandler_OBD2(GeneratorMap *variableMap, const OBD2VariableMap *obd2VariableMap) : 10 | _variableMap(variableMap), 11 | _obd2VariableMap(obd2VariableMap) 12 | { 13 | } 14 | 15 | /** 16 | * @brief Parse up to length bytes of data and perform communication service 17 | * response based on service code detected. 18 | * 19 | * The form of the data will generally be service type followed by PID. The 20 | * PID typically specifies physical sensor readings such as pressure or 21 | * engine load. This function will interpret that data into the proper format 22 | * for communication on the CAN bus. 23 | * 24 | * @param data A pointer to length bytes. Service type is the first byte and PID is the second. 25 | * @param length Number of bytes that the data pointer is pointing to. 26 | * @return size_t Number of bytes parsed from data. 27 | */ 28 | size_t CommunicationHandler_OBD2::Receive(communication_send_callback_t sendCallBack, const void *data, size_t length) 29 | { 30 | uint8_t service = *reinterpret_cast(data); //grab service from data 31 | data = reinterpret_cast(data) + 1; //ofset data 32 | 33 | switch(service) 34 | { 35 | // Service/Mode 01: Show Current Data 36 | case 1: 37 | { 38 | uint8_t pid = *reinterpret_cast(data); //grab pid from data 39 | data = reinterpret_cast(data) + 1; //offset data 40 | switch(pid) 41 | { 42 | // Calculated Engine Load 43 | case 4: 44 | { 45 | GeneratorMap::iterator it = _variableMap->find(_obd2VariableMap->CalculatedEngineLoadID); 46 | if(it != _variableMap->end()) 47 | { 48 | // Engine load will be a float value between 0 and 1 49 | // Need to normalize to a byte value by multiplying by 255, then perform a type conversion. 50 | uint8_t cel = static_cast(*it->second) * 255; 51 | sendCallBack(&cel, 1); 52 | return 2; 53 | } 54 | } 55 | // Engine Coolant Temp 56 | case 5: 57 | { 58 | GeneratorMap::iterator it = _variableMap->find(_obd2VariableMap->EngineCoolantTempID); 59 | if(it != _variableMap->end()) 60 | { 61 | //add 40 to align with -40 to 215 of obd2 pid. then convert to uint8_t 62 | uint8_t ect = static_cast(*it->second) + 40; 63 | sendCallBack(&ect, 1); 64 | return 2; 65 | } 66 | } 67 | // Fuel Trim 68 | case 6: 69 | case 7: 70 | case 8: 71 | case 9: 72 | { 73 | GeneratorMap::iterator it = _variableMap->find(_obd2VariableMap->FuelTrimID[pid - 6]); 74 | if(it != _variableMap->end()) 75 | { 76 | // Min value is -100, max is 99.2. Need to normalize so it will fit in an unsigned byte. 77 | uint8_t ft = static_cast(static_cast(*it->second) * 128) + 128; 78 | sendCallBack(&ft, 1); 79 | return 2; 80 | } 81 | } 82 | // Fuel Pressure 83 | case 10: 84 | { 85 | GeneratorMap::iterator it = _variableMap->find(_obd2VariableMap->FuelPressureID); 86 | if(it != _variableMap->end()) 87 | { 88 | // Fuel pressure is given in Bar but needs to be returned in kPa. Additionally, the byte 89 | // will be multiplied by 3 by the receiver so must divide by 3 as well. 90 | uint8_t fp = static_cast(*it->second) * 100 / 3; 91 | sendCallBack(&fp, 1); 92 | return 2; 93 | } 94 | } 95 | // Intake Manifold Absolute Pressure 96 | case 11: 97 | { 98 | GeneratorMap::iterator it = _variableMap->find(_obd2VariableMap->IntakeManifoldPressureID); 99 | if(it != _variableMap->end()) 100 | { 101 | // Intake manifold pressure is given in Bar but needs to be returned in kPa. 102 | uint8_t imp = static_cast(*it->second) * 100; 103 | sendCallBack(&imp, 1); 104 | return 2; 105 | } 106 | } 107 | } 108 | } 109 | } 110 | 111 | return 0; 112 | } 113 | } 114 | #endif -------------------------------------------------------------------------------- /test/src/CylinderAirmassService_SDTests.cpp: -------------------------------------------------------------------------------- 1 | // #include "gmock/gmock.h" 2 | // #include "gtest/gtest.h" 3 | // #include "EngineControlServices/CylinderAirmassService/CylinderAirmassService_SD.h" 4 | // #include "MockTimerService.h" 5 | // #include "MockFloatInputService.h" 6 | // #include "MockCylinderAirTemperatureService.h" 7 | // #include "EngineControlServices/RpmService/RpmService.h" 8 | // #include "Service/EngineControlServicesServiceBuilderRegister.h" 9 | // #include "Service/IOServicesServiceBuilderRegister.h" 10 | // #include "Service/HardwareAbstractionServiceBuilder.h" 11 | // using ::testing::AtLeast; 12 | // using ::testing::Return; 13 | 14 | // using namespace Reluctor; 15 | // using namespace HardwareAbstraction; 16 | // using namespace IOServices; 17 | // using namespace Service; 18 | 19 | // namespace UnitTests 20 | // { 21 | // class CylinderAirmassService_SDTests : public ::testing::Test 22 | // { 23 | // protected: 24 | // ICylinderAirmassService *_cylinderAirmassService; 25 | // ServiceLocator *_serviceLocator; 26 | // MockFloatInputService _mapService; 27 | // MockCylinderAirTemperatureService _cylinderAirTemperatureService; 28 | // RpmService *_rpmService; 29 | // CallBackGroup *_tickCallBackGroup; 30 | 31 | // CylinderAirmassService_SDTests() 32 | // { 33 | // _serviceLocator = new ServiceLocator(); 34 | 35 | // _serviceLocator->Register(BUILDER_IFLOATINPUTSERVICE, INSTANCE_MANIFOLD_ABSOLUTE_PRESSURE, &_mapService); 36 | // _serviceLocator->Register(BUILDER_ICYLINDERAIRTEMPERATURESERVICE, 0, &_cylinderAirTemperatureService); 37 | // _rpmService = new RpmService(0, 0); 38 | // _serviceLocator->Register(RPMSERVICE, _rpmService); 39 | 40 | // _tickCallBackGroup = new CallBackGroup(); 41 | // _serviceLocator->Register(TICK_CALL_BACK_GROUP, (void *)_tickCallBackGroup); 42 | 43 | // //GAS AFR TABLE, values in 1/1024 0 2000 4000 6000 44 | // unsigned short veTable[4 * 4] = { 8192, 8192, 4096, 8192, //0 45 | // 8192, 8192, 4096, 8192, //33 46 | // 8192, 8192, 4096, 8192, //67 47 | // 8192, 8192, 4096, 8192};//100 48 | 49 | // CylinderAirmassService_SDConfig *airmassConfig = reinterpret_cast(malloc(sizeof(CylinderAirmassService_SDConfig))); 50 | 51 | // airmassConfig->Cylinders = 8; 52 | // airmassConfig->Ml8thPerCylinder = 500 * 8; 53 | 54 | // airmassConfig->MaxRpm = 6000; 55 | // airmassConfig->MaxMap = 1; 56 | // airmassConfig->VeRpmResolution = 4; 57 | // airmassConfig->VeMapResolution = 4; 58 | 59 | // void *config = malloc(airmassConfig->Size()+1); 60 | // void *buildConfig = config; 61 | 62 | // *((uint8_t *)buildConfig) = 1; 63 | // buildConfig = (void *)(((uint8_t *)buildConfig) + 1); 64 | 65 | // memcpy(buildConfig, airmassConfig, sizeof(CylinderAirmassService_SDConfig)); 66 | // buildConfig = (void *)((unsigned char *)buildConfig + sizeof(CylinderAirmassService_SDConfig)); 67 | 68 | // memcpy(buildConfig, veTable, sizeof(veTable)); 69 | // buildConfig = (void*)((unsigned char *)buildConfig + sizeof(veTable)); 70 | 71 | // _cylinderAirTemperatureService.CylinderAirTemperature = reinterpret_cast(calloc(sizeof(float) * airmassConfig->Cylinders, sizeof(float) * airmassConfig->Cylinders)); 72 | 73 | // unsigned int size = 0; 74 | // _cylinderAirmassService = reinterpret_cast(ICylinderAirmassService::CreateCylinderAirmassService(_serviceLocator, config, size)); 75 | // } 76 | 77 | // ~CylinderAirmassService_SDTests() override 78 | // { 79 | // free(_cylinderAirmassService); 80 | // free(_rpmService); 81 | // free(_tickCallBackGroup); 82 | // free(_serviceLocator); 83 | // } 84 | // }; 85 | 86 | // TEST_F(CylinderAirmassService_SDTests, WhenGettingCylinderAirmassThenCorrectCylinderAirmassIsReturned) 87 | // { 88 | // _rpmService->Rpm = 2000; 89 | // _mapService.Value = 0.33f; 90 | // _cylinderAirTemperatureService.CylinderAirTemperature[0] = 50; 91 | // _cylinderAirmassService->CalculateCylinderAirmass(); 92 | // ASSERT_NEAR(0.18027f, _cylinderAirmassService->CylinderAirmass[0], 0.01f); 93 | 94 | // _mapService.Value = 0.66f; 95 | // _cylinderAirmassService->CalculateCylinderAirmass(); 96 | // ASSERT_NEAR(0.36054f, _cylinderAirmassService->CylinderAirmass[0], 0.01f); 97 | 98 | // _rpmService->Rpm = 4000; 99 | // _cylinderAirmassService->CalculateCylinderAirmass(); 100 | // ASSERT_NEAR(0.18027f, _cylinderAirmassService->CylinderAirmass[0], 0.01f); 101 | // } 102 | // } -------------------------------------------------------------------------------- /src/CommunicationHandlers/CommunicationHandler_EFIGenie.cpp: -------------------------------------------------------------------------------- 1 | #include "CommunicationHandlers/CommunicationHandler_EFIGenie.h" 2 | 3 | using namespace EmbeddedIOServices; 4 | using namespace EmbeddedIOOperations; 5 | using namespace OperationArchitecture; 6 | 7 | #ifdef COMMUNICATIONHANDLER_EFIGENIE_H 8 | namespace EFIGenie 9 | { 10 | CommunicationHandler_EFIGenie::CommunicationHandler_EFIGenie(GeneratorMap *variableMap, 11 | communicationhandler_efigenie_write_t writeCallback, 12 | communicationhandler_quit_t quitCallback, 13 | communicationhandler_start_t startCallback, 14 | const void *config) 15 | { 16 | //Get Variable 17 | _getVariableHandler = new CommunicationHandler_GetVariable(variableMap); 18 | RegisterReceiveCallBack([this](communication_send_callback_t send, const void *data, size_t length){ return _getVariableHandler->Receive(send, data, length);}, "g", 1); 19 | 20 | //Read 21 | RegisterReceiveCallBack([](communication_send_callback_t send, const void *data, size_t length) 22 | { 23 | const size_t minDataSize = sizeof(void *) + sizeof(size_t); 24 | if(length < minDataSize) 25 | return static_cast(0); 26 | 27 | const void *readData = *reinterpret_cast(data); 28 | const size_t readDataLength = *reinterpret_cast(reinterpret_cast(data) + sizeof(void *)); 29 | send(readData, readDataLength); 30 | 31 | return minDataSize; 32 | }, "r", 1); 33 | 34 | //Write 35 | RegisterReceiveCallBack([writeCallback](communication_send_callback_t send, const void *data, size_t length) 36 | { 37 | const char ack[1] = {6}; 38 | const char nack[1] = {21}; 39 | 40 | const size_t minDataSize = sizeof(void *) + sizeof(size_t); 41 | if(length < minDataSize) 42 | return static_cast(0); 43 | 44 | void *writeDataDest = *reinterpret_cast(data); 45 | const size_t writeDataLength = *reinterpret_cast(reinterpret_cast(data) + sizeof(void *)); 46 | const void *writeData = reinterpret_cast(reinterpret_cast(data) + sizeof(void *) + sizeof(size_t)); 47 | 48 | if(length < minDataSize + writeDataLength) 49 | return static_cast(0); 50 | 51 | if(writeCallback(writeDataDest, writeData, writeDataLength)) 52 | send(ack, sizeof(ack)); 53 | else 54 | send(nack, sizeof(nack)); 55 | 56 | return minDataSize + writeDataLength; 57 | }, "w", 1); 58 | 59 | //quit 60 | RegisterReceiveCallBack([quitCallback](communication_send_callback_t send, const void *data, size_t length) 61 | { 62 | const char ack[1] = {6}; 63 | const char nack[1] = {21}; 64 | 65 | if(quitCallback()) 66 | send(ack, sizeof(ack)); 67 | else 68 | send(nack, sizeof(nack)); 69 | 70 | return static_cast(0); 71 | }, "q", 1, false); 72 | 73 | //start 74 | RegisterReceiveCallBack([startCallback](communication_send_callback_t send, const void *data, size_t length) 75 | { 76 | const char ack[1] = {6}; 77 | const char nack[1] = {21}; 78 | 79 | if(startCallback()) 80 | send(ack, sizeof(ack)); 81 | else 82 | send(nack, sizeof(nack)); 83 | 84 | return static_cast(0); 85 | }, "s", 1, false); 86 | 87 | //metadata read 64 bytes 88 | RegisterReceiveCallBack([config](communication_send_callback_t send, const void *data, size_t length) 89 | { 90 | if(length < sizeof(uint32_t))//make sure there are enough bytes to process a request 91 | return static_cast(0); 92 | const uint8_t offset = *reinterpret_cast(data); //grab offset from data 93 | 94 | if(config == 0) 95 | { 96 | const uint32_t length = 0; 97 | send(reinterpret_cast(&length), 4); 98 | } 99 | else 100 | { 101 | const void *metadata = reinterpret_cast(config) + (*reinterpret_cast(config) + 2 * sizeof(uint32_t)); 102 | send(reinterpret_cast(metadata) + offset * 64, 64); 103 | } 104 | 105 | return static_cast(sizeof(uint32_t));//return number of bytes handled 106 | }, "m", 1); 107 | 108 | //config location 109 | RegisterReceiveCallBack([config](communication_send_callback_t send, const void *data, size_t length) 110 | { 111 | size_t configLocation[1] = { reinterpret_cast(config) }; 112 | send(configLocation, sizeof(configLocation)); 113 | 114 | return static_cast(0); 115 | }, "c", 1, false); 116 | } 117 | 118 | CommunicationHandler_EFIGenie::~CommunicationHandler_EFIGenie() { 119 | delete _getVariableHandler; 120 | } 121 | 122 | } 123 | #endif -------------------------------------------------------------------------------- /test/src/CylinderAirTemperatureService_IAT_ECT_BiasTests.cpp: -------------------------------------------------------------------------------- 1 | // #include "gmock/gmock.h" 2 | // #include "gtest/gtest.h" 3 | // #include "EngineControlServices/CylinderAirTemperatureService/CylinderAirTemperatureService_IAT_ECT_Bias.h" 4 | // #include "MockTimerService.h" 5 | // #include "MockFloatInputService.h" 6 | // #include "MockCylinderAirTemperatureService.h" 7 | // #include "MockCylinderAirMassService.h" 8 | // #include "EngineControlServices/RpmService/RpmService.h" 9 | // #include "Service/EngineControlServicesServiceBuilderRegister.h" 10 | // #include "Service/IOServicesServiceBuilderRegister.h" 11 | // #include "Service/HardwareAbstractionServiceBuilder.h" 12 | // using ::testing::AtLeast; 13 | // using ::testing::Return; 14 | 15 | // using namespace Reluctor; 16 | // using namespace HardwareAbstraction; 17 | // using namespace IOServices; 18 | // using namespace Service; 19 | 20 | // namespace UnitTests 21 | // { 22 | // class CylinderAirTemperatureService_IAT_ECT_BiasTests : public ::testing::Test 23 | // { 24 | // protected: 25 | // ICylinderAirTemperatureService *_cylinderAirTemperatureService; 26 | // ServiceLocator *_serviceLocator; 27 | // MockFloatInputService _intakeAirTemperatureService; 28 | // MockFloatInputService _engineCoolantTemperatureService; 29 | // MockCylinderAirmassService _cylinderAirmassService; 30 | // RpmService *_rpmService; 31 | // CallBackGroup *_tickCallBackGroup; 32 | 33 | // CylinderAirTemperatureService_IAT_ECT_BiasTests() 34 | // { 35 | // _serviceLocator = new ServiceLocator(); 36 | 37 | // _serviceLocator->Register(BUILDER_IFLOATINPUTSERVICE, INSTANCE_INTAKE_AIR_TEMPERATURE, &_intakeAirTemperatureService); 38 | // _serviceLocator->Register(BUILDER_IFLOATINPUTSERVICE, INSTANCE_ENGINE_COOLANT_TEMPERATURE, &_engineCoolantTemperatureService); 39 | // _serviceLocator->Register(BUILDER_ICYLINDERAIRMASSSERVICE, 0, &_cylinderAirmassService); 40 | // _rpmService = new RpmService(0, 0); 41 | // _serviceLocator->Register(RPMSERVICE, _rpmService); 42 | 43 | // _tickCallBackGroup = new CallBackGroup(); 44 | // _serviceLocator->Register(TICK_CALL_BACK_GROUP, (void *)_tickCallBackGroup); 45 | 46 | // CylinderAirTemperatureService_IAT_ECT_BiasConfig *airTemperatureConfig = reinterpret_cast(malloc(sizeof(CylinderAirTemperatureService_IAT_ECT_BiasConfig))); 47 | 48 | // float biasTable[16] = {0.39990234375f, 0.255859375f, 0.2216796875f, 0.2060546875f, 0.1953125f, 0.18701171875f, 0.1796875f, 0.17236328125f, 0.166015625f, 0.1591796875f, 0.15283203125f, 0.146484375f, 0.140625f, 0.134765625f, 0.12890625f, 0.123046875}; 49 | 50 | // airTemperatureConfig->Cylinders = 8; 51 | // airTemperatureConfig->DefaultTemperatureBias = 0.5; 52 | // airTemperatureConfig->MaxTemperatureBiasAirflow = 150; 53 | // airTemperatureConfig->TemperatureBiasResolution = 16; 54 | 55 | // void *config = malloc(airTemperatureConfig->Size()+1); 56 | // void *buildConfig = config; 57 | 58 | // *((uint8_t *)buildConfig) = 1; 59 | // buildConfig = (void *)(((uint8_t *)buildConfig) + 1); 60 | 61 | // memcpy(buildConfig, airTemperatureConfig, sizeof(CylinderAirTemperatureService_IAT_ECT_BiasConfig)); 62 | // buildConfig = (void *)((unsigned char *)buildConfig + sizeof(CylinderAirTemperatureService_IAT_ECT_BiasConfig)); 63 | 64 | // memcpy(buildConfig, biasTable, sizeof(biasTable)); 65 | // buildConfig = (void*)((unsigned char *)buildConfig + sizeof(biasTable)); 66 | 67 | // _cylinderAirmassService.CylinderAirmass = reinterpret_cast(calloc(sizeof(float) * airTemperatureConfig->Cylinders, sizeof(float) * airTemperatureConfig->Cylinders)); 68 | 69 | // unsigned int size = 0; 70 | // _cylinderAirTemperatureService = reinterpret_cast(ICylinderAirTemperatureService::CreateCylinderAirTemperatureService(_serviceLocator, config, size)); 71 | // } 72 | 73 | // ~CylinderAirTemperatureService_IAT_ECT_BiasTests() override 74 | // { 75 | // free(_cylinderAirTemperatureService); 76 | // free(_rpmService); 77 | // free(_tickCallBackGroup); 78 | // free(_serviceLocator); 79 | // } 80 | // }; 81 | 82 | // TEST_F(CylinderAirTemperatureService_IAT_ECT_BiasTests, WhenGettingCylinderAirTemperatureThenCorrectCylinderAirTemperatureIsReturned) 83 | // { 84 | // _rpmService->Rpm = 2000; 85 | // _intakeAirTemperatureService.Value = 20; 86 | // _engineCoolantTemperatureService.Value = 100; 87 | 88 | // _cylinderAirTemperatureService->CalculateCylinderAirTemperature(); 89 | // ASSERT_NEAR(60.0f, _cylinderAirTemperatureService->CylinderAirTemperature[0], 0.001f); 90 | 91 | // _cylinderAirmassService.CylinderAirmass[0] = 0.4f; 92 | // _cylinderAirTemperatureService->CalculateCylinderAirTemperature(); 93 | // ASSERT_NEAR(34.765625f, _cylinderAirTemperatureService->CylinderAirTemperature[0], 0.001f); 94 | // } 95 | // } -------------------------------------------------------------------------------- /test/src/InjectorTimingServiceTests.cpp: -------------------------------------------------------------------------------- 1 | // #include "gmock/gmock.h" 2 | // #include "gtest/gtest.h" 3 | // #include "EngineControlServices/InjectorTimingService/InjectorTimingService.h" 4 | // #include "MockTimerService.h" 5 | // #include "MockFloatInputService.h" 6 | // #include "MockInjectorGramService.h" 7 | // #include "EngineControlServices/RpmService/RpmService.h" 8 | // #include "Service/EngineControlServicesServiceBuilderRegister.h" 9 | // #include "Service/IOServicesServiceBuilderRegister.h" 10 | // #include "Service/HardwareAbstractionServiceBuilder.h" 11 | // using ::testing::AtLeast; 12 | // using ::testing::Return; 13 | 14 | // using namespace Reluctor; 15 | // using namespace HardwareAbstraction; 16 | // using namespace IOServices; 17 | // using namespace Service; 18 | 19 | // namespace UnitTests 20 | // { 21 | // class InjectorTimingServiceTests : public ::testing::Test 22 | // { 23 | // protected: 24 | // IInjectorTimingService *_injectorTimingService; 25 | // ServiceLocator *_serviceLocator; 26 | // MockInjectorGramService _injectorGramService; 27 | // MockFloatInputService _mapService; 28 | // MockFloatInputService _voltageService; 29 | // CallBackGroup *_tickCallBackGroup; 30 | 31 | // InjectorTimingServiceTests() 32 | // { 33 | // _serviceLocator = new ServiceLocator(); 34 | 35 | // _serviceLocator->Register(BUILDER_IFLOATINPUTSERVICE, INSTANCE_MANIFOLD_ABSOLUTE_PRESSURE, &_mapService); 36 | // _serviceLocator->Register(BUILDER_IFLOATINPUTSERVICE, INSTANCE_VOLTAGE, &_voltageService); 37 | // _serviceLocator->Register(BUILDER_IINJECTORGRAMSERVICE, 0, &_injectorGramService); 38 | 39 | // _tickCallBackGroup = new CallBackGroup(); 40 | // _serviceLocator->Register(TICK_CALL_BACK_GROUP, (void *)_tickCallBackGroup); 41 | 42 | // uint16_t injectorGramsPerMinute[8] = { 800, 800, 800, 800, 800, 800, 800, 800 }; 43 | 44 | // int16_t shortPulseAdder[29] = {0, 1800, 1700, 1500, 1400, 1200, 1100, 900, 800, 800, 600, 500, 500, 500, 500, 500, 300, 200, 200, 200, 200, 200, 300, 300, 300, 200, 200, 200, 0}; 45 | 46 | // int16_t offset[9 * 9] = { 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 47 | // 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 48 | // 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 49 | // 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 50 | // 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 51 | // 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 52 | // 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 53 | // 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 54 | // 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000 }; 55 | 56 | // InjectorTimingServiceConfig *injectorTimingConfig = reinterpret_cast(malloc(sizeof(InjectorTimingServiceConfig))); 57 | 58 | // injectorTimingConfig->Injectors = 8; 59 | // injectorTimingConfig->VoltageMax = 16; 60 | // injectorTimingConfig->VoltageMin = 8; 61 | // injectorTimingConfig->MapResolution = 9; 62 | // injectorTimingConfig->MapMax = 1; 63 | // injectorTimingConfig->VoltageResolution = 9; 64 | // injectorTimingConfig->ShortPulseAdderResolution = 29; 65 | // injectorTimingConfig->ShortPulseLimit = 0.0017f; 66 | // injectorTimingConfig->InjectorOpenPosition = 500; 67 | 68 | // void *config = malloc(injectorTimingConfig->Size()+1); 69 | // void *buildConfig = config; 70 | 71 | // *((uint8_t *)buildConfig) = 1; 72 | // buildConfig = (void *)(((uint8_t *)buildConfig) + 1); 73 | 74 | // memcpy(buildConfig, injectorTimingConfig, sizeof(InjectorTimingServiceConfig)); 75 | // buildConfig = (void *)((unsigned char *)buildConfig + sizeof(InjectorTimingServiceConfig)); 76 | 77 | // memcpy(buildConfig, injectorGramsPerMinute, sizeof(injectorGramsPerMinute)); 78 | // buildConfig = (void *)((unsigned char *)buildConfig + sizeof(injectorGramsPerMinute)); 79 | 80 | // memcpy(buildConfig, shortPulseAdder, sizeof(shortPulseAdder)); 81 | // buildConfig = (void *)((unsigned char *)buildConfig + sizeof(shortPulseAdder)); 82 | 83 | // memcpy(buildConfig, offset, sizeof(offset)); 84 | // buildConfig = (void *)((unsigned char *)buildConfig + sizeof(offset)); 85 | 86 | // _injectorGramService.InjectorGrams = reinterpret_cast(calloc(sizeof(float) * injectorTimingConfig->Injectors, sizeof(float) * injectorTimingConfig->Injectors)); 87 | 88 | // unsigned int size = 0; 89 | // _injectorTimingService = reinterpret_cast(IInjectorTimingService::CreateInjectorTimingService(_serviceLocator, config, size)); 90 | // } 91 | 92 | // ~InjectorTimingServiceTests() override 93 | // { 94 | // free(_injectorTimingService); 95 | // free(_tickCallBackGroup); 96 | // free(_serviceLocator); 97 | // } 98 | // }; 99 | 100 | // TEST_F(InjectorTimingServiceTests, WhenGettingInjectorTimingThenCorrectInjectorTimingIsReturned) 101 | // { 102 | // _injectorGramService.InjectorGrams[0] = 0.08; 103 | // _injectorTimingService->CalculateInjectorTiming(); 104 | // ASSERT_NEAR(0.007f, _injectorTimingService->InjectorTiming[0].PulseWidth, 0.0001f); 105 | // ASSERT_EQ(500, _injectorTimingService->InjectorTiming[0].OpenPosition); 106 | // } 107 | // } -------------------------------------------------------------------------------- /test/src/IgnitionSchedulingServiceTests.cpp: -------------------------------------------------------------------------------- 1 | // #include "gmock/gmock.h" 2 | // #include "gtest/gtest.h" 3 | // #include "Service/EngineControlServicesServiceBuilderRegister.h" 4 | // #include "MockBooleanOutputService.h" 5 | // #include "MockReluctor.h" 6 | // #include "MockTimerService.h" 7 | // #include "MockIgnitionConfig.h" 8 | // using ::testing::AtLeast; 9 | // using ::testing::Return; 10 | 11 | // using namespace HardwareAbstraction; 12 | // using namespace EngineControlServices; 13 | // using namespace IOServices; 14 | // using namespace Reluctor; 15 | 16 | // namespace UnitTests 17 | // { 18 | // class IgnitionSchedulingServiceTests : public ::testing::Test 19 | // { 20 | // protected: 21 | // IgnitionSchedulingService *_ignitionSchedulingService; 22 | // MockBooleanOutputService _ignitorOutputService0; 23 | // MockBooleanOutputService _ignitorOutputService1; 24 | // MockBooleanOutputService _ignitorOutputService2; 25 | // MockBooleanOutputService _ignitorOutputService3; 26 | // MockReluctor _camReluctor; 27 | // MockReluctor _crankReluctor; 28 | // MockTimerService _timerService; 29 | // MockIgnitionConfig _ignitionConfig; 30 | 31 | // IgnitionSchedulingServiceTests() 32 | // { 33 | // EXPECT_CALL(_timerService, GetTick()).WillOnce(Return(0)); 34 | // IgnitionSchedulingServiceConfig *ignitionSchedulingConfig = (IgnitionSchedulingServiceConfig *)malloc(sizeof(IgnitionSchedulingServiceConfig) + 8); 35 | 36 | // ignitionSchedulingConfig->SequentialRequired = false; 37 | // ignitionSchedulingConfig->Ignitors = 4; 38 | // unsigned short *IgnitorTdc = (unsigned short *)(ignitionSchedulingConfig + 1); 39 | // IgnitorTdc[0] = 0; 40 | // IgnitorTdc[1] = 180 * 64; 41 | // IgnitorTdc[2] = 360 * 64; 42 | // IgnitorTdc[3] = 540 * 64; 43 | 44 | // IBooleanOutputService **ignitorOutputServices = (IBooleanOutputService **)malloc(sizeof(IBooleanOutputService **) * 5); 45 | // ignitorOutputServices[0] = (IBooleanOutputService *)(&_ignitorOutputService0); 46 | // ignitorOutputServices[1] = (IBooleanOutputService *)(&_ignitorOutputService1); 47 | // ignitorOutputServices[2] = (IBooleanOutputService *)(&_ignitorOutputService2); 48 | // ignitorOutputServices[3] = (IBooleanOutputService *)(&_ignitorOutputService3); 49 | // ignitorOutputServices[4] = 0; 50 | 51 | // unsigned int size = 0; 52 | // _ignitionSchedulingService = new IgnitionSchedulingService(ignitionSchedulingConfig, &_ignitionConfig, ignitorOutputServices, &_timerService, &_crankReluctor, &_camReluctor); 53 | // } 54 | // }; 55 | 56 | // TEST_F(IgnitionSchedulingServiceTests, IgnitionSchedulingServiceTests_WhenSchedulingIgnition) 57 | // { 58 | // EXPECT_CALL(_camReluctor, IsSynced()).WillRepeatedly(Return(true)); 59 | // EXPECT_CALL(_crankReluctor, IsSynced()).WillRepeatedly(Return(true)); 60 | // EXPECT_CALL(_camReluctor, GetResolution()).WillRepeatedly(Return(2)); 61 | // EXPECT_CALL(_crankReluctor, GetResolution()).WillRepeatedly(Return(24)); 62 | // EXPECT_CALL(_crankReluctor, GetTickPerDegree()).WillRepeatedly(Return(1)); 63 | // EXPECT_CALL(_timerService, GetTicksPerSecond()).WillRepeatedly(Return(5000)); 64 | 65 | // EXPECT_CALL(_crankReluctor, GetPosition()).WillOnce(Return(15)); 66 | // EXPECT_CALL(_camReluctor, GetPosition()).WillOnce(Return(7.5)); 67 | // EXPECT_CALL(_timerService, GetTick()).WillRepeatedly(Return(0)); 68 | // IgnitionTiming ignitionTiming = IgnitionTiming(); 69 | // ignitionTiming.IgnitionAdvance64thDegree = 15 * 64; 70 | // ignitionTiming.IgnitionDwellTime = 0.004; 71 | // ignitionTiming.IgnitionEnable = true; 72 | // EXPECT_CALL(_ignitionConfig, GetIgnitionTiming()).WillOnce(Return(ignitionTiming)); 73 | // EXPECT_CALL(_timerService, ScheduleCallBack(0x2B2)); 74 | // EXPECT_CALL(_timerService, ScheduleCallBack(0x29e)); 75 | // EXPECT_CALL(_timerService, ScheduleCallBack(0x96)); 76 | // EXPECT_CALL(_timerService, ScheduleCallBack(0x82)).Times(5); 77 | // _ignitionSchedulingService->ScheduleEvents(); 78 | // EXPECT_CALL(_timerService, ScheduleCallBack(0x96)); 79 | // EXPECT_CALL(_timerService, GetTick()).Times(3).WillRepeatedly(Return(0x82)); 80 | // EXPECT_CALL(_ignitorOutputService1, OutputSet()).Times(1); 81 | // _timerService.ReturnCallBack(); 82 | // EXPECT_CALL(_timerService, ScheduleCallBack(0x136)); 83 | // EXPECT_CALL(_timerService, GetTick()).Times(3).WillRepeatedly(Return(0x96)); 84 | // EXPECT_CALL(_ignitorOutputService1, OutputReset()).Times(1); 85 | // _timerService.ReturnCallBack(); 86 | // EXPECT_CALL(_timerService, ScheduleCallBack(0x14a)); 87 | // EXPECT_CALL(_timerService, GetTick()).Times(3).WillRepeatedly(Return(0x136)); 88 | // EXPECT_CALL(_ignitorOutputService2, OutputSet()).Times(1); 89 | // _timerService.ReturnCallBack(); 90 | // EXPECT_CALL(_timerService, ScheduleCallBack(0x1ea)); 91 | // EXPECT_CALL(_timerService, GetTick()).Times(3).WillRepeatedly(Return(0x14a)); 92 | // EXPECT_CALL(_ignitorOutputService2, OutputReset()).Times(1); 93 | // _timerService.ReturnCallBack(); 94 | // EXPECT_CALL(_timerService, ScheduleCallBack(0x1fe)); 95 | // EXPECT_CALL(_timerService, GetTick()).Times(3).WillRepeatedly(Return(0x1ea)); 96 | // EXPECT_CALL(_ignitorOutputService3, OutputSet()).Times(1); 97 | // _timerService.ReturnCallBack(); 98 | // EXPECT_CALL(_timerService, ScheduleCallBack(0x29e)); 99 | // EXPECT_CALL(_timerService, GetTick()).Times(3).WillRepeatedly(Return(0x1fe)); 100 | // EXPECT_CALL(_ignitorOutputService3, OutputReset()).Times(1); 101 | // _timerService.ReturnCallBack(); 102 | // EXPECT_CALL(_timerService, ScheduleCallBack(0x2B2)); 103 | // EXPECT_CALL(_timerService, GetTick()).Times(3).WillRepeatedly(Return(0x29e)); 104 | // EXPECT_CALL(_ignitorOutputService0, OutputSet()).Times(1); 105 | // _timerService.ReturnCallBack(); 106 | // EXPECT_CALL(_timerService, GetTick()).Times(2).WillRepeatedly(Return(0x2B2)); 107 | // EXPECT_CALL(_ignitorOutputService0, OutputReset()).Times(1); 108 | // _timerService.ReturnCallBack(); 109 | 110 | // //TODO: More comprehensive testing 111 | // } 112 | // } -------------------------------------------------------------------------------- /src/Operations/Operation_EngineScheduleInjection.cpp: -------------------------------------------------------------------------------- 1 | #include "Operations/Operation_EngineScheduleInjection.h" 2 | #include "Config.h" 3 | 4 | using namespace EmbeddedIOServices; 5 | using namespace OperationArchitecture; 6 | using namespace EmbeddedIOOperations; 7 | 8 | #ifdef OPERATION_ENGINESCHEDULEINJECTION_H 9 | namespace EFIGenie 10 | { 11 | Operation_EngineScheduleInjection::Operation_EngineScheduleInjection(ITimerService * const timerService, const float tdc, const Operation_EngineScheduleInjection_InjectAt injectAt, const callback_t openCallBack, const callback_t closeCallBack) : 12 | _timerService(timerService), 13 | _tdc(tdc), 14 | _injectAt(injectAt), 15 | _openCallBack(openCallBack), 16 | _closeCallBack(closeCallBack), 17 | _openTask(new Task([this]() { this->Open(); })), 18 | _closeTask(new Task([this]() { this->Close(); })) 19 | { } 20 | 21 | Operation_EngineScheduleInjection::~Operation_EngineScheduleInjection() 22 | { 23 | _timerService->UnScheduleTask(_openTask); 24 | while(_open) ; 25 | _timerService->UnScheduleTask(_closeTask); 26 | delete _openTask; 27 | delete _closeTask; 28 | } 29 | 30 | std::tuple Operation_EngineScheduleInjection::Execute(EnginePosition enginePosition, bool enable, float injectionPulseWidth, float injectionPosition) 31 | { 32 | const tick_t ticksPerSecond = _timerService->GetTicksPerSecond(); 33 | const tick_t pulseTicks = static_cast(injectionPulseWidth * ticksPerSecond); 34 | 35 | if(enginePosition.Synced == false || !enable) 36 | { 37 | _timerService->UnScheduleTask(_openTask); 38 | //if we are open and have no matching close event, schedule one 39 | if(!_closeTask->Scheduled && _open) 40 | { 41 | const tick_t closeAt = _lastOpenTick + pulseTicks; 42 | _timerService->ScheduleTask(_closeTask, closeAt); 43 | _lastOpenTick = 0; 44 | 45 | return std::tuple(0, closeAt); 46 | } 47 | 48 | return std::tuple(0, 0); 49 | } 50 | 51 | const uint16_t cycleDegrees = enginePosition.Sequential? 720 : 360; 52 | const float ticksPerDegree = ticksPerSecond / enginePosition.PositionDot; 53 | const tick_t ticksPerCycle = static_cast(cycleDegrees * ticksPerDegree); 54 | 55 | float delta = _tdc - injectionPosition - enginePosition.Position; 56 | delta -= (static_cast(delta) / cycleDegrees) * cycleDegrees; 57 | if(delta < 0) 58 | delta += cycleDegrees; 59 | tick_t openAt = static_cast(ticksPerDegree * delta) + enginePosition.CalculatedTick - (ticksPerCycle << 1) - ((pulseTicks * _injectAt) / 2); 60 | 61 | //check _lastOpenTick is within range and _openTask is not scheduled 62 | if( !_openTask->Scheduled && 63 | (_lastOpenTick == 0 || 64 | ITimerService::TickLessThanTick(_lastOpenTick + pulseTicks, enginePosition.CalculatedTick - ((ticksPerCycle * 3) / 2)) || 65 | ITimerService::TickLessThanTick(enginePosition.CalculatedTick + ((ticksPerCycle * 3) / 2), _lastOpenTick))) 66 | { 67 | //if it is not within range, set it to what would be the previous open 68 | _lastOpenTick = openAt; 69 | while(ITimerService::TickLessThanTick(_lastOpenTick, _timerService->GetTick() - ticksPerCycle)) 70 | _lastOpenTick += ticksPerCycle; 71 | } 72 | 73 | // if we aren't open, schedule the open event 74 | const uint32_t lastOpenTickBeforeOpenCheck = _lastOpenTick; 75 | tick_t closeAt = 0; 76 | if(!_open) 77 | { 78 | while(ITimerService::TickLessThanTick(openAt - (ticksPerCycle / 2), lastOpenTickBeforeOpenCheck)) 79 | openAt += ticksPerCycle; 80 | closeAt = openAt + pulseTicks; 81 | 82 | //schedule open event 83 | _timerService->ScheduleTask(_openTask, openAt); 84 | } 85 | 86 | // if we are open. schedule close based on when it was opened 87 | if(_open) 88 | { 89 | //schedule close based off the last open tick 90 | while(ITimerService::TickLessThanTick(openAt + (ticksPerCycle / 2), _lastOpenTick)) 91 | openAt += ticksPerCycle; 92 | closeAt = _lastOpenTick + pulseTicks; 93 | 94 | //schedule close 95 | _timerService->ScheduleTask(_closeTask, closeAt); 96 | 97 | //schedule next open event 98 | openAt += ticksPerCycle; 99 | _timerService->ScheduleTask(_openTask, openAt); 100 | } 101 | //if we are not open, just use the previously calculated close tick. 102 | else 103 | { 104 | _timerService->ScheduleTask(_closeTask, closeAt); 105 | } 106 | 107 | //return the ticks of the open and close. for debugging purposes 108 | return std::tuple(openAt, closeAt); 109 | } 110 | 111 | void Operation_EngineScheduleInjection::Open() 112 | { 113 | _openCallBack(); 114 | if(!_open) 115 | _lastOpenTick = _openTask->ExecutedTick == 0? 1 : _openTask->ExecutedTick; 116 | _open = true; 117 | } 118 | 119 | void Operation_EngineScheduleInjection::Close() 120 | { 121 | _closeCallBack(); 122 | _open = false; 123 | } 124 | 125 | AbstractOperation *Operation_EngineScheduleInjection::Create(const void *config, size_t &sizeOut, const EmbeddedIOServiceCollection *embeddedIOServiceCollection, OperationFactory *factory) 126 | { 127 | const float tdc = Config::CastAndOffset(config, sizeOut); 128 | const Operation_EngineScheduleInjection_InjectAt injectAt = Config::CastAndOffset(config, sizeOut); 129 | callback_t openCallBack = 0; 130 | callback_t closeCallBack = 0; 131 | 132 | size_t size = 0; 133 | AbstractOperation *operation = factory->Create(config, size); 134 | Config::OffsetConfig(config, sizeOut, size); 135 | if(operation->NumberOfParameters == 1) 136 | { 137 | openCallBack = [operation]() { operation->Execute(true); }; 138 | closeCallBack = [operation]() { operation->Execute(false); }; 139 | } 140 | else 141 | { 142 | openCallBack = [operation]() { operation->Execute(); }; 143 | 144 | size = 0; 145 | AbstractOperation *operationClose = factory->Create(config, size); 146 | Config::OffsetConfig(config, sizeOut, size); 147 | closeCallBack = [operationClose]() { operationClose->Execute(); }; 148 | } 149 | 150 | return new Operation_EngineScheduleInjection(embeddedIOServiceCollection->TimerService, tdc, injectAt, openCallBack, closeCallBack); 151 | } 152 | } 153 | #endif -------------------------------------------------------------------------------- /src/Operations/Operation_EngineScheduleIgnition.cpp: -------------------------------------------------------------------------------- 1 | #include "Operations/Operation_EngineScheduleIgnition.h" 2 | #include "Config.h" 3 | 4 | using namespace EmbeddedIOServices; 5 | using namespace OperationArchitecture; 6 | using namespace EmbeddedIOOperations; 7 | 8 | #ifdef OPERATION_ENGINESCHEDULEIGNITION_H 9 | namespace EFIGenie 10 | { 11 | Operation_EngineScheduleIgnition::Operation_EngineScheduleIgnition(ITimerService *timerService, const float tdc, callback_t dwellCallBack, callback_t igniteCallBack) : 12 | _timerService(timerService), 13 | _tdc(tdc), 14 | _dwellCallBack(dwellCallBack), 15 | _igniteCallBack(igniteCallBack), 16 | _dwellTask(new Task([this]() { this->Dwell(); })), 17 | _igniteTask(new Task([this]() { this->Ignite(); })) 18 | { } 19 | 20 | Operation_EngineScheduleIgnition::~Operation_EngineScheduleIgnition() 21 | { 22 | _timerService->UnScheduleTask(_dwellTask); 23 | while(_dwelling) ; 24 | _timerService->UnScheduleTask(_igniteTask); 25 | delete _dwellTask; 26 | delete _igniteTask; 27 | } 28 | 29 | std::tuple Operation_EngineScheduleIgnition::Execute(EnginePosition enginePosition, bool enable, float ignitionDwell, float ignitionAdvance, float ignitionDwellMaxDeviation) 30 | { 31 | const tick_t ticksPerSecond = _timerService->GetTicksPerSecond(); 32 | const tick_t dwellTicks = static_cast(ignitionDwell * ticksPerSecond); 33 | 34 | if(enginePosition.Synced == false || !enable) 35 | { 36 | _timerService->UnScheduleTask(_dwellTask); 37 | //if we are open and have no matching close event, schedule one 38 | if(!_igniteTask->Scheduled && _dwelling) 39 | { 40 | const tick_t igniteAt = _lastDwellTick + dwellTicks; 41 | _timerService->ScheduleTask(_igniteTask, igniteAt); 42 | _lastDwellTick = 0; 43 | 44 | return std::tuple(0, igniteAt); 45 | } 46 | 47 | return std::tuple(0, 0); 48 | } 49 | 50 | //if sequntial is changing then treat as a brand new dwell with no prior history 51 | if(_previousSequential != enginePosition.Sequential) 52 | { 53 | _timerService->UnScheduleTask(_dwellTask); 54 | _lastDwellTick = 0; 55 | _previousSequential = enginePosition.Sequential; 56 | } 57 | 58 | const uint16_t cycleDegrees = enginePosition.Sequential? 720 : 360; 59 | const float ticksPerDegree = ticksPerSecond / enginePosition.PositionDot; 60 | const tick_t ticksPerCycle = static_cast(cycleDegrees * ticksPerDegree); 61 | const tick_t ticksPerCycleHalf = ticksPerCycle / 2; 62 | const tick_t maxDwellDeviationTicks = static_cast(ignitionDwellMaxDeviation * ticksPerSecond); 63 | 64 | float delta = _tdc - ignitionAdvance - enginePosition.Position; 65 | delta -= (static_cast(delta) / cycleDegrees) * cycleDegrees; 66 | if(delta < 0) 67 | delta += cycleDegrees; 68 | tick_t igniteAt = static_cast(ticksPerDegree * delta) + enginePosition.CalculatedTick - (ticksPerCycle << 1); 69 | tick_t dwellAt = igniteAt - dwellTicks; 70 | 71 | //check _lastDwellTick is within range and _dwellTask is not scheduled 72 | if( !_dwellTask->Scheduled && 73 | (_lastDwellTick == 0 || 74 | ITimerService::TickLessThanTick(_lastDwellTick + dwellTicks, enginePosition.CalculatedTick - (ticksPerCycleHalf * 3)) || 75 | ITimerService::TickLessThanTick(enginePosition.CalculatedTick + (ticksPerCycleHalf * 3), _lastDwellTick))) 76 | { 77 | //if it is not within range, set it to what would be the previous dwell 78 | _lastDwellTick = dwellAt; 79 | while(ITimerService::TickLessThanEqualToTick(_lastDwellTick, _timerService->GetTick() - ticksPerCycle)) 80 | _lastDwellTick += ticksPerCycle; 81 | } 82 | 83 | // if we aren't dwelling, schedule dwell 84 | const uint32_t lastDwellTickBeforeDwellingCheck = _lastDwellTick; 85 | if(!_dwelling) 86 | { 87 | while(ITimerService::TickLessThanTick(dwellAt - ticksPerCycleHalf, lastDwellTickBeforeDwellingCheck)) 88 | dwellAt += ticksPerCycle; 89 | igniteAt = dwellAt + dwellTicks; 90 | 91 | //schedule dwell 92 | _timerService->ScheduleTask(_dwellTask, dwellAt); 93 | } 94 | 95 | // if we are dwelling. schedule ignition and next dwell 96 | if(_dwelling) 97 | { 98 | //schedule ignition based off the last dwell tick 99 | while(ITimerService::TickLessThanTick(dwellAt + ticksPerCycleHalf, _lastDwellTick)) 100 | dwellAt += ticksPerCycle; 101 | igniteAt = dwellAt + dwellTicks; 102 | 103 | const tick_t minIgniteAt = _dwellTask->ExecutedTick + dwellTicks - maxDwellDeviationTicks; 104 | const tick_t maxIgniteAt = _dwellTask->ExecutedTick + dwellTicks + maxDwellDeviationTicks; 105 | if(ITimerService::TickLessThanTick(igniteAt, minIgniteAt)) 106 | igniteAt = minIgniteAt; 107 | else if(ITimerService::TickLessThanTick(maxIgniteAt, igniteAt)) 108 | igniteAt = maxIgniteAt; 109 | 110 | //schedule ignition 111 | _timerService->ScheduleTask(_igniteTask, igniteAt); 112 | 113 | //schedule next dwell 114 | dwellAt += ticksPerCycle; 115 | _timerService->ScheduleTask(_dwellTask, dwellAt); 116 | } 117 | //if we are not dwelling, just use the previously calculated ignition tick. 118 | else 119 | { 120 | _timerService->ScheduleTask(_igniteTask, igniteAt); 121 | } 122 | 123 | //return the ticks of the dwell and ignition. for debugging purposes 124 | return std::tuple(dwellAt, igniteAt); 125 | } 126 | 127 | void Operation_EngineScheduleIgnition::Dwell() 128 | { 129 | _dwellCallBack(); 130 | if(!_dwelling) 131 | _lastDwellTick = _dwellTask->ExecutedTick == 0? 1 : _dwellTask->ExecutedTick; 132 | _dwelling = true; 133 | } 134 | 135 | void Operation_EngineScheduleIgnition::Ignite() 136 | { 137 | _igniteCallBack(); 138 | _dwelling = false; 139 | } 140 | 141 | AbstractOperation *Operation_EngineScheduleIgnition::Create(const void *config, size_t &sizeOut, const EmbeddedIOServiceCollection *embeddedIOServiceCollection, OperationFactory *factory) 142 | { 143 | const float tdc = Config::CastAndOffset(config, sizeOut); 144 | callback_t dwellCallBack = 0; 145 | callback_t igniteCallBack = 0; 146 | 147 | size_t size = 0; 148 | AbstractOperation *operation = factory->Create(config, size); 149 | Config::OffsetConfig(config, sizeOut, size); 150 | if(operation->NumberOfParameters == 1) 151 | { 152 | dwellCallBack = [operation]() { operation->Execute(true); }; 153 | igniteCallBack = [operation]() { operation->Execute(false); }; 154 | } 155 | else 156 | { 157 | dwellCallBack = [operation]() { operation->Execute(); }; 158 | 159 | size = 0; 160 | AbstractOperation *operationIgnite = factory->Create(config, size); 161 | Config::OffsetConfig(config, sizeOut, size); 162 | igniteCallBack = [operationIgnite]() { operationIgnite->Execute(); }; 163 | } 164 | 165 | return new Operation_EngineScheduleIgnition(embeddedIOServiceCollection->TimerService, tdc, dwellCallBack, igniteCallBack); 166 | } 167 | } 168 | #endif -------------------------------------------------------------------------------- /test/src/FuelPumpServiceTests.cpp: -------------------------------------------------------------------------------- 1 | // #include "gmock/gmock.h" 2 | // #include "gtest/gtest.h" 3 | // #include "IOServices/BooleanOutputService/BooleanOutputService.h" 4 | // #include "Service/EngineControlServicesServiceBuilderRegister.h" 5 | // #include "Service/HardwareAbstractionServiceBuilder.h" 6 | // #include "EngineControlServices/FuelPumpService/FuelPumpService.h" 7 | // #include "MockDigitalService.h" 8 | // #include "MockTimerService.h" 9 | // using ::testing::AtLeast; 10 | // using ::testing::Return; 11 | 12 | // using namespace HardwareAbstraction; 13 | // using namespace IOServices; 14 | // using namespace Service; 15 | // using namespace EngineControlServices; 16 | 17 | // namespace UnitTests 18 | // { 19 | // class FuelPumpServiceTests : public ::testing::Test 20 | // { 21 | // protected: 22 | // MockDigitalService _digitalService; 23 | // MockTimerService _timerService; 24 | // IFuelPumpService * _fuelPumpService; 25 | // HardwareAbstractionCollection _hardwareAbstractionCollection; 26 | // ServiceLocator *_serviceLocator; 27 | // CallBackGroup *_tickCallBackGroup; 28 | // CallBackGroup *_preReluctorCallBackGroup; 29 | // CallBackGroup *_postReluctorCallBackGroup; 30 | 31 | // FuelPumpServiceTests() 32 | // { 33 | // _serviceLocator = new ServiceLocator(); 34 | // _serviceLocator->Register(DIGITAL_SERVICE_ID, &_digitalService); 35 | // _serviceLocator->Register(TIMER_SERVICE_ID, &_timerService); 36 | // _tickCallBackGroup = new CallBackGroup(); 37 | // _serviceLocator->Register(TICK_CALL_BACK_GROUP, (void *)_tickCallBackGroup); 38 | // _preReluctorCallBackGroup = new CallBackGroup(); 39 | // _serviceLocator->Register(PRE_RELUCTOR_SYNC_CALL_BACK_GROUP, (void *)_preReluctorCallBackGroup); 40 | // _postReluctorCallBackGroup = new CallBackGroup(); 41 | // _serviceLocator->Register(POST_RELUCTOR_SYNC_CALL_BACK_GROUP, (void *)_postReluctorCallBackGroup); 42 | 43 | // _hardwareAbstractionCollection.TimerService = &_timerService; 44 | // _hardwareAbstractionCollection.DigitalService = &_digitalService; 45 | 46 | // _serviceLocator->Register(HARDWARE_ABSTRACTION_COLLECTION_ID, &_hardwareAbstractionCollection); 47 | // } 48 | 49 | // void SetUp(bool normalOn, bool highZ) 50 | // { 51 | // _tickCallBackGroup->Clear(); 52 | // _preReluctorCallBackGroup->Clear(); 53 | // _postReluctorCallBackGroup->Clear(); 54 | // FuelPumpServiceConfig *fuelPumpServiceConfig = (FuelPumpServiceConfig *)malloc(sizeof(FuelPumpServiceConfig)); 55 | // fuelPumpServiceConfig->PrimeTime = 1; 56 | 57 | // BooleanOutputServiceConfig *booleanOutputConfig = (BooleanOutputServiceConfig *)malloc(sizeof(BooleanOutputServiceConfig)); 58 | // booleanOutputConfig->Pin = 1; 59 | // booleanOutputConfig->NormalOn = normalOn; 60 | // booleanOutputConfig->HighZ = highZ; 61 | 62 | // void *config = malloc(fuelPumpServiceConfig->Size() + booleanOutputConfig->Size() + 2); 63 | // void *buildConfig = config; 64 | 65 | // //fuel pump service id 66 | // *((unsigned char *)buildConfig) = 1; 67 | // buildConfig = (void *)(((unsigned char *)buildConfig) + 1); 68 | // memcpy(buildConfig, fuelPumpServiceConfig, fuelPumpServiceConfig->Size()); 69 | // buildConfig = (void *)(((unsigned char *)buildConfig) + fuelPumpServiceConfig->Size()); 70 | 71 | // //boolean output service id 72 | // *((unsigned char *)buildConfig) = 1; 73 | // buildConfig = (void *)(((unsigned char *)buildConfig) + 1); 74 | // memcpy(buildConfig, booleanOutputConfig, booleanOutputConfig->Size()); 75 | 76 | // unsigned int size = 0; 77 | // _fuelPumpService = reinterpret_cast(IFuelPumpService::CreateFuelPumpService(_serviceLocator, config, size)); 78 | // } 79 | // }; 80 | 81 | // TEST_F(FuelPumpServiceTests, WhenUsingFuelPumpThenFuelPumpIsUsed) 82 | // { 83 | // EXPECT_CALL(_digitalService, InitPin(1, HardwareAbstraction::Out)).Times(1); 84 | // EXPECT_CALL(_digitalService, WritePin(1, false)).Times(1); 85 | // SetUp(false, false); 86 | 87 | // EXPECT_CALL(_timerService, GetTicksPerSecond()).WillRepeatedly(Return(5000)); 88 | // EXPECT_CALL(_timerService, GetTick()).Times(2).WillRepeatedly(Return(0)); 89 | // EXPECT_CALL(_timerService, ScheduleCallBack(5000)).Times(1); 90 | // EXPECT_CALL(_digitalService, WritePin(1, true)).Times(1); 91 | // _preReluctorCallBackGroup->Execute(); 92 | 93 | // EXPECT_CALL(_digitalService, WritePin(1, false)).Times(1); 94 | // EXPECT_CALL(_timerService, GetTick()).Times(2).WillRepeatedly(Return(5000)); 95 | // _timerService.ReturnCallBack(); 96 | 97 | // EXPECT_CALL(_digitalService, WritePin(1, true)).Times(1); 98 | // _postReluctorCallBackGroup->Execute(); 99 | 100 | // EXPECT_CALL(_digitalService, WritePin(1, false)).Times(1); 101 | // _fuelPumpService->Off(); 102 | 103 | // EXPECT_CALL(_digitalService, InitPin(1, HardwareAbstraction::Out)).Times(1); 104 | // EXPECT_CALL(_digitalService, WritePin(1, true)).Times(1); 105 | // SetUp(true, false); 106 | 107 | // EXPECT_CALL(_timerService, GetTick()).Times(2).WillRepeatedly(Return(0)); 108 | // EXPECT_CALL(_timerService, ScheduleCallBack(5000)).Times(1); 109 | // EXPECT_CALL(_digitalService, WritePin(1, false)).Times(1); 110 | // _preReluctorCallBackGroup->Execute(); 111 | 112 | // EXPECT_CALL(_digitalService, WritePin(1, true)).Times(1); 113 | // EXPECT_CALL(_timerService, GetTick()).Times(2).WillRepeatedly(Return(5000)); 114 | // _timerService.ReturnCallBack(); 115 | 116 | // EXPECT_CALL(_digitalService, WritePin(1, false)).Times(1); 117 | // _postReluctorCallBackGroup->Execute(); 118 | 119 | // EXPECT_CALL(_digitalService, WritePin(1, true)).Times(1); 120 | // _fuelPumpService->Off(); 121 | 122 | // //highz 123 | // EXPECT_CALL(_digitalService, InitPin(1, HardwareAbstraction::Out)).Times(1); 124 | // EXPECT_CALL(_digitalService, WritePin(1, false)).Times(1); 125 | // SetUp(false, true); 126 | 127 | // EXPECT_CALL(_timerService, GetTick()).Times(2).WillRepeatedly(Return(0)); 128 | // EXPECT_CALL(_timerService, ScheduleCallBack(5000)).Times(1); 129 | // EXPECT_CALL(_digitalService, InitPin(1, HardwareAbstraction::In)).Times(1); 130 | // _preReluctorCallBackGroup->Execute(); 131 | 132 | // EXPECT_CALL(_digitalService, InitPin(1, HardwareAbstraction::Out)).Times(1); 133 | // EXPECT_CALL(_digitalService, WritePin(1, false)).Times(1); 134 | // EXPECT_CALL(_timerService, GetTick()).Times(2).WillRepeatedly(Return(5000)); 135 | // _timerService.ReturnCallBack(); 136 | 137 | // EXPECT_CALL(_digitalService, InitPin(1, HardwareAbstraction::In)).Times(1); 138 | // _postReluctorCallBackGroup->Execute(); 139 | 140 | // EXPECT_CALL(_digitalService, InitPin(1, HardwareAbstraction::Out)).Times(1); 141 | // EXPECT_CALL(_digitalService, WritePin(1, false)).Times(1); 142 | // _fuelPumpService->Off(); 143 | 144 | // EXPECT_CALL(_digitalService, InitPin(1, HardwareAbstraction::In)).Times(1); 145 | // SetUp(true, true); 146 | 147 | // EXPECT_CALL(_timerService, GetTick()).Times(2).WillRepeatedly(Return(0)); 148 | // EXPECT_CALL(_timerService, ScheduleCallBack(5000)).Times(1); 149 | // EXPECT_CALL(_digitalService, InitPin(1, HardwareAbstraction::Out)).Times(1); 150 | // EXPECT_CALL(_digitalService, WritePin(1, false)).Times(1); 151 | // _preReluctorCallBackGroup->Execute(); 152 | 153 | // EXPECT_CALL(_digitalService, InitPin(1, HardwareAbstraction::In)).Times(1); 154 | // EXPECT_CALL(_timerService, GetTick()).Times(2).WillRepeatedly(Return(5000)); 155 | // _timerService.ReturnCallBack(); 156 | 157 | // EXPECT_CALL(_digitalService, InitPin(1, HardwareAbstraction::Out)).Times(1); 158 | // EXPECT_CALL(_digitalService, WritePin(1, false)).Times(1); 159 | // _postReluctorCallBackGroup->Execute(); 160 | 161 | // EXPECT_CALL(_digitalService, InitPin(1, HardwareAbstraction::In)).Times(1); 162 | // _fuelPumpService->Off(); 163 | // } 164 | // } -------------------------------------------------------------------------------- /test/src/AfrService_Map_EthanolTests.cpp: -------------------------------------------------------------------------------- 1 | // #include "gmock/gmock.h" 2 | // #include "gtest/gtest.h" 3 | // #include "EngineControlServices/AfrService/AfrService_Map_Ethanol.h" 4 | // #include "MockTimerService.h" 5 | // #include "MockFloatInputService.h" 6 | // #include "EngineControlServices/RpmService/RpmService.h" 7 | // #include "Service/EngineControlServicesServiceBuilderRegister.h" 8 | // #include "Service/IOServicesServiceBuilderRegister.h" 9 | // #include "Service/HardwareAbstractionServiceBuilder.h" 10 | // using ::testing::AtLeast; 11 | // using ::testing::Return; 12 | 13 | // using namespace Reluctor; 14 | // using namespace HardwareAbstraction; 15 | // using namespace IOServices; 16 | // using namespace Service; 17 | 18 | // namespace UnitTests 19 | // { 20 | // class AfrService_Map_EthanolTests : public ::testing::Test 21 | // { 22 | // protected: 23 | // MockTimerService _timerService; 24 | // MockFloatInputService _mapService; 25 | // MockFloatInputService _ectService; 26 | // MockFloatInputService _tpsService; 27 | // MockFloatInputService _ethanolService; 28 | // ServiceLocator *_serviceLocator; 29 | // EngineControlServices::IAfrService *_afrService; 30 | // RpmService *_rpmService; 31 | // CallBackGroup *_tickCallBackGroup; 32 | 33 | // AfrService_Map_EthanolTests() 34 | // { 35 | // _serviceLocator = new ServiceLocator(); 36 | 37 | // _serviceLocator->Register(BUILDER_IFLOATINPUTSERVICE, INSTANCE_MANIFOLD_ABSOLUTE_PRESSURE, &_mapService); 38 | // _serviceLocator->Register(BUILDER_IFLOATINPUTSERVICE, INSTANCE_ENGINE_COOLANT_TEMPERATURE, &_ectService); 39 | // _serviceLocator->Register(BUILDER_IFLOATINPUTSERVICE, INSTANCE_THROTTLE_POSITION, &_tpsService); 40 | // _serviceLocator->Register(BUILDER_IFLOATINPUTSERVICE, INSTANCE_ETHANOL_CONTENT, &_ethanolService); 41 | // _rpmService = new RpmService(0, 0); 42 | // _serviceLocator->Register(RPMSERVICE, _rpmService); 43 | // _serviceLocator->Register(TIMER_SERVICE_ID, &_timerService); 44 | // _tickCallBackGroup = new CallBackGroup(); 45 | // _serviceLocator->Register(TICK_CALL_BACK_GROUP, (void *)_tickCallBackGroup); 46 | 47 | // //GAS AFR TABLE, values in 1/1024 0 2000 4000 6000 48 | // unsigned short gasTable[4 * 4] = { 16076, 16076, 16076, 16076, //0 49 | // 16076, 15052, 15052, 15052, //33 50 | // 15052, 15052, 15052, 15052, //67 51 | // 13245, 12041, 12041, 12041};//100 52 | 53 | // //ETHANOL AFR TABLE, values in 1/1024 0 2000 4000 6000 54 | // unsigned short ethanolTable[4 * 4] = { 11024, 11024, 11024, 11024, //0 55 | // 11024, 10000, 10000, 10000, //33 56 | // 10000, 10000, 10000, 10000, //67 57 | // 9000, 8400, 8400, 8400}; //100 58 | 59 | // //ECT MULTIPLIER TABLE -40 13 67 120 60 | // unsigned char ectMultiplierTable[4] = { 0.8f * 255, 0.85f * 255, 0.95f * 255, 1 * 255 }; 61 | 62 | // //TPS MIN AFR TABLE GAS values in 1/1024 0 33 67 100 63 | // unsigned short tpsMinAfrTableGasTable[4] = { 16076, 15052, 14148, 13245 }; 64 | 65 | // //TPS MIN AFR TABLE ETHANOL values in 1/1024 0 33 67 100 66 | // unsigned short tpsMinAfrTableEthanolTable[4] = { 11024, 10000, 9200, 9000 }; 67 | 68 | // //Stoich Table values in 1/1024 69 | // unsigned short stoichTable[2] = { 15052, 10000 }; 70 | 71 | // AfrService_Map_EthanolConfig *afrConfig = (AfrService_Map_EthanolConfig *)malloc(sizeof(AfrService_Map_EthanolConfig)); 72 | 73 | // afrConfig->StartupAfrMultiplier = 0.9f; 74 | // afrConfig->StartupAfrDelay = 1; 75 | // afrConfig->StartupAfrDecay = 10; 76 | // afrConfig->MaxRpm = 6000; 77 | // afrConfig->MaxMapBar = 1; 78 | // afrConfig->AfrRpmResolution = 4; 79 | // afrConfig->AfrMapResolution = 4; 80 | // afrConfig->MaxEct = 120; 81 | // afrConfig->MinEct = -40; 82 | // afrConfig->AfrEctResolution = sizeof(ectMultiplierTable) / sizeof(unsigned char); 83 | // afrConfig->StoichResolution = sizeof(stoichTable) / sizeof(unsigned short); 84 | // afrConfig->AfrTpsResolution = sizeof(tpsMinAfrTableGasTable) / sizeof(unsigned short); 85 | 86 | // void *config = malloc(afrConfig->Size()+1); 87 | // void *buildConfig = config; 88 | 89 | // //afr service id 90 | // *((unsigned char *)buildConfig) = 2; 91 | // buildConfig = (void *)(((unsigned char *)buildConfig) + 1); 92 | 93 | // memcpy(buildConfig, afrConfig, sizeof(AfrService_Map_EthanolConfig)); 94 | // buildConfig = (void *)((unsigned char *)buildConfig + sizeof(AfrService_Map_EthanolConfig)); 95 | 96 | // //GAS AFR TABLE 97 | // memcpy(buildConfig, gasTable, sizeof(gasTable)); 98 | // buildConfig = (void*)((unsigned char *)buildConfig + sizeof(gasTable)); 99 | 100 | // //ETHANOL AFR TABLE 101 | // memcpy(buildConfig, ethanolTable, sizeof(ethanolTable)); 102 | // buildConfig = (void*)((unsigned char *)buildConfig + sizeof(ethanolTable)); 103 | 104 | // //ECT MULTIPLIER TABLE 105 | // memcpy(buildConfig, ectMultiplierTable, sizeof(ectMultiplierTable)); 106 | // buildConfig = (void*)((unsigned char *)buildConfig + sizeof(ectMultiplierTable)); 107 | 108 | // //TPS MIN AFR TABLE GAS 109 | // memcpy(buildConfig, tpsMinAfrTableGasTable, sizeof(tpsMinAfrTableGasTable)); 110 | // buildConfig = (void*)((unsigned char *)buildConfig + sizeof(tpsMinAfrTableGasTable)); 111 | 112 | // //TPS MIN AFR TABLE ETHANOL 113 | // memcpy(buildConfig, tpsMinAfrTableEthanolTable, sizeof(tpsMinAfrTableEthanolTable)); 114 | // buildConfig = (void*)((unsigned char *)buildConfig + sizeof(tpsMinAfrTableEthanolTable)); 115 | 116 | // //Stoich table 117 | // memcpy(buildConfig, stoichTable, sizeof(stoichTable)); 118 | // buildConfig = (void*)((unsigned char *)buildConfig + sizeof(stoichTable)); 119 | 120 | // EXPECT_CALL(_timerService, GetTicksPerSecond()) 121 | // .WillRepeatedly(Return(5000)); 122 | // unsigned int size = 0; 123 | // _afrService = reinterpret_cast(IAfrService::CreateAfrService(_serviceLocator, config, size)); 124 | // } 125 | 126 | // ~AfrService_Map_EthanolTests() override 127 | // { 128 | // free(_afrService); 129 | // free(_rpmService); 130 | // free(_tickCallBackGroup); 131 | // free(_serviceLocator); 132 | // } 133 | // }; 134 | 135 | // TEST_F(AfrService_Map_EthanolTests, WhenGettingAfrThenCorrectAfrIsReturned) 136 | // { 137 | // EXPECT_CALL(_timerService, GetTick()).Times(1).WillOnce(Return(0)); 138 | // _rpmService->Rpm = 0; 139 | // _mapService.Value = 0; 140 | // _ectService.Value = -40; 141 | // _tpsService.Value = 0; 142 | // _ethanolService.Value = 0; 143 | // _tickCallBackGroup->Execute(); 144 | // ASSERT_FLOAT_EQ(11.3034375f, _afrService->Afr); 145 | // ASSERT_NEAR(0.769f, _afrService->Lambda, 0.001f); 146 | 147 | // EXPECT_CALL(_timerService, GetTick()).Times(1).WillOnce(Return(5000)); 148 | // _rpmService->Rpm = 0; 149 | // _tickCallBackGroup->Execute(); 150 | // ASSERT_FLOAT_EQ(11.3034375f, _afrService->Afr); 151 | // ASSERT_NEAR(0.769f, _afrService->Lambda, 0.001f); 152 | 153 | // EXPECT_CALL(_timerService, GetTick()).Times(1).WillOnce(Return(27500)); 154 | // _rpmService->Rpm = 0; 155 | // _tickCallBackGroup->Execute(); 156 | // ASSERT_FLOAT_EQ(11.93140625f, _afrService->Afr); 157 | // ASSERT_NEAR(0.81f, _afrService->Lambda, 0.002f); 158 | 159 | // EXPECT_CALL(_timerService, GetTick()).Times(1).WillOnce(Return(50000)); 160 | // _rpmService->Rpm = 0; 161 | // _tickCallBackGroup->Execute(); 162 | // ASSERT_FLOAT_EQ(12.559375f, _afrService->Afr); 163 | // ASSERT_NEAR(0.855f, _afrService->Lambda, 0.001f); 164 | // EXPECT_CALL(_timerService, GetTick()).WillRepeatedly(Return(50001)); 165 | 166 | // _mapService.Value = 0.33; 167 | // _rpmService->Rpm = 2000; 168 | // _tickCallBackGroup->Execute(); 169 | // ASSERT_NEAR(11.76f, _afrService->Afr, 0.01f); 170 | // ASSERT_NEAR(0.8f, _afrService->Lambda, 0.001f); 171 | 172 | // _mapService.Value = 0.165; 173 | // _ectService.Value = 100; 174 | // _rpmService->Rpm = 1000; 175 | // _tickCallBackGroup->Execute(); 176 | // ASSERT_NEAR(15.14f, _afrService->Afr, 0.01f); 177 | 178 | // _mapService.Value = 0.165; 179 | // _rpmService->Rpm = 2000; 180 | // _tickCallBackGroup->Execute(); 181 | // ASSERT_NEAR(14.9f, _afrService->Afr, 0.01f); 182 | 183 | // _mapService.Value = 0; 184 | // _tpsService.Value = 1; 185 | // _rpmService->Rpm = 0; 186 | // _tickCallBackGroup->Execute(); 187 | // ASSERT_NEAR(12.94f, _afrService->Afr, 0.01f); 188 | 189 | // _ethanolService.Value = 1; 190 | // _rpmService->Rpm = 0; 191 | // _tickCallBackGroup->Execute(); 192 | // ASSERT_NEAR(8.79f, _afrService->Afr, 0.01f); 193 | 194 | // _tpsService.Value = 0; 195 | // _rpmService->Rpm = 0; 196 | // _tickCallBackGroup->Execute(); 197 | // ASSERT_NEAR(10.55f, _afrService->Afr, 0.01f); 198 | 199 | // _ethanolService.Value = 0.5; 200 | // _rpmService->Rpm = 0; 201 | // _tickCallBackGroup->Execute(); 202 | // ASSERT_NEAR(12.97f, _afrService->Afr, 0.01f); 203 | // } 204 | // } -------------------------------------------------------------------------------- /tune.json: -------------------------------------------------------------------------------- 1 | {"Inputs":{"inputs":[{"name":"MAP","translationConfig":{"calculationValues":[{"table":{"value":[0.1,1.081],"xAxis":[0,3.3],"yAxis":[]},"className":"Calculation_LookupTable"},{"pin":6,"className":"MAP_GM1Bar"}],"selection":"MAP_GM1Bar"},"rawConfig":{"calculationValues":[{"pin":6,"className":"Input_Analog"}]}},{"name":"Crank","translationConfig":{"calculationValues":[{"inverted":false,"pin":45,"length":100,"className":"Reluctor_GM24x"}],"selection":"Reluctor_GM24x"},"rawConfig":{"calculationValues":[{"inverted":false,"pin":45,"length":100,"className":"Input_DigitalRecord"}]}},{"name":"Cam","translationConfig":{"calculationValues":[{"inverted":false,"pin":18,"length":8,"risingPosition":0,"fallingPosition":180,"className":"Reluctor_Universal1x"}],"selection":"Reluctor_Universal1x"},"rawConfig":{"calculationValues":[{"inverted":false,"pin":18,"length":10,"className":"Input_DigitalRecord"}]}},{"name":"ECT","translationConfig":{"calculationValues":[{"pin":5,"className":"Input_Analog"}],"selection":"Input_Analog"},"rawConfig":{"calculationValues":[{"pin":5,"className":"Input_Analog"}]}},{"name":"TPS","translationConfig":{"calculationValues":[{"pin":4,"className":"Input_Analog"}],"selection":"Input_Analog"},"rawConfig":{"calculationValues":[{"pin":4,"className":"Input_Analog"}]}},{"name":"IAT","translationConfig":{"calculationValues":[{"pin":3,"className":"Input_Analog"}],"selection":"Input_Analog"},"rawConfig":{"calculationValues":[{"pin":3,"className":"Input_Analog"}]}},{"name":"green","translationConfig":{"calculationValues":[{"pin":2,"className":"Input_Analog"}],"selection":"Input_Analog"},"rawConfig":{"calculationValues":[{"pin":2,"className":"Input_Analog"}]}},{"name":"purple","translationConfig":{"calculationValues":[{"pin":1,"className":"Input_Analog"}],"selection":"Input_Analog"},"rawConfig":{"calculationValues":[{"pin":1,"className":"Input_Analog"}]}},{"name":"Battery Voltage","translationConfig":{"calculationValues":[{"pin":0,"className":"Input_Analog"}],"selection":"Input_Analog"},"rawConfig":{"calculationValues":[{"pin":0,"className":"Input_Analog"}]}}],"targetDevice":"STM32F401C"},"Engine":{"CrankPositionConfigOrVariableSelection":{"selection":{"name":"Inputs.Crank","type":"ReluctorResult"},"liveUpdate":"Reluctor"},"CamPositionConfigOrVariableSelection":{"selection":{"name":"Inputs.Cam","type":"ReluctorResult"},"liveUpdate":"Reluctor"},"CylinderAirmassConfigOrVariableSelection":{"calculationValues":[{"CylinderVolume":{"unit":"mL","value":0.66594},"className":"CylinderAirmass_SpeedDensity"}],"selection":"CylinderAirmass_SpeedDensity"},"CylinderAirTemperatureConfigOrVariableSelection":{"calculationValues":[{"value":31,"className":"Calculation_Static"}],"selection":"Calculation_Static"},"ManifoldAbsolutePressureConfigOrVariableSelection":{"selection":{"name":"Inputs.MAP","unit":"Bar"}},"VolumetricEfficiencyConfigOrVariableSelection":{"calculationValues":[{"table":{"value":[0.6475,0.67875,0.719375,0.73875,0.77125,0.778125,0.805625,0.84375,0.865625,0.865625,0.845,0.818125,0.79875,0.78875,0.779375,0.666875,0.69625,0.733125,0.759375,0.789375,0.79625,0.82,0.86,0.883125,0.883125,0.865,0.840625,0.821875,0.8125,0.803125,0.6825,0.7125,0.749375,0.77375,0.80125,0.808125,0.833125,0.870625,0.893125,0.89375,0.878125,0.855625,0.8375,0.82875,0.82,0.688125,0.724375,0.7625,0.789375,0.811875,0.818125,0.84,0.8775,0.901875,0.904375,0.889375,0.87,0.851875,0.843125,0.835,0.700625,0.735,0.775625,0.79875,0.818125,0.825625,0.84625,0.88375,0.90875,0.915625,0.900625,0.88125,0.863125,0.855625,0.8475,0.71375,0.745625,0.79,0.80125,0.823125,0.8325,0.855625,0.890625,0.915,0.923125,0.911875,0.89875,0.8875,0.88625,0.885,0.731875,0.76125,0.801875,0.8075,0.83125,0.84125,0.8625,0.896875,0.920625,0.929375,0.920625,0.905,0.89375,0.8925,0.89125,0.75125,0.77625,0.813125,0.816875,0.835625,0.845625,0.866875,0.900625,0.925625,0.934375,0.928125,0.914375,0.90375,0.9025,0.901875,0.765,0.7825,0.820625,0.823125,0.83875,0.848125,0.87,0.905,0.929375,0.94,0.93375,0.92125,0.910625,0.909375,0.90875,0.779375,0.788125,0.82375,0.826875,0.8425,0.853125,0.876875,0.911875,0.934375,0.945625,0.93875,0.928125,0.9175,0.916875,0.915625,0.793125,0.79125,0.824375,0.834375,0.84625,0.855625,0.880625,0.915,0.939375,0.94875,0.94375,0.935625,0.925625,0.925625,0.925625,0.805625,0.8,0.8325,0.841875,0.849375,0.86125,0.885625,0.920625,0.944375,0.954375,0.950625,0.94375,0.934375,0.934375,0.935,0.810625,0.809375,0.84,0.845625,0.85,0.86375,0.89,0.925,0.94875,0.961875,0.958125,0.953125,0.94375,0.945,0.945625,0.81625,0.81875,0.848125,0.8525,0.854375,0.86875,0.896875,0.9325,0.954375,0.969375,0.965,0.960625,0.951875,0.953125,0.954375,0.816875,0.82375,0.851875,0.855,0.85375,0.8675,0.89875,0.934375,0.956875,0.971875,0.96875,0.964375,0.955625,0.9575,0.959375],"xAxis":[500,1000,1500,2000,2500,3000,3500,4000,4500,5000,5500,6000,6500,7000,7500],"yAxis":[0.35,0.4,0.45,0.5,0.55,0.6,0.65,0.7,0.75,0.8,0.85,0.9,0.95,1,1.05]},"className":"Calculation_2AxisTable"},{"formula":"Base([0.0-1.0])","parameterValues":{"Base([0.0-1.0])":{"calculationValues":[{"table":{"value":[0],"xAxis":[],"yAxis":[0]},"className":"Calculation_LookupTable"},{"table":{"value":[0.6475,0.67875,0.719375,0.73875,0.77125,0.778125,0.805625,0.84375,0.865625,0.865625,0.845,0.818125,0.79875,0.78875,0.779375,0.666875,0.69625,0.733125,0.759375,0.789375,0.79625,0.82,0.86,0.883125,0.883125,0.865,0.840625,0.821875,0.8125,0.803125,0.6825,0.7125,0.749375,0.77375,0.80125,0.808125,0.833125,0.870625,0.893125,0.89375,0.878125,0.855625,0.8375,0.82875,0.82,0.688125,0.724375,0.7625,0.789375,0.811875,0.818125,0.84,0.8775,0.901875,0.904375,0.889375,0.87,0.851875,0.843125,0.835,0.700625,0.735,0.775625,0.79875,0.818125,0.825625,0.84625,0.88375,0.90875,0.915625,0.900625,0.88125,0.863125,0.855625,0.8475,0.71375,0.745625,0.79,0.80125,0.823125,0.8325,0.855625,0.890625,0.915,0.923125,0.911875,0.89875,0.8875,0.88625,0.885,0.731875,0.76125,0.801875,0.8075,0.83125,0.84125,0.8625,0.896875,0.920625,0.929375,0.920625,0.905,0.89375,0.8925,0.89125,0.75125,0.77625,0.813125,0.816875,0.835625,0.845625,0.866875,0.900625,0.925625,0.934375,0.928125,0.914375,0.90375,0.9025,0.901875,0.765,0.7825,0.820625,0.823125,0.83875,0.848125,0.87,0.905,0.929375,0.94,0.93375,0.92125,0.910625,0.909375,0.90875,0.779375,0.788125,0.82375,0.826875,0.8425,0.853125,0.876875,0.911875,0.934375,0.945625,0.93875,0.928125,0.9175,0.916875,0.915625,0.793125,0.79125,0.824375,0.834375,0.84625,0.855625,0.880625,0.915,0.939375,0.94875,0.94375,0.935625,0.925625,0.925625,0.925625,0.805625,0.8,0.8325,0.841875,0.849375,0.86125,0.885625,0.920625,0.944375,0.954375,0.950625,0.94375,0.934375,0.934375,0.935,0.810625,0.809375,0.84,0.845625,0.85,0.86375,0.89,0.925,0.94875,0.961875,0.958125,0.953125,0.94375,0.945,0.945625,0.81625,0.81875,0.848125,0.8525,0.854375,0.86875,0.896875,0.9325,0.954375,0.969375,0.965,0.960625,0.951875,0.953125,0.954375,0.816875,0.82375,0.851875,0.855,0.85375,0.8675,0.89875,0.934375,0.956875,0.971875,0.96875,0.964375,0.955625,0.9575,0.959375],"xAxis":[500,1000,1500,2000,2500,3000,3500,4000,4500,5000,5500,6000,6500,7000,7500],"yAxis":[0.35,0.39999999999999997,0.44999999999999996,0.5,0.55,0.6,0.65,0.7,0.75,0.8,0.85,0.9,0.9500000000000001,1,1.05]},"XSelection":{"name":"EngineParameters.Engine Speed","unit":"RPM"},"YSelection":{"name":"Inputs.MAP","unit":"Bar"},"className":"Calculation_2AxisTable"}],"selection":"Calculation_2AxisTable","liveUpdate":"[0.0-1.0]"},"Base":{"calculationValues":[{"table":{"value":[0.6475,0.67875,0.719375,0.73875,0.77125,0.778125,0.805625,0.84375,0.865625,0.865625,0.845,0.818125,0.79875,0.78875,0.779375,0.666875,0.69625,0.733125,0.759375,0.789375,0.79625,0.82,0.86,0.883125,0.883125,0.865,0.840625,0.821875,0.8125,0.803125,0.6825,0.7125,0.749375,0.77375,0.80125,0.808125,0.833125,0.870625,0.893125,0.89375,0.878125,0.855625,0.8375,0.82875,0.82,0.688125,0.724375,0.7625,0.789375,0.811875,0.818125,0.84,0.8775,0.901875,0.904375,0.889375,0.87,0.851875,0.843125,0.835,0.700625,0.735,0.775625,0.79875,0.818125,0.825625,0.84625,0.88375,0.90875,0.915625,0.900625,0.88125,0.863125,0.855625,0.8475,0.71375,0.745625,0.79,0.80125,0.823125,0.8325,0.855625,0.890625,0.915,0.923125,0.911875,0.89875,0.8875,0.88625,0.885,0.731875,0.76125,0.801875,0.8075,0.83125,0.84125,0.8625,0.896875,0.920625,0.929375,0.920625,0.905,0.89375,0.8925,0.89125,0.75125,0.77625,0.813125,0.816875,0.835625,0.845625,0.866875,0.900625,0.925625,0.934375,0.928125,0.914375,0.90375,0.9025,0.901875,0.765,0.7825,0.820625,0.823125,0.83875,0.848125,0.87,0.905,0.929375,0.94,0.93375,0.92125,0.910625,0.909375,0.90875,0.779375,0.788125,0.82375,0.826875,0.8425,0.853125,0.876875,0.911875,0.934375,0.945625,0.93875,0.928125,0.9175,0.916875,0.915625,0.793125,0.79125,0.824375,0.834375,0.84625,0.855625,0.880625,0.915,0.939375,0.94875,0.94375,0.935625,0.925625,0.925625,0.925625,0.805625,0.8,0.8325,0.841875,0.849375,0.86125,0.885625,0.920625,0.944375,0.954375,0.950625,0.94375,0.934375,0.934375,0.935,0.810625,0.809375,0.84,0.845625,0.85,0.86375,0.89,0.925,0.94875,0.961875,0.958125,0.953125,0.94375,0.945,0.945625,0.81625,0.81875,0.848125,0.8525,0.854375,0.86875,0.896875,0.9325,0.954375,0.969375,0.965,0.960625,0.951875,0.953125,0.954375,0.816875,0.82375,0.851875,0.855,0.85375,0.8675,0.89875,0.934375,0.956875,0.971875,0.96875,0.964375,0.955625,0.9575,0.959375],"xAxis":[500,1000,1500,2000,2500,3000,3500,4000,4500,5000,5500,6000,6500,7000,7500],"yAxis":[0.35,0.39999999999999997,0.44999999999999996,0.5,0.55,0.6,0.65,0.7,0.75,0.8,0.85,0.9,0.9500000000000001,1,1.05]},"XSelection":{"name":"EngineParameters.Engine Speed","unit":"RPM"},"YSelection":{"name":"Inputs.MAP","unit":"Bar"},"className":"Calculation_2AxisTable"}],"selection":"Calculation_2AxisTable"},"Base(%)":{"calculationValues":[{"table":{"value":[0.6475,0.67875,0.719375,0.73875,0.77125,0.778125,0.805625,0.84375,0.865625,0.865625,0.845,0.818125,0.79875,0.78875,0.779375,0.666875,0.69625,0.733125,0.759375,0.789375,0.79625,0.82,0.86,0.883125,0.883125,0.865,0.840625,0.821875,0.8125,0.803125,0.6825,0.7125,0.749375,0.77375,0.80125,0.808125,0.833125,0.870625,0.893125,0.89375,0.878125,0.855625,0.8375,0.82875,0.82,0.688125,0.724375,0.7625,0.789375,0.811875,0.818125,0.84,0.8775,0.901875,0.904375,0.889375,0.87,0.851875,0.843125,0.835,0.700625,0.735,0.775625,0.79875,0.818125,0.825625,0.84625,0.88375,0.90875,0.915625,0.900625,0.88125,0.863125,0.855625,0.8475,0.71375,0.745625,0.79,0.80125,0.823125,0.8325,0.855625,0.890625,0.915,0.923125,0.911875,0.89875,0.8875,0.88625,0.885,0.731875,0.76125,0.801875,0.8075,0.83125,0.84125,0.8625,0.896875,0.920625,0.929375,0.920625,0.905,0.89375,0.8925,0.89125,0.75125,0.77625,0.813125,0.816875,0.835625,0.845625,0.866875,0.900625,0.925625,0.934375,0.928125,0.914375,0.90375,0.9025,0.901875,0.765,0.7825,0.820625,0.823125,0.83875,0.848125,0.87,0.905,0.929375,0.94,0.93375,0.92125,0.910625,0.909375,0.90875,0.779375,0.788125,0.82375,0.826875,0.8425,0.853125,0.876875,0.911875,0.934375,0.945625,0.93875,0.928125,0.9175,0.916875,0.915625,0.793125,0.79125,0.824375,0.834375,0.84625,0.855625,0.880625,0.915,0.939375,0.94875,0.94375,0.935625,0.925625,0.925625,0.925625,0.805625,0.8,0.8325,0.841875,0.849375,0.86125,0.885625,0.920625,0.944375,0.954375,0.950625,0.94375,0.934375,0.934375,0.935,0.810625,0.809375,0.84,0.845625,0.85,0.86375,0.89,0.925,0.94875,0.961875,0.958125,0.953125,0.94375,0.945,0.945625,0.81625,0.81875,0.848125,0.8525,0.854375,0.86875,0.896875,0.9325,0.954375,0.969375,0.965,0.960625,0.951875,0.953125,0.954375,0.816875,0.82375,0.851875,0.855,0.85375,0.8675,0.89875,0.934375,0.956875,0.971875,0.96875,0.964375,0.955625,0.9575,0.959375],"xAxis":[500,1000,1500,2000,2500,3000,3500,4000,4500,5000,5500,6000,6500,7000,7500],"yAxis":[0.35,0.39999999999999997,0.44999999999999996,0.5,0.55,0.6,0.65,0.7,0.75,0.8,0.85,0.9,0.9500000000000001,1,1.05]},"XSelection":"EngineParameters.Engine Speed(AngularSpeed)","YSelection":"Inputs.MAP(Pressure)","className":"Calculation_2AxisTable"}],"selection":"Calculation_2AxisTable"},"test":{"calculationValues":[{"value":1,"className":"Calculation_Static"}],"selection":"Calculation_Static"},"test3":{"calculationValues":[{"value":0.01,"className":"Calculation_Static"}],"selection":"Calculation_Static"},"test2":{"calculationValues":[{"value":0.01,"className":"Calculation_Static"}],"selection":"Calculation_Static"},"Base([0.01.0])":{"calculationValues":[{"table":{"value":[0.6475,0.67875,0.719375,0.73875,0.77125,0.778125,0.805625,0.84375,0.865625,0.865625,0.845,0.818125,0.79875,0.78875,0.779375,0.666875,0.69625,0.733125,0.759375,0.789375,0.79625,0.82,0.86,0.883125,0.883125,0.865,0.840625,0.821875,0.8125,0.803125,0.6825,0.7125,0.749375,0.77375,0.80125,0.808125,0.833125,0.870625,0.893125,0.89375,0.878125,0.855625,0.8375,0.82875,0.82,0.688125,0.724375,0.7625,0.789375,0.811875,0.818125,0.84,0.8775,0.901875,0.904375,0.889375,0.87,0.851875,0.843125,0.835,0.700625,0.735,0.775625,0.79875,0.818125,0.825625,0.84625,0.88375,0.90875,0.915625,0.900625,0.88125,0.863125,0.855625,0.8475,0.71375,0.745625,0.79,0.80125,0.823125,0.8325,0.855625,0.890625,0.915,0.923125,0.911875,0.89875,0.8875,0.88625,0.885,0.731875,0.76125,0.801875,0.8075,0.83125,0.84125,0.8625,0.896875,0.920625,0.929375,0.920625,0.905,0.89375,0.8925,0.89125,0.75125,0.77625,0.813125,0.816875,0.835625,0.845625,0.866875,0.900625,0.925625,0.934375,0.928125,0.914375,0.90375,0.9025,0.901875,0.765,0.7825,0.820625,0.823125,0.83875,0.848125,0.87,0.905,0.929375,0.94,0.93375,0.92125,0.910625,0.909375,0.90875,0.779375,0.788125,0.82375,0.826875,0.8425,0.853125,0.876875,0.911875,0.934375,0.945625,0.93875,0.928125,0.9175,0.916875,0.915625,0.793125,0.79125,0.824375,0.834375,0.84625,0.855625,0.880625,0.915,0.939375,0.94875,0.94375,0.935625,0.925625,0.925625,0.925625,0.805625,0.8,0.8325,0.841875,0.849375,0.86125,0.885625,0.920625,0.944375,0.954375,0.950625,0.94375,0.934375,0.934375,0.935,0.810625,0.809375,0.84,0.845625,0.85,0.86375,0.89,0.925,0.94875,0.961875,0.958125,0.953125,0.94375,0.945,0.945625,0.81625,0.81875,0.848125,0.8525,0.854375,0.86875,0.896875,0.9325,0.954375,0.969375,0.965,0.960625,0.951875,0.953125,0.954375,0.816875,0.82375,0.851875,0.855,0.85375,0.8675,0.89875,0.934375,0.956875,0.971875,0.96875,0.964375,0.955625,0.9575,0.959375],"xAxis":[500,1000,1500,2000,2500,3000,3500,4000,4500,5000,5500,6000,6500,7000,7500],"yAxis":[0.35,0.39999999999999997,0.44999999999999996,0.5,0.55,0.6,0.65,0.7,0.75,0.8,0.85,0.9,0.9500000000000001,1,1.05]},"XSelection":{"name":"EngineParameters.Engine Speed","unit":"RPM"},"YSelection":{"name":"Inputs.MAP","unit":"Bar"},"className":"Calculation_2AxisTable"}],"selection":"Calculation_2AxisTable","liveUpdate":"[0.01.0]"}},"className":"Calculation_Formula"}],"selection":"Calculation_Formula","liveUpdate":"[0.0-1.0]"}},"Fuel":{"AFRConfigOrVariableSelection":{"formula":"Base(:1)","parameterValues":{"Base(:1)":{"calculationValues":[{"value":14.7,"className":"Calculation_Static"}],"selection":"Calculation_Static"},"Base":{"calculationValues":[{"value":14.7,"className":"Calculation_Static"}],"selection":"Calculation_Static"}}},"InjectorEnableConfigOrVariableSelection":{"calculationValues":[{"formula":"RPM(RPM) > 40","parameterValues":{"RPM(RPM)":{"selection":{"name":"EngineParameters.Engine Speed","unit":"RPM"}},"RPM":{"selection":{"name":"EngineParameters.Engine Speed","unit":"RPM"},"liveUpdate":"AngularSpeed"}},"className":"Calculation_Formula"}],"selection":"Calculation_Formula"},"InjectorPulseWidthConfigOrVariableSelection":{"calculationValues":[{"FlowRateConfigOrVariableSelection":{"calculationValues":[{"value":26.46,"className":"Calculation_Static"}],"selection":"Calculation_Static"},"DeadTimeConfigOrVariableSelection":{"calculationValues":[{"unit":"ms","value":0.00045,"className":"Calculation_Static"},{"formula":"Base(s) * VoltageCompensation","parameterValues":{"Base":{"calculationValues":[{"unit":"ms","value":0.00045,"className":"Calculation_Static"}],"selection":"Calculation_Static"},"VoltageCompensation":{"calculationValues":[{"table":{"value":[1.5,1.2,1,0.95,0.9,0.8500000000000001,0.8000000000000002,0.7500000000000002],"xAxis":[6,9,12,15,18,21,24,27],"yAxis":[]},"parameterSelection":{"name":"Inputs.Battery Voltage","unit":"V"},"className":"Calculation_LookupTable"}],"selection":"Calculation_LookupTable"},"VoltageCompensation(V)":{"selection":"Calculation_LookupTable","calculationValues":[{"table":{"value":[0,0,0,0,0,0,0,0,0,0],"xAxis":[0,0,0,0,0,0,0,0,0,0],"yAxis":[]},"className":"Calculation_LookupTable"}]},"Base(s)":{"selection":"Calculation_Static","calculationValues":[{"unit":"ms","value":0.00045,"className":"Calculation_Static"}]}},"className":"Calculation_Formula"},{"table":{"value":[0],"xAxis":[],"yAxis":[0]},"className":"Calculation_LookupTable"}],"selection":"Calculation_Formula","liveUpdate":"ms"},"MinInjectorFuelMass":{"unit":"mg","value":0.01},"className":"InjectorPulseWidth_DeadTime"}],"selection":"InjectorPulseWidth_DeadTime","liveUpdate":"ms"},"InjectorEndPositionConfigOrVariableSelection":{"calculationValues":[{"value":180,"className":"Calculation_Static"}],"selection":"Calculation_Static"},"Outputs":[{"calculationValues":[{"pin":21,"inverted":false,"highZ":false,"className":"Output_Digital"}],"selection":"Output_Digital","TDC":0},{"calculationValues":[{"pin":7,"inverted":false,"highZ":false,"className":"Output_Digital"}],"selection":"Output_Digital","TDC":270},{"calculationValues":[{"pin":28,"inverted":false,"highZ":false,"className":"Output_Digital"}],"selection":"Output_Digital","TDC":630},{"calculationValues":[{"pin":29,"inverted":false,"highZ":false,"className":"Output_Digital"}],"selection":"Output_Digital","TDC":540},{"calculationValues":[{"pin":30,"inverted":false,"highZ":false,"className":"Output_Digital"}],"selection":"Output_Digital","TDC":450},{"calculationValues":[{"pin":31,"inverted":false,"highZ":false,"className":"Output_Digital"}],"selection":"Output_Digital","TDC":360},{"calculationValues":[{"pin":8,"inverted":false,"highZ":false,"className":"Output_Digital"}],"selection":"Output_Digital","TDC":180},{"calculationValues":[{"pin":9,"inverted":false,"highZ":false,"className":"Output_Digital"}],"selection":"Output_Digital","TDC":90}]},"Ignition":{"IgnitionEnableConfigOrVariableSelection":{"calculationValues":[{"formula":"RPM(RPM) > 40","parameterValues":{"RPM(RPM)":{"selection":{"name":"EngineParameters.Engine Speed","unit":"RPM"}},"RPM":{"selection":{"name":"EngineParameters.Engine Speed","unit":"RPM"},"liveUpdate":"AngularSpeed"},"static":{"calculationValues":[{"className":"Calculation_Static"}],"selection":"Calculation_Static"}},"className":"Calculation_Formula"}],"selection":"Calculation_Formula"},"IgnitionAdvanceConfigOrVariableSelection":{"calculationValues":[{"table":{"value":[33.9144,37.2772,38.2133,39.3337,40.5954,41.3312,41.7881,41.6027,41.7208,42.7041,43.7328,45,45.25,45.25,45.25,33.5658,35.841,36.5531,37.9047,38.9617,39.7952,40.7189,40.524,40.7931,41.5187,42.4029,44.0998,44.3873,44.4999,44.9499,31.6128,33.4773,34.5123,36.4071,37.6877,38.9298,39.0206,38.6783,39.4891,40.6508,41.1639,42.9788,43.5258,43.4788,44.5415,27.6319,29.8078,32.3368,34.5686,35.9179,37.0978,37.2708,36.6758,37.7186,39.5211,39.8538,41.9313,42.7904,42.5588,44.0588,23.7257,26.4385,29.8661,32.5346,34.2512,35.4802,35.7588,35.1885,36.0149,37.9175,39.014,40.8703,41.6396,41.8271,43.4406,20.8026,23.7187,27.5617,30.7721,32.4899,33.8956,34.2539,33.8179,34.5606,36.5477,38.1123,39.5339,39.8577,40.0452,42.828,18.5878,20.5773,25.0011,28.9646,30.3593,31.9928,32.839,32.6443,32.943,34.2277,36.1255,38.4922,38.2,38.3875,41.3512,14.6828,17.0153,22.7439,27.1342,28.7449,30.6303,31.6784,31.547,31.1236,32.2786,34.0781,36.5797,36.467,36.5153,39.5153,11.4857,14.7894,21.2019,25.5631,28.1463,29.8263,30.4757,30.0062,29.8024,30.7856,32.1872,34.641,34.5277,34.641,37.641,8.7167,13.7885,19.4963,24.1944,26.6526,28.354,28.678,27.7255,27.4756,28.4355,30.3905,32.7936,32.6359,32.9131,35.6441,6.28202,10.6503,17.3278,22.6758,24.8663,26.7368,26.7646,25.6422,24.8858,26.0004,28.3478,31.0061,30.9973,31.1631,33.6703,2.99706,7.04458,14.167,20.1296,22.7149,24.4901,24.3051,23.6751,23.3456,23.9346,26.0619,29.0706,29.2751,29.1386,31.6107,0,2.77396,10.2382,17.1073,20.2926,22.5283,22.8047,22.4084,21.9208,22.1322,24.1616,26.6346,26.823,26.823,28.7932,0,0,6.70473,14.3665,18.1275,20.9676,21.1505,20.8864,19.7229,20.1827,22.1259,24.3576,24.3031,24.2704,25.8926,0,0,4.06295,12.3385,16.1213,19.4514,19.4945,18.8251,17.0623,17.7066,20.4335,22.538,22.2308,22.0465,23.3653],"xAxis":[500,1000,1500,2000,2500,3000,3500,4000,4500,5000,5500,6000,6500,7000,7500],"yAxis":[0.35,0.4,0.45,0.5,0.55,0.6,0.65,0.7,0.75,0.8,0.85,0.9,0.95,1,1.05]},"XSelection":{"name":"EngineParameters.Engine Speed","unit":"RPM"},"YSelection":{"name":"Inputs.MAP","unit":"Bar"},"className":"Calculation_2AxisTable"}],"selection":"Calculation_2AxisTable"},"IgnitionDwellConfigOrVariableSelection":{"calculationValues":[{"unit":"ms","value":0.003,"className":"Calculation_Static"}],"selection":"Calculation_Static","liveUpdate":"ms"},"IgnitionDwellDeviationConfigOrVariableSelection":{"calculationValues":[{"unit":"ms","value":0.0005,"className":"Calculation_Static"}],"selection":"Calculation_Static","liveUpdate":"ms"},"Outputs":[{"calculationValues":[{"pin":19,"inverted":true,"highZ":true,"className":"Output_Digital"}],"selection":"Output_Digital","TDC":0},{"calculationValues":[{"pin":15,"inverted":true,"highZ":true,"className":"Output_Digital"}],"selection":"Output_Digital","TDC":270},{"calculationValues":[{"pin":22,"inverted":true,"highZ":true,"className":"Output_Digital"}],"selection":"Output_Digital","TDC":630},{"calculationValues":[{"pin":20,"inverted":true,"highZ":true,"className":"Output_Digital"}],"selection":"Output_Digital","TDC":540},{"calculationValues":[{"pin":24,"inverted":true,"highZ":true,"className":"Output_Digital"}],"selection":"Output_Digital","TDC":450},{"calculationValues":[{"pin":23,"inverted":true,"highZ":true,"className":"Output_Digital"}],"selection":"Output_Digital","TDC":360},{"calculationValues":[{"pin":26,"inverted":true,"highZ":true,"className":"Output_Digital"}],"selection":"Output_Digital","TDC":180},{"calculationValues":[{"pin":25,"inverted":true,"highZ":true,"className":"Output_Digital"}],"selection":"Output_Digital","TDC":90}]}} --------------------------------------------------------------------------------