Add machine workshop, typed logic, mechanics and sensor-driven ECUs
This commit is contained in:
@@ -1,8 +1,29 @@
|
||||
#pragma once
|
||||
#include "FluidSim/MachinePorts.hpp"
|
||||
#include <string_view>
|
||||
#include <vector>
|
||||
|
||||
namespace fluidsim::port {
|
||||
|
||||
// Accepts only coherent, current channels from one physical crank pickup.
|
||||
// Signed reverse measurements remain valid sensors, but cannot drive this
|
||||
// forward-only ignition controller. A stationary crank is valid.
|
||||
std::optional<SignalTiming> ecu_crank_sample(const SignalValue& speed,
|
||||
const SignalValue& angle,double time);
|
||||
|
||||
class CrankIgnitionClock {
|
||||
public:
|
||||
struct Result {bool valid{};unsigned sparks{};};
|
||||
// Crank-only wasted spark: one event per revolution at the configured
|
||||
// pickup angle, without consulting a cylinder's hidden 720-degree phase.
|
||||
Result sample(const SignalValue& speed,const SignalValue& angle,
|
||||
double time,double advance_degrees,double tdc_degrees=0);
|
||||
void reset(){previous_.reset();last_event_cycle_.reset();}
|
||||
private:
|
||||
std::optional<SignalTiming> previous_;
|
||||
std::optional<double> last_event_cycle_;
|
||||
};
|
||||
|
||||
// Ideal sensors and injectors; calibration parameters are not engine measurements.
|
||||
struct EcuConfig {
|
||||
enum class IdleControl { Feedback, FixedOpening };
|
||||
@@ -18,6 +39,8 @@ struct EcuConfig {
|
||||
float rev_limit_rpm{10000.0f}, rev_resume_rpm{9750.0f};
|
||||
bool rev_limit_enabled{true};
|
||||
bool rev_limit_fuel_cut_enabled{true};
|
||||
bool rev_limit_ignition_cut_enabled{true};
|
||||
float rev_limit_cut_time{}; // Minimum cut duration in seconds, followed by RPM hysteresis.
|
||||
float overrun_cut_rpm{3500.0f}, overrun_resume_rpm{2400.0f};
|
||||
bool overrun_fuel_cut_enabled{true};
|
||||
bool overrun_ignition_cut_enabled{false};
|
||||
@@ -35,7 +58,7 @@ public:
|
||||
float target_afr() const { return afr_; }
|
||||
float spark_advance() const { return spark_; }
|
||||
bool fuel_cut() const { return (limiter_ && rev_limit_fuel_cut_enabled) || (overrun_ && overrun_fuel_cut_enabled) || fault_; }
|
||||
bool ignition_cut() const { return limiter_ || (overrun_ && overrun_ignition_cut_enabled) || fault_; }
|
||||
bool ignition_cut() const { return (limiter_ && rev_limit_ignition_cut_enabled) || (overrun_ && overrun_ignition_cut_enabled) || fault_; }
|
||||
bool rev_limited() const { return limiter_; }
|
||||
bool idle_active() const { return idle_; }
|
||||
bool sensor_fault() const { return fault_; }
|
||||
@@ -45,5 +68,51 @@ private:
|
||||
bool initialized_{};
|
||||
float opening_{}, target_{}, integral_{}, afr_{14.7f}, spark_{10.0f};
|
||||
bool limiter_{}, overrun_{}, fault_{}, idle_{};
|
||||
float limiter_time_{};
|
||||
};
|
||||
|
||||
// Separate from the legacy ECU: measurements are supplied through typed wires.
|
||||
// Alpha-N cells are calibrated kg per cylinder per four-stroke cycle, indexed
|
||||
// by RPM rows and measured TPS columns. No trapped-air-mass input is available.
|
||||
struct SensorEcuConfig {
|
||||
enum class Strategy {Ignition,AlphaN,SpeedDensity};
|
||||
Strategy strategy{Strategy::Ignition};
|
||||
std::vector<EcuConfig::IgnitionPoint> ignition_map{{0,10},{6000,30}};
|
||||
std::vector<double> fuel_rpm{0,6000},fuel_tps{0,1};
|
||||
std::vector<double> fuel_per_cycle{0.000002,0.00002,0.000002,0.00002};
|
||||
// Open-loop dry ideal-gas estimate per cylinder. Geometry, VE and fuel AFR
|
||||
// are calibration; MAP and IAT must arrive as current wired measurements.
|
||||
double displacement{.0003},air_gas_constant{287},target_afr{14.7};
|
||||
std::vector<double> ve_rpm{0,6000},ve_pressure{20000,100000};
|
||||
std::vector<double> volumetric_efficiency{.9,.9,.9,.9};
|
||||
bool rev_limit_enabled{true};
|
||||
double rev_limit_rpm{7000},rev_resume_rpm{6800};
|
||||
};
|
||||
inline constexpr int sensor_ecu_strategy_count=3;
|
||||
const char* sensor_ecu_strategy_key(SensorEcuConfig::Strategy);
|
||||
const char* sensor_ecu_strategy_name(SensorEcuConfig::Strategy);
|
||||
SensorEcuConfig::Strategy sensor_ecu_strategy_from_key(std::string_view);
|
||||
SensorEcuConfig::Strategy next_sensor_ecu_strategy(SensorEcuConfig::Strategy);
|
||||
struct SensorEcuMeasurements {SignalValue speed,angle,tps,map,iat;};
|
||||
struct SensorEcuState {
|
||||
bool sensor_fault{true},rev_limited{};
|
||||
double spark_advance{},fuel_per_cycle{},fuel_mass_flow{};
|
||||
double estimated_air_per_cycle{},volumetric_efficiency{};
|
||||
unsigned spark_events{};
|
||||
};
|
||||
class SensorEcu {
|
||||
public:
|
||||
explicit SensorEcu(SensorEcuConfig config);
|
||||
void apply_calibration(SensorEcuConfig config);
|
||||
const SensorEcuState& sample(const SignalValue& speed,const SignalValue& angle,
|
||||
const SignalValue& tps,double time);
|
||||
const SensorEcuState& sample(const SensorEcuMeasurements&,double time);
|
||||
const SensorEcuState& state()const{return state_;}
|
||||
void reset(){clock_.reset();state_={};limiter_=false;}
|
||||
private:
|
||||
SensorEcuConfig config_;
|
||||
CrankIgnitionClock clock_;
|
||||
SensorEcuState state_;
|
||||
bool limiter_{};
|
||||
};
|
||||
} // namespace fluidsim::port
|
||||
|
||||
Reference in New Issue
Block a user