76 lines
3.6 KiB
C++
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";
|
|
}
|