1 #include <simgear/debug/logstream.hxx>
4 #include <Main/fg_props.hxx>
6 #include "Rotorpart.hpp"
21 using std::setprecision;
26 const float pi=3.14159;
28 static inline float sqr(float a) { return a * a; }
45 _base[0] = _base[1] = _base[2] = 0;
54 _forward[1]=_forward[2]=0;
55 _max_pitch=13./180*pi;
56 _maxcyclicail=10./180*pi;
57 _maxcyclicele=10./180*pi;
59 _mincyclicail=-10./180*pi;
60 _mincyclicele=-10./180*pi;
61 _min_pitch=-.5/180*pi;
65 _normal[0] = _normal[1] = 0;
67 _normal_with_yaw_roll[0]= _normal_with_yaw_roll[1]=0;
68 _normal_with_yaw_roll[2]=1;
70 _omega=_omegan=_omegarel=_omegarelneu=0;
80 _shared_flap_hinge=false;
81 _rellenteeterhinge=0.01;
88 _translift_maxfactor=1.3;
89 _ground_effect_constant=0.1;
90 _vortex_state_lift_factor=0.4;
103 _number_of_segments=1;
105 _rel_len_where_incidence_is_measured=0.7;
106 _torque_of_inertia=1;
110 _airfoil_incidence_no_lift=0;
111 _rel_len_blade_start=0;
112 _airfoil_lift_coefficient=0;
113 _airfoil_drag_coefficient0=0;
114 _airfoil_drag_coefficient1=0;
116 _global_ground[i] = _tilt_center[i] = 0;
117 _global_ground[2] = 1;
118 _global_ground[3] = -1e3;
119 _incidence_stall_zero_speed=18*pi/180.;
120 _incidence_stall_half_sonic_speed=14*pi/180.;
121 _lift_factor_stall=0.28;
122 _stall_change_over=2*pi/180.;
123 _drag_factor_stall=8;
128 for (int k=0;k<4;k++)
130 _groundeffectpos[k][i]=0;
131 _ground_effect_altitude=1;
133 _lift_factor=_f_ge=_f_vs=_f_tl=1;
134 _rotor_correction_factor=.65;
138 _num_ground_contact_pos=0;
139 _directions_and_postions_dirty=true;
158 for(i=0; i<_rotorparts.size(); i++) {
159 Rotorpart* r = (Rotorpart*)_rotorparts.get(i);
162 //untie the properties
165 SGPropertyNode * node = fgGetNode("/rotors", true)->getNode(_name,true);
166 node->untie("balance-ext");
167 node->untie("balance-int");
172 void Rotor::inititeration(float dt,float omegarel,float ddt_omegarel,float *rot)
177 _omega=_omegan*_omegarel;
178 _ddt_omega=_omegan*ddt_omegarel;
181 updateDirectionsAndPositions(drot);
182 Math::add3(rot,drot,drot);
183 for(i=0; i<_rotorparts.size(); i++) {
184 float s = Math::sin(float(2*pi*i/_number_of_parts+(_phi-pi/2.)*(_ccw?1:-1)));
185 float c = Math::cos(float(2*pi*i/_number_of_parts+(_phi-pi/2.)*(_ccw?1:-1)));
186 Rotorpart* r = (Rotorpart*)_rotorparts.get(i);
188 r->setDdtOmega(_ddt_omega);
189 r->inititeration(dt,drot);
190 r->setCyclic(_cyclicail*c+_cyclicele*s);
193 //calculate the normal of the rotor disc, for calcualtion of the downwash
194 float side[3],help[3];
195 Math::cross3(_normal,_forward,side);
196 Math::mul3(Math::cos(_yaw)*Math::cos(_roll),_normal,_normal_with_yaw_roll);
198 Math::mul3(Math::sin(_yaw),_forward,help);
199 Math::add3(_normal_with_yaw_roll,help,_normal_with_yaw_roll);
201 Math::mul3(Math::sin(_roll),side,help);
202 Math::add3(_normal_with_yaw_roll,help,_normal_with_yaw_roll);
205 if ((_balance1*_balance2 < 0.97) && (_balance1>-1))
207 _balance1-=(0.97-_balance1*_balance2)*(0.97-_balance1*_balance2)*0.005;
208 if (_balance1<-1) _balance1=-1;
212 float Rotor::calcStall(float incidence,float speed)
214 float stall_incidence=_incidence_stall_zero_speed
215 +(_incidence_stall_half_sonic_speed
216 -_incidence_stall_zero_speed)*speed/(343./2);
217 //missing: Temeperature dependency of sonic speed
218 incidence = Math::abs(incidence);
219 if (incidence > (90./180.*pi))
220 incidence = pi-incidence;
221 float stall = (incidence-stall_incidence)/_stall_change_over;
222 stall = Math::clamp(stall,0,1);
224 _stall_sum+=stall*speed*speed;
225 _stall_v2sum+=speed*speed;
230 float Rotor::getLiftCoef(float incidence,float speed)
232 float stall=calcStall(incidence,speed);
233 /* the next shold look like this, but this is the inner loop of
234 the rotor simulation. For small angles (and we hav only small
235 angles) the first order approximation works well
236 float c1= Math::sin(incidence-_airfoil_incidence_no_lift)*_liftcoef;
237 for c2 we would need higher order, because in stall the angle can be large
240 if (incidence > (pi/2))
242 else if (incidence <-(pi/2))
246 float c1= (i2-_airfoil_incidence_no_lift)*_liftcoef;
249 float c2= Math::sin(2*(incidence-_airfoil_incidence_no_lift))
250 *_liftcoef*_lift_factor_stall;
251 return (1-stall)*c1 + stall *c2;
257 float Rotor::getDragCoef(float incidence,float speed)
259 float stall=calcStall(incidence,speed);
260 float c1= (Math::abs(Math::sin(incidence-_airfoil_incidence_no_lift))
261 *_dragcoef1+_dragcoef0);
262 float c2= c1*_drag_factor_stall;
263 return (1-stall)*c1 + stall *c2;
266 int Rotor::getValueforFGSet(int j,char *text,float *f)
268 if (_name[0]==0) return 0;
269 if (4>numRotorparts()) return 0; //compile first!
272 sprintf(text,"/rotors/%s/cone-deg", _name);
273 *f=(_balance1>-1)?( ((Rotorpart*)getRotorpart(0))->getrealAlpha()
274 +((Rotorpart*)getRotorpart(1*(_number_of_parts>>2)))->getrealAlpha()
275 +((Rotorpart*)getRotorpart(2*(_number_of_parts>>2)))->getrealAlpha()
276 +((Rotorpart*)getRotorpart(3*(_number_of_parts>>2)))->getrealAlpha()
282 sprintf(text,"/rotors/%s/roll-deg", _name);
283 _roll = ( ((Rotorpart*)getRotorpart(0))->getrealAlpha()
284 -((Rotorpart*)getRotorpart(2*(_number_of_parts>>2)))->getrealAlpha()
286 *f=(_balance1>-1)?_roll *180/pi:0;
291 sprintf(text,"/rotors/%s/yaw-deg", _name);
292 _yaw=( ((Rotorpart*)getRotorpart(1*(_number_of_parts>>2)))->getrealAlpha()
293 -((Rotorpart*)getRotorpart(3*(_number_of_parts>>2)))->getrealAlpha()
295 *f=(_balance1>-1)?_yaw*180/pi:0;
300 sprintf(text,"/rotors/%s/rpm", _name);
301 *f=(_balance1>-1)?_omega/2/pi*60:0;
306 sprintf(text,"/rotors/%s/tilt/pitch-deg",_name);
307 *f=_tilt_pitch*180/pi;
311 sprintf(text,"/rotors/%s/tilt/roll-deg",_name);
312 *f=_tilt_roll*180/pi;
316 sprintf(text,"/rotors/%s/tilt/yaw-deg",_name);
321 sprintf(text,"/rotors/%s/balance", _name);
326 sprintf(text,"/rotors/%s/stall",_name);
327 *f=getOverallStall();
331 sprintf(text,"/rotors/%s/torque",_name);
337 if (b>=_number_of_blades)
342 sprintf(text,"/rotors/%s/blade[%i]/%s",
344 w==0?"position-deg":(w==1?"flap-deg":"incidence-deg"));
345 *f=((Rotorpart*)getRotorpart(0))->getPhi()*180/pi
346 +360*b/_number_of_blades*(_ccw?1:-1);
349 if (_balance1<=-1) *f=0;
356 rk=Math::clamp(rk,0,1);//Delete this
358 if(w==2) {k+=2;l+=2;}
360 if(w==1) {k+=1;l+=1;}
363 if (w==1) *f=rk*((Rotorpart*) getRotorpart(k*(_number_of_parts>>2)))->getrealAlpha()*180/pi
364 +rl*((Rotorpart*) getRotorpart(l*(_number_of_parts>>2)))->getrealAlpha()*180/pi;
365 else if(w==2) *f=rk*((Rotorpart*)getRotorpart(k*(_number_of_parts>>2)))->getIncidence()*180/pi
366 +rl*((Rotorpart*)getRotorpart(l*(_number_of_parts>>2)))->getIncidence()*180/pi;
371 void Rotorgear::setEngineOn(int value)
376 void Rotorgear::setRotorEngineMaxRelTorque(float lval)
378 _max_rel_torque=lval;
381 void Rotorgear::setRotorRelTarget(float lval)
383 _target_rel_rpm=lval;
386 void Rotor::setAlpha0(float f)
391 void Rotor::setAlphamin(float f)
396 void Rotor::setAlphamax(float f)
401 void Rotor::setAlpha0factor(float f)
406 int Rotor::numRotorparts()
408 return _rotorparts.size();
411 Rotorpart* Rotor::getRotorpart(int n)
413 return ((Rotorpart*)_rotorparts.get(n));
416 int Rotorgear::getEngineon()
421 float Rotor::getTorqueOfInertia()
423 return _torque_of_inertia;
426 void Rotor::setTorque(float f)
431 void Rotor::addTorque(float f)
436 void Rotor::strncpy(char *dest,const char *src,int maxlen)
439 while(src[n]&&n<(maxlen-1))
447 void Rotor::setNormal(float* normal)
450 float invsum,sqrsum=0;
451 for(i=0; i<3; i++) { sqrsum+=normal[i]*normal[i];}
453 invsum=1/Math::sqrt(sqrsum);
458 _normal_with_yaw_roll[i]=_normal[i] = normal[i]*invsum;
462 void Rotor::setForward(float* forward)
465 float invsum,sqrsum=0;
466 for(i=0; i<3; i++) { sqrsum+=forward[i]*forward[i];}
468 invsum=1/Math::sqrt(sqrsum);
471 for(i=0; i<3; i++) { _forward[i] = forward[i]*invsum; }
474 void Rotor::setForceAtPitchA(float force)
476 _force_at_pitch_a=force;
479 void Rotor::setPowerAtPitch0(float value)
481 _power_at_pitch_0=value;
484 void Rotor::setPowerAtPitchB(float value)
486 _power_at_pitch_b=value;
489 void Rotor::setPitchA(float value)
491 _pitch_a=value/180*pi;
494 void Rotor::setPitchB(float value)
496 _pitch_b=value/180*pi;
499 void Rotor::setBase(float* base)
502 for(i=0; i<3; i++) _base[i] = base[i];
505 void Rotor::setMaxCyclicail(float value)
507 _maxcyclicail=value/180*pi;
510 void Rotor::setMaxCyclicele(float value)
512 _maxcyclicele=value/180*pi;
515 void Rotor::setMinCyclicail(float value)
517 _mincyclicail=value/180*pi;
520 void Rotor::setMinCyclicele(float value)
522 _mincyclicele=value/180*pi;
525 void Rotor::setMinCollective(float value)
527 _min_pitch=value/180*pi;
530 void Rotor::setMinTiltYaw(float value)
532 _min_tilt_yaw=value/180*pi;
535 void Rotor::setMinTiltPitch(float value)
537 _min_tilt_pitch=value/180*pi;
540 void Rotor::setMinTiltRoll(float value)
542 _min_tilt_roll=value/180*pi;
545 void Rotor::setMaxTiltYaw(float value)
547 _max_tilt_yaw=value/180*pi;
550 void Rotor::setMaxTiltPitch(float value)
552 _max_tilt_pitch=value/180*pi;
555 void Rotor::setMaxTiltRoll(float value)
557 _max_tilt_roll=value/180*pi;
560 void Rotor::setTiltCenterX(float value)
562 _tilt_center[0] = value;
565 void Rotor::setTiltCenterY(float value)
567 _tilt_center[1] = value;
570 void Rotor::setTiltCenterZ(float value)
572 _tilt_center[2] = value;
575 void Rotor::setMaxCollective(float value)
577 _max_pitch=value/180*pi;
580 void Rotor::setDiameter(float value)
585 void Rotor::setWeightPerBlade(float value)
587 _weight_per_blade=value;
590 void Rotor::setNumberOfBlades(float value)
592 _number_of_blades=int(value+.5);
595 void Rotor::setRelBladeCenter(float value)
597 _rel_blade_center=value;
600 void Rotor::setDynamic(float value)
605 void Rotor::setDelta3(float value)
610 void Rotor::setDelta(float value)
615 void Rotor::setTranslift(float value)
620 void Rotor::setSharedFlapHinge(bool s)
622 _shared_flap_hinge=s;
625 void Rotor::setBalance(float b)
630 void Rotor::setC2(float value)
635 void Rotor::setStepspersecond(float steps)
637 _stepspersecond=steps;
640 void Rotor::setRPM(float value)
645 void Rotor::setPhiNull(float value)
650 void Rotor::setRelLenHinge(float value)
652 _rel_len_hinge=value;
655 void Rotor::setDownwashFactor(float value)
657 _downwash_factor=value;
660 void Rotor::setAlphaoutput(int i, const char *text)
662 strncpy(_alphaoutput[i],text,255);
665 void Rotor::setName(const char *text)
667 strncpy(_name,text,256);//256: some space needed for settings
670 void Rotor::setCcw(int ccw)
675 void Rotor::setNotorque(int value)
680 void Rotor::setRelLenTeeterHinge(float f)
682 _rellenteeterhinge=f;
685 void Rotor::setTeeterdamp(float f)
690 void Rotor::setMaxteeterdamp(float f)
695 void Rotor::setGlobalGround(double *global_ground, float* global_vel)
698 for(i=0; i<4; i++) _global_ground[i] = global_ground[i];
701 void Rotor::setParameter(const char *parametername, float value)
703 #define p(a,b) if (strcmp(parametername,#a)==0) _##a = (value * (b)); else
705 p(translift_maxfactor,1)
706 p(ground_effect_constant,1)
707 p(vortex_state_lift_factor,1)
715 p(number_of_segments,1)
717 p(rel_len_where_incidence_is_measured,1)
720 p(airfoil_incidence_no_lift,pi/180.)
721 p(rel_len_blade_start,1)
722 p(incidence_stall_zero_speed,pi/180.)
723 p(incidence_stall_half_sonic_speed,pi/180.)
724 p(lift_factor_stall,1)
725 p(stall_change_over,pi/180.)
726 p(drag_factor_stall,1)
727 p(airfoil_lift_coefficient,1)
728 p(airfoil_drag_coefficient0,1)
729 p(airfoil_drag_coefficient1,1)
731 p(rotor_correction_factor,1)
732 SG_LOG(SG_INPUT, SG_ALERT,
733 "internal error in parameter set up for rotor: '" <<
734 parametername <<"'" << std::endl);
738 float Rotor::getLiftFactor()
743 void Rotorgear::setRotorBrake(float lval)
745 lval = Math::clamp(lval, 0, 1);
749 void Rotor::setTiltYaw(float lval)
751 lval = Math::clamp(lval, -1, 1);
752 _tilt_yaw = _min_tilt_yaw+(lval+1)/2*(_max_tilt_yaw-_min_tilt_yaw);
753 _directions_and_postions_dirty = true;
756 void Rotor::setTiltPitch(float lval)
758 lval = Math::clamp(lval, -1, 1);
759 _tilt_pitch = _min_tilt_pitch+(lval+1)/2*(_max_tilt_pitch-_min_tilt_pitch);
760 _directions_and_postions_dirty = true;
763 void Rotor::setTiltRoll(float lval)
765 lval = Math::clamp(lval, -1, 1);
766 _tilt_roll = _min_tilt_roll+(lval+1)/2*(_max_tilt_roll-_min_tilt_roll);
767 _directions_and_postions_dirty = true;
770 void Rotor::setCollective(float lval)
772 lval = Math::clamp(lval, -1, 1);
774 _collective=_min_pitch+(lval+1)/2*(_max_pitch-_min_pitch);
775 for(i=0; i<_rotorparts.size(); i++) {
776 ((Rotorpart*)_rotorparts.get(i))->setCollective(_collective);
780 void Rotor::setCyclicele(float lval,float rval)
782 lval = Math::clamp(lval, -1, 1);
783 _cyclicele=_mincyclicele+(lval+1)/2*(_maxcyclicele-_mincyclicele);
786 void Rotor::setCyclicail(float lval,float rval)
788 lval = Math::clamp(lval, -1, 1);
790 _cyclicail=-(_mincyclicail+(lval+1)/2*(_maxcyclicail-_mincyclicail));
793 void Rotor::setRotorBalance(float lval)
795 lval = Math::clamp(lval, -1, 1);
799 void Rotor::getPosition(float* out)
802 for(i=0; i<3; i++) out[i] = _base[i];
805 void Rotor::calcLiftFactor(float* v, float rho, State *s)
807 //calculates _lift_factor, which is a foactor for the lift of the rotor
809 //- ground effect (_f_ge)
810 //- vortex state (_f_vs)
811 //- translational lift (_f_tl)
816 // Calculate ground effect
817 _f_ge=1+_diameter/_ground_effect_altitude*_ground_effect_constant;
819 // Now calculate translational lift
820 // float v_vert = Math::dot3(v,_normal);
822 Math::cross3(v,_normal,help);
823 float v_horiz = Math::mag3(help);
824 _f_tl = ((1-Math::pow(2.7183,-v_horiz/_translift_ve))
825 *(_translift_maxfactor-1)+1)/_translift_maxfactor;
827 _lift_factor = _f_ge*_f_tl*_f_vs;
829 //store the gravity direction
830 Glue::geodUp(s->pos, _grav_direction);
831 s->velGlobalToLocal(_grav_direction, _grav_direction);
834 void Rotor::findGroundEffectAltitude(Ground * ground_cb,State *s)
836 _ground_effect_altitude=findGroundEffectAltitude(ground_cb,s,
837 _groundeffectpos[0],_groundeffectpos[1],
838 _groundeffectpos[2],_groundeffectpos[3]);
839 testForRotorGroundContact(ground_cb,s);
842 void Rotor::testForRotorGroundContact(Ground * ground_cb,State *s)
845 for (i=0;i<_num_ground_contact_pos;i++)
848 s->posLocalToGlobal(_ground_contact_pos[i], pt);
850 // Ask for the ground plane in the global coordinate system
851 double global_ground[4];
853 ground_cb->getGroundPlane(pt, global_ground, global_vel);
854 // find h, the distance to the ground
855 // The ground plane transformed to the local frame.
857 s->planeGlobalToLocal(global_ground, ground);
859 h = ground[3] - Math::dot3(_ground_contact_pos[i], ground);
860 // Now h is the distance from _ground_contact_pos[i] to ground
863 _balance1 -= (-h)/_diameter/_num_ground_contact_pos;
864 _balance1 = (_balance1<-1)?-1:_balance1;
868 float Rotor::findGroundEffectAltitude(Ground * ground_cb,State *s,
869 float *pos0,float *pos1,float *pos2,float *pos3,
870 int iteration,float a0,float a1,float a2,float a3)
884 Math::add3(p[0],p[2],p[4]);
885 Math::mul3(0.5,p[4],p[4]);//the center
887 float mina=100*_diameter;
889 for (int i=0;i<5;i++)
891 if (a[i]==-1)//in the first iteration,(iteration==0) no height is
892 //passed to this function, these missing values are
896 s->posLocalToGlobal(p[i], pt);
898 // Ask for the ground plane in the global coordinate system
899 double global_ground[4];
901 ground_cb->getGroundPlane(pt, global_ground, global_vel);
902 // find h, the distance to the ground
903 // The ground plane transformed to the local frame.
905 s->planeGlobalToLocal(global_ground, ground);
907 a[i] = ground[3] - Math::dot3(p[i], ground);
908 // Now a[i] is the distance from p[i] to ground
914 if (mina>=10*_diameter)
915 return mina; //the ground effect will be zero
917 //check if further recursion is neccessary
918 //if the height does not differ more than 20%, than
919 //we can return then mean height, if not split
920 //zhe square to four parts and calcualte the height
922 //suma * 0.2 is the mean
923 //0.15 is the maximum allowed difference from the mean
924 //to the height at the center
926 ||(Math::abs(suma*0.2-a[4])<(0.15*0.2*suma*(1<<iteration))))
929 float pc[4][3],ac[4]; //pc[i]=center of pos[i] and pos[(i+1)&3]
930 for (int i=0;i<4;i++)
932 Math::add3(p[i],p[(i+1)&3],pc[i]);
933 Math::mul3(0.5,pc[i],pc[i]);
935 s->posLocalToGlobal(pc[i], pt);
937 // Ask for the ground plane in the global coordinate system
938 double global_ground[4];
940 ground_cb->getGroundPlane(pt, global_ground, global_vel);
941 // find h, the distance to the ground
942 // The ground plane transformed to the local frame.
944 s->planeGlobalToLocal(global_ground, ground);
946 ac[i] = ground[3] - Math::dot3(p[i], ground);
947 // Now ac[i] is the distance from pc[i] to ground
950 (findGroundEffectAltitude(ground_cb,s,p[0],pc[1],p[4],pc[3],
951 iteration+1,a[0],ac[0],a[4],ac[3])
952 +findGroundEffectAltitude(ground_cb,s,p[1],pc[0],p[4],pc[1],
953 iteration+1,a[1],ac[0],a[4],ac[1])
954 +findGroundEffectAltitude(ground_cb,s,p[2],pc[1],p[4],pc[2],
955 iteration+1,a[2],ac[1],a[4],ac[2])
956 +findGroundEffectAltitude(ground_cb,s,p[3],pc[2],p[4],pc[3],
957 iteration+1,a[3],ac[2],a[4],ac[3])
961 void Rotor::getDownWash(float *pos, float *v_heli, float *downwash)
963 float pos2rotor[3],tmp[3];
964 Math::sub3(_base,pos,pos2rotor);
965 float dist=Math::dot3(pos2rotor,_normal_with_yaw_roll);
966 //calculate incidence at 0.7r;
967 float inc = _collective+_twist *0.7
968 - _twist*_rel_len_where_incidence_is_measured;
971 if (dist<0) // we are not in the downwash region
973 downwash[0]=downwash[1]=downwash[2]=0.;
977 //calculate the mean downwash speed directly beneath the rotor disk
978 float v1bar = Math::sin(inc) *_omega * 0.35 * _diameter * 0.8;
979 //0.35 * d = 0.7 *r, a good position to calcualte the mean downwashd
980 //0.8 the slip of the rotor.
982 //calculate the time the wind needed from thr rotor to here
983 if (v1bar< 1) v1bar = 1;
984 float time=dist/v1bar;
986 //calculate the pos2rotor, where the rotor was, "time" ago
987 Math::mul3(time,v_heli,tmp);
988 Math::sub3(pos2rotor,tmp,pos2rotor);
990 //and again calculate dist
991 dist=Math::dot3(pos2rotor,_normal_with_yaw_roll);
992 //missing the normal is offen not pointing to the normal of the rotor
993 //disk. Rotate the normal by yaw and tilt angle
997 if (dist<0) // we are not in the downwash region
999 downwash[0]=downwash[1]=downwash[2]=0.;
1002 //of course this could be done in a runge kutta integrator, but it's such
1003 //a approximation that I beleave, it would'nt be more realistic
1005 //calculate the dist to the rotor-axis
1006 Math::cross3(pos2rotor,_normal_with_yaw_roll,tmp);
1007 float r= Math::mag3(tmp);
1008 //calculate incidence at r;
1009 float rel_r = r *2 /_diameter;
1010 float inc_r = _collective+_twist * r /_diameter * 2
1011 - _twist*_rel_len_where_incidence_is_measured;
1013 //calculate the downwash speed directly beneath the rotor disk
1016 v1 = Math::sin(inc_r) *_omega * r * 0.8;
1018 //calcualte the downwash speed in a distance "dist" to the rotor disc,
1019 //for large dist. The speed is assumed do follow a gausian distribution
1020 //with sigma increasing with dist^2:
1021 //sigma is assumed to be half of the rotor diameter directly beneath the
1022 //disc and is assumed to the rotor diameter at dist = (diameter * sqrt(2))
1024 float sigma=_diameter/2 + dist * dist / _diameter /4.;
1025 float v2 = v1bar*_diameter/ (Math::sqrt(2 * pi) * sigma)
1026 * Math::pow(2.7183,-.5*r*r/(sigma*sigma))*_diameter/2/sigma;
1028 //calculate the weight of the two downwash velocities.
1029 //Directly beneath the disc it is v1, far away it is v2
1030 float g = Math::pow(2.7183,-2*dist/_diameter);
1031 //at dist = rotor radius it is assumed to be 1/e * v1 + (1-1/e)* v2
1033 float v = g * v1 + (1-g) * v2;
1034 Math::mul3(-v*_downwash_factor,_normal_with_yaw_roll,downwash);
1035 //the downwash is calculated in the opposite direction of the normal
1038 void Rotor::euler2orient(float roll, float pitch, float hdg, float* out)
1040 // the Glue::euler2orient, inverts y<z due to different bases
1041 // therefore the negation of all "y" and "z" coeffizients
1042 Glue::euler2orient(roll,pitch,hdg,out);
1043 for (int i=3;i<9;i++) out[i]*=-1.0;
1047 void Rotor::updateDirectionsAndPositions(float *rot)
1049 if (!_directions_and_postions_dirty)
1051 rot[0]=rot[1]=rot[2]=0;
1054 rot[0]=_old_tilt_roll-_tilt_roll;
1055 rot[1]=_old_tilt_pitch-_tilt_pitch;
1056 rot[2]=_old_tilt_yaw-_tilt_yaw;
1057 _old_tilt_roll=_tilt_roll;
1058 _old_tilt_pitch=_tilt_pitch;
1059 _old_tilt_yaw=_tilt_yaw;
1061 euler2orient(_tilt_roll, _tilt_pitch, _tilt_yaw, orient);
1065 Math::sub3(_base,_tilt_center,base);
1066 Math::vmul33(orient, base, base);
1067 Math::add3(base,_tilt_center,base);
1068 Math::vmul33(orient, _forward, forward);
1069 Math::vmul33(orient, _normal, normal);
1071 #define _forward forward
1072 #define _normal normal
1073 float directions[5][3];
1074 //pointing forward, right, ... the 5th is ony for calculation
1075 directions[0][0]=_forward[0];
1076 directions[0][1]=_forward[1];
1077 directions[0][2]=_forward[2];
1082 Math::cross3(directions[i-1],_normal,directions[i]);
1084 Math::cross3(_normal,directions[i-1],directions[i]);
1086 Math::set3(directions[4],directions[0]);
1087 // now directions[0] is perpendicular to the _normal.and has a length
1088 // of 1. if _forward is already normalized and perpendicular to the
1089 // normal, directions[0] will be the same
1090 //_num_ground_contact_pos=(_number_of_parts<16)?_number_of_parts:16;
1091 for (i=0;i<_num_ground_contact_pos;i++)
1094 float s = Math::sin(pi*2*i/_num_ground_contact_pos);
1095 float c = Math::cos(pi*2*i/_num_ground_contact_pos);
1096 Math::mul3(c*_diameter*0.5,directions[0],_ground_contact_pos[i]);
1097 Math::mul3(s*_diameter*0.5,directions[1],help);
1098 Math::add3(help,_ground_contact_pos[i],_ground_contact_pos[i]);
1099 Math::add3(_base,_ground_contact_pos[i],_ground_contact_pos[i]);
1103 Math::mul3(_diameter*0.7,directions[i],_groundeffectpos[i]);
1104 Math::add3(_base,_groundeffectpos[i],_groundeffectpos[i]);
1106 for (i=0;i<_number_of_parts;i++)
1108 Rotorpart* rp = getRotorpart(i);
1109 float lpos[3],lforceattac[3],lspeed[3],dirzentforce[3];
1110 float s = Math::sin(2*pi*i/_number_of_parts);
1111 float c = Math::cos(2*pi*i/_number_of_parts);
1112 float sp = Math::sin(float(2*pi*i/_number_of_parts-pi/2.+_phi));
1113 float cp = Math::cos(float(2*pi*i/_number_of_parts-pi/2.+_phi));
1114 float direction[3],nextdirection[3],help[3],direction90deg[3];
1115 float rotorpartmass = _weight_per_blade*_number_of_blades/_number_of_parts*.453;
1116 float speed=_rotor_rpm/60*_diameter*_rel_blade_center*pi;
1117 float lentocenter=_diameter*_rel_blade_center*0.5;
1118 float lentoforceattac=_diameter*_rel_len_hinge*0.5;
1119 float zentforce=rotorpartmass*speed*speed/lentocenter;
1121 Math::mul3(c ,directions[0],help);
1122 Math::mul3(s ,directions[1],direction);
1123 Math::add3(help,direction,direction);
1125 Math::mul3(c ,directions[1],help);
1126 Math::mul3(s ,directions[2],direction90deg);
1127 Math::add3(help,direction90deg,direction90deg);
1129 Math::mul3(cp ,directions[1],help);
1130 Math::mul3(sp ,directions[2],nextdirection);
1131 Math::add3(help,nextdirection,nextdirection);
1133 Math::mul3(lentocenter,direction,lpos);
1134 Math::add3(lpos,_base,lpos);
1135 Math::mul3(lentoforceattac,nextdirection,lforceattac);
1136 //nextdirection: +90deg (gyro)!!!
1138 Math::add3(lforceattac,_base,lforceattac);
1139 Math::mul3(speed,direction90deg,lspeed);
1140 Math::mul3(1,nextdirection,dirzentforce);
1141 rp->setPosition(lpos);
1142 rp->setNormal(_normal);
1143 rp->setZentipetalForce(zentforce);
1144 rp->setPositionForceAttac(lforceattac);
1145 rp->setSpeed(lspeed);
1146 rp->setDirectionofZentipetalforce(dirzentforce);
1147 rp->setDirectionofRotorPart(direction);
1152 _directions_and_postions_dirty=false;
1155 void Rotor::compile()
1157 // Have we already been compiled?
1158 if(_rotorparts.size() != 0) return;
1160 //rotor is divided into _number_of_parts parts
1161 //each part is calcualted at _number_of_segments points
1164 //and make it a factor of 4
1165 _number_of_parts=(int(Math::clamp(_number_of_parts,4,256))>>2)<<2;
1167 _dynamic=_dynamic*(1/ //inverse of the time
1168 ( (60/_rotor_rpm)/4 //for rotating 90 deg
1169 +(60/_rotor_rpm)/(2*_number_of_blades) //+ meantime a rotorblade
1170 //will pass a given point
1172 //normalize the directions
1173 Math::unit3(_forward,_forward);
1174 Math::unit3(_normal,_normal);
1175 _num_ground_contact_pos=(_number_of_parts<16)?_number_of_parts:16;
1176 float rotorpartmass = _weight_per_blade*_number_of_blades/_number_of_parts*.453;
1177 //was pounds -> now kg
1179 _torque_of_inertia = 1/12. * ( _number_of_parts * rotorpartmass) * _diameter
1180 * _diameter * _rel_blade_center * _rel_blade_center /(0.5*0.5);
1181 float speed=_rotor_rpm/60*_diameter*_rel_blade_center*pi;
1182 float lentocenter=_diameter*_rel_blade_center*0.5;
1183 // float lentoforceattac=_diameter*_rel_len_hinge*0.5;
1184 float zentforce=rotorpartmass*speed*speed/lentocenter;
1185 float pitchaforce=_force_at_pitch_a/_number_of_parts*.453*9.81;
1186 // was pounds of force, now N, devided by _number_of_parts
1187 //(so its now per rotorpart)
1189 float torque0=0,torquemax=0,torqueb=0;
1190 float omega=_rotor_rpm/60*2*pi;
1192 float omega0=omega*Math::sqrt(1/(1-_rel_len_hinge));
1193 float delta_theoretical=pitchaforce/(_pitch_a*omega*lentocenter*2*rotorpartmass);
1194 _delta*=delta_theoretical;
1196 float relamp=(omega*omega/(2*_delta*Math::sqrt(sqr(omega0*omega0-omega*omega)
1197 +4*_delta*_delta*omega*omega)))*_cyclic_factor;
1198 //float relamp_theoretical=(omega*omega/(2*delta_theoretical*Math::sqrt(sqr(omega0*omega0-omega*omega)
1199 // +4*delta_theoretical*delta_theoretical*omega*omega)))*_cyclic_factor;
1200 _phi=Math::acos(_rel_len_hinge);
1201 _phi-=Math::atan(_delta3);
1204 torque0=_power_at_pitch_0/_number_of_parts*1000/omega;
1205 // f*r=p/w ; p=f*s/t; r=s/t/w ; r*w*t = s
1206 torqueb=_power_at_pitch_b/_number_of_parts*1000/omega;
1207 torquemax=_power_at_pitch_b/_number_of_parts*1000/omega/_pitch_b*_max_pitch;
1217 Rotorpart* rps[256];
1219 for (i=0;i<_number_of_parts;i++)
1221 Rotorpart* rp=rps[i]=newRotorpart(zentforce,pitchaforce,_delta3,rotorpartmass,
1222 _translift,_rel_len_hinge,lentocenter);
1223 int k = i*4/_number_of_parts;
1224 rp->setAlphaoutput(_alphaoutput[k&1?k:(_ccw?k^2:k)],0);
1225 rp->setAlphaoutput(_alphaoutput[4+(k&1?k:(_ccw?k^2:k))],1+(k>1));
1226 _rotorparts.add(rp);
1227 rp->setTorque(torquemax,torque0);
1228 rp->setRelamp(relamp);
1229 rp->setTorqueOfInertia(_torque_of_inertia/_number_of_parts);
1230 rp->setDirection(2*pi*i/_number_of_parts);
1232 for (i=0;i<_number_of_parts;i++)
1234 rps[i]->setlastnextrp(rps[(i-1+_number_of_parts)%_number_of_parts],
1235 rps[(i+1)%_number_of_parts],
1236 rps[(i+_number_of_parts/2)%_number_of_parts],
1237 rps[(i-_number_of_parts/4+_number_of_parts)%_number_of_parts],
1238 rps[(i+_number_of_parts/4)%_number_of_parts]);
1241 updateDirectionsAndPositions(drot);
1242 for (i=0;i<_number_of_parts;i++)
1244 rps[i]->setCompiled();
1246 float lift[4],torque[4], v_wind[3];
1247 v_wind[0]=v_wind[1]=v_wind[2]=0;
1248 rps[0]->setOmega(_omegan);
1250 if (_airfoil_lift_coefficient==0)
1252 //calculate the lift and drag coefficients now
1256 rps[0]->calculateAlpha(v_wind,rho_null,_pitch_a,0,0,
1257 &(torque[0]),&(lift[0])); //max_pitch a
1258 _liftcoef = pitchaforce/lift[0];
1261 rps[0]->calculateAlpha(v_wind,rho_null,0,0,0,&(torque[0]),&(lift[0]));
1266 rps[0]->calculateAlpha(v_wind,rho_null,0,0,0,&(torque[1]),&(lift[1]));
1271 rps[0]->calculateAlpha(v_wind,rho_null,_pitch_b,0,0,&(torque[2]),&(lift[2]));
1276 rps[0]->calculateAlpha(v_wind,rho_null,_pitch_b,0,0,&(torque[3]),&(lift[3]));
1281 _dragcoef1=torque0/torque[1];
1282 _dragcoef0=(torqueb-_dragcoef1*torque[3])/torque[2];
1286 _dragcoef1=(torque0/torque[0]-torqueb/torque[2])
1287 /(torque[1]/torque[0]-torque[3]/torque[2]);
1288 _dragcoef0=(torqueb-_dragcoef1*torque[3])/torque[2];
1293 _liftcoef=_airfoil_lift_coefficient/_number_of_parts*_number_of_blades;
1294 _dragcoef0=_airfoil_drag_coefficient0/_number_of_parts*_number_of_blades*_c2;
1295 _dragcoef1=_airfoil_drag_coefficient1/_number_of_parts*_number_of_blades*_c2;
1299 rps[0]->calculateAlpha(v_wind,rho_null,_pitch_a,0,0,
1300 &(torque[0]),&(lift[0])); //pitch a
1301 rps[0]->calculateAlpha(v_wind,rho_null,_pitch_b,0,0,
1302 &(torque[1]),&(lift[1])); //pitch b
1303 rps[0]->calculateAlpha(v_wind,rho_null,0,0,0,
1304 &(torque[3]),&(lift[3])); //pitch 0
1305 SG_LOG(SG_GENERAL, SG_INFO,
1306 "Rotor: coefficients for airfoil:" << endl << setprecision(6)
1307 << " drag0: " << _dragcoef0*_number_of_parts/_number_of_blades/_c2
1308 << " drag1: " << _dragcoef1*_number_of_parts/_number_of_blades/_c2
1309 << " lift: " << _liftcoef*_number_of_parts/_number_of_blades
1311 << "at 10 deg:" << endl
1312 << "drag: " << (Math::sin(10./180*pi)*_dragcoef1+_dragcoef0)
1313 *_number_of_parts/_number_of_blades/_c2
1314 << " lift: " << Math::sin(10./180*pi)*_liftcoef*_number_of_parts/_number_of_blades
1316 << "Some results (Pitch [degree], Power [kW], Lift [N])" << endl
1317 << 0.0f << "deg " << Math::abs(torque[3]*_number_of_parts*_omegan/1000) << "kW "
1318 << lift[3]*_number_of_parts << endl
1319 << _pitch_a*180/pi << "deg " << Math::abs(torque[0]*_number_of_parts*_omegan/1000)
1320 << "kW " << lift[0]*_number_of_parts << endl
1321 << _pitch_b*180/pi << "deg " << Math::abs(torque[1]*_number_of_parts*_omegan/1000)
1322 << "kW " << lift[1]*_number_of_parts << endl << endl );
1324 //first calculation of relamp is wrong
1325 //it used pitchaforce, but this was unknown and
1326 //on the default value
1327 _delta*=lift[0]/pitchaforce;
1328 relamp=(omega*omega/(2*_delta*Math::sqrt(sqr(omega0*omega0-omega*omega)
1329 +4*_delta*_delta*omega*omega)))*_cyclic_factor;
1330 for (i=0;i<_number_of_parts;i++)
1332 rps[i]->setRelamp(relamp);
1334 rps[0]->setOmega(0);
1341 //tie the properties
1342 /* After reset these values are totally wrong. I have to find out why
1343 SGPropertyNode * node = fgGetNode("/rotors", true)->getNode(_name,true);
1344 node->tie("balance_ext",SGRawValuePointer<float>(&_balance2),false);
1345 node->tie("balance_int",SGRawValuePointer<float>(&_balance1));
1349 std::ostream & operator<<(std::ostream & out, Rotor& r)
1351 #define i(x) << #x << ":" << r.x << endl
1352 #define iv(x) << #x << ":" << r.x[0] << ";" << r.x[1] << ";" <<r.x[2] << ";" << endl
1353 out << "Writing Info on Rotor "
1356 i(_omega) i(_omegan) i(_omegarel) i(_ddt_omega) i(_omegarelneu)
1359 i( _airfoil_incidence_no_lift)
1361 i( _airfoil_lift_coefficient)
1362 i( _airfoil_drag_coefficient0)
1363 i( _airfoil_drag_coefficient1)
1365 i( _number_of_segments)
1366 i( _number_of_parts)
1368 iv( _groundeffectpos[0])iv( _groundeffectpos[1])iv( _groundeffectpos[2])iv( _groundeffectpos[3])
1369 i( _ground_effect_altitude)
1371 iv( _normal_with_yaw_roll)
1374 i( _number_of_blades)
1375 i( _weight_per_blade)
1376 i( _rel_blade_center)
1379 i( _force_at_pitch_a)
1381 i( _power_at_pitch_0)
1382 i( _power_at_pitch_b)
1399 i( _alphamin) i(_alphamax) i(_alpha0) i(_alpha0factor)
1400 i( _teeterdamp) i(_maxteeterdamp)
1401 i( _rellenteeterhinge)
1403 i( _translift_maxfactor)
1404 i( _ground_effect_constant)
1405 i( _vortex_state_lift_factor)
1406 i( _vortex_state_c1)
1407 i( _vortex_state_c2)
1408 i( _vortex_state_c3)
1409 i( _vortex_state_e1)
1410 i( _vortex_state_e2)
1411 i( _vortex_state_e3)
1412 i( _lift_factor) i(_f_ge) i(_f_vs) i(_f_tl)
1417 i( _twist) //outer incidence = inner inner incidence + _twist
1418 i( _rel_len_where_incidence_is_measured)
1419 i( _torque_of_inertia)
1420 i( _rel_len_blade_start)
1421 i( _incidence_stall_zero_speed)
1422 i( _incidence_stall_half_sonic_speed)
1423 i( _lift_factor_stall)
1424 i( _stall_change_over)
1425 i( _drag_factor_stall)
1432 i( _cyclic_factor) <<endl;
1434 for(j=0; j<r._rotorparts.size(); j++) {
1435 out << *((Rotorpart*)r._rotorparts.get(j));
1442 void Rotor:: writeInfo()
1445 std::ostringstream buffer;
1447 FILE*f=fopen("c:\\fgmsvc\\bat\\log.txt","at");
1448 if (!f) f=fopen("c:\\fgmsvc\\bat\\log.txt","wt");
1451 fprintf(f,"%s",(const char *)buffer.str().c_str());
1456 Rotorpart* Rotor::newRotorpart(float zentforce,float maxpitchforce,
1457 float delta3,float mass,float translift,float rellenhinge,float len)
1459 Rotorpart *r = new Rotorpart();
1460 r->setDelta3(delta3);
1461 r->setDynamic(_dynamic);
1462 r->setTranslift(_translift);
1465 r->setRelLenHinge(rellenhinge);
1466 r->setSharedFlapHinge(_shared_flap_hinge);
1467 r->setOmegaN(_omegan);
1468 r->setPhi(_phi_null);
1469 r->setAlpha0(_alpha0);
1470 r->setAlphamin(_alphamin);
1471 r->setAlphamax(_alphamax);
1472 r->setAlpha0factor(_alpha0factor);
1474 r->setDiameter(_diameter);
1476 #define p(a) r->setParameter(#a,_##a);
1478 p(number_of_segments)
1479 p(rel_len_where_incidence_is_measured)
1480 p(rel_len_blade_start)
1481 p(rotor_correction_factor)
1486 void Rotor::interp(float* v1, float* v2, float frac, float* out)
1488 out[0] = v1[0] + frac*(v2[0]-v1[0]);
1489 out[1] = v1[1] + frac*(v2[1]-v1[1]);
1490 out[2] = v1[2] + frac*(v2[2]-v1[2]);
1493 void Rotorgear::initRotorIteration(float *lrot,float dt)
1497 if (!_rotors.size()) return;
1498 Rotor* r0 = (Rotor*)_rotors.get(0);
1499 omegarel= r0->getOmegaRelNeu();
1500 for(i=0; i<_rotors.size(); i++) {
1501 Rotor* r = (Rotor*)_rotors.get(i);
1502 r->inititeration(dt,omegarel,0,lrot);
1506 void Rotorgear::calcForces(float* torqueOut)
1509 torqueOut[0]=torqueOut[1]=torqueOut[2]=0;
1510 // check,<if the engine can handle the torque of the rotors.
1511 // If not reduce the torque to the fueselage and change rotational
1512 // speed of the rotors instead
1515 float omegarel,omegan;
1516 Rotor* r0 = (Rotor*)_rotors.get(0);
1517 omegarel= r0->getOmegaRel();
1519 float total_torque_of_inertia=0;
1520 float total_torque=0;
1521 for(i=0; i<_rotors.size(); i++) {
1522 Rotor* r = (Rotor*)_rotors.get(i);
1523 omegan=r->getOmegan();
1524 total_torque_of_inertia+=r->getTorqueOfInertia()*omegan*omegan;
1525 //FIXME: this is constant, so this can be done in compile
1527 total_torque+=r->getTorque()*omegan;
1529 float max_torque_of_engine=0;
1530 // SGPropertyNode * node=fgGetNode("/rotors/gear", true);
1533 max_torque_of_engine=_max_power_engine*_max_rel_torque;
1534 float df=_target_rel_rpm-omegarel;
1535 df/=_engine_prop_factor;
1536 df = Math::clamp(df, 0, 1);
1537 max_torque_of_engine = df * _max_power_engine*_max_rel_torque;
1541 float rel_torque_engine=1;
1542 if (total_torque<=0)
1543 rel_torque_engine=0;
1545 if (max_torque_of_engine>0)
1546 rel_torque_engine=1/max_torque_of_engine*total_torque;
1548 rel_torque_engine=0;
1550 //add the rotor brake and the gear fritcion
1552 if (r0->_rotorparts.size()) dt=((Rotorpart*)r0->_rotorparts.get(0))->getDt();
1554 float rotor_brake_torque;
1555 rotor_brake_torque=_rotorbrake*_max_power_rotor_brake+_rotorgear_friction;
1556 //clamp it to the value you need to stop the rotor
1557 //to avod accelerate the rotor to neagtive rpm:
1558 rotor_brake_torque=Math::clamp(rotor_brake_torque,0,
1559 total_torque_of_inertia/dt*omegarel);
1560 max_torque_of_engine-=rotor_brake_torque;
1562 //change the rotation of the rotors
1563 if ((max_torque_of_engine<total_torque) //decreasing rotation
1564 ||((max_torque_of_engine>total_torque)&&(omegarel<_target_rel_rpm))
1565 //increasing rotation due to engine
1566 ||(total_torque<0) ) //increasing rotation due to autorotation
1568 _ddt_omegarel=(max_torque_of_engine-total_torque)/total_torque_of_inertia;
1569 if(max_torque_of_engine>total_torque)
1571 //check if the acceleration is due to the engine. If yes,
1572 //the engine self limits the accel.
1573 float lim1=-total_torque/total_torque_of_inertia;
1574 //accel. by autorotation
1576 if (lim1<_engine_accel_limit) lim1=_engine_accel_limit;
1577 //if the accel by autorotation greater than the max. engine
1578 //accel, then this is the limit, if not: the engine is the limit
1579 if (_ddt_omegarel>lim1) _ddt_omegarel=lim1;
1581 if (_ddt_omegarel>5.5)_ddt_omegarel=5.5;
1582 //clamp it to avoid overflow. Should never be reached
1583 if (_ddt_omegarel<-5.5)_ddt_omegarel=-5.5;
1585 if (_max_power_engine<0.001) {omegarel=1;_ddt_omegarel=0;}
1586 //for debug: negative or no maxpower will result
1587 //in permanet 100% rotation
1589 omegarel+=dt*_ddt_omegarel;
1591 if (omegarel>2.5) omegarel=2.5;
1592 //clamp it to avoid overflow. Should never be reached
1593 if (omegarel<-.5) omegarel=-.5;
1595 r0->setOmegaRelNeu(omegarel);
1596 //calculate the torque, which is needed to accelerate the rotors.
1597 //Add this additional torque to the body
1598 for(j=0; j<_rotors.size(); j++) {
1599 Rotor* r = (Rotor*)_rotors.get(j);
1600 for(i=0; i<r->_rotorparts.size(); i++) {
1601 // float torque_scalar=0;
1602 Rotorpart* rp = (Rotorpart*)r->_rotorparts.get(i);
1604 rp->getAccelTorque(_ddt_omegarel,torque);
1605 Math::add3(torque,torqueOut,torqueOut);
1609 _total_torque_on_engine=total_torque+_ddt_omegarel*total_torque_of_inertia;
1613 void Rotorgear::addRotor(Rotor* rotor)
1619 void Rotorgear::compile()
1622 for(int j=0; j<_rotors.size(); j++) {
1623 Rotor* r = (Rotor*)_rotors.get(j);
1628 void Rotorgear::getDownWash(float *pos, float * v_heli, float *downwash)
1631 downwash[0]=downwash[1]=downwash[2]=0;
1632 for(int i=0; i<_rotors.size(); i++) {
1633 Rotor* ro = (Rotor*)_rotors.get(i);
1634 ro->getDownWash(pos,v_heli,tmp);
1635 Math::add3(downwash,tmp,downwash); // + downwash
1639 void Rotorgear::setParameter(char *parametername, float value)
1641 #define p(a,b) if (strcmp(parametername,#a)==0) _##a = (value * (b)); else
1642 p(max_power_engine,1000)
1643 p(engine_prop_factor,1)
1644 p(yasimdragfactor,1)
1645 p(yasimliftfactor,1)
1646 p(max_power_rotor_brake,1000)
1647 p(rotorgear_friction,1000)
1648 p(engine_accel_limit,0.01)
1649 SG_LOG(SG_INPUT, SG_ALERT,
1650 "internal error in parameter set up for rotorgear: '"
1651 << parametername <<"'" << endl);
1654 int Rotorgear::getValueforFGSet(int j,char *text,float *f)
1658 sprintf(text,"/rotors/gear/total-torque");
1659 *f=_total_torque_on_engine;
1663 Rotorgear::Rotorgear()
1668 _max_power_rotor_brake=1;
1669 _rotorgear_friction=1;
1670 _max_power_engine=1000*450;
1671 _engine_prop_factor=0.05f;
1675 _engine_accel_limit=0.05f;
1676 _total_torque_on_engine=0;
1681 Rotorgear::~Rotorgear()
1683 for(int i=0; i<_rotors.size(); i++)
1684 delete (Rotor*)_rotors.get(i);
1687 }; // namespace yasim