1 #include <simgear/debug/logstream.hxx>
4 #include <Main/fg_props.hxx>
6 #include "Rotorpart.hpp"
14 using std::setprecision;
27 const float pi=3.14159;
29 static inline float sqr(float a) { return a * a; }
46 _base[0] = _base[1] = _base[2] = 0;
55 _forward[1]=_forward[2]=0;
56 _max_pitch=13./180*pi;
57 _maxcyclicail=10./180*pi;
58 _maxcyclicele=10./180*pi;
60 _mincyclicail=-10./180*pi;
61 _mincyclicele=-10./180*pi;
62 _min_pitch=-.5/180*pi;
66 _normal[0] = _normal[1] = 0;
68 _normal_with_yaw_roll[0]= _normal_with_yaw_roll[1]=0;
69 _normal_with_yaw_roll[2]=1;
71 _omega=_omegan=_omegarel=_omegarelneu=0;
81 _shared_flap_hinge=false;
82 _rellenteeterhinge=0.01;
89 _translift_maxfactor=1.3;
90 _ground_effect_constant=0.1;
91 _vortex_state_lift_factor=0.4;
104 _number_of_segments=1;
106 _rel_len_where_incidence_is_measured=0.7;
107 _torque_of_inertia=1;
111 _airfoil_incidence_no_lift=0;
112 _rel_len_blade_start=0;
113 _airfoil_lift_coefficient=0;
114 _airfoil_drag_coefficient0=0;
115 _airfoil_drag_coefficient1=0;
117 _global_ground[i] = _tilt_center[i] = 0;
118 _global_ground[2] = 1;
119 _global_ground[3] = -1e3;
120 _incidence_stall_zero_speed=18*pi/180.;
121 _incidence_stall_half_sonic_speed=14*pi/180.;
122 _lift_factor_stall=0.28;
123 _stall_change_over=2*pi/180.;
124 _drag_factor_stall=8;
129 for (int k=0;k<4;k++)
131 _groundeffectpos[k][i]=0;
132 _ground_effect_altitude=1;
134 _lift_factor=_f_ge=_f_vs=_f_tl=1;
135 _rotor_correction_factor=.65;
139 _num_ground_contact_pos=0;
140 _directions_and_postions_dirty=true;
159 for(i=0; i<_rotorparts.size(); i++) {
160 Rotorpart* r = (Rotorpart*)_rotorparts.get(i);
163 //untie the properties
166 SGPropertyNode * node = fgGetNode("/rotors", true)->getNode(_name,true);
167 node->untie("balance-ext");
168 node->untie("balance-int");
173 void Rotor::inititeration(float dt,float omegarel,float ddt_omegarel,float *rot)
178 _omega=_omegan*_omegarel;
179 _ddt_omega=_omegan*ddt_omegarel;
182 updateDirectionsAndPositions(drot);
183 Math::add3(rot,drot,drot);
184 for(i=0; i<_rotorparts.size(); i++) {
185 float s = Math::sin(float(2*pi*i/_number_of_parts+(_phi-pi/2.)*(_ccw?1:-1)));
186 float c = Math::cos(float(2*pi*i/_number_of_parts+(_phi-pi/2.)*(_ccw?1:-1)));
187 Rotorpart* r = (Rotorpart*)_rotorparts.get(i);
189 r->setDdtOmega(_ddt_omega);
190 r->inititeration(dt,drot);
191 r->setCyclic(_cyclicail*c+_cyclicele*s);
194 //calculate the normal of the rotor disc, for calcualtion of the downwash
195 float side[3],help[3];
196 Math::cross3(_normal,_forward,side);
197 Math::mul3(Math::cos(_yaw)*Math::cos(_roll),_normal,_normal_with_yaw_roll);
199 Math::mul3(Math::sin(_yaw),_forward,help);
200 Math::add3(_normal_with_yaw_roll,help,_normal_with_yaw_roll);
202 Math::mul3(Math::sin(_roll),side,help);
203 Math::add3(_normal_with_yaw_roll,help,_normal_with_yaw_roll);
206 if ((_balance1*_balance2 < 0.97) && (_balance1>-1))
208 _balance1-=(0.97-_balance1*_balance2)*(0.97-_balance1*_balance2)*0.005;
209 if (_balance1<-1) _balance1=-1;
213 float Rotor::calcStall(float incidence,float speed)
215 float stall_incidence=_incidence_stall_zero_speed
216 +(_incidence_stall_half_sonic_speed
217 -_incidence_stall_zero_speed)*speed/(343./2);
218 //missing: Temeperature dependency of sonic speed
219 incidence = Math::abs(incidence);
220 if (incidence > (90./180.*pi))
221 incidence = pi-incidence;
222 float stall = (incidence-stall_incidence)/_stall_change_over;
223 stall = Math::clamp(stall,0,1);
225 _stall_sum+=stall*speed*speed;
226 _stall_v2sum+=speed*speed;
231 float Rotor::getLiftCoef(float incidence,float speed)
233 float stall=calcStall(incidence,speed);
234 /* the next shold look like this, but this is the inner loop of
235 the rotor simulation. For small angles (and we hav only small
236 angles) the first order approximation works well
237 float c1= Math::sin(incidence-_airfoil_incidence_no_lift)*_liftcoef;
238 for c2 we would need higher order, because in stall the angle can be large
241 if (incidence > (pi/2))
243 else if (incidence <-(pi/2))
247 float c1= (i2-_airfoil_incidence_no_lift)*_liftcoef;
250 float c2= Math::sin(2*(incidence-_airfoil_incidence_no_lift))
251 *_liftcoef*_lift_factor_stall;
252 return (1-stall)*c1 + stall *c2;
258 float Rotor::getDragCoef(float incidence,float speed)
260 float stall=calcStall(incidence,speed);
261 float c1= (Math::abs(Math::sin(incidence-_airfoil_incidence_no_lift))
262 *_dragcoef1+_dragcoef0);
263 float c2= c1*_drag_factor_stall;
264 return (1-stall)*c1 + stall *c2;
267 int Rotor::getValueforFGSet(int j,char *text,float *f)
269 if (_name[0]==0) return 0;
270 if (4>numRotorparts()) return 0; //compile first!
273 sprintf(text,"/rotors/%s/cone-deg", _name);
274 *f=(_balance1>-1)?( ((Rotorpart*)getRotorpart(0))->getrealAlpha()
275 +((Rotorpart*)getRotorpart(1*(_number_of_parts>>2)))->getrealAlpha()
276 +((Rotorpart*)getRotorpart(2*(_number_of_parts>>2)))->getrealAlpha()
277 +((Rotorpart*)getRotorpart(3*(_number_of_parts>>2)))->getrealAlpha()
283 sprintf(text,"/rotors/%s/roll-deg", _name);
284 _roll = ( ((Rotorpart*)getRotorpart(0))->getrealAlpha()
285 -((Rotorpart*)getRotorpart(2*(_number_of_parts>>2)))->getrealAlpha()
287 *f=(_balance1>-1)?_roll *180/pi:0;
292 sprintf(text,"/rotors/%s/yaw-deg", _name);
293 _yaw=( ((Rotorpart*)getRotorpart(1*(_number_of_parts>>2)))->getrealAlpha()
294 -((Rotorpart*)getRotorpart(3*(_number_of_parts>>2)))->getrealAlpha()
296 *f=(_balance1>-1)?_yaw*180/pi:0;
301 sprintf(text,"/rotors/%s/rpm", _name);
302 *f=(_balance1>-1)?_omega/2/pi*60:0;
307 sprintf(text,"/rotors/%s/tilt/pitch-deg",_name);
308 *f=_tilt_pitch*180/pi;
312 sprintf(text,"/rotors/%s/tilt/roll-deg",_name);
313 *f=_tilt_roll*180/pi;
317 sprintf(text,"/rotors/%s/tilt/yaw-deg",_name);
322 sprintf(text,"/rotors/%s/balance", _name);
327 sprintf(text,"/rotors/%s/stall",_name);
328 *f=getOverallStall();
332 sprintf(text,"/rotors/%s/torque",_name);
338 if (b>=_number_of_blades)
343 sprintf(text,"/rotors/%s/blade[%i]/%s",
345 w==0?"position-deg":(w==1?"flap-deg":"incidence-deg"));
346 *f=((Rotorpart*)getRotorpart(0))->getPhi()*180/pi
347 +360*b/_number_of_blades*(_ccw?1:-1);
350 if (_balance1<=-1) *f=0;
357 rk=Math::clamp(rk,0,1);//Delete this
359 if(w==2) {k+=2;l+=2;}
361 if(w==1) {k+=1;l+=1;}
364 if (w==1) *f=rk*((Rotorpart*) getRotorpart(k*(_number_of_parts>>2)))->getrealAlpha()*180/pi
365 +rl*((Rotorpart*) getRotorpart(l*(_number_of_parts>>2)))->getrealAlpha()*180/pi;
366 else if(w==2) *f=rk*((Rotorpart*)getRotorpart(k*(_number_of_parts>>2)))->getIncidence()*180/pi
367 +rl*((Rotorpart*)getRotorpart(l*(_number_of_parts>>2)))->getIncidence()*180/pi;
372 void Rotorgear::setEngineOn(int value)
377 void Rotorgear::setRotorEngineMaxRelTorque(float lval)
379 _max_rel_torque=lval;
382 void Rotorgear::setRotorRelTarget(float lval)
384 _target_rel_rpm=lval;
387 void Rotor::setAlpha0(float f)
392 void Rotor::setAlphamin(float f)
397 void Rotor::setAlphamax(float f)
402 void Rotor::setAlpha0factor(float f)
407 int Rotor::numRotorparts()
409 return _rotorparts.size();
412 Rotorpart* Rotor::getRotorpart(int n)
414 return ((Rotorpart*)_rotorparts.get(n));
417 int Rotorgear::getEngineon()
422 float Rotor::getTorqueOfInertia()
424 return _torque_of_inertia;
427 void Rotor::setTorque(float f)
432 void Rotor::addTorque(float f)
437 void Rotor::strncpy(char *dest,const char *src,int maxlen)
440 while(src[n]&&n<(maxlen-1))
448 void Rotor::setNormal(float* normal)
451 float invsum,sqrsum=0;
452 for(i=0; i<3; i++) { sqrsum+=normal[i]*normal[i];}
454 invsum=1/Math::sqrt(sqrsum);
459 _normal_with_yaw_roll[i]=_normal[i] = normal[i]*invsum;
463 void Rotor::setForward(float* forward)
466 float invsum,sqrsum=0;
467 for(i=0; i<3; i++) { sqrsum+=forward[i]*forward[i];}
469 invsum=1/Math::sqrt(sqrsum);
472 for(i=0; i<3; i++) { _forward[i] = forward[i]*invsum; }
475 void Rotor::setForceAtPitchA(float force)
477 _force_at_pitch_a=force;
480 void Rotor::setPowerAtPitch0(float value)
482 _power_at_pitch_0=value;
485 void Rotor::setPowerAtPitchB(float value)
487 _power_at_pitch_b=value;
490 void Rotor::setPitchA(float value)
492 _pitch_a=value/180*pi;
495 void Rotor::setPitchB(float value)
497 _pitch_b=value/180*pi;
500 void Rotor::setBase(float* base)
503 for(i=0; i<3; i++) _base[i] = base[i];
506 void Rotor::setMaxCyclicail(float value)
508 _maxcyclicail=value/180*pi;
511 void Rotor::setMaxCyclicele(float value)
513 _maxcyclicele=value/180*pi;
516 void Rotor::setMinCyclicail(float value)
518 _mincyclicail=value/180*pi;
521 void Rotor::setMinCyclicele(float value)
523 _mincyclicele=value/180*pi;
526 void Rotor::setMinCollective(float value)
528 _min_pitch=value/180*pi;
531 void Rotor::setMinTiltYaw(float value)
533 _min_tilt_yaw=value/180*pi;
536 void Rotor::setMinTiltPitch(float value)
538 _min_tilt_pitch=value/180*pi;
541 void Rotor::setMinTiltRoll(float value)
543 _min_tilt_roll=value/180*pi;
546 void Rotor::setMaxTiltYaw(float value)
548 _max_tilt_yaw=value/180*pi;
551 void Rotor::setMaxTiltPitch(float value)
553 _max_tilt_pitch=value/180*pi;
556 void Rotor::setMaxTiltRoll(float value)
558 _max_tilt_roll=value/180*pi;
561 void Rotor::setTiltCenterX(float value)
563 _tilt_center[0] = value;
566 void Rotor::setTiltCenterY(float value)
568 _tilt_center[1] = value;
571 void Rotor::setTiltCenterZ(float value)
573 _tilt_center[2] = value;
576 void Rotor::setMaxCollective(float value)
578 _max_pitch=value/180*pi;
581 void Rotor::setDiameter(float value)
586 void Rotor::setWeightPerBlade(float value)
588 _weight_per_blade=value;
591 void Rotor::setNumberOfBlades(float value)
593 _number_of_blades=int(value+.5);
596 void Rotor::setRelBladeCenter(float value)
598 _rel_blade_center=value;
601 void Rotor::setDynamic(float value)
606 void Rotor::setDelta3(float value)
611 void Rotor::setDelta(float value)
616 void Rotor::setTranslift(float value)
621 void Rotor::setSharedFlapHinge(bool s)
623 _shared_flap_hinge=s;
626 void Rotor::setBalance(float b)
631 void Rotor::setC2(float value)
636 void Rotor::setStepspersecond(float steps)
638 _stepspersecond=steps;
641 void Rotor::setRPM(float value)
646 void Rotor::setPhiNull(float value)
651 void Rotor::setRelLenHinge(float value)
653 _rel_len_hinge=value;
656 void Rotor::setDownwashFactor(float value)
658 _downwash_factor=value;
661 void Rotor::setAlphaoutput(int i, const char *text)
663 strncpy(_alphaoutput[i],text,255);
666 void Rotor::setName(const char *text)
668 strncpy(_name,text,256);//256: some space needed for settings
671 void Rotor::setCcw(int ccw)
676 void Rotor::setNotorque(int value)
681 void Rotor::setRelLenTeeterHinge(float f)
683 _rellenteeterhinge=f;
686 void Rotor::setTeeterdamp(float f)
691 void Rotor::setMaxteeterdamp(float f)
696 void Rotor::setGlobalGround(double *global_ground, float* global_vel)
699 for(i=0; i<4; i++) _global_ground[i] = global_ground[i];
702 void Rotor::setParameter(const char *parametername, float value)
704 #define p(a,b) if (strcmp(parametername,#a)==0) _##a = (value * (b)); else
706 p(translift_maxfactor,1)
707 p(ground_effect_constant,1)
708 p(vortex_state_lift_factor,1)
716 p(number_of_segments,1)
718 p(rel_len_where_incidence_is_measured,1)
721 p(airfoil_incidence_no_lift,pi/180.)
722 p(rel_len_blade_start,1)
723 p(incidence_stall_zero_speed,pi/180.)
724 p(incidence_stall_half_sonic_speed,pi/180.)
725 p(lift_factor_stall,1)
726 p(stall_change_over,pi/180.)
727 p(drag_factor_stall,1)
728 p(airfoil_lift_coefficient,1)
729 p(airfoil_drag_coefficient0,1)
730 p(airfoil_drag_coefficient1,1)
732 p(rotor_correction_factor,1)
733 SG_LOG(SG_INPUT, SG_ALERT,
734 "internal error in parameter set up for rotor: '" <<
735 parametername <<"'" << endl);
739 float Rotor::getLiftFactor()
744 void Rotorgear::setRotorBrake(float lval)
746 lval = Math::clamp(lval, 0, 1);
750 void Rotor::setTiltYaw(float lval)
752 lval = Math::clamp(lval, -1, 1);
753 _tilt_yaw = _min_tilt_yaw+(lval+1)/2*(_max_tilt_yaw-_min_tilt_yaw);
754 _directions_and_postions_dirty = true;
757 void Rotor::setTiltPitch(float lval)
759 lval = Math::clamp(lval, -1, 1);
760 _tilt_pitch = _min_tilt_pitch+(lval+1)/2*(_max_tilt_pitch-_min_tilt_pitch);
761 _directions_and_postions_dirty = true;
764 void Rotor::setTiltRoll(float lval)
766 lval = Math::clamp(lval, -1, 1);
767 _tilt_roll = _min_tilt_roll+(lval+1)/2*(_max_tilt_roll-_min_tilt_roll);
768 _directions_and_postions_dirty = true;
771 void Rotor::setCollective(float lval)
773 lval = Math::clamp(lval, -1, 1);
775 _collective=_min_pitch+(lval+1)/2*(_max_pitch-_min_pitch);
776 for(i=0; i<_rotorparts.size(); i++) {
777 ((Rotorpart*)_rotorparts.get(i))->setCollective(_collective);
781 void Rotor::setCyclicele(float lval,float rval)
783 lval = Math::clamp(lval, -1, 1);
784 _cyclicele=_mincyclicele+(lval+1)/2*(_maxcyclicele-_mincyclicele);
787 void Rotor::setCyclicail(float lval,float rval)
789 lval = Math::clamp(lval, -1, 1);
791 _cyclicail=-(_mincyclicail+(lval+1)/2*(_maxcyclicail-_mincyclicail));
794 void Rotor::setRotorBalance(float lval)
796 lval = Math::clamp(lval, -1, 1);
800 void Rotor::getPosition(float* out)
803 for(i=0; i<3; i++) out[i] = _base[i];
806 void Rotor::calcLiftFactor(float* v, float rho, State *s)
808 //calculates _lift_factor, which is a foactor for the lift of the rotor
810 //- ground effect (_f_ge)
811 //- vortex state (_f_vs)
812 //- translational lift (_f_tl)
817 // Calculate ground effect
818 _f_ge=1+_diameter/_ground_effect_altitude*_ground_effect_constant;
820 // Now calculate translational lift
821 // float v_vert = Math::dot3(v,_normal);
823 Math::cross3(v,_normal,help);
824 float v_horiz = Math::mag3(help);
825 _f_tl = ((1-Math::pow(2.7183,-v_horiz/_translift_ve))
826 *(_translift_maxfactor-1)+1)/_translift_maxfactor;
828 _lift_factor = _f_ge*_f_tl*_f_vs;
830 //store the gravity direction
831 Glue::geodUp(s->pos, _grav_direction);
832 s->velGlobalToLocal(_grav_direction, _grav_direction);
835 void Rotor::findGroundEffectAltitude(Ground * ground_cb,State *s)
837 _ground_effect_altitude=findGroundEffectAltitude(ground_cb,s,
838 _groundeffectpos[0],_groundeffectpos[1],
839 _groundeffectpos[2],_groundeffectpos[3]);
840 testForRotorGroundContact(ground_cb,s);
843 void Rotor::testForRotorGroundContact(Ground * ground_cb,State *s)
846 for (i=0;i<_num_ground_contact_pos;i++)
849 s->posLocalToGlobal(_ground_contact_pos[i], pt);
851 // Ask for the ground plane in the global coordinate system
852 double global_ground[4];
854 ground_cb->getGroundPlane(pt, global_ground, global_vel);
855 // find h, the distance to the ground
856 // The ground plane transformed to the local frame.
858 s->planeGlobalToLocal(global_ground, ground);
860 h = ground[3] - Math::dot3(_ground_contact_pos[i], ground);
861 // Now h is the distance from _ground_contact_pos[i] to ground
864 _balance1 -= (-h)/_diameter/_num_ground_contact_pos;
865 _balance1 = (_balance1<-1)?-1:_balance1;
869 float 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]);//the center
888 float mina=100*_diameter;
890 for (int i=0;i<5;i++)
892 if (a[i]==-1)//in the first iteration,(iteration==0) no height is
893 //passed to this function, these missing values are
897 s->posLocalToGlobal(p[i], pt);
899 // Ask for the ground plane in the global coordinate system
900 double global_ground[4];
902 ground_cb->getGroundPlane(pt, global_ground, global_vel);
903 // find h, the distance to the ground
904 // The ground plane transformed to the local frame.
906 s->planeGlobalToLocal(global_ground, ground);
908 a[i] = ground[3] - Math::dot3(p[i], ground);
909 // Now a[i] is the distance from p[i] to ground
915 if (mina>=10*_diameter)
916 return mina; //the ground effect will be zero
918 //check if further recursion is neccessary
919 //if the height does not differ more than 20%, than
920 //we can return then mean height, if not split
921 //zhe square to four parts and calcualte the height
923 //suma * 0.2 is the mean
924 //0.15 is the maximum allowed difference from the mean
925 //to the height at the center
927 ||(Math::abs(suma*0.2-a[4])<(0.15*0.2*suma*(1<<iteration))))
930 float pc[4][3],ac[4]; //pc[i]=center of pos[i] and pos[(i+1)&3]
931 for (int i=0;i<4;i++)
933 Math::add3(p[i],p[(i+1)&3],pc[i]);
934 Math::mul3(0.5,pc[i],pc[i]);
936 s->posLocalToGlobal(pc[i], pt);
938 // Ask for the ground plane in the global coordinate system
939 double global_ground[4];
941 ground_cb->getGroundPlane(pt, global_ground, global_vel);
942 // find h, the distance to the ground
943 // The ground plane transformed to the local frame.
945 s->planeGlobalToLocal(global_ground, ground);
947 ac[i] = ground[3] - Math::dot3(p[i], ground);
948 // Now ac[i] is the distance from pc[i] to ground
951 (findGroundEffectAltitude(ground_cb,s,p[0],pc[1],p[4],pc[3],
952 iteration+1,a[0],ac[0],a[4],ac[3])
953 +findGroundEffectAltitude(ground_cb,s,p[1],pc[0],p[4],pc[1],
954 iteration+1,a[1],ac[0],a[4],ac[1])
955 +findGroundEffectAltitude(ground_cb,s,p[2],pc[1],p[4],pc[2],
956 iteration+1,a[2],ac[1],a[4],ac[2])
957 +findGroundEffectAltitude(ground_cb,s,p[3],pc[2],p[4],pc[3],
958 iteration+1,a[3],ac[2],a[4],ac[3])
962 void Rotor::getDownWash(float *pos, float *v_heli, float *downwash)
964 float pos2rotor[3],tmp[3];
965 Math::sub3(_base,pos,pos2rotor);
966 float dist=Math::dot3(pos2rotor,_normal_with_yaw_roll);
967 //calculate incidence at 0.7r;
968 float inc = _collective+_twist *0.7
969 - _twist*_rel_len_where_incidence_is_measured;
972 if (dist<0) // we are not in the downwash region
974 downwash[0]=downwash[1]=downwash[2]=0.;
978 //calculate the mean downwash speed directly beneath the rotor disk
979 float v1bar = Math::sin(inc) *_omega * 0.35 * _diameter * 0.8;
980 //0.35 * d = 0.7 *r, a good position to calcualte the mean downwashd
981 //0.8 the slip of the rotor.
983 //calculate the time the wind needed from thr rotor to here
984 if (v1bar< 1) v1bar = 1;
985 float time=dist/v1bar;
987 //calculate the pos2rotor, where the rotor was, "time" ago
988 Math::mul3(time,v_heli,tmp);
989 Math::sub3(pos2rotor,tmp,pos2rotor);
991 //and again calculate dist
992 dist=Math::dot3(pos2rotor,_normal_with_yaw_roll);
993 //missing the normal is offen not pointing to the normal of the rotor
994 //disk. Rotate the normal by yaw and tilt angle
998 if (dist<0) // we are not in the downwash region
1000 downwash[0]=downwash[1]=downwash[2]=0.;
1003 //of course this could be done in a runge kutta integrator, but it's such
1004 //a approximation that I beleave, it would'nt be more realistic
1006 //calculate the dist to the rotor-axis
1007 Math::cross3(pos2rotor,_normal_with_yaw_roll,tmp);
1008 float r= Math::mag3(tmp);
1009 //calculate incidence at r;
1010 float rel_r = r *2 /_diameter;
1011 float inc_r = _collective+_twist * r /_diameter * 2
1012 - _twist*_rel_len_where_incidence_is_measured;
1014 //calculate the downwash speed directly beneath the rotor disk
1017 v1 = Math::sin(inc_r) *_omega * r * 0.8;
1019 //calcualte the downwash speed in a distance "dist" to the rotor disc,
1020 //for large dist. The speed is assumed do follow a gausian distribution
1021 //with sigma increasing with dist^2:
1022 //sigma is assumed to be half of the rotor diameter directly beneath the
1023 //disc and is assumed to the rotor diameter at dist = (diameter * sqrt(2))
1025 float sigma=_diameter/2 + dist * dist / _diameter /4.;
1026 float v2 = v1bar*_diameter/ (Math::sqrt(2 * pi) * sigma)
1027 * Math::pow(2.7183,-.5*r*r/(sigma*sigma))*_diameter/2/sigma;
1029 //calculate the weight of the two downwash velocities.
1030 //Directly beneath the disc it is v1, far away it is v2
1031 float g = Math::pow(2.7183,-2*dist/_diameter);
1032 //at dist = rotor radius it is assumed to be 1/e * v1 + (1-1/e)* v2
1034 float v = g * v1 + (1-g) * v2;
1035 Math::mul3(-v*_downwash_factor,_normal_with_yaw_roll,downwash);
1036 //the downwash is calculated in the opposite direction of the normal
1039 void Rotor::euler2orient(float roll, float pitch, float hdg, float* out)
1041 // the Glue::euler2orient, inverts y<z due to different bases
1042 // therefore the negation of all "y" and "z" coeffizients
1043 Glue::euler2orient(roll,pitch,hdg,out);
1044 for (int i=3;i<9;i++) out[i]*=-1.0;
1048 void Rotor::updateDirectionsAndPositions(float *rot)
1050 if (!_directions_and_postions_dirty)
1052 rot[0]=rot[1]=rot[2]=0;
1055 rot[0]=_old_tilt_roll-_tilt_roll;
1056 rot[1]=_old_tilt_pitch-_tilt_pitch;
1057 rot[2]=_old_tilt_yaw-_tilt_yaw;
1058 _old_tilt_roll=_tilt_roll;
1059 _old_tilt_pitch=_tilt_pitch;
1060 _old_tilt_yaw=_tilt_yaw;
1062 euler2orient(_tilt_roll, _tilt_pitch, _tilt_yaw, orient);
1066 Math::sub3(_base,_tilt_center,base);
1067 Math::vmul33(orient, base, base);
1068 Math::add3(base,_tilt_center,base);
1069 Math::vmul33(orient, _forward, forward);
1070 Math::vmul33(orient, _normal, normal);
1072 #define _forward forward
1073 #define _normal normal
1074 float directions[5][3];
1075 //pointing forward, right, ... the 5th is ony for calculation
1076 directions[0][0]=_forward[0];
1077 directions[0][1]=_forward[1];
1078 directions[0][2]=_forward[2];
1083 Math::cross3(directions[i-1],_normal,directions[i]);
1085 Math::cross3(_normal,directions[i-1],directions[i]);
1087 Math::set3(directions[4],directions[0]);
1088 // now directions[0] is perpendicular to the _normal.and has a length
1089 // of 1. if _forward is already normalized and perpendicular to the
1090 // normal, directions[0] will be the same
1091 //_num_ground_contact_pos=(_number_of_parts<16)?_number_of_parts:16;
1092 for (i=0;i<_num_ground_contact_pos;i++)
1095 float s = Math::sin(pi*2*i/_num_ground_contact_pos);
1096 float c = Math::cos(pi*2*i/_num_ground_contact_pos);
1097 Math::mul3(c*_diameter*0.5,directions[0],_ground_contact_pos[i]);
1098 Math::mul3(s*_diameter*0.5,directions[1],help);
1099 Math::add3(help,_ground_contact_pos[i],_ground_contact_pos[i]);
1100 Math::add3(_base,_ground_contact_pos[i],_ground_contact_pos[i]);
1104 Math::mul3(_diameter*0.7,directions[i],_groundeffectpos[i]);
1105 Math::add3(_base,_groundeffectpos[i],_groundeffectpos[i]);
1107 for (i=0;i<_number_of_parts;i++)
1109 Rotorpart* rp = getRotorpart(i);
1110 float lpos[3],lforceattac[3],lspeed[3],dirzentforce[3];
1111 float s = Math::sin(2*pi*i/_number_of_parts);
1112 float c = Math::cos(2*pi*i/_number_of_parts);
1113 float sp = Math::sin(float(2*pi*i/_number_of_parts-pi/2.+_phi));
1114 float cp = Math::cos(float(2*pi*i/_number_of_parts-pi/2.+_phi));
1115 float direction[3],nextdirection[3],help[3],direction90deg[3];
1116 float rotorpartmass = _weight_per_blade*_number_of_blades/_number_of_parts*.453;
1117 float speed=_rotor_rpm/60*_diameter*_rel_blade_center*pi;
1118 float lentocenter=_diameter*_rel_blade_center*0.5;
1119 float lentoforceattac=_diameter*_rel_len_hinge*0.5;
1120 float zentforce=rotorpartmass*speed*speed/lentocenter;
1122 Math::mul3(c ,directions[0],help);
1123 Math::mul3(s ,directions[1],direction);
1124 Math::add3(help,direction,direction);
1126 Math::mul3(c ,directions[1],help);
1127 Math::mul3(s ,directions[2],direction90deg);
1128 Math::add3(help,direction90deg,direction90deg);
1130 Math::mul3(cp ,directions[1],help);
1131 Math::mul3(sp ,directions[2],nextdirection);
1132 Math::add3(help,nextdirection,nextdirection);
1134 Math::mul3(lentocenter,direction,lpos);
1135 Math::add3(lpos,_base,lpos);
1136 Math::mul3(lentoforceattac,nextdirection,lforceattac);
1137 //nextdirection: +90deg (gyro)!!!
1139 Math::add3(lforceattac,_base,lforceattac);
1140 Math::mul3(speed,direction90deg,lspeed);
1141 Math::mul3(1,nextdirection,dirzentforce);
1142 rp->setPosition(lpos);
1143 rp->setNormal(_normal);
1144 rp->setZentipetalForce(zentforce);
1145 rp->setPositionForceAttac(lforceattac);
1146 rp->setSpeed(lspeed);
1147 rp->setDirectionofZentipetalforce(dirzentforce);
1148 rp->setDirectionofRotorPart(direction);
1153 _directions_and_postions_dirty=false;
1156 void Rotor::compile()
1158 // Have we already been compiled?
1159 if(_rotorparts.size() != 0) return;
1161 //rotor is divided into _number_of_parts parts
1162 //each part is calcualted at _number_of_segments points
1165 //and make it a factor of 4
1166 _number_of_parts=(int(Math::clamp(_number_of_parts,4,256))>>2)<<2;
1168 _dynamic=_dynamic*(1/ //inverse of the time
1169 ( (60/_rotor_rpm)/4 //for rotating 90 deg
1170 +(60/_rotor_rpm)/(2*_number_of_blades) //+ meantime a rotorblade
1171 //will pass a given point
1173 //normalize the directions
1174 Math::unit3(_forward,_forward);
1175 Math::unit3(_normal,_normal);
1176 _num_ground_contact_pos=(_number_of_parts<16)?_number_of_parts:16;
1177 float rotorpartmass = _weight_per_blade*_number_of_blades/_number_of_parts*.453;
1178 //was pounds -> now kg
1180 _torque_of_inertia = 1/12. * ( _number_of_parts * rotorpartmass) * _diameter
1181 * _diameter * _rel_blade_center * _rel_blade_center /(0.5*0.5);
1182 float speed=_rotor_rpm/60*_diameter*_rel_blade_center*pi;
1183 float lentocenter=_diameter*_rel_blade_center*0.5;
1184 // float lentoforceattac=_diameter*_rel_len_hinge*0.5;
1185 float zentforce=rotorpartmass*speed*speed/lentocenter;
1186 float pitchaforce=_force_at_pitch_a/_number_of_parts*.453*9.81;
1187 // was pounds of force, now N, devided by _number_of_parts
1188 //(so its now per rotorpart)
1190 float torque0=0,torquemax=0,torqueb=0;
1191 float omega=_rotor_rpm/60*2*pi;
1193 float omega0=omega*Math::sqrt(1/(1-_rel_len_hinge));
1194 float delta_theoretical=pitchaforce/(_pitch_a*omega*lentocenter*2*rotorpartmass);
1195 _delta*=delta_theoretical;
1197 float relamp=(omega*omega/(2*_delta*Math::sqrt(sqr(omega0*omega0-omega*omega)
1198 +4*_delta*_delta*omega*omega)))*_cyclic_factor;
1199 //float relamp_theoretical=(omega*omega/(2*delta_theoretical*Math::sqrt(sqr(omega0*omega0-omega*omega)
1200 // +4*delta_theoretical*delta_theoretical*omega*omega)))*_cyclic_factor;
1201 _phi=Math::acos(_rel_len_hinge);
1202 _phi-=Math::atan(_delta3);
1205 torque0=_power_at_pitch_0/_number_of_parts*1000/omega;
1206 // f*r=p/w ; p=f*s/t; r=s/t/w ; r*w*t = s
1207 torqueb=_power_at_pitch_b/_number_of_parts*1000/omega;
1208 torquemax=_power_at_pitch_b/_number_of_parts*1000/omega/_pitch_b*_max_pitch;
1218 Rotorpart* rps[256];
1220 for (i=0;i<_number_of_parts;i++)
1222 Rotorpart* rp=rps[i]=newRotorpart(zentforce,pitchaforce,_delta3,rotorpartmass,
1223 _translift,_rel_len_hinge,lentocenter);
1224 int k = i*4/_number_of_parts;
1225 rp->setAlphaoutput(_alphaoutput[k&1?k:(_ccw?k^2:k)],0);
1226 rp->setAlphaoutput(_alphaoutput[4+(k&1?k:(_ccw?k^2:k))],1+(k>1));
1227 _rotorparts.add(rp);
1228 rp->setTorque(torquemax,torque0);
1229 rp->setRelamp(relamp);
1230 rp->setTorqueOfInertia(_torque_of_inertia/_number_of_parts);
1231 rp->setDirection(2*pi*i/_number_of_parts);
1233 for (i=0;i<_number_of_parts;i++)
1235 rps[i]->setlastnextrp(rps[(i-1+_number_of_parts)%_number_of_parts],
1236 rps[(i+1)%_number_of_parts],
1237 rps[(i+_number_of_parts/2)%_number_of_parts],
1238 rps[(i-_number_of_parts/4+_number_of_parts)%_number_of_parts],
1239 rps[(i+_number_of_parts/4)%_number_of_parts]);
1242 updateDirectionsAndPositions(drot);
1243 for (i=0;i<_number_of_parts;i++)
1245 rps[i]->setCompiled();
1247 float lift[4],torque[4], v_wind[3];
1248 v_wind[0]=v_wind[1]=v_wind[2]=0;
1249 rps[0]->setOmega(_omegan);
1251 if (_airfoil_lift_coefficient==0)
1253 //calculate the lift and drag coefficients now
1257 rps[0]->calculateAlpha(v_wind,rho_null,_pitch_a,0,0,
1258 &(torque[0]),&(lift[0])); //max_pitch a
1259 _liftcoef = pitchaforce/lift[0];
1262 rps[0]->calculateAlpha(v_wind,rho_null,0,0,0,&(torque[0]),&(lift[0]));
1267 rps[0]->calculateAlpha(v_wind,rho_null,0,0,0,&(torque[1]),&(lift[1]));
1272 rps[0]->calculateAlpha(v_wind,rho_null,_pitch_b,0,0,&(torque[2]),&(lift[2]));
1277 rps[0]->calculateAlpha(v_wind,rho_null,_pitch_b,0,0,&(torque[3]),&(lift[3]));
1282 _dragcoef1=torque0/torque[1];
1283 _dragcoef0=(torqueb-_dragcoef1*torque[3])/torque[2];
1287 _dragcoef1=(torque0/torque[0]-torqueb/torque[2])
1288 /(torque[1]/torque[0]-torque[3]/torque[2]);
1289 _dragcoef0=(torqueb-_dragcoef1*torque[3])/torque[2];
1294 _liftcoef=_airfoil_lift_coefficient/_number_of_parts*_number_of_blades;
1295 _dragcoef0=_airfoil_drag_coefficient0/_number_of_parts*_number_of_blades*_c2;
1296 _dragcoef1=_airfoil_drag_coefficient1/_number_of_parts*_number_of_blades*_c2;
1300 rps[0]->calculateAlpha(v_wind,rho_null,_pitch_a,0,0,
1301 &(torque[0]),&(lift[0])); //pitch a
1302 rps[0]->calculateAlpha(v_wind,rho_null,_pitch_b,0,0,
1303 &(torque[1]),&(lift[1])); //pitch b
1304 rps[0]->calculateAlpha(v_wind,rho_null,0,0,0,
1305 &(torque[3]),&(lift[3])); //pitch 0
1306 SG_LOG(SG_GENERAL, SG_INFO,
1307 "Rotor: coefficients for airfoil:" << endl << setprecision(6)
1308 << " drag0: " << _dragcoef0*_number_of_parts/_number_of_blades/_c2
1309 << " drag1: " << _dragcoef1*_number_of_parts/_number_of_blades/_c2
1310 << " lift: " << _liftcoef*_number_of_parts/_number_of_blades
1312 << "at 10 deg:" << endl
1313 << "drag: " << (Math::sin(10./180*pi)*_dragcoef1+_dragcoef0)
1314 *_number_of_parts/_number_of_blades/_c2
1315 << " lift: " << Math::sin(10./180*pi)*_liftcoef*_number_of_parts/_number_of_blades
1317 << "Some results (Pitch [degree], Power [kW], Lift [N])" << endl
1318 << 0.0f << "deg " << Math::abs(torque[3]*_number_of_parts*_omegan/1000) << "kW "
1319 << lift[3]*_number_of_parts << endl
1320 << _pitch_a*180/pi << "deg " << Math::abs(torque[0]*_number_of_parts*_omegan/1000)
1321 << "kW " << lift[0]*_number_of_parts << endl
1322 << _pitch_b*180/pi << "deg " << Math::abs(torque[1]*_number_of_parts*_omegan/1000)
1323 << "kW " << lift[1]*_number_of_parts << endl << endl );
1325 //first calculation of relamp is wrong
1326 //it used pitchaforce, but this was unknown and
1327 //on the default value
1328 _delta*=lift[0]/pitchaforce;
1329 relamp=(omega*omega/(2*_delta*Math::sqrt(sqr(omega0*omega0-omega*omega)
1330 +4*_delta*_delta*omega*omega)))*_cyclic_factor;
1331 for (i=0;i<_number_of_parts;i++)
1333 rps[i]->setRelamp(relamp);
1335 rps[0]->setOmega(0);
1342 //tie the properties
1343 /* After reset these values are totally wrong. I have to find out why
1344 SGPropertyNode * node = fgGetNode("/rotors", true)->getNode(_name,true);
1345 node->tie("balance_ext",SGRawValuePointer<float>(&_balance2),false);
1346 node->tie("balance_int",SGRawValuePointer<float>(&_balance1));
1350 std::ostream & operator<<(std::ostream & out, Rotor& r)
1352 #define i(x) << #x << ":" << r.x << endl
1353 #define iv(x) << #x << ":" << r.x[0] << ";" << r.x[1] << ";" <<r.x[2] << ";" << endl
1354 out << "Writing Info on Rotor "
1357 i(_omega) i(_omegan) i(_omegarel) i(_ddt_omega) i(_omegarelneu)
1360 i( _airfoil_incidence_no_lift)
1362 i( _airfoil_lift_coefficient)
1363 i( _airfoil_drag_coefficient0)
1364 i( _airfoil_drag_coefficient1)
1366 i( _number_of_segments)
1367 i( _number_of_parts)
1369 iv( _groundeffectpos[0])iv( _groundeffectpos[1])iv( _groundeffectpos[2])iv( _groundeffectpos[3])
1370 i( _ground_effect_altitude)
1372 iv( _normal_with_yaw_roll)
1375 i( _number_of_blades)
1376 i( _weight_per_blade)
1377 i( _rel_blade_center)
1380 i( _force_at_pitch_a)
1382 i( _power_at_pitch_0)
1383 i( _power_at_pitch_b)
1400 i( _alphamin) i(_alphamax) i(_alpha0) i(_alpha0factor)
1401 i( _teeterdamp) i(_maxteeterdamp)
1402 i( _rellenteeterhinge)
1404 i( _translift_maxfactor)
1405 i( _ground_effect_constant)
1406 i( _vortex_state_lift_factor)
1407 i( _vortex_state_c1)
1408 i( _vortex_state_c2)
1409 i( _vortex_state_c3)
1410 i( _vortex_state_e1)
1411 i( _vortex_state_e2)
1412 i( _vortex_state_e3)
1413 i( _lift_factor) i(_f_ge) i(_f_vs) i(_f_tl)
1418 i( _twist) //outer incidence = inner inner incidence + _twist
1419 i( _rel_len_where_incidence_is_measured)
1420 i( _torque_of_inertia)
1421 i( _rel_len_blade_start)
1422 i( _incidence_stall_zero_speed)
1423 i( _incidence_stall_half_sonic_speed)
1424 i( _lift_factor_stall)
1425 i( _stall_change_over)
1426 i( _drag_factor_stall)
1433 i( _cyclic_factor) <<endl;
1435 for(j=0; j<r._rotorparts.size(); j++) {
1436 out << *((Rotorpart*)r._rotorparts.get(j));
1443 void Rotor:: writeInfo()
1446 std::ostringstream buffer;
1448 FILE*f=fopen("c:\\fgmsvc\\bat\\log.txt","at");
1449 if (!f) f=fopen("c:\\fgmsvc\\bat\\log.txt","wt");
1452 fprintf(f,"%s",(const char *)buffer.str().c_str());
1457 Rotorpart* Rotor::newRotorpart(float zentforce,float maxpitchforce,
1458 float delta3,float mass,float translift,float rellenhinge,float len)
1460 Rotorpart *r = new Rotorpart();
1461 r->setDelta3(delta3);
1462 r->setDynamic(_dynamic);
1463 r->setTranslift(_translift);
1466 r->setRelLenHinge(rellenhinge);
1467 r->setSharedFlapHinge(_shared_flap_hinge);
1468 r->setOmegaN(_omegan);
1469 r->setPhi(_phi_null);
1470 r->setAlpha0(_alpha0);
1471 r->setAlphamin(_alphamin);
1472 r->setAlphamax(_alphamax);
1473 r->setAlpha0factor(_alpha0factor);
1475 r->setDiameter(_diameter);
1477 #define p(a) r->setParameter(#a,_##a);
1479 p(number_of_segments)
1480 p(rel_len_where_incidence_is_measured)
1481 p(rel_len_blade_start)
1482 p(rotor_correction_factor)
1487 void Rotor::interp(float* v1, float* v2, float frac, float* out)
1489 out[0] = v1[0] + frac*(v2[0]-v1[0]);
1490 out[1] = v1[1] + frac*(v2[1]-v1[1]);
1491 out[2] = v1[2] + frac*(v2[2]-v1[2]);
1494 void Rotorgear::initRotorIteration(float *lrot,float dt)
1498 if (!_rotors.size()) return;
1499 Rotor* r0 = (Rotor*)_rotors.get(0);
1500 omegarel= r0->getOmegaRelNeu();
1501 for(i=0; i<_rotors.size(); i++) {
1502 Rotor* r = (Rotor*)_rotors.get(i);
1503 r->inititeration(dt,omegarel,0,lrot);
1507 void Rotorgear::calcForces(float* torqueOut)
1510 torqueOut[0]=torqueOut[1]=torqueOut[2]=0;
1511 // check,<if the engine can handle the torque of the rotors.
1512 // If not reduce the torque to the fueselage and change rotational
1513 // speed of the rotors instead
1516 float omegarel,omegan;
1517 Rotor* r0 = (Rotor*)_rotors.get(0);
1518 omegarel= r0->getOmegaRel();
1520 float total_torque_of_inertia=0;
1521 float total_torque=0;
1522 for(i=0; i<_rotors.size(); i++) {
1523 Rotor* r = (Rotor*)_rotors.get(i);
1524 omegan=r->getOmegan();
1525 total_torque_of_inertia+=r->getTorqueOfInertia()*omegan*omegan;
1526 //FIXME: this is constant, so this can be done in compile
1528 total_torque+=r->getTorque()*omegan;
1530 float max_torque_of_engine=0;
1531 // SGPropertyNode * node=fgGetNode("/rotors/gear", true);
1534 max_torque_of_engine=_max_power_engine*_max_rel_torque;
1535 float df=_target_rel_rpm-omegarel;
1536 df/=_engine_prop_factor;
1537 df = Math::clamp(df, 0, 1);
1538 max_torque_of_engine = df * _max_power_engine*_max_rel_torque;
1542 float rel_torque_engine=1;
1543 if (total_torque<=0)
1544 rel_torque_engine=0;
1546 if (max_torque_of_engine>0)
1547 rel_torque_engine=1/max_torque_of_engine*total_torque;
1549 rel_torque_engine=0;
1551 //add the rotor brake and the gear fritcion
1553 if (r0->_rotorparts.size()) dt=((Rotorpart*)r0->_rotorparts.get(0))->getDt();
1555 float rotor_brake_torque;
1556 rotor_brake_torque=_rotorbrake*_max_power_rotor_brake+_rotorgear_friction;
1557 //clamp it to the value you need to stop the rotor
1558 //to avod accelerate the rotor to neagtive rpm:
1559 rotor_brake_torque=Math::clamp(rotor_brake_torque,0,
1560 total_torque_of_inertia/dt*omegarel);
1561 max_torque_of_engine-=rotor_brake_torque;
1563 //change the rotation of the rotors
1564 if ((max_torque_of_engine<total_torque) //decreasing rotation
1565 ||((max_torque_of_engine>total_torque)&&(omegarel<_target_rel_rpm))
1566 //increasing rotation due to engine
1567 ||(total_torque<0) ) //increasing rotation due to autorotation
1569 _ddt_omegarel=(max_torque_of_engine-total_torque)/total_torque_of_inertia;
1570 if(max_torque_of_engine>total_torque)
1572 //check if the acceleration is due to the engine. If yes,
1573 //the engine self limits the accel.
1574 float lim1=-total_torque/total_torque_of_inertia;
1575 //accel. by autorotation
1577 if (lim1<_engine_accel_limit) lim1=_engine_accel_limit;
1578 //if the accel by autorotation greater than the max. engine
1579 //accel, then this is the limit, if not: the engine is the limit
1580 if (_ddt_omegarel>lim1) _ddt_omegarel=lim1;
1582 if (_ddt_omegarel>5.5)_ddt_omegarel=5.5;
1583 //clamp it to avoid overflow. Should never be reached
1584 if (_ddt_omegarel<-5.5)_ddt_omegarel=-5.5;
1586 if (_max_power_engine<0.001) {omegarel=1;_ddt_omegarel=0;}
1587 //for debug: negative or no maxpower will result
1588 //in permanet 100% rotation
1590 omegarel+=dt*_ddt_omegarel;
1592 if (omegarel>2.5) omegarel=2.5;
1593 //clamp it to avoid overflow. Should never be reached
1594 if (omegarel<-.5) omegarel=-.5;
1596 r0->setOmegaRelNeu(omegarel);
1597 //calculate the torque, which is needed to accelerate the rotors.
1598 //Add this additional torque to the body
1599 for(j=0; j<_rotors.size(); j++) {
1600 Rotor* r = (Rotor*)_rotors.get(j);
1601 for(i=0; i<r->_rotorparts.size(); i++) {
1602 // float torque_scalar=0;
1603 Rotorpart* rp = (Rotorpart*)r->_rotorparts.get(i);
1605 rp->getAccelTorque(_ddt_omegarel,torque);
1606 Math::add3(torque,torqueOut,torqueOut);
1610 _total_torque_on_engine=total_torque+_ddt_omegarel*total_torque_of_inertia;
1614 void Rotorgear::addRotor(Rotor* rotor)
1620 void Rotorgear::compile()
1623 for(int j=0; j<_rotors.size(); j++) {
1624 Rotor* r = (Rotor*)_rotors.get(j);
1629 void Rotorgear::getDownWash(float *pos, float * v_heli, float *downwash)
1632 downwash[0]=downwash[1]=downwash[2]=0;
1633 for(int i=0; i<_rotors.size(); i++) {
1634 Rotor* ro = (Rotor*)_rotors.get(i);
1635 ro->getDownWash(pos,v_heli,tmp);
1636 Math::add3(downwash,tmp,downwash); // + downwash
1640 void Rotorgear::setParameter(char *parametername, float value)
1642 #define p(a,b) if (strcmp(parametername,#a)==0) _##a = (value * (b)); else
1643 p(max_power_engine,1000)
1644 p(engine_prop_factor,1)
1645 p(yasimdragfactor,1)
1646 p(yasimliftfactor,1)
1647 p(max_power_rotor_brake,1000)
1648 p(rotorgear_friction,1000)
1649 p(engine_accel_limit,0.01)
1650 SG_LOG(SG_INPUT, SG_ALERT,
1651 "internal error in parameter set up for rotorgear: '"
1652 << parametername <<"'" << endl);
1655 int Rotorgear::getValueforFGSet(int j,char *text,float *f)
1659 sprintf(text,"/rotors/gear/total-torque");
1660 *f=_total_torque_on_engine;
1664 Rotorgear::Rotorgear()
1669 _max_power_rotor_brake=1;
1670 _rotorgear_friction=1;
1671 _max_power_engine=1000*450;
1672 _engine_prop_factor=0.05f;
1676 _engine_accel_limit=0.05f;
1677 _total_torque_on_engine=0;
1682 Rotorgear::~Rotorgear()
1684 for(int i=0; i<_rotors.size(); i++)
1685 delete (Rotor*)_rotors.get(i);
1688 }; // namespace yasim