1 #include <simgear/debug/logstream.hxx>
4 #include <Main/fg_props.hxx>
6 #include "Rotorpart.hpp"
14 SG_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;
155 for(i=0; i<_rotorparts.size(); i++) {
156 Rotorpart* r = (Rotorpart*)_rotorparts.get(i);
159 //untie the properties
162 SGPropertyNode * node = fgGetNode("/rotors", true)->getNode(_name,true);
163 node->untie("balance-ext");
164 node->untie("balance-int");
169 void Rotor::inititeration(float dt,float omegarel,float ddt_omegarel,float *rot)
174 _omega=_omegan*_omegarel;
175 _ddt_omega=_omegan*ddt_omegarel;
177 updateDirectionsAndPositions();
178 for(i=0; i<_rotorparts.size(); i++) {
179 float s = Math::sin(float(2*pi*i/_number_of_parts+(_phi-pi/2.)*(_ccw?1:-1)));
180 float c = Math::cos(float(2*pi*i/_number_of_parts+(_phi-pi/2.)*(_ccw?1:-1)));
181 Rotorpart* r = (Rotorpart*)_rotorparts.get(i);
183 r->setDdtOmega(_ddt_omega);
184 r->inititeration(dt,rot);
185 r->setCyclic(_cyclicail*c+_cyclicele*s);
188 //calculate the normal of the rotor disc, for calcualtion of the downwash
189 float side[3],help[3];
190 Math::cross3(_normal,_forward,side);
191 Math::mul3(Math::cos(_yaw)*Math::cos(_roll),_normal,_normal_with_yaw_roll);
193 Math::mul3(Math::sin(_yaw),_forward,help);
194 Math::add3(_normal_with_yaw_roll,help,_normal_with_yaw_roll);
196 Math::mul3(Math::sin(_roll),side,help);
197 Math::add3(_normal_with_yaw_roll,help,_normal_with_yaw_roll);
200 if ((_balance1*_balance2 < 0.97) && (_balance1>-1))
202 _balance1-=(0.97-_balance1*_balance2)*(0.97-_balance1*_balance2)*0.005;
203 if (_balance1<-1) _balance1=-1;
207 float Rotor::calcStall(float incidence,float speed)
209 float stall_incidence=_incidence_stall_zero_speed
210 +(_incidence_stall_half_sonic_speed
211 -_incidence_stall_zero_speed)*speed/(343./2);
212 //missing: Temeperature dependency of sonic speed
213 incidence = Math::abs(incidence);
214 if (incidence > (90./180.*pi))
215 incidence = pi-incidence;
216 float stall = (incidence-stall_incidence)/_stall_change_over;
217 stall = Math::clamp(stall,0,1);
219 _stall_sum+=stall*speed*speed;
220 _stall_v2sum+=speed*speed;
225 float Rotor::getLiftCoef(float incidence,float speed)
227 float stall=calcStall(incidence,speed);
228 /* the next shold look like this, but this is the inner loop of
229 the rotor simulation. For small angles (and we hav only small
230 angles) the first order approximation works well
231 float c1= Math::sin(incidence-_airfoil_incidence_no_lift)*_liftcoef;
232 for c2 we would need higher order, because in stall the angle can be large
235 if (incidence > (pi/2))
237 else if (incidence <-(pi/2))
241 float c1= (i2-_airfoil_incidence_no_lift)*_liftcoef;
244 float c2= Math::sin(2*(incidence-_airfoil_incidence_no_lift))
245 *_liftcoef*_lift_factor_stall;
246 return (1-stall)*c1 + stall *c2;
252 float Rotor::getDragCoef(float incidence,float speed)
254 float stall=calcStall(incidence,speed);
255 float c1= (Math::abs(Math::sin(incidence-_airfoil_incidence_no_lift))
256 *_dragcoef1+_dragcoef0);
257 float c2= c1*_drag_factor_stall;
258 return (1-stall)*c1 + stall *c2;
261 int Rotor::getValueforFGSet(int j,char *text,float *f)
263 if (_name[0]==0) return 0;
264 if (4>numRotorparts()) return 0; //compile first!
267 sprintf(text,"/rotors/%s/cone-deg", _name);
268 *f=(_balance1>-1)?( ((Rotorpart*)getRotorpart(0))->getrealAlpha()
269 +((Rotorpart*)getRotorpart(1*(_number_of_parts>>2)))->getrealAlpha()
270 +((Rotorpart*)getRotorpart(2*(_number_of_parts>>2)))->getrealAlpha()
271 +((Rotorpart*)getRotorpart(3*(_number_of_parts>>2)))->getrealAlpha()
277 sprintf(text,"/rotors/%s/roll-deg", _name);
278 _roll = ( ((Rotorpart*)getRotorpart(0))->getrealAlpha()
279 -((Rotorpart*)getRotorpart(2*(_number_of_parts>>2)))->getrealAlpha()
281 *f=(_balance1>-1)?_roll *180/pi:0;
286 sprintf(text,"/rotors/%s/yaw-deg", _name);
287 _yaw=( ((Rotorpart*)getRotorpart(1*(_number_of_parts>>2)))->getrealAlpha()
288 -((Rotorpart*)getRotorpart(3*(_number_of_parts>>2)))->getrealAlpha()
290 *f=(_balance1>-1)?_yaw*180/pi:0;
295 sprintf(text,"/rotors/%s/rpm", _name);
296 *f=(_balance1>-1)?_omega/2/pi*60:0;
301 sprintf(text,"/rotors/%s/tilt/pitch-deg",_name);
302 *f=_tilt_pitch*180/pi;
306 sprintf(text,"/rotors/%s/tilt/roll-deg",_name);
307 *f=_tilt_roll*180/pi;
311 sprintf(text,"/rotors/%s/tilt/yaw-deg",_name);
316 sprintf(text,"/rotors/%s/balance", _name);
321 sprintf(text,"/rotors/%s/stall",_name);
322 *f=getOverallStall();
326 sprintf(text,"/rotors/%s/torque",_name);
332 if (b>=_number_of_blades)
337 sprintf(text,"/rotors/%s/blade[%i]/%s",
339 w==0?"position-deg":(w==1?"flap-deg":"incidence-deg"));
340 *f=((Rotorpart*)getRotorpart(0))->getPhi()*180/pi
341 +360*b/_number_of_blades*(_ccw?1:-1);
344 if (_balance1<=-1) *f=0;
351 rk=Math::clamp(rk,0,1);//Delete this
353 if(w==2) {k+=2;l+=2;}
355 if(w==1) {k+=1;l+=1;}
358 if (w==1) *f=rk*((Rotorpart*) getRotorpart(k*(_number_of_parts>>2)))->getrealAlpha()*180/pi
359 +rl*((Rotorpart*) getRotorpart(l*(_number_of_parts>>2)))->getrealAlpha()*180/pi;
360 else if(w==2) *f=rk*((Rotorpart*)getRotorpart(k*(_number_of_parts>>2)))->getIncidence()*180/pi
361 +rl*((Rotorpart*)getRotorpart(l*(_number_of_parts>>2)))->getIncidence()*180/pi;
366 void Rotorgear::setEngineOn(int value)
371 void Rotorgear::setRotorEngineMaxRelTorque(float lval)
373 SGPropertyNode * node = fgGetNode("/rotors/gear/max-rel-torque", true);
374 if (node) node->setDoubleValue(lval);
377 void Rotorgear::setRotorRelTarget(float lval)
379 SGPropertyNode * node = fgGetNode("/rotors/gear/target-rel-rpm", true);
380 if (node) node->setDoubleValue(lval);
383 void Rotor::setAlpha0(float f)
388 void Rotor::setAlphamin(float f)
393 void Rotor::setAlphamax(float f)
398 void Rotor::setAlpha0factor(float f)
403 int Rotor::numRotorparts()
405 return _rotorparts.size();
408 Rotorpart* Rotor::getRotorpart(int n)
410 return ((Rotorpart*)_rotorparts.get(n));
413 int Rotorgear::getEngineon()
418 float Rotor::getTorqueOfInertia()
420 return _torque_of_inertia;
423 void Rotor::setTorque(float f)
428 void Rotor::addTorque(float f)
433 void Rotor::strncpy(char *dest,const char *src,int maxlen)
436 while(src[n]&&n<(maxlen-1))
444 void Rotor::setNormal(float* normal)
447 float invsum,sqrsum=0;
448 for(i=0; i<3; i++) { sqrsum+=normal[i]*normal[i];}
450 invsum=1/Math::sqrt(sqrsum);
455 _normal_with_yaw_roll[i]=_normal[i] = normal[i]*invsum;
459 void Rotor::setForward(float* forward)
462 float invsum,sqrsum=0;
463 for(i=0; i<3; i++) { sqrsum+=forward[i]*forward[i];}
465 invsum=1/Math::sqrt(sqrsum);
468 for(i=0; i<3; i++) { _forward[i] = forward[i]*invsum; }
471 void Rotor::setForceAtPitchA(float force)
473 _force_at_pitch_a=force;
476 void Rotor::setPowerAtPitch0(float value)
478 _power_at_pitch_0=value;
481 void Rotor::setPowerAtPitchB(float value)
483 _power_at_pitch_b=value;
486 void Rotor::setPitchA(float value)
488 _pitch_a=value/180*pi;
491 void Rotor::setPitchB(float value)
493 _pitch_b=value/180*pi;
496 void Rotor::setBase(float* base)
499 for(i=0; i<3; i++) _base[i] = base[i];
502 void Rotor::setMaxCyclicail(float value)
504 _maxcyclicail=value/180*pi;
507 void Rotor::setMaxCyclicele(float value)
509 _maxcyclicele=value/180*pi;
512 void Rotor::setMinCyclicail(float value)
514 _mincyclicail=value/180*pi;
517 void Rotor::setMinCyclicele(float value)
519 _mincyclicele=value/180*pi;
522 void Rotor::setMinCollective(float value)
524 _min_pitch=value/180*pi;
527 void Rotor::setMinTiltYaw(float value)
529 _min_tilt_yaw=value/180*pi;
532 void Rotor::setMinTiltPitch(float value)
534 _min_tilt_pitch=value/180*pi;
537 void Rotor::setMinTiltRoll(float value)
539 _min_tilt_roll=value/180*pi;
542 void Rotor::setMaxTiltYaw(float value)
544 _max_tilt_yaw=value/180*pi;
547 void Rotor::setMaxTiltPitch(float value)
549 _max_tilt_pitch=value/180*pi;
552 void Rotor::setMaxTiltRoll(float value)
554 _max_tilt_roll=value/180*pi;
557 void Rotor::setTiltCenterX(float value)
559 _tilt_center[0] = value;
562 void Rotor::setTiltCenterY(float value)
564 _tilt_center[1] = value;
567 void Rotor::setTiltCenterZ(float value)
569 _tilt_center[2] = value;
572 void Rotor::setMaxCollective(float value)
574 _max_pitch=value/180*pi;
577 void Rotor::setDiameter(float value)
582 void Rotor::setWeightPerBlade(float value)
584 _weight_per_blade=value;
587 void Rotor::setNumberOfBlades(float value)
589 _number_of_blades=int(value+.5);
592 void Rotor::setRelBladeCenter(float value)
594 _rel_blade_center=value;
597 void Rotor::setDynamic(float value)
602 void Rotor::setDelta3(float value)
607 void Rotor::setDelta(float value)
612 void Rotor::setTranslift(float value)
617 void Rotor::setSharedFlapHinge(bool s)
619 _shared_flap_hinge=s;
622 void Rotor::setBalance(float b)
627 void Rotor::setC2(float value)
632 void Rotor::setStepspersecond(float steps)
634 _stepspersecond=steps;
637 void Rotor::setRPM(float value)
642 void Rotor::setPhiNull(float value)
647 void Rotor::setRelLenHinge(float value)
649 _rel_len_hinge=value;
652 void Rotor::setAlphaoutput(int i, const char *text)
654 strncpy(_alphaoutput[i],text,255);
657 void Rotor::setName(const char *text)
659 strncpy(_name,text,256);//256: some space needed for settings
662 void Rotor::setCcw(int ccw)
667 void Rotor::setNotorque(int value)
672 void Rotor::setRelLenTeeterHinge(float f)
674 _rellenteeterhinge=f;
677 void Rotor::setTeeterdamp(float f)
682 void Rotor::setMaxteeterdamp(float f)
687 void Rotor::setGlobalGround(double *global_ground, float* global_vel)
690 for(i=0; i<4; i++) _global_ground[i] = global_ground[i];
693 void Rotor::setParameter(char *parametername, float value)
695 #define p(a,b) if (strcmp(parametername,#a)==0) _##a = (value * (b)); else
697 p(translift_maxfactor,1)
698 p(ground_effect_constant,1)
699 p(vortex_state_lift_factor,1)
707 p(number_of_segments,1)
709 p(rel_len_where_incidence_is_measured,1)
712 p(airfoil_incidence_no_lift,pi/180.)
713 p(rel_len_blade_start,1)
714 p(incidence_stall_zero_speed,pi/180.)
715 p(incidence_stall_half_sonic_speed,pi/180.)
716 p(lift_factor_stall,1)
717 p(stall_change_over,pi/180.)
718 p(drag_factor_stall,1)
719 p(airfoil_lift_coefficient,1)
720 p(airfoil_drag_coefficient0,1)
721 p(airfoil_drag_coefficient1,1)
723 p(rotor_correction_factor,1)
724 SG_LOG(SG_INPUT, SG_ALERT,
725 "internal error in parameter set up for rotor: '" <<
726 parametername <<"'" << endl);
730 float Rotor::getLiftFactor()
735 void Rotorgear::setRotorBrake(float lval)
737 lval = Math::clamp(lval, 0, 1);
741 void Rotor::setTiltYaw(float lval)
743 lval = Math::clamp(lval, -1, 1);
744 _tilt_yaw = _min_tilt_yaw+(lval+1)/2*(_max_tilt_yaw-_min_tilt_yaw);
745 _directions_and_postions_dirty = true;
748 void Rotor::setTiltPitch(float lval)
750 lval = Math::clamp(lval, -1, 1);
751 _tilt_pitch = _min_tilt_pitch+(lval+1)/2*(_max_tilt_pitch-_min_tilt_pitch);
752 _directions_and_postions_dirty = true;
755 void Rotor::setTiltRoll(float lval)
757 lval = Math::clamp(lval, -1, 1);
758 _tilt_roll = _min_tilt_roll+(lval+1)/2*(_max_tilt_roll-_min_tilt_roll);
759 _directions_and_postions_dirty = true;
762 void Rotor::setCollective(float lval)
764 lval = Math::clamp(lval, -1, 1);
766 _collective=_min_pitch+(lval+1)/2*(_max_pitch-_min_pitch);
767 for(i=0; i<_rotorparts.size(); i++) {
768 ((Rotorpart*)_rotorparts.get(i))->setCollective(_collective);
772 void Rotor::setCyclicele(float lval,float rval)
774 lval = Math::clamp(lval, -1, 1);
775 _cyclicele=_mincyclicele+(lval+1)/2*(_maxcyclicele-_mincyclicele);
778 void Rotor::setCyclicail(float lval,float rval)
780 lval = Math::clamp(lval, -1, 1);
782 _cyclicail=-(_mincyclicail+(lval+1)/2*(_maxcyclicail-_mincyclicail));
785 void Rotor::getPosition(float* out)
788 for(i=0; i<3; i++) out[i] = _base[i];
791 void Rotor::calcLiftFactor(float* v, float rho, State *s)
793 //calculates _lift_factor, which is a foactor for the lift of the rotor
795 //- ground effect (_f_ge)
796 //- vortex state (_f_vs)
797 //- translational lift (_f_tl)
802 // Calculate ground effect
803 _f_ge=1+_diameter/_ground_effect_altitude*_ground_effect_constant;
805 // Now calculate translational lift
806 float v_vert = Math::dot3(v,_normal);
808 Math::cross3(v,_normal,help);
809 float v_horiz = Math::mag3(help);
810 _f_tl = ((1-Math::pow(2.7183,-v_horiz/_translift_ve))
811 *(_translift_maxfactor-1)+1)/_translift_maxfactor;
813 _lift_factor = _f_ge*_f_tl*_f_vs;
815 //store the gravity direction
816 Glue::geodUp(s->pos, _grav_direction);
819 void Rotor::findGroundEffectAltitude(Ground * ground_cb,State *s)
821 _ground_effect_altitude=findGroundEffectAltitude(ground_cb,s,
822 _groundeffectpos[0],_groundeffectpos[1],
823 _groundeffectpos[2],_groundeffectpos[3]);
824 testForRotorGroundContact(ground_cb,s);
827 void Rotor::testForRotorGroundContact(Ground * ground_cb,State *s)
830 for (i=0;i<_num_ground_contact_pos;i++)
833 s->posLocalToGlobal(_ground_contact_pos[i], pt);
835 // Ask for the ground plane in the global coordinate system
836 double global_ground[4];
838 ground_cb->getGroundPlane(pt, global_ground, global_vel);
839 // find h, the distance to the ground
840 // The ground plane transformed to the local frame.
842 s->planeGlobalToLocal(global_ground, ground);
844 h = ground[3] - Math::dot3(_ground_contact_pos[i], ground);
845 // Now h is the distance from _ground_contact_pos[i] to ground
848 _balance1 -= (-h)/_diameter/_num_ground_contact_pos;
849 _balance1 = (_balance1<-1)?-1:_balance1;
853 float Rotor::findGroundEffectAltitude(Ground * ground_cb,State *s,
854 float *pos0,float *pos1,float *pos2,float *pos3,
855 int iteration,float a0,float a1,float a2,float a3)
869 Math::add3(p[0],p[2],p[4]);
870 Math::mul3(0.5,p[4],p[4]);//the center
872 float mina=100*_diameter;
874 for (int i=0;i<5;i++)
876 if (a[i]==-1)//in the first iteration,(iteration==0) no height is
877 //passed to this function, these missing values are
881 s->posLocalToGlobal(p[i], pt);
883 // Ask for the ground plane in the global coordinate system
884 double global_ground[4];
886 ground_cb->getGroundPlane(pt, global_ground, global_vel);
887 // find h, the distance to the ground
888 // The ground plane transformed to the local frame.
890 s->planeGlobalToLocal(global_ground, ground);
892 a[i] = ground[3] - Math::dot3(p[i], ground);
893 // Now a[i] is the distance from p[i] to ground
899 if (mina>=10*_diameter)
900 return mina; //the ground effect will be zero
902 //check if further recursion is neccessary
903 //if the height does not differ more than 20%, than
904 //we can return then mean height, if not split
905 //zhe square to four parts and calcualte the height
907 //suma * 0.2 is the mean
908 //0.15 is the maximum allowed difference from the mean
909 //to the height at the center
911 ||(Math::abs(suma*0.2-a[4])<(0.15*0.2*suma*(1<<iteration))))
914 float pc[4][3],ac[4]; //pc[i]=center of pos[i] and pos[(i+1)&3]
915 for (int i=0;i<4;i++)
917 Math::add3(p[i],p[(i+1)&3],pc[i]);
918 Math::mul3(0.5,pc[i],pc[i]);
920 s->posLocalToGlobal(pc[i], pt);
922 // Ask for the ground plane in the global coordinate system
923 double global_ground[4];
925 ground_cb->getGroundPlane(pt, global_ground, global_vel);
926 // find h, the distance to the ground
927 // The ground plane transformed to the local frame.
929 s->planeGlobalToLocal(global_ground, ground);
931 ac[i] = ground[3] - Math::dot3(p[i], ground);
932 // Now ac[i] is the distance from pc[i] to ground
935 (findGroundEffectAltitude(ground_cb,s,p[0],pc[1],p[4],pc[3],
936 iteration+1,a[0],ac[0],a[4],ac[3])
937 +findGroundEffectAltitude(ground_cb,s,p[1],pc[0],p[4],pc[1],
938 iteration+1,a[1],ac[0],a[4],ac[1])
939 +findGroundEffectAltitude(ground_cb,s,p[2],pc[1],p[4],pc[2],
940 iteration+1,a[2],ac[1],a[4],ac[2])
941 +findGroundEffectAltitude(ground_cb,s,p[3],pc[2],p[4],pc[3],
942 iteration+1,a[3],ac[2],a[4],ac[3])
946 void Rotor::getDownWash(float *pos, float *v_heli, float *downwash)
948 float pos2rotor[3],tmp[3];
949 Math::sub3(_base,pos,pos2rotor);
950 float dist=Math::dot3(pos2rotor,_normal_with_yaw_roll);
951 //calculate incidence at 0.7r;
952 float inc = _collective+_twist *0.7
953 - _twist*_rel_len_where_incidence_is_measured;
956 if (dist<0) // we are not in the downwash region
958 downwash[0]=downwash[1]=downwash[2]=0.;
962 //calculate the mean downwash speed directly beneath the rotor disk
963 float v1bar = Math::sin(inc) *_omega * 0.35 * _diameter * 0.8;
964 //0.35 * d = 0.7 *r, a good position to calcualte the mean downwashd
965 //0.8 the slip of the rotor.
967 //calculate the time the wind needed from thr rotor to here
968 if (v1bar< 1) v1bar = 1;
969 float time=dist/v1bar;
971 //calculate the pos2rotor, where the rotor was, "time" ago
972 Math::mul3(time,v_heli,tmp);
973 Math::sub3(pos2rotor,tmp,pos2rotor);
975 //and again calculate dist
976 dist=Math::dot3(pos2rotor,_normal_with_yaw_roll);
977 //missing the normal is offen not pointing to the normal of the rotor
978 //disk. Rotate the normal by yaw and tilt angle
982 if (dist<0) // we are not in the downwash region
984 downwash[0]=downwash[1]=downwash[2]=0.;
987 //of course this could be done in a runge kutta integrator, but it's such
988 //a approximation that I beleave, it would'nt be more realistic
990 //calculate the dist to the rotor-axis
991 Math::cross3(pos2rotor,_normal_with_yaw_roll,tmp);
992 float r= Math::mag3(tmp);
993 //calculate incidence at r;
994 float rel_r = r *2 /_diameter;
995 float inc_r = _collective+_twist * r /_diameter * 2
996 - _twist*_rel_len_where_incidence_is_measured;
998 //calculate the downwash speed directly beneath the rotor disk
1001 v1 = Math::sin(inc_r) *_omega * r * 0.8;
1003 //calcualte the downwash speed in a distance "dist" to the rotor disc,
1004 //for large dist. The speed is assumed do follow a gausian distribution
1005 //with sigma increasing with dist^2:
1006 //sigma is assumed to be half of the rotor diameter directly beneath the
1007 //disc and is assumed to the rotor diameter at dist = (diameter * sqrt(2))
1009 float sigma=_diameter/2 + dist * dist / _diameter /4.;
1010 float v2 = v1bar*_diameter/ (Math::sqrt(2 * pi) * sigma)
1011 * Math::pow(2.7183,-.5*r*r/(sigma*sigma))*_diameter/2/sigma;
1013 //calculate the weight of the two downwash velocities.
1014 //Directly beneath the disc it is v1, far away it is v2
1015 float g = Math::pow(2.7183,-2*dist/_diameter);
1016 //at dist = rotor radius it is assumed to be 1/e * v1 + (1-1/e)* v2
1018 float v = g * v1 + (1-g) * v2;
1019 Math::mul3(-v,_normal_with_yaw_roll,downwash);
1020 //the downwash is calculated in the opposite direction of the normal
1023 void Rotor::euler2orient(float roll, float pitch, float hdg, float* out)
1025 // the Glue::euler2orient, inverts y<z due to different bases
1026 // therefore the negation of all "y" and "z" coeffizients
1027 Glue::euler2orient(roll,pitch,hdg,out);
1028 for (int i=3;i<9;i++) out[i]*=-1.0;
1032 void Rotor::updateDirectionsAndPositions()
1034 if (!_directions_and_postions_dirty)
1037 euler2orient(_tilt_roll, _tilt_pitch, _tilt_yaw, orient);
1041 Math::sub3(_base,_tilt_center,base);
1042 Math::vmul33(orient, base, base);
1043 Math::add3(base,_tilt_center,base);
1044 Math::vmul33(orient, _forward, forward);
1045 Math::vmul33(orient, _normal, normal);
1047 #define _forward forward
1048 #define _normal normal
1049 float directions[5][3];
1050 //pointing forward, right, ... the 5th is ony for calculation
1051 directions[0][0]=_forward[0];
1052 directions[0][1]=_forward[1];
1053 directions[0][2]=_forward[2];
1058 Math::cross3(directions[i-1],_normal,directions[i]);
1060 Math::cross3(_normal,directions[i-1],directions[i]);
1062 Math::set3(directions[4],directions[0]);
1063 // now directions[0] is perpendicular to the _normal.and has a length
1064 // of 1. if _forward is already normalized and perpendicular to the
1065 // normal, directions[0] will be the same
1066 //_num_ground_contact_pos=(_number_of_parts<16)?_number_of_parts:16;
1067 for (i=0;i<_num_ground_contact_pos;i++)
1070 float s = Math::sin(pi*2*i/_num_ground_contact_pos);
1071 float c = Math::cos(pi*2*i/_num_ground_contact_pos);
1072 Math::mul3(c*_diameter*0.5,directions[0],_ground_contact_pos[i]);
1073 Math::mul3(s*_diameter*0.5,directions[1],help);
1074 Math::add3(help,_ground_contact_pos[i],_ground_contact_pos[i]);
1075 Math::add3(_base,_ground_contact_pos[i],_ground_contact_pos[i]);
1079 Math::mul3(_diameter*0.7,directions[i],_groundeffectpos[i]);
1080 Math::add3(_base,_groundeffectpos[i],_groundeffectpos[i]);
1082 for (i=0;i<_number_of_parts;i++)
1084 Rotorpart* rp = getRotorpart(i);
1085 float lpos[3],lforceattac[3],lspeed[3],dirzentforce[3];
1086 float s = Math::sin(2*pi*i/_number_of_parts);
1087 float c = Math::cos(2*pi*i/_number_of_parts);
1088 float sp = Math::sin(float(2*pi*i/_number_of_parts-pi/2.+_phi));
1089 float cp = Math::cos(float(2*pi*i/_number_of_parts-pi/2.+_phi));
1090 float direction[3],nextdirection[3],help[3],direction90deg[3];
1091 float rotorpartmass = _weight_per_blade*_number_of_blades/_number_of_parts*.453;
1092 float speed=_rotor_rpm/60*_diameter*_rel_blade_center*pi;
1093 float lentocenter=_diameter*_rel_blade_center*0.5;
1094 float lentoforceattac=_diameter*_rel_len_hinge*0.5;
1095 float zentforce=rotorpartmass*speed*speed/lentocenter;
1097 Math::mul3(c ,directions[0],help);
1098 Math::mul3(s ,directions[1],direction);
1099 Math::add3(help,direction,direction);
1101 Math::mul3(c ,directions[1],help);
1102 Math::mul3(s ,directions[2],direction90deg);
1103 Math::add3(help,direction90deg,direction90deg);
1105 Math::mul3(cp ,directions[1],help);
1106 Math::mul3(sp ,directions[2],nextdirection);
1107 Math::add3(help,nextdirection,nextdirection);
1109 Math::mul3(lentocenter,direction,lpos);
1110 Math::add3(lpos,_base,lpos);
1111 Math::mul3(lentoforceattac,nextdirection,lforceattac);
1112 //nextdirection: +90deg (gyro)!!!
1114 Math::add3(lforceattac,_base,lforceattac);
1115 Math::mul3(speed,direction90deg,lspeed);
1116 Math::mul3(1,nextdirection,dirzentforce);
1117 rp->setPosition(lpos);
1118 rp->setNormal(_normal);
1119 rp->setZentipetalForce(zentforce);
1120 rp->setPositionForceAttac(lforceattac);
1121 rp->setSpeed(lspeed);
1122 rp->setDirectionofZentipetalforce(dirzentforce);
1123 rp->setDirectionofRotorPart(direction);
1128 _directions_and_postions_dirty=false;
1131 void Rotor::compile()
1133 // Have we already been compiled?
1134 if(_rotorparts.size() != 0) return;
1136 //rotor is divided into _number_of_parts parts
1137 //each part is calcualted at _number_of_segments points
1140 //and make it a factor of 4
1141 _number_of_parts=(int(Math::clamp(_number_of_parts,4,256))>>2)<<2;
1143 _dynamic=_dynamic*(1/ //inverse of the time
1144 ( (60/_rotor_rpm)/4 //for rotating 90 deg
1145 +(60/_rotor_rpm)/(2*_number_of_blades) //+ meantime a rotorblade
1146 //will pass a given point
1148 //normalize the directions
1149 Math::unit3(_forward,_forward);
1150 Math::unit3(_normal,_normal);
1151 _num_ground_contact_pos=(_number_of_parts<16)?_number_of_parts:16;
1152 float rotorpartmass = _weight_per_blade*_number_of_blades/_number_of_parts*.453;
1153 //was pounds -> now kg
1155 _torque_of_inertia = 1/12. * ( _number_of_parts * rotorpartmass) * _diameter
1156 * _diameter * _rel_blade_center * _rel_blade_center /(0.5*0.5);
1157 float speed=_rotor_rpm/60*_diameter*_rel_blade_center*pi;
1158 float lentocenter=_diameter*_rel_blade_center*0.5;
1159 float lentoforceattac=_diameter*_rel_len_hinge*0.5;
1160 float zentforce=rotorpartmass*speed*speed/lentocenter;
1161 float pitchaforce=_force_at_pitch_a/_number_of_parts*.453*9.81;
1162 // was pounds of force, now N, devided by _number_of_parts
1163 //(so its now per rotorpart)
1165 float torque0=0,torquemax=0,torqueb=0;
1166 float omega=_rotor_rpm/60*2*pi;
1168 float omega0=omega*Math::sqrt(1/(1-_rel_len_hinge));
1169 float delta_theoretical=pitchaforce/(_pitch_a*omega*lentocenter*2*rotorpartmass);
1170 _delta*=delta_theoretical;
1172 float relamp=(omega*omega/(2*_delta*Math::sqrt(sqr(omega0*omega0-omega*omega)
1173 +4*_delta*_delta*omega*omega)))*_cyclic_factor;
1174 float relamp_theoretical=(omega*omega/(2*delta_theoretical*Math::sqrt(sqr(omega0*omega0-omega*omega)
1175 +4*delta_theoretical*delta_theoretical*omega*omega)))*_cyclic_factor;
1176 _phi=Math::acos(_rel_len_hinge);
1177 _phi-=Math::atan(_delta3);
1180 torque0=_power_at_pitch_0/_number_of_parts*1000/omega;
1181 // f*r=p/w ; p=f*s/t; r=s/t/w ; r*w*t = s
1182 torqueb=_power_at_pitch_b/_number_of_parts*1000/omega;
1183 torquemax=_power_at_pitch_b/_number_of_parts*1000/omega/_pitch_b*_max_pitch;
1193 Rotorpart* rps[256];
1195 for (i=0;i<_number_of_parts;i++)
1197 Rotorpart* rp=rps[i]=newRotorpart(zentforce,pitchaforce,_delta3,rotorpartmass,
1198 _translift,_rel_len_hinge,lentocenter);
1199 int k = i*4/_number_of_parts;
1200 rp->setAlphaoutput(_alphaoutput[k&1?k:(_ccw?k^2:k)],0);
1201 rp->setAlphaoutput(_alphaoutput[4+(k&1?k:(_ccw?k^2:k))],1+(k>1));
1202 _rotorparts.add(rp);
1203 rp->setTorque(torquemax,torque0);
1204 rp->setRelamp(relamp);
1205 rp->setTorqueOfInertia(_torque_of_inertia/_number_of_parts);
1206 rp->setDirection(2*pi*i/_number_of_parts);
1208 for (i=0;i<_number_of_parts;i++)
1210 rps[i]->setlastnextrp(rps[(i-1+_number_of_parts)%_number_of_parts],
1211 rps[(i+1)%_number_of_parts],
1212 rps[(i+_number_of_parts/2)%_number_of_parts],
1213 rps[(i-_number_of_parts/4+_number_of_parts)%_number_of_parts],
1214 rps[(i+_number_of_parts/4)%_number_of_parts]);
1216 updateDirectionsAndPositions();
1217 for (i=0;i<_number_of_parts;i++)
1219 rps[i]->setCompiled();
1221 float lift[4],torque[4], v_wind[3];
1222 v_wind[0]=v_wind[1]=v_wind[2]=0;
1223 rps[0]->setOmega(_omegan);
1225 if (_airfoil_lift_coefficient==0)
1227 //calculate the lift and drag coefficients now
1231 rps[0]->calculateAlpha(v_wind,rho_null,_pitch_a,0,0,
1232 &(torque[0]),&(lift[0])); //max_pitch a
1233 _liftcoef = pitchaforce/lift[0];
1236 rps[0]->calculateAlpha(v_wind,rho_null,0,0,0,&(torque[0]),&(lift[0]));
1241 rps[0]->calculateAlpha(v_wind,rho_null,0,0,0,&(torque[1]),&(lift[1]));
1246 rps[0]->calculateAlpha(v_wind,rho_null,_pitch_b,0,0,&(torque[2]),&(lift[2]));
1251 rps[0]->calculateAlpha(v_wind,rho_null,_pitch_b,0,0,&(torque[3]),&(lift[3]));
1256 _dragcoef1=torque0/torque[1];
1257 _dragcoef0=(torqueb-_dragcoef1*torque[3])/torque[2];
1261 _dragcoef1=(torque0/torque[0]-torqueb/torque[2])
1262 /(torque[1]/torque[0]-torque[3]/torque[2]);
1263 _dragcoef0=(torqueb-_dragcoef1*torque[3])/torque[2];
1268 _liftcoef=_airfoil_lift_coefficient/_number_of_parts*_number_of_blades;
1269 _dragcoef0=_airfoil_drag_coefficient0/_number_of_parts*_number_of_blades*_c2;
1270 _dragcoef1=_airfoil_drag_coefficient1/_number_of_parts*_number_of_blades*_c2;
1274 rps[0]->calculateAlpha(v_wind,rho_null,_pitch_a,0,0,
1275 &(torque[0]),&(lift[0])); //pitch a
1276 rps[0]->calculateAlpha(v_wind,rho_null,_pitch_b,0,0,
1277 &(torque[1]),&(lift[1])); //pitch b
1278 rps[0]->calculateAlpha(v_wind,rho_null,0,0,0,
1279 &(torque[3]),&(lift[3])); //pitch 0
1280 SG_LOG(SG_GENERAL, SG_INFO,
1281 "Rotor: coefficients for airfoil:" << endl << setprecision(6)
1282 << " drag0: " << _dragcoef0*_number_of_parts/_number_of_blades/_c2
1283 << " drag1: " << _dragcoef1*_number_of_parts/_number_of_blades/_c2
1284 << " lift: " << _liftcoef*_number_of_parts/_number_of_blades
1286 << "at 10 deg:" << endl
1287 << "drag: " << (Math::sin(10./180*pi)*_dragcoef1+_dragcoef0)
1288 *_number_of_parts/_number_of_blades/_c2
1289 << " lift: " << Math::sin(10./180*pi)*_liftcoef*_number_of_parts/_number_of_blades
1291 << "Some results (Pitch [degree], Power [kW], Lift [N])" << endl
1292 << 0.0f << "deg " << Math::abs(torque[3]*_number_of_parts*_omegan/1000) << "kW "
1293 << lift[3]*_number_of_parts << endl
1294 << _pitch_a*180/pi << "deg " << Math::abs(torque[0]*_number_of_parts*_omegan/1000)
1295 << "kW " << lift[0]*_number_of_parts << endl
1296 << _pitch_b*180/pi << "deg " << Math::abs(torque[1]*_number_of_parts*_omegan/1000)
1297 << "kW " << lift[1]*_number_of_parts << endl << endl );
1299 //first calculation of relamp is wrong
1300 //it used pitchaforce, but this was unknown and
1301 //on the default value
1302 _delta*=lift[0]/pitchaforce;
1303 relamp=(omega*omega/(2*_delta*Math::sqrt(sqr(omega0*omega0-omega*omega)
1304 +4*_delta*_delta*omega*omega)))*_cyclic_factor;
1305 for (i=0;i<_number_of_parts;i++)
1307 rps[i]->setRelamp(relamp);
1309 rps[0]->setOmega(0);
1316 //tie the properties
1317 /* After reset these values are totally wrong. I have to find out why
1318 SGPropertyNode * node = fgGetNode("/rotors", true)->getNode(_name,true);
1319 node->tie("balance_ext",SGRawValuePointer<float>(&_balance2),false);
1320 node->tie("balance_int",SGRawValuePointer<float>(&_balance1));
1324 std::ostream & operator<<(std::ostream & out, Rotor& r)
1326 #define i(x) << #x << ":" << r.x << endl
1327 #define iv(x) << #x << ":" << r.x[0] << ";" << r.x[1] << ";" <<r.x[2] << ";" << endl
1328 out << "Writing Info on Rotor "
1331 i(_omega) i(_omegan) i(_omegarel) i(_ddt_omega) i(_omegarelneu)
1334 i( _airfoil_incidence_no_lift)
1336 i( _airfoil_lift_coefficient)
1337 i( _airfoil_drag_coefficient0)
1338 i( _airfoil_drag_coefficient1)
1340 i( _number_of_segments)
1341 i( _number_of_parts)
1343 iv( _groundeffectpos[0])iv( _groundeffectpos[1])iv( _groundeffectpos[2])iv( _groundeffectpos[3])
1344 i( _ground_effect_altitude)
1346 iv( _normal_with_yaw_roll)
1349 i( _number_of_blades)
1350 i( _weight_per_blade)
1351 i( _rel_blade_center)
1354 i( _force_at_pitch_a)
1356 i( _power_at_pitch_0)
1357 i( _power_at_pitch_b)
1374 i( _alphamin) i(_alphamax) i(_alpha0) i(_alpha0factor)
1375 i( _teeterdamp) i(_maxteeterdamp)
1376 i( _rellenteeterhinge)
1378 i( _translift_maxfactor)
1379 i( _ground_effect_constant)
1380 i( _vortex_state_lift_factor)
1381 i( _vortex_state_c1)
1382 i( _vortex_state_c2)
1383 i( _vortex_state_c3)
1384 i( _vortex_state_e1)
1385 i( _vortex_state_e2)
1386 i( _vortex_state_e3)
1387 i( _lift_factor) i(_f_ge) i(_f_vs) i(_f_tl)
1392 i( _twist) //outer incidence = inner inner incidence + _twist
1393 i( _rel_len_where_incidence_is_measured)
1394 i( _torque_of_inertia)
1395 i( _rel_len_blade_start)
1396 i( _incidence_stall_zero_speed)
1397 i( _incidence_stall_half_sonic_speed)
1398 i( _lift_factor_stall)
1399 i( _stall_change_over)
1400 i( _drag_factor_stall)
1407 i( _cyclic_factor) <<endl;
1409 for(j=0; j<r._rotorparts.size(); j++) {
1410 out << *((Rotorpart*)r._rotorparts.get(j));
1417 void Rotor:: writeInfo()
1420 std::ostringstream buffer;
1422 FILE*f=fopen("c:\\fgmsvc\\bat\\log.txt","at");
1423 if (!f) f=fopen("c:\\fgmsvc\\bat\\log.txt","wt");
1426 fprintf(f,"%s",(const char *)buffer.str().c_str());
1431 Rotorpart* Rotor::newRotorpart(float zentforce,float maxpitchforce,
1432 float delta3,float mass,float translift,float rellenhinge,float len)
1434 Rotorpart *r = new Rotorpart();
1435 r->setDelta3(delta3);
1436 r->setDynamic(_dynamic);
1437 r->setTranslift(_translift);
1440 r->setRelLenHinge(rellenhinge);
1441 r->setSharedFlapHinge(_shared_flap_hinge);
1442 r->setOmegaN(_omegan);
1443 r->setPhi(_phi_null);
1444 r->setAlpha0(_alpha0);
1445 r->setAlphamin(_alphamin);
1446 r->setAlphamax(_alphamax);
1447 r->setAlpha0factor(_alpha0factor);
1449 r->setDiameter(_diameter);
1451 #define p(a) r->setParameter(#a,_##a);
1453 p(number_of_segments)
1454 p(rel_len_where_incidence_is_measured)
1455 p(rel_len_blade_start)
1456 p(rotor_correction_factor)
1461 void Rotor::interp(float* v1, float* v2, float frac, float* out)
1463 out[0] = v1[0] + frac*(v2[0]-v1[0]);
1464 out[1] = v1[1] + frac*(v2[1]-v1[1]);
1465 out[2] = v1[2] + frac*(v2[2]-v1[2]);
1468 void Rotorgear::initRotorIteration(float *lrot,float dt)
1472 if (!_rotors.size()) return;
1473 Rotor* r0 = (Rotor*)_rotors.get(0);
1474 omegarel= r0->getOmegaRelNeu();
1475 for(i=0; i<_rotors.size(); i++) {
1476 Rotor* r = (Rotor*)_rotors.get(i);
1477 r->inititeration(dt,omegarel,0,lrot);
1481 void Rotorgear::calcForces(float* torqueOut)
1484 torqueOut[0]=torqueOut[1]=torqueOut[2]=0;
1485 // check,<if the engine can handle the torque of the rotors.
1486 // If not reduce the torque to the fueselage and change rotational
1487 // speed of the rotors instead
1490 float omegarel,omegan;
1491 Rotor* r0 = (Rotor*)_rotors.get(0);
1492 omegarel= r0->getOmegaRel();
1494 float total_torque_of_inertia=0;
1495 float total_torque=0;
1496 for(i=0; i<_rotors.size(); i++) {
1497 Rotor* r = (Rotor*)_rotors.get(i);
1498 omegan=r->getOmegan();
1499 total_torque_of_inertia+=r->getTorqueOfInertia()*omegan*omegan;
1500 //FIXME: this is constant, so this can be done in compile
1502 total_torque+=r->getTorque()*omegan;
1504 float max_torque_of_engine=0;
1505 SGPropertyNode * node=fgGetNode("/rotors/gear", true);
1506 float target_rel_rpm=(node==0?1:node->getDoubleValue("target-rel-rpm",1));
1509 float max_rel_torque=(node==0?1:node->getDoubleValue("max-rel-torque",1));
1510 max_torque_of_engine=_max_power_engine*max_rel_torque;
1511 float df=target_rel_rpm-omegarel;
1512 df/=_engine_prop_factor;
1513 df = Math::clamp(df, 0, 1);
1514 max_torque_of_engine = df * _max_power_engine*max_rel_torque;
1518 float rel_torque_engine=1;
1519 if (total_torque<=0)
1520 rel_torque_engine=0;
1522 if (max_torque_of_engine>0)
1523 rel_torque_engine=1/max_torque_of_engine*total_torque;
1525 rel_torque_engine=0;
1527 //add the rotor brake and the gear fritcion
1529 if (r0->_rotorparts.size()) dt=((Rotorpart*)r0->_rotorparts.get(0))->getDt();
1531 float rotor_brake_torque;
1532 rotor_brake_torque=_rotorbrake*_max_power_rotor_brake+_rotorgear_friction;
1533 //clamp it to the value you need to stop the rotor
1534 //to avod accelerate the rotor to neagtive rpm:
1535 rotor_brake_torque=Math::clamp(rotor_brake_torque,0,
1536 total_torque_of_inertia/dt*omegarel);
1537 max_torque_of_engine-=rotor_brake_torque;
1539 //change the rotation of the rotors
1540 if ((max_torque_of_engine<total_torque) //decreasing rotation
1541 ||((max_torque_of_engine>total_torque)&&(omegarel<target_rel_rpm))
1542 //increasing rotation due to engine
1543 ||(total_torque<0) ) //increasing rotation due to autorotation
1545 _ddt_omegarel=(max_torque_of_engine-total_torque)/total_torque_of_inertia;
1546 if(max_torque_of_engine>total_torque)
1548 //check if the acceleration is due to the engine. If yes,
1549 //the engine self limits the accel.
1550 float lim1=-total_torque/total_torque_of_inertia;
1551 //accel. by autorotation
1553 if (lim1<_engine_accel_limit) lim1=_engine_accel_limit;
1554 //if the accel by autorotation greater than the max. engine
1555 //accel, then this is the limit, if not: the engine is the limit
1556 if (_ddt_omegarel>lim1) _ddt_omegarel=lim1;
1558 if (_ddt_omegarel>5.5)_ddt_omegarel=5.5;
1559 //clamp it to avoid overflow. Should never be reached
1560 if (_ddt_omegarel<-5.5)_ddt_omegarel=-5.5;
1562 if (_max_power_engine<0.001) {omegarel=1;_ddt_omegarel=0;}
1563 //for debug: negative or no maxpower will result
1564 //in permanet 100% rotation
1566 omegarel+=dt*_ddt_omegarel;
1568 if (omegarel>2.5) omegarel=2.5;
1569 //clamp it to avoid overflow. Should never be reached
1570 if (omegarel<-.5) omegarel=-.5;
1572 r0->setOmegaRelNeu(omegarel);
1573 //calculate the torque, which is needed to accelerate the rotors.
1574 //Add this additional torque to the body
1575 for(j=0; j<_rotors.size(); j++) {
1576 Rotor* r = (Rotor*)_rotors.get(j);
1577 for(i=0; i<r->_rotorparts.size(); i++) {
1578 float torque_scalar=0;
1579 Rotorpart* rp = (Rotorpart*)r->_rotorparts.get(i);
1581 rp->getAccelTorque(_ddt_omegarel,torque);
1582 Math::add3(torque,torqueOut,torqueOut);
1586 _total_torque_on_engine=total_torque+_ddt_omegarel*total_torque_of_inertia;
1590 void Rotorgear::addRotor(Rotor* rotor)
1596 void Rotorgear::compile()
1599 for(int j=0; j<_rotors.size(); j++) {
1600 Rotor* r = (Rotor*)_rotors.get(j);
1603 SGPropertyNode * node = fgGetNode("/rotors/gear/target-rel-rpm", true);
1604 if (node) node->setDoubleValue(1);
1605 node =fgGetNode("/rotors/gear/max-rel-torque", true);
1606 if (node) node->setDoubleValue(1);
1609 void Rotorgear::getDownWash(float *pos, float * v_heli, float *downwash)
1612 downwash[0]=downwash[1]=downwash[2]=0;
1613 for(int i=0; i<_rotors.size(); i++) {
1614 Rotor* ro = (Rotor*)_rotors.get(i);
1615 ro->getDownWash(pos,v_heli,tmp);
1616 Math::add3(downwash,tmp,downwash); // + downwash
1620 void Rotorgear::setParameter(char *parametername, float value)
1622 #define p(a,b) if (strcmp(parametername,#a)==0) _##a = (value * (b)); else
1623 p(max_power_engine,1000)
1624 p(engine_prop_factor,1)
1625 p(yasimdragfactor,1)
1626 p(yasimliftfactor,1)
1627 p(max_power_rotor_brake,1000)
1628 p(rotorgear_friction,1000)
1629 p(engine_accel_limit,0.01)
1630 SG_LOG(SG_INPUT, SG_ALERT,
1631 "internal error in parameter set up for rotorgear: '"
1632 << parametername <<"'" << endl);
1635 int Rotorgear::getValueforFGSet(int j,char *text,float *f)
1639 sprintf(text,"/rotors/gear/total-torque");
1640 *f=_total_torque_on_engine;
1644 Rotorgear::Rotorgear()
1649 _max_power_rotor_brake=1;
1650 _rotorgear_friction=1;
1651 _max_power_engine=1000*450;
1652 _engine_prop_factor=0.05f;
1656 _engine_accel_limit=0.05f;
1657 _total_torque_on_engine=0;
1660 Rotorgear::~Rotorgear()
1662 for(int i=0; i<_rotors.size(); i++)
1663 delete (Rotor*)_rotors.get(i);
1666 }; // namespace yasim