ardupilot/Tools/ArdupilotMegaPlanner/HIL/Wind.cs

217 lines
7.8 KiB
C#

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();
}
/// <summary>
/// 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.
/// </summary>
/// <param name="mu">The mean of the distribution. Default is zero.</param>
/// <param name="sigma">The standard deviation of the distribution. Default is one.</param>
/// <returns></returns>
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;
}
}
}