use crate::math::constants;
pub fn wave_speed(frequency: f64, wavelength: f64) -> f64 {
frequency * wavelength
}
pub fn wavelength(speed: f64, frequency: f64) -> f64 {
assert!(frequency > 0.0, "frequency must be positive");
speed / frequency
}
pub fn frequency(speed: f64, wavelength: f64) -> f64 {
assert!(wavelength > 0.0, "wavelength must be positive");
speed / wavelength
}
pub fn period(frequency: f64) -> f64 {
assert!(frequency > 0.0, "frequency must be positive");
1.0 / frequency
}
pub fn angular_frequency(frequency: f64) -> f64 {
2.0 * constants::PI * frequency
}
pub fn wave_number(wavelength: f64) -> f64 {
assert!(wavelength > 0.0, "wavelength must be positive");
2.0 * constants::PI / wavelength
}
pub fn wave_displacement(
amplitude: f64,
wave_number: f64,
x: f64,
angular_freq: f64,
t: f64,
phase: f64,
) -> f64 {
amplitude * (wave_number * x - angular_freq * t + phase).sin()
}
pub fn wave_energy_density(amplitude: f64, frequency: f64, linear_density: f64) -> f64 {
0.5 * linear_density * (2.0 * constants::PI * frequency).powi(2) * amplitude * amplitude
}
pub fn wave_intensity(power: f64, area: f64) -> f64 {
assert!(area > 0.0, "area must be positive");
power / area
}
pub fn spherical_wave_intensity(power: f64, distance: f64) -> f64 {
assert!(distance > 0.0, "distance must be positive");
power / (4.0 * constants::PI * distance * distance)
}
pub fn decibel_level(intensity: f64, reference_intensity: f64) -> f64 {
assert!(reference_intensity > 0.0, "reference_intensity must be positive");
assert!(intensity > 0.0, "intensity must be positive");
10.0 * (intensity / reference_intensity).log10()
}
pub fn intensity_from_decibels(decibels: f64, reference_intensity: f64) -> f64 {
reference_intensity * 10.0_f64.powf(decibels / 10.0)
}
pub fn doppler_frequency(
source_freq: f64,
wave_speed: f64,
observer_velocity: f64,
source_velocity: f64,
) -> f64 {
assert!(wave_speed + source_velocity != 0.0, "wave_speed + source_velocity must not be zero");
source_freq * (wave_speed + observer_velocity) / (wave_speed + source_velocity)
}
pub fn relativistic_doppler(source_freq: f64, beta: f64) -> f64 {
assert!(beta != 1.0, "beta must not equal 1.0");
source_freq * ((1.0 + beta) / (1.0 - beta)).sqrt()
}
pub fn mach_cone_angle(mach: f64) -> f64 {
assert!(mach > 0.0, "mach must be positive");
(1.0 / mach).asin()
}
pub fn standing_wave_frequency(harmonic: u32, wave_speed: f64, length: f64) -> f64 {
assert!(length > 0.0, "length must be positive");
harmonic as f64 * wave_speed / (2.0 * length)
}
pub fn string_fundamental(length: f64, tension: f64, linear_density: f64) -> f64 {
assert!(length > 0.0, "length must be positive");
assert!(linear_density > 0.0, "linear_density must be positive");
(1.0 / (2.0 * length)) * (tension / linear_density).sqrt()
}
pub fn open_pipe_frequency(harmonic: u32, sound_speed: f64, length: f64) -> f64 {
assert!(length > 0.0, "length must be positive");
harmonic as f64 * sound_speed / (2.0 * length)
}
pub fn closed_pipe_frequency(odd_harmonic: u32, sound_speed: f64, length: f64) -> f64 {
assert!(length > 0.0, "length must be positive");
odd_harmonic as f64 * sound_speed / (4.0 * length)
}
pub fn beat_frequency(f1: f64, f2: f64) -> f64 {
(f1 - f2).abs()
}
pub fn superposition_amplitude(a1: f64, a2: f64, phase_diff: f64) -> f64 {
(a1 * a1 + a2 * a2 + 2.0 * a1 * a2 * phase_diff.cos()).sqrt()
}
pub fn speed_of_sound_gas(gamma: f64, temperature: f64, molar_mass: f64) -> f64 {
assert!(molar_mass > 0.0, "molar_mass must be positive");
(gamma * constants::R * temperature / molar_mass).sqrt()
}
pub fn wave_speed_string(tension: f64, linear_density: f64) -> f64 {
assert!(linear_density > 0.0, "linear_density must be positive");
(tension / linear_density).sqrt()
}
pub fn phase_velocity(angular_freq: f64, wave_number: f64) -> f64 {
assert!(wave_number > 0.0, "wave_number must be positive");
angular_freq / wave_number
}
pub fn group_velocity(d_omega: f64, d_k: f64) -> f64 {
assert!(d_k != 0.0, "d_k must not be zero");
d_omega / d_k
}
pub fn wave_impedance(density: f64, wave_speed: f64) -> f64 {
density * wave_speed
}
pub fn reflection_coefficient(z1: f64, z2: f64) -> f64 {
assert!(z1 + z2 != 0.0, "z1 + z2 must not be zero");
(z2 - z1) / (z2 + z1)
}
pub fn transmission_coefficient(z1: f64, z2: f64) -> f64 {
assert!(z1 + z2 != 0.0, "z1 + z2 must not be zero");
2.0 * z2 / (z1 + z2)
}
pub fn intensity_reflection(z1: f64, z2: f64) -> f64 {
let r = reflection_coefficient(z1, z2);
r * r
}
pub fn intensity_transmission(z1: f64, z2: f64) -> f64 {
assert!(z1 + z2 != 0.0, "z1 + z2 must not be zero");
let sum = z1 + z2;
4.0 * z1 * z2 / (sum * sum)
}
pub fn attenuated_amplitude(initial: f64, attenuation_coeff: f64, distance: f64) -> f64 {
initial * (-attenuation_coeff * distance).exp()
}
pub fn absorption_coefficient_from_db(db_per_meter: f64) -> f64 {
db_per_meter * 10.0_f64.ln() / 20.0
}
pub fn penetration_depth(attenuation_coeff: f64) -> f64 {
assert!(attenuation_coeff > 0.0, "attenuation_coeff must be positive");
1.0 / attenuation_coeff
}
pub fn sound_pressure_level(pressure: f64, reference: f64) -> f64 {
assert!(reference > 0.0, "reference must be positive");
assert!(pressure > 0.0, "pressure must be positive");
20.0 * (pressure / reference).log10()
}
pub fn acoustic_power(pressure: f64, area: f64, impedance: f64) -> f64 {
assert!(impedance > 0.0, "impedance must be positive");
pressure * pressure * area / impedance
}
pub fn resonant_frequency_tube_open(length: f64, speed: f64) -> f64 {
assert!(length > 0.0, "length must be positive");
speed / (2.0 * length)
}
pub fn resonant_frequency_tube_closed(length: f64, speed: f64) -> f64 {
assert!(length > 0.0, "length must be positive");
speed / (4.0 * length)
}
pub fn acoustic_intensity_from_pressure(pressure: f64, impedance: f64) -> f64 {
assert!(impedance > 0.0, "impedance must be positive");
pressure * pressure / impedance
}
pub fn wavelength_in_medium(frequency: f64, speed_in_medium: f64) -> f64 {
assert!(frequency > 0.0, "frequency must be positive");
speed_in_medium / frequency
}
pub fn p_wave_speed(bulk_modulus: f64, shear_modulus: f64, density: f64) -> f64 {
assert!(density > 0.0, "density must be positive");
((bulk_modulus + 4.0 * shear_modulus / 3.0) / density).sqrt()
}
pub fn s_wave_speed(shear_modulus: f64, density: f64) -> f64 {
assert!(density > 0.0, "density must be positive");
(shear_modulus / density).sqrt()
}
pub fn rayleigh_wave_speed(shear_speed: f64, poisson_ratio: f64) -> f64 {
assert!(1.0 + poisson_ratio != 0.0, "1 + poisson_ratio must not be zero");
shear_speed * (0.862 + 1.14 * poisson_ratio) / (1.0 + poisson_ratio)
}
pub fn love_wave_speed_range(
shear_speed_layer: f64,
shear_speed_halfspace: f64,
) -> (f64, f64) {
let low = shear_speed_layer.min(shear_speed_halfspace);
let high = shear_speed_layer.max(shear_speed_halfspace);
(low, high)
}
pub fn path_difference_constructive(order: i32, wavelength: f64) -> f64 {
order as f64 * wavelength
}
pub fn path_difference_destructive(order: i32, wavelength: f64) -> f64 {
(order as f64 + 0.5) * wavelength
}
pub fn fraunhofer_single_slit_intensity(
angle: f64,
slit_width: f64,
wavelength: f64,
) -> f64 {
assert!(wavelength > 0.0, "wavelength must be positive");
let beta = constants::PI * slit_width * angle.sin() / wavelength;
if beta.abs() < 1e-12 {
return 1.0;
}
let sinc = beta.sin() / beta;
sinc * sinc
}
pub fn airy_disk_radius(wavelength: f64, focal_length: f64, aperture: f64) -> f64 {
assert!(aperture > 0.0, "aperture must be positive");
1.22 * wavelength * focal_length / aperture
}
pub fn fresnel_number(aperture: f64, distance: f64, wavelength: f64) -> f64 {
assert!(wavelength > 0.0, "wavelength must be positive");
assert!(distance > 0.0, "distance must be positive");
aperture * aperture / (wavelength * distance)
}
#[cfg(test)]
mod tests {
use super::*;
fn approx(a: f64, b: f64, tol: f64) -> bool {
(a - b).abs() < tol
}
fn approx_rel(a: f64, b: f64, tol: f64) -> bool {
((a - b) / b).abs() < tol
}
#[test]
fn test_wave_speed() {
assert!(approx(wave_speed(440.0, 0.773), 340.12, 0.01));
}
#[test]
fn test_doppler_approaching() {
let f = doppler_frequency(440.0, 340.0, 0.0, -30.0);
assert!(f > 440.0); }
#[test]
fn test_doppler_receding() {
let f = doppler_frequency(440.0, 340.0, 0.0, 30.0);
assert!(f < 440.0); }
#[test]
fn test_decibel_level() {
let db = decibel_level(1e-10, 1e-12);
assert!(approx(db, 20.0, 0.01));
}
#[test]
fn test_standing_wave() {
let f = standing_wave_frequency(2, 340.0, 1.0);
assert!(approx(f, 340.0, 1e-6));
}
#[test]
fn test_beat_frequency() {
assert!(approx(beat_frequency(440.0, 442.0), 2.0, 1e-9));
}
#[test]
fn test_superposition_constructive() {
let a = superposition_amplitude(1.0, 1.0, 0.0);
assert!(approx(a, 2.0, 1e-9));
}
#[test]
fn test_superposition_destructive() {
let a = superposition_amplitude(1.0, 1.0, constants::PI);
assert!(approx(a, 0.0, 1e-9));
}
#[test]
fn test_speed_of_sound_air() {
let v = speed_of_sound_gas(1.4, 293.0, 0.029);
assert!(approx_rel(v, 343.0, 0.02));
}
#[test]
fn test_spherical_wave_intensity() {
let i = spherical_wave_intensity(100.0, 10.0);
assert!(approx_rel(i, 0.0796, 0.01));
}
#[test]
fn test_phase_velocity() {
assert!(approx(phase_velocity(100.0, 5.0), 20.0, 1e-9));
}
#[test]
fn test_group_velocity() {
assert!(approx(group_velocity(10.0, 2.0), 5.0, 1e-9));
}
#[test]
fn test_wave_impedance() {
let z = wave_impedance(1.225, 343.0);
assert!(approx(z, 420.175, 0.001));
}
#[test]
fn test_reflection_coefficient() {
assert!(approx(reflection_coefficient(400.0, 400.0), 0.0, 1e-9));
assert!(approx(reflection_coefficient(400.0, 1600.0), 0.6, 1e-9));
}
#[test]
fn test_transmission_coefficient() {
assert!(approx(transmission_coefficient(400.0, 400.0), 1.0, 1e-9));
assert!(approx(transmission_coefficient(400.0, 1600.0), 1.6, 1e-9));
}
#[test]
fn test_intensity_reflection() {
assert!(approx(intensity_reflection(400.0, 1600.0), 0.36, 1e-9));
}
#[test]
fn test_intensity_transmission() {
assert!(approx(intensity_transmission(400.0, 1600.0), 0.64, 1e-9));
}
#[test]
fn test_intensity_reflection_transmission_sum() {
let r = intensity_reflection(420.0, 1500.0);
let t = intensity_transmission(420.0, 1500.0);
assert!(approx(r + t, 1.0, 1e-9));
}
#[test]
fn test_attenuated_amplitude() {
assert!(approx(attenuated_amplitude(5.0, 0.1, 0.0), 5.0, 1e-9));
assert!(approx(attenuated_amplitude(5.0, 1.0, 1.0), 1.83940, 0.001));
}
#[test]
fn test_absorption_coefficient_from_db() {
let alpha = absorption_coefficient_from_db(1.0);
assert!(approx(alpha, 0.115129, 1e-4));
}
#[test]
fn test_penetration_depth() {
assert!(approx(penetration_depth(0.5), 2.0, 1e-9));
}
#[test]
fn test_sound_pressure_level() {
let spl = sound_pressure_level(1.0, 2e-5);
assert!(approx(spl, 93.979, 0.01));
}
#[test]
fn test_acoustic_power() {
assert!(approx(acoustic_power(2.0, 0.5, 400.0), 0.005, 1e-9));
}
#[test]
fn test_resonant_frequency_tube_open() {
assert!(approx(resonant_frequency_tube_open(0.5, 340.0), 340.0, 1e-9));
}
#[test]
fn test_resonant_frequency_tube_closed() {
assert!(approx(resonant_frequency_tube_closed(0.5, 340.0), 170.0, 1e-9));
}
#[test]
fn test_acoustic_intensity_from_pressure() {
assert!(approx(acoustic_intensity_from_pressure(2.0, 400.0), 0.01, 1e-9));
}
#[test]
fn test_wavelength_in_medium() {
assert!(approx(wavelength_in_medium(1000.0, 1500.0), 1.5, 1e-9));
}
#[test]
fn test_p_wave_speed() {
let vp = p_wave_speed(40e9, 30e9, 2700.0);
assert!(approx_rel(vp, 5443.3, 0.001));
}
#[test]
fn test_s_wave_speed() {
let vs = s_wave_speed(30e9, 2700.0);
assert!(approx_rel(vs, 3333.3, 0.001));
}
#[test]
fn test_p_wave_faster_than_s_wave() {
let vp = p_wave_speed(40e9, 30e9, 2700.0);
let vs = s_wave_speed(30e9, 2700.0);
assert!(vp > vs);
}
#[test]
fn test_rayleigh_wave_speed() {
let vr = rayleigh_wave_speed(3000.0, 0.25);
assert!(approx(vr, 2752.8, 0.1));
}
#[test]
fn test_rayleigh_slower_than_shear() {
let vs = 3000.0;
let vr = rayleigh_wave_speed(vs, 0.25);
assert!(vr < vs);
}
#[test]
fn test_love_wave_speed_range() {
let (low, high) = love_wave_speed_range(2000.0, 3500.0);
assert!(approx(low, 2000.0, 1e-9));
assert!(approx(high, 3500.0, 1e-9));
}
#[test]
fn test_love_wave_speed_range_reversed() {
let (low, high) = love_wave_speed_range(3500.0, 2000.0);
assert!(approx(low, 2000.0, 1e-9));
assert!(approx(high, 3500.0, 1e-9));
}
#[test]
fn test_path_difference_constructive() {
assert!(approx(path_difference_constructive(2, 0.5), 1.0, 1e-9));
assert!(approx(path_difference_constructive(0, 0.5), 0.0, 1e-9));
}
#[test]
fn test_path_difference_destructive() {
assert!(approx(path_difference_destructive(0, 0.5), 0.25, 1e-9));
assert!(approx(path_difference_destructive(1, 0.5), 0.75, 1e-9));
}
#[test]
fn test_fraunhofer_central_maximum() {
assert!(approx(fraunhofer_single_slit_intensity(0.0, 1e-3, 500e-9), 1.0, 1e-9));
}
#[test]
fn test_fraunhofer_first_minimum() {
let a = 1e-3;
let lambda = 500e-9;
let theta = (lambda as f64 / a as f64).asin();
let intensity = fraunhofer_single_slit_intensity(theta, a, lambda);
assert!(approx(intensity, 0.0, 1e-6));
}
#[test]
fn test_fraunhofer_symmetry() {
let a = 1e-3;
let lambda = 500e-9;
let theta = 0.01;
let pos = fraunhofer_single_slit_intensity(theta, a, lambda);
let neg = fraunhofer_single_slit_intensity(-theta, a, lambda);
assert!(approx(pos, neg, 1e-12));
}
#[test]
fn test_airy_disk_radius() {
let r = airy_disk_radius(500e-9, 0.1, 0.05);
assert!(approx(r, 1.22e-6, 1e-12));
}
#[test]
fn test_fresnel_number() {
let f = fresnel_number(1e-3, 1.0, 500e-9);
assert!(approx(f, 2.0, 1e-6));
}
#[test]
fn test_wavelength() {
let l = wavelength(340.0, 440.0);
assert!(approx_rel(l, 0.7727, 0.01));
}
#[test]
fn test_frequency() {
let f = frequency(340.0, 0.773);
assert!(approx_rel(f, 440.0, 0.01));
}
#[test]
fn test_period() {
let t = period(500.0);
assert!(approx(t, 0.002, 1e-9));
}
#[test]
fn test_angular_frequency() {
let omega = angular_frequency(1.0);
assert!(approx(omega, 6.283185307, 1e-6));
}
#[test]
fn test_wave_number() {
let k = wave_number(1.0);
assert!(approx(k, 6.283185307, 1e-6));
}
#[test]
fn test_wave_displacement() {
let y = wave_displacement(1.0, 1.0, 0.0, 1.0, 0.0, constants::PI / 2.0);
assert!(approx(y, 1.0, 1e-9));
}
#[test]
fn test_wave_energy_density() {
let e = wave_energy_density(0.05, 100.0, 0.01);
assert!(approx_rel(e, 4.935, 0.001));
}
#[test]
fn test_wave_intensity() {
let i = wave_intensity(100.0, 4.0);
assert!(approx(i, 25.0, 1e-9));
}
#[test]
fn test_intensity_from_decibels() {
let i = intensity_from_decibels(20.0, 1e-12);
assert!(approx_rel(i, 1e-10, 1e-6));
}
#[test]
fn test_relativistic_doppler() {
let f_no_shift = relativistic_doppler(440.0, 0.0);
assert!(approx(f_no_shift, 440.0, 1e-9));
let f_approach = relativistic_doppler(440.0, 0.5);
assert!(approx_rel(f_approach, 762.102, 1e-4));
}
#[test]
fn test_mach_cone_angle() {
let theta = mach_cone_angle(2.0);
assert!(approx(theta, 0.523599, 1e-4));
}
#[test]
fn test_string_fundamental() {
let f = string_fundamental(0.65, 100.0, 0.01);
assert!(approx_rel(f, 76.923, 0.001));
}
#[test]
fn test_open_pipe_frequency() {
let f = open_pipe_frequency(1, 340.0, 0.85);
assert!(approx(f, 200.0, 1e-9));
}
#[test]
fn test_closed_pipe_frequency() {
let f = closed_pipe_frequency(1, 340.0, 0.85);
assert!(approx(f, 100.0, 1e-9));
}
#[test]
fn test_wave_speed_string() {
let v = wave_speed_string(100.0, 0.01);
assert!(approx(v, 100.0, 1e-9));
}
}