6 #include <simgear/debug/logstream.hxx>
9 #include <Main/fg_props.hxx>
10 #include "Surface.hpp"
11 #include "Rotorpart.hpp"
26 using std::setprecision;
31 const float pi=3.14159;
33 static inline float sqr(float a) { return a * a; }
50 _base[0] = _base[1] = _base[2] = 0;
59 _forward[1]=_forward[2]=0;
60 _max_pitch=13./180*pi;
61 _maxcyclicail=10./180*pi;
62 _maxcyclicele=10./180*pi;
64 _mincyclicail=-10./180*pi;
65 _mincyclicele=-10./180*pi;
66 _min_pitch=-.5/180*pi;
70 _normal[0] = _normal[1] = 0;
72 _normal_with_yaw_roll[0]= _normal_with_yaw_roll[1]=0;
73 _normal_with_yaw_roll[2]=1;
75 _omega=_omegan=_omegarel=_omegarelneu=0;
85 _shared_flap_hinge=false;
86 _rellenteeterhinge=0.01;
93 _translift_maxfactor=1.3;
94 _ground_effect_constant=0.1;
95 _vortex_state_lift_factor=0.4;
108 _number_of_segments=1;
110 _rel_len_where_incidence_is_measured=0.7;
111 _torque_of_inertia=1;
115 _airfoil_incidence_no_lift=0;
116 _rel_len_blade_start=0;
117 _airfoil_lift_coefficient=0;
118 _airfoil_drag_coefficient0=0;
119 _airfoil_drag_coefficient1=0;
121 _global_ground[i] = _tilt_center[i] = 0;
122 _global_ground[2] = 1;
123 _global_ground[3] = -1e3;
124 _incidence_stall_zero_speed=18*pi/180.;
125 _incidence_stall_half_sonic_speed=14*pi/180.;
126 _lift_factor_stall=0.28;
127 _stall_change_over=2*pi/180.;
128 _drag_factor_stall=8;
133 for (int k=0;k<4;k++)
135 _groundeffectpos[k][i]=0;
136 _ground_effect_altitude=1;
138 _lift_factor=_f_ge=_f_vs=_f_tl=1;
139 _rotor_correction_factor=.65;
143 _num_ground_contact_pos=0;
144 _directions_and_postions_dirty=true;
163 for(i=0; i<_rotorparts.size(); i++) {
164 Rotorpart* r = (Rotorpart*)_rotorparts.get(i);
167 //untie the properties
170 SGPropertyNode * node = fgGetNode("/rotors", true)->getNode(_name,true);
171 node->untie("balance-ext");
172 node->untie("balance-int");
177 void Rotor::inititeration(float dt,float omegarel,float ddt_omegarel,float *rot)
182 _omega=_omegan*_omegarel;
183 _ddt_omega=_omegan*ddt_omegarel;
186 updateDirectionsAndPositions(drot);
187 Math::add3(rot,drot,drot);
188 for(i=0; i<_rotorparts.size(); i++) {
189 float s = Math::sin(float(2*pi*i/_number_of_parts+(_phi-pi/2.)*(_ccw?1:-1)));
190 float c = Math::cos(float(2*pi*i/_number_of_parts+(_phi-pi/2.)*(_ccw?1:-1)));
191 Rotorpart* r = (Rotorpart*)_rotorparts.get(i);
193 r->setDdtOmega(_ddt_omega);
194 r->inititeration(dt,drot);
195 r->setCyclic(_cyclicail*c+_cyclicele*s);
198 //calculate the normal of the rotor disc, for calcualtion of the downwash
199 float side[3],help[3];
200 Math::cross3(_normal,_forward,side);
201 Math::mul3(Math::cos(_yaw)*Math::cos(_roll),_normal,_normal_with_yaw_roll);
203 Math::mul3(Math::sin(_yaw),_forward,help);
204 Math::add3(_normal_with_yaw_roll,help,_normal_with_yaw_roll);
206 Math::mul3(Math::sin(_roll),side,help);
207 Math::add3(_normal_with_yaw_roll,help,_normal_with_yaw_roll);
210 if ((_balance1*_balance2 < 0.97) && (_balance1>-1))
212 _balance1-=(0.97-_balance1*_balance2)*(0.97-_balance1*_balance2)*0.005;
213 if (_balance1<-1) _balance1=-1;
217 float Rotor::calcStall(float incidence,float speed)
219 float stall_incidence=_incidence_stall_zero_speed
220 +(_incidence_stall_half_sonic_speed
221 -_incidence_stall_zero_speed)*speed/(343./2);
222 //missing: Temeperature dependency of sonic speed
223 incidence = Math::abs(incidence);
224 if (incidence > (90./180.*pi))
225 incidence = pi-incidence;
226 float stall = (incidence-stall_incidence)/_stall_change_over;
227 stall = Math::clamp(stall,0,1);
229 _stall_sum+=stall*speed*speed;
230 _stall_v2sum+=speed*speed;
235 float Rotor::getLiftCoef(float incidence,float speed)
237 float stall=calcStall(incidence,speed);
238 /* the next shold look like this, but this is the inner loop of
239 the rotor simulation. For small angles (and we hav only small
240 angles) the first order approximation works well
241 float c1= Math::sin(incidence-_airfoil_incidence_no_lift)*_liftcoef;
242 for c2 we would need higher order, because in stall the angle can be large
245 if (incidence > (pi/2))
247 else if (incidence <-(pi/2))
251 float c1= (i2-_airfoil_incidence_no_lift)*_liftcoef;
254 float c2= Math::sin(2*(incidence-_airfoil_incidence_no_lift))
255 *_liftcoef*_lift_factor_stall;
256 return (1-stall)*c1 + stall *c2;
262 float Rotor::getDragCoef(float incidence,float speed)
264 float stall=calcStall(incidence,speed);
265 float c1= (Math::abs(Math::sin(incidence-_airfoil_incidence_no_lift))
266 *_dragcoef1+_dragcoef0);
267 float c2= c1*_drag_factor_stall;
268 return (1-stall)*c1 + stall *c2;
271 int Rotor::getValueforFGSet(int j,char *text,float *f)
273 if (_name[0]==0) return 0;
274 if (4>numRotorparts()) return 0; //compile first!
277 sprintf(text,"/rotors/%s/cone-deg", _name);
278 *f=(_balance1>-1)?( ((Rotorpart*)getRotorpart(0))->getrealAlpha()
279 +((Rotorpart*)getRotorpart(1*(_number_of_parts>>2)))->getrealAlpha()
280 +((Rotorpart*)getRotorpart(2*(_number_of_parts>>2)))->getrealAlpha()
281 +((Rotorpart*)getRotorpart(3*(_number_of_parts>>2)))->getrealAlpha()
287 sprintf(text,"/rotors/%s/roll-deg", _name);
288 _roll = ( ((Rotorpart*)getRotorpart(0))->getrealAlpha()
289 -((Rotorpart*)getRotorpart(2*(_number_of_parts>>2)))->getrealAlpha()
291 *f=(_balance1>-1)?_roll *180/pi:0;
296 sprintf(text,"/rotors/%s/yaw-deg", _name);
297 _yaw=( ((Rotorpart*)getRotorpart(1*(_number_of_parts>>2)))->getrealAlpha()
298 -((Rotorpart*)getRotorpart(3*(_number_of_parts>>2)))->getrealAlpha()
300 *f=(_balance1>-1)?_yaw*180/pi:0;
305 sprintf(text,"/rotors/%s/rpm", _name);
306 *f=(_balance1>-1)?_omega/2/pi*60:0;
311 sprintf(text,"/rotors/%s/tilt/pitch-deg",_name);
312 *f=_tilt_pitch*180/pi;
316 sprintf(text,"/rotors/%s/tilt/roll-deg",_name);
317 *f=_tilt_roll*180/pi;
321 sprintf(text,"/rotors/%s/tilt/yaw-deg",_name);
326 sprintf(text,"/rotors/%s/balance", _name);
331 sprintf(text,"/rotors/%s/stall",_name);
332 *f=getOverallStall();
336 sprintf(text,"/rotors/%s/torque",_name);
342 if (b>=_number_of_blades)
347 sprintf(text,"/rotors/%s/blade[%i]/%s",
349 w==0?"position-deg":(w==1?"flap-deg":"incidence-deg"));
350 *f=((Rotorpart*)getRotorpart(0))->getPhi()*180/pi
351 +360*b/_number_of_blades*(_ccw?1:-1);
354 if (_balance1<=-1) *f=0;
361 rk=Math::clamp(rk,0,1);//Delete this
363 if(w==2) {k+=2;l+=2;}
365 if(w==1) {k+=1;l+=1;}
368 if (w==1) *f=rk*((Rotorpart*) getRotorpart(k*(_number_of_parts>>2)))->getrealAlpha()*180/pi
369 +rl*((Rotorpart*) getRotorpart(l*(_number_of_parts>>2)))->getrealAlpha()*180/pi;
370 else if(w==2) *f=rk*((Rotorpart*)getRotorpart(k*(_number_of_parts>>2)))->getIncidence()*180/pi
371 +rl*((Rotorpart*)getRotorpart(l*(_number_of_parts>>2)))->getIncidence()*180/pi;
376 void Rotorgear::setEngineOn(int value)
381 void Rotorgear::setRotorEngineMaxRelTorque(float lval)
383 _max_rel_torque=lval;
386 void Rotorgear::setRotorRelTarget(float lval)
388 _target_rel_rpm=lval;
391 void Rotor::setAlpha0(float f)
396 void Rotor::setAlphamin(float f)
401 void Rotor::setAlphamax(float f)
406 void Rotor::setAlpha0factor(float f)
411 int Rotor::numRotorparts()
413 return _rotorparts.size();
416 Rotorpart* Rotor::getRotorpart(int n)
418 return ((Rotorpart*)_rotorparts.get(n));
421 int Rotorgear::getEngineon()
426 float Rotor::getTorqueOfInertia()
428 return _torque_of_inertia;
431 void Rotor::setTorque(float f)
436 void Rotor::addTorque(float f)
441 void Rotor::strncpy(char *dest,const char *src,int maxlen)
444 while(src[n]&&n<(maxlen-1))
452 void Rotor::setNormal(float* normal)
455 float invsum,sqrsum=0;
456 for(i=0; i<3; i++) { sqrsum+=normal[i]*normal[i];}
458 invsum=1/Math::sqrt(sqrsum);
463 _normal_with_yaw_roll[i]=_normal[i] = normal[i]*invsum;
467 void Rotor::setForward(float* forward)
470 float invsum,sqrsum=0;
471 for(i=0; i<3; i++) { sqrsum+=forward[i]*forward[i];}
473 invsum=1/Math::sqrt(sqrsum);
476 for(i=0; i<3; i++) { _forward[i] = forward[i]*invsum; }
479 void Rotor::setForceAtPitchA(float force)
481 _force_at_pitch_a=force;
484 void Rotor::setPowerAtPitch0(float value)
486 _power_at_pitch_0=value;
489 void Rotor::setPowerAtPitchB(float value)
491 _power_at_pitch_b=value;
494 void Rotor::setPitchA(float value)
496 _pitch_a=value/180*pi;
499 void Rotor::setPitchB(float value)
501 _pitch_b=value/180*pi;
504 void Rotor::setBase(float* base)
507 for(i=0; i<3; i++) _base[i] = base[i];
510 void Rotor::setMaxCyclicail(float value)
512 _maxcyclicail=value/180*pi;
515 void Rotor::setMaxCyclicele(float value)
517 _maxcyclicele=value/180*pi;
520 void Rotor::setMinCyclicail(float value)
522 _mincyclicail=value/180*pi;
525 void Rotor::setMinCyclicele(float value)
527 _mincyclicele=value/180*pi;
530 void Rotor::setMinCollective(float value)
532 _min_pitch=value/180*pi;
535 void Rotor::setMinTiltYaw(float value)
537 _min_tilt_yaw=value/180*pi;
540 void Rotor::setMinTiltPitch(float value)
542 _min_tilt_pitch=value/180*pi;
545 void Rotor::setMinTiltRoll(float value)
547 _min_tilt_roll=value/180*pi;
550 void Rotor::setMaxTiltYaw(float value)
552 _max_tilt_yaw=value/180*pi;
555 void Rotor::setMaxTiltPitch(float value)
557 _max_tilt_pitch=value/180*pi;
560 void Rotor::setMaxTiltRoll(float value)
562 _max_tilt_roll=value/180*pi;
565 void Rotor::setTiltCenterX(float value)
567 _tilt_center[0] = value;
570 void Rotor::setTiltCenterY(float value)
572 _tilt_center[1] = value;
575 void Rotor::setTiltCenterZ(float value)
577 _tilt_center[2] = value;
580 void Rotor::setMaxCollective(float value)
582 _max_pitch=value/180*pi;
585 void Rotor::setDiameter(float value)
590 void Rotor::setWeightPerBlade(float value)
592 _weight_per_blade=value;
595 void Rotor::setNumberOfBlades(float value)
597 _number_of_blades=int(value+.5);
600 void Rotor::setRelBladeCenter(float value)
602 _rel_blade_center=value;
605 void Rotor::setDynamic(float value)
610 void Rotor::setDelta3(float value)
615 void Rotor::setDelta(float value)
620 void Rotor::setTranslift(float value)
625 void Rotor::setSharedFlapHinge(bool s)
627 _shared_flap_hinge=s;
630 void Rotor::setBalance(float b)
635 void Rotor::setC2(float value)
640 void Rotor::setStepspersecond(float steps)
642 _stepspersecond=steps;
645 void Rotor::setRPM(float value)
650 void Rotor::setPhiNull(float value)
655 void Rotor::setRelLenHinge(float value)
657 _rel_len_hinge=value;
660 void Rotor::setDownwashFactor(float value)
662 _downwash_factor=value;
665 void Rotor::setAlphaoutput(int i, const char *text)
667 strncpy(_alphaoutput[i],text,255);
670 void Rotor::setName(const char *text)
672 strncpy(_name,text,256);//256: some space needed for settings
675 void Rotor::setCcw(int ccw)
680 void Rotor::setNotorque(int value)
685 void Rotor::setRelLenTeeterHinge(float f)
687 _rellenteeterhinge=f;
690 void Rotor::setTeeterdamp(float f)
695 void Rotor::setMaxteeterdamp(float f)
700 void Rotor::setGlobalGround(double *global_ground, float* global_vel)
703 for(i=0; i<4; i++) _global_ground[i] = global_ground[i];
706 void Rotor::setParameter(const char *parametername, float value)
708 #define p(a,b) if (strcmp(parametername,#a)==0) _##a = (value * (b)); else
710 p(translift_maxfactor,1)
711 p(ground_effect_constant,1)
712 p(vortex_state_lift_factor,1)
720 p(number_of_segments,1)
722 p(rel_len_where_incidence_is_measured,1)
725 p(airfoil_incidence_no_lift,pi/180.)
726 p(rel_len_blade_start,1)
727 p(incidence_stall_zero_speed,pi/180.)
728 p(incidence_stall_half_sonic_speed,pi/180.)
729 p(lift_factor_stall,1)
730 p(stall_change_over,pi/180.)
731 p(drag_factor_stall,1)
732 p(airfoil_lift_coefficient,1)
733 p(airfoil_drag_coefficient0,1)
734 p(airfoil_drag_coefficient1,1)
736 p(rotor_correction_factor,1)
737 SG_LOG(SG_INPUT, SG_ALERT,
738 "internal error in parameter set up for rotor: '" <<
739 parametername <<"'" << std::endl);
743 float Rotor::getLiftFactor()
748 void Rotorgear::setRotorBrake(float lval)
750 lval = Math::clamp(lval, 0, 1);
754 void Rotor::setTiltYaw(float lval)
756 lval = Math::clamp(lval, -1, 1);
757 _tilt_yaw = _min_tilt_yaw+(lval+1)/2*(_max_tilt_yaw-_min_tilt_yaw);
758 _directions_and_postions_dirty = true;
761 void Rotor::setTiltPitch(float lval)
763 lval = Math::clamp(lval, -1, 1);
764 _tilt_pitch = _min_tilt_pitch+(lval+1)/2*(_max_tilt_pitch-_min_tilt_pitch);
765 _directions_and_postions_dirty = true;
768 void Rotor::setTiltRoll(float lval)
770 lval = Math::clamp(lval, -1, 1);
771 _tilt_roll = _min_tilt_roll+(lval+1)/2*(_max_tilt_roll-_min_tilt_roll);
772 _directions_and_postions_dirty = true;
775 void Rotor::setCollective(float lval)
777 lval = Math::clamp(lval, -1, 1);
779 _collective=_min_pitch+(lval+1)/2*(_max_pitch-_min_pitch);
780 for(i=0; i<_rotorparts.size(); i++) {
781 ((Rotorpart*)_rotorparts.get(i))->setCollective(_collective);
785 void Rotor::setCyclicele(float lval,float rval)
787 lval = Math::clamp(lval, -1, 1);
788 _cyclicele=_mincyclicele+(lval+1)/2*(_maxcyclicele-_mincyclicele);
791 void Rotor::setCyclicail(float lval,float rval)
793 lval = Math::clamp(lval, -1, 1);
795 _cyclicail=-(_mincyclicail+(lval+1)/2*(_maxcyclicail-_mincyclicail));
798 void Rotor::setRotorBalance(float lval)
800 lval = Math::clamp(lval, -1, 1);
804 void Rotor::getPosition(float* out)
807 for(i=0; i<3; i++) out[i] = _base[i];
810 void Rotor::calcLiftFactor(float* v, float rho, State *s)
812 //calculates _lift_factor, which is a foactor for the lift of the rotor
814 //- ground effect (_f_ge)
815 //- vortex state (_f_vs)
816 //- translational lift (_f_tl)
821 // Calculate ground effect
822 _f_ge=1+_diameter/_ground_effect_altitude*_ground_effect_constant;
824 // Now calculate translational lift
825 // float v_vert = Math::dot3(v,_normal);
827 Math::cross3(v,_normal,help);
828 float v_horiz = Math::mag3(help);
829 _f_tl = ((1-Math::pow(2.7183,-v_horiz/_translift_ve))
830 *(_translift_maxfactor-1)+1)/_translift_maxfactor;
832 _lift_factor = _f_ge*_f_tl*_f_vs;
834 //store the gravity direction
835 Glue::geodUp(s->pos, _grav_direction);
836 s->velGlobalToLocal(_grav_direction, _grav_direction);
839 void Rotor::findGroundEffectAltitude(Ground * ground_cb,State *s)
841 _ground_effect_altitude=findGroundEffectAltitude(ground_cb,s,
842 _groundeffectpos[0],_groundeffectpos[1],
843 _groundeffectpos[2],_groundeffectpos[3]);
844 testForRotorGroundContact(ground_cb,s);
847 void Rotor::testForRotorGroundContact(Ground * ground_cb,State *s)
850 for (i=0;i<_num_ground_contact_pos;i++)
853 s->posLocalToGlobal(_ground_contact_pos[i], pt);
855 // Ask for the ground plane in the global coordinate system
856 double global_ground[4];
858 ground_cb->getGroundPlane(pt, global_ground, global_vel);
859 // find h, the distance to the ground
860 // The ground plane transformed to the local frame.
862 s->planeGlobalToLocal(global_ground, ground);
864 h = ground[3] - Math::dot3(_ground_contact_pos[i], ground);
865 // Now h is the distance from _ground_contact_pos[i] to ground
868 _balance1 -= (-h)/_diameter/_num_ground_contact_pos;
869 _balance1 = (_balance1<-1)?-1:_balance1;
873 float Rotor::findGroundEffectAltitude(Ground * ground_cb,State *s,
874 float *pos0,float *pos1,float *pos2,float *pos3,
875 int iteration,float a0,float a1,float a2,float a3)
889 Math::add3(p[0],p[2],p[4]);
890 Math::mul3(0.5,p[4],p[4]);//the center
892 float mina=100*_diameter;
894 for (int i=0;i<5;i++)
896 if (a[i]==-1)//in the first iteration,(iteration==0) no height is
897 //passed to this function, these missing values are
901 s->posLocalToGlobal(p[i], pt);
903 // Ask for the ground plane in the global coordinate system
904 double global_ground[4];
906 ground_cb->getGroundPlane(pt, global_ground, global_vel);
907 // find h, the distance to the ground
908 // The ground plane transformed to the local frame.
910 s->planeGlobalToLocal(global_ground, ground);
912 a[i] = ground[3] - Math::dot3(p[i], ground);
913 // Now a[i] is the distance from p[i] to ground
919 if (mina>=10*_diameter)
920 return mina; //the ground effect will be zero
922 //check if further recursion is neccessary
923 //if the height does not differ more than 20%, than
924 //we can return then mean height, if not split
925 //zhe square to four parts and calcualte the height
927 //suma * 0.2 is the mean
928 //0.15 is the maximum allowed difference from the mean
929 //to the height at the center
931 ||(Math::abs(suma*0.2-a[4])<(0.15*0.2*suma*(1<<iteration))))
934 float pc[4][3],ac[4]; //pc[i]=center of pos[i] and pos[(i+1)&3]
935 for (int i=0;i<4;i++)
937 Math::add3(p[i],p[(i+1)&3],pc[i]);
938 Math::mul3(0.5,pc[i],pc[i]);
940 s->posLocalToGlobal(pc[i], pt);
942 // Ask for the ground plane in the global coordinate system
943 double global_ground[4];
945 ground_cb->getGroundPlane(pt, global_ground, global_vel);
946 // find h, the distance to the ground
947 // The ground plane transformed to the local frame.
949 s->planeGlobalToLocal(global_ground, ground);
951 ac[i] = ground[3] - Math::dot3(p[i], ground);
952 // Now ac[i] is the distance from pc[i] to ground
955 (findGroundEffectAltitude(ground_cb,s,p[0],pc[1],p[4],pc[3],
956 iteration+1,a[0],ac[0],a[4],ac[3])
957 +findGroundEffectAltitude(ground_cb,s,p[1],pc[0],p[4],pc[1],
958 iteration+1,a[1],ac[0],a[4],ac[1])
959 +findGroundEffectAltitude(ground_cb,s,p[2],pc[1],p[4],pc[2],
960 iteration+1,a[2],ac[1],a[4],ac[2])
961 +findGroundEffectAltitude(ground_cb,s,p[3],pc[2],p[4],pc[3],
962 iteration+1,a[3],ac[2],a[4],ac[3])
966 void Rotor::getDownWash(float *pos, float *v_heli, float *downwash)
968 float pos2rotor[3],tmp[3];
969 Math::sub3(_base,pos,pos2rotor);
970 float dist=Math::dot3(pos2rotor,_normal_with_yaw_roll);
971 //calculate incidence at 0.7r;
972 float inc = _collective+_twist *0.7
973 - _twist*_rel_len_where_incidence_is_measured;
976 if (dist<0) // we are not in the downwash region
978 downwash[0]=downwash[1]=downwash[2]=0.;
982 //calculate the mean downwash speed directly beneath the rotor disk
983 float v1bar = Math::sin(inc) *_omega * 0.35 * _diameter * 0.8;
984 //0.35 * d = 0.7 *r, a good position to calcualte the mean downwashd
985 //0.8 the slip of the rotor.
987 //calculate the time the wind needed from thr rotor to here
988 if (v1bar< 1) v1bar = 1;
989 float time=dist/v1bar;
991 //calculate the pos2rotor, where the rotor was, "time" ago
992 Math::mul3(time,v_heli,tmp);
993 Math::sub3(pos2rotor,tmp,pos2rotor);
995 //and again calculate dist
996 dist=Math::dot3(pos2rotor,_normal_with_yaw_roll);
997 //missing the normal is offen not pointing to the normal of the rotor
998 //disk. Rotate the normal by yaw and tilt angle
1002 if (dist<0) // we are not in the downwash region
1004 downwash[0]=downwash[1]=downwash[2]=0.;
1007 //of course this could be done in a runge kutta integrator, but it's such
1008 //a approximation that I beleave, it would'nt be more realistic
1010 //calculate the dist to the rotor-axis
1011 Math::cross3(pos2rotor,_normal_with_yaw_roll,tmp);
1012 float r= Math::mag3(tmp);
1013 //calculate incidence at r;
1014 float rel_r = r *2 /_diameter;
1015 float inc_r = _collective+_twist * r /_diameter * 2
1016 - _twist*_rel_len_where_incidence_is_measured;
1018 //calculate the downwash speed directly beneath the rotor disk
1021 v1 = Math::sin(inc_r) *_omega * r * 0.8;
1023 //calcualte the downwash speed in a distance "dist" to the rotor disc,
1024 //for large dist. The speed is assumed do follow a gausian distribution
1025 //with sigma increasing with dist^2:
1026 //sigma is assumed to be half of the rotor diameter directly beneath the
1027 //disc and is assumed to the rotor diameter at dist = (diameter * sqrt(2))
1029 float sigma=_diameter/2 + dist * dist / _diameter /4.;
1030 float v2 = v1bar*_diameter/ (Math::sqrt(2 * pi) * sigma)
1031 * Math::pow(2.7183,-.5*r*r/(sigma*sigma))*_diameter/2/sigma;
1033 //calculate the weight of the two downwash velocities.
1034 //Directly beneath the disc it is v1, far away it is v2
1035 float g = Math::pow(2.7183,-2*dist/_diameter);
1036 //at dist = rotor radius it is assumed to be 1/e * v1 + (1-1/e)* v2
1038 float v = g * v1 + (1-g) * v2;
1039 Math::mul3(-v*_downwash_factor,_normal_with_yaw_roll,downwash);
1040 //the downwash is calculated in the opposite direction of the normal
1043 void Rotor::euler2orient(float roll, float pitch, float hdg, float* out)
1045 // the Glue::euler2orient, inverts y<z due to different bases
1046 // therefore the negation of all "y" and "z" coeffizients
1047 Glue::euler2orient(roll,pitch,hdg,out);
1048 for (int i=3;i<9;i++) out[i]*=-1.0;
1052 void Rotor::updateDirectionsAndPositions(float *rot)
1054 if (!_directions_and_postions_dirty)
1056 rot[0]=rot[1]=rot[2]=0;
1059 rot[0]=_old_tilt_roll-_tilt_roll;
1060 rot[1]=_old_tilt_pitch-_tilt_pitch;
1061 rot[2]=_old_tilt_yaw-_tilt_yaw;
1062 _old_tilt_roll=_tilt_roll;
1063 _old_tilt_pitch=_tilt_pitch;
1064 _old_tilt_yaw=_tilt_yaw;
1066 euler2orient(_tilt_roll, _tilt_pitch, _tilt_yaw, orient);
1070 Math::sub3(_base,_tilt_center,base);
1071 Math::vmul33(orient, base, base);
1072 Math::add3(base,_tilt_center,base);
1073 Math::vmul33(orient, _forward, forward);
1074 Math::vmul33(orient, _normal, normal);
1076 #define _forward forward
1077 #define _normal normal
1078 float directions[5][3];
1079 //pointing forward, right, ... the 5th is ony for calculation
1080 directions[0][0]=_forward[0];
1081 directions[0][1]=_forward[1];
1082 directions[0][2]=_forward[2];
1087 Math::cross3(directions[i-1],_normal,directions[i]);
1089 Math::cross3(_normal,directions[i-1],directions[i]);
1091 Math::set3(directions[4],directions[0]);
1092 // now directions[0] is perpendicular to the _normal.and has a length
1093 // of 1. if _forward is already normalized and perpendicular to the
1094 // normal, directions[0] will be the same
1095 //_num_ground_contact_pos=(_number_of_parts<16)?_number_of_parts:16;
1096 for (i=0;i<_num_ground_contact_pos;i++)
1099 float s = Math::sin(pi*2*i/_num_ground_contact_pos);
1100 float c = Math::cos(pi*2*i/_num_ground_contact_pos);
1101 Math::mul3(c*_diameter*0.5,directions[0],_ground_contact_pos[i]);
1102 Math::mul3(s*_diameter*0.5,directions[1],help);
1103 Math::add3(help,_ground_contact_pos[i],_ground_contact_pos[i]);
1104 Math::add3(_base,_ground_contact_pos[i],_ground_contact_pos[i]);
1108 Math::mul3(_diameter*0.7,directions[i],_groundeffectpos[i]);
1109 Math::add3(_base,_groundeffectpos[i],_groundeffectpos[i]);
1111 for (i=0;i<_number_of_parts;i++)
1113 Rotorpart* rp = getRotorpart(i);
1114 float lpos[3],lforceattac[3],lspeed[3],dirzentforce[3];
1115 float s = Math::sin(2*pi*i/_number_of_parts);
1116 float c = Math::cos(2*pi*i/_number_of_parts);
1117 float sp = Math::sin(float(2*pi*i/_number_of_parts-pi/2.+_phi));
1118 float cp = Math::cos(float(2*pi*i/_number_of_parts-pi/2.+_phi));
1119 float direction[3],nextdirection[3],help[3],direction90deg[3];
1120 float rotorpartmass = _weight_per_blade*_number_of_blades/_number_of_parts*.453;
1121 float speed=_rotor_rpm/60*_diameter*_rel_blade_center*pi;
1122 float lentocenter=_diameter*_rel_blade_center*0.5;
1123 float lentoforceattac=_diameter*_rel_len_hinge*0.5;
1124 float zentforce=rotorpartmass*speed*speed/lentocenter;
1126 Math::mul3(c ,directions[0],help);
1127 Math::mul3(s ,directions[1],direction);
1128 Math::add3(help,direction,direction);
1130 Math::mul3(c ,directions[1],help);
1131 Math::mul3(s ,directions[2],direction90deg);
1132 Math::add3(help,direction90deg,direction90deg);
1134 Math::mul3(cp ,directions[1],help);
1135 Math::mul3(sp ,directions[2],nextdirection);
1136 Math::add3(help,nextdirection,nextdirection);
1138 Math::mul3(lentocenter,direction,lpos);
1139 Math::add3(lpos,_base,lpos);
1140 Math::mul3(lentoforceattac,nextdirection,lforceattac);
1141 //nextdirection: +90deg (gyro)!!!
1143 Math::add3(lforceattac,_base,lforceattac);
1144 Math::mul3(speed,direction90deg,lspeed);
1145 Math::mul3(1,nextdirection,dirzentforce);
1146 rp->setPosition(lpos);
1147 rp->setNormal(_normal);
1148 rp->setZentipetalForce(zentforce);
1149 rp->setPositionForceAttac(lforceattac);
1150 rp->setSpeed(lspeed);
1151 rp->setDirectionofZentipetalforce(dirzentforce);
1152 rp->setDirectionofRotorPart(direction);
1157 _directions_and_postions_dirty=false;
1160 void Rotor::compile()
1162 // Have we already been compiled?
1163 if(_rotorparts.size() != 0) return;
1165 //rotor is divided into _number_of_parts parts
1166 //each part is calcualted at _number_of_segments points
1169 //and make it a factor of 4
1170 _number_of_parts=(int(Math::clamp(_number_of_parts,4,256))>>2)<<2;
1172 _dynamic=_dynamic*(1/ //inverse of the time
1173 ( (60/_rotor_rpm)/4 //for rotating 90 deg
1174 +(60/_rotor_rpm)/(2*_number_of_blades) //+ meantime a rotorblade
1175 //will pass a given point
1177 //normalize the directions
1178 Math::unit3(_forward,_forward);
1179 Math::unit3(_normal,_normal);
1180 _num_ground_contact_pos=(_number_of_parts<16)?_number_of_parts:16;
1181 float rotorpartmass = _weight_per_blade*_number_of_blades/_number_of_parts*.453;
1182 //was pounds -> now kg
1184 _torque_of_inertia = 1/12. * ( _number_of_parts * rotorpartmass) * _diameter
1185 * _diameter * _rel_blade_center * _rel_blade_center /(0.5*0.5);
1186 float speed=_rotor_rpm/60*_diameter*_rel_blade_center*pi;
1187 float lentocenter=_diameter*_rel_blade_center*0.5;
1188 // float lentoforceattac=_diameter*_rel_len_hinge*0.5;
1189 float zentforce=rotorpartmass*speed*speed/lentocenter;
1190 float pitchaforce=_force_at_pitch_a/_number_of_parts*.453*9.81;
1191 // was pounds of force, now N, devided by _number_of_parts
1192 //(so its now per rotorpart)
1194 float torque0=0,torquemax=0,torqueb=0;
1195 float omega=_rotor_rpm/60*2*pi;
1197 float omega0=omega*Math::sqrt(1/(1-_rel_len_hinge));
1198 float delta_theoretical=pitchaforce/(_pitch_a*omega*lentocenter*2*rotorpartmass);
1199 _delta*=delta_theoretical;
1201 float relamp=(omega*omega/(2*_delta*Math::sqrt(sqr(omega0*omega0-omega*omega)
1202 +4*_delta*_delta*omega*omega)))*_cyclic_factor;
1203 //float relamp_theoretical=(omega*omega/(2*delta_theoretical*Math::sqrt(sqr(omega0*omega0-omega*omega)
1204 // +4*delta_theoretical*delta_theoretical*omega*omega)))*_cyclic_factor;
1205 _phi=Math::acos(_rel_len_hinge);
1206 _phi-=Math::atan(_delta3);
1209 torque0=_power_at_pitch_0/_number_of_parts*1000/omega;
1210 // f*r=p/w ; p=f*s/t; r=s/t/w ; r*w*t = s
1211 torqueb=_power_at_pitch_b/_number_of_parts*1000/omega;
1212 torquemax=_power_at_pitch_b/_number_of_parts*1000/omega/_pitch_b*_max_pitch;
1222 Rotorpart* rps[256];
1224 for (i=0;i<_number_of_parts;i++)
1226 Rotorpart* rp=rps[i]=newRotorpart(zentforce,pitchaforce,_delta3,rotorpartmass,
1227 _translift,_rel_len_hinge,lentocenter);
1228 int k = i*4/_number_of_parts;
1229 rp->setAlphaoutput(_alphaoutput[k&1?k:(_ccw?k^2:k)],0);
1230 rp->setAlphaoutput(_alphaoutput[4+(k&1?k:(_ccw?k^2:k))],1+(k>1));
1231 _rotorparts.add(rp);
1232 rp->setTorque(torquemax,torque0);
1233 rp->setRelamp(relamp);
1234 rp->setTorqueOfInertia(_torque_of_inertia/_number_of_parts);
1235 rp->setDirection(2*pi*i/_number_of_parts);
1237 for (i=0;i<_number_of_parts;i++)
1239 rps[i]->setlastnextrp(rps[(i-1+_number_of_parts)%_number_of_parts],
1240 rps[(i+1)%_number_of_parts],
1241 rps[(i+_number_of_parts/2)%_number_of_parts],
1242 rps[(i-_number_of_parts/4+_number_of_parts)%_number_of_parts],
1243 rps[(i+_number_of_parts/4)%_number_of_parts]);
1246 updateDirectionsAndPositions(drot);
1247 for (i=0;i<_number_of_parts;i++)
1249 rps[i]->setCompiled();
1251 float lift[4],torque[4], v_wind[3];
1252 v_wind[0]=v_wind[1]=v_wind[2]=0;
1253 rps[0]->setOmega(_omegan);
1255 if (_airfoil_lift_coefficient==0)
1257 //calculate the lift and drag coefficients now
1261 rps[0]->calculateAlpha(v_wind,rho_null,_pitch_a,0,0,
1262 &(torque[0]),&(lift[0])); //max_pitch a
1263 _liftcoef = pitchaforce/lift[0];
1266 rps[0]->calculateAlpha(v_wind,rho_null,0,0,0,&(torque[0]),&(lift[0]));
1271 rps[0]->calculateAlpha(v_wind,rho_null,0,0,0,&(torque[1]),&(lift[1]));
1276 rps[0]->calculateAlpha(v_wind,rho_null,_pitch_b,0,0,&(torque[2]),&(lift[2]));
1281 rps[0]->calculateAlpha(v_wind,rho_null,_pitch_b,0,0,&(torque[3]),&(lift[3]));
1286 _dragcoef1=torque0/torque[1];
1287 _dragcoef0=(torqueb-_dragcoef1*torque[3])/torque[2];
1291 _dragcoef1=(torque0/torque[0]-torqueb/torque[2])
1292 /(torque[1]/torque[0]-torque[3]/torque[2]);
1293 _dragcoef0=(torqueb-_dragcoef1*torque[3])/torque[2];
1298 _liftcoef=_airfoil_lift_coefficient/_number_of_parts*_number_of_blades;
1299 _dragcoef0=_airfoil_drag_coefficient0/_number_of_parts*_number_of_blades*_c2;
1300 _dragcoef1=_airfoil_drag_coefficient1/_number_of_parts*_number_of_blades*_c2;
1304 rps[0]->calculateAlpha(v_wind,rho_null,_pitch_a,0,0,
1305 &(torque[0]),&(lift[0])); //pitch a
1306 rps[0]->calculateAlpha(v_wind,rho_null,_pitch_b,0,0,
1307 &(torque[1]),&(lift[1])); //pitch b
1308 rps[0]->calculateAlpha(v_wind,rho_null,0,0,0,
1309 &(torque[3]),&(lift[3])); //pitch 0
1310 SG_LOG(SG_FLIGHT, SG_INFO,
1311 "Rotor: coefficients for airfoil:" << endl << setprecision(6)
1312 << " drag0: " << _dragcoef0*_number_of_parts/_number_of_blades/_c2
1313 << " drag1: " << _dragcoef1*_number_of_parts/_number_of_blades/_c2
1314 << " lift: " << _liftcoef*_number_of_parts/_number_of_blades
1316 << "at 10 deg:" << endl
1317 << "drag: " << (Math::sin(10./180*pi)*_dragcoef1+_dragcoef0)
1318 *_number_of_parts/_number_of_blades/_c2
1319 << " lift: " << Math::sin(10./180*pi)*_liftcoef*_number_of_parts/_number_of_blades
1321 << "Some results (Pitch [degree], Power [kW], Lift [N])" << endl
1322 << 0.0f << "deg " << Math::abs(torque[3]*_number_of_parts*_omegan/1000) << "kW "
1323 << lift[3]*_number_of_parts << endl
1324 << _pitch_a*180/pi << "deg " << Math::abs(torque[0]*_number_of_parts*_omegan/1000)
1325 << "kW " << lift[0]*_number_of_parts << endl
1326 << _pitch_b*180/pi << "deg " << Math::abs(torque[1]*_number_of_parts*_omegan/1000)
1327 << "kW " << lift[1]*_number_of_parts << endl << endl );
1329 //first calculation of relamp is wrong
1330 //it used pitchaforce, but this was unknown and
1331 //on the default value
1332 _delta*=lift[0]/pitchaforce;
1333 relamp=(omega*omega/(2*_delta*Math::sqrt(sqr(omega0*omega0-omega*omega)
1334 +4*_delta*_delta*omega*omega)))*_cyclic_factor;
1335 for (i=0;i<_number_of_parts;i++)
1337 rps[i]->setRelamp(relamp);
1339 rps[0]->setOmega(0);
1346 //tie the properties
1347 /* After reset these values are totally wrong. I have to find out why
1348 SGPropertyNode * node = fgGetNode("/rotors", true)->getNode(_name,true);
1349 node->tie("balance_ext",SGRawValuePointer<float>(&_balance2),false);
1350 node->tie("balance_int",SGRawValuePointer<float>(&_balance1));
1354 std::ostream & operator<<(std::ostream & out, Rotor& r)
1356 #define i(x) << #x << ":" << r.x << endl
1357 #define iv(x) << #x << ":" << r.x[0] << ";" << r.x[1] << ";" <<r.x[2] << ";" << endl
1358 out << "Writing Info on Rotor "
1361 i(_omega) i(_omegan) i(_omegarel) i(_ddt_omega) i(_omegarelneu)
1364 i( _airfoil_incidence_no_lift)
1366 i( _airfoil_lift_coefficient)
1367 i( _airfoil_drag_coefficient0)
1368 i( _airfoil_drag_coefficient1)
1370 i( _number_of_segments)
1371 i( _number_of_parts)
1373 iv( _groundeffectpos[0])iv( _groundeffectpos[1])iv( _groundeffectpos[2])iv( _groundeffectpos[3])
1374 i( _ground_effect_altitude)
1376 iv( _normal_with_yaw_roll)
1379 i( _number_of_blades)
1380 i( _weight_per_blade)
1381 i( _rel_blade_center)
1384 i( _force_at_pitch_a)
1386 i( _power_at_pitch_0)
1387 i( _power_at_pitch_b)
1404 i( _alphamin) i(_alphamax) i(_alpha0) i(_alpha0factor)
1405 i( _teeterdamp) i(_maxteeterdamp)
1406 i( _rellenteeterhinge)
1408 i( _translift_maxfactor)
1409 i( _ground_effect_constant)
1410 i( _vortex_state_lift_factor)
1411 i( _vortex_state_c1)
1412 i( _vortex_state_c2)
1413 i( _vortex_state_c3)
1414 i( _vortex_state_e1)
1415 i( _vortex_state_e2)
1416 i( _vortex_state_e3)
1417 i( _lift_factor) i(_f_ge) i(_f_vs) i(_f_tl)
1422 i( _twist) //outer incidence = inner inner incidence + _twist
1423 i( _rel_len_where_incidence_is_measured)
1424 i( _torque_of_inertia)
1425 i( _rel_len_blade_start)
1426 i( _incidence_stall_zero_speed)
1427 i( _incidence_stall_half_sonic_speed)
1428 i( _lift_factor_stall)
1429 i( _stall_change_over)
1430 i( _drag_factor_stall)
1437 i( _cyclic_factor) <<endl;
1439 for(j=0; j<r._rotorparts.size(); j++) {
1440 out << *((Rotorpart*)r._rotorparts.get(j));
1447 void Rotor:: writeInfo()
1450 std::ostringstream buffer;
1452 FILE*f=fopen("c:\\fgmsvc\\bat\\log.txt","at");
1453 if (!f) f=fopen("c:\\fgmsvc\\bat\\log.txt","wt");
1456 fprintf(f,"%s",(const char *)buffer.str().c_str());
1461 Rotorpart* Rotor::newRotorpart(float zentforce,float maxpitchforce,
1462 float delta3,float mass,float translift,float rellenhinge,float len)
1464 Rotorpart *r = new Rotorpart();
1465 r->setDelta3(delta3);
1466 r->setDynamic(_dynamic);
1467 r->setTranslift(_translift);
1470 r->setRelLenHinge(rellenhinge);
1471 r->setSharedFlapHinge(_shared_flap_hinge);
1472 r->setOmegaN(_omegan);
1473 r->setPhi(_phi_null);
1474 r->setAlpha0(_alpha0);
1475 r->setAlphamin(_alphamin);
1476 r->setAlphamax(_alphamax);
1477 r->setAlpha0factor(_alpha0factor);
1479 r->setDiameter(_diameter);
1481 #define p(a) r->setParameter(#a,_##a);
1483 p(number_of_segments)
1484 p(rel_len_where_incidence_is_measured)
1485 p(rel_len_blade_start)
1486 p(rotor_correction_factor)
1491 void Rotor::interp(float* v1, float* v2, float frac, float* out)
1493 out[0] = v1[0] + frac*(v2[0]-v1[0]);
1494 out[1] = v1[1] + frac*(v2[1]-v1[1]);
1495 out[2] = v1[2] + frac*(v2[2]-v1[2]);
1498 void Rotorgear::initRotorIteration(float *lrot,float dt)
1502 if (!_rotors.size()) return;
1503 Rotor* r0 = (Rotor*)_rotors.get(0);
1504 omegarel= r0->getOmegaRelNeu();
1505 for(i=0; i<_rotors.size(); i++) {
1506 Rotor* r = (Rotor*)_rotors.get(i);
1507 r->inititeration(dt,omegarel,0,lrot);
1511 void Rotorgear::calcForces(float* torqueOut)
1514 torqueOut[0]=torqueOut[1]=torqueOut[2]=0;
1515 // check,<if the engine can handle the torque of the rotors.
1516 // If not reduce the torque to the fueselage and change rotational
1517 // speed of the rotors instead
1520 float omegarel,omegan;
1521 Rotor* r0 = (Rotor*)_rotors.get(0);
1522 omegarel= r0->getOmegaRel();
1524 float total_torque_of_inertia=0;
1525 float total_torque=0;
1526 for(i=0; i<_rotors.size(); i++) {
1527 Rotor* r = (Rotor*)_rotors.get(i);
1528 omegan=r->getOmegan();
1529 total_torque_of_inertia+=r->getTorqueOfInertia()*omegan*omegan;
1530 //FIXME: this is constant, so this can be done in compile
1532 total_torque+=r->getTorque()*omegan;
1534 float max_torque_of_engine=0;
1535 // SGPropertyNode * node=fgGetNode("/rotors/gear", true);
1538 max_torque_of_engine=_max_power_engine*_max_rel_torque;
1539 float df=_target_rel_rpm-omegarel;
1540 df/=_engine_prop_factor;
1541 df = Math::clamp(df, 0, 1);
1542 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;
1555 //add the rotor brake and the gear fritcion
1557 if (r0->_rotorparts.size()) dt=((Rotorpart*)r0->_rotorparts.get(0))->getDt();
1559 float rotor_brake_torque;
1560 rotor_brake_torque=_rotorbrake*_max_power_rotor_brake+_rotorgear_friction;
1561 //clamp it to the value you need to stop the rotor
1562 //to avod accelerate the rotor to neagtive rpm:
1563 rotor_brake_torque=Math::clamp(rotor_brake_torque,0,
1564 total_torque_of_inertia/dt*omegarel);
1565 max_torque_of_engine-=rotor_brake_torque;
1567 //change the rotation of the rotors
1568 if ((max_torque_of_engine<total_torque) //decreasing rotation
1569 ||((max_torque_of_engine>total_torque)&&(omegarel<_target_rel_rpm))
1570 //increasing rotation due to engine
1571 ||(total_torque<0) ) //increasing rotation due to autorotation
1573 _ddt_omegarel=(max_torque_of_engine-total_torque)/total_torque_of_inertia;
1574 if(max_torque_of_engine>total_torque)
1576 //check if the acceleration is due to the engine. If yes,
1577 //the engine self limits the accel.
1578 float lim1=-total_torque/total_torque_of_inertia;
1579 //accel. by autorotation
1581 if (lim1<_engine_accel_limit) lim1=_engine_accel_limit;
1582 //if the accel by autorotation greater than the max. engine
1583 //accel, then this is the limit, if not: the engine is the limit
1584 if (_ddt_omegarel>lim1) _ddt_omegarel=lim1;
1586 if (_ddt_omegarel>5.5)_ddt_omegarel=5.5;
1587 //clamp it to avoid overflow. Should never be reached
1588 if (_ddt_omegarel<-5.5)_ddt_omegarel=-5.5;
1590 if (_max_power_engine<0.001) {omegarel=1;_ddt_omegarel=0;}
1591 //for debug: negative or no maxpower will result
1592 //in permanet 100% rotation
1594 omegarel+=dt*_ddt_omegarel;
1596 if (omegarel>2.5) omegarel=2.5;
1597 //clamp it to avoid overflow. Should never be reached
1598 if (omegarel<-.5) omegarel=-.5;
1600 r0->setOmegaRelNeu(omegarel);
1601 //calculate the torque, which is needed to accelerate the rotors.
1602 //Add this additional torque to the body
1603 for(j=0; j<_rotors.size(); j++) {
1604 Rotor* r = (Rotor*)_rotors.get(j);
1605 for(i=0; i<r->_rotorparts.size(); i++) {
1606 // float torque_scalar=0;
1607 Rotorpart* rp = (Rotorpart*)r->_rotorparts.get(i);
1609 rp->getAccelTorque(_ddt_omegarel,torque);
1610 Math::add3(torque,torqueOut,torqueOut);
1614 _total_torque_on_engine=total_torque+_ddt_omegarel*total_torque_of_inertia;
1618 void Rotorgear::addRotor(Rotor* rotor)
1624 void Rotorgear::compile()
1627 for(int j=0; j<_rotors.size(); j++) {
1628 Rotor* r = (Rotor*)_rotors.get(j);
1633 void Rotorgear::getDownWash(float *pos, float * v_heli, float *downwash)
1636 downwash[0]=downwash[1]=downwash[2]=0;
1637 for(int i=0; i<_rotors.size(); i++) {
1638 Rotor* ro = (Rotor*)_rotors.get(i);
1639 ro->getDownWash(pos,v_heli,tmp);
1640 Math::add3(downwash,tmp,downwash); // + downwash
1644 void Rotorgear::setParameter(char *parametername, float value)
1646 #define p(a,b) if (strcmp(parametername,#a)==0) _##a = (value * (b)); else
1647 p(max_power_engine,1000)
1648 p(engine_prop_factor,1)
1649 p(yasimdragfactor,1)
1650 p(yasimliftfactor,1)
1651 p(max_power_rotor_brake,1000)
1652 p(rotorgear_friction,1000)
1653 p(engine_accel_limit,0.01)
1654 SG_LOG(SG_INPUT, SG_ALERT,
1655 "internal error in parameter set up for rotorgear: '"
1656 << parametername <<"'" << endl);
1659 int Rotorgear::getValueforFGSet(int j,char *text,float *f)
1663 sprintf(text,"/rotors/gear/total-torque");
1664 *f=_total_torque_on_engine;
1668 Rotorgear::Rotorgear()
1673 _max_power_rotor_brake=1;
1674 _rotorgear_friction=1;
1675 _max_power_engine=1000*450;
1676 _engine_prop_factor=0.05f;
1680 _engine_accel_limit=0.05f;
1681 _total_torque_on_engine=0;
1686 Rotorgear::~Rotorgear()
1688 for(int i=0; i<_rotors.size(); i++)
1689 delete (Rotor*)_rotors.get(i);
1692 }; // namespace yasim