6#include <simgear/debug/logstream.hxx>
10#include "Rotorpart.hpp"
24using std::setprecision;
29const float pi=3.14159;
31static inline float sqr(
float a) {
return a * a; }
58 _max_pitch=13./180*
pi;
59 _maxcyclicail=10./180*
pi;
60 _maxcyclicele=10./180*
pi;
62 _mincyclicail=-10./180*
pi;
63 _mincyclicele=-10./180*
pi;
64 _min_pitch=-.5/180*
pi;
70 _normal_with_yaw_roll[0]= _normal_with_yaw_roll[1]=0;
71 _normal_with_yaw_roll[2]=1;
73 _omega=_omegan=_omegarel=_omegarelneu=0;
83 _shared_flap_hinge=
false;
84 _rellenteeterhinge=0.01;
91 _translift_maxfactor=1.3;
92 _ground_effect_constant=0.1;
93 _vortex_state_lift_factor=0.4;
106 _number_of_segments=1;
108 _rel_len_where_incidence_is_measured=0.7;
109 _torque_of_inertia=1;
113 _airfoil_incidence_no_lift=0;
114 _rel_len_blade_start=0;
115 _airfoil_lift_coefficient=0;
116 _airfoil_drag_coefficient0=0;
117 _airfoil_drag_coefficient1=0;
119 _global_ground[
i] = _tilt_center[
i] = 0;
120 _global_ground[2] = 1;
121 _global_ground[3] = -1e3;
122 _incidence_stall_zero_speed=18*
pi/180.;
123 _incidence_stall_half_sonic_speed=14*
pi/180.;
124 _lift_factor_stall=0.28;
125 _stall_change_over=2*
pi/180.;
126 _drag_factor_stall=8;
131 for (
int k=0;k<4;k++)
133 _groundeffectpos[k][
i]=0;
134 _ground_effect_altitude=1;
136 _lift_factor=_f_ge=_f_vs=_f_tl=1;
137 _rotor_correction_factor=.65;
141 _num_ground_contact_pos=0;
142 _directions_and_postions_dirty=
true;
161 for(
i=0;
i<_rotorparts.size();
i++) {
162 Rotorpart* r = (Rotorpart*)_rotorparts.get(
i);
168 SGPropertyNode * node =
fgGetNode(
"/rotors",
true)->getNode(_name,
true);
169 node->untie(
"balance-ext");
170 node->untie(
"balance-int");
175void Rotor::inititeration(
float dt,
float omegarel,
float ddt_omegarel,
float *rot)
180 _omega=_omegan*_omegarel;
181 _ddt_omega=_omegan*ddt_omegarel;
184 updateDirectionsAndPositions(drot);
185 Math::add3(rot,drot,drot);
186 for(
i=0;
i<_rotorparts.size();
i++) {
187 float s = Math::sin(
float(2*
pi*
i/_number_of_parts+(_phi-
pi/2.)*(_ccw?1:-1)));
188 float c = Math::cos(
float(2*
pi*
i/_number_of_parts+(_phi-
pi/2.)*(_ccw?1:-1)));
189 Rotorpart* r = (Rotorpart*)_rotorparts.get(
i);
191 r->setDdtOmega(_ddt_omega);
192 r->inititeration(dt,drot);
193 r->setCyclic(_cyclicail*c+_cyclicele*s);
197 float side[3],help[3];
199 Math::mul3(Math::cos(_yaw)*Math::cos(_roll),
_normal,_normal_with_yaw_roll);
201 Math::mul3(Math::sin(_yaw),
_forward,help);
202 Math::add3(_normal_with_yaw_roll,help,_normal_with_yaw_roll);
204 Math::mul3(Math::sin(_roll),side,help);
205 Math::add3(_normal_with_yaw_roll,help,_normal_with_yaw_roll);
208 if ((_balance1*_balance2 < 0.97) && (_balance1>-1))
210 _balance1-=(0.97-_balance1*_balance2)*(0.97-_balance1*_balance2)*0.005;
211 if (_balance1<-1) _balance1=-1;
215float Rotor::calcStall(
float incidence,
float speed)
217 float stall_incidence=_incidence_stall_zero_speed
218 +(_incidence_stall_half_sonic_speed
219 -_incidence_stall_zero_speed)*speed/(343./2);
221 incidence = Math::abs(incidence);
222 if (incidence > (90./180.*
pi))
223 incidence =
pi-incidence;
224 float stall = (incidence-stall_incidence)/_stall_change_over;
225 stall = Math::clamp(stall,0,1);
227 _stall_sum+=stall*speed*speed;
228 _stall_v2sum+=speed*speed;
233float Rotor::getLiftCoef(
float incidence,
float speed)
235 float stall=calcStall(incidence,speed);
243 if (incidence > (
pi/2))
245 else if (incidence <-(
pi/2))
249 float c1= (i2-_airfoil_incidence_no_lift)*_liftcoef;
252 float c2= Math::sin(2*(incidence-_airfoil_incidence_no_lift))
253 *_liftcoef*_lift_factor_stall;
254 return (1-stall)*c1 + stall *c2;
260float Rotor::getDragCoef(
float incidence,
float speed)
262 float stall=calcStall(incidence,speed);
263 float c1= (Math::abs(Math::sin(incidence-_airfoil_incidence_no_lift))
264 *_dragcoef1+_dragcoef0);
265 float c2= c1*_drag_factor_stall;
266 return (1-stall)*c1 + stall *c2;
269int Rotor::getValueforFGSet(
int j,
char *text,
float *f)
271 if (_name[0]==0)
return 0;
272 if (4>numRotorparts())
return 0;
276 snprintf(text, 300,
"/rotors/%s/cone-deg", _name);
277 *f=(_balance1>-1)?( ((Rotorpart*)getRotorpart(0))->getrealAlpha()
278 +((Rotorpart*)getRotorpart(1*(_number_of_parts>>2)))->getrealAlpha()
279 +((Rotorpart*)getRotorpart(2*(_number_of_parts>>2)))->getrealAlpha()
280 +((Rotorpart*)getRotorpart(3*(_number_of_parts>>2)))->getrealAlpha()
285 snprintf(text, 300,
"/rotors/%s/roll-deg", _name);
286 _roll = ( ((Rotorpart*)getRotorpart(0))->getrealAlpha()
287 -((Rotorpart*)getRotorpart(2*(_number_of_parts>>2)))->getrealAlpha()
289 *f=(_balance1>-1)?_roll *180/
pi:0;
293 snprintf(text, 300,
"/rotors/%s/yaw-deg", _name);
294 _yaw=( ((Rotorpart*)getRotorpart(1*(_number_of_parts>>2)))->getrealAlpha()
295 -((Rotorpart*)getRotorpart(3*(_number_of_parts>>2)))->getrealAlpha()
297 *f=(_balance1>-1)?_yaw*180/
pi:0;
301 snprintf(text, 300,
"/rotors/%s/rpm", _name);
302 *f=(_balance1>-1)?_omega/2/
pi*60:0;
306 snprintf(text, 300,
"/rotors/%s/tilt/pitch-deg",_name);
307 *f=_tilt_pitch*180/
pi;
311 snprintf(text, 300,
"/rotors/%s/tilt/roll-deg",_name);
312 *f=_tilt_roll*180/
pi;
316 snprintf(text, 300,
"/rotors/%s/tilt/yaw-deg",_name);
321 snprintf(text, 300,
"/rotors/%s/balance", _name);
326 snprintf(text, 300,
"/rotors/%s/stall",_name);
327 *f=getOverallStall();
331 snprintf(text, 300,
"/rotors/%s/torque",_name);
337 if (b>=_number_of_blades)
342 snprintf(text, 300,
"/rotors/%s/blade[%i]/%s",
344 w==0?
"position-deg":(w==1?
"flap-deg":
"incidence-deg"));
345 *f=((Rotorpart*)getRotorpart(0))->getPhi()*180/
pi
346 +360*b/_number_of_blades*(_ccw?1:-1);
349 if (_balance1<=-1) *f=0;
356 rk=Math::clamp(rk,0,1);
358 if(w==2) {k+=2;l+=2;}
359 else if(w==1) {k+=1;l+=1;}
363 if (w==1) *f=rk*((Rotorpart*) getRotorpart(k*(_number_of_parts>>2)))->getrealAlpha()*180/
pi
364 +rl*((Rotorpart*) getRotorpart(l*(_number_of_parts>>2)))->getrealAlpha()*180/
pi;
365 else if(w==2) *f=rk*((Rotorpart*)getRotorpart(k*(_number_of_parts>>2)))->getIncidence()*180/
pi
366 +rl*((Rotorpart*)getRotorpart(l*(_number_of_parts>>2)))->getIncidence()*180/
pi;
371void Rotorgear::setEngineOn(
int value)
376void Rotorgear::setRotorEngineMaxRelTorque(
float lval)
378 _max_rel_torque=lval;
381void Rotorgear::setRotorRelTarget(
float lval)
383 _target_rel_rpm=lval;
386void Rotor::setAlpha0(
float f)
391void Rotor::setAlphamin(
float f)
396void Rotor::setAlphamax(
float f)
401void Rotor::setAlpha0factor(
float f)
406int Rotor::numRotorparts()
408 return _rotorparts.size();
411Rotorpart* Rotor::getRotorpart(
int n)
413 return ((Rotorpart*)_rotorparts.get(n));
416int Rotorgear::getEngineon()
421float Rotor::getTorqueOfInertia()
423 return _torque_of_inertia;
426void Rotor::setTorque(
float f)
431void Rotor::addTorque(
float f)
436void Rotor::strncpy(
char *dest,
const char *src,
int maxlen)
439 while(src[n]&&n<(maxlen-1))
447void Rotor::setNormal(
float* normal)
450 float invsum,sqrsum=0;
451 for(
i=0;
i<3;
i++) { sqrsum+=normal[
i]*normal[
i];}
453 invsum=1/Math::sqrt(sqrsum);
458 _normal_with_yaw_roll[
i]=
_normal[
i] = normal[
i]*invsum;
462void Rotor::setForward(
float* forward)
465 float invsum,sqrsum=0;
466 for(
i=0;
i<3;
i++) { sqrsum+=forward[
i]*forward[
i];}
468 invsum=1/Math::sqrt(sqrsum);
474void Rotor::setForceAtPitchA(
float force)
476 _force_at_pitch_a=force;
479void Rotor::setPowerAtPitch0(
float value)
481 _power_at_pitch_0=value;
484void Rotor::setPowerAtPitchB(
float value)
486 _power_at_pitch_b=value;
489void Rotor::setPitchA(
float value)
491 _pitch_a=value/180*
pi;
494void Rotor::setPitchB(
float value)
496 _pitch_b=value/180*
pi;
499void Rotor::setBase(
float* base)
505void Rotor::setMaxCyclicail(
float value)
507 _maxcyclicail=value/180*
pi;
510void Rotor::setMaxCyclicele(
float value)
512 _maxcyclicele=value/180*
pi;
515void Rotor::setMinCyclicail(
float value)
517 _mincyclicail=value/180*
pi;
520void Rotor::setMinCyclicele(
float value)
522 _mincyclicele=value/180*
pi;
525void Rotor::setMinCollective(
float value)
527 _min_pitch=value/180*
pi;
530void Rotor::setMinTiltYaw(
float value)
532 _min_tilt_yaw=value/180*
pi;
535void Rotor::setMinTiltPitch(
float value)
537 _min_tilt_pitch=value/180*
pi;
540void Rotor::setMinTiltRoll(
float value)
542 _min_tilt_roll=value/180*
pi;
545void Rotor::setMaxTiltYaw(
float value)
547 _max_tilt_yaw=value/180*
pi;
550void Rotor::setMaxTiltPitch(
float value)
552 _max_tilt_pitch=value/180*
pi;
555void Rotor::setMaxTiltRoll(
float value)
557 _max_tilt_roll=value/180*
pi;
560void Rotor::setTiltCenterX(
float value)
562 _tilt_center[0] = value;
565void Rotor::setTiltCenterY(
float value)
567 _tilt_center[1] = value;
570void Rotor::setTiltCenterZ(
float value)
572 _tilt_center[2] = value;
575void Rotor::setMaxCollective(
float value)
577 _max_pitch=value/180*
pi;
580void Rotor::setDiameter(
float value)
585void Rotor::setWeightPerBlade(
float value)
587 _weight_per_blade=value;
590void Rotor::setNumberOfBlades(
float value)
592 _number_of_blades=int(value+.5);
595void Rotor::setRelBladeCenter(
float value)
597 _rel_blade_center=value;
600void Rotor::setDynamic(
float value)
605void Rotor::setDelta3(
float value)
610void Rotor::setDelta(
float value)
615void Rotor::setTranslift(
float value)
620void Rotor::setSharedFlapHinge(
bool s)
622 _shared_flap_hinge=s;
625void Rotor::setBalance(
float b)
630void Rotor::setC2(
float value)
635void Rotor::setStepspersecond(
float steps)
637 _stepspersecond=steps;
640void Rotor::setRPM(
float value)
645void Rotor::setPhiNull(
float value)
650void Rotor::setRelLenHinge(
float value)
652 _rel_len_hinge=value;
655void Rotor::setDownwashFactor(
float value)
657 _downwash_factor=value;
660void Rotor::setAlphaoutput(
int i,
const char *text)
662 strncpy(_alphaoutput[
i],text,255);
665void Rotor::setName(
const char *text)
667 strncpy(_name,text,256);
670void Rotor::setCcw(
int ccw)
675void Rotor::setNotorque(
int value)
680void Rotor::setRelLenTeeterHinge(
float f)
682 _rellenteeterhinge=f;
685void Rotor::setTeeterdamp(
float f)
690void Rotor::setMaxteeterdamp(
float f)
695void Rotor::setGlobalGround(
double *global_ground,
float* global_vel)
698 for(
i=0;
i<4;
i++) _global_ground[
i] = global_ground[
i];
701void Rotor::setParameter(
const char *parametername,
float value)
703#define p(a,b) if (strcmp(parametername,#a)==0) _##a = (value * (b)); else
705 p(translift_maxfactor,1)
706 p(ground_effect_constant,1)
707 p(vortex_state_lift_factor,1)
715 p(number_of_segments,1)
717 p(rel_len_where_incidence_is_measured,1)
720 p(airfoil_incidence_no_lift,
pi/180.)
721 p(rel_len_blade_start,1)
722 p(incidence_stall_zero_speed,
pi/180.)
723 p(incidence_stall_half_sonic_speed,
pi/180.)
724 p(lift_factor_stall,1)
725 p(stall_change_over,
pi/180.)
726 p(drag_factor_stall,1)
727 p(airfoil_lift_coefficient,1)
728 p(airfoil_drag_coefficient0,1)
729 p(airfoil_drag_coefficient1,1)
731 p(rotor_correction_factor,1)
732 SG_LOG(SG_INPUT, SG_ALERT,
733 "internal error in parameter set up for rotor: '" <<
734 parametername <<
"'" << std::endl);
738float Rotor::getLiftFactor()
743void Rotorgear::setRotorBrake(
float lval)
745 lval = Math::clamp(lval, 0, 1);
749void Rotor::setTiltYaw(
float lval)
751 lval = Math::clamp(lval, -1, 1);
752 _tilt_yaw = _min_tilt_yaw+(lval+1)/2*(_max_tilt_yaw-_min_tilt_yaw);
753 _directions_and_postions_dirty =
true;
756void Rotor::setTiltPitch(
float lval)
758 lval = Math::clamp(lval, -1, 1);
759 _tilt_pitch = _min_tilt_pitch+(lval+1)/2*(_max_tilt_pitch-_min_tilt_pitch);
760 _directions_and_postions_dirty =
true;
763void Rotor::setTiltRoll(
float lval)
765 lval = Math::clamp(lval, -1, 1);
766 _tilt_roll = _min_tilt_roll+(lval+1)/2*(_max_tilt_roll-_min_tilt_roll);
767 _directions_and_postions_dirty =
true;
770void Rotor::setCollective(
float lval)
772 lval = Math::clamp(lval, -1, 1);
774 _collective=_min_pitch+(lval+1)/2*(_max_pitch-_min_pitch);
775 for(
i=0;
i<_rotorparts.size();
i++) {
776 ((Rotorpart*)_rotorparts.get(
i))->setCollective(_collective);
780void Rotor::setCyclicele(
float lval,
float rval)
782 lval = Math::clamp(lval, -1, 1);
783 _cyclicele=_mincyclicele+(lval+1)/2*(_maxcyclicele-_mincyclicele);
786void Rotor::setCyclicail(
float lval,
float rval)
788 lval = Math::clamp(lval, -1, 1);
790 _cyclicail=-(_mincyclicail+(lval+1)/2*(_maxcyclicail-_mincyclicail));
793void Rotor::setRotorBalance(
float lval)
795 lval = Math::clamp(lval, -1, 1);
799void Rotor::getPosition(
float* out)
805void Rotor::calcLiftFactor(
float* v,
float rho, State *s)
817 _f_ge=1+_diameter/_ground_effect_altitude*_ground_effect_constant;
823 float v_horiz = Math::mag3(help);
824 _f_tl = ((1-Math::pow(2.7183,-v_horiz/_translift_ve))
825 *(_translift_maxfactor-1)+1)/_translift_maxfactor;
827 _lift_factor = _f_ge*_f_tl*_f_vs;
830 Glue::geodUp(s->pos, _grav_direction);
831 s->globalToLocal(_grav_direction, _grav_direction);
834void Rotor::findGroundEffectAltitude(Ground * ground_cb,State *s)
836 _ground_effect_altitude=findGroundEffectAltitude(ground_cb,s,
837 _groundeffectpos[0],_groundeffectpos[1],
838 _groundeffectpos[2],_groundeffectpos[3]);
839 testForRotorGroundContact(ground_cb,s);
842void Rotor::testForRotorGroundContact(Ground * ground_cb,State *s)
845 for (
i=0;
i<_num_ground_contact_pos;
i++)
848 s->posLocalToGlobal(_ground_contact_pos[
i], pt);
851 double global_ground[4];
854 ground_cb->getGroundPlane(pt, global_ground, global_vel, body);
858 s->planeGlobalToLocal(global_ground, ground);
860 h = ground[3] - Math::dot3(_ground_contact_pos[
i], ground);
864 _balance1 -= (-h)/_diameter/_num_ground_contact_pos;
865 _balance1 = (_balance1<-1)?-1:_balance1;
869float Rotor::findGroundEffectAltitude(Ground * ground_cb,State *s,
870 float *pos0,
float *pos1,
float *pos2,
float *pos3,
871 int iteration,
float a0,
float a1,
float a2,
float a3)
885 Math::add3(
p[0],
p[2],
p[4]);
886 Math::mul3(0.5,
p[4],
p[4]);
888 float mina=100*_diameter;
890 for (
int i=0;
i<5;
i++)
897 s->posLocalToGlobal(
p[
i], pt);
900 double global_ground[4];
903 ground_cb->getGroundPlane(pt, global_ground, global_vel, body);
907 s->planeGlobalToLocal(global_ground, ground);
909 a[
i] = ground[3] - Math::dot3(
p[
i], ground);
916 if (mina>=10*_diameter)
928 ||(Math::abs(suma*0.2-a[4])<(0.15*0.2*suma*(1<<iteration))))
931 float pc[4][3],ac[4];
932 for (
int i=0;
i<4;
i++)
934 Math::add3(
p[
i],
p[(
i+1)&3],pc[
i]);
935 Math::mul3(0.5,pc[
i],pc[
i]);
937 s->posLocalToGlobal(pc[
i], pt);
940 double global_ground[4];
943 ground_cb->getGroundPlane(pt, global_ground, global_vel, body);
947 s->planeGlobalToLocal(global_ground, ground);
949 ac[
i] = ground[3] - Math::dot3(
p[
i], ground);
953 (findGroundEffectAltitude(ground_cb,s,
p[0],pc[1],
p[4],pc[3],
954 iteration+1,a[0],ac[0],a[4],ac[3])
955 +findGroundEffectAltitude(ground_cb,s,
p[1],pc[0],
p[4],pc[1],
956 iteration+1,a[1],ac[0],a[4],ac[1])
957 +findGroundEffectAltitude(ground_cb,s,
p[2],pc[1],
p[4],pc[2],
958 iteration+1,a[2],ac[1],a[4],ac[2])
959 +findGroundEffectAltitude(ground_cb,s,
p[3],pc[2],
p[4],pc[3],
960 iteration+1,a[3],ac[2],a[4],ac[3])
964void Rotor::getDownWash(
const float *pos,
const float *v_heli,
float *downwash)
966 float pos2rotor[3],tmp[3];
967 Math::sub3(
_base,pos,pos2rotor);
968 float dist=Math::dot3(pos2rotor,_normal_with_yaw_roll);
970 float inc = _collective+_twist *0.7
971 - _twist*_rel_len_where_incidence_is_measured;
976 downwash[0]=downwash[1]=downwash[2]=0.;
981 float v1bar = Math::sin(inc) *_omega * 0.35 * _diameter * 0.8;
986 if (v1bar< 1) v1bar = 1;
987 float time=dist/v1bar;
990 Math::mul3(time,v_heli,tmp);
991 Math::sub3(pos2rotor,tmp,pos2rotor);
994 dist=Math::dot3(pos2rotor,_normal_with_yaw_roll);
1002 downwash[0]=downwash[1]=downwash[2]=0.;
1009 Math::cross3(pos2rotor,_normal_with_yaw_roll,tmp);
1010 float r= Math::mag3(tmp);
1012 float rel_r = r *2 /_diameter;
1013 float inc_r = _collective+_twist * r /_diameter * 2
1014 - _twist*_rel_len_where_incidence_is_measured;
1019 v1 = Math::sin(inc_r) *_omega * r * 0.8;
1027 float sigma=_diameter/2 + dist * dist / _diameter /4.;
1028 float v2 = v1bar*_diameter/ (Math::sqrt(2 *
pi) * sigma)
1029 * Math::pow(2.7183,-.5*r*r/(sigma*sigma))*_diameter/2/sigma;
1033 float g = Math::pow(2.7183,-2*dist/_diameter);
1036 float v =
g * v1 + (1-
g) * v2;
1037 Math::mul3(-v*_downwash_factor,_normal_with_yaw_roll,downwash);
1041void Rotor::euler2orient(
float roll,
float pitch,
float hdg,
float* out)
1045 Glue::euler2orient(roll,pitch,hdg,out);
1046 for (
int i=3;
i<9;
i++) out[
i]*=-1.0;
1050void Rotor::updateDirectionsAndPositions(
float *rot)
1052 if (!_directions_and_postions_dirty)
1054 rot[0]=rot[1]=rot[2]=0;
1057 rot[0]=_old_tilt_roll-_tilt_roll;
1058 rot[1]=_old_tilt_pitch-_tilt_pitch;
1059 rot[2]=_old_tilt_yaw-_tilt_yaw;
1060 _old_tilt_roll=_tilt_roll;
1061 _old_tilt_pitch=_tilt_pitch;
1062 _old_tilt_yaw=_tilt_yaw;
1064 euler2orient(_tilt_roll, _tilt_pitch, _tilt_yaw, orient);
1068 Math::sub3(
_base,_tilt_center,base);
1069 Math::vmul33(orient, base, base);
1070 Math::add3(base,_tilt_center,base);
1071 Math::vmul33(orient,
_forward, forward);
1072 Math::vmul33(orient,
_normal, normal);
1074#define _forward forward
1075#define _normal normal
1076 float directions[5][3];
1085 Math::cross3(directions[
i-1],
_normal,directions[
i]);
1087 Math::cross3(
_normal,directions[
i-1],directions[
i]);
1089 Math::set3(directions[4],directions[0]);
1094 for (
i=0;
i<_num_ground_contact_pos;
i++)
1097 float s = Math::sin(
pi*2*
i/_num_ground_contact_pos);
1098 float c = Math::cos(
pi*2*
i/_num_ground_contact_pos);
1099 Math::mul3(c*_diameter*0.5,directions[0],_ground_contact_pos[
i]);
1100 Math::mul3(s*_diameter*0.5,directions[1],help);
1101 Math::add3(help,_ground_contact_pos[
i],_ground_contact_pos[
i]);
1102 Math::add3(
_base,_ground_contact_pos[
i],_ground_contact_pos[
i]);
1106 Math::mul3(_diameter*0.7,directions[
i],_groundeffectpos[
i]);
1107 Math::add3(
_base,_groundeffectpos[
i],_groundeffectpos[
i]);
1109 for (
i=0;
i<_number_of_parts;
i++)
1111 Rotorpart* rp = getRotorpart(
i);
1112 float lpos[3],lforceattac[3],lspeed[3],dirzentforce[3];
1113 float s = Math::sin(2*
pi*
i/_number_of_parts);
1114 float c = Math::cos(2*
pi*
i/_number_of_parts);
1115 float sp = Math::sin(
float(2*
pi*
i/_number_of_parts-
pi/2.+_phi));
1116 float cp = Math::cos(
float(2*
pi*
i/_number_of_parts-
pi/2.+_phi));
1117 float direction[3],nextdirection[3],help[3],direction90deg[3];
1118 float rotorpartmass = _weight_per_blade*_number_of_blades/_number_of_parts*.453;
1119 float speed=_rotor_rpm/60*_diameter*_rel_blade_center*
pi;
1120 float lentocenter=_diameter*_rel_blade_center*0.5;
1121 float lentoforceattac=_diameter*_rel_len_hinge*0.5;
1122 float zentforce=rotorpartmass*speed*speed/lentocenter;
1124 Math::mul3(c ,directions[0],help);
1125 Math::mul3(s ,directions[1],direction);
1126 Math::add3(help,direction,direction);
1128 Math::mul3(c ,directions[1],help);
1129 Math::mul3(s ,directions[2],direction90deg);
1130 Math::add3(help,direction90deg,direction90deg);
1132 Math::mul3(cp ,directions[1],help);
1133 Math::mul3(sp ,directions[2],nextdirection);
1134 Math::add3(help,nextdirection,nextdirection);
1136 Math::mul3(lentocenter,direction,lpos);
1137 Math::add3(lpos,
_base,lpos);
1138 Math::mul3(lentoforceattac,nextdirection,lforceattac);
1141 Math::add3(lforceattac,
_base,lforceattac);
1142 Math::mul3(speed,direction90deg,lspeed);
1143 Math::mul3(1,nextdirection,dirzentforce);
1144 rp->setPosition(lpos);
1146 rp->setZentipetalForce(zentforce);
1147 rp->setPositionForceAttac(lforceattac);
1148 rp->setSpeed(lspeed);
1149 rp->setDirectionofZentipetalforce(dirzentforce);
1150 rp->setDirectionofRotorPart(direction);
1155 _directions_and_postions_dirty=
false;
1158void Rotor::compile()
1161 if(! _rotorparts.empty())
return;
1168 _number_of_parts=(int(Math::clamp(_number_of_parts,4,256))>>2)<<2;
1170 _dynamic=_dynamic*(1/
1172 +(60/_rotor_rpm)/(2*_number_of_blades)
1178 _num_ground_contact_pos=(_number_of_parts<16)?_number_of_parts:16;
1179 float rotorpartmass = _weight_per_blade*_number_of_blades/_number_of_parts*.453;
1182 _torque_of_inertia = 1/12. * ( _number_of_parts * rotorpartmass) * _diameter
1183 * _diameter * _rel_blade_center * _rel_blade_center /(0.5*0.5);
1184 float speed=_rotor_rpm/60*_diameter*_rel_blade_center*
pi;
1185 float lentocenter=_diameter*_rel_blade_center*0.5;
1187 float zentforce=rotorpartmass*speed*speed/lentocenter;
1188 float pitchaforce=_force_at_pitch_a/_number_of_parts*.453*9.81;
1192 float torque0=0,torquemax=0,torqueb=0;
1193 float omega=_rotor_rpm/60*2*
pi;
1195 float omega0=omega*Math::sqrt(1/(1-_rel_len_hinge));
1196 float delta_theoretical=pitchaforce/(_pitch_a*omega*lentocenter*2*rotorpartmass);
1197 _delta*=delta_theoretical;
1199 float relamp=(omega*omega/(2*_delta*Math::sqrt(
sqr(omega0*omega0-omega*omega)
1200 +4*_delta*_delta*omega*omega)))*_cyclic_factor;
1203 _phi=Math::acos(_rel_len_hinge);
1204 _phi-=Math::atan(_delta3);
1207 torque0=_power_at_pitch_0/_number_of_parts*1000/omega;
1209 torqueb=_power_at_pitch_b/_number_of_parts*1000/omega;
1210 torquemax=_power_at_pitch_b/_number_of_parts*1000/omega/_pitch_b*_max_pitch;
1220 Rotorpart* rps[256];
1222 for (
i=0;
i<_number_of_parts;
i++)
1224 Rotorpart* rp=rps[
i]=newRotorpart(zentforce,pitchaforce,_delta3,rotorpartmass,
1225 _translift,_rel_len_hinge,lentocenter);
1226 int k =
i*4/_number_of_parts;
1227 rp->setAlphaoutput(_alphaoutput[k&1?k:(_ccw?k^2:k)],0);
1228 rp->setAlphaoutput(_alphaoutput[4+(k&1?k:(_ccw?k^2:k))],1+(k>1));
1229 _rotorparts.add(rp);
1230 rp->setTorque(torquemax,torque0);
1231 rp->setRelamp(relamp);
1232 rp->setTorqueOfInertia(_torque_of_inertia/_number_of_parts);
1233 rp->setDirection(2*
pi*
i/_number_of_parts);
1235 for (
i=0;
i<_number_of_parts;
i++)
1237 rps[
i]->setlastnextrp(rps[(
i-1+_number_of_parts)%_number_of_parts],
1238 rps[(
i+1)%_number_of_parts],
1239 rps[(
i+_number_of_parts/2)%_number_of_parts],
1240 rps[(
i-_number_of_parts/4+_number_of_parts)%_number_of_parts],
1241 rps[(
i+_number_of_parts/4)%_number_of_parts]);
1244 updateDirectionsAndPositions(drot);
1245 for (
i=0;
i<_number_of_parts;
i++)
1247 rps[
i]->setCompiled();
1249 float lift[4],torque[4], v_wind[3];
1250 v_wind[0]=v_wind[1]=v_wind[2]=0;
1251 rps[0]->setOmega(_omegan);
1253 if (_airfoil_lift_coefficient==0)
1259 rps[0]->calculateAlpha(v_wind,rho_null,_pitch_a,0,0,
1260 &(torque[0]),&(lift[0]));
1261 _liftcoef = pitchaforce/lift[0];
1264 rps[0]->calculateAlpha(v_wind,rho_null,0,0,0,&(torque[0]),&(lift[0]));
1269 rps[0]->calculateAlpha(v_wind,rho_null,0,0,0,&(torque[1]),&(lift[1]));
1274 rps[0]->calculateAlpha(v_wind,rho_null,_pitch_b,0,0,&(torque[2]),&(lift[2]));
1279 rps[0]->calculateAlpha(v_wind,rho_null,_pitch_b,0,0,&(torque[3]),&(lift[3]));
1284 _dragcoef1=torque0/torque[1];
1285 _dragcoef0=(torqueb-_dragcoef1*torque[3])/torque[2];
1289 _dragcoef1=(torque0/torque[0]-torqueb/torque[2])
1290 /(torque[1]/torque[0]-torque[3]/torque[2]);
1291 _dragcoef0=(torqueb-_dragcoef1*torque[3])/torque[2];
1296 _liftcoef=_airfoil_lift_coefficient/_number_of_parts*_number_of_blades;
1297 _dragcoef0=_airfoil_drag_coefficient0/_number_of_parts*_number_of_blades*_c2;
1298 _dragcoef1=_airfoil_drag_coefficient1/_number_of_parts*_number_of_blades*_c2;
1302 rps[0]->calculateAlpha(v_wind,rho_null,_pitch_a,0,0,
1303 &(torque[0]),&(lift[0]));
1304 rps[0]->calculateAlpha(v_wind,rho_null,_pitch_b,0,0,
1305 &(torque[1]),&(lift[1]));
1306 rps[0]->calculateAlpha(v_wind,rho_null,0,0,0,
1307 &(torque[3]),&(lift[3]));
1308 SG_LOG(SG_FLIGHT, SG_INFO,
1309 "Rotor: coefficients for airfoil:" << endl << setprecision(6)
1310 <<
" drag0: " << _dragcoef0*_number_of_parts/_number_of_blades/_c2
1311 <<
" drag1: " << _dragcoef1*_number_of_parts/_number_of_blades/_c2
1312 <<
" lift: " << _liftcoef*_number_of_parts/_number_of_blades
1314 <<
"at 10 deg:" << endl
1315 <<
"drag: " << (Math::sin(10./180*
pi)*_dragcoef1+_dragcoef0)
1316 *_number_of_parts/_number_of_blades/_c2
1317 <<
" lift: " << Math::sin(10./180*
pi)*_liftcoef*_number_of_parts/_number_of_blades
1319 <<
"Some results (Pitch [degree], Power [kW], Lift [N])" << endl
1320 << 0.0f <<
"deg " << Math::abs(torque[3]*_number_of_parts*_omegan/1000) <<
"kW "
1321 << lift[3]*_number_of_parts << endl
1322 << _pitch_a*180/
pi <<
"deg " << Math::abs(torque[0]*_number_of_parts*_omegan/1000)
1323 <<
"kW " << lift[0]*_number_of_parts << endl
1324 << _pitch_b*180/
pi <<
"deg " << Math::abs(torque[1]*_number_of_parts*_omegan/1000)
1325 <<
"kW " << lift[1]*_number_of_parts << endl << endl );
1330 _delta*=lift[0]/pitchaforce;
1331 relamp=(omega*omega/(2*_delta*Math::sqrt(
sqr(omega0*omega0-omega*omega)
1332 +4*_delta*_delta*omega*omega)))*_cyclic_factor;
1333 for (
i=0;
i<_number_of_parts;
i++)
1335 rps[
i]->setRelamp(relamp);
1337 rps[0]->setOmega(0);
1354#define i(x) << #x << ":" << r.x << endl
1355#define iv(x) << #x << ":" << r.x[0] << ";" << r.x[1] << ";" <<r.x[2] << ";" << endl
1356 out <<
"Writing Info on Rotor "
1359 i(_omega)
i(_omegan)
i(_omegarel)
i(_ddt_omega)
i(_omegarelneu)
1362 i( _airfoil_incidence_no_lift)
1364 i( _airfoil_lift_coefficient)
1365 i( _airfoil_drag_coefficient0)
1366 i( _airfoil_drag_coefficient1)
1368 i( _number_of_segments)
1369 i( _number_of_parts)
1371 iv( _groundeffectpos[0])
iv( _groundeffectpos[1])
iv( _groundeffectpos[2])
iv( _groundeffectpos[3])
1372 i( _ground_effect_altitude)
1374 iv( _normal_with_yaw_roll)
1377 i( _number_of_blades)
1378 i( _weight_per_blade)
1379 i( _rel_blade_center)
1382 i( _force_at_pitch_a)
1384 i( _power_at_pitch_0)
1385 i( _power_at_pitch_b)
1402 i( _alphamin)
i(_alphamax)
i(_alpha0)
i(_alpha0factor)
1403 i( _teeterdamp)
i(_maxteeterdamp)
1404 i( _rellenteeterhinge)
1406 i( _translift_maxfactor)
1407 i( _ground_effect_constant)
1408 i( _vortex_state_lift_factor)
1409 i( _vortex_state_c1)
1410 i( _vortex_state_c2)
1411 i( _vortex_state_c3)
1412 i( _vortex_state_e1)
1413 i( _vortex_state_e2)
1414 i( _vortex_state_e3)
1415 i( _lift_factor)
i(_f_ge)
i(_f_vs)
i(_f_tl)
1421 i( _rel_len_where_incidence_is_measured)
1422 i( _torque_of_inertia)
1423 i( _rel_len_blade_start)
1424 i( _incidence_stall_zero_speed)
1425 i( _incidence_stall_half_sonic_speed)
1426 i( _lift_factor_stall)
1427 i( _stall_change_over)
1428 i( _drag_factor_stall)
1435 i( _cyclic_factor) <<endl;
1437 for(j=0; j<r._rotorparts.size(); j++) {
1438 out << *((Rotorpart*)r._rotorparts.get(j));
1445void Rotor:: writeInfo()
1448 std::ostringstream buffer;
1450 FILE*f=fopen(
"c:\\fgmsvc\\bat\\log.txt",
"at");
1451 if (!f) f=fopen(
"c:\\fgmsvc\\bat\\log.txt",
"wt");
1454 fprintf(f,
"%s",(
const char *)buffer.str().c_str());
1459Rotorpart* Rotor::newRotorpart(
float zentforce,
float maxpitchforce,
1460 float delta3,
float mass,
float translift,
float rellenhinge,
float len)
1462 Rotorpart *r =
new Rotorpart();
1463 r->setDelta3(delta3);
1464 r->setDynamic(_dynamic);
1465 r->setTranslift(_translift);
1468 r->setRelLenHinge(rellenhinge);
1469 r->setSharedFlapHinge(_shared_flap_hinge);
1470 r->setOmegaN(_omegan);
1471 r->setPhi(_phi_null);
1472 r->setAlpha0(_alpha0);
1473 r->setAlphamin(_alphamin);
1474 r->setAlphamax(_alphamax);
1475 r->setAlpha0factor(_alpha0factor);
1477 r->setDiameter(_diameter);
1479#define p(a) r->setParameter(#a,_##a);
1481 p(number_of_segments)
1482 p(rel_len_where_incidence_is_measured)
1483 p(rel_len_blade_start)
1484 p(rotor_correction_factor)
1489void Rotor::interp(
float* v1,
float* v2,
float frac,
float* out)
1491 out[0] = v1[0] + frac*(v2[0]-v1[0]);
1492 out[1] = v1[1] + frac*(v2[1]-v1[1]);
1493 out[2] = v1[2] + frac*(v2[2]-v1[2]);
1496void Rotorgear::initRotorIteration(
float *lrot,
float dt)
1500 if (_rotors.empty())
return;
1501 Rotor* r0 = (Rotor*)_rotors.get(0);
1502 omegarel= r0->getOmegaRelNeu();
1503 for(
i=0;
i<_rotors.size();
i++) {
1504 Rotor* r = (Rotor*)_rotors.get(
i);
1505 r->inititeration(dt,omegarel,0,lrot);
1509void Rotorgear::calcForces(
float* torqueOut)
1512 torqueOut[0]=torqueOut[1]=torqueOut[2]=0;
1516 if (! _rotors.empty())
1518 float omegarel,omegan;
1519 Rotor* r0 = (Rotor*)_rotors.get(0);
1520 omegarel= r0->getOmegaRel();
1522 float total_torque_of_inertia=0;
1523 float total_torque=0;
1524 for(
i=0;
i<_rotors.size();
i++) {
1525 Rotor* r = (Rotor*)_rotors.get(
i);
1526 omegan=r->getOmegan();
1527 total_torque_of_inertia+=r->getTorqueOfInertia()*omegan*omegan;
1530 total_torque+=r->getTorque()*omegan;
1532 float max_torque_of_engine=0;
1536 max_torque_of_engine=_max_power_engine*_max_rel_torque;
1537 float df=_target_rel_rpm-omegarel;
1538 df/=_engine_prop_factor;
1539 df = Math::clamp(df, 0, 1);
1540 max_torque_of_engine = df * _max_power_engine*_max_rel_torque;
1546 float rel_torque_engine=1;
1547 if (total_torque<=0)
1548 rel_torque_engine=0;
1550 if (max_torque_of_engine>0)
1551 rel_torque_engine=1/max_torque_of_engine*total_torque;
1553 rel_torque_engine=0;
1558 if (! r0->_rotorparts.empty()) dt=((Rotorpart*)r0->_rotorparts.get(0))->getDt();
1560 float rotor_brake_torque;
1561 rotor_brake_torque=_rotorbrake*_max_power_rotor_brake+_rotorgear_friction;
1564 rotor_brake_torque=Math::clamp(rotor_brake_torque,0,
1565 total_torque_of_inertia/dt*omegarel);
1566 max_torque_of_engine-=rotor_brake_torque;
1569 if ((max_torque_of_engine<total_torque)
1570 ||((max_torque_of_engine>total_torque)&&(omegarel<_target_rel_rpm))
1572 ||(total_torque<0) )
1574 _ddt_omegarel=(max_torque_of_engine-total_torque)/total_torque_of_inertia;
1575 if(max_torque_of_engine>total_torque)
1579 float lim1=-total_torque/total_torque_of_inertia;
1582 if (lim1<_engine_accel_limit) lim1=_engine_accel_limit;
1585 if (_ddt_omegarel>lim1) _ddt_omegarel=lim1;
1587 if (_ddt_omegarel>5.5)_ddt_omegarel=5.5;
1589 if (_ddt_omegarel<-5.5)_ddt_omegarel=-5.5;
1591 if (_max_power_engine<0.001) {omegarel=1;_ddt_omegarel=0;}
1595 omegarel+=dt*_ddt_omegarel;
1597 if (omegarel>2.5) omegarel=2.5;
1599 if (omegarel<-.5) omegarel=-.5;
1601 r0->setOmegaRelNeu(omegarel);
1604 for(j=0; j<_rotors.size(); j++) {
1605 Rotor* r = (Rotor*)_rotors.get(j);
1606 for(
i=0;
i<r->_rotorparts.size();
i++) {
1608 Rotorpart* rp = (Rotorpart*)r->_rotorparts.get(
i);
1610 rp->getAccelTorque(_ddt_omegarel,torque);
1611 Math::add3(torque,torqueOut,torqueOut);
1615 _total_torque_on_engine=total_torque+_ddt_omegarel*total_torque_of_inertia;
1619void Rotorgear::addRotor(Rotor* rotor)
1625void Rotorgear::compile()
1628 for(
int j=0; j<_rotors.size(); j++) {
1629 Rotor* r = (Rotor*)_rotors.get(j);
1634void Rotorgear::getDownWash(
const float *pos,
const float * v_heli,
float *downwash)
1637 downwash[0]=downwash[1]=downwash[2]=0;
1638 for(
int i=0;
i<_rotors.size();
i++) {
1639 Rotor* ro = (Rotor*)_rotors.get(
i);
1640 ro->getDownWash(pos,v_heli,tmp);
1641 Math::add3(downwash,tmp,downwash);
1645void Rotorgear::setParameter(
char *parametername,
float value)
1647#define p(a,b) if (strcmp(parametername,#a)==0) _##a = (value * (b)); else
1648 p(max_power_engine,1000)
1649 p(engine_prop_factor,1)
1650 p(yasimdragfactor,1)
1651 p(yasimliftfactor,1)
1652 p(max_power_rotor_brake,1000)
1653 p(rotorgear_friction,1000)
1654 p(engine_accel_limit,0.01)
1655 SG_LOG(SG_INPUT, SG_ALERT,
1656 "internal error in parameter set up for rotorgear: '"
1657 << parametername <<
"'" << endl);
1660int Rotorgear::getValueforFGSet(
int j,
char *text,
float *f)
1664 snprintf(text, 300,
"/rotors/gear/total-torque");
1665 *f=_total_torque_on_engine;
1669Rotorgear::Rotorgear()
1674 _max_power_rotor_brake=1;
1675 _rotorgear_friction=1;
1676 _max_power_engine=1000*450;
1677 _engine_prop_factor=0.05f;
1681 _engine_accel_limit=0.05f;
1682 _total_torque_on_engine=0;
1687Rotorgear::~Rotorgear()
1689 for(
int i=0;
i<_rotors.size();
i++)
1690 delete (Rotor*)_rotors.get(
i);
static float sqr(float a)
std::ostream & operator<<(std::ostream &out, Rotor &r)
SGPropertyNode * fgGetNode(const char *path, bool create)
Get a property node.