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);
801 void Rotor::getPosition(float* out)
804 for(i=0; i<3; i++) out[i] = _base[i];
807 void Rotor::calcLiftFactor(float* v, float rho, State *s)
809 //calculates _lift_factor, which is a foactor for the lift of the rotor
811 //- ground effect (_f_ge)
812 //- vortex state (_f_vs)
813 //- translational lift (_f_tl)
818 // Calculate ground effect
819 _f_ge=1+_diameter/_ground_effect_altitude*_ground_effect_constant;
821 // Now calculate translational lift
822 float v_vert = Math::dot3(v,_normal);
824 Math::cross3(v,_normal,help);
825 float v_horiz = Math::mag3(help);
826 _f_tl = ((1-Math::pow(2.7183,-v_horiz/_translift_ve))
827 *(_translift_maxfactor-1)+1)/_translift_maxfactor;
829 _lift_factor = _f_ge*_f_tl*_f_vs;
831 //store the gravity direction
832 Glue::geodUp(s->pos, _grav_direction);
833 s->velGlobalToLocal(_grav_direction, _grav_direction);
836 void Rotor::findGroundEffectAltitude(Ground * ground_cb,State *s)
838 _ground_effect_altitude=findGroundEffectAltitude(ground_cb,s,
839 _groundeffectpos[0],_groundeffectpos[1],
840 _groundeffectpos[2],_groundeffectpos[3]);
841 testForRotorGroundContact(ground_cb,s);
844 void Rotor::testForRotorGroundContact(Ground * ground_cb,State *s)
847 for (i=0;i<_num_ground_contact_pos;i++)
850 s->posLocalToGlobal(_ground_contact_pos[i], pt);
852 // Ask for the ground plane in the global coordinate system
853 double global_ground[4];
855 ground_cb->getGroundPlane(pt, global_ground, global_vel);
856 // find h, the distance to the ground
857 // The ground plane transformed to the local frame.
859 s->planeGlobalToLocal(global_ground, ground);
861 h = ground[3] - Math::dot3(_ground_contact_pos[i], ground);
862 // Now h is the distance from _ground_contact_pos[i] to ground
865 _balance1 -= (-h)/_diameter/_num_ground_contact_pos;
866 _balance1 = (_balance1<-1)?-1:_balance1;
870 float Rotor::findGroundEffectAltitude(Ground * ground_cb,State *s,
871 float *pos0,float *pos1,float *pos2,float *pos3,
872 int iteration,float a0,float a1,float a2,float a3)
886 Math::add3(p[0],p[2],p[4]);
887 Math::mul3(0.5,p[4],p[4]);//the center
889 float mina=100*_diameter;
891 for (int i=0;i<5;i++)
893 if (a[i]==-1)//in the first iteration,(iteration==0) no height is
894 //passed to this function, these missing values are
898 s->posLocalToGlobal(p[i], pt);
900 // Ask for the ground plane in the global coordinate system
901 double global_ground[4];
903 ground_cb->getGroundPlane(pt, global_ground, global_vel);
904 // find h, the distance to the ground
905 // The ground plane transformed to the local frame.
907 s->planeGlobalToLocal(global_ground, ground);
909 a[i] = ground[3] - Math::dot3(p[i], ground);
910 // Now a[i] is the distance from p[i] to ground
916 if (mina>=10*_diameter)
917 return mina; //the ground effect will be zero
919 //check if further recursion is neccessary
920 //if the height does not differ more than 20%, than
921 //we can return then mean height, if not split
922 //zhe square to four parts and calcualte the height
924 //suma * 0.2 is the mean
925 //0.15 is the maximum allowed difference from the mean
926 //to the height at the center
928 ||(Math::abs(suma*0.2-a[4])<(0.15*0.2*suma*(1<<iteration))))
931 float pc[4][3],ac[4]; //pc[i]=center of pos[i] and pos[(i+1)&3]
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);
939 // Ask for the ground plane in the global coordinate system
940 double global_ground[4];
942 ground_cb->getGroundPlane(pt, global_ground, global_vel);
943 // find h, the distance to the ground
944 // The ground plane transformed to the local frame.
946 s->planeGlobalToLocal(global_ground, ground);
948 ac[i] = ground[3] - Math::dot3(p[i], ground);
949 // Now ac[i] is the distance from pc[i] to ground
952 (findGroundEffectAltitude(ground_cb,s,p[0],pc[1],p[4],pc[3],
953 iteration+1,a[0],ac[0],a[4],ac[3])
954 +findGroundEffectAltitude(ground_cb,s,p[1],pc[0],p[4],pc[1],
955 iteration+1,a[1],ac[0],a[4],ac[1])
956 +findGroundEffectAltitude(ground_cb,s,p[2],pc[1],p[4],pc[2],
957 iteration+1,a[2],ac[1],a[4],ac[2])
958 +findGroundEffectAltitude(ground_cb,s,p[3],pc[2],p[4],pc[3],
959 iteration+1,a[3],ac[2],a[4],ac[3])
963 void Rotor::getDownWash(float *pos, float *v_heli, float *downwash)
965 float pos2rotor[3],tmp[3];
966 Math::sub3(_base,pos,pos2rotor);
967 float dist=Math::dot3(pos2rotor,_normal_with_yaw_roll);
968 //calculate incidence at 0.7r;
969 float inc = _collective+_twist *0.7
970 - _twist*_rel_len_where_incidence_is_measured;
973 if (dist<0) // we are not in the downwash region
975 downwash[0]=downwash[1]=downwash[2]=0.;
979 //calculate the mean downwash speed directly beneath the rotor disk
980 float v1bar = Math::sin(inc) *_omega * 0.35 * _diameter * 0.8;
981 //0.35 * d = 0.7 *r, a good position to calcualte the mean downwashd
982 //0.8 the slip of the rotor.
984 //calculate the time the wind needed from thr rotor to here
985 if (v1bar< 1) v1bar = 1;
986 float time=dist/v1bar;
988 //calculate the pos2rotor, where the rotor was, "time" ago
989 Math::mul3(time,v_heli,tmp);
990 Math::sub3(pos2rotor,tmp,pos2rotor);
992 //and again calculate dist
993 dist=Math::dot3(pos2rotor,_normal_with_yaw_roll);
994 //missing the normal is offen not pointing to the normal of the rotor
995 //disk. Rotate the normal by yaw and tilt angle
999 if (dist<0) // we are not in the downwash region
1001 downwash[0]=downwash[1]=downwash[2]=0.;
1004 //of course this could be done in a runge kutta integrator, but it's such
1005 //a approximation that I beleave, it would'nt be more realistic
1007 //calculate the dist to the rotor-axis
1008 Math::cross3(pos2rotor,_normal_with_yaw_roll,tmp);
1009 float r= Math::mag3(tmp);
1010 //calculate incidence at r;
1011 float rel_r = r *2 /_diameter;
1012 float inc_r = _collective+_twist * r /_diameter * 2
1013 - _twist*_rel_len_where_incidence_is_measured;
1015 //calculate the downwash speed directly beneath the rotor disk
1018 v1 = Math::sin(inc_r) *_omega * r * 0.8;
1020 //calcualte the downwash speed in a distance "dist" to the rotor disc,
1021 //for large dist. The speed is assumed do follow a gausian distribution
1022 //with sigma increasing with dist^2:
1023 //sigma is assumed to be half of the rotor diameter directly beneath the
1024 //disc and is assumed to the rotor diameter at dist = (diameter * sqrt(2))
1026 float sigma=_diameter/2 + dist * dist / _diameter /4.;
1027 float v2 = v1bar*_diameter/ (Math::sqrt(2 * pi) * sigma)
1028 * Math::pow(2.7183,-.5*r*r/(sigma*sigma))*_diameter/2/sigma;
1030 //calculate the weight of the two downwash velocities.
1031 //Directly beneath the disc it is v1, far away it is v2
1032 float g = Math::pow(2.7183,-2*dist/_diameter);
1033 //at dist = rotor radius it is assumed to be 1/e * v1 + (1-1/e)* v2
1035 float v = g * v1 + (1-g) * v2;
1036 Math::mul3(-v*_downwash_factor,_normal_with_yaw_roll,downwash);
1037 //the downwash is calculated in the opposite direction of the normal
1040 void Rotor::euler2orient(float roll, float pitch, float hdg, float* out)
1042 // the Glue::euler2orient, inverts y<z due to different bases
1043 // therefore the negation of all "y" and "z" coeffizients
1044 Glue::euler2orient(roll,pitch,hdg,out);
1045 for (int i=3;i<9;i++) out[i]*=-1.0;
1049 void Rotor::updateDirectionsAndPositions(float *rot)
1051 if (!_directions_and_postions_dirty)
1053 rot[0]=rot[1]=rot[2]=0;
1056 rot[0]=_old_tilt_roll-_tilt_roll;
1057 rot[1]=_old_tilt_pitch-_tilt_pitch;
1058 rot[2]=_old_tilt_yaw-_tilt_yaw;
1059 _old_tilt_roll=_tilt_roll;
1060 _old_tilt_pitch=_tilt_pitch;
1061 _old_tilt_yaw=_tilt_yaw;
1063 euler2orient(_tilt_roll, _tilt_pitch, _tilt_yaw, orient);
1067 Math::sub3(_base,_tilt_center,base);
1068 Math::vmul33(orient, base, base);
1069 Math::add3(base,_tilt_center,base);
1070 Math::vmul33(orient, _forward, forward);
1071 Math::vmul33(orient, _normal, normal);
1073 #define _forward forward
1074 #define _normal normal
1075 float directions[5][3];
1076 //pointing forward, right, ... the 5th is ony for calculation
1077 directions[0][0]=_forward[0];
1078 directions[0][1]=_forward[1];
1079 directions[0][2]=_forward[2];
1084 Math::cross3(directions[i-1],_normal,directions[i]);
1086 Math::cross3(_normal,directions[i-1],directions[i]);
1088 Math::set3(directions[4],directions[0]);
1089 // now directions[0] is perpendicular to the _normal.and has a length
1090 // of 1. if _forward is already normalized and perpendicular to the
1091 // normal, directions[0] will be the same
1092 //_num_ground_contact_pos=(_number_of_parts<16)?_number_of_parts:16;
1093 for (i=0;i<_num_ground_contact_pos;i++)
1096 float s = Math::sin(pi*2*i/_num_ground_contact_pos);
1097 float c = Math::cos(pi*2*i/_num_ground_contact_pos);
1098 Math::mul3(c*_diameter*0.5,directions[0],_ground_contact_pos[i]);
1099 Math::mul3(s*_diameter*0.5,directions[1],help);
1100 Math::add3(help,_ground_contact_pos[i],_ground_contact_pos[i]);
1101 Math::add3(_base,_ground_contact_pos[i],_ground_contact_pos[i]);
1105 Math::mul3(_diameter*0.7,directions[i],_groundeffectpos[i]);
1106 Math::add3(_base,_groundeffectpos[i],_groundeffectpos[i]);
1108 for (i=0;i<_number_of_parts;i++)
1110 Rotorpart* rp = getRotorpart(i);
1111 float lpos[3],lforceattac[3],lspeed[3],dirzentforce[3];
1112 float s = Math::sin(2*pi*i/_number_of_parts);
1113 float c = Math::cos(2*pi*i/_number_of_parts);
1114 float sp = Math::sin(float(2*pi*i/_number_of_parts-pi/2.+_phi));
1115 float cp = Math::cos(float(2*pi*i/_number_of_parts-pi/2.+_phi));
1116 float direction[3],nextdirection[3],help[3],direction90deg[3];
1117 float rotorpartmass = _weight_per_blade*_number_of_blades/_number_of_parts*.453;
1118 float speed=_rotor_rpm/60*_diameter*_rel_blade_center*pi;
1119 float lentocenter=_diameter*_rel_blade_center*0.5;
1120 float lentoforceattac=_diameter*_rel_len_hinge*0.5;
1121 float zentforce=rotorpartmass*speed*speed/lentocenter;
1123 Math::mul3(c ,directions[0],help);
1124 Math::mul3(s ,directions[1],direction);
1125 Math::add3(help,direction,direction);
1127 Math::mul3(c ,directions[1],help);
1128 Math::mul3(s ,directions[2],direction90deg);
1129 Math::add3(help,direction90deg,direction90deg);
1131 Math::mul3(cp ,directions[1],help);
1132 Math::mul3(sp ,directions[2],nextdirection);
1133 Math::add3(help,nextdirection,nextdirection);
1135 Math::mul3(lentocenter,direction,lpos);
1136 Math::add3(lpos,_base,lpos);
1137 Math::mul3(lentoforceattac,nextdirection,lforceattac);
1138 //nextdirection: +90deg (gyro)!!!
1140 Math::add3(lforceattac,_base,lforceattac);
1141 Math::mul3(speed,direction90deg,lspeed);
1142 Math::mul3(1,nextdirection,dirzentforce);
1143 rp->setPosition(lpos);
1144 rp->setNormal(_normal);
1145 rp->setZentipetalForce(zentforce);
1146 rp->setPositionForceAttac(lforceattac);
1147 rp->setSpeed(lspeed);
1148 rp->setDirectionofZentipetalforce(dirzentforce);
1149 rp->setDirectionofRotorPart(direction);
1154 _directions_and_postions_dirty=false;
1157 void Rotor::compile()
1159 // Have we already been compiled?
1160 if(_rotorparts.size() != 0) return;
1162 //rotor is divided into _number_of_parts parts
1163 //each part is calcualted at _number_of_segments points
1166 //and make it a factor of 4
1167 _number_of_parts=(int(Math::clamp(_number_of_parts,4,256))>>2)<<2;
1169 _dynamic=_dynamic*(1/ //inverse of the time
1170 ( (60/_rotor_rpm)/4 //for rotating 90 deg
1171 +(60/_rotor_rpm)/(2*_number_of_blades) //+ meantime a rotorblade
1172 //will pass a given point
1174 //normalize the directions
1175 Math::unit3(_forward,_forward);
1176 Math::unit3(_normal,_normal);
1177 _num_ground_contact_pos=(_number_of_parts<16)?_number_of_parts:16;
1178 float rotorpartmass = _weight_per_blade*_number_of_blades/_number_of_parts*.453;
1179 //was pounds -> now kg
1181 _torque_of_inertia = 1/12. * ( _number_of_parts * rotorpartmass) * _diameter
1182 * _diameter * _rel_blade_center * _rel_blade_center /(0.5*0.5);
1183 float speed=_rotor_rpm/60*_diameter*_rel_blade_center*pi;
1184 float lentocenter=_diameter*_rel_blade_center*0.5;
1185 float lentoforceattac=_diameter*_rel_len_hinge*0.5;
1186 float zentforce=rotorpartmass*speed*speed/lentocenter;
1187 float pitchaforce=_force_at_pitch_a/_number_of_parts*.453*9.81;
1188 // was pounds of force, now N, devided by _number_of_parts
1189 //(so its now per rotorpart)
1191 float torque0=0,torquemax=0,torqueb=0;
1192 float omega=_rotor_rpm/60*2*pi;
1194 float omega0=omega*Math::sqrt(1/(1-_rel_len_hinge));
1195 float delta_theoretical=pitchaforce/(_pitch_a*omega*lentocenter*2*rotorpartmass);
1196 _delta*=delta_theoretical;
1198 float relamp=(omega*omega/(2*_delta*Math::sqrt(sqr(omega0*omega0-omega*omega)
1199 +4*_delta*_delta*omega*omega)))*_cyclic_factor;
1200 float relamp_theoretical=(omega*omega/(2*delta_theoretical*Math::sqrt(sqr(omega0*omega0-omega*omega)
1201 +4*delta_theoretical*delta_theoretical*omega*omega)))*_cyclic_factor;
1202 _phi=Math::acos(_rel_len_hinge);
1203 _phi-=Math::atan(_delta3);
1206 torque0=_power_at_pitch_0/_number_of_parts*1000/omega;
1207 // f*r=p/w ; p=f*s/t; r=s/t/w ; r*w*t = s
1208 torqueb=_power_at_pitch_b/_number_of_parts*1000/omega;
1209 torquemax=_power_at_pitch_b/_number_of_parts*1000/omega/_pitch_b*_max_pitch;
1219 Rotorpart* rps[256];
1221 for (i=0;i<_number_of_parts;i++)
1223 Rotorpart* rp=rps[i]=newRotorpart(zentforce,pitchaforce,_delta3,rotorpartmass,
1224 _translift,_rel_len_hinge,lentocenter);
1225 int k = i*4/_number_of_parts;
1226 rp->setAlphaoutput(_alphaoutput[k&1?k:(_ccw?k^2:k)],0);
1227 rp->setAlphaoutput(_alphaoutput[4+(k&1?k:(_ccw?k^2:k))],1+(k>1));
1228 _rotorparts.add(rp);
1229 rp->setTorque(torquemax,torque0);
1230 rp->setRelamp(relamp);
1231 rp->setTorqueOfInertia(_torque_of_inertia/_number_of_parts);
1232 rp->setDirection(2*pi*i/_number_of_parts);
1234 for (i=0;i<_number_of_parts;i++)
1236 rps[i]->setlastnextrp(rps[(i-1+_number_of_parts)%_number_of_parts],
1237 rps[(i+1)%_number_of_parts],
1238 rps[(i+_number_of_parts/2)%_number_of_parts],
1239 rps[(i-_number_of_parts/4+_number_of_parts)%_number_of_parts],
1240 rps[(i+_number_of_parts/4)%_number_of_parts]);
1243 updateDirectionsAndPositions(drot);
1244 for (i=0;i<_number_of_parts;i++)
1246 rps[i]->setCompiled();
1248 float lift[4],torque[4], v_wind[3];
1249 v_wind[0]=v_wind[1]=v_wind[2]=0;
1250 rps[0]->setOmega(_omegan);
1252 if (_airfoil_lift_coefficient==0)
1254 //calculate the lift and drag coefficients now
1258 rps[0]->calculateAlpha(v_wind,rho_null,_pitch_a,0,0,
1259 &(torque[0]),&(lift[0])); //max_pitch a
1260 _liftcoef = pitchaforce/lift[0];
1263 rps[0]->calculateAlpha(v_wind,rho_null,0,0,0,&(torque[0]),&(lift[0]));
1268 rps[0]->calculateAlpha(v_wind,rho_null,0,0,0,&(torque[1]),&(lift[1]));
1273 rps[0]->calculateAlpha(v_wind,rho_null,_pitch_b,0,0,&(torque[2]),&(lift[2]));
1278 rps[0]->calculateAlpha(v_wind,rho_null,_pitch_b,0,0,&(torque[3]),&(lift[3]));
1283 _dragcoef1=torque0/torque[1];
1284 _dragcoef0=(torqueb-_dragcoef1*torque[3])/torque[2];
1288 _dragcoef1=(torque0/torque[0]-torqueb/torque[2])
1289 /(torque[1]/torque[0]-torque[3]/torque[2]);
1290 _dragcoef0=(torqueb-_dragcoef1*torque[3])/torque[2];
1295 _liftcoef=_airfoil_lift_coefficient/_number_of_parts*_number_of_blades;
1296 _dragcoef0=_airfoil_drag_coefficient0/_number_of_parts*_number_of_blades*_c2;
1297 _dragcoef1=_airfoil_drag_coefficient1/_number_of_parts*_number_of_blades*_c2;
1301 rps[0]->calculateAlpha(v_wind,rho_null,_pitch_a,0,0,
1302 &(torque[0]),&(lift[0])); //pitch a
1303 rps[0]->calculateAlpha(v_wind,rho_null,_pitch_b,0,0,
1304 &(torque[1]),&(lift[1])); //pitch b
1305 rps[0]->calculateAlpha(v_wind,rho_null,0,0,0,
1306 &(torque[3]),&(lift[3])); //pitch 0
1307 SG_LOG(SG_GENERAL, SG_INFO,
1308 "Rotor: coefficients for airfoil:" << endl << setprecision(6)
1309 << " drag0: " << _dragcoef0*_number_of_parts/_number_of_blades/_c2
1310 << " drag1: " << _dragcoef1*_number_of_parts/_number_of_blades/_c2
1311 << " lift: " << _liftcoef*_number_of_parts/_number_of_blades
1313 << "at 10 deg:" << endl
1314 << "drag: " << (Math::sin(10./180*pi)*_dragcoef1+_dragcoef0)
1315 *_number_of_parts/_number_of_blades/_c2
1316 << " lift: " << Math::sin(10./180*pi)*_liftcoef*_number_of_parts/_number_of_blades
1318 << "Some results (Pitch [degree], Power [kW], Lift [N])" << endl
1319 << 0.0f << "deg " << Math::abs(torque[3]*_number_of_parts*_omegan/1000) << "kW "
1320 << lift[3]*_number_of_parts << endl
1321 << _pitch_a*180/pi << "deg " << Math::abs(torque[0]*_number_of_parts*_omegan/1000)
1322 << "kW " << lift[0]*_number_of_parts << endl
1323 << _pitch_b*180/pi << "deg " << Math::abs(torque[1]*_number_of_parts*_omegan/1000)
1324 << "kW " << lift[1]*_number_of_parts << endl << endl );
1326 //first calculation of relamp is wrong
1327 //it used pitchaforce, but this was unknown and
1328 //on the default value
1329 _delta*=lift[0]/pitchaforce;
1330 relamp=(omega*omega/(2*_delta*Math::sqrt(sqr(omega0*omega0-omega*omega)
1331 +4*_delta*_delta*omega*omega)))*_cyclic_factor;
1332 for (i=0;i<_number_of_parts;i++)
1334 rps[i]->setRelamp(relamp);
1336 rps[0]->setOmega(0);
1343 //tie the properties
1344 /* After reset these values are totally wrong. I have to find out why
1345 SGPropertyNode * node = fgGetNode("/rotors", true)->getNode(_name,true);
1346 node->tie("balance_ext",SGRawValuePointer<float>(&_balance2),false);
1347 node->tie("balance_int",SGRawValuePointer<float>(&_balance1));
1351 std::ostream & operator<<(std::ostream & out, Rotor& r)
1353 #define i(x) << #x << ":" << r.x << endl
1354 #define iv(x) << #x << ":" << r.x[0] << ";" << r.x[1] << ";" <<r.x[2] << ";" << endl
1355 out << "Writing Info on Rotor "
1358 i(_omega) i(_omegan) i(_omegarel) i(_ddt_omega) i(_omegarelneu)
1361 i( _airfoil_incidence_no_lift)
1363 i( _airfoil_lift_coefficient)
1364 i( _airfoil_drag_coefficient0)
1365 i( _airfoil_drag_coefficient1)
1367 i( _number_of_segments)
1368 i( _number_of_parts)
1370 iv( _groundeffectpos[0])iv( _groundeffectpos[1])iv( _groundeffectpos[2])iv( _groundeffectpos[3])
1371 i( _ground_effect_altitude)
1373 iv( _normal_with_yaw_roll)
1376 i( _number_of_blades)
1377 i( _weight_per_blade)
1378 i( _rel_blade_center)
1381 i( _force_at_pitch_a)
1383 i( _power_at_pitch_0)
1384 i( _power_at_pitch_b)
1401 i( _alphamin) i(_alphamax) i(_alpha0) i(_alpha0factor)
1402 i( _teeterdamp) i(_maxteeterdamp)
1403 i( _rellenteeterhinge)
1405 i( _translift_maxfactor)
1406 i( _ground_effect_constant)
1407 i( _vortex_state_lift_factor)
1408 i( _vortex_state_c1)
1409 i( _vortex_state_c2)
1410 i( _vortex_state_c3)
1411 i( _vortex_state_e1)
1412 i( _vortex_state_e2)
1413 i( _vortex_state_e3)
1414 i( _lift_factor) i(_f_ge) i(_f_vs) i(_f_tl)
1419 i( _twist) //outer incidence = inner inner incidence + _twist
1420 i( _rel_len_where_incidence_is_measured)
1421 i( _torque_of_inertia)
1422 i( _rel_len_blade_start)
1423 i( _incidence_stall_zero_speed)
1424 i( _incidence_stall_half_sonic_speed)
1425 i( _lift_factor_stall)
1426 i( _stall_change_over)
1427 i( _drag_factor_stall)
1434 i( _cyclic_factor) <<endl;
1436 for(j=0; j<r._rotorparts.size(); j++) {
1437 out << *((Rotorpart*)r._rotorparts.get(j));
1444 void Rotor:: writeInfo()
1447 std::ostringstream buffer;
1449 FILE*f=fopen("c:\\fgmsvc\\bat\\log.txt","at");
1450 if (!f) f=fopen("c:\\fgmsvc\\bat\\log.txt","wt");
1453 fprintf(f,"%s",(const char *)buffer.str().c_str());
1458 Rotorpart* Rotor::newRotorpart(float zentforce,float maxpitchforce,
1459 float delta3,float mass,float translift,float rellenhinge,float len)
1461 Rotorpart *r = new Rotorpart();
1462 r->setDelta3(delta3);
1463 r->setDynamic(_dynamic);
1464 r->setTranslift(_translift);
1467 r->setRelLenHinge(rellenhinge);
1468 r->setSharedFlapHinge(_shared_flap_hinge);
1469 r->setOmegaN(_omegan);
1470 r->setPhi(_phi_null);
1471 r->setAlpha0(_alpha0);
1472 r->setAlphamin(_alphamin);
1473 r->setAlphamax(_alphamax);
1474 r->setAlpha0factor(_alpha0factor);
1476 r->setDiameter(_diameter);
1478 #define p(a) r->setParameter(#a,_##a);
1480 p(number_of_segments)
1481 p(rel_len_where_incidence_is_measured)
1482 p(rel_len_blade_start)
1483 p(rotor_correction_factor)
1488 void Rotor::interp(float* v1, float* v2, float frac, float* out)
1490 out[0] = v1[0] + frac*(v2[0]-v1[0]);
1491 out[1] = v1[1] + frac*(v2[1]-v1[1]);
1492 out[2] = v1[2] + frac*(v2[2]-v1[2]);
1495 void Rotorgear::initRotorIteration(float *lrot,float dt)
1499 if (!_rotors.size()) return;
1500 Rotor* r0 = (Rotor*)_rotors.get(0);
1501 omegarel= r0->getOmegaRelNeu();
1502 for(i=0; i<_rotors.size(); i++) {
1503 Rotor* r = (Rotor*)_rotors.get(i);
1504 r->inititeration(dt,omegarel,0,lrot);
1508 void Rotorgear::calcForces(float* torqueOut)
1511 torqueOut[0]=torqueOut[1]=torqueOut[2]=0;
1512 // check,<if the engine can handle the torque of the rotors.
1513 // If not reduce the torque to the fueselage and change rotational
1514 // speed of the rotors instead
1517 float omegarel,omegan;
1518 Rotor* r0 = (Rotor*)_rotors.get(0);
1519 omegarel= r0->getOmegaRel();
1521 float total_torque_of_inertia=0;
1522 float total_torque=0;
1523 for(i=0; i<_rotors.size(); i++) {
1524 Rotor* r = (Rotor*)_rotors.get(i);
1525 omegan=r->getOmegan();
1526 total_torque_of_inertia+=r->getTorqueOfInertia()*omegan*omegan;
1527 //FIXME: this is constant, so this can be done in compile
1529 total_torque+=r->getTorque()*omegan;
1531 float max_torque_of_engine=0;
1532 SGPropertyNode * node=fgGetNode("/rotors/gear", true);
1535 max_torque_of_engine=_max_power_engine*_max_rel_torque;
1536 float df=_target_rel_rpm-omegarel;
1537 df/=_engine_prop_factor;
1538 df = Math::clamp(df, 0, 1);
1539 max_torque_of_engine = df * _max_power_engine*_max_rel_torque;
1543 float rel_torque_engine=1;
1544 if (total_torque<=0)
1545 rel_torque_engine=0;
1547 if (max_torque_of_engine>0)
1548 rel_torque_engine=1/max_torque_of_engine*total_torque;
1550 rel_torque_engine=0;
1552 //add the rotor brake and the gear fritcion
1554 if (r0->_rotorparts.size()) dt=((Rotorpart*)r0->_rotorparts.get(0))->getDt();
1556 float rotor_brake_torque;
1557 rotor_brake_torque=_rotorbrake*_max_power_rotor_brake+_rotorgear_friction;
1558 //clamp it to the value you need to stop the rotor
1559 //to avod accelerate the rotor to neagtive rpm:
1560 rotor_brake_torque=Math::clamp(rotor_brake_torque,0,
1561 total_torque_of_inertia/dt*omegarel);
1562 max_torque_of_engine-=rotor_brake_torque;
1564 //change the rotation of the rotors
1565 if ((max_torque_of_engine<total_torque) //decreasing rotation
1566 ||((max_torque_of_engine>total_torque)&&(omegarel<_target_rel_rpm))
1567 //increasing rotation due to engine
1568 ||(total_torque<0) ) //increasing rotation due to autorotation
1570 _ddt_omegarel=(max_torque_of_engine-total_torque)/total_torque_of_inertia;
1571 if(max_torque_of_engine>total_torque)
1573 //check if the acceleration is due to the engine. If yes,
1574 //the engine self limits the accel.
1575 float lim1=-total_torque/total_torque_of_inertia;
1576 //accel. by autorotation
1578 if (lim1<_engine_accel_limit) lim1=_engine_accel_limit;
1579 //if the accel by autorotation greater than the max. engine
1580 //accel, then this is the limit, if not: the engine is the limit
1581 if (_ddt_omegarel>lim1) _ddt_omegarel=lim1;
1583 if (_ddt_omegarel>5.5)_ddt_omegarel=5.5;
1584 //clamp it to avoid overflow. Should never be reached
1585 if (_ddt_omegarel<-5.5)_ddt_omegarel=-5.5;
1587 if (_max_power_engine<0.001) {omegarel=1;_ddt_omegarel=0;}
1588 //for debug: negative or no maxpower will result
1589 //in permanet 100% rotation
1591 omegarel+=dt*_ddt_omegarel;
1593 if (omegarel>2.5) omegarel=2.5;
1594 //clamp it to avoid overflow. Should never be reached
1595 if (omegarel<-.5) omegarel=-.5;
1597 r0->setOmegaRelNeu(omegarel);
1598 //calculate the torque, which is needed to accelerate the rotors.
1599 //Add this additional torque to the body
1600 for(j=0; j<_rotors.size(); j++) {
1601 Rotor* r = (Rotor*)_rotors.get(j);
1602 for(i=0; i<r->_rotorparts.size(); i++) {
1603 float torque_scalar=0;
1604 Rotorpart* rp = (Rotorpart*)r->_rotorparts.get(i);
1606 rp->getAccelTorque(_ddt_omegarel,torque);
1607 Math::add3(torque,torqueOut,torqueOut);
1611 _total_torque_on_engine=total_torque+_ddt_omegarel*total_torque_of_inertia;
1615 void Rotorgear::addRotor(Rotor* rotor)
1621 void Rotorgear::compile()
1624 for(int j=0; j<_rotors.size(); j++) {
1625 Rotor* r = (Rotor*)_rotors.get(j);
1630 void Rotorgear::getDownWash(float *pos, float * v_heli, float *downwash)
1633 downwash[0]=downwash[1]=downwash[2]=0;
1634 for(int i=0; i<_rotors.size(); i++) {
1635 Rotor* ro = (Rotor*)_rotors.get(i);
1636 ro->getDownWash(pos,v_heli,tmp);
1637 Math::add3(downwash,tmp,downwash); // + downwash
1641 void Rotorgear::setParameter(char *parametername, float value)
1643 #define p(a,b) if (strcmp(parametername,#a)==0) _##a = (value * (b)); else
1644 p(max_power_engine,1000)
1645 p(engine_prop_factor,1)
1646 p(yasimdragfactor,1)
1647 p(yasimliftfactor,1)
1648 p(max_power_rotor_brake,1000)
1649 p(rotorgear_friction,1000)
1650 p(engine_accel_limit,0.01)
1651 SG_LOG(SG_INPUT, SG_ALERT,
1652 "internal error in parameter set up for rotorgear: '"
1653 << parametername <<"'" << endl);
1656 int Rotorgear::getValueforFGSet(int j,char *text,float *f)
1660 sprintf(text,"/rotors/gear/total-torque");
1661 *f=_total_torque_on_engine;
1665 Rotorgear::Rotorgear()
1670 _max_power_rotor_brake=1;
1671 _rotorgear_friction=1;
1672 _max_power_engine=1000*450;
1673 _engine_prop_factor=0.05f;
1677 _engine_accel_limit=0.05f;
1678 _total_torque_on_engine=0;
1683 Rotorgear::~Rotorgear()
1685 for(int i=0; i<_rotors.size(); i++)
1686 delete (Rotor*)_rotors.get(i);
1689 }; // namespace yasim