1431 lines
42 KiB
C++
1431 lines
42 KiB
C++
#include <simgear/debug/logstream.hxx>
|
|
|
|
#include "Math.hpp"
|
|
#include "Surface.hpp"
|
|
#include "Rotorpart.hpp"
|
|
#include "Ground.hpp"
|
|
#include "Rotor.hpp"
|
|
|
|
#include STL_IOSTREAM
|
|
#include STL_IOMANIP
|
|
|
|
SG_USING_STD(setprecision);
|
|
|
|
#ifdef TEST_DEBUG
|
|
#include <stdio.h>
|
|
#endif
|
|
#include <string.h>
|
|
#include <iostream>
|
|
#include <sstream>
|
|
|
|
|
|
|
|
namespace yasim {
|
|
|
|
const float pi=3.14159;
|
|
|
|
static inline float sqr(float a) { return a * a; }
|
|
|
|
Rotor::Rotor()
|
|
{
|
|
int i;
|
|
_alpha0=-.05;
|
|
_alpha0factor=1;
|
|
_alphamin=-.1;
|
|
_alphamax= .1;
|
|
_alphaoutput[0][0]=0;
|
|
_alphaoutput[1][0]=0;
|
|
_alphaoutput[2][0]=0;
|
|
_alphaoutput[3][0]=0;
|
|
_alphaoutput[4][0]=0;
|
|
_alphaoutput[5][0]=0;
|
|
_alphaoutput[6][0]=0;
|
|
_alphaoutput[7][0]=0;
|
|
_base[0] = _base[1] = _base[2] = 0;
|
|
_ccw=0;
|
|
_delta=1;
|
|
_delta3=0;
|
|
_diameter =10;
|
|
_dynamic=1;
|
|
_engineon=0;
|
|
_force_at_pitch_a=0;
|
|
_forward[0]=1;
|
|
_forward[1]=_forward[2]=0;
|
|
_max_pitch=13./180*pi;
|
|
_maxcyclicail=10./180*pi;
|
|
_maxcyclicele=10./180*pi;
|
|
_maxteeterdamp=0;
|
|
_mincyclicail=-10./180*pi;
|
|
_mincyclicele=-10./180*pi;
|
|
_min_pitch=-.5/180*pi;
|
|
_cyclicele=0;
|
|
_cyclicail=0;
|
|
_name[0] = 0;
|
|
_normal[0] = _normal[1] = 0;
|
|
_normal[2] = 1;
|
|
_normal_with_yaw_roll[0]= _normal_with_yaw_roll[1]=0;
|
|
_normal_with_yaw_roll[2]=1;
|
|
_number_of_blades=4;
|
|
_omega=_omegan=_omegarel=_omegarelneu=0;
|
|
_ddt_omega=0;
|
|
_pitch_a=0;
|
|
_pitch_b=0;
|
|
_power_at_pitch_0=0;
|
|
_power_at_pitch_b=0;
|
|
_no_torque=0;
|
|
_rel_blade_center=.7;
|
|
_rel_len_hinge=0.01;
|
|
_rellenteeterhinge=0.01;
|
|
_rotor_rpm=442;
|
|
_sim_blades=0;
|
|
_teeterdamp=0.00001;
|
|
_translift=0.05;
|
|
_weight_per_blade=42;
|
|
_translift_ve=20;
|
|
_translift_maxfactor=1.3;
|
|
_ground_effect_constant=0.1;
|
|
_vortex_state_lift_factor=0.4;
|
|
_vortex_state_c1=0.1;
|
|
_vortex_state_c2=0;
|
|
_vortex_state_c3=0;
|
|
_vortex_state_e1=1;
|
|
_vortex_state_e2=1;
|
|
_vortex_state_e3=1;
|
|
_vortex_state=0;
|
|
_lift_factor=1;
|
|
_liftcoef=0.1;
|
|
_dragcoef0=0.1;
|
|
_dragcoef1=0.1;
|
|
_twist=0;
|
|
_number_of_segments=1;
|
|
_number_of_parts=4;
|
|
_rel_len_where_incidence_is_measured=0.7;
|
|
_torque_of_inertia=1;
|
|
_torque=0;
|
|
_chord=0.3;
|
|
_taper=1;
|
|
_airfoil_incidence_no_lift=0;
|
|
_rel_len_blade_start=0;
|
|
_airfoil_lift_coefficient=0;
|
|
_airfoil_drag_coefficient0=0;
|
|
_airfoil_drag_coefficient1=0;
|
|
for(i=0; i<2; i++)
|
|
_global_ground[i] = 0;
|
|
_global_ground[2] = 1;
|
|
_global_ground[3] = -1e3;
|
|
_incidence_stall_zero_speed=18*pi/180.;
|
|
_incidence_stall_half_sonic_speed=14*pi/180.;
|
|
_lift_factor_stall=0.28;
|
|
_stall_change_over=2*pi/180.;
|
|
_drag_factor_stall=8;
|
|
_stall_sum=1;
|
|
_stall_v2sum=1;
|
|
_collective=0;
|
|
_yaw=_roll=0;
|
|
for (int k=0;k<4;k++)
|
|
for (i=0;i<3;i++)
|
|
_groundeffectpos[k][i]=0;
|
|
_ground_effect_altitude=1;
|
|
_cyclic_factor=1;
|
|
_lift_factor=_f_ge=_f_vs=_f_tl=1;
|
|
_rotor_correction_factor=.65;
|
|
}
|
|
|
|
Rotor::~Rotor()
|
|
{
|
|
int i;
|
|
for(i=0; i<_rotorparts.size(); i++) {
|
|
Rotorpart* r = (Rotorpart*)_rotorparts.get(i);
|
|
delete r;
|
|
}
|
|
}
|
|
|
|
void Rotor::inititeration(float dt,float omegarel,float ddt_omegarel,float *rot)
|
|
{
|
|
_stall_sum=0;
|
|
_stall_v2sum=0;
|
|
_omegarel=omegarel;
|
|
_omega=_omegan*_omegarel;
|
|
_ddt_omega=_omegan*ddt_omegarel;
|
|
int i;
|
|
for(i=0; i<_rotorparts.size(); i++) {
|
|
float s = Math::sin(2*pi*i/_number_of_parts);
|
|
float c = Math::cos(2*pi*i/_number_of_parts);
|
|
Rotorpart* r = (Rotorpart*)_rotorparts.get(i);
|
|
r->setOmega(_omega);
|
|
r->setDdtOmega(_ddt_omega);
|
|
r->inititeration(dt,rot);
|
|
r->setCyclic(_cyclicail*c+_cyclicele*s);
|
|
}
|
|
|
|
//calculate the normal of the rotor disc, for calcualtion of the downwash
|
|
float side[3],help[3];
|
|
Math::cross3(_normal,_forward,side);
|
|
Math::mul3(Math::cos(_yaw)*Math::cos(_roll),_normal,_normal_with_yaw_roll);
|
|
|
|
Math::mul3(Math::sin(_yaw),_forward,help);
|
|
Math::add3(_normal_with_yaw_roll,help,_normal_with_yaw_roll);
|
|
|
|
Math::mul3(Math::sin(_roll),side,help);
|
|
Math::add3(_normal_with_yaw_roll,help,_normal_with_yaw_roll);
|
|
}
|
|
|
|
float Rotor::calcStall(float incidence,float speed)
|
|
{
|
|
float stall_incidence=_incidence_stall_zero_speed
|
|
+(_incidence_stall_half_sonic_speed
|
|
-_incidence_stall_zero_speed)*speed/(343./2);
|
|
//missing: Temeperature dependency of sonic speed
|
|
incidence = Math::abs(incidence);
|
|
if (incidence > (90./180.*pi))
|
|
incidence = pi-incidence;
|
|
float stall = (incidence-stall_incidence)/_stall_change_over;
|
|
stall = Math::clamp(stall,0,1);
|
|
|
|
_stall_sum+=stall*speed*speed;
|
|
_stall_v2sum+=speed*speed;
|
|
|
|
return stall;
|
|
}
|
|
|
|
float Rotor::getLiftCoef(float incidence,float speed)
|
|
{
|
|
float stall=calcStall(incidence,speed);
|
|
/* the next shold look like this, but this is the inner loop of
|
|
the rotor simulation. For small angles (and we hav only small
|
|
angles) the first order approximation works well
|
|
float c1= Math::sin(incidence-_airfoil_incidence_no_lift)*_liftcoef;
|
|
for c2 we would need higher order, because in stall the angle can be large
|
|
*/
|
|
float i2;
|
|
if (incidence > (pi/2))
|
|
i2 = incidence-pi;
|
|
else if (incidence <-(pi/2))
|
|
i2 = (incidence+pi);
|
|
else
|
|
i2 = incidence;
|
|
float c1= (i2-_airfoil_incidence_no_lift)*_liftcoef;
|
|
if (stall > 0)
|
|
{
|
|
float c2= Math::sin(2*(incidence-_airfoil_incidence_no_lift))
|
|
*_liftcoef*_lift_factor_stall;
|
|
return (1-stall)*c1 + stall *c2;
|
|
}
|
|
else
|
|
return c1;
|
|
}
|
|
|
|
float Rotor::getDragCoef(float incidence,float speed)
|
|
{
|
|
float stall=calcStall(incidence,speed);
|
|
float c1= (Math::abs(Math::sin(incidence-_airfoil_incidence_no_lift))
|
|
*_dragcoef1+_dragcoef0);
|
|
float c2= c1*_drag_factor_stall;
|
|
return (1-stall)*c1 + stall *c2;
|
|
}
|
|
|
|
int Rotor::getValueforFGSet(int j,char *text,float *f)
|
|
{
|
|
if (_name[0]==0) return 0;
|
|
if (4>numRotorparts()) return 0; //compile first!
|
|
if (j==0)
|
|
{
|
|
sprintf(text,"/rotors/%s/cone-deg", _name);
|
|
*f=( ((Rotorpart*)getRotorpart(0))->getrealAlpha()
|
|
+((Rotorpart*)getRotorpart(1*(_number_of_parts>>2)))->getrealAlpha()
|
|
+((Rotorpart*)getRotorpart(2*(_number_of_parts>>2)))->getrealAlpha()
|
|
+((Rotorpart*)getRotorpart(3*(_number_of_parts>>2)))->getrealAlpha()
|
|
)/4*180/pi;
|
|
}
|
|
else
|
|
if (j==1)
|
|
{
|
|
sprintf(text,"/rotors/%s/roll-deg", _name);
|
|
_roll = ( ((Rotorpart*)getRotorpart(0))->getrealAlpha()
|
|
-((Rotorpart*)getRotorpart(2*(_number_of_parts>>2)))->getrealAlpha()
|
|
)/2*(_ccw?-1:1);
|
|
*f=_roll *180/pi;
|
|
}
|
|
else
|
|
if (j==2)
|
|
{
|
|
sprintf(text,"/rotors/%s/yaw-deg", _name);
|
|
_yaw=( ((Rotorpart*)getRotorpart(1*(_number_of_parts>>2)))->getrealAlpha()
|
|
-((Rotorpart*)getRotorpart(3*(_number_of_parts>>2)))->getrealAlpha()
|
|
)/2;
|
|
*f=_yaw*180/pi;
|
|
}
|
|
else
|
|
if (j==3)
|
|
{
|
|
sprintf(text,"/rotors/%s/rpm", _name);
|
|
*f=_omega/2/pi*60;
|
|
}
|
|
else
|
|
if (j==4)
|
|
{
|
|
sprintf(text,"/rotors/%s/debug/debugfge",_name);
|
|
*f=_f_ge;
|
|
}
|
|
else if (j==5)
|
|
{
|
|
sprintf(text,"/rotors/%s/debug/debugfvs",_name);
|
|
*f=_f_vs;
|
|
}
|
|
else if (j==6)
|
|
{
|
|
sprintf(text,"/rotors/%s/debug/debugftl",_name);
|
|
*f=_f_tl;
|
|
}
|
|
else if (j==7)
|
|
{
|
|
sprintf(text,"/rotors/%s/debug/vortexstate",_name);
|
|
*f=_vortex_state;
|
|
}
|
|
else if (j==8)
|
|
{
|
|
sprintf(text,"/rotors/%s/stall",_name);
|
|
*f=getOverallStall();
|
|
}
|
|
else if (j==9)
|
|
{
|
|
sprintf(text,"/rotors/%s/torque",_name);
|
|
*f=-_torque;;
|
|
}
|
|
else
|
|
{
|
|
int b=(j-10)/3;
|
|
if (b>=_number_of_blades)
|
|
{
|
|
return 0;
|
|
}
|
|
int w=j%3;
|
|
sprintf(text,"/rotors/%s/blade[%i]/%s",
|
|
_name,b,
|
|
w==0?"position-deg":(w==1?"flap-deg":"incidence-deg"));
|
|
*f=((Rotorpart*)getRotorpart(0))->getPhi()*180/pi
|
|
+360*b/_number_of_blades*(_ccw?1:-1);
|
|
if (*f>360) *f-=360;
|
|
if (*f<0) *f+=360;
|
|
int k,l;
|
|
float rk,rl,p;
|
|
p=(*f/90);
|
|
k=int(p);
|
|
l=k+1;
|
|
rk=l-p;
|
|
rk=Math::clamp(rk,0,1);//Delete this
|
|
rl=1-rk;
|
|
if(w==2) {k+=2;l+=2;}
|
|
else
|
|
if(w==1) {k+=1;l+=1;}
|
|
k%=4;
|
|
l%=4;
|
|
if (w==1) *f=rk*((Rotorpart*) getRotorpart(k*(_number_of_parts>>2)))->getrealAlpha()*180/pi
|
|
+rl*((Rotorpart*) getRotorpart(l*(_number_of_parts>>2)))->getrealAlpha()*180/pi;
|
|
else if(w==2) *f=rk*((Rotorpart*)getRotorpart(k*(_number_of_parts>>2)))->getIncidence()*180/pi
|
|
+rl*((Rotorpart*)getRotorpart(l*(_number_of_parts>>2)))->getIncidence()*180/pi;
|
|
}
|
|
return j+1;
|
|
}
|
|
|
|
void Rotorgear::setEngineOn(int value)
|
|
{
|
|
_engineon=value;
|
|
}
|
|
|
|
void Rotor::setAlpha0(float f)
|
|
{
|
|
_alpha0=f;
|
|
}
|
|
|
|
void Rotor::setAlphamin(float f)
|
|
{
|
|
_alphamin=f;
|
|
}
|
|
|
|
void Rotor::setAlphamax(float f)
|
|
{
|
|
_alphamax=f;
|
|
}
|
|
|
|
void Rotor::setAlpha0factor(float f)
|
|
{
|
|
_alpha0factor=f;
|
|
}
|
|
|
|
int Rotor::numRotorparts()
|
|
{
|
|
return _rotorparts.size();
|
|
}
|
|
|
|
Rotorpart* Rotor::getRotorpart(int n)
|
|
{
|
|
return ((Rotorpart*)_rotorparts.get(n));
|
|
}
|
|
|
|
int Rotorgear::getEngineon()
|
|
{
|
|
return _engineon;
|
|
}
|
|
|
|
float Rotor::getTorqueOfInertia()
|
|
{
|
|
return _torque_of_inertia;
|
|
}
|
|
|
|
void Rotor::setTorque(float f)
|
|
{
|
|
_torque=f;
|
|
}
|
|
|
|
void Rotor::addTorque(float f)
|
|
{
|
|
_torque+=f;
|
|
}
|
|
|
|
void Rotor::strncpy(char *dest,const char *src,int maxlen)
|
|
{
|
|
int n=0;
|
|
while(src[n]&&n<(maxlen-1))
|
|
{
|
|
dest[n]=src[n];
|
|
n++;
|
|
}
|
|
dest[n]=0;
|
|
}
|
|
|
|
void Rotor::setNormal(float* normal)
|
|
{
|
|
int i;
|
|
float invsum,sqrsum=0;
|
|
for(i=0; i<3; i++) { sqrsum+=normal[i]*normal[i];}
|
|
if (sqrsum!=0)
|
|
invsum=1/Math::sqrt(sqrsum);
|
|
else
|
|
invsum=1;
|
|
for(i=0; i<3; i++)
|
|
{
|
|
_normal_with_yaw_roll[i]=_normal[i] = normal[i]*invsum;
|
|
}
|
|
}
|
|
|
|
void Rotor::setForward(float* forward)
|
|
{
|
|
int i;
|
|
float invsum,sqrsum=0;
|
|
for(i=0; i<3; i++) { sqrsum+=forward[i]*forward[i];}
|
|
if (sqrsum!=0)
|
|
invsum=1/Math::sqrt(sqrsum);
|
|
else
|
|
invsum=1;
|
|
for(i=0; i<3; i++) { _forward[i] = forward[i]*invsum; }
|
|
}
|
|
|
|
void Rotor::setForceAtPitchA(float force)
|
|
{
|
|
_force_at_pitch_a=force;
|
|
}
|
|
|
|
void Rotor::setPowerAtPitch0(float value)
|
|
{
|
|
_power_at_pitch_0=value;
|
|
}
|
|
|
|
void Rotor::setPowerAtPitchB(float value)
|
|
{
|
|
_power_at_pitch_b=value;
|
|
}
|
|
|
|
void Rotor::setPitchA(float value)
|
|
{
|
|
_pitch_a=value/180*pi;
|
|
}
|
|
|
|
void Rotor::setPitchB(float value)
|
|
{
|
|
_pitch_b=value/180*pi;
|
|
}
|
|
|
|
void Rotor::setBase(float* base)
|
|
{
|
|
int i;
|
|
for(i=0; i<3; i++) _base[i] = base[i];
|
|
}
|
|
|
|
void Rotor::setMaxCyclicail(float value)
|
|
{
|
|
_maxcyclicail=value/180*pi;
|
|
}
|
|
|
|
void Rotor::setMaxCyclicele(float value)
|
|
{
|
|
_maxcyclicele=value/180*pi;
|
|
}
|
|
|
|
void Rotor::setMinCyclicail(float value)
|
|
{
|
|
_mincyclicail=value/180*pi;
|
|
}
|
|
|
|
void Rotor::setMinCyclicele(float value)
|
|
{
|
|
_mincyclicele=value/180*pi;
|
|
}
|
|
|
|
void Rotor::setMinCollective(float value)
|
|
{
|
|
_min_pitch=value/180*pi;
|
|
}
|
|
|
|
void Rotor::setMaxCollective(float value)
|
|
{
|
|
_max_pitch=value/180*pi;
|
|
}
|
|
|
|
void Rotor::setDiameter(float value)
|
|
{
|
|
_diameter=value;
|
|
}
|
|
|
|
void Rotor::setWeightPerBlade(float value)
|
|
{
|
|
_weight_per_blade=value;
|
|
}
|
|
|
|
void Rotor::setNumberOfBlades(float value)
|
|
{
|
|
_number_of_blades=int(value+.5);
|
|
}
|
|
|
|
void Rotor::setRelBladeCenter(float value)
|
|
{
|
|
_rel_blade_center=value;
|
|
}
|
|
|
|
void Rotor::setDynamic(float value)
|
|
{
|
|
_dynamic=value;
|
|
}
|
|
|
|
void Rotor::setDelta3(float value)
|
|
{
|
|
_delta3=value;
|
|
}
|
|
|
|
void Rotor::setDelta(float value)
|
|
{
|
|
_delta=value;
|
|
}
|
|
|
|
void Rotor::setTranslift(float value)
|
|
{
|
|
_translift=value;
|
|
}
|
|
|
|
void Rotor::setC2(float value)
|
|
{
|
|
_c2=value;
|
|
}
|
|
|
|
void Rotor::setStepspersecond(float steps)
|
|
{
|
|
_stepspersecond=steps;
|
|
}
|
|
|
|
void Rotor::setRPM(float value)
|
|
{
|
|
_rotor_rpm=value;
|
|
}
|
|
|
|
void Rotor::setRelLenHinge(float value)
|
|
{
|
|
_rel_len_hinge=value;
|
|
}
|
|
|
|
void Rotor::setAlphaoutput(int i, const char *text)
|
|
{
|
|
strncpy(_alphaoutput[i],text,255);
|
|
}
|
|
|
|
void Rotor::setName(const char *text)
|
|
{
|
|
strncpy(_name,text,256);//256: some space needed for settings
|
|
}
|
|
|
|
void Rotor::setCcw(int ccw)
|
|
{
|
|
_ccw=ccw;
|
|
}
|
|
|
|
void Rotor::setNotorque(int value)
|
|
{
|
|
_no_torque=value;
|
|
}
|
|
|
|
void Rotor::setRelLenTeeterHinge(float f)
|
|
{
|
|
_rellenteeterhinge=f;
|
|
}
|
|
|
|
void Rotor::setTeeterdamp(float f)
|
|
{
|
|
_teeterdamp=f;
|
|
}
|
|
|
|
void Rotor::setMaxteeterdamp(float f)
|
|
{
|
|
_maxteeterdamp=f;
|
|
}
|
|
|
|
void Rotor::setGlobalGround(double *global_ground, float* global_vel)
|
|
{
|
|
int i;
|
|
for(i=0; i<4; i++) _global_ground[i] = global_ground[i];
|
|
}
|
|
|
|
void Rotor::setParameter(char *parametername, float value)
|
|
{
|
|
#define p(a,b) if (strcmp(parametername,#a)==0) _##a = (value * (b)); else
|
|
p(translift_ve,1)
|
|
p(translift_maxfactor,1)
|
|
p(ground_effect_constant,1)
|
|
p(vortex_state_lift_factor,1)
|
|
p(vortex_state_c1,1)
|
|
p(vortex_state_c2,1)
|
|
p(vortex_state_c3,1)
|
|
p(vortex_state_e1,1)
|
|
p(vortex_state_e2,1)
|
|
p(vortex_state_e3,1)
|
|
p(twist,pi/180.)
|
|
p(number_of_segments,1)
|
|
p(number_of_parts,1)
|
|
p(rel_len_where_incidence_is_measured,1)
|
|
p(chord,1)
|
|
p(taper,1)
|
|
p(airfoil_incidence_no_lift,pi/180.)
|
|
p(rel_len_blade_start,1)
|
|
p(incidence_stall_zero_speed,pi/180.)
|
|
p(incidence_stall_half_sonic_speed,pi/180.)
|
|
p(lift_factor_stall,1)
|
|
p(stall_change_over,pi/180.)
|
|
p(drag_factor_stall,1)
|
|
p(airfoil_lift_coefficient,1)
|
|
p(airfoil_drag_coefficient0,1)
|
|
p(airfoil_drag_coefficient1,1)
|
|
p(cyclic_factor,1)
|
|
p(rotor_correction_factor,1)
|
|
SG_LOG(SG_INPUT, SG_ALERT,
|
|
"internal error in parameter set up for rotor: '" <<
|
|
parametername <<"'" << endl);
|
|
#undef p
|
|
}
|
|
|
|
float Rotor::getLiftFactor()
|
|
{
|
|
return _lift_factor;
|
|
}
|
|
|
|
void Rotorgear::setRotorBrake(float lval)
|
|
{
|
|
lval = Math::clamp(lval, 0, 1);
|
|
_rotorbrake=lval;
|
|
}
|
|
|
|
void Rotor::setCollective(float lval)
|
|
{
|
|
lval = Math::clamp(lval, -1, 1);
|
|
int i;
|
|
for(i=0; i<_rotorparts.size(); i++) {
|
|
((Rotorpart*)_rotorparts.get(i))->setCollective(lval);
|
|
}
|
|
_collective=_min_pitch+(lval+1)/2*(_max_pitch-_min_pitch);
|
|
}
|
|
|
|
void Rotor::setCyclicele(float lval,float rval)
|
|
{
|
|
lval = Math::clamp(lval, -1, 1);
|
|
_cyclicele=_mincyclicele+(lval+1)/2*(_maxcyclicele-_mincyclicele);
|
|
}
|
|
|
|
void Rotor::setCyclicail(float lval,float rval)
|
|
{
|
|
lval = Math::clamp(lval, -1, 1);
|
|
if (_ccw) lval *=-1;
|
|
_cyclicail=-(_mincyclicail+(lval+1)/2*(_maxcyclicail-_mincyclicail));
|
|
}
|
|
|
|
void Rotor::getPosition(float* out)
|
|
{
|
|
int i;
|
|
for(i=0; i<3; i++) out[i] = _base[i];
|
|
}
|
|
|
|
void Rotor::calcLiftFactor(float* v, float rho, State *s)
|
|
{
|
|
//calculates _lift_factor, which is a foactor for the lift of the rotor
|
|
//due to
|
|
//- ground effect (_f_ge)
|
|
//- vortex state (_f_vs)
|
|
//- translational lift (_f_tl)
|
|
_f_ge=1;
|
|
_f_tl=1;
|
|
_f_vs=1;
|
|
|
|
// Calculate ground effect
|
|
_f_ge=1+_diameter/_ground_effect_altitude*_ground_effect_constant;
|
|
|
|
// Now calculate translational lift
|
|
float v_vert = Math::dot3(v,_normal);
|
|
float help[3];
|
|
Math::cross3(v,_normal,help);
|
|
float v_horiz = Math::mag3(help);
|
|
_f_tl = ((1-Math::pow(2.7183,-v_horiz/_translift_ve))
|
|
*(_translift_maxfactor-1)+1)/_translift_maxfactor;
|
|
|
|
_lift_factor = _f_ge*_f_tl*_f_vs;
|
|
}
|
|
|
|
void Rotor::findGroundEffectAltitude(Ground * ground_cb,State *s)
|
|
{
|
|
_ground_effect_altitude=findGroundEffectAltitude(ground_cb,s,
|
|
_groundeffectpos[0],_groundeffectpos[1],
|
|
_groundeffectpos[2],_groundeffectpos[3]);
|
|
}
|
|
|
|
float Rotor::findGroundEffectAltitude(Ground * ground_cb,State *s,
|
|
float *pos0,float *pos1,float *pos2,float *pos3,
|
|
int iteration,float a0,float a1,float a2,float a3)
|
|
{
|
|
float a[5];
|
|
float *p[5],pos4[3];
|
|
a[0]=a0;
|
|
a[1]=a1;
|
|
a[2]=a2;
|
|
a[3]=a3;
|
|
a[4]=-1;
|
|
p[0]=pos0;
|
|
p[1]=pos1;
|
|
p[2]=pos2;
|
|
p[3]=pos3;
|
|
p[4]=pos4;
|
|
Math::add3(p[0],p[2],p[4]);
|
|
Math::mul3(0.5,p[4],p[4]);//the center
|
|
|
|
float mina=100*_diameter;
|
|
float suma=0;
|
|
for (int i=0;i<5;i++)
|
|
{
|
|
if (a[i]==-1)//in the first iteration,(iteration==0) no height is
|
|
//passed to this function, these missing values are
|
|
//marked by ==-1
|
|
{
|
|
double pt[3];
|
|
s->posLocalToGlobal(p[i], pt);
|
|
|
|
// Ask for the ground plane in the global coordinate system
|
|
double global_ground[4];
|
|
float global_vel[3];
|
|
ground_cb->getGroundPlane(pt, global_ground, global_vel);
|
|
// find h, the distance to the ground
|
|
// The ground plane transformed to the local frame.
|
|
float ground[4];
|
|
s->planeGlobalToLocal(global_ground, ground);
|
|
|
|
a[i] = ground[3] - Math::dot3(p[i], ground);
|
|
// Now a[i] is the distance from p[i] to ground
|
|
}
|
|
suma+=a[i];
|
|
if (a[i]<mina)
|
|
mina=a[i];
|
|
}
|
|
if (mina>=10*_diameter)
|
|
return mina; //the ground effect will be zero
|
|
|
|
//check if further recursion is neccessary
|
|
//if the height does not differ more than 20%, than
|
|
//we can return then mean height, if not split
|
|
//zhe square to four parts and calcualte the height
|
|
//for each part
|
|
//suma * 0.2 is the mean
|
|
//0.15 is the maximum allowed difference from the mean
|
|
//to the height at the center
|
|
if ((iteration>2)
|
|
||(Math::abs(suma*0.2-a[4])<(0.15*0.2*suma*(1<<iteration))))
|
|
return suma*0.2;
|
|
suma=0;
|
|
float pc[4][3],ac[4]; //pc[i]=center of pos[i] and pos[(i+1)&3]
|
|
for (int i=0;i<4;i++)
|
|
{
|
|
Math::add3(p[i],p[(i+1)&3],pc[i]);
|
|
Math::mul3(0.5,pc[i],pc[i]);
|
|
double pt[3];
|
|
s->posLocalToGlobal(pc[i], pt);
|
|
|
|
// Ask for the ground plane in the global coordinate system
|
|
double global_ground[4];
|
|
float global_vel[3];
|
|
ground_cb->getGroundPlane(pt, global_ground, global_vel);
|
|
// find h, the distance to the ground
|
|
// The ground plane transformed to the local frame.
|
|
float ground[4];
|
|
s->planeGlobalToLocal(global_ground, ground);
|
|
|
|
ac[i] = ground[3] - Math::dot3(p[i], ground);
|
|
// Now ac[i] is the distance from pc[i] to ground
|
|
}
|
|
return 0.25*
|
|
(findGroundEffectAltitude(ground_cb,s,p[0],pc[1],p[4],pc[3],
|
|
iteration+1,a[0],ac[0],a[4],ac[3])
|
|
+findGroundEffectAltitude(ground_cb,s,p[1],pc[0],p[4],pc[1],
|
|
iteration+1,a[1],ac[0],a[4],ac[1])
|
|
+findGroundEffectAltitude(ground_cb,s,p[2],pc[1],p[4],pc[2],
|
|
iteration+1,a[2],ac[1],a[4],ac[2])
|
|
+findGroundEffectAltitude(ground_cb,s,p[3],pc[2],p[4],pc[3],
|
|
iteration+1,a[3],ac[2],a[4],ac[3])
|
|
);
|
|
}
|
|
|
|
void Rotor::getDownWash(float *pos, float *v_heli, float *downwash)
|
|
{
|
|
float pos2rotor[3],tmp[3];
|
|
Math::sub3(_base,pos,pos2rotor);
|
|
float dist=Math::dot3(pos2rotor,_normal_with_yaw_roll);
|
|
//calculate incidence at 0.7r;
|
|
float inc = _collective+_twist *0.7
|
|
- _twist*_rel_len_where_incidence_is_measured;
|
|
if (inc < 0)
|
|
dist *=-1;
|
|
if (dist<0) // we are not in the downwash region
|
|
{
|
|
downwash[0]=downwash[1]=downwash[2]=0.;
|
|
return;
|
|
}
|
|
|
|
//calculate the mean downwash speed directly beneath the rotor disk
|
|
float v1bar = Math::sin(inc) *_omega * 0.35 * _diameter * 0.8;
|
|
//0.35 * d = 0.7 *r, a good position to calcualte the mean downwashd
|
|
//0.8 the slip of the rotor.
|
|
|
|
//calculate the time the wind needed from thr rotor to here
|
|
if (v1bar< 1) v1bar = 1;
|
|
float time=dist/v1bar;
|
|
|
|
//calculate the pos2rotor, where the rotor was, "time" ago
|
|
Math::mul3(time,v_heli,tmp);
|
|
Math::sub3(pos2rotor,tmp,pos2rotor);
|
|
|
|
//and again calculate dist
|
|
dist=Math::dot3(pos2rotor,_normal_with_yaw_roll);
|
|
//missing the normal is offen not pointing to the normal of the rotor
|
|
//disk. Rotate the normal by yaw and tilt angle
|
|
|
|
if (inc < 0)
|
|
dist *=-1;
|
|
if (dist<0) // we are not in the downwash region
|
|
{
|
|
downwash[0]=downwash[1]=downwash[2]=0.;
|
|
return;
|
|
}
|
|
//of course this could be done in a runge kutta integrator, but it's such
|
|
//a approximation that I beleave, it would'nt be more realistic
|
|
|
|
//calculate the dist to the rotor-axis
|
|
Math::cross3(pos2rotor,_normal_with_yaw_roll,tmp);
|
|
float r= Math::mag3(tmp);
|
|
//calculate incidence at r;
|
|
float rel_r = r *2 /_diameter;
|
|
float inc_r = _collective+_twist * r /_diameter * 2
|
|
- _twist*_rel_len_where_incidence_is_measured;
|
|
|
|
//calculate the downwash speed directly beneath the rotor disk
|
|
float v1=0;
|
|
if (rel_r<1)
|
|
v1 = Math::sin(inc_r) *_omega * r * 0.8;
|
|
|
|
//calcualte the downwash speed in a distance "dist" to the rotor disc,
|
|
//for large dist. The speed is assumed do follow a gausian distribution
|
|
//with sigma increasing with dist^2:
|
|
//sigma is assumed to be half of the rotor diameter directly beneath the
|
|
//disc and is assumed to the rotor diameter at dist = (diameter * sqrt(2))
|
|
|
|
float sigma=_diameter/2 + dist * dist / _diameter /4.;
|
|
float v2 = v1bar*_diameter/ (Math::sqrt(2 * pi) * sigma)
|
|
* Math::pow(2.7183,-.5*r*r/(sigma*sigma))*_diameter/2/sigma;
|
|
|
|
//calculate the weight of the two downwash velocities.
|
|
//Directly beneath the disc it is v1, far away it is v2
|
|
float g = Math::pow(2.7183,-2*dist/_diameter);
|
|
//at dist = rotor radius it is assumed to be 1/e * v1 + (1-1/e)* v2
|
|
|
|
float v = g * v1 + (1-g) * v2;
|
|
Math::mul3(-v,_normal_with_yaw_roll,downwash);
|
|
//the downwash is calculated in the opposite direction of the normal
|
|
}
|
|
|
|
void Rotor::compile()
|
|
{
|
|
// Have we already been compiled?
|
|
if(_rotorparts.size() != 0) return;
|
|
|
|
//rotor is divided into _number_of_parts parts
|
|
//each part is calcualted at _number_of_segments points
|
|
|
|
//clamp to 4..256
|
|
//and make it a factor of 4
|
|
_number_of_parts=(int(Math::clamp(_number_of_parts,4,256))>>2)<<2;
|
|
|
|
_dynamic=_dynamic*(1/ //inverse of the time
|
|
( (60/_rotor_rpm)/4 //for rotating 90 deg
|
|
+(60/_rotor_rpm)/(2*_number_of_blades) //+ meantime a rotorblade
|
|
//will pass a given point
|
|
));
|
|
float directions[5][3];
|
|
//pointing forward, right, ... the 5th is ony for calculation
|
|
directions[0][0]=_forward[0];
|
|
directions[0][1]=_forward[1];
|
|
directions[0][2]=_forward[2];
|
|
int i;
|
|
for (i=1;i<5;i++)
|
|
{
|
|
if (!_ccw)
|
|
Math::cross3(directions[i-1],_normal,directions[i]);
|
|
else
|
|
Math::cross3(_normal,directions[i-1],directions[i]);
|
|
Math::unit3(directions[i],directions[i]);
|
|
}
|
|
Math::set3(directions[4],directions[0]);
|
|
// now directions[0] is perpendicular to the _normal.and has a length
|
|
// of 1. if _forward is already normalized and perpendicular to the
|
|
// normal, directions[0] will be the same
|
|
for (i=0;i<4;i++)
|
|
{
|
|
Math::mul3(_diameter*0.7,directions[i],_groundeffectpos[i]);
|
|
Math::add3(_base,_groundeffectpos[i],_groundeffectpos[i]);
|
|
}
|
|
float rotorpartmass = _weight_per_blade*_number_of_blades/_number_of_parts*.453;
|
|
//was pounds -> now kg
|
|
|
|
_torque_of_inertia = 1/12. * ( _number_of_parts * rotorpartmass) * _diameter
|
|
* _diameter * _rel_blade_center * _rel_blade_center /(0.5*0.5);
|
|
float speed=_rotor_rpm/60*_diameter*_rel_blade_center*pi;
|
|
float lentocenter=_diameter*_rel_blade_center*0.5;
|
|
float lentoforceattac=_diameter*_rel_len_hinge*0.5;
|
|
float zentforce=rotorpartmass*speed*speed/lentocenter;
|
|
float pitchaforce=_force_at_pitch_a/_number_of_parts*.453*9.81;
|
|
// was pounds of force, now N, devided by _number_of_parts
|
|
//(so its now per rotorpart)
|
|
|
|
float torque0=0,torquemax=0,torqueb=0;
|
|
float omega=_rotor_rpm/60*2*pi;
|
|
_omegan=omega;
|
|
float omega0=omega*Math::sqrt(1/(1-_rel_len_hinge));
|
|
_delta*=pitchaforce/(_pitch_a*omega*lentocenter*2*rotorpartmass);
|
|
|
|
float phi=Math::atan2(2*omega*_delta,omega0*omega0-omega*omega);
|
|
float relamp=(omega*omega/(2*_delta*Math::sqrt(sqr(omega0*omega0-omega*omega)
|
|
+4*_delta*_delta*omega*omega)))*_cyclic_factor;
|
|
if (!_no_torque)
|
|
{
|
|
torque0=_power_at_pitch_0/_number_of_parts*1000/omega;
|
|
// f*r=p/w ; p=f*s/t; r=s/t/w ; r*w*t = s
|
|
torqueb=_power_at_pitch_b/_number_of_parts*1000/omega;
|
|
torquemax=_power_at_pitch_b/_number_of_parts*1000/omega/_pitch_b*_max_pitch;
|
|
|
|
if(_ccw)
|
|
{
|
|
torque0*=-1;
|
|
torquemax*=-1;
|
|
torqueb*=-1;
|
|
}
|
|
}
|
|
|
|
Rotorpart* rps[256];
|
|
for (i=0;i<_number_of_parts;i++)
|
|
{
|
|
float lpos[3],lforceattac[3],lspeed[3],dirzentforce[3];
|
|
float s = Math::sin(2*pi*i/_number_of_parts);
|
|
float c = Math::cos(2*pi*i/_number_of_parts);
|
|
float direction[3],nextdirection[3],help[3];
|
|
Math::mul3(c ,directions[0],help);
|
|
Math::mul3(s ,directions[1],direction);
|
|
Math::add3(help,direction,direction);
|
|
|
|
Math::mul3(c ,directions[1],help);
|
|
Math::mul3(s ,directions[2],nextdirection);
|
|
Math::add3(help,nextdirection,nextdirection);
|
|
|
|
Math::mul3(lentocenter,direction,lpos);
|
|
Math::add3(lpos,_base,lpos);
|
|
Math::mul3(lentoforceattac,nextdirection,lforceattac);
|
|
//nextdirection: +90deg (gyro)!!!
|
|
|
|
Math::add3(lforceattac,_base,lforceattac);
|
|
Math::mul3(speed,nextdirection,lspeed);
|
|
Math::mul3(1,nextdirection,dirzentforce);
|
|
|
|
|
|
float maxcyclic=(i&1)?_maxcyclicele:_maxcyclicail;
|
|
float mincyclic=(i&1)?_mincyclicele:_mincyclicail;
|
|
|
|
Rotorpart* rp=rps[i]=newRotorpart(lpos, lforceattac, _normal,
|
|
lspeed,dirzentforce,zentforce,pitchaforce, _max_pitch,_min_pitch,
|
|
mincyclic,maxcyclic,_delta3,rotorpartmass,_translift,
|
|
_rel_len_hinge,lentocenter);
|
|
int k = i*4/_number_of_parts;
|
|
rp->setAlphaoutput(_alphaoutput[k&1?k:(_ccw?k^2:k)],0);
|
|
rp->setAlphaoutput(_alphaoutput[4+(k&1?k:(_ccw?k^2:k))],1+(k>1));
|
|
_rotorparts.add(rp);
|
|
rp->setTorque(torquemax,torque0);
|
|
rp->setRelamp(relamp);
|
|
rp->setDirectionofRotorPart(direction);
|
|
rp->setTorqueOfInertia(_torque_of_inertia/_number_of_parts);
|
|
}
|
|
for (i=0;i<_number_of_parts;i++)
|
|
{
|
|
rps[i]->setlastnextrp(rps[(i-1+_number_of_parts)%_number_of_parts],
|
|
rps[(i+1)%_number_of_parts],
|
|
rps[(i+_number_of_parts/2)%_number_of_parts],
|
|
rps[(i-_number_of_parts/4+_number_of_parts)%_number_of_parts],
|
|
rps[(i+_number_of_parts/4)%_number_of_parts]);
|
|
}
|
|
for (i=0;i<_number_of_parts;i++)
|
|
{
|
|
rps[i]->setCompiled();
|
|
}
|
|
float lift[4],torque[4], v_wind[3];
|
|
v_wind[0]=v_wind[1]=v_wind[2]=0;
|
|
rps[0]->setOmega(_omegan);
|
|
|
|
if (_airfoil_lift_coefficient==0)
|
|
{
|
|
//calculate the lift and drag coefficients now
|
|
_dragcoef0=1;
|
|
_dragcoef1=1;
|
|
_liftcoef=1;
|
|
rps[0]->calculateAlpha(v_wind,rho_null,_pitch_a,0,0,
|
|
&(torque[0]),&(lift[0])); //max_pitch a
|
|
_liftcoef = pitchaforce/lift[0];
|
|
_dragcoef0=1;
|
|
_dragcoef1=0;
|
|
rps[0]->calculateAlpha(v_wind,rho_null,0,0,0,&(torque[0]),&(lift[0]));
|
|
//0 degree, c0
|
|
|
|
_dragcoef0=0;
|
|
_dragcoef1=1;
|
|
rps[0]->calculateAlpha(v_wind,rho_null,0,0,0,&(torque[1]),&(lift[1]));
|
|
//0 degree, c1
|
|
|
|
_dragcoef0=1;
|
|
_dragcoef1=0;
|
|
rps[0]->calculateAlpha(v_wind,rho_null,_pitch_b,0,0,&(torque[2]),&(lift[2]));
|
|
//picth b, c0
|
|
|
|
_dragcoef0=0;
|
|
_dragcoef1=1;
|
|
rps[0]->calculateAlpha(v_wind,rho_null,_pitch_b,0,0,&(torque[3]),&(lift[3]));
|
|
//picth b, c1
|
|
|
|
if (torque[0]==0)
|
|
{
|
|
_dragcoef1=torque0/torque[1];
|
|
_dragcoef0=(torqueb-_dragcoef1*torque[3])/torque[2];
|
|
}
|
|
else
|
|
{
|
|
_dragcoef1=(torque0/torque[0]-torqueb/torque[2])
|
|
/(torque[1]/torque[0]-torque[3]/torque[2]);
|
|
_dragcoef0=(torqueb-_dragcoef1*torque[3])/torque[2];
|
|
}
|
|
}
|
|
else
|
|
{
|
|
_liftcoef=_airfoil_lift_coefficient/_number_of_parts*_number_of_blades;
|
|
_dragcoef0=_airfoil_drag_coefficient0/_number_of_parts*_number_of_blades*_c2;
|
|
_dragcoef1=_airfoil_drag_coefficient1/_number_of_parts*_number_of_blades*_c2;
|
|
}
|
|
|
|
//Check
|
|
rps[0]->calculateAlpha(v_wind,rho_null,_pitch_a,0,0,
|
|
&(torque[0]),&(lift[0])); //pitch a
|
|
rps[0]->calculateAlpha(v_wind,rho_null,_pitch_b,0,0,
|
|
&(torque[1]),&(lift[1])); //pitch b
|
|
rps[0]->calculateAlpha(v_wind,rho_null,0,0,0,
|
|
&(torque[3]),&(lift[3])); //pitch 0
|
|
SG_LOG(SG_GENERAL, SG_INFO,
|
|
"Rotor: coefficients for airfoil:" << endl << setprecision(6)
|
|
<< " drag0: " << _dragcoef0*_number_of_parts/_number_of_blades/_c2
|
|
<< " drag1: " << _dragcoef1*_number_of_parts/_number_of_blades/_c2
|
|
<< " lift: " << _liftcoef*_number_of_parts/_number_of_blades
|
|
<< endl
|
|
<< "at 10 deg:" << endl
|
|
<< "drag: " << (Math::sin(10./180*pi)*_dragcoef1+_dragcoef0)
|
|
*_number_of_parts/_number_of_blades/_c2
|
|
<< " lift: " << Math::sin(10./180*pi)*_liftcoef*_number_of_parts/_number_of_blades
|
|
<< endl
|
|
<< "Some results (Pitch [degree], Power [kW], Lift [N])" << endl
|
|
<< 0.0f << "deg " << Math::abs(torque[3]*_number_of_parts*_omegan/1000) << "kW "
|
|
<< lift[3]*_number_of_parts << endl
|
|
<< _pitch_a*180/pi << "deg " << Math::abs(torque[0]*_number_of_parts*_omegan/1000)
|
|
<< "kW " << lift[0]*_number_of_parts << endl
|
|
<< _pitch_b*180/pi << "deg " << Math::abs(torque[1]*_number_of_parts*_omegan/1000)
|
|
<< "kW " << lift[1]*_number_of_parts << endl << endl );
|
|
|
|
//first calculation of relamp is wrong
|
|
//it used pitchaforce, but this was unknown and
|
|
//on the default value
|
|
_delta*=lift[0]/pitchaforce;
|
|
relamp=(omega*omega/(2*_delta*Math::sqrt(sqr(omega0*omega0-omega*omega)
|
|
+4*_delta*_delta*omega*omega)))*_cyclic_factor;
|
|
for (i=0;i<_number_of_parts;i++)
|
|
{
|
|
rps[i]->setRelamp(relamp);
|
|
}
|
|
rps[0]->setOmega(0);
|
|
writeInfo();
|
|
}
|
|
std::ostream & operator<<(std::ostream & out, /*const*/ Rotor& r)
|
|
{
|
|
#define i(x) << #x << ":" << r.x << endl
|
|
#define iv(x) << #x << ":" << r.x[0] << ";" << r.x[1] << ";" <<r.x[2] << ";" << endl
|
|
out << "Writing Info on Rotor "
|
|
i(_name)
|
|
i(_torque)
|
|
i(_omega) i(_omegan) i(_omegarel) i(_ddt_omega) i(_omegarelneu)
|
|
i (_chord)
|
|
i( _taper)
|
|
i( _airfoil_incidence_no_lift)
|
|
i( _collective)
|
|
i( _airfoil_lift_coefficient)
|
|
i( _airfoil_drag_coefficient0)
|
|
i( _airfoil_drag_coefficient1)
|
|
i( _ccw)
|
|
i( _number_of_segments)
|
|
i( _number_of_parts)
|
|
iv( _base)
|
|
iv( _groundeffectpos[0])iv( _groundeffectpos[1])iv( _groundeffectpos[2])iv( _groundeffectpos[3])
|
|
i( _ground_effect_altitude)
|
|
iv( _normal)
|
|
iv( _normal_with_yaw_roll)
|
|
iv( _forward)
|
|
i( _diameter)
|
|
i( _number_of_blades)
|
|
i( _weight_per_blade)
|
|
i( _rel_blade_center)
|
|
i( _min_pitch)
|
|
i( _max_pitch)
|
|
i( _force_at_pitch_a)
|
|
i( _pitch_a)
|
|
i( _power_at_pitch_0)
|
|
i( _power_at_pitch_b)
|
|
i( _no_torque)
|
|
i( _sim_blades)
|
|
i( _pitch_b)
|
|
i( _rotor_rpm)
|
|
i( _rel_len_hinge)
|
|
i( _maxcyclicail)
|
|
i( _maxcyclicele)
|
|
i( _mincyclicail)
|
|
i( _mincyclicele)
|
|
i( _delta3)
|
|
i( _delta)
|
|
i( _dynamic)
|
|
i( _translift)
|
|
i( _c2)
|
|
i( _stepspersecond)
|
|
i( _engineon)
|
|
i( _alphamin) i(_alphamax) i(_alpha0) i(_alpha0factor)
|
|
i( _teeterdamp) i(_maxteeterdamp)
|
|
i( _rellenteeterhinge)
|
|
i( _translift_ve)
|
|
i( _translift_maxfactor)
|
|
i( _ground_effect_constant)
|
|
i( _vortex_state_lift_factor)
|
|
i( _vortex_state_c1)
|
|
i( _vortex_state_c2)
|
|
i( _vortex_state_c3)
|
|
i( _vortex_state_e1)
|
|
i( _vortex_state_e2)
|
|
i( _vortex_state_e3)
|
|
i( _lift_factor) i(_f_ge) i(_f_vs) i(_f_tl)
|
|
i( _vortex_state)
|
|
i( _liftcoef)
|
|
i( _dragcoef0)
|
|
i( _dragcoef1)
|
|
i( _twist) //outer incidence = inner inner incidence + _twist
|
|
i( _rel_len_where_incidence_is_measured)
|
|
i( _torque_of_inertia)
|
|
i( _rel_len_blade_start)
|
|
i( _incidence_stall_zero_speed)
|
|
i( _incidence_stall_half_sonic_speed)
|
|
i( _lift_factor_stall)
|
|
i( _stall_change_over)
|
|
i( _drag_factor_stall)
|
|
i( _stall_sum)
|
|
i( _stall_v2sum)
|
|
i( _yaw)
|
|
i( _roll)
|
|
i( _cyclicail)
|
|
i( _cyclicele)
|
|
i( _cyclic_factor) <<endl;
|
|
int j;
|
|
for(j=0; j<r._rotorparts.size(); j++) {
|
|
out << *((Rotorpart*)r._rotorparts.get(j));
|
|
}
|
|
out <<endl << endl;
|
|
#undef i
|
|
#undef iv
|
|
return out;
|
|
}
|
|
void Rotor:: writeInfo()
|
|
{
|
|
#ifdef TEST_DEBUG
|
|
std::ostringstream buffer;
|
|
buffer << *this;
|
|
FILE*f=fopen("c:\\fgmsvc\\bat\\log.txt","at");
|
|
if (!f) f=fopen("c:\\fgmsvc\\bat\\log.txt","wt");
|
|
if (f)
|
|
{
|
|
fprintf(f,"%s",(const char *)buffer.str().c_str());
|
|
fclose (f);
|
|
}
|
|
#endif
|
|
}
|
|
Rotorpart* Rotor::newRotorpart(float* pos, float *posforceattac, float *normal,
|
|
float* speed,float *dirzentforce, float zentforce,float maxpitchforce,
|
|
float maxpitch, float minpitch, float mincyclic,float maxcyclic,
|
|
float delta3,float mass,float translift,float rellenhinge,float len)
|
|
{
|
|
Rotorpart *r = new Rotorpart();
|
|
r->setPosition(pos);
|
|
r->setNormal(normal);
|
|
r->setZentipetalForce(zentforce);
|
|
r->setPositionForceAttac(posforceattac);
|
|
r->setSpeed(speed);
|
|
r->setDirectionofZentipetalforce(dirzentforce);
|
|
r->setMaxpitch(maxpitch);
|
|
r->setMinpitch(minpitch);
|
|
r->setMaxcyclic(maxcyclic);
|
|
r->setMincyclic(mincyclic);
|
|
r->setDelta3(delta3);
|
|
r->setDynamic(_dynamic);
|
|
r->setTranslift(_translift);
|
|
r->setC2(_c2);
|
|
r->setWeight(mass);
|
|
r->setRelLenHinge(rellenhinge);
|
|
r->setOmegaN(_omegan);
|
|
r->setAlpha0(_alpha0);
|
|
r->setAlphamin(_alphamin);
|
|
r->setAlphamax(_alphamax);
|
|
r->setAlpha0factor(_alpha0factor);
|
|
r->setLen(len);
|
|
r->setDiameter(_diameter);
|
|
r->setRotor(this);
|
|
#define p(a) r->setParameter(#a,_##a);
|
|
p(twist)
|
|
p(number_of_segments)
|
|
p(rel_len_where_incidence_is_measured)
|
|
p(rel_len_blade_start)
|
|
p(rotor_correction_factor)
|
|
#undef p
|
|
return r;
|
|
}
|
|
|
|
void Rotor::interp(float* v1, float* v2, float frac, float* out)
|
|
{
|
|
out[0] = v1[0] + frac*(v2[0]-v1[0]);
|
|
out[1] = v1[1] + frac*(v2[1]-v1[1]);
|
|
out[2] = v1[2] + frac*(v2[2]-v1[2]);
|
|
}
|
|
|
|
void Rotorgear::initRotorIteration(float *lrot,float dt)
|
|
{
|
|
int i;
|
|
float omegarel;
|
|
if (!_rotors.size()) return;
|
|
Rotor* r0 = (Rotor*)_rotors.get(0);
|
|
omegarel= r0->getOmegaRelNeu();
|
|
for(i=0; i<_rotors.size(); i++) {
|
|
Rotor* r = (Rotor*)_rotors.get(i);
|
|
r->inititeration(dt,omegarel,0,lrot);
|
|
}
|
|
}
|
|
|
|
void Rotorgear::calcForces(float* torqueOut)
|
|
{
|
|
int i,j;
|
|
torqueOut[0]=torqueOut[1]=torqueOut[2]=0;
|
|
// check,<if the engine can handle the torque of the rotors.
|
|
// If not reduce the torque to the fueselage and change rotational
|
|
// speed of the rotors instead
|
|
if (_rotors.size())
|
|
{
|
|
float omegarel,omegan;
|
|
Rotor* r0 = (Rotor*)_rotors.get(0);
|
|
omegarel= r0->getOmegaRel();
|
|
|
|
float total_torque_of_inertia=0;
|
|
float total_torque=0;
|
|
for(i=0; i<_rotors.size(); i++) {
|
|
Rotor* r = (Rotor*)_rotors.get(i);
|
|
omegan=r->getOmegan();
|
|
total_torque_of_inertia+=r->getTorqueOfInertia()*omegan*omegan;
|
|
//FIXME: this is constant, so this can be done in compile
|
|
|
|
total_torque+=r->getTorque()*omegan;
|
|
}
|
|
float max_torque_of_engine=0;
|
|
if (_engineon)
|
|
{
|
|
max_torque_of_engine=_max_power_engine;
|
|
float df=1-omegarel;
|
|
df/=_engine_prop_factor;
|
|
df = Math::clamp(df, 0, 1);
|
|
max_torque_of_engine = df * _max_power_engine;
|
|
}
|
|
total_torque*=-1;
|
|
_ddt_omegarel=0;
|
|
float rel_torque_engine=1;
|
|
if (total_torque<=0)
|
|
rel_torque_engine=0;
|
|
else
|
|
if (max_torque_of_engine>0)
|
|
rel_torque_engine=1/max_torque_of_engine*total_torque;
|
|
else
|
|
rel_torque_engine=0;
|
|
|
|
//add the rotor brake and the gear fritcion
|
|
float dt=0.1f;
|
|
if (r0->_rotorparts.size()) dt=((Rotorpart*)r0->_rotorparts.get(0))->getDt();
|
|
|
|
float rotor_brake_torque;
|
|
rotor_brake_torque=_rotorbrake*_max_power_rotor_brake+_rotorgear_friction;
|
|
//clamp it to the value you need to stop the rotor
|
|
//to avod accelerate the rotor to neagtive rpm:
|
|
rotor_brake_torque=Math::clamp(rotor_brake_torque,0,
|
|
total_torque_of_inertia/dt*omegarel);
|
|
max_torque_of_engine-=rotor_brake_torque;
|
|
|
|
//change the rotation of the rotors
|
|
if ((max_torque_of_engine<total_torque) //decreasing rotation
|
|
||((max_torque_of_engine>total_torque)&&(omegarel<1))
|
|
//increasing rotation due to engine
|
|
||(total_torque<0) ) //increasing rotation due to autorotation
|
|
{
|
|
_ddt_omegarel=(max_torque_of_engine-total_torque)/total_torque_of_inertia;
|
|
if(max_torque_of_engine>total_torque)
|
|
{
|
|
//check if the acceleration is due to the engine. If yes,
|
|
//the engine self limits the accel.
|
|
float lim1=-total_torque/total_torque_of_inertia;
|
|
//accel. by autorotation
|
|
|
|
if (lim1<_engine_accel_limit) lim1=_engine_accel_limit;
|
|
//if the accel by autorotation greater than the max. engine
|
|
//accel, then this is the limit, if not: the engine is the limit
|
|
if (_ddt_omegarel>lim1) _ddt_omegarel=lim1;
|
|
}
|
|
if (_ddt_omegarel>5.5)_ddt_omegarel=5.5;
|
|
//clamp it to avoid overflow. Should never be reached
|
|
if (_ddt_omegarel<-5.5)_ddt_omegarel=-5.5;
|
|
|
|
if (_max_power_engine<0.001) {omegarel=1;_ddt_omegarel=0;}
|
|
//for debug: negative or no maxpower will result
|
|
//in permanet 100% rotation
|
|
|
|
omegarel+=dt*_ddt_omegarel;
|
|
|
|
if (omegarel>2.5) omegarel=2.5;
|
|
//clamp it to avoid overflow. Should never be reached
|
|
if (omegarel<-.5) omegarel=-.5;
|
|
|
|
r0->setOmegaRelNeu(omegarel);
|
|
//calculate the torque, which is needed to accelerate the rotors.
|
|
//Add this additional torque to the body
|
|
for(j=0; j<_rotors.size(); j++) {
|
|
Rotor* r = (Rotor*)_rotors.get(j);
|
|
for(i=0; i<r->_rotorparts.size(); i++) {
|
|
float torque_scalar=0;
|
|
Rotorpart* rp = (Rotorpart*)r->_rotorparts.get(i);
|
|
float torque[3];
|
|
rp->getAccelTorque(_ddt_omegarel,torque);
|
|
Math::add3(torque,torqueOut,torqueOut);
|
|
}
|
|
}
|
|
}
|
|
_total_torque_on_engine=total_torque+_ddt_omegarel*total_torque_of_inertia;
|
|
}
|
|
}
|
|
|
|
void Rotorgear::addRotor(Rotor* rotor)
|
|
{
|
|
_rotors.add(rotor);
|
|
_in_use = 1;
|
|
}
|
|
|
|
void Rotorgear::compile()
|
|
{
|
|
float wgt = 0;
|
|
for(int j=0; j<_rotors.size(); j++) {
|
|
Rotor* r = (Rotor*)_rotors.get(j);
|
|
r->compile();
|
|
}
|
|
}
|
|
|
|
void Rotorgear::getDownWash(float *pos, float * v_heli, float *downwash)
|
|
{
|
|
float tmp[3];
|
|
downwash[0]=downwash[1]=downwash[2]=0;
|
|
for(int i=0; i<_rotors.size(); i++) {
|
|
Rotor* ro = (Rotor*)_rotors.get(i);
|
|
ro->getDownWash(pos,v_heli,tmp);
|
|
Math::add3(downwash,tmp,downwash); // + downwash
|
|
}
|
|
}
|
|
|
|
void Rotorgear::setParameter(char *parametername, float value)
|
|
{
|
|
#define p(a,b) if (strcmp(parametername,#a)==0) _##a = (value * (b)); else
|
|
p(max_power_engine,1000)
|
|
p(engine_prop_factor,1)
|
|
p(yasimdragfactor,1)
|
|
p(yasimliftfactor,1)
|
|
p(max_power_rotor_brake,1000)
|
|
p(rotorgear_friction,1000)
|
|
p(engine_accel_limit,0.01)
|
|
SG_LOG(SG_INPUT, SG_ALERT,
|
|
"internal error in parameter set up for rotorgear: '"
|
|
<< parametername <<"'" << endl);
|
|
#undef p
|
|
}
|
|
int Rotorgear::getValueforFGSet(int j,char *text,float *f)
|
|
{
|
|
if (j==0)
|
|
{
|
|
sprintf(text,"/rotors/gear/total-torque");
|
|
*f=_total_torque_on_engine;
|
|
} else return 0;
|
|
return j+1;
|
|
}
|
|
Rotorgear::Rotorgear()
|
|
{
|
|
_in_use=0;
|
|
_engineon=0;
|
|
_rotorbrake=0;
|
|
_max_power_rotor_brake=1;
|
|
_rotorgear_friction=1;
|
|
_max_power_engine=1000*450;
|
|
_engine_prop_factor=0.05f;
|
|
_yasimdragfactor=1;
|
|
_yasimliftfactor=1;
|
|
_ddt_omegarel=0;
|
|
_engine_accel_limit=0.05f;
|
|
_total_torque_on_engine=0;
|
|
}
|
|
|
|
Rotorgear::~Rotorgear()
|
|
{
|
|
for(int i=0; i<_rotors.size(); i++)
|
|
delete (Rotor*)_rotors.get(i);
|
|
}
|
|
|
|
}; // namespace yasim
|