Files
FluidSimCpp/tests/b205_regression.cpp

76 lines
3.6 KiB
C++

#include "FluidSim/Scenario.hpp"
#include <algorithm>
#include <chrono>
#include <cmath>
#include <iostream>
#include <stdexcept>
#include <vector>
using namespace fluidsim::port;
namespace {
void check(bool condition, const char* message) {
if (!condition) throw std::runtime_error(message);
}
}
int main() {
SaabB205Scenario s;
const auto cylinders = s.cylinders();
check(s.cylinder_count() == 4 && s.pipes.size() == 10, "B205 topology is not four-cylinder");
check(std::abs(4*s.cylinder1.swept_volume()*1e6f-1984.9f) < 1, "B205 displacement incorrect");
check(s.vehicle.gear_ratios.size() == 5 && s.vehicle.mass == 1675, "wagon configuration incorrect");
for (int i = 0; i < 4; ++i) {
check(cylinders[i]->compression == 8.8f && cylinders[i]->intake_valve_count == 2 &&
cylinders[i]->exhaust_valve_count == 2, "B205 compression/valve count incorrect");
}
s.set_controls(.5f, 0, 1);
std::array<bool, 4> burning{};
std::vector<int> firing_order;
float peak = 0;
const auto started = std::chrono::steady_clock::now();
const auto step = [&] {
const auto audio = s.process();
check(std::isfinite(audio.left) && std::isfinite(audio.right), "nonfinite B205 audio");
peak = std::max({peak, std::abs(audio.left), std::abs(audio.right)});
for (const auto* c : cylinders)
check(std::isfinite(c->pressure) && c->pressure > 0 && c->total_mass() > 0,
"nonphysical B205 cylinder");
};
// Prescribed-speed absorbing dyno isolates phasing from acceleration.
for (int sample = 0; sample < 44100; ++sample) {
s.crankshaft.omega = 2000 * 2*pi/60;
step();
for (int i = 0; i < 4; ++i) {
if (sample > 22050 && cylinders[i]->combustion_active && !burning[i])
firing_order.push_back(i);
burning[i] = cylinders[i]->combustion_active;
}
}
check(firing_order.size() >= 28 && firing_order.size() <= 36, "B205 did not fire twice per revolution");
constexpr std::array<int, 4> next{2, 0, 3, 1};
for (std::size_t i = 1; i < firing_order.size(); ++i)
check(firing_order[i] == next[firing_order[i-1]], "B205 firing order is not 1-3-4-2");
s.set_first_gear();
for (int sample = 0; sample < 4*44100; ++sample) {
const float clutch = std::max(0.f, 1-sample/(.8f*44100));
s.set_controls(.6f, 0, clutch);
step();
}
check(s.vehicle.speed > 1 && s.vehicle.current_gear == 1, "B205 failed to launch wagon");
const float speed = s.vehicle.speed*3.6f;
for (int i = 0; i < 8; ++i) s.shift_up();
check(s.vehicle.current_gear == 5, "wagon has a phantom sixth gear");
check(std::abs(s.vehicle.total_gear_ratio()-2.67f) < 1e-5f, "wagon top overall ratio incorrect");
s.set_neutral();
s.set_controls(0, 0, 1);
for (int sample = 0; sample < 3*44100; ++sample) step();
const auto final = s.snapshot();
check(final.rpm > 300 && final.rpm < 2500, "B205 did not recover toward idle");
check(final.cylinder_count == 4 && !final.pipe_states[9].empty(), "B205 desktop snapshot incomplete");
for (const auto& pipe : s.pipes)
check(pipe.positivity_repairs == 0, "B205 required pipe positivity repairs");
const double wall = std::chrono::duration<double>(std::chrono::steady_clock::now()-started).count();
std::cout << "B205 passed: firing events=" << firing_order.size() << ", wagon speed=" << speed
<< " km/h, recovered RPM=" << final.rpm << ", audio peak=" << peak
<< ", wall seconds=" << wall << " for 8 simulated seconds\n";
}