Add machine workshop, typed logic, mechanics and sensor-driven ECUs

This commit is contained in:
max
2026-11-02 07:27:13 +01:00
parent eb2b6366c2
commit abfcce0f46
99 changed files with 28835 additions and 145 deletions

View File

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