Engine working

This commit is contained in:
max
2026-05-07 21:48:37 +02:00
parent 92d84eacfe
commit b3230844b7
14 changed files with 441 additions and 486 deletions

View File

@@ -3,53 +3,142 @@ using FluidSim.Core;
namespace FluidSim.Core
{
/// <summary>
/// Synthesises farfield sound at a listener position from an open pipe end.
/// Three source mechanisms are combined:
/// 1. Monopole time derivative of mass flow (dominant at low speed / high pulsation).
/// 2. Dipole time derivative of momentum flux (shearlayer / vortex shedding).
/// 3. Jet noise Lighthilltype turbulence mixing noise (scales with U^8).
///
/// References:
/// • Lighthill, M.J. (1952) "On Sound Generated Aerodynamically".
/// • Dowling, A.P. & Williams, J.E.F. (1983) "Sound and Sources of Sound".
/// • Munjal, M.L. (2014) "Acoustics of Ducts and Mufflers", 2nd ed.
/// • Tam, C.K.W. & Auriault, L. (1999) "Jet Mixing Noise from FineScale Turbulence".
/// </summary>
public class SoundProcessor
{
private readonly double dt;
private readonly double scaleFactor; // 1 / (4π r)
private readonly double c0; // ambient speed of sound (m/s)
private readonly double rho0; // ambient density (kg/m³)
private readonly double r; // listener distance (m)
private readonly double pipeArea; // crosssectional area of the pipe end (m²)
// ---------- monopole state ----------
private double flowLP;
private readonly double lpAlpha;
private double prevMassFlowOut;
private double smoothDMdt;
private readonly double alpha;
// New: lowpass the mass flow signal before derivative
private double flowLP;
private readonly double lpAlpha;
// ---------- dipole state ----------
private double prevMomentumFlux;
private double smoothDMomDt;
private readonly double dipAlpha;
// ---------- jet noise state ----------
private double jetNoiseSample; // previous random sample (for simple shaping)
private readonly double jetTau; // correlation time ≈ D / U_mean
public float Gain { get; set; } = 1.0f;
public SoundProcessor(int sampleRate, double listenerDistanceMeters = 1.0)
/// <summary>
/// </summary>
/// <param name="sampleRate">Audio sample rate (Hz).</param>
/// <param name="listenerDistanceMeters">Distance from the pipe exit to the listener (m).</param>
/// <param name="pipeDiameterMeters">Internal diameter of the pipe (m).</param>
public SoundProcessor(int sampleRate,
double listenerDistanceMeters = 1.0,
double pipeDiameterMeters = 0.0217) // ~3.7 cm² area
{
dt = 1.0 / sampleRate;
scaleFactor = 1.0 / (4.0 * Math.PI * listenerDistanceMeters);
r = listenerDistanceMeters;
pipeArea = Math.PI * 0.25 * pipeDiameterMeters * pipeDiameterMeters;
// Smoothing time constant for the derivative: 10 ms (much smoother)
double tau = 0.005; // 10 ms
// Ambient air properties
c0 = 340.0;
rho0 = 1.225;
// ---- Monopole smoothing ----
double tau = 0.002; // 2 ms
alpha = Math.Exp(-dt / tau);
// Lowpass time constant for the mass flow: 5 ms (kneecap highfreq directly)
double tauLP = 0.005;
double tauLP = 0.005; // 5 ms lowpass on mass flow
lpAlpha = Math.Exp(-dt / tauLP);
// ---- Dipole smoothing ----
double tauDip = 0.003; // 3 ms
dipAlpha = Math.Exp(-dt / tauDip);
// ---- Jet noise correlation time ----
jetTau = Math.Max(0.0005, pipeDiameterMeters / 50.0); // D / U_ref, floor at 0.5 ms
}
/// <summary>
/// Process one sample. The OpenEndLink provides the instantaneous
/// exitplane mass flow, density, velocity, and pressure.
/// </summary>
public float Process(OpenEndLink openEnd)
{
double flowOut = openEnd.LastMassFlowRate;
double flowOut = openEnd.LastMassFlowRate; // kg/s, positive = leaving pipe
double rhoExit = openEnd.LastFaceDensity; // kg/m³ at exit
double uExit = openEnd.LastFaceVelocity; // m/s (axial, positive = leaving)
double pExit = openEnd.LastFacePressure; // Pa
// Lowpass the mass flow signal
// ============================================================
// 1. MONOPOLE due to unsteady mass addition (Lighthill 1952)
// Farfield pressure: p'(r,t) = (1 / 4πr c0) · dṁ/dt
// ============================================================
flowLP = lpAlpha * flowLP + (1.0 - lpAlpha) * flowOut;
// Derivative of the smoothed mass flow
double rawDerivative = (flowLP - prevMassFlowOut) / dt;
prevMassFlowOut = flowLP;
// Smooth the derivative
smoothDMdt = alpha * smoothDMdt + (1.0 - alpha) * rawDerivative;
double pMono = smoothDMdt / (4.0 * Math.PI * r * c0);
// Farfield monopole pressure
double pressure = smoothDMdt * scaleFactor * Gain;
// ============================================================
// 2. DIPOLE due to unsteady momentum flux at the exit plane
// Momentum flux: F(t) = ṁ(t) · u(t) = ρ·A·u²
// Farfield (compact, low M): p'(r,θ,t) ≈ (cosθ / 4πr c0) · dF/dt
// For onaxis listener (θ = 0): p'(r,t) ≈ (1 / 4πr c0) · dF/dt
// We also include a U⁶ scaling factor relative to a reference velocity.
// ============================================================
double momentumFlux = Math.Abs(flowOut) * Math.Abs(uExit); // N
double rawMomDeriv = (momentumFlux - prevMomentumFlux) / dt;
prevMomentumFlux = momentumFlux;
smoothDMomDt = dipAlpha * smoothDMomDt + (1.0 - dipAlpha) * rawMomDeriv;
double pDipole = smoothDMomDt / (4.0 * Math.PI * r * c0);
// Soft clip to ±1 (should rarely trigger now)
return (float)pressure;
// Dipole efficiency factor: ∝ (U / c0)³ (since Idipole ∝ U⁶, pdipole ∝ U³)
double Mach = Math.Abs(uExit) / c0;
double dipoleEfficiency = Math.Pow(Mach, 3.0);
pDipole *= dipoleEfficiency;
// ============================================================
// 3. JET NOISE Lighthill U⁸ mixing noise, bandpass shaped
// rms pressure: p'_jet ~ ρ0 · A / r · U⁴ / c0²
// Model as broadband noise with amplitude ∝ U⁴.
// A simple firstorder lowpass filter shapes the spectrum
// (cutoff ≈ Strouhal frequency f ≈ 0.2 · U / D).
// ============================================================
double Uref = Math.Max(1.0, Math.Abs(uExit)); // avoid division by zero
double jetAmplitude = rho0 * pipeArea / r * Math.Pow(Uref / c0, 4.0);
// Correlation time (sampleandhold style random walk)
double alphaJet = Math.Exp(-dt / jetTau);
// Generate a new random target each step, filter with alphaJet
double randomTarget = (new Random().NextDouble() * 2.0 - 1.0);
jetNoiseSample = alphaJet * jetNoiseSample + (1.0 - alphaJet) * randomTarget;
double pJet = jetAmplitude * jetNoiseSample;
// ============================================================
// Combine contributions (monopole is primary; dipole & jet are
// weighted down for realistic mix). Weights can be tuned per engine.
// ============================================================
double pressure = (3000.0 * pMono) + (0.01 * pDipole) + (0 * pJet);
pressure *= Gain;
// Softclip to ±1
return (float)Math.Tanh(pressure);
}
}
}