TrackedSource.java
package org.hammer.audio.experimental.acoustic.tracking;
import java.util.Objects;
import org.hammer.audio.geometry.Vector2;
import org.hammer.audio.geometry.Vector3;
/**
* One acoustic source tracked over time.
*
* <p>{@code TrackedSource} carries the latest smoothed position and velocity estimate, the source
* identity ({@link #id()}) that persists across frames, the most recently observed dominant
* frequency and the frame index at which the source was last updated. The {@link #confidence()} is
* a smoothed measure in {@code [0, 1]} that decays when the source is not observed for a frame.
*/
public record TrackedSource(
int id,
double frequencyHz,
double observedFrequencyHz,
Vector2 positionMeters,
Vector2 velocityMetersPerSecond,
Vector3 velocityMetersPerSecond3d,
double radialVelocityMetersPerSecond,
double frequencyVarianceHzSquared,
double confidence,
long lastUpdatedFrameIndex,
int observationCount,
double dopplerVelocityWeight,
double radialVelocityStdDevMetersPerSecond) {
/** Create a source snapshot without explicit Doppler diagnostics. */
public TrackedSource(
int id,
double frequencyHz,
double observedFrequencyHz,
Vector2 positionMeters,
Vector2 velocityMetersPerSecond,
Vector3 velocityMetersPerSecond3d,
double radialVelocityMetersPerSecond,
double frequencyVarianceHzSquared,
double confidence,
long lastUpdatedFrameIndex,
int observationCount) {
this(
id,
frequencyHz,
observedFrequencyHz,
positionMeters,
velocityMetersPerSecond,
velocityMetersPerSecond3d,
radialVelocityMetersPerSecond,
frequencyVarianceHzSquared,
confidence,
lastUpdatedFrameIndex,
observationCount,
0.0,
0.0);
}
/** Validate fields. */
public TrackedSource {
if (id < 0) {
throw new IllegalArgumentException("id must be >= 0");
}
if (!Double.isFinite(frequencyHz) || frequencyHz < 0.0) {
throw new IllegalArgumentException("frequencyHz must be finite and >= 0");
}
if (!Double.isFinite(observedFrequencyHz) || observedFrequencyHz < 0.0) {
throw new IllegalArgumentException("observedFrequencyHz must be finite and >= 0");
}
Objects.requireNonNull(positionMeters, "positionMeters");
Objects.requireNonNull(velocityMetersPerSecond, "velocityMetersPerSecond");
Objects.requireNonNull(velocityMetersPerSecond3d, "velocityMetersPerSecond3d");
if (!Double.isFinite(radialVelocityMetersPerSecond)) {
throw new IllegalArgumentException("radialVelocityMetersPerSecond must be finite");
}
if (!Double.isFinite(frequencyVarianceHzSquared) || frequencyVarianceHzSquared < 0.0) {
throw new IllegalArgumentException("frequencyVarianceHzSquared must be finite and >= 0");
}
if (!Double.isFinite(confidence) || confidence < 0.0 || confidence > 1.0) {
throw new IllegalArgumentException("confidence must be finite and in [0,1]");
}
if (observationCount < 1) {
throw new IllegalArgumentException("observationCount must be >= 1");
}
if (!Double.isFinite(dopplerVelocityWeight)
|| dopplerVelocityWeight < 0.0
|| dopplerVelocityWeight > 1.0) {
throw new IllegalArgumentException("dopplerVelocityWeight must be finite and in [0,1]");
}
if (!Double.isFinite(radialVelocityStdDevMetersPerSecond)
|| radialVelocityStdDevMetersPerSecond < 0.0) {
throw new IllegalArgumentException(
"radialVelocityStdDevMetersPerSecond must be finite and >= 0");
}
}
}