using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace ArdupilotMega.HIL { public class Wind : Utils { Wind self; public float speed; public float direction; public float turbulance; public double cross_section; public double turbulance_time_constant; public DateTime tlast; public double turbulance_mul; //'''a wind generation object//''' public Wind (string windstring, double cross_section=0.1) { self = this; string[] a = windstring.Split(','); if (Utils.len(a) != 3) { return; //raise RuntimeError("Expected wind in speed,direction,turbulance form, not %s" % windstring); } self.speed = float.Parse(a[0]); //# m/s self.direction = float.Parse(a[1]); //# direction the wind is going in self.turbulance= float.Parse(a[2]); //# turbulance factor (standard deviation) //# the cross-section of the aircraft to wind. This is multiplied by the //# difference in the wind and the velocity of the aircraft to give the acceleration self.cross_section = cross_section; //# the time constant for the turbulance - the average period of the //# changes over time self.turbulance_time_constant = 5.0; //# wind time record self.tlast = DateTime.Now; //# initial turbulance multiplier self.turbulance_mul = 1.0; } public void current(double deltat, out double speed, out double direction) { //'''return current wind speed and direction as a tuple //speed is in m/s, direction in degrees //''' if (deltat == 0) { DateTime tnow = DateTime.Now; deltat = (tnow - self.tlast).TotalSeconds; self.tlast = tnow; } //# update turbulance random walk double w_delta = Utils.sqrt(deltat) * (1.0 - new GaussianRandom().NextGaussian(1.0, self.turbulance)); w_delta -= (self.turbulance_mul-1.0)*(deltat/self.turbulance_time_constant); self.turbulance_mul += w_delta; speed = self.speed * (float)Utils.fabs(self.turbulance_mul); direction = self.direction; } //# Calculate drag. public Vector3 drag(Vector3 velocity, double deltat = 0)//, testing=None) { //'''return current wind force in Earth frame. The velocity parameter is // a Vector3 of the current velocity of the aircraft in earth frame, m/s//''' //# (m/s, degrees) : wind vector as a magnitude and angle. double speed, direction; self.current(deltat,out speed,out direction); //# speed = self.speed //# direction = self.direction //# Get the wind vector. Vector3 w = toVec(speed, Utils.radians(direction)); double obj_speed = velocity.length(); //# Compute the angle between the object vector and wind vector by taking //# the dot product and dividing by the magnitudes. double alpha = 0; double d = w.length() * obj_speed; if (d == 0) { alpha = 0; }else{ int checkme; alpha = Utils.acos(((w * velocity).length() / d)); } //# Get the relative wind speed and angle from the object. Note that the //# relative wind speed includes the velocity of the object; i.e., there //# is a headwind equivalent to the object's speed even if there is no //# absolute wind. double rel_speed, beta; apparent_wind(speed, obj_speed, alpha,out rel_speed,out beta); //# Return the vector of the relative wind, relative to the coordinate //# system. Vector3 relWindVec = toVec(rel_speed, beta + Utils.atan2(velocity.y, velocity.x)); //# Combine them to get the acceleration vector. return new Vector3( acc(relWindVec.x, drag_force(self, relWindVec.x)) , acc(relWindVec.y, drag_force(self, relWindVec.y)) , 0 ); } //# http://en.wikipedia.org/wiki/Apparent_wind //# //# Returns apparent wind speed and angle of apparent wind. Alpha is the angle //# between the object and the true wind. alpha of 0 rads is a headwind; pi a //# tailwind. Speeds should always be positive. public void apparent_wind(double wind_sp, double obj_speed, double alpha, out double rel_speed, out double beta) { double delta = wind_sp * Utils.cos(alpha); double x = Math.Pow(wind_sp, 2) + Math.Pow(obj_speed, 2) + 2 * obj_speed * delta; rel_speed = Utils.sqrt(x); if (rel_speed == 0) { beta = Math.PI; } else { beta = acos((delta + obj_speed) / rel_speed); } } //# See http://en.wikipedia.org/wiki/Drag_equation //# //# Drag equation is F(a) = cl * p/2 * v^2 * a, where cl : drag coefficient //# (let's assume it's low, .e.g., 0.2), p : density of air (assume about 1 //# kg/m^3, the density just over 1500m elevation), v : relative speed of wind //# (to the body), a : area acted on (this is captured by the cross_section //# paramter). //# //# So then we have //# F(a) = 0.2 * 1/2 * v^2 * cross_section = 0.1 * v^2 * cross_section public double drag_force(Wind wind, double sp){ return (Math.Pow(sp,2.0)) * 0.1 * wind.cross_section; } //# Function to make the force vector. relWindVec is the direction the apparent //# wind comes *from*. We want to compute the accleration vector in the direction //# the wind blows to. public double acc(double val,double mag){ if (val == 0) { return mag; }else{ return (val / Utils.fabs(val)) * (0 - mag); } } //# Converts a magnitude and angle (radians) to a vector in the xy plane. public Vector3 toVec(double magnitude, double angle) { Vector3 v = new Vector3(magnitude, 0, 0); Matrix3 m = new Matrix3(); m.from_euler(0, 0, angle); return m.transposed() * v; } } public sealed class GaussianRandom { private bool _hasDeviate; private double _storedDeviate; private readonly Random _random; public GaussianRandom(Random random = null) { _random = random ?? new Random(); } /// /// Obtains normally (Gaussian) distributed random numbers, using the Box-Muller /// transformation. This transformation takes two uniformly distributed deviates /// within the unit circle, and transforms them into two independently /// distributed normal deviates. /// /// The mean of the distribution. Default is zero. /// The standard deviation of the distribution. Default is one. /// public double NextGaussian(double mu = 0, double sigma = 1) { if (sigma <= 0) throw new ArgumentOutOfRangeException("sigma", "Must be greater than zero."); if (_hasDeviate) { _hasDeviate = false; return _storedDeviate * sigma + mu; } double v1, v2, rSquared; do { // two random values between -1.0 and 1.0 v1 = 2 * _random.NextDouble() - 1; v2 = 2 * _random.NextDouble() - 1; rSquared = v1 * v1 + v2 * v2; // ensure within the unit circle } while (rSquared >= 1 || rSquared == 0); // calculate polar tranformation for each deviate var polar = Math.Sqrt(-2 * Math.Log(rSquared) / rSquared); // store first deviate _storedDeviate = v2 * polar; _hasDeviate = true; // return second deviate return v1 * polar * sigma + mu; } } }