package net.sf.openrocket.models.wind;
import java.util.Random;
import net.sf.openrocket.util.Coordinate;
import net.sf.openrocket.util.MathUtil;
import net.sf.openrocket.util.PinkNoise;
/**
* A wind simulator that generates wind speed as pink noise from a specified average wind speed
* and standard deviance. Currently the wind is always directed in the direction of the negative
* X-axis. The simulated wind is unaffected by the altitude.
*
* @author Sampo Niskanen <sampo.niskanen@iki.fi>
*/
public class PinkNoiseWindModel implements WindModel {
/** Random value with which to XOR the random seed value */
private static final int SEED_RANDOMIZATION = 0x7343AA03;
/** Pink noise alpha parameter. */
private static final double ALPHA = 5.0 / 3.0;
/** Number of poles to use in the pink noise IIR filter. */
private static final int POLES = 2;
/** The standard deviation of the generated pink noise with the specified number of poles. */
private static final double STDDEV = 2.252;
/** Time difference between random samples. */
private static final double DELTA_T = 0.05;
private double average = 0;
private double direction = Math.PI / 2; // this is an East wind
private double standardDeviation = 0;
private final int seed;
private PinkNoise randomSource = null;
private double time1;
private double value1, value2;
/**
* Construct a new wind simulation with a specific seed value.
* @param seed the seed value.
*/
public PinkNoiseWindModel(int seed) {
this.seed = seed ^ SEED_RANDOMIZATION;
}
/**
* Return the average wind speed.
*
* @return the average wind speed.
*/
public double getAverage() {
return average;
}
/**
* Set the average wind speed. This method will also modify the
* standard deviation such that the turbulence intensity remains constant.
*
* @param average the average wind speed to set
*/
public void setAverage(double average) {
double intensity = getTurbulenceIntensity();
this.average = Math.max(average, 0);
setTurbulenceIntensity(intensity);
}
public void setDirection(double direction) {
this.direction = direction;
}
public double getDirection() {
return this.direction;
}
/**
* Return the standard deviation from the average wind speed.
*
* @return the standard deviation of the wind speed
*/
public double getStandardDeviation() {
return standardDeviation;
}
/**
* Set the standard deviation of the average wind speed.
*
* @param standardDeviation the standardDeviation to set
*/
public void setStandardDeviation(double standardDeviation) {
this.standardDeviation = Math.max(standardDeviation, 0);
}
/**
* Return the turbulence intensity (standard deviation / average).
*
* @return the turbulence intensity
*/
public double getTurbulenceIntensity() {
if (MathUtil.equals(average, 0)) {
if (MathUtil.equals(standardDeviation, 0))
return 0;
else
return 1000;
}
return standardDeviation / average;
}
/**
* Set the standard deviation to match the turbulence intensity.
*
* @param intensity the turbulence intensity
*/
public void setTurbulenceIntensity(double intensity) {
setStandardDeviation(intensity * average);
}
@Override
public Coordinate getWindVelocity(double time, double altitude) {
if (time < 0) {
throw new IllegalArgumentException("Requesting wind speed at t=" + time);
}
if (randomSource == null) {
randomSource = new PinkNoise(ALPHA, POLES, new Random(seed));
time1 = 0;
value1 = randomSource.nextValue();
value2 = randomSource.nextValue();
}
if (time < time1) {
reset();
return getWindVelocity(time, altitude);
}
while (time1 + DELTA_T < time) {
value1 = value2;
value2 = randomSource.nextValue();
time1 += DELTA_T;
}
double a = (time - time1) / DELTA_T;
double speed = average + (value1 * (1 - a) + value2 * a) * standardDeviation / STDDEV;
return new Coordinate(speed * Math.sin(direction), speed * Math.cos(direction), 0);
}
private void reset() {
randomSource = null;
}
@Override
public int getModID() {
return (int) (average * 1000 + standardDeviation);
}
}