103 lines
5.3 KiB
C++
103 lines
5.3 KiB
C++
#include "FluidSim/TwoStrokeScenario.hpp"
|
|
#include "FluidSim/DynoCurve.hpp"
|
|
#include <algorithm>
|
|
#include <cmath>
|
|
#include <iostream>
|
|
#include <stdexcept>
|
|
|
|
using namespace fluidsim::port;
|
|
|
|
int main() {
|
|
TwoStrokeScenario scenario;
|
|
bool reed_open_seen = false, reed_closed_seen = false, above_old_limit = false;
|
|
DynoCurve dyno;
|
|
float peak = 0, maximum_rpm = 0;
|
|
std::uint64_t release_firings = 0, one_second_coast_firings = 0;
|
|
float release_rpm = 0;
|
|
for (int sample = 0; sample < 14 * 44100; ++sample) {
|
|
scenario.set_throttle(sample >= 2 * 44100 && sample < 6 * 44100 ? .6f : 0.f);
|
|
const auto audio = scenario.process();
|
|
if (sample == 6 * 44100) {
|
|
release_firings = scenario.cylinder.combustion_count();
|
|
release_rpm = scenario.snapshot().rpm;
|
|
} else if (sample == 7 * 44100) {
|
|
one_second_coast_firings = scenario.cylinder.combustion_count() - release_firings;
|
|
}
|
|
if (!std::isfinite(audio.left) || !std::isfinite(audio.right))
|
|
throw std::runtime_error("Nonfinite two-stroke audio");
|
|
peak = std::max({peak, std::abs(audio.left), std::abs(audio.right)});
|
|
if (sample % 44 != 0)
|
|
continue;
|
|
const auto state = scenario.snapshot();
|
|
if (!std::isfinite(state.rpm) || !(state.cylinder_pressure > 0) ||
|
|
!(state.crankcase_pressure > 0) || !(state.intake_pressure > 0))
|
|
throw std::runtime_error("Invalid two-stroke physical state");
|
|
if (scenario.ecu.rev_limited() || scenario.cylinder.rev_limit_active)
|
|
throw std::runtime_error("Two-stroke rev limiter is active");
|
|
if (sample >= 2 * 44100 && sample < 6 * 44100) {
|
|
if (state.fuel_cut)
|
|
throw std::runtime_error("Two-stroke cut fuel with the pedal open");
|
|
above_old_limit |= state.rpm > 10200;
|
|
}
|
|
if (sample >= 6 * 44100 && sample < 7 * 44100 && state.rpm > 5000 && state.fuel_cut)
|
|
throw std::runtime_error("Carbureted two-stroke cut fuel on overrun");
|
|
dyno.update(state.rpm, scenario.crankshaft.average_power / 1000,
|
|
scenario.crankshaft.average_torque);
|
|
reed_open_seen |= state.reed_open;
|
|
reed_closed_seen |= !state.reed_open;
|
|
maximum_rpm = std::max(maximum_rpm, state.rpm);
|
|
}
|
|
const auto final = scenario.snapshot();
|
|
if (!above_old_limit)
|
|
throw std::runtime_error("Two-stroke did not exceed the old rev limit: peak=" +
|
|
std::to_string(maximum_rpm) + ", idle=" + std::to_string(final.rpm));
|
|
if (!reed_open_seen || !reed_closed_seen)
|
|
throw std::runtime_error("Reed did not open and close");
|
|
if (std::abs(final.throttle-scenario.configuration.idle_slide_opening)>1e-5f)
|
|
throw std::runtime_error("Two-stroke did not return to its configured fixed carb stop");
|
|
if (final.rpm < 300 || final.rpm >= release_rpm)
|
|
throw std::runtime_error("Two-stroke failed to coast down and remain running at fixed idle stop: " +
|
|
std::to_string(final.rpm));
|
|
if (peak >= 1)
|
|
throw std::runtime_error("Two-stroke audio exceeds device range");
|
|
for (const auto &pipe : scenario.pipes)
|
|
if (pipe.positivity_repairs != 0)
|
|
throw std::runtime_error("Two-stroke required pipe positivity repairs");
|
|
// Launch the same motorcycle used by the twin, then exercise gear controls.
|
|
scenario.shift_up();
|
|
scenario.set_controls(.7f, 0, 0);
|
|
for (int sample = 0; sample < 6 * 44100; ++sample) {
|
|
const auto audio = scenario.process();
|
|
if (!std::isfinite(audio.left) || !std::isfinite(audio.right))
|
|
throw std::runtime_error("Nonfinite motorcycle launch audio");
|
|
if (scenario.ecu.rev_limited())
|
|
throw std::runtime_error("Limiter re-enabled under motorcycle load");
|
|
}
|
|
if (scenario.vehicle.current_gear != 1 || scenario.vehicle.speed < 1 ||
|
|
!std::isfinite(scenario.vehicle.kinetic_energy()) || scenario.vehicle.engagement < .9f)
|
|
throw std::runtime_error("Two-stroke motorcycle did not launch with clutch engaged");
|
|
scenario.shift_up();
|
|
if (scenario.vehicle.current_gear != 2)
|
|
throw std::runtime_error("Two-stroke did not shift up");
|
|
scenario.shift_down();
|
|
if (scenario.vehicle.current_gear != 1)
|
|
throw std::runtime_error("Two-stroke did not shift down");
|
|
scenario.set_neutral();
|
|
if (scenario.vehicle.current_gear != 0)
|
|
throw std::runtime_error("Two-stroke neutral selection failed");
|
|
bool positive_power = false, positive_torque = false;
|
|
for (const auto& point : dyno.bins()) {
|
|
positive_power |= point.power_kw > 0;
|
|
positive_torque |= point.torque_nm > 0;
|
|
}
|
|
if (!positive_power || !positive_torque)
|
|
throw std::runtime_error("Two-stroke dyno did not record power and torque");
|
|
for (const auto& pipe : scenario.pipes)
|
|
if (pipe.positivity_repairs != 0)
|
|
throw std::runtime_error("Motorcycle launch required pipe positivity repairs");
|
|
std::cout << "Two-stroke regression passed: peak=" << peak << ", maximum RPM=" << maximum_rpm
|
|
<< ", post-release RPM=" << final.rpm << ", bike speed=" << scenario.vehicle.speed * 3.6f
|
|
<< " km/h, release RPM=" << release_rpm
|
|
<< ", first coast-second firings=" << one_second_coast_firings << '\n';
|
|
}
|