1 #include <simgear/debug/logstream.hxx>
5 #include "Rotorpart.hpp"
11 SG_USING_STD(setprecision);
17 const float pi=3.14159;
19 static inline float sqr(float a) { return a * a; }
36 _base[0] = _base[1] = _base[2] = 0;
45 _forward[1]=_forward[2]=0;
46 _max_pitch=13./180*pi;
47 _maxcyclicail=10./180*pi;
48 _maxcyclicele=10./180*pi;
50 _mincyclicail=-10./180*pi;
51 _mincyclicele=-10./180*pi;
52 _min_pitch=-.5/180*pi;
54 _normal[0] = _normal[1] = 0;
56 _normal_with_yaw_roll[0]= _normal_with_yaw_roll[1]=0;
57 _normal_with_yaw_roll[2]=1;
59 _omega=_omegan=_omegarel=_omegarelneu=0;
68 _rellenteeterhinge=0.01;
75 _translift_maxfactor=1.3;
76 _ground_effect_constant=0.1;
77 _vortex_state_lift_factor=0.4;
89 _number_of_segments=1;
90 _rel_len_where_incidence_is_measured=0.7;
95 _airfoil_incidence_no_lift=0;
96 _rel_len_blade_start=0;
97 _airfoil_lift_coefficient=0;
98 _airfoil_drag_coefficient0=0;
99 _airfoil_drag_coefficient1=0;
101 _global_ground[i] = 0;
102 _global_ground[2] = 1;
103 _global_ground[3] = -1e3;
104 _incidence_stall_zero_speed=18*pi/180.;
105 _incidence_stall_half_sonic_speed=14*pi/180.;
106 _lift_factor_stall=0.28;
107 _stall_change_over=2*pi/180.;
108 _drag_factor_stall=8;
118 for(i=0; i<_rotorparts.size(); i++) {
119 Rotorpart* r = (Rotorpart*)_rotorparts.get(i);
124 void Rotor::inititeration(float dt,float omegarel,float ddt_omegarel,float *rot)
129 _omega=_omegan*_omegarel;
130 _ddt_omega=_omegan*ddt_omegarel;
132 for(i=0; i<_rotorparts.size(); i++) {
133 Rotorpart* r = (Rotorpart*)_rotorparts.get(i);
135 r->setDdtOmega(_ddt_omega);
136 r->inititeration(dt,rot);
139 //calculate the normal of the rotor disc, for calcualtion of the downwash
140 float side[3],help[3];
141 Math::cross3(_normal,_forward,side);
142 Math::mul3(Math::cos(_yaw)*Math::cos(_roll),_normal,_normal_with_yaw_roll);
144 Math::mul3(Math::sin(_yaw),_forward,help);
145 Math::add3(_normal_with_yaw_roll,help,_normal_with_yaw_roll);
147 Math::mul3(Math::sin(_roll),side,help);
148 Math::add3(_normal_with_yaw_roll,help,_normal_with_yaw_roll);
151 float Rotor::calcStall(float incidence,float speed)
153 float stall_incidence=_incidence_stall_zero_speed
154 +(_incidence_stall_half_sonic_speed
155 -_incidence_stall_zero_speed)*speed/(343./2);
156 //missing: Temeperature dependency of sonic speed
157 incidence = Math::abs(incidence);
158 if (incidence > (90./180.*pi))
159 incidence = pi-incidence;
160 float stall = (incidence-stall_incidence)/_stall_change_over;
161 stall = Math::clamp(stall,0,1);
163 _stall_sum+=stall*speed*speed;
164 _stall_v2sum+=speed*speed;
169 float Rotor::getLiftCoef(float incidence,float speed)
171 float stall=calcStall(incidence,speed);
172 float c1= Math::sin(incidence-_airfoil_incidence_no_lift)*_liftcoef;
173 float c2= Math::sin(2*(incidence-_airfoil_incidence_no_lift))
174 *_liftcoef*_lift_factor_stall;
175 return (1-stall)*c1 + stall *c2;
178 float Rotor::getDragCoef(float incidence,float speed)
180 float stall=calcStall(incidence,speed);
181 float c1= (Math::abs(Math::sin(incidence-_airfoil_incidence_no_lift))
182 *_dragcoef1+_dragcoef0);
183 float c2= c1*_drag_factor_stall;
184 return (1-stall)*c1 + stall *c2;
187 int Rotor::getValueforFGSet(int j,char *text,float *f)
189 if (_name[0]==0) return 0;
190 if (4!=numRotorparts()) return 0; //compile first!
193 sprintf(text,"/rotors/%s/cone", _name);
194 *f=( ((Rotorpart*)getRotorpart(0))->getrealAlpha()
195 +((Rotorpart*)getRotorpart(1))->getrealAlpha()
196 +((Rotorpart*)getRotorpart(2))->getrealAlpha()
197 +((Rotorpart*)getRotorpart(3))->getrealAlpha()
203 sprintf(text,"/rotors/%s/roll", _name);
204 _roll = ( ((Rotorpart*)getRotorpart(0))->getrealAlpha()
205 -((Rotorpart*)getRotorpart(2))->getrealAlpha()
212 sprintf(text,"/rotors/%s/yaw", _name);
213 _yaw=( ((Rotorpart*)getRotorpart(1))->getrealAlpha()
214 -((Rotorpart*)getRotorpart(3))->getrealAlpha()
221 sprintf(text,"/rotors/%s/rpm", _name);
227 sprintf(text,"/rotors/%s/debug/debugfge",_name);
232 sprintf(text,"/rotors/%s/debug/debugfvs",_name);
237 sprintf(text,"/rotors/%s/debug/debugftl",_name);
242 sprintf(text,"/rotors/%s/debug/vortexstate",_name);
247 sprintf(text,"/rotors/%s/stall",_name);
248 *f=getOverallStall();
252 sprintf(text,"/rotors/%s/torque",_name);
258 if (b>=_number_of_blades)
263 sprintf(text,"/rotors/%s/blade%i_%s",
265 w==0?"pos":(w==1?"flap":"incidence"));
266 *f=((Rotorpart*)getRotorpart(0))->getPhi()*180/pi
267 +360*b/_number_of_blades*(_ccw?1:-1);
277 if(w==2) {k+=2;l+=2;}
279 if(w==1) {k+=1;l+=1;}
282 if (w==1) *f=rk*((Rotorpart*) getRotorpart(k))->getrealAlpha()*180/pi
283 +rl*((Rotorpart*) getRotorpart(l))->getrealAlpha()*180/pi;
284 else if(w==2) *f=rk*((Rotorpart*)getRotorpart(k))->getIncidence()*180/pi
285 +rl*((Rotorpart*)getRotorpart(l))->getIncidence()*180/pi;
290 void Rotorgear::setEngineOn(int value)
295 void Rotor::setAlpha0(float f)
300 void Rotor::setAlphamin(float f)
305 void Rotor::setAlphamax(float f)
310 void Rotor::setAlpha0factor(float f)
315 int Rotor::numRotorparts()
317 return _rotorparts.size();
320 Rotorpart* Rotor::getRotorpart(int n)
322 return ((Rotorpart*)_rotorparts.get(n));
325 int Rotorgear::getEngineon()
330 float Rotor::getTorqueOfInertia()
332 return _torque_of_inertia;
335 void Rotor::setTorque(float f)
340 void Rotor::addTorque(float f)
345 void Rotor::strncpy(char *dest,const char *src,int maxlen)
348 while(src[n]&&n<(maxlen-1))
356 void Rotor::setNormal(float* normal)
359 float invsum,sqrsum=0;
360 for(i=0; i<3; i++) { sqrsum+=normal[i]*normal[i];}
362 invsum=1/Math::sqrt(sqrsum);
367 _normal_with_yaw_roll[i]=_normal[i] = normal[i]*invsum;
371 void Rotor::setForward(float* forward)
374 float invsum,sqrsum=0;
375 for(i=0; i<3; i++) { sqrsum+=forward[i]*forward[i];}
377 invsum=1/Math::sqrt(sqrsum);
380 for(i=0; i<3; i++) { _forward[i] = forward[i]*invsum; }
383 void Rotor::setForceAtPitchA(float force)
385 _force_at_pitch_a=force;
388 void Rotor::setPowerAtPitch0(float value)
390 _power_at_pitch_0=value;
393 void Rotor::setPowerAtPitchB(float value)
395 _power_at_pitch_b=value;
398 void Rotor::setPitchA(float value)
400 _pitch_a=value/180*pi;
403 void Rotor::setPitchB(float value)
405 _pitch_b=value/180*pi;
408 void Rotor::setBase(float* base)
411 for(i=0; i<3; i++) _base[i] = base[i];
414 void Rotor::setMaxCyclicail(float value)
416 _maxcyclicail=value/180*pi;
419 void Rotor::setMaxCyclicele(float value)
421 _maxcyclicele=value/180*pi;
424 void Rotor::setMinCyclicail(float value)
426 _mincyclicail=value/180*pi;
429 void Rotor::setMinCyclicele(float value)
431 _mincyclicele=value/180*pi;
434 void Rotor::setMinCollective(float value)
436 _min_pitch=value/180*pi;
439 void Rotor::setMaxCollective(float value)
441 _max_pitch=value/180*pi;
444 void Rotor::setDiameter(float value)
449 void Rotor::setWeightPerBlade(float value)
451 _weight_per_blade=value;
454 void Rotor::setNumberOfBlades(float value)
456 _number_of_blades=int(value+.5);
459 void Rotor::setRelBladeCenter(float value)
461 _rel_blade_center=value;
464 void Rotor::setDynamic(float value)
469 void Rotor::setDelta3(float value)
474 void Rotor::setDelta(float value)
479 void Rotor::setTranslift(float value)
484 void Rotor::setC2(float value)
489 void Rotor::setStepspersecond(float steps)
491 _stepspersecond=steps;
494 void Rotor::setRPM(float value)
499 void Rotor::setRelLenHinge(float value)
501 _rel_len_hinge=value;
504 void Rotor::setAlphaoutput(int i, const char *text)
506 strncpy(_alphaoutput[i],text,255);
509 void Rotor::setName(const char *text)
511 strncpy(_name,text,256);//256: some space needed for settings
514 void Rotor::setCcw(int ccw)
519 void Rotor::setNotorque(int value)
524 void Rotor::setRelLenTeeterHinge(float f)
526 _rellenteeterhinge=f;
529 void Rotor::setTeeterdamp(float f)
534 void Rotor::setMaxteeterdamp(float f)
539 void Rotor::setGlobalGround(double *global_ground, float* global_vel)
542 for(i=0; i<4; i++) _global_ground[i] = global_ground[i];
545 void Rotor::setParameter(char *parametername, float value)
547 #define p(a,b) if (strcmp(parametername,#a)==0) _##a = (value * (b)); else
549 p(translift_maxfactor,1)
550 p(ground_effect_constant,1)
551 p(vortex_state_lift_factor,1)
559 p(number_of_segments,1)
560 p(rel_len_where_incidence_is_measured,1)
563 p(airfoil_incidence_no_lift,pi/180.)
564 p(rel_len_blade_start,1)
565 p(incidence_stall_zero_speed,pi/180.)
566 p(incidence_stall_half_sonic_speed,pi/180.)
567 p(lift_factor_stall,1)
568 p(stall_change_over,pi/180.)
569 p(drag_factor_stall,1)
570 p(airfoil_lift_coefficient,1)
571 p(airfoil_drag_coefficient0,1)
572 p(airfoil_drag_coefficient1,1)
573 cout << "internal error in parameter set up for rotor: '"
574 << parametername <<"'" << endl;
578 float Rotor::getLiftFactor()
583 void Rotorgear::setRotorBrake(float lval)
585 lval = Math::clamp(lval, 0, 1);
589 void Rotor::setCollective(float lval)
591 lval = Math::clamp(lval, -1, 1);
593 for(i=0; i<_rotorparts.size(); i++) {
594 ((Rotorpart*)_rotorparts.get(i))->setCollective(lval);
596 _collective=_min_pitch+(lval+1)/2*(_max_pitch-_min_pitch);
599 void Rotor::setCyclicele(float lval,float rval)
601 rval = Math::clamp(rval, -1, 1);
602 lval = Math::clamp(lval, -1, 1);
603 float col=_mincyclicele+(lval+1)/2*(_maxcyclicele-_mincyclicele);
604 if (_rotorparts.size()!=4) return;
605 ((Rotorpart*)_rotorparts.get(1))->setCyclic(lval);
606 ((Rotorpart*)_rotorparts.get(3))->setCyclic(-lval);
609 void Rotor::setCyclicail(float lval,float rval)
611 lval = Math::clamp(lval, -1, 1);
612 rval = Math::clamp(rval, -1, 1);
613 float col=_mincyclicail+(lval+1)/2*(_maxcyclicail-_mincyclicail);
614 if (_rotorparts.size()!=4) return;
616 ((Rotorpart*)_rotorparts.get(0))->setCyclic(-lval);
617 ((Rotorpart*)_rotorparts.get(2))->setCyclic( lval);
620 void Rotor::getPosition(float* out)
623 for(i=0; i<3; i++) out[i] = _base[i];
626 void Rotor::calcLiftFactor(float* v, float rho, State *s)
628 //calculates _lift_factor, which is a foactor for the lift of the rotor
630 //- ground effect (_f_ge)
631 //- vortex state (_f_vs)
632 //- translational lift (_f_tl)
637 // find h, the distance to the ground
638 // The ground plane transformed to the local frame.
640 s->planeGlobalToLocal(_global_ground, ground);
642 float h = ground[3] - Math::dot3(_base, ground);
643 // Now h is the distance from the rotor center to ground
645 // Calculate ground effect
646 _f_ge=1+_diameter/h*_ground_effect_constant;
648 // Now calculate translational lift
649 float v_vert = Math::dot3(v,_normal);
651 Math::cross3(v,_normal,help);
652 float v_horiz = Math::mag3(help);
653 _f_tl = ((1-Math::pow(2.7183,-v_horiz/_translift_ve))
654 *(_translift_maxfactor-1)+1)/_translift_maxfactor;
656 _lift_factor = _f_ge*_f_tl*_f_vs;
659 float Rotor::getGroundEffect(float* posOut)
661 return _diameter*0; //ground effect for rotor is calcualted not here
664 void Rotor::getDownWash(float *pos, float *v_heli, float *downwash)
666 float pos2rotor[3],tmp[3];
667 Math::sub3(_base,pos,pos2rotor);
668 float dist=Math::dot3(pos2rotor,_normal_with_yaw_roll);
669 //calculate incidence at 0.7r;
670 float inc = _collective+_twist *0.7
671 - _twist*_rel_len_where_incidence_is_measured;
674 if (dist<0) // we are not in the downwash region
676 downwash[0]=downwash[1]=downwash[2]=0.;
680 //calculate the mean downwash speed directly beneath the rotor disk
681 float v1bar = Math::sin(inc) *_omega * 0.35 * _diameter * 0.8;
682 //0.35 * d = 0.7 *r, a good position to calcualte the mean downwashd
683 //0.8 the slip of the rotor.
685 //calculate the time the wind needed from thr rotor to here
686 if (v1bar< 1) v1bar = 1;
687 float time=dist/v1bar;
689 //calculate the pos2rotor, where the rotor was, "time" ago
690 Math::mul3(time,v_heli,tmp);
691 Math::sub3(pos2rotor,tmp,pos2rotor);
693 //and again calculate dist
694 dist=Math::dot3(pos2rotor,_normal_with_yaw_roll);
695 //missing the normal is offen not pointing to the normal of the rotor
696 //disk. Rotate the normal by yaw and tilt angle
700 if (dist<0) // we are not in the downwash region
702 downwash[0]=downwash[1]=downwash[2]=0.;
705 //of course this could be done in a runge kutta integrator, but it's such
706 //a approximation that I beleave, it would'nt be more realistic
708 //calculate the dist to the rotor-axis
709 Math::cross3(pos2rotor,_normal_with_yaw_roll,tmp);
710 float r= Math::mag3(tmp);
711 //calculate incidence at r;
712 float rel_r = r *2 /_diameter;
713 float inc_r = _collective+_twist * r /_diameter * 2
714 - _twist*_rel_len_where_incidence_is_measured;
716 //calculate the downwash speed directly beneath the rotor disk
719 v1 = Math::sin(inc_r) *_omega * r * 0.8;
721 //calcualte the downwash speed in a distance "dist" to the rotor disc,
722 //for large dist. The speed is assumed do follow a gausian distribution
723 //with sigma increasing with dist^2:
724 //sigma is assumed to be half of the rotor diameter directly beneath the
725 //disc and is assumed to the rotor diameter at dist = (diameter * sqrt(2))
727 float sigma=_diameter/2 + dist * dist / _diameter /4.;
728 float v2 = v1bar*_diameter/ (Math::sqrt(2 * pi) * sigma)
729 * Math::pow(2.7183,-.5*r*r/(sigma*sigma))*_diameter/2/sigma;
731 //calculate the weight of the two downwash velocities.
732 //Directly beneath the disc it is v1, far away it is v2
733 float g = Math::pow(2.7183,-2*dist/_diameter);
734 //at dist = rotor radius it is assumed to be 1/e * v1 + (1-1/e)* v2
736 float v = g * v1 + (1-g) * v2;
737 Math::mul3(-v,_normal_with_yaw_roll,downwash);
738 //the downwash is calculated in the opposite direction of the normal
741 void Rotor::compile()
743 // Have we already been compiled?
744 if(_rotorparts.size() != 0) return;
746 //rotor is divided into 4 pointlike parts
748 SG_LOG(SG_FLIGHT, SG_DEBUG, "debug: e "
749 << _mincyclicele << "..." <<_maxcyclicele << ' '
750 << _mincyclicail << "..." << _maxcyclicail << ' '
751 << _min_pitch << "..." << _max_pitch);
753 _dynamic=_dynamic*(1/ //inverse of the time
754 ( (60/_rotor_rpm)/4 //for rotating 90 deg
755 +(60/_rotor_rpm)/(2*_number_of_blades) //+ meantime a rotorblade
756 //will pass a given point
758 float directions[5][3];
759 //pointing forward, right, ... the 5th is ony for calculation
760 directions[0][0]=_forward[0];
761 directions[0][1]=_forward[1];
762 directions[0][2]=_forward[2];
764 SG_LOG(SG_FLIGHT, SG_DEBUG, "Rotor rotating ccw? " << _ccw);
768 Math::cross3(directions[i-1],_normal,directions[i]);
770 Math::cross3(_normal,directions[i-1],directions[i]);
771 Math::unit3(directions[i],directions[i]);
773 Math::set3(directions[4],directions[0]);
774 float rotorpartmass = _weight_per_blade*_number_of_blades/4*.453;
775 //was pounds -> now kg
777 _torque_of_inertia = 1/12. * ( 4 * rotorpartmass) * _diameter
778 * _diameter * _rel_blade_center * _rel_blade_center /(0.5*0.5);
779 float speed=_rotor_rpm/60*_diameter*_rel_blade_center*pi;
780 float lentocenter=_diameter*_rel_blade_center*0.5;
781 float lentoforceattac=_diameter*_rel_len_hinge*0.5;
782 float zentforce=rotorpartmass*speed*speed/lentocenter;
783 float pitchaforce=_force_at_pitch_a/4*.453*9.81;
784 //was pounds of force, now N, devided by 4 (so its now per rotorpart)
786 float torque0=0,torquemax=0,torqueb=0;
787 float omega=_rotor_rpm/60*2*pi;
789 float omega0=omega*Math::sqrt(1/(1-_rel_len_hinge));
790 _delta*=pitchaforce/(_pitch_a*omega*lentocenter*2*rotorpartmass);
792 float phi=Math::atan2(2*omega*_delta,omega0*omega0-omega*omega);
793 float relamp=omega*omega/(2*_delta*Math::sqrt(sqr(omega0*omega0-omega*omega)
794 +4*_delta*_delta*omega*omega));
797 torque0=_power_at_pitch_0/4*1000/omega;
798 // f*r=p/w ; p=f*s/t; r=s/t/w ; r*w*t = s
799 torqueb=_power_at_pitch_b/4*1000/omega;
800 torquemax=_power_at_pitch_b/4*1000/omega/_pitch_b*_max_pitch;
810 SG_LOG(SG_FLIGHT, SG_DEBUG,
811 "spd: " << setprecision(8) << speed
812 << " lentoc: " << lentocenter
813 << " dia: " << _diameter
814 << " rbl: " << _rel_blade_center
815 << " hing: " << _rel_len_hinge
816 << " lfa: " << lentoforceattac);
818 SG_LOG(SG_FLIGHT, SG_DEBUG,
819 "tq: " << setprecision(8) << torque0 << ".." << torquemax
820 << " d3: " << _delta3);
821 SG_LOG(SG_FLIGHT, SG_DEBUG,
822 "o/o0: " << setprecision(8) << omega/omega0
823 << " phi: " << phi*180/pi
824 << " relamp: " << relamp
825 << " delta: " <<_delta);
830 float lpos[3],lforceattac[3],lspeed[3],dirzentforce[3];
832 Math::mul3(lentocenter,directions[i],lpos);
833 Math::add3(lpos,_base,lpos);
834 Math::mul3(lentoforceattac,directions[i+1],lforceattac);
835 //i+1: +90deg (gyro)!!!
837 Math::add3(lforceattac,_base,lforceattac);
838 Math::mul3(speed,directions[i+1],lspeed);
839 Math::mul3(1,directions[i+1],dirzentforce);
841 float maxcyclic=(i&1)?_maxcyclicele:_maxcyclicail;
842 float mincyclic=(i&1)?_mincyclicele:_mincyclicail;
844 Rotorpart* rp=rps[i]=newRotorpart(lpos, lforceattac, _normal,
845 lspeed,dirzentforce,zentforce,pitchaforce, _max_pitch,_min_pitch,
846 mincyclic,maxcyclic,_delta3,rotorpartmass,_translift,
847 _rel_len_hinge,lentocenter);
848 rp->setAlphaoutput(_alphaoutput[i&1?i:(_ccw?i^2:i)],0);
849 rp->setAlphaoutput(_alphaoutput[4+(i&1?i:(_ccw?i^2:i))],1+(i>1));
851 rp->setTorque(torquemax,torque0);
852 rp->setRelamp(relamp);
853 rp->setDirectionofRotorPart(directions[i]);
854 rp->setTorqueOfInertia(_torque_of_inertia/4.);
858 rps[i]->setlastnextrp(rps[(i+3)%4],rps[(i+1)%4],rps[(i+2)%4]);
862 rps[i]->setCompiled();
864 float lift[4],torque[4], v_wind[3];
865 v_wind[0]=v_wind[1]=v_wind[2]=0;
866 rps[0]->setOmega(_omegan);
868 if (_airfoil_lift_coefficient==0)
870 //calculate the lift and drag coefficients now
874 rps[0]->calculateAlpha(v_wind,rho_null,0,0,0,&(torque[0]),&(lift[0]));
880 rps[0]->calculateAlpha(v_wind,rho_null,0,0,0,&(torque[1]),&(lift[1]));
886 rps[0]->calculateAlpha(v_wind,rho_null,_pitch_b,0,0,&(torque[2]),&(lift[2]));
892 rps[0]->calculateAlpha(v_wind,rho_null,_pitch_b,0,0,&(torque[3]),&(lift[3]));
897 _dragcoef1=torque0/torque[1];
898 _dragcoef0=(torqueb-_dragcoef1*torque[3])/torque[2];
902 _dragcoef1=(torque0/torque[0]-torqueb/torque[2])
903 /(torque[1]/torque[0]-torque[3]/torque[2]);
904 _dragcoef0=(torqueb-_dragcoef1*torque[3])/torque[2];
908 rps[0]->calculateAlpha(v_wind,rho_null,_pitch_a,0,0,
909 &(torque[0]),&(lift[0])); //max_pitch a
910 _liftcoef = pitchaforce/lift[0];
914 _liftcoef=_airfoil_lift_coefficient/4*_number_of_blades;
915 _dragcoef0=_airfoil_drag_coefficient0/4*_number_of_blades;
916 _dragcoef1=_airfoil_drag_coefficient1/4*_number_of_blades;
920 rps[0]->calculateAlpha(v_wind,rho_null,_pitch_a,0,0,
921 &(torque[0]),&(lift[0])); //pitch a
922 rps[0]->calculateAlpha(v_wind,rho_null,_pitch_b,0,0,
923 &(torque[1]),&(lift[1])); //pitch b
924 rps[0]->calculateAlpha(v_wind,rho_null,0,0,0,
925 &(torque[3]),&(lift[3])); //pitch 0
926 SG_LOG(SG_FLIGHT, SG_DEBUG,
927 "Rotor: coefficients for airfoil:" << endl << setprecision(6)
928 << " drag0: " << _dragcoef0*4/_number_of_blades
929 << " drag1: " << _dragcoef1*4/_number_of_blades
930 << " lift: " << _liftcoef*4/_number_of_blades
932 << "at 10 deg:" << endl
933 << "drag: " << (Math::sin(10./180*pi)*_dragcoef1+_dragcoef0)
935 << "lift: " << Math::sin(10./180*pi)*_liftcoef*4/_number_of_blades
937 << "Some results (Pitch [degree], Power [kW], Lift [N]" << endl
938 << 0.0f << "deg " << Math::abs(torque[3]*4*_omegan/1000) << "kW "
940 << _pitch_a*180/pi << "deg " << Math::abs(torque[0]*4*_omegan/1000)
941 << "kW " << lift[0] << endl
942 << _pitch_b*180/pi << "deg " << Math::abs(torque[1]*4*_omegan/1000)
943 << "kW " << lift[1] << endl << endl);
948 Rotorpart* Rotor::newRotorpart(float* pos, float *posforceattac, float *normal,
949 float* speed,float *dirzentforce, float zentforce,float maxpitchforce,
950 float maxpitch, float minpitch, float mincyclic,float maxcyclic,
951 float delta3,float mass,float translift,float rellenhinge,float len)
953 Rotorpart *r = new Rotorpart();
955 r->setNormal(normal);
956 r->setZentipetalForce(zentforce);
957 r->setPositionForceAttac(posforceattac);
959 r->setDirectionofZentipetalforce(dirzentforce);
960 r->setMaxpitch(maxpitch);
961 r->setMinpitch(minpitch);
962 r->setMaxcyclic(maxcyclic);
963 r->setMincyclic(mincyclic);
964 r->setDelta3(delta3);
965 r->setDynamic(_dynamic);
966 r->setTranslift(_translift);
969 r->setRelLenHinge(rellenhinge);
970 r->setOmegaN(_omegan);
971 r->setAlpha0(_alpha0);
972 r->setAlphamin(_alphamin);
973 r->setAlphamax(_alphamax);
974 r->setAlpha0factor(_alpha0factor);
976 r->setDiameter(_diameter);
978 #define p(a) r->setParameter(#a,_##a);
980 p(number_of_segments)
981 p(rel_len_where_incidence_is_measured)
982 p(rel_len_blade_start)
985 SG_LOG(SG_FLIGHT, SG_DEBUG, setprecision(8)
987 << pos[0] << ' ' << pos[1] << ' ' << pos[2]
989 << posforceattac[0] << ' ' << posforceattac[1] << ' '
990 << posforceattac[2] << ')');
991 SG_LOG(SG_FLIGHT, SG_DEBUG, setprecision(8)
993 << normal[0] << ' ' << normal[1] << ' ' << normal[2]
995 << speed[0] << ' ' << speed[1] << ' ' << speed[2] << ')');
996 SG_LOG(SG_FLIGHT, SG_DEBUG, setprecision(8)
998 << dirzentforce[0] << ' ' << dirzentforce[1] << dirzentforce[2]
999 << ") zf (" << zentforce << ") mpf (" << maxpitchforce << ')');
1000 SG_LOG(SG_FLIGHT, SG_DEBUG, setprecision(8)
1001 << " pit(" << minpitch << ".." << maxpitch
1002 << ") mcy (" << mincyclic << ".." << maxcyclic
1003 << ") d3 (" << delta3 << ')');
1007 void Rotor::interp(float* v1, float* v2, float frac, float* out)
1009 out[0] = v1[0] + frac*(v2[0]-v1[0]);
1010 out[1] = v1[1] + frac*(v2[1]-v1[1]);
1011 out[2] = v1[2] + frac*(v2[2]-v1[2]);
1014 void Rotorgear::initRotorIteration(float *lrot,float dt)
1018 if (!_rotors.size()) return;
1019 Rotor* r0 = (Rotor*)_rotors.get(0);
1020 omegarel= r0->getOmegaRelNeu();
1021 for(i=0; i<_rotors.size(); i++) {
1022 Rotor* r = (Rotor*)_rotors.get(i);
1023 r->inititeration(dt,omegarel,0,lrot);
1027 void Rotorgear::calcForces(float* torqueOut)
1030 torqueOut[0]=torqueOut[1]=torqueOut[2]=0;
1031 // check,<if the engine can handle the torque of the rotors.
1032 // If not reduce the torque to the fueselage and change rotational
1033 // speed of the rotors instead
1036 float omegarel,omegan;
1037 Rotor* r0 = (Rotor*)_rotors.get(0);
1038 omegarel= r0->getOmegaRel();
1040 float total_torque_of_inertia=0;
1041 float total_torque=0;
1042 for(i=0; i<_rotors.size(); i++) {
1043 Rotor* r = (Rotor*)_rotors.get(i);
1044 omegan=r->getOmegan();
1045 total_torque_of_inertia+=r->getTorqueOfInertia()*omegan*omegan;
1046 //FIXME: this is constant, so this can be done in compile
1048 total_torque+=r->getTorque()*omegan;
1050 float max_torque_of_engine=0;
1053 max_torque_of_engine=_max_power_engine;
1054 float df=1-omegarel;
1055 df/=_engine_prop_factor;
1056 df = Math::clamp(df, 0, 1);
1057 max_torque_of_engine = df * _max_power_engine;
1061 float rel_torque_engine=1;
1062 if (total_torque<=0)
1063 rel_torque_engine=0;
1065 if (max_torque_of_engine>0)
1066 rel_torque_engine=1/max_torque_of_engine*total_torque;
1068 rel_torque_engine=0;
1070 //add the rotor brake
1072 if (r0->_rotorparts.size()) dt=((Rotorpart*)r0->_rotorparts.get(0))->getDt();
1074 float rotor_brake_torque;
1075 rotor_brake_torque=_rotorbrake*_max_power_rotor_brake;
1076 //clamp it to the value you need to stop the rotor
1077 rotor_brake_torque=Math::clamp(rotor_brake_torque,0,
1078 total_torque_of_inertia/dt*omegarel);
1079 max_torque_of_engine-=rotor_brake_torque;
1081 //change the rotation of the rotors
1082 if ((max_torque_of_engine<total_torque) //decreasing rotation
1083 ||((max_torque_of_engine>total_torque)&&(omegarel<1))
1084 //increasing rotation due to engine
1085 ||(total_torque<0) ) //increasing rotation due to autorotation
1087 _ddt_omegarel=(max_torque_of_engine-total_torque)/total_torque_of_inertia;
1088 if(max_torque_of_engine>total_torque)
1090 //check if the acceleration is due to the engine. If yes,
1091 //the engine self limits the accel.
1092 float lim1=-total_torque/total_torque_of_inertia;
1093 //accel. by autorotation
1095 if (lim1<_engine_accell_limit) lim1=_engine_accell_limit;
1096 //if the accel by autorotation greater than the max. engine
1097 //accel, then this is the limit, if not: the engine is the limit
1098 if (_ddt_omegarel>lim1) _ddt_omegarel=lim1;
1100 if (_ddt_omegarel>5.5)_ddt_omegarel=5.5;
1101 //clamp it to avoid overflow. Should never be reached
1102 if (_ddt_omegarel<-5.5)_ddt_omegarel=-5.5;
1104 if (_max_power_engine<0.001) {omegarel=1;_ddt_omegarel=0;}
1105 //for debug: negative or no maxpower will result
1106 //in permanet 100% rotation
1108 omegarel+=dt*_ddt_omegarel;
1110 if (omegarel>2.5) omegarel=2.5;
1111 //clamp it to avoid overflow. Should never be reached
1112 if (omegarel<-.5) omegarel=-.5;
1114 r0->setOmegaRelNeu(omegarel);
1115 //calculate the torque, which is needed to accelerate the rotors.
1116 //Add this additional torque to the body
1117 for(j=0; j<_rotors.size(); j++) {
1118 Rotor* r = (Rotor*)_rotors.get(j);
1119 for(i=0; i<r->_rotorparts.size(); i++) {
1120 float torque_scalar=0;
1121 Rotorpart* rp = (Rotorpart*)r->_rotorparts.get(i);
1123 rp->getAccelTorque(_ddt_omegarel,torque);
1124 Math::add3(torque,torqueOut,torqueOut);
1131 void Rotorgear::addRotor(Rotor* rotor)
1137 float Rotorgear::compile(RigidBody* body)
1140 for(int j=0; j<_rotors.size(); j++) {
1141 Rotor* r = (Rotor*)_rotors.get(j);
1144 for(i=0; i<r->numRotorparts(); i++) {
1145 Rotorpart* rp = (Rotorpart*)r->getRotorpart(i);
1146 float mass = rp->getWeight();
1147 mass = mass * Math::sqrt(mass);
1149 rp->getPosition(pos);
1150 body->addMass(mass, pos);
1157 void Rotorgear::getDownWash(float *pos, float * v_heli, float *downwash)
1160 downwash[0]=downwash[1]=downwash[2]=0;
1161 for(int i=0; i<_rotors.size(); i++) {
1162 Rotor* ro = (Rotor*)_rotors.get(i);
1163 ro->getDownWash(pos,v_heli,tmp);
1164 Math::add3(downwash,tmp,downwash); // + downwash
1168 void Rotorgear::setParameter(char *parametername, float value)
1170 #define p(a,b) if (strcmp(parametername,#a)==0) _##a = (value * (b)); else
1171 p(max_power_engine,1000)
1172 p(engine_prop_factor,1)
1173 p(yasimdragfactor,1)
1174 p(yasimliftfactor,1)
1175 p(max_power_rotor_brake,1000)
1176 p(engine_accell_limit,0.01)
1177 cout << "internal error in parameter set up for rotorgear: '"
1178 << parametername <<"'" << endl;
1182 Rotorgear::Rotorgear()
1187 _max_power_rotor_brake=1;
1188 _max_power_engine=1000*450;
1189 _engine_prop_factor=0.05f;
1193 _engine_accell_limit=0.05f;
1196 Rotorgear::~Rotorgear()
1198 for(int i=0; i<_rotors.size(); i++)
1199 delete (Rotor*)_rotors.get(i);
1202 }; // namespace yasim