Add American V8 preset, carb fuel transport, and adaptive desktop audio
This commit is contained in:
@@ -7,11 +7,17 @@ namespace fluidsim::port {
|
||||
|
||||
FourStrokeScenario::FourStrokeScenario(int sample_rate, FourStrokePreset selected_preset, bool turbo_enabled)
|
||||
: preset(selected_preset),
|
||||
intake_plenum(selected_preset == FourStrokePreset::SaabB205 ? 3e-3f : 1e-3f, 101325, 300),
|
||||
exhaust_collector(selected_preset == FourStrokePreset::SaabB205 ? .6e-3f : .15e-3f, 101325, 700),
|
||||
intake_plenum(selected_preset == FourStrokePreset::AmericanV8 ? 4e-3f : selected_preset == FourStrokePreset::SaabB205 ? 3e-3f : 1e-3f, 101325, 300),
|
||||
exhaust_collector(selected_preset == FourStrokePreset::AmericanV8 ? 1.5e-3f : selected_preset == FourStrokePreset::SaabB205 ? .6e-3f : .15e-3f, 101325, 700),
|
||||
pipes(create_pipes(selected_preset, turbo_enabled)), dt_(1.f / std::max(sample_rate, 1)),
|
||||
boundaries_(pipes), solver_(pipes, boundaries_), audio_(sample_rate), mechanical_sound_(sample_rate) {
|
||||
boundaries_(pipes), solver_(pipes, boundaries_), audio_(sample_rate, [&] {
|
||||
OutdoorAudioConfig config;
|
||||
config.dual_exhaust = selected_preset == FourStrokePreset::AmericanV8;
|
||||
return config;
|
||||
}()), mechanical_sound_(sample_rate) {
|
||||
const bool saab = preset == FourStrokePreset::SaabB205;
|
||||
const bool v8 = preset == FourStrokePreset::AmericanV8;
|
||||
if (v8 && turbo_enabled) throw std::invalid_argument("American V8 preset is naturally aspirated");
|
||||
const int count = cylinder_count();
|
||||
crankshaft.omega = (saab ? 1200 : 2000) * 2 * pi / 60;
|
||||
crankshaft.inertia = saab ? .18f : .04f;
|
||||
@@ -36,13 +42,54 @@ FourStrokeScenario::FourStrokeScenario(int sample_rate, FourStrokePreset selecte
|
||||
ecu.overrun_cut_rpm = 2000;
|
||||
ecu.overrun_resume_rpm = 1300;
|
||||
}
|
||||
if (v8) {
|
||||
// Estimated mild 1980s 302-class pushrod small block and manual coupe.
|
||||
crankshaft.omega = 1000 * 2*pi/60;
|
||||
crankshaft.inertia = 1.1f;
|
||||
crankshaft.friction_constant = 22;
|
||||
crankshaft.friction_viscous = .025f;
|
||||
vehicle.mass = 3000;
|
||||
vehicle.primary_reduction = 1;
|
||||
vehicle.final_drive_ratio = 6.16f / 1.7f; // 1.7x taller gearing in every gear.
|
||||
vehicle.gear_ratios = {3.35f, 1.93f, 1.29f, 1.f, .68f};
|
||||
vehicle.wheel_radius = .32f;
|
||||
vehicle.clutch_capacity = 1100; // Hold V8 torque with the heavy flywheel/car.
|
||||
vehicle.drag_coefficient = .4f;
|
||||
vehicle.frontal_area = 2.f;
|
||||
ecu.idle_rpm = 750;
|
||||
ecu.throttle_time_constant = .065f;
|
||||
ecu.throttle_rate = 2.3f;
|
||||
ecu.throttle_closing_time_constant = .16f;
|
||||
ecu.throttle_closing_rate = 1.4f;
|
||||
ecu.idle_base_opening = .08f;
|
||||
ecu.idle_spark_gain = .02f;
|
||||
ecu.ignition_map = {{0, 10}, {750, 12}, {2500, 28}, {4000, 32}, {6000, 30}};
|
||||
ecu.rev_limit_rpm = 7000;
|
||||
ecu.rev_resume_rpm = 6750;
|
||||
ecu.overrun_cut_rpm = 2000;
|
||||
ecu.overrun_resume_rpm = 1100;
|
||||
// One minimum substep; acoustic CFL still subdivides when needed.
|
||||
solver_.minimum_substeps = 1;
|
||||
}
|
||||
vehicle.set_neutral();
|
||||
vehicle.clutch_input = clutch;
|
||||
const auto engines = cylinders();
|
||||
const std::array<float, 4> phases{0, pi, 3*pi, 2*pi}; // I4 firing: 1-3-4-2
|
||||
const std::array<float, 8> phases{0, pi, 3*pi, 2*pi}; // I4 firing: 1-3-4-2
|
||||
const std::array<int, 8> firing_order{1, 5, 4, 2, 6, 3, 7, 8};
|
||||
for (int i = 0; i < count; ++i) {
|
||||
auto& c = *engines[i];
|
||||
c.phase = phases[i];
|
||||
if (v8) {
|
||||
const auto position = std::find(firing_order.begin(), firing_order.end(), i+1) - firing_order.begin();
|
||||
c.phase = std::fmod(4*pi - float(position)*pi*.5f, 4*pi);
|
||||
c.bore = .1016f;
|
||||
c.stroke = .0762f;
|
||||
c.rod = .129f;
|
||||
c.compression = 7.0f;
|
||||
c.intake_open = 350; c.intake_close = 570;
|
||||
c.exhaust_open = 140; c.exhaust_close = 370;
|
||||
c.burn_duration = 65;
|
||||
}
|
||||
if (saab) {
|
||||
c.bore = .090f;
|
||||
c.stroke = .078f;
|
||||
@@ -54,13 +101,17 @@ FourStrokeScenario::FourStrokeScenario(int sample_rate, FourStrokePreset selecte
|
||||
c.intake_valve_lift = saab ? .0085f : .008f;
|
||||
c.exhaust_valve_diameter = saab ? .028f : .026f;
|
||||
c.exhaust_valve_lift = saab ? .008f : .0075f;
|
||||
if (v8) {
|
||||
c.intake_valve_diameter = .045f; c.exhaust_valve_diameter = .037f;
|
||||
c.intake_valve_lift = .010f; c.exhaust_valve_lift = .010f;
|
||||
}
|
||||
c.rev_limit_enabled = true;
|
||||
c.rev_limit_rpm = ecu.rev_limit_rpm;
|
||||
c.rev_limit_hysteresis = 250;
|
||||
c.rev_limit_cut_time = .020f;
|
||||
c.ecu = &ecu;
|
||||
c.initialize(crankshaft);
|
||||
if (saab) {
|
||||
if (saab || v8) {
|
||||
// Initialize each phased cylinder at its actual piston position.
|
||||
c.volume = c.compute_volume(c.phase);
|
||||
c.air_mass = ambient_density * c.volume;
|
||||
@@ -80,6 +131,7 @@ FourStrokeScenario::FourStrokeScenario(int sample_rate, FourStrokePreset selecte
|
||||
solver_.add_component(intake_atmosphere);
|
||||
solver_.add_component(intake_plenum);
|
||||
solver_.add_component(exhaust_collector);
|
||||
if (v8) solver_.add_component(second_bank_collector);
|
||||
if (turbo_enabled) {
|
||||
turbo = std::make_unique<Turbocharger>(saab ? TurboConfig::gt17() : TurboConfig{});
|
||||
ecu.boost_control_enabled = saab;
|
||||
@@ -93,7 +145,8 @@ FourStrokeScenario::FourStrokeScenario(int sample_rate, FourStrokePreset selecte
|
||||
boundaries_.add_orifice(throttle_port, 0, false, throttle_area_, .7f);
|
||||
for (int i = 0; i < count; ++i) {
|
||||
auto& feed = intake_plenum.create_port();
|
||||
auto& collector_in = exhaust_collector.create_port();
|
||||
// 1--4 and 5--8 are separate physical banks for this firing order.
|
||||
auto& collector_in = (v8 && i >= 4 ? second_bank_collector : exhaust_collector).create_port();
|
||||
solver_.add_component(*engines[i]);
|
||||
boundaries_.add_orifice(feed, 1+i, true, runner_area_);
|
||||
boundaries_.add_orifice(engines[i]->intake, 1+i, false, intake_valve_areas_[i]);
|
||||
@@ -104,11 +157,16 @@ FourStrokeScenario::FourStrokeScenario(int sample_rate, FourStrokePreset selecte
|
||||
boundaries_.add_orifice(collector_out, 1+2*count, true, tail_area_);
|
||||
const int exhaust_exit = turbo_enabled ? 3+2*count : 1+2*count;
|
||||
boundaries_.add_open_end(exhaust_exit, false, 101325, pipes[exhaust_exit].area);
|
||||
if (v8) {
|
||||
auto& outlet = second_bank_collector.create_port();
|
||||
boundaries_.add_orifice(outlet, 18, true, tail_area_);
|
||||
boundaries_.add_open_end(18, false, 101325, pipes[18].area);
|
||||
}
|
||||
}
|
||||
|
||||
AudioFrame FourStrokeScenario::process() {
|
||||
const std::lock_guard guard(mutex_);
|
||||
step_motorcycle(crankshaft, dt_, preset == FourStrokePreset::SaabB205 ? 200.f : 30.f);
|
||||
step_motorcycle(crankshaft, dt_, preset == FourStrokePreset::AmericanV8 ? 300.f : preset == FourStrokePreset::SaabB205 ? 200.f : 30.f);
|
||||
ecu.step(dt_, throttle, crankshaft.omega * 60 / (2*pi));
|
||||
if (turbo) {
|
||||
ecu.control_boost(dt_, throttle, crankshaft.omega * 60/(2*pi), intake_plenum.pressure(), turbo->compressor_outlet.pressure());
|
||||
@@ -122,12 +180,15 @@ AudioFrame FourStrokeScenario::process() {
|
||||
intake_valve_areas_[i] = c.valve_area(degrees, true);
|
||||
exhaust_valve_areas_[i] = c.valve_area(degrees, false);
|
||||
}
|
||||
const float angle = ecu.throttle_opening() * pi * .5f;
|
||||
throttle_area_ = area_for_diameter(preset == FourStrokePreset::SaabB205 ? .052f : .026f) * (1-std::cos(angle));
|
||||
const float opening = ecu.throttle_opening();
|
||||
const float angle = opening * pi * .5f;
|
||||
throttle_area_ = area_for_diameter(preset == FourStrokePreset::AmericanV8 ? .065f : preset == FourStrokePreset::SaabB205 ? .052f : .026f) * (1-std::cos(angle));
|
||||
solver_.step(dt_);
|
||||
intake_flow = intake_atmosphere.port.mass_flow;
|
||||
exhaust_flow = boundaries_.open_end_mass_flow(0);
|
||||
const auto rendered = audio_.process(intake_flow, exhaust_flow);
|
||||
const float other_exhaust = preset == FourStrokePreset::AmericanV8 ? boundaries_.open_end_mass_flow(1) : 0.f;
|
||||
const auto rendered = audio_.process(intake_flow, exhaust_flow, other_exhaust);
|
||||
exhaust_flow += other_exhaust;
|
||||
const float mechanical = mechanical_sound_enabled
|
||||
? mechanical_sound_.process(crankshaft, cylinder1, load) * mechanical_mix : 0;
|
||||
last_mix_l = rendered.left + mechanical;
|
||||
@@ -222,7 +283,8 @@ ScenarioSnapshot FourStrokeScenario::snapshot() const {
|
||||
result.mechanical_sound_enabled = mechanical_sound_enabled;
|
||||
result.cylinder_count = cylinder_count();
|
||||
result.rev_limit_rpm = ecu.rev_limit_rpm;
|
||||
const std::array<const FourStrokeCylinder*, 4> engines{&cylinder1, &cylinder2, &cylinder3, &cylinder4};
|
||||
const std::array<const FourStrokeCylinder*, 8> engines{&cylinder1, &cylinder2, &cylinder3, &cylinder4,
|
||||
&cylinder5, &cylinder6, &cylinder7, &cylinder8};
|
||||
for (int i = 0; i < cylinder_count(); ++i) {
|
||||
result.cylinder_pressures[i] = engines[i]->pressure;
|
||||
result.piston_fractions[i] = (engines[i]->volume-engines[i]->clearance_volume())/engines[i]->swept_volume();
|
||||
@@ -239,6 +301,20 @@ float FourStrokeScenario::area_for_diameter(float diameter) {
|
||||
|
||||
std::vector<Pipe1D> FourStrokeScenario::create_pipes(FourStrokePreset preset, bool turbo_enabled) {
|
||||
const bool saab = preset == FourStrokePreset::SaabB205;
|
||||
if (preset == FourStrokePreset::AmericanV8) {
|
||||
// One inlet/throttle -> shared plenum -> short individual runners.
|
||||
// Independent exhaust banks. Cells stay long enough to avoid routine
|
||||
// extra CFL substeps during high-vacuum coast-down.
|
||||
std::vector<Pipe1D> result;
|
||||
result.emplace_back(area_for_diameter(.070f), .18f, 3);
|
||||
for (int i = 0; i < 8; ++i)
|
||||
result.emplace_back(area_for_diameter(.038f), .12f, 2);
|
||||
for (int i = 0; i < 8; ++i)
|
||||
result.emplace_back(area_for_diameter(.040f), .60f + .06f*(i%4), 12);
|
||||
result.emplace_back(area_for_diameter(.050f), 1.8f, 36);
|
||||
result.emplace_back(area_for_diameter(.050f), 1.95f, 39);
|
||||
return result;
|
||||
}
|
||||
const int count = saab ? 4 : 2;
|
||||
std::vector<Pipe1D> result;
|
||||
result.reserve(4+2*count);
|
||||
|
||||
Reference in New Issue
Block a user