1 #include <simgear/debug/logstream.hxx>
5 #include "Rotorpart.hpp"
13 SG_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] = 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;
140 for(i=0; i<_rotorparts.size(); i++) {
141 Rotorpart* r = (Rotorpart*)_rotorparts.get(i);
146 void Rotor::inititeration(float dt,float omegarel,float ddt_omegarel,float *rot)
151 _omega=_omegan*_omegarel;
152 _ddt_omega=_omegan*ddt_omegarel;
154 for(i=0; i<_rotorparts.size(); i++) {
155 float s = Math::sin(float(2*pi*i/_number_of_parts+(_phi-pi/2.)*(_ccw?1:-1)));
156 float c = Math::cos(float(2*pi*i/_number_of_parts+(_phi-pi/2.)*(_ccw?1:-1)));
157 Rotorpart* r = (Rotorpart*)_rotorparts.get(i);
159 r->setDdtOmega(_ddt_omega);
160 r->inititeration(dt,rot);
161 r->setCyclic(_cyclicail*c+_cyclicele*s);
164 //calculate the normal of the rotor disc, for calcualtion of the downwash
165 float side[3],help[3];
166 Math::cross3(_normal,_forward,side);
167 Math::mul3(Math::cos(_yaw)*Math::cos(_roll),_normal,_normal_with_yaw_roll);
169 Math::mul3(Math::sin(_yaw),_forward,help);
170 Math::add3(_normal_with_yaw_roll,help,_normal_with_yaw_roll);
172 Math::mul3(Math::sin(_roll),side,help);
173 Math::add3(_normal_with_yaw_roll,help,_normal_with_yaw_roll);
176 float Rotor::calcStall(float incidence,float speed)
178 float stall_incidence=_incidence_stall_zero_speed
179 +(_incidence_stall_half_sonic_speed
180 -_incidence_stall_zero_speed)*speed/(343./2);
181 //missing: Temeperature dependency of sonic speed
182 incidence = Math::abs(incidence);
183 if (incidence > (90./180.*pi))
184 incidence = pi-incidence;
185 float stall = (incidence-stall_incidence)/_stall_change_over;
186 stall = Math::clamp(stall,0,1);
188 _stall_sum+=stall*speed*speed;
189 _stall_v2sum+=speed*speed;
194 float Rotor::getLiftCoef(float incidence,float speed)
196 float stall=calcStall(incidence,speed);
197 /* the next shold look like this, but this is the inner loop of
198 the rotor simulation. For small angles (and we hav only small
199 angles) the first order approximation works well
200 float c1= Math::sin(incidence-_airfoil_incidence_no_lift)*_liftcoef;
201 for c2 we would need higher order, because in stall the angle can be large
204 if (incidence > (pi/2))
206 else if (incidence <-(pi/2))
210 float c1= (i2-_airfoil_incidence_no_lift)*_liftcoef;
213 float c2= Math::sin(2*(incidence-_airfoil_incidence_no_lift))
214 *_liftcoef*_lift_factor_stall;
215 return (1-stall)*c1 + stall *c2;
221 float Rotor::getDragCoef(float incidence,float speed)
223 float stall=calcStall(incidence,speed);
224 float c1= (Math::abs(Math::sin(incidence-_airfoil_incidence_no_lift))
225 *_dragcoef1+_dragcoef0);
226 float c2= c1*_drag_factor_stall;
227 return (1-stall)*c1 + stall *c2;
230 int Rotor::getValueforFGSet(int j,char *text,float *f)
232 if (_name[0]==0) return 0;
233 if (4>numRotorparts()) return 0; //compile first!
236 sprintf(text,"/rotors/%s/cone-deg", _name);
237 *f=( ((Rotorpart*)getRotorpart(0))->getrealAlpha()
238 +((Rotorpart*)getRotorpart(1*(_number_of_parts>>2)))->getrealAlpha()
239 +((Rotorpart*)getRotorpart(2*(_number_of_parts>>2)))->getrealAlpha()
240 +((Rotorpart*)getRotorpart(3*(_number_of_parts>>2)))->getrealAlpha()
246 sprintf(text,"/rotors/%s/roll-deg", _name);
247 _roll = ( ((Rotorpart*)getRotorpart(0))->getrealAlpha()
248 -((Rotorpart*)getRotorpart(2*(_number_of_parts>>2)))->getrealAlpha()
255 sprintf(text,"/rotors/%s/yaw-deg", _name);
256 _yaw=( ((Rotorpart*)getRotorpart(1*(_number_of_parts>>2)))->getrealAlpha()
257 -((Rotorpart*)getRotorpart(3*(_number_of_parts>>2)))->getrealAlpha()
264 sprintf(text,"/rotors/%s/rpm", _name);
270 sprintf(text,"/rotors/%s/debug/debugfge",_name);
275 sprintf(text,"/rotors/%s/debug/debugfvs",_name);
280 sprintf(text,"/rotors/%s/debug/debugftl",_name);
285 sprintf(text,"/rotors/%s/debug/vortexstate",_name);
290 sprintf(text,"/rotors/%s/stall",_name);
291 *f=getOverallStall();
295 sprintf(text,"/rotors/%s/torque",_name);
301 if (b>=_number_of_blades)
306 sprintf(text,"/rotors/%s/blade[%i]/%s",
308 w==0?"position-deg":(w==1?"flap-deg":"incidence-deg"));
309 *f=((Rotorpart*)getRotorpart(0))->getPhi()*180/pi
310 +360*b/_number_of_blades*(_ccw?1:-1);
319 rk=Math::clamp(rk,0,1);//Delete this
321 if(w==2) {k+=2;l+=2;}
323 if(w==1) {k+=1;l+=1;}
326 if (w==1) *f=rk*((Rotorpart*) getRotorpart(k*(_number_of_parts>>2)))->getrealAlpha()*180/pi
327 +rl*((Rotorpart*) getRotorpart(l*(_number_of_parts>>2)))->getrealAlpha()*180/pi;
328 else if(w==2) *f=rk*((Rotorpart*)getRotorpart(k*(_number_of_parts>>2)))->getIncidence()*180/pi
329 +rl*((Rotorpart*)getRotorpart(l*(_number_of_parts>>2)))->getIncidence()*180/pi;
334 void Rotorgear::setEngineOn(int value)
339 void Rotor::setAlpha0(float f)
344 void Rotor::setAlphamin(float f)
349 void Rotor::setAlphamax(float f)
354 void Rotor::setAlpha0factor(float f)
359 int Rotor::numRotorparts()
361 return _rotorparts.size();
364 Rotorpart* Rotor::getRotorpart(int n)
366 return ((Rotorpart*)_rotorparts.get(n));
369 int Rotorgear::getEngineon()
374 float Rotor::getTorqueOfInertia()
376 return _torque_of_inertia;
379 void Rotor::setTorque(float f)
384 void Rotor::addTorque(float f)
389 void Rotor::strncpy(char *dest,const char *src,int maxlen)
392 while(src[n]&&n<(maxlen-1))
400 void Rotor::setNormal(float* normal)
403 float invsum,sqrsum=0;
404 for(i=0; i<3; i++) { sqrsum+=normal[i]*normal[i];}
406 invsum=1/Math::sqrt(sqrsum);
411 _normal_with_yaw_roll[i]=_normal[i] = normal[i]*invsum;
415 void Rotor::setForward(float* forward)
418 float invsum,sqrsum=0;
419 for(i=0; i<3; i++) { sqrsum+=forward[i]*forward[i];}
421 invsum=1/Math::sqrt(sqrsum);
424 for(i=0; i<3; i++) { _forward[i] = forward[i]*invsum; }
427 void Rotor::setForceAtPitchA(float force)
429 _force_at_pitch_a=force;
432 void Rotor::setPowerAtPitch0(float value)
434 _power_at_pitch_0=value;
437 void Rotor::setPowerAtPitchB(float value)
439 _power_at_pitch_b=value;
442 void Rotor::setPitchA(float value)
444 _pitch_a=value/180*pi;
447 void Rotor::setPitchB(float value)
449 _pitch_b=value/180*pi;
452 void Rotor::setBase(float* base)
455 for(i=0; i<3; i++) _base[i] = base[i];
458 void Rotor::setMaxCyclicail(float value)
460 _maxcyclicail=value/180*pi;
463 void Rotor::setMaxCyclicele(float value)
465 _maxcyclicele=value/180*pi;
468 void Rotor::setMinCyclicail(float value)
470 _mincyclicail=value/180*pi;
473 void Rotor::setMinCyclicele(float value)
475 _mincyclicele=value/180*pi;
478 void Rotor::setMinCollective(float value)
480 _min_pitch=value/180*pi;
483 void Rotor::setMaxCollective(float value)
485 _max_pitch=value/180*pi;
488 void Rotor::setDiameter(float value)
493 void Rotor::setWeightPerBlade(float value)
495 _weight_per_blade=value;
498 void Rotor::setNumberOfBlades(float value)
500 _number_of_blades=int(value+.5);
503 void Rotor::setRelBladeCenter(float value)
505 _rel_blade_center=value;
508 void Rotor::setDynamic(float value)
513 void Rotor::setDelta3(float value)
518 void Rotor::setDelta(float value)
523 void Rotor::setTranslift(float value)
528 void Rotor::setSharedFlapHinge(bool s)
530 _shared_flap_hinge=s;
533 void Rotor::setC2(float value)
538 void Rotor::setStepspersecond(float steps)
540 _stepspersecond=steps;
543 void Rotor::setRPM(float value)
548 void Rotor::setPhiNull(float value)
553 void Rotor::setRelLenHinge(float value)
555 _rel_len_hinge=value;
558 void Rotor::setAlphaoutput(int i, const char *text)
560 strncpy(_alphaoutput[i],text,255);
563 void Rotor::setName(const char *text)
565 strncpy(_name,text,256);//256: some space needed for settings
568 void Rotor::setCcw(int ccw)
573 void Rotor::setNotorque(int value)
578 void Rotor::setRelLenTeeterHinge(float f)
580 _rellenteeterhinge=f;
583 void Rotor::setTeeterdamp(float f)
588 void Rotor::setMaxteeterdamp(float f)
593 void Rotor::setGlobalGround(double *global_ground, float* global_vel)
596 for(i=0; i<4; i++) _global_ground[i] = global_ground[i];
599 void Rotor::setParameter(char *parametername, float value)
601 #define p(a,b) if (strcmp(parametername,#a)==0) _##a = (value * (b)); else
603 p(translift_maxfactor,1)
604 p(ground_effect_constant,1)
605 p(vortex_state_lift_factor,1)
613 p(number_of_segments,1)
615 p(rel_len_where_incidence_is_measured,1)
618 p(airfoil_incidence_no_lift,pi/180.)
619 p(rel_len_blade_start,1)
620 p(incidence_stall_zero_speed,pi/180.)
621 p(incidence_stall_half_sonic_speed,pi/180.)
622 p(lift_factor_stall,1)
623 p(stall_change_over,pi/180.)
624 p(drag_factor_stall,1)
625 p(airfoil_lift_coefficient,1)
626 p(airfoil_drag_coefficient0,1)
627 p(airfoil_drag_coefficient1,1)
629 p(rotor_correction_factor,1)
630 SG_LOG(SG_INPUT, SG_ALERT,
631 "internal error in parameter set up for rotor: '" <<
632 parametername <<"'" << endl);
636 float Rotor::getLiftFactor()
641 void Rotorgear::setRotorBrake(float lval)
643 lval = Math::clamp(lval, 0, 1);
647 void Rotor::setCollective(float lval)
649 lval = Math::clamp(lval, -1, 1);
651 _collective=_min_pitch+(lval+1)/2*(_max_pitch-_min_pitch);
652 for(i=0; i<_rotorparts.size(); i++) {
653 ((Rotorpart*)_rotorparts.get(i))->setCollective(_collective);
657 void Rotor::setCyclicele(float lval,float rval)
659 lval = Math::clamp(lval, -1, 1);
660 _cyclicele=_mincyclicele+(lval+1)/2*(_maxcyclicele-_mincyclicele);
663 void Rotor::setCyclicail(float lval,float rval)
665 lval = Math::clamp(lval, -1, 1);
667 _cyclicail=-(_mincyclicail+(lval+1)/2*(_maxcyclicail-_mincyclicail));
670 void Rotor::getPosition(float* out)
673 for(i=0; i<3; i++) out[i] = _base[i];
676 void Rotor::calcLiftFactor(float* v, float rho, State *s)
678 //calculates _lift_factor, which is a foactor for the lift of the rotor
680 //- ground effect (_f_ge)
681 //- vortex state (_f_vs)
682 //- translational lift (_f_tl)
687 // Calculate ground effect
688 _f_ge=1+_diameter/_ground_effect_altitude*_ground_effect_constant;
690 // Now calculate translational lift
691 float v_vert = Math::dot3(v,_normal);
693 Math::cross3(v,_normal,help);
694 float v_horiz = Math::mag3(help);
695 _f_tl = ((1-Math::pow(2.7183,-v_horiz/_translift_ve))
696 *(_translift_maxfactor-1)+1)/_translift_maxfactor;
698 _lift_factor = _f_ge*_f_tl*_f_vs;
700 //store the gravity direction
701 Glue::geodUp(s->pos, _grav_direction);
704 void Rotor::findGroundEffectAltitude(Ground * ground_cb,State *s)
706 _ground_effect_altitude=findGroundEffectAltitude(ground_cb,s,
707 _groundeffectpos[0],_groundeffectpos[1],
708 _groundeffectpos[2],_groundeffectpos[3]);
711 float Rotor::findGroundEffectAltitude(Ground * ground_cb,State *s,
712 float *pos0,float *pos1,float *pos2,float *pos3,
713 int iteration,float a0,float a1,float a2,float a3)
727 Math::add3(p[0],p[2],p[4]);
728 Math::mul3(0.5,p[4],p[4]);//the center
730 float mina=100*_diameter;
732 for (int i=0;i<5;i++)
734 if (a[i]==-1)//in the first iteration,(iteration==0) no height is
735 //passed to this function, these missing values are
739 s->posLocalToGlobal(p[i], pt);
741 // Ask for the ground plane in the global coordinate system
742 double global_ground[4];
744 ground_cb->getGroundPlane(pt, global_ground, global_vel);
745 // find h, the distance to the ground
746 // The ground plane transformed to the local frame.
748 s->planeGlobalToLocal(global_ground, ground);
750 a[i] = ground[3] - Math::dot3(p[i], ground);
751 // Now a[i] is the distance from p[i] to ground
757 if (mina>=10*_diameter)
758 return mina; //the ground effect will be zero
760 //check if further recursion is neccessary
761 //if the height does not differ more than 20%, than
762 //we can return then mean height, if not split
763 //zhe square to four parts and calcualte the height
765 //suma * 0.2 is the mean
766 //0.15 is the maximum allowed difference from the mean
767 //to the height at the center
769 ||(Math::abs(suma*0.2-a[4])<(0.15*0.2*suma*(1<<iteration))))
772 float pc[4][3],ac[4]; //pc[i]=center of pos[i] and pos[(i+1)&3]
773 for (int i=0;i<4;i++)
775 Math::add3(p[i],p[(i+1)&3],pc[i]);
776 Math::mul3(0.5,pc[i],pc[i]);
778 s->posLocalToGlobal(pc[i], pt);
780 // Ask for the ground plane in the global coordinate system
781 double global_ground[4];
783 ground_cb->getGroundPlane(pt, global_ground, global_vel);
784 // find h, the distance to the ground
785 // The ground plane transformed to the local frame.
787 s->planeGlobalToLocal(global_ground, ground);
789 ac[i] = ground[3] - Math::dot3(p[i], ground);
790 // Now ac[i] is the distance from pc[i] to ground
793 (findGroundEffectAltitude(ground_cb,s,p[0],pc[1],p[4],pc[3],
794 iteration+1,a[0],ac[0],a[4],ac[3])
795 +findGroundEffectAltitude(ground_cb,s,p[1],pc[0],p[4],pc[1],
796 iteration+1,a[1],ac[0],a[4],ac[1])
797 +findGroundEffectAltitude(ground_cb,s,p[2],pc[1],p[4],pc[2],
798 iteration+1,a[2],ac[1],a[4],ac[2])
799 +findGroundEffectAltitude(ground_cb,s,p[3],pc[2],p[4],pc[3],
800 iteration+1,a[3],ac[2],a[4],ac[3])
804 void Rotor::getDownWash(float *pos, float *v_heli, float *downwash)
806 float pos2rotor[3],tmp[3];
807 Math::sub3(_base,pos,pos2rotor);
808 float dist=Math::dot3(pos2rotor,_normal_with_yaw_roll);
809 //calculate incidence at 0.7r;
810 float inc = _collective+_twist *0.7
811 - _twist*_rel_len_where_incidence_is_measured;
814 if (dist<0) // we are not in the downwash region
816 downwash[0]=downwash[1]=downwash[2]=0.;
820 //calculate the mean downwash speed directly beneath the rotor disk
821 float v1bar = Math::sin(inc) *_omega * 0.35 * _diameter * 0.8;
822 //0.35 * d = 0.7 *r, a good position to calcualte the mean downwashd
823 //0.8 the slip of the rotor.
825 //calculate the time the wind needed from thr rotor to here
826 if (v1bar< 1) v1bar = 1;
827 float time=dist/v1bar;
829 //calculate the pos2rotor, where the rotor was, "time" ago
830 Math::mul3(time,v_heli,tmp);
831 Math::sub3(pos2rotor,tmp,pos2rotor);
833 //and again calculate dist
834 dist=Math::dot3(pos2rotor,_normal_with_yaw_roll);
835 //missing the normal is offen not pointing to the normal of the rotor
836 //disk. Rotate the normal by yaw and tilt angle
840 if (dist<0) // we are not in the downwash region
842 downwash[0]=downwash[1]=downwash[2]=0.;
845 //of course this could be done in a runge kutta integrator, but it's such
846 //a approximation that I beleave, it would'nt be more realistic
848 //calculate the dist to the rotor-axis
849 Math::cross3(pos2rotor,_normal_with_yaw_roll,tmp);
850 float r= Math::mag3(tmp);
851 //calculate incidence at r;
852 float rel_r = r *2 /_diameter;
853 float inc_r = _collective+_twist * r /_diameter * 2
854 - _twist*_rel_len_where_incidence_is_measured;
856 //calculate the downwash speed directly beneath the rotor disk
859 v1 = Math::sin(inc_r) *_omega * r * 0.8;
861 //calcualte the downwash speed in a distance "dist" to the rotor disc,
862 //for large dist. The speed is assumed do follow a gausian distribution
863 //with sigma increasing with dist^2:
864 //sigma is assumed to be half of the rotor diameter directly beneath the
865 //disc and is assumed to the rotor diameter at dist = (diameter * sqrt(2))
867 float sigma=_diameter/2 + dist * dist / _diameter /4.;
868 float v2 = v1bar*_diameter/ (Math::sqrt(2 * pi) * sigma)
869 * Math::pow(2.7183,-.5*r*r/(sigma*sigma))*_diameter/2/sigma;
871 //calculate the weight of the two downwash velocities.
872 //Directly beneath the disc it is v1, far away it is v2
873 float g = Math::pow(2.7183,-2*dist/_diameter);
874 //at dist = rotor radius it is assumed to be 1/e * v1 + (1-1/e)* v2
876 float v = g * v1 + (1-g) * v2;
877 Math::mul3(-v,_normal_with_yaw_roll,downwash);
878 //the downwash is calculated in the opposite direction of the normal
881 void Rotor::compile()
883 // Have we already been compiled?
884 if(_rotorparts.size() != 0) return;
886 //rotor is divided into _number_of_parts parts
887 //each part is calcualted at _number_of_segments points
890 //and make it a factor of 4
891 _number_of_parts=(int(Math::clamp(_number_of_parts,4,256))>>2)<<2;
893 _dynamic=_dynamic*(1/ //inverse of the time
894 ( (60/_rotor_rpm)/4 //for rotating 90 deg
895 +(60/_rotor_rpm)/(2*_number_of_blades) //+ meantime a rotorblade
896 //will pass a given point
898 float directions[5][3];
899 //pointing forward, right, ... the 5th is ony for calculation
900 directions[0][0]=_forward[0];
901 directions[0][1]=_forward[1];
902 directions[0][2]=_forward[2];
907 Math::cross3(directions[i-1],_normal,directions[i]);
909 Math::cross3(_normal,directions[i-1],directions[i]);
910 Math::unit3(directions[i],directions[i]);
912 Math::set3(directions[4],directions[0]);
913 // now directions[0] is perpendicular to the _normal.and has a length
914 // of 1. if _forward is already normalized and perpendicular to the
915 // normal, directions[0] will be the same
918 Math::mul3(_diameter*0.7,directions[i],_groundeffectpos[i]);
919 Math::add3(_base,_groundeffectpos[i],_groundeffectpos[i]);
921 float rotorpartmass = _weight_per_blade*_number_of_blades/_number_of_parts*.453;
922 //was pounds -> now kg
924 _torque_of_inertia = 1/12. * ( _number_of_parts * rotorpartmass) * _diameter
925 * _diameter * _rel_blade_center * _rel_blade_center /(0.5*0.5);
926 float speed=_rotor_rpm/60*_diameter*_rel_blade_center*pi;
927 float lentocenter=_diameter*_rel_blade_center*0.5;
928 float lentoforceattac=_diameter*_rel_len_hinge*0.5;
929 float zentforce=rotorpartmass*speed*speed/lentocenter;
930 float pitchaforce=_force_at_pitch_a/_number_of_parts*.453*9.81;
931 // was pounds of force, now N, devided by _number_of_parts
932 //(so its now per rotorpart)
934 float torque0=0,torquemax=0,torqueb=0;
935 float omega=_rotor_rpm/60*2*pi;
937 float omega0=omega*Math::sqrt(1/(1-_rel_len_hinge));
938 float delta_theoretical=pitchaforce/(_pitch_a*omega*lentocenter*2*rotorpartmass);
939 _delta*=delta_theoretical;
941 float relamp=(omega*omega/(2*_delta*Math::sqrt(sqr(omega0*omega0-omega*omega)
942 +4*_delta*_delta*omega*omega)))*_cyclic_factor;
943 float relamp_theoretical=(omega*omega/(2*delta_theoretical*Math::sqrt(sqr(omega0*omega0-omega*omega)
944 +4*delta_theoretical*delta_theoretical*omega*omega)))*_cyclic_factor;
945 _phi=Math::acos(_rel_len_hinge);
946 _phi-=Math::atan(_delta3);
949 torque0=_power_at_pitch_0/_number_of_parts*1000/omega;
950 // f*r=p/w ; p=f*s/t; r=s/t/w ; r*w*t = s
951 torqueb=_power_at_pitch_b/_number_of_parts*1000/omega;
952 torquemax=_power_at_pitch_b/_number_of_parts*1000/omega/_pitch_b*_max_pitch;
963 for (i=0;i<_number_of_parts;i++)
965 float lpos[3],lforceattac[3],lspeed[3],dirzentforce[3];
966 float s = Math::sin(2*pi*i/_number_of_parts);
967 float c = Math::cos(2*pi*i/_number_of_parts);
968 float sp = Math::sin(float(2*pi*i/_number_of_parts-pi/2.+_phi));
969 float cp = Math::cos(float(2*pi*i/_number_of_parts-pi/2.+_phi));
970 float direction[3],nextdirection[3],help[3],direction90deg[3];
971 Math::mul3(c ,directions[0],help);
972 Math::mul3(s ,directions[1],direction);
973 Math::add3(help,direction,direction);
975 Math::mul3(c ,directions[1],help);
976 Math::mul3(s ,directions[2],direction90deg);
977 Math::add3(help,direction90deg,direction90deg);
979 Math::mul3(cp ,directions[1],help);
980 Math::mul3(sp ,directions[2],nextdirection);
981 Math::add3(help,nextdirection,nextdirection);
983 Math::mul3(lentocenter,direction,lpos);
984 Math::add3(lpos,_base,lpos);
985 Math::mul3(lentoforceattac,nextdirection,lforceattac);
986 //nextdirection: +90deg (gyro)!!!
988 Math::add3(lforceattac,_base,lforceattac);
989 Math::mul3(speed,direction90deg,lspeed);
990 Math::mul3(1,nextdirection,dirzentforce);
992 Rotorpart* rp=rps[i]=newRotorpart(lpos, lforceattac, _normal,
993 lspeed,dirzentforce,zentforce,pitchaforce,_delta3,rotorpartmass,
994 _translift,_rel_len_hinge,lentocenter);
995 int k = i*4/_number_of_parts;
996 rp->setAlphaoutput(_alphaoutput[k&1?k:(_ccw?k^2:k)],0);
997 rp->setAlphaoutput(_alphaoutput[4+(k&1?k:(_ccw?k^2:k))],1+(k>1));
999 rp->setTorque(torquemax,torque0);
1000 rp->setRelamp(relamp);
1001 rp->setDirectionofRotorPart(direction);
1002 rp->setTorqueOfInertia(_torque_of_inertia/_number_of_parts);
1004 for (i=0;i<_number_of_parts;i++)
1006 rps[i]->setlastnextrp(rps[(i-1+_number_of_parts)%_number_of_parts],
1007 rps[(i+1)%_number_of_parts],
1008 rps[(i+_number_of_parts/2)%_number_of_parts],
1009 rps[(i-_number_of_parts/4+_number_of_parts)%_number_of_parts],
1010 rps[(i+_number_of_parts/4)%_number_of_parts]);
1012 for (i=0;i<_number_of_parts;i++)
1014 rps[i]->setCompiled();
1016 float lift[4],torque[4], v_wind[3];
1017 v_wind[0]=v_wind[1]=v_wind[2]=0;
1018 rps[0]->setOmega(_omegan);
1020 if (_airfoil_lift_coefficient==0)
1022 //calculate the lift and drag coefficients now
1026 rps[0]->calculateAlpha(v_wind,rho_null,_pitch_a,0,0,
1027 &(torque[0]),&(lift[0])); //max_pitch a
1028 _liftcoef = pitchaforce/lift[0];
1031 rps[0]->calculateAlpha(v_wind,rho_null,0,0,0,&(torque[0]),&(lift[0]));
1036 rps[0]->calculateAlpha(v_wind,rho_null,0,0,0,&(torque[1]),&(lift[1]));
1041 rps[0]->calculateAlpha(v_wind,rho_null,_pitch_b,0,0,&(torque[2]),&(lift[2]));
1046 rps[0]->calculateAlpha(v_wind,rho_null,_pitch_b,0,0,&(torque[3]),&(lift[3]));
1051 _dragcoef1=torque0/torque[1];
1052 _dragcoef0=(torqueb-_dragcoef1*torque[3])/torque[2];
1056 _dragcoef1=(torque0/torque[0]-torqueb/torque[2])
1057 /(torque[1]/torque[0]-torque[3]/torque[2]);
1058 _dragcoef0=(torqueb-_dragcoef1*torque[3])/torque[2];
1063 _liftcoef=_airfoil_lift_coefficient/_number_of_parts*_number_of_blades;
1064 _dragcoef0=_airfoil_drag_coefficient0/_number_of_parts*_number_of_blades*_c2;
1065 _dragcoef1=_airfoil_drag_coefficient1/_number_of_parts*_number_of_blades*_c2;
1069 rps[0]->calculateAlpha(v_wind,rho_null,_pitch_a,0,0,
1070 &(torque[0]),&(lift[0])); //pitch a
1071 rps[0]->calculateAlpha(v_wind,rho_null,_pitch_b,0,0,
1072 &(torque[1]),&(lift[1])); //pitch b
1073 rps[0]->calculateAlpha(v_wind,rho_null,0,0,0,
1074 &(torque[3]),&(lift[3])); //pitch 0
1075 SG_LOG(SG_GENERAL, SG_INFO,
1076 "Rotor: coefficients for airfoil:" << endl << setprecision(6)
1077 << " drag0: " << _dragcoef0*_number_of_parts/_number_of_blades/_c2
1078 << " drag1: " << _dragcoef1*_number_of_parts/_number_of_blades/_c2
1079 << " lift: " << _liftcoef*_number_of_parts/_number_of_blades
1081 << "at 10 deg:" << endl
1082 << "drag: " << (Math::sin(10./180*pi)*_dragcoef1+_dragcoef0)
1083 *_number_of_parts/_number_of_blades/_c2
1084 << " lift: " << Math::sin(10./180*pi)*_liftcoef*_number_of_parts/_number_of_blades
1086 << "Some results (Pitch [degree], Power [kW], Lift [N])" << endl
1087 << 0.0f << "deg " << Math::abs(torque[3]*_number_of_parts*_omegan/1000) << "kW "
1088 << lift[3]*_number_of_parts << endl
1089 << _pitch_a*180/pi << "deg " << Math::abs(torque[0]*_number_of_parts*_omegan/1000)
1090 << "kW " << lift[0]*_number_of_parts << endl
1091 << _pitch_b*180/pi << "deg " << Math::abs(torque[1]*_number_of_parts*_omegan/1000)
1092 << "kW " << lift[1]*_number_of_parts << endl << endl );
1094 //first calculation of relamp is wrong
1095 //it used pitchaforce, but this was unknown and
1096 //on the default value
1097 _delta*=lift[0]/pitchaforce;
1098 relamp=(omega*omega/(2*_delta*Math::sqrt(sqr(omega0*omega0-omega*omega)
1099 +4*_delta*_delta*omega*omega)))*_cyclic_factor;
1100 for (i=0;i<_number_of_parts;i++)
1102 rps[i]->setRelamp(relamp);
1104 rps[0]->setOmega(0);
1107 std::ostream & operator<<(std::ostream & out, Rotor& r)
1109 #define i(x) << #x << ":" << r.x << endl
1110 #define iv(x) << #x << ":" << r.x[0] << ";" << r.x[1] << ";" <<r.x[2] << ";" << endl
1111 out << "Writing Info on Rotor "
1114 i(_omega) i(_omegan) i(_omegarel) i(_ddt_omega) i(_omegarelneu)
1117 i( _airfoil_incidence_no_lift)
1119 i( _airfoil_lift_coefficient)
1120 i( _airfoil_drag_coefficient0)
1121 i( _airfoil_drag_coefficient1)
1123 i( _number_of_segments)
1124 i( _number_of_parts)
1126 iv( _groundeffectpos[0])iv( _groundeffectpos[1])iv( _groundeffectpos[2])iv( _groundeffectpos[3])
1127 i( _ground_effect_altitude)
1129 iv( _normal_with_yaw_roll)
1132 i( _number_of_blades)
1133 i( _weight_per_blade)
1134 i( _rel_blade_center)
1137 i( _force_at_pitch_a)
1139 i( _power_at_pitch_0)
1140 i( _power_at_pitch_b)
1157 i( _alphamin) i(_alphamax) i(_alpha0) i(_alpha0factor)
1158 i( _teeterdamp) i(_maxteeterdamp)
1159 i( _rellenteeterhinge)
1161 i( _translift_maxfactor)
1162 i( _ground_effect_constant)
1163 i( _vortex_state_lift_factor)
1164 i( _vortex_state_c1)
1165 i( _vortex_state_c2)
1166 i( _vortex_state_c3)
1167 i( _vortex_state_e1)
1168 i( _vortex_state_e2)
1169 i( _vortex_state_e3)
1170 i( _lift_factor) i(_f_ge) i(_f_vs) i(_f_tl)
1175 i( _twist) //outer incidence = inner inner incidence + _twist
1176 i( _rel_len_where_incidence_is_measured)
1177 i( _torque_of_inertia)
1178 i( _rel_len_blade_start)
1179 i( _incidence_stall_zero_speed)
1180 i( _incidence_stall_half_sonic_speed)
1181 i( _lift_factor_stall)
1182 i( _stall_change_over)
1183 i( _drag_factor_stall)
1190 i( _cyclic_factor) <<endl;
1192 for(j=0; j<r._rotorparts.size(); j++) {
1193 out << *((Rotorpart*)r._rotorparts.get(j));
1200 void Rotor:: writeInfo()
1203 std::ostringstream buffer;
1205 FILE*f=fopen("c:\\fgmsvc\\bat\\log.txt","at");
1206 if (!f) f=fopen("c:\\fgmsvc\\bat\\log.txt","wt");
1209 fprintf(f,"%s",(const char *)buffer.str().c_str());
1214 Rotorpart* Rotor::newRotorpart(float* pos, float *posforceattac, float *normal,
1215 float* speed,float *dirzentforce, float zentforce,float maxpitchforce,
1216 float delta3,float mass,float translift,float rellenhinge,float len)
1218 Rotorpart *r = new Rotorpart();
1219 r->setPosition(pos);
1220 r->setNormal(normal);
1221 r->setZentipetalForce(zentforce);
1222 r->setPositionForceAttac(posforceattac);
1224 r->setDirectionofZentipetalforce(dirzentforce);
1225 r->setDelta3(delta3);
1226 r->setDynamic(_dynamic);
1227 r->setTranslift(_translift);
1230 r->setRelLenHinge(rellenhinge);
1231 r->setSharedFlapHinge(_shared_flap_hinge);
1232 r->setOmegaN(_omegan);
1233 r->setPhi(_phi_null);
1234 r->setAlpha0(_alpha0);
1235 r->setAlphamin(_alphamin);
1236 r->setAlphamax(_alphamax);
1237 r->setAlpha0factor(_alpha0factor);
1239 r->setDiameter(_diameter);
1241 #define p(a) r->setParameter(#a,_##a);
1243 p(number_of_segments)
1244 p(rel_len_where_incidence_is_measured)
1245 p(rel_len_blade_start)
1246 p(rotor_correction_factor)
1251 void Rotor::interp(float* v1, float* v2, float frac, float* out)
1253 out[0] = v1[0] + frac*(v2[0]-v1[0]);
1254 out[1] = v1[1] + frac*(v2[1]-v1[1]);
1255 out[2] = v1[2] + frac*(v2[2]-v1[2]);
1258 void Rotorgear::initRotorIteration(float *lrot,float dt)
1262 if (!_rotors.size()) return;
1263 Rotor* r0 = (Rotor*)_rotors.get(0);
1264 omegarel= r0->getOmegaRelNeu();
1265 for(i=0; i<_rotors.size(); i++) {
1266 Rotor* r = (Rotor*)_rotors.get(i);
1267 r->inititeration(dt,omegarel,0,lrot);
1271 void Rotorgear::calcForces(float* torqueOut)
1274 torqueOut[0]=torqueOut[1]=torqueOut[2]=0;
1275 // check,<if the engine can handle the torque of the rotors.
1276 // If not reduce the torque to the fueselage and change rotational
1277 // speed of the rotors instead
1280 float omegarel,omegan;
1281 Rotor* r0 = (Rotor*)_rotors.get(0);
1282 omegarel= r0->getOmegaRel();
1284 float total_torque_of_inertia=0;
1285 float total_torque=0;
1286 for(i=0; i<_rotors.size(); i++) {
1287 Rotor* r = (Rotor*)_rotors.get(i);
1288 omegan=r->getOmegan();
1289 total_torque_of_inertia+=r->getTorqueOfInertia()*omegan*omegan;
1290 //FIXME: this is constant, so this can be done in compile
1292 total_torque+=r->getTorque()*omegan;
1294 float max_torque_of_engine=0;
1297 max_torque_of_engine=_max_power_engine;
1298 float df=1-omegarel;
1299 df/=_engine_prop_factor;
1300 df = Math::clamp(df, 0, 1);
1301 max_torque_of_engine = df * _max_power_engine;
1305 float rel_torque_engine=1;
1306 if (total_torque<=0)
1307 rel_torque_engine=0;
1309 if (max_torque_of_engine>0)
1310 rel_torque_engine=1/max_torque_of_engine*total_torque;
1312 rel_torque_engine=0;
1314 //add the rotor brake and the gear fritcion
1316 if (r0->_rotorparts.size()) dt=((Rotorpart*)r0->_rotorparts.get(0))->getDt();
1318 float rotor_brake_torque;
1319 rotor_brake_torque=_rotorbrake*_max_power_rotor_brake+_rotorgear_friction;
1320 //clamp it to the value you need to stop the rotor
1321 //to avod accelerate the rotor to neagtive rpm:
1322 rotor_brake_torque=Math::clamp(rotor_brake_torque,0,
1323 total_torque_of_inertia/dt*omegarel);
1324 max_torque_of_engine-=rotor_brake_torque;
1326 //change the rotation of the rotors
1327 if ((max_torque_of_engine<total_torque) //decreasing rotation
1328 ||((max_torque_of_engine>total_torque)&&(omegarel<1))
1329 //increasing rotation due to engine
1330 ||(total_torque<0) ) //increasing rotation due to autorotation
1332 _ddt_omegarel=(max_torque_of_engine-total_torque)/total_torque_of_inertia;
1333 if(max_torque_of_engine>total_torque)
1335 //check if the acceleration is due to the engine. If yes,
1336 //the engine self limits the accel.
1337 float lim1=-total_torque/total_torque_of_inertia;
1338 //accel. by autorotation
1340 if (lim1<_engine_accel_limit) lim1=_engine_accel_limit;
1341 //if the accel by autorotation greater than the max. engine
1342 //accel, then this is the limit, if not: the engine is the limit
1343 if (_ddt_omegarel>lim1) _ddt_omegarel=lim1;
1345 if (_ddt_omegarel>5.5)_ddt_omegarel=5.5;
1346 //clamp it to avoid overflow. Should never be reached
1347 if (_ddt_omegarel<-5.5)_ddt_omegarel=-5.5;
1349 if (_max_power_engine<0.001) {omegarel=1;_ddt_omegarel=0;}
1350 //for debug: negative or no maxpower will result
1351 //in permanet 100% rotation
1353 omegarel+=dt*_ddt_omegarel;
1355 if (omegarel>2.5) omegarel=2.5;
1356 //clamp it to avoid overflow. Should never be reached
1357 if (omegarel<-.5) omegarel=-.5;
1359 r0->setOmegaRelNeu(omegarel);
1360 //calculate the torque, which is needed to accelerate the rotors.
1361 //Add this additional torque to the body
1362 for(j=0; j<_rotors.size(); j++) {
1363 Rotor* r = (Rotor*)_rotors.get(j);
1364 for(i=0; i<r->_rotorparts.size(); i++) {
1365 float torque_scalar=0;
1366 Rotorpart* rp = (Rotorpart*)r->_rotorparts.get(i);
1368 rp->getAccelTorque(_ddt_omegarel,torque);
1369 Math::add3(torque,torqueOut,torqueOut);
1373 _total_torque_on_engine=total_torque+_ddt_omegarel*total_torque_of_inertia;
1377 void Rotorgear::addRotor(Rotor* rotor)
1383 void Rotorgear::compile()
1386 for(int j=0; j<_rotors.size(); j++) {
1387 Rotor* r = (Rotor*)_rotors.get(j);
1392 void Rotorgear::getDownWash(float *pos, float * v_heli, float *downwash)
1395 downwash[0]=downwash[1]=downwash[2]=0;
1396 for(int i=0; i<_rotors.size(); i++) {
1397 Rotor* ro = (Rotor*)_rotors.get(i);
1398 ro->getDownWash(pos,v_heli,tmp);
1399 Math::add3(downwash,tmp,downwash); // + downwash
1403 void Rotorgear::setParameter(char *parametername, float value)
1405 #define p(a,b) if (strcmp(parametername,#a)==0) _##a = (value * (b)); else
1406 p(max_power_engine,1000)
1407 p(engine_prop_factor,1)
1408 p(yasimdragfactor,1)
1409 p(yasimliftfactor,1)
1410 p(max_power_rotor_brake,1000)
1411 p(rotorgear_friction,1000)
1412 p(engine_accel_limit,0.01)
1413 SG_LOG(SG_INPUT, SG_ALERT,
1414 "internal error in parameter set up for rotorgear: '"
1415 << parametername <<"'" << endl);
1418 int Rotorgear::getValueforFGSet(int j,char *text,float *f)
1422 sprintf(text,"/rotors/gear/total-torque");
1423 *f=_total_torque_on_engine;
1427 Rotorgear::Rotorgear()
1432 _max_power_rotor_brake=1;
1433 _rotorgear_friction=1;
1434 _max_power_engine=1000*450;
1435 _engine_prop_factor=0.05f;
1439 _engine_accel_limit=0.05f;
1440 _total_torque_on_engine=0;
1443 Rotorgear::~Rotorgear()
1445 for(int i=0; i<_rotors.size(); i++)
1446 delete (Rotor*)_rotors.get(i);
1449 }; // namespace yasim