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;
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 SGPropertyNode * node = fgGetNode("/rotors/gear/max-rel-torque", true);
379 if (node) node->setDoubleValue(lval);
382 void Rotorgear::setRotorRelTarget(float lval)
384 SGPropertyNode * node = fgGetNode("/rotors/gear/target-rel-rpm", true);
385 if (node) node->setDoubleValue(lval);
388 void Rotor::setAlpha0(float f)
393 void Rotor::setAlphamin(float f)
398 void Rotor::setAlphamax(float f)
403 void Rotor::setAlpha0factor(float f)
408 int Rotor::numRotorparts()
410 return _rotorparts.size();
413 Rotorpart* Rotor::getRotorpart(int n)
415 return ((Rotorpart*)_rotorparts.get(n));
418 int Rotorgear::getEngineon()
423 float Rotor::getTorqueOfInertia()
425 return _torque_of_inertia;
428 void Rotor::setTorque(float f)
433 void Rotor::addTorque(float f)
438 void Rotor::strncpy(char *dest,const char *src,int maxlen)
441 while(src[n]&&n<(maxlen-1))
449 void Rotor::setNormal(float* normal)
452 float invsum,sqrsum=0;
453 for(i=0; i<3; i++) { sqrsum+=normal[i]*normal[i];}
455 invsum=1/Math::sqrt(sqrsum);
460 _normal_with_yaw_roll[i]=_normal[i] = normal[i]*invsum;
464 void Rotor::setForward(float* forward)
467 float invsum,sqrsum=0;
468 for(i=0; i<3; i++) { sqrsum+=forward[i]*forward[i];}
470 invsum=1/Math::sqrt(sqrsum);
473 for(i=0; i<3; i++) { _forward[i] = forward[i]*invsum; }
476 void Rotor::setForceAtPitchA(float force)
478 _force_at_pitch_a=force;
481 void Rotor::setPowerAtPitch0(float value)
483 _power_at_pitch_0=value;
486 void Rotor::setPowerAtPitchB(float value)
488 _power_at_pitch_b=value;
491 void Rotor::setPitchA(float value)
493 _pitch_a=value/180*pi;
496 void Rotor::setPitchB(float value)
498 _pitch_b=value/180*pi;
501 void Rotor::setBase(float* base)
504 for(i=0; i<3; i++) _base[i] = base[i];
507 void Rotor::setMaxCyclicail(float value)
509 _maxcyclicail=value/180*pi;
512 void Rotor::setMaxCyclicele(float value)
514 _maxcyclicele=value/180*pi;
517 void Rotor::setMinCyclicail(float value)
519 _mincyclicail=value/180*pi;
522 void Rotor::setMinCyclicele(float value)
524 _mincyclicele=value/180*pi;
527 void Rotor::setMinCollective(float value)
529 _min_pitch=value/180*pi;
532 void Rotor::setMinTiltYaw(float value)
534 _min_tilt_yaw=value/180*pi;
537 void Rotor::setMinTiltPitch(float value)
539 _min_tilt_pitch=value/180*pi;
542 void Rotor::setMinTiltRoll(float value)
544 _min_tilt_roll=value/180*pi;
547 void Rotor::setMaxTiltYaw(float value)
549 _max_tilt_yaw=value/180*pi;
552 void Rotor::setMaxTiltPitch(float value)
554 _max_tilt_pitch=value/180*pi;
557 void Rotor::setMaxTiltRoll(float value)
559 _max_tilt_roll=value/180*pi;
562 void Rotor::setTiltCenterX(float value)
564 _tilt_center[0] = value;
567 void Rotor::setTiltCenterY(float value)
569 _tilt_center[1] = value;
572 void Rotor::setTiltCenterZ(float value)
574 _tilt_center[2] = value;
577 void Rotor::setMaxCollective(float value)
579 _max_pitch=value/180*pi;
582 void Rotor::setDiameter(float value)
587 void Rotor::setWeightPerBlade(float value)
589 _weight_per_blade=value;
592 void Rotor::setNumberOfBlades(float value)
594 _number_of_blades=int(value+.5);
597 void Rotor::setRelBladeCenter(float value)
599 _rel_blade_center=value;
602 void Rotor::setDynamic(float value)
607 void Rotor::setDelta3(float value)
612 void Rotor::setDelta(float value)
617 void Rotor::setTranslift(float value)
622 void Rotor::setSharedFlapHinge(bool s)
624 _shared_flap_hinge=s;
627 void Rotor::setBalance(float b)
632 void Rotor::setC2(float value)
637 void Rotor::setStepspersecond(float steps)
639 _stepspersecond=steps;
642 void Rotor::setRPM(float value)
647 void Rotor::setPhiNull(float value)
652 void Rotor::setRelLenHinge(float value)
654 _rel_len_hinge=value;
657 void Rotor::setAlphaoutput(int i, const char *text)
659 strncpy(_alphaoutput[i],text,255);
662 void Rotor::setName(const char *text)
664 strncpy(_name,text,256);//256: some space needed for settings
667 void Rotor::setCcw(int ccw)
672 void Rotor::setNotorque(int value)
677 void Rotor::setRelLenTeeterHinge(float f)
679 _rellenteeterhinge=f;
682 void Rotor::setTeeterdamp(float f)
687 void Rotor::setMaxteeterdamp(float f)
692 void Rotor::setGlobalGround(double *global_ground, float* global_vel)
695 for(i=0; i<4; i++) _global_ground[i] = global_ground[i];
698 void Rotor::setParameter(char *parametername, float value)
700 #define p(a,b) if (strcmp(parametername,#a)==0) _##a = (value * (b)); else
702 p(translift_maxfactor,1)
703 p(ground_effect_constant,1)
704 p(vortex_state_lift_factor,1)
712 p(number_of_segments,1)
714 p(rel_len_where_incidence_is_measured,1)
717 p(airfoil_incidence_no_lift,pi/180.)
718 p(rel_len_blade_start,1)
719 p(incidence_stall_zero_speed,pi/180.)
720 p(incidence_stall_half_sonic_speed,pi/180.)
721 p(lift_factor_stall,1)
722 p(stall_change_over,pi/180.)
723 p(drag_factor_stall,1)
724 p(airfoil_lift_coefficient,1)
725 p(airfoil_drag_coefficient0,1)
726 p(airfoil_drag_coefficient1,1)
728 p(rotor_correction_factor,1)
729 SG_LOG(SG_INPUT, SG_ALERT,
730 "internal error in parameter set up for rotor: '" <<
731 parametername <<"'" << endl);
735 float Rotor::getLiftFactor()
740 void Rotorgear::setRotorBrake(float lval)
742 lval = Math::clamp(lval, 0, 1);
746 void Rotor::setTiltYaw(float lval)
748 lval = Math::clamp(lval, -1, 1);
749 _tilt_yaw = _min_tilt_yaw+(lval+1)/2*(_max_tilt_yaw-_min_tilt_yaw);
750 _directions_and_postions_dirty = true;
753 void Rotor::setTiltPitch(float lval)
755 lval = Math::clamp(lval, -1, 1);
756 _tilt_pitch = _min_tilt_pitch+(lval+1)/2*(_max_tilt_pitch-_min_tilt_pitch);
757 _directions_and_postions_dirty = true;
760 void Rotor::setTiltRoll(float lval)
762 lval = Math::clamp(lval, -1, 1);
763 _tilt_roll = _min_tilt_roll+(lval+1)/2*(_max_tilt_roll-_min_tilt_roll);
764 _directions_and_postions_dirty = true;
767 void Rotor::setCollective(float lval)
769 lval = Math::clamp(lval, -1, 1);
771 _collective=_min_pitch+(lval+1)/2*(_max_pitch-_min_pitch);
772 for(i=0; i<_rotorparts.size(); i++) {
773 ((Rotorpart*)_rotorparts.get(i))->setCollective(_collective);
777 void Rotor::setCyclicele(float lval,float rval)
779 lval = Math::clamp(lval, -1, 1);
780 _cyclicele=_mincyclicele+(lval+1)/2*(_maxcyclicele-_mincyclicele);
783 void Rotor::setCyclicail(float lval,float rval)
785 lval = Math::clamp(lval, -1, 1);
787 _cyclicail=-(_mincyclicail+(lval+1)/2*(_maxcyclicail-_mincyclicail));
790 void Rotor::getPosition(float* out)
793 for(i=0; i<3; i++) out[i] = _base[i];
796 void Rotor::calcLiftFactor(float* v, float rho, State *s)
798 //calculates _lift_factor, which is a foactor for the lift of the rotor
800 //- ground effect (_f_ge)
801 //- vortex state (_f_vs)
802 //- translational lift (_f_tl)
807 // Calculate ground effect
808 _f_ge=1+_diameter/_ground_effect_altitude*_ground_effect_constant;
810 // Now calculate translational lift
811 float v_vert = Math::dot3(v,_normal);
813 Math::cross3(v,_normal,help);
814 float v_horiz = Math::mag3(help);
815 _f_tl = ((1-Math::pow(2.7183,-v_horiz/_translift_ve))
816 *(_translift_maxfactor-1)+1)/_translift_maxfactor;
818 _lift_factor = _f_ge*_f_tl*_f_vs;
820 //store the gravity direction
821 Glue::geodUp(s->pos, _grav_direction);
824 void Rotor::findGroundEffectAltitude(Ground * ground_cb,State *s)
826 _ground_effect_altitude=findGroundEffectAltitude(ground_cb,s,
827 _groundeffectpos[0],_groundeffectpos[1],
828 _groundeffectpos[2],_groundeffectpos[3]);
829 testForRotorGroundContact(ground_cb,s);
832 void Rotor::testForRotorGroundContact(Ground * ground_cb,State *s)
835 for (i=0;i<_num_ground_contact_pos;i++)
838 s->posLocalToGlobal(_ground_contact_pos[i], pt);
840 // Ask for the ground plane in the global coordinate system
841 double global_ground[4];
843 ground_cb->getGroundPlane(pt, global_ground, global_vel);
844 // find h, the distance to the ground
845 // The ground plane transformed to the local frame.
847 s->planeGlobalToLocal(global_ground, ground);
849 h = ground[3] - Math::dot3(_ground_contact_pos[i], ground);
850 // Now h is the distance from _ground_contact_pos[i] to ground
853 _balance1 -= (-h)/_diameter/_num_ground_contact_pos;
854 _balance1 = (_balance1<-1)?-1:_balance1;
858 float Rotor::findGroundEffectAltitude(Ground * ground_cb,State *s,
859 float *pos0,float *pos1,float *pos2,float *pos3,
860 int iteration,float a0,float a1,float a2,float a3)
874 Math::add3(p[0],p[2],p[4]);
875 Math::mul3(0.5,p[4],p[4]);//the center
877 float mina=100*_diameter;
879 for (int i=0;i<5;i++)
881 if (a[i]==-1)//in the first iteration,(iteration==0) no height is
882 //passed to this function, these missing values are
886 s->posLocalToGlobal(p[i], pt);
888 // Ask for the ground plane in the global coordinate system
889 double global_ground[4];
891 ground_cb->getGroundPlane(pt, global_ground, global_vel);
892 // find h, the distance to the ground
893 // The ground plane transformed to the local frame.
895 s->planeGlobalToLocal(global_ground, ground);
897 a[i] = ground[3] - Math::dot3(p[i], ground);
898 // Now a[i] is the distance from p[i] to ground
904 if (mina>=10*_diameter)
905 return mina; //the ground effect will be zero
907 //check if further recursion is neccessary
908 //if the height does not differ more than 20%, than
909 //we can return then mean height, if not split
910 //zhe square to four parts and calcualte the height
912 //suma * 0.2 is the mean
913 //0.15 is the maximum allowed difference from the mean
914 //to the height at the center
916 ||(Math::abs(suma*0.2-a[4])<(0.15*0.2*suma*(1<<iteration))))
919 float pc[4][3],ac[4]; //pc[i]=center of pos[i] and pos[(i+1)&3]
920 for (int i=0;i<4;i++)
922 Math::add3(p[i],p[(i+1)&3],pc[i]);
923 Math::mul3(0.5,pc[i],pc[i]);
925 s->posLocalToGlobal(pc[i], pt);
927 // Ask for the ground plane in the global coordinate system
928 double global_ground[4];
930 ground_cb->getGroundPlane(pt, global_ground, global_vel);
931 // find h, the distance to the ground
932 // The ground plane transformed to the local frame.
934 s->planeGlobalToLocal(global_ground, ground);
936 ac[i] = ground[3] - Math::dot3(p[i], ground);
937 // Now ac[i] is the distance from pc[i] to ground
940 (findGroundEffectAltitude(ground_cb,s,p[0],pc[1],p[4],pc[3],
941 iteration+1,a[0],ac[0],a[4],ac[3])
942 +findGroundEffectAltitude(ground_cb,s,p[1],pc[0],p[4],pc[1],
943 iteration+1,a[1],ac[0],a[4],ac[1])
944 +findGroundEffectAltitude(ground_cb,s,p[2],pc[1],p[4],pc[2],
945 iteration+1,a[2],ac[1],a[4],ac[2])
946 +findGroundEffectAltitude(ground_cb,s,p[3],pc[2],p[4],pc[3],
947 iteration+1,a[3],ac[2],a[4],ac[3])
951 void Rotor::getDownWash(float *pos, float *v_heli, float *downwash)
953 float pos2rotor[3],tmp[3];
954 Math::sub3(_base,pos,pos2rotor);
955 float dist=Math::dot3(pos2rotor,_normal_with_yaw_roll);
956 //calculate incidence at 0.7r;
957 float inc = _collective+_twist *0.7
958 - _twist*_rel_len_where_incidence_is_measured;
961 if (dist<0) // we are not in the downwash region
963 downwash[0]=downwash[1]=downwash[2]=0.;
967 //calculate the mean downwash speed directly beneath the rotor disk
968 float v1bar = Math::sin(inc) *_omega * 0.35 * _diameter * 0.8;
969 //0.35 * d = 0.7 *r, a good position to calcualte the mean downwashd
970 //0.8 the slip of the rotor.
972 //calculate the time the wind needed from thr rotor to here
973 if (v1bar< 1) v1bar = 1;
974 float time=dist/v1bar;
976 //calculate the pos2rotor, where the rotor was, "time" ago
977 Math::mul3(time,v_heli,tmp);
978 Math::sub3(pos2rotor,tmp,pos2rotor);
980 //and again calculate dist
981 dist=Math::dot3(pos2rotor,_normal_with_yaw_roll);
982 //missing the normal is offen not pointing to the normal of the rotor
983 //disk. Rotate the normal by yaw and tilt angle
987 if (dist<0) // we are not in the downwash region
989 downwash[0]=downwash[1]=downwash[2]=0.;
992 //of course this could be done in a runge kutta integrator, but it's such
993 //a approximation that I beleave, it would'nt be more realistic
995 //calculate the dist to the rotor-axis
996 Math::cross3(pos2rotor,_normal_with_yaw_roll,tmp);
997 float r= Math::mag3(tmp);
998 //calculate incidence at r;
999 float rel_r = r *2 /_diameter;
1000 float inc_r = _collective+_twist * r /_diameter * 2
1001 - _twist*_rel_len_where_incidence_is_measured;
1003 //calculate the downwash speed directly beneath the rotor disk
1006 v1 = Math::sin(inc_r) *_omega * r * 0.8;
1008 //calcualte the downwash speed in a distance "dist" to the rotor disc,
1009 //for large dist. The speed is assumed do follow a gausian distribution
1010 //with sigma increasing with dist^2:
1011 //sigma is assumed to be half of the rotor diameter directly beneath the
1012 //disc and is assumed to the rotor diameter at dist = (diameter * sqrt(2))
1014 float sigma=_diameter/2 + dist * dist / _diameter /4.;
1015 float v2 = v1bar*_diameter/ (Math::sqrt(2 * pi) * sigma)
1016 * Math::pow(2.7183,-.5*r*r/(sigma*sigma))*_diameter/2/sigma;
1018 //calculate the weight of the two downwash velocities.
1019 //Directly beneath the disc it is v1, far away it is v2
1020 float g = Math::pow(2.7183,-2*dist/_diameter);
1021 //at dist = rotor radius it is assumed to be 1/e * v1 + (1-1/e)* v2
1023 float v = g * v1 + (1-g) * v2;
1024 Math::mul3(-v,_normal_with_yaw_roll,downwash);
1025 //the downwash is calculated in the opposite direction of the normal
1028 void Rotor::euler2orient(float roll, float pitch, float hdg, float* out)
1030 // the Glue::euler2orient, inverts y<z due to different bases
1031 // therefore the negation of all "y" and "z" coeffizients
1032 Glue::euler2orient(roll,pitch,hdg,out);
1033 for (int i=3;i<9;i++) out[i]*=-1.0;
1037 void Rotor::updateDirectionsAndPositions(float *rot)
1039 if (!_directions_and_postions_dirty)
1041 rot[0]=rot[1]=rot[2]=0;
1044 rot[0]=_old_tilt_roll-_tilt_roll;
1045 rot[1]=_old_tilt_pitch-_tilt_pitch;
1046 rot[2]=_old_tilt_yaw-_tilt_yaw;
1047 _old_tilt_roll=_tilt_roll;
1048 _old_tilt_pitch=_tilt_pitch;
1049 _old_tilt_yaw=_tilt_yaw;
1051 euler2orient(_tilt_roll, _tilt_pitch, _tilt_yaw, orient);
1055 Math::sub3(_base,_tilt_center,base);
1056 Math::vmul33(orient, base, base);
1057 Math::add3(base,_tilt_center,base);
1058 Math::vmul33(orient, _forward, forward);
1059 Math::vmul33(orient, _normal, normal);
1061 #define _forward forward
1062 #define _normal normal
1063 float directions[5][3];
1064 //pointing forward, right, ... the 5th is ony for calculation
1065 directions[0][0]=_forward[0];
1066 directions[0][1]=_forward[1];
1067 directions[0][2]=_forward[2];
1072 Math::cross3(directions[i-1],_normal,directions[i]);
1074 Math::cross3(_normal,directions[i-1],directions[i]);
1076 Math::set3(directions[4],directions[0]);
1077 // now directions[0] is perpendicular to the _normal.and has a length
1078 // of 1. if _forward is already normalized and perpendicular to the
1079 // normal, directions[0] will be the same
1080 //_num_ground_contact_pos=(_number_of_parts<16)?_number_of_parts:16;
1081 for (i=0;i<_num_ground_contact_pos;i++)
1084 float s = Math::sin(pi*2*i/_num_ground_contact_pos);
1085 float c = Math::cos(pi*2*i/_num_ground_contact_pos);
1086 Math::mul3(c*_diameter*0.5,directions[0],_ground_contact_pos[i]);
1087 Math::mul3(s*_diameter*0.5,directions[1],help);
1088 Math::add3(help,_ground_contact_pos[i],_ground_contact_pos[i]);
1089 Math::add3(_base,_ground_contact_pos[i],_ground_contact_pos[i]);
1093 Math::mul3(_diameter*0.7,directions[i],_groundeffectpos[i]);
1094 Math::add3(_base,_groundeffectpos[i],_groundeffectpos[i]);
1096 for (i=0;i<_number_of_parts;i++)
1098 Rotorpart* rp = getRotorpart(i);
1099 float lpos[3],lforceattac[3],lspeed[3],dirzentforce[3];
1100 float s = Math::sin(2*pi*i/_number_of_parts);
1101 float c = Math::cos(2*pi*i/_number_of_parts);
1102 float sp = Math::sin(float(2*pi*i/_number_of_parts-pi/2.+_phi));
1103 float cp = Math::cos(float(2*pi*i/_number_of_parts-pi/2.+_phi));
1104 float direction[3],nextdirection[3],help[3],direction90deg[3];
1105 float rotorpartmass = _weight_per_blade*_number_of_blades/_number_of_parts*.453;
1106 float speed=_rotor_rpm/60*_diameter*_rel_blade_center*pi;
1107 float lentocenter=_diameter*_rel_blade_center*0.5;
1108 float lentoforceattac=_diameter*_rel_len_hinge*0.5;
1109 float zentforce=rotorpartmass*speed*speed/lentocenter;
1111 Math::mul3(c ,directions[0],help);
1112 Math::mul3(s ,directions[1],direction);
1113 Math::add3(help,direction,direction);
1115 Math::mul3(c ,directions[1],help);
1116 Math::mul3(s ,directions[2],direction90deg);
1117 Math::add3(help,direction90deg,direction90deg);
1119 Math::mul3(cp ,directions[1],help);
1120 Math::mul3(sp ,directions[2],nextdirection);
1121 Math::add3(help,nextdirection,nextdirection);
1123 Math::mul3(lentocenter,direction,lpos);
1124 Math::add3(lpos,_base,lpos);
1125 Math::mul3(lentoforceattac,nextdirection,lforceattac);
1126 //nextdirection: +90deg (gyro)!!!
1128 Math::add3(lforceattac,_base,lforceattac);
1129 Math::mul3(speed,direction90deg,lspeed);
1130 Math::mul3(1,nextdirection,dirzentforce);
1131 rp->setPosition(lpos);
1132 rp->setNormal(_normal);
1133 rp->setZentipetalForce(zentforce);
1134 rp->setPositionForceAttac(lforceattac);
1135 rp->setSpeed(lspeed);
1136 rp->setDirectionofZentipetalforce(dirzentforce);
1137 rp->setDirectionofRotorPart(direction);
1142 _directions_and_postions_dirty=false;
1145 void Rotor::compile()
1147 // Have we already been compiled?
1148 if(_rotorparts.size() != 0) return;
1150 //rotor is divided into _number_of_parts parts
1151 //each part is calcualted at _number_of_segments points
1154 //and make it a factor of 4
1155 _number_of_parts=(int(Math::clamp(_number_of_parts,4,256))>>2)<<2;
1157 _dynamic=_dynamic*(1/ //inverse of the time
1158 ( (60/_rotor_rpm)/4 //for rotating 90 deg
1159 +(60/_rotor_rpm)/(2*_number_of_blades) //+ meantime a rotorblade
1160 //will pass a given point
1162 //normalize the directions
1163 Math::unit3(_forward,_forward);
1164 Math::unit3(_normal,_normal);
1165 _num_ground_contact_pos=(_number_of_parts<16)?_number_of_parts:16;
1166 float rotorpartmass = _weight_per_blade*_number_of_blades/_number_of_parts*.453;
1167 //was pounds -> now kg
1169 _torque_of_inertia = 1/12. * ( _number_of_parts * rotorpartmass) * _diameter
1170 * _diameter * _rel_blade_center * _rel_blade_center /(0.5*0.5);
1171 float speed=_rotor_rpm/60*_diameter*_rel_blade_center*pi;
1172 float lentocenter=_diameter*_rel_blade_center*0.5;
1173 float lentoforceattac=_diameter*_rel_len_hinge*0.5;
1174 float zentforce=rotorpartmass*speed*speed/lentocenter;
1175 float pitchaforce=_force_at_pitch_a/_number_of_parts*.453*9.81;
1176 // was pounds of force, now N, devided by _number_of_parts
1177 //(so its now per rotorpart)
1179 float torque0=0,torquemax=0,torqueb=0;
1180 float omega=_rotor_rpm/60*2*pi;
1182 float omega0=omega*Math::sqrt(1/(1-_rel_len_hinge));
1183 float delta_theoretical=pitchaforce/(_pitch_a*omega*lentocenter*2*rotorpartmass);
1184 _delta*=delta_theoretical;
1186 float relamp=(omega*omega/(2*_delta*Math::sqrt(sqr(omega0*omega0-omega*omega)
1187 +4*_delta*_delta*omega*omega)))*_cyclic_factor;
1188 float relamp_theoretical=(omega*omega/(2*delta_theoretical*Math::sqrt(sqr(omega0*omega0-omega*omega)
1189 +4*delta_theoretical*delta_theoretical*omega*omega)))*_cyclic_factor;
1190 _phi=Math::acos(_rel_len_hinge);
1191 _phi-=Math::atan(_delta3);
1194 torque0=_power_at_pitch_0/_number_of_parts*1000/omega;
1195 // f*r=p/w ; p=f*s/t; r=s/t/w ; r*w*t = s
1196 torqueb=_power_at_pitch_b/_number_of_parts*1000/omega;
1197 torquemax=_power_at_pitch_b/_number_of_parts*1000/omega/_pitch_b*_max_pitch;
1207 Rotorpart* rps[256];
1209 for (i=0;i<_number_of_parts;i++)
1211 Rotorpart* rp=rps[i]=newRotorpart(zentforce,pitchaforce,_delta3,rotorpartmass,
1212 _translift,_rel_len_hinge,lentocenter);
1213 int k = i*4/_number_of_parts;
1214 rp->setAlphaoutput(_alphaoutput[k&1?k:(_ccw?k^2:k)],0);
1215 rp->setAlphaoutput(_alphaoutput[4+(k&1?k:(_ccw?k^2:k))],1+(k>1));
1216 _rotorparts.add(rp);
1217 rp->setTorque(torquemax,torque0);
1218 rp->setRelamp(relamp);
1219 rp->setTorqueOfInertia(_torque_of_inertia/_number_of_parts);
1220 rp->setDirection(2*pi*i/_number_of_parts);
1222 for (i=0;i<_number_of_parts;i++)
1224 rps[i]->setlastnextrp(rps[(i-1+_number_of_parts)%_number_of_parts],
1225 rps[(i+1)%_number_of_parts],
1226 rps[(i+_number_of_parts/2)%_number_of_parts],
1227 rps[(i-_number_of_parts/4+_number_of_parts)%_number_of_parts],
1228 rps[(i+_number_of_parts/4)%_number_of_parts]);
1231 updateDirectionsAndPositions(drot);
1232 for (i=0;i<_number_of_parts;i++)
1234 rps[i]->setCompiled();
1236 float lift[4],torque[4], v_wind[3];
1237 v_wind[0]=v_wind[1]=v_wind[2]=0;
1238 rps[0]->setOmega(_omegan);
1240 if (_airfoil_lift_coefficient==0)
1242 //calculate the lift and drag coefficients now
1246 rps[0]->calculateAlpha(v_wind,rho_null,_pitch_a,0,0,
1247 &(torque[0]),&(lift[0])); //max_pitch a
1248 _liftcoef = pitchaforce/lift[0];
1251 rps[0]->calculateAlpha(v_wind,rho_null,0,0,0,&(torque[0]),&(lift[0]));
1256 rps[0]->calculateAlpha(v_wind,rho_null,0,0,0,&(torque[1]),&(lift[1]));
1261 rps[0]->calculateAlpha(v_wind,rho_null,_pitch_b,0,0,&(torque[2]),&(lift[2]));
1266 rps[0]->calculateAlpha(v_wind,rho_null,_pitch_b,0,0,&(torque[3]),&(lift[3]));
1271 _dragcoef1=torque0/torque[1];
1272 _dragcoef0=(torqueb-_dragcoef1*torque[3])/torque[2];
1276 _dragcoef1=(torque0/torque[0]-torqueb/torque[2])
1277 /(torque[1]/torque[0]-torque[3]/torque[2]);
1278 _dragcoef0=(torqueb-_dragcoef1*torque[3])/torque[2];
1283 _liftcoef=_airfoil_lift_coefficient/_number_of_parts*_number_of_blades;
1284 _dragcoef0=_airfoil_drag_coefficient0/_number_of_parts*_number_of_blades*_c2;
1285 _dragcoef1=_airfoil_drag_coefficient1/_number_of_parts*_number_of_blades*_c2;
1289 rps[0]->calculateAlpha(v_wind,rho_null,_pitch_a,0,0,
1290 &(torque[0]),&(lift[0])); //pitch a
1291 rps[0]->calculateAlpha(v_wind,rho_null,_pitch_b,0,0,
1292 &(torque[1]),&(lift[1])); //pitch b
1293 rps[0]->calculateAlpha(v_wind,rho_null,0,0,0,
1294 &(torque[3]),&(lift[3])); //pitch 0
1295 SG_LOG(SG_GENERAL, SG_INFO,
1296 "Rotor: coefficients for airfoil:" << endl << setprecision(6)
1297 << " drag0: " << _dragcoef0*_number_of_parts/_number_of_blades/_c2
1298 << " drag1: " << _dragcoef1*_number_of_parts/_number_of_blades/_c2
1299 << " lift: " << _liftcoef*_number_of_parts/_number_of_blades
1301 << "at 10 deg:" << endl
1302 << "drag: " << (Math::sin(10./180*pi)*_dragcoef1+_dragcoef0)
1303 *_number_of_parts/_number_of_blades/_c2
1304 << " lift: " << Math::sin(10./180*pi)*_liftcoef*_number_of_parts/_number_of_blades
1306 << "Some results (Pitch [degree], Power [kW], Lift [N])" << endl
1307 << 0.0f << "deg " << Math::abs(torque[3]*_number_of_parts*_omegan/1000) << "kW "
1308 << lift[3]*_number_of_parts << endl
1309 << _pitch_a*180/pi << "deg " << Math::abs(torque[0]*_number_of_parts*_omegan/1000)
1310 << "kW " << lift[0]*_number_of_parts << endl
1311 << _pitch_b*180/pi << "deg " << Math::abs(torque[1]*_number_of_parts*_omegan/1000)
1312 << "kW " << lift[1]*_number_of_parts << endl << endl );
1314 //first calculation of relamp is wrong
1315 //it used pitchaforce, but this was unknown and
1316 //on the default value
1317 _delta*=lift[0]/pitchaforce;
1318 relamp=(omega*omega/(2*_delta*Math::sqrt(sqr(omega0*omega0-omega*omega)
1319 +4*_delta*_delta*omega*omega)))*_cyclic_factor;
1320 for (i=0;i<_number_of_parts;i++)
1322 rps[i]->setRelamp(relamp);
1324 rps[0]->setOmega(0);
1331 //tie the properties
1332 /* After reset these values are totally wrong. I have to find out why
1333 SGPropertyNode * node = fgGetNode("/rotors", true)->getNode(_name,true);
1334 node->tie("balance_ext",SGRawValuePointer<float>(&_balance2),false);
1335 node->tie("balance_int",SGRawValuePointer<float>(&_balance1));
1339 std::ostream & operator<<(std::ostream & out, Rotor& r)
1341 #define i(x) << #x << ":" << r.x << endl
1342 #define iv(x) << #x << ":" << r.x[0] << ";" << r.x[1] << ";" <<r.x[2] << ";" << endl
1343 out << "Writing Info on Rotor "
1346 i(_omega) i(_omegan) i(_omegarel) i(_ddt_omega) i(_omegarelneu)
1349 i( _airfoil_incidence_no_lift)
1351 i( _airfoil_lift_coefficient)
1352 i( _airfoil_drag_coefficient0)
1353 i( _airfoil_drag_coefficient1)
1355 i( _number_of_segments)
1356 i( _number_of_parts)
1358 iv( _groundeffectpos[0])iv( _groundeffectpos[1])iv( _groundeffectpos[2])iv( _groundeffectpos[3])
1359 i( _ground_effect_altitude)
1361 iv( _normal_with_yaw_roll)
1364 i( _number_of_blades)
1365 i( _weight_per_blade)
1366 i( _rel_blade_center)
1369 i( _force_at_pitch_a)
1371 i( _power_at_pitch_0)
1372 i( _power_at_pitch_b)
1389 i( _alphamin) i(_alphamax) i(_alpha0) i(_alpha0factor)
1390 i( _teeterdamp) i(_maxteeterdamp)
1391 i( _rellenteeterhinge)
1393 i( _translift_maxfactor)
1394 i( _ground_effect_constant)
1395 i( _vortex_state_lift_factor)
1396 i( _vortex_state_c1)
1397 i( _vortex_state_c2)
1398 i( _vortex_state_c3)
1399 i( _vortex_state_e1)
1400 i( _vortex_state_e2)
1401 i( _vortex_state_e3)
1402 i( _lift_factor) i(_f_ge) i(_f_vs) i(_f_tl)
1407 i( _twist) //outer incidence = inner inner incidence + _twist
1408 i( _rel_len_where_incidence_is_measured)
1409 i( _torque_of_inertia)
1410 i( _rel_len_blade_start)
1411 i( _incidence_stall_zero_speed)
1412 i( _incidence_stall_half_sonic_speed)
1413 i( _lift_factor_stall)
1414 i( _stall_change_over)
1415 i( _drag_factor_stall)
1422 i( _cyclic_factor) <<endl;
1424 for(j=0; j<r._rotorparts.size(); j++) {
1425 out << *((Rotorpart*)r._rotorparts.get(j));
1432 void Rotor:: writeInfo()
1435 std::ostringstream buffer;
1437 FILE*f=fopen("c:\\fgmsvc\\bat\\log.txt","at");
1438 if (!f) f=fopen("c:\\fgmsvc\\bat\\log.txt","wt");
1441 fprintf(f,"%s",(const char *)buffer.str().c_str());
1446 Rotorpart* Rotor::newRotorpart(float zentforce,float maxpitchforce,
1447 float delta3,float mass,float translift,float rellenhinge,float len)
1449 Rotorpart *r = new Rotorpart();
1450 r->setDelta3(delta3);
1451 r->setDynamic(_dynamic);
1452 r->setTranslift(_translift);
1455 r->setRelLenHinge(rellenhinge);
1456 r->setSharedFlapHinge(_shared_flap_hinge);
1457 r->setOmegaN(_omegan);
1458 r->setPhi(_phi_null);
1459 r->setAlpha0(_alpha0);
1460 r->setAlphamin(_alphamin);
1461 r->setAlphamax(_alphamax);
1462 r->setAlpha0factor(_alpha0factor);
1464 r->setDiameter(_diameter);
1466 #define p(a) r->setParameter(#a,_##a);
1468 p(number_of_segments)
1469 p(rel_len_where_incidence_is_measured)
1470 p(rel_len_blade_start)
1471 p(rotor_correction_factor)
1476 void Rotor::interp(float* v1, float* v2, float frac, float* out)
1478 out[0] = v1[0] + frac*(v2[0]-v1[0]);
1479 out[1] = v1[1] + frac*(v2[1]-v1[1]);
1480 out[2] = v1[2] + frac*(v2[2]-v1[2]);
1483 void Rotorgear::initRotorIteration(float *lrot,float dt)
1487 if (!_rotors.size()) return;
1488 Rotor* r0 = (Rotor*)_rotors.get(0);
1489 omegarel= r0->getOmegaRelNeu();
1490 for(i=0; i<_rotors.size(); i++) {
1491 Rotor* r = (Rotor*)_rotors.get(i);
1492 r->inititeration(dt,omegarel,0,lrot);
1496 void Rotorgear::calcForces(float* torqueOut)
1499 torqueOut[0]=torqueOut[1]=torqueOut[2]=0;
1500 // check,<if the engine can handle the torque of the rotors.
1501 // If not reduce the torque to the fueselage and change rotational
1502 // speed of the rotors instead
1505 float omegarel,omegan;
1506 Rotor* r0 = (Rotor*)_rotors.get(0);
1507 omegarel= r0->getOmegaRel();
1509 float total_torque_of_inertia=0;
1510 float total_torque=0;
1511 for(i=0; i<_rotors.size(); i++) {
1512 Rotor* r = (Rotor*)_rotors.get(i);
1513 omegan=r->getOmegan();
1514 total_torque_of_inertia+=r->getTorqueOfInertia()*omegan*omegan;
1515 //FIXME: this is constant, so this can be done in compile
1517 total_torque+=r->getTorque()*omegan;
1519 float max_torque_of_engine=0;
1520 SGPropertyNode * node=fgGetNode("/rotors/gear", true);
1521 float target_rel_rpm=(node==0?1:node->getDoubleValue("target-rel-rpm",1));
1524 float max_rel_torque=(node==0?1:node->getDoubleValue("max-rel-torque",1));
1525 max_torque_of_engine=_max_power_engine*max_rel_torque;
1526 float df=target_rel_rpm-omegarel;
1527 df/=_engine_prop_factor;
1528 df = Math::clamp(df, 0, 1);
1529 max_torque_of_engine = df * _max_power_engine*max_rel_torque;
1533 float rel_torque_engine=1;
1534 if (total_torque<=0)
1535 rel_torque_engine=0;
1537 if (max_torque_of_engine>0)
1538 rel_torque_engine=1/max_torque_of_engine*total_torque;
1540 rel_torque_engine=0;
1542 //add the rotor brake and the gear fritcion
1544 if (r0->_rotorparts.size()) dt=((Rotorpart*)r0->_rotorparts.get(0))->getDt();
1546 float rotor_brake_torque;
1547 rotor_brake_torque=_rotorbrake*_max_power_rotor_brake+_rotorgear_friction;
1548 //clamp it to the value you need to stop the rotor
1549 //to avod accelerate the rotor to neagtive rpm:
1550 rotor_brake_torque=Math::clamp(rotor_brake_torque,0,
1551 total_torque_of_inertia/dt*omegarel);
1552 max_torque_of_engine-=rotor_brake_torque;
1554 //change the rotation of the rotors
1555 if ((max_torque_of_engine<total_torque) //decreasing rotation
1556 ||((max_torque_of_engine>total_torque)&&(omegarel<target_rel_rpm))
1557 //increasing rotation due to engine
1558 ||(total_torque<0) ) //increasing rotation due to autorotation
1560 _ddt_omegarel=(max_torque_of_engine-total_torque)/total_torque_of_inertia;
1561 if(max_torque_of_engine>total_torque)
1563 //check if the acceleration is due to the engine. If yes,
1564 //the engine self limits the accel.
1565 float lim1=-total_torque/total_torque_of_inertia;
1566 //accel. by autorotation
1568 if (lim1<_engine_accel_limit) lim1=_engine_accel_limit;
1569 //if the accel by autorotation greater than the max. engine
1570 //accel, then this is the limit, if not: the engine is the limit
1571 if (_ddt_omegarel>lim1) _ddt_omegarel=lim1;
1573 if (_ddt_omegarel>5.5)_ddt_omegarel=5.5;
1574 //clamp it to avoid overflow. Should never be reached
1575 if (_ddt_omegarel<-5.5)_ddt_omegarel=-5.5;
1577 if (_max_power_engine<0.001) {omegarel=1;_ddt_omegarel=0;}
1578 //for debug: negative or no maxpower will result
1579 //in permanet 100% rotation
1581 omegarel+=dt*_ddt_omegarel;
1583 if (omegarel>2.5) omegarel=2.5;
1584 //clamp it to avoid overflow. Should never be reached
1585 if (omegarel<-.5) omegarel=-.5;
1587 r0->setOmegaRelNeu(omegarel);
1588 //calculate the torque, which is needed to accelerate the rotors.
1589 //Add this additional torque to the body
1590 for(j=0; j<_rotors.size(); j++) {
1591 Rotor* r = (Rotor*)_rotors.get(j);
1592 for(i=0; i<r->_rotorparts.size(); i++) {
1593 float torque_scalar=0;
1594 Rotorpart* rp = (Rotorpart*)r->_rotorparts.get(i);
1596 rp->getAccelTorque(_ddt_omegarel,torque);
1597 Math::add3(torque,torqueOut,torqueOut);
1601 _total_torque_on_engine=total_torque+_ddt_omegarel*total_torque_of_inertia;
1605 void Rotorgear::addRotor(Rotor* rotor)
1611 void Rotorgear::compile()
1614 for(int j=0; j<_rotors.size(); j++) {
1615 Rotor* r = (Rotor*)_rotors.get(j);
1618 SGPropertyNode * node = fgGetNode("/rotors/gear/target-rel-rpm", true);
1619 if (node) node->setDoubleValue(1);
1620 node =fgGetNode("/rotors/gear/max-rel-torque", true);
1621 if (node) node->setDoubleValue(1);
1624 void Rotorgear::getDownWash(float *pos, float * v_heli, float *downwash)
1627 downwash[0]=downwash[1]=downwash[2]=0;
1628 for(int i=0; i<_rotors.size(); i++) {
1629 Rotor* ro = (Rotor*)_rotors.get(i);
1630 ro->getDownWash(pos,v_heli,tmp);
1631 Math::add3(downwash,tmp,downwash); // + downwash
1635 void Rotorgear::setParameter(char *parametername, float value)
1637 #define p(a,b) if (strcmp(parametername,#a)==0) _##a = (value * (b)); else
1638 p(max_power_engine,1000)
1639 p(engine_prop_factor,1)
1640 p(yasimdragfactor,1)
1641 p(yasimliftfactor,1)
1642 p(max_power_rotor_brake,1000)
1643 p(rotorgear_friction,1000)
1644 p(engine_accel_limit,0.01)
1645 SG_LOG(SG_INPUT, SG_ALERT,
1646 "internal error in parameter set up for rotorgear: '"
1647 << parametername <<"'" << endl);
1650 int Rotorgear::getValueforFGSet(int j,char *text,float *f)
1654 sprintf(text,"/rotors/gear/total-torque");
1655 *f=_total_torque_on_engine;
1659 Rotorgear::Rotorgear()
1664 _max_power_rotor_brake=1;
1665 _rotorgear_friction=1;
1666 _max_power_engine=1000*450;
1667 _engine_prop_factor=0.05f;
1671 _engine_accel_limit=0.05f;
1672 _total_torque_on_engine=0;
1675 Rotorgear::~Rotorgear()
1677 for(int i=0; i<_rotors.size(); i++)
1678 delete (Rotor*)_rotors.get(i);
1681 }; // namespace yasim