Python module serving as an interface client to interact with the Unreal Engine implementation of SonoTraceUE.
You can install the Python client from pip with pip install sonotraceuepy
From source clone this repository and run:
pip install -e .
# or, for the optional interactive 3D PyVista world-scatter plot:
pip install -e ".[pyvista]"- Unreal Engine with SonoTraceUE Plugin v3.1.0 or higher.
- Python 3.12 or higher
- numpy
- scipy
- matplotlib
sonotraceuepy coordinate system:
- Right-handed
- X: Forward, Y: Right, Z: Up
- Units: meters
Unreal Engine coordinate system:
- Left-handed, Z-up
- Units: centimeters
Conversions happen transparently via transforms.unreal_position_to_local/local_position_to_unreal (positions) and transforms.unreal_pose_to_matrix/matrix_to_unreal_pose.
import sonotraceuepy as stp
stp.configure_logging("INFO")
interface = stp.Interface("localhost", 9098)
settings = interface.receive_settings()
settings.prepare_ir_and_signal_generation(
number_of_samples_ir_filter=256,
enable_pattern_sum=True,
ir_filter_gauss_alpha=5,
number_of_ir_samples=18000,
approximate_ir_cut_db=-90,
enable_approximate_ir=False,
use_base_kernels=True,
)
while True:
if not interface.trigger_measurement():
break
data, data_type = interface.receive_data()
if data_type != 1: # not a Measurement
break
measurement = data
measurement.impulse_responses = stp.synthesize_ir_from_points(
measurement.reflected_points,
sample_rate=settings.sample_rate,
number_of_samples_ir_filter=settings.number_of_samples_ir_filter,
frequencies=settings.frequencies,
ir_filter_gauss_alpha=settings.ir_filter_gauss_alpha,
number_of_ir_samples=settings.number_of_ir_samples,
number_of_emitters=measurement.number_of_emitters,
number_of_receivers=measurement.number_of_receivers,
approximate_ir_cut_db=settings.approximate_ir_cut_db,
enable_approximate_ir=settings.enable_approximate_ir,
speed_of_sound=settings.speed_of_sound,
)
measurement.receiver_signals = stp.generate_receiver_signals_from_ir(
measurement.impulse_responses, settings.emitter_signals, measurement.emitter_signal_indexes
)
stp.plot_measurement(measurement, plot_ir=True, plot_signals=True)See examples/example.py for a complete example.
TCP server that Unreal's plugin connects to.
interface = stp.Interface(server_ip="localhost", server_port=9098)Key methods: receive_settings(), trigger_measurement(override_emitter_signal_indexes=None),
receive_data(), send_data_message(data_message), set_new_sensor_world_transform(matrix, teleport=False),
set_new_sensor_relative_transform(matrix), set_new_sensor_owner_world_transform(matrix, teleport=False),
set_new_emitter_positions(indexes, positions_m, relative_transform, re_apply_offset),
set_new_receiver_positions(...), set_current_emitter_signal_indexes(indexes),
get_current_emitter_signal_indexes().
One-time simulation configuration (emitter/receiver positions, ray tracing parameters, object BRDF/material settings, frequencies, emitter signal waveforms).
Call settings.prepare_ir_and_signal_generation(...) once before synthesizing impulse responses.
Everything from one triggered acoustic measurement: sensor_pose/owner_pose/emitter_poses/ receiver_poses (4x4 matrices, emitter_poses/receiver_poses shaped (N, 4, 4)),
reflected_points (a ReflectedPoints structure-of-arrays), direct_path_los, specular_sub_output/diffraction_sub_output/direct_path_sub_output (each a SubOutput, present only if the plugin's component-separated output is enabled).
A structure-of-arrays (not a Python object per point, for speed): location, reflection_direction (N, 3); summed_strength, total_distance, distance_to_sensor, curvature_magnitude,
object_type_index, ray_index, bounce_index, is_hit, is_last_hit, is_specular, is_diffraction, is_direct_path, label (N,); total_distances_from_emitters,
emitter_directivities (N, E); strengths (N, E, R, F); total_distance_to_receivers (N, E, R).
synthesize_ir_from_points(reflected_points, sample_rate, number_of_samples_ir_filter, frequencies, ir_filter_gauss_alpha, number_of_ir_samples, number_of_emitters, number_of_receivers, approximate_ir_cut_db, enable_approximate_ir, speed_of_sound)→(E, R, number_of_ir_samples)impulse responses.generate_receiver_signals_from_ir(impulse_responses, emitter_signals, emitter_signal_indexes)→(R, samples)receiver signals (convolves each emitter's IR with its assigned emitter signal).generate_ir_base_kernels(sample_rate, frequencies, ir_filter_gauss_alpha, number_of_samples_ir_filter),fm_sweep(f_start, f_end, sample_rate, duration_ms, amplitude, window_percent),norm_lin,norm_log.
plot_measurement(measurement, plot_sensor=True, plot_reflected_points=True, plot_direct_path_flag=False, plot_ir=False, plot_signals=False, db_cutoff=60.0, plot_limit_around_sensor=False)- convenience all-in-one entry point, returns a dict of the figures it created.plot_world(measurement, ...)- Matplotlib 3D scatter of poses + reflected points colored by strength.plot_world_interactive(measurement, ...)- the same, but an interactive/rotatable PyVista viewer (requires the optionalpyvistaextra).plot_direct_path(measurement, ...),plot_sub_output(sub_output, title, db_cutoff),plot_impulse_responses(impulse_responses, sample_rate),plot_receiver_signal_spectrograms(receiver_signals, sample_rate).
Generic typed string/int/float channel for custom data exchange with the plugin (e.g. requesting a scene-capture image, as in examples/example.py).
message = stp.DataMessage(
type=1,
order=[stp.DataFieldType.FLOAT, stp.DataFieldType.STRING],
strings=["hello"],
integers=[],
floats=[3.14],
)
interface.send_data_message(message)This project is released under the CC-BY-NC-SA-4.0 license.