1 #include <simgear/debug/logstream.hxx>
5 #include "Rotorpart.hpp"
12 SG_USING_STD(setprecision);
25 const float pi=3.14159;
27 static inline float sqr(float a) { return a * a; }
44 _base[0] = _base[1] = _base[2] = 0;
53 _forward[1]=_forward[2]=0;
54 _max_pitch=13./180*pi;
55 _maxcyclicail=10./180*pi;
56 _maxcyclicele=10./180*pi;
58 _mincyclicail=-10./180*pi;
59 _mincyclicele=-10./180*pi;
60 _min_pitch=-.5/180*pi;
64 _normal[0] = _normal[1] = 0;
66 _normal_with_yaw_roll[0]= _normal_with_yaw_roll[1]=0;
67 _normal_with_yaw_roll[2]=1;
69 _omega=_omegan=_omegarel=_omegarelneu=0;
78 _rellenteeterhinge=0.01;
85 _translift_maxfactor=1.3;
86 _ground_effect_constant=0.1;
87 _vortex_state_lift_factor=0.4;
100 _number_of_segments=1;
102 _rel_len_where_incidence_is_measured=0.7;
103 _torque_of_inertia=1;
107 _airfoil_incidence_no_lift=0;
108 _rel_len_blade_start=0;
109 _airfoil_lift_coefficient=0;
110 _airfoil_drag_coefficient0=0;
111 _airfoil_drag_coefficient1=0;
113 _global_ground[i] = 0;
114 _global_ground[2] = 1;
115 _global_ground[3] = -1e3;
116 _incidence_stall_zero_speed=18*pi/180.;
117 _incidence_stall_half_sonic_speed=14*pi/180.;
118 _lift_factor_stall=0.28;
119 _stall_change_over=2*pi/180.;
120 _drag_factor_stall=8;
125 for (int k=0;k<4;k++)
127 _groundeffectpos[k][i]=0;
128 _ground_effect_altitude=1;
130 _lift_factor=_f_ge=_f_vs=_f_tl=1;
131 _rotor_correction_factor=.65;
137 for(i=0; i<_rotorparts.size(); i++) {
138 Rotorpart* r = (Rotorpart*)_rotorparts.get(i);
143 void Rotor::inititeration(float dt,float omegarel,float ddt_omegarel,float *rot)
148 _omega=_omegan*_omegarel;
149 _ddt_omega=_omegan*ddt_omegarel;
151 for(i=0; i<_rotorparts.size(); i++) {
152 float s = Math::sin(2*pi*i/_number_of_parts);
153 float c = Math::cos(2*pi*i/_number_of_parts);
154 Rotorpart* r = (Rotorpart*)_rotorparts.get(i);
156 r->setDdtOmega(_ddt_omega);
157 r->inititeration(dt,rot);
158 r->setCyclic(_cyclicail*c+_cyclicele*s);
161 //calculate the normal of the rotor disc, for calcualtion of the downwash
162 float side[3],help[3];
163 Math::cross3(_normal,_forward,side);
164 Math::mul3(Math::cos(_yaw)*Math::cos(_roll),_normal,_normal_with_yaw_roll);
166 Math::mul3(Math::sin(_yaw),_forward,help);
167 Math::add3(_normal_with_yaw_roll,help,_normal_with_yaw_roll);
169 Math::mul3(Math::sin(_roll),side,help);
170 Math::add3(_normal_with_yaw_roll,help,_normal_with_yaw_roll);
173 float Rotor::calcStall(float incidence,float speed)
175 float stall_incidence=_incidence_stall_zero_speed
176 +(_incidence_stall_half_sonic_speed
177 -_incidence_stall_zero_speed)*speed/(343./2);
178 //missing: Temeperature dependency of sonic speed
179 incidence = Math::abs(incidence);
180 if (incidence > (90./180.*pi))
181 incidence = pi-incidence;
182 float stall = (incidence-stall_incidence)/_stall_change_over;
183 stall = Math::clamp(stall,0,1);
185 _stall_sum+=stall*speed*speed;
186 _stall_v2sum+=speed*speed;
191 float Rotor::getLiftCoef(float incidence,float speed)
193 float stall=calcStall(incidence,speed);
194 /* the next shold look like this, but this is the inner loop of
195 the rotor simulation. For small angles (and we hav only small
196 angles) the first order approximation works well
197 float c1= Math::sin(incidence-_airfoil_incidence_no_lift)*_liftcoef;
198 for c2 we would need higher order, because in stall the angle can be large
201 if (incidence > (pi/2))
203 else if (incidence <-(pi/2))
207 float c1= (i2-_airfoil_incidence_no_lift)*_liftcoef;
210 float c2= Math::sin(2*(incidence-_airfoil_incidence_no_lift))
211 *_liftcoef*_lift_factor_stall;
212 return (1-stall)*c1 + stall *c2;
218 float Rotor::getDragCoef(float incidence,float speed)
220 float stall=calcStall(incidence,speed);
221 float c1= (Math::abs(Math::sin(incidence-_airfoil_incidence_no_lift))
222 *_dragcoef1+_dragcoef0);
223 float c2= c1*_drag_factor_stall;
224 return (1-stall)*c1 + stall *c2;
227 int Rotor::getValueforFGSet(int j,char *text,float *f)
229 if (_name[0]==0) return 0;
230 if (4>numRotorparts()) return 0; //compile first!
233 sprintf(text,"/rotors/%s/cone-deg", _name);
234 *f=( ((Rotorpart*)getRotorpart(0))->getrealAlpha()
235 +((Rotorpart*)getRotorpart(1*(_number_of_parts>>2)))->getrealAlpha()
236 +((Rotorpart*)getRotorpart(2*(_number_of_parts>>2)))->getrealAlpha()
237 +((Rotorpart*)getRotorpart(3*(_number_of_parts>>2)))->getrealAlpha()
243 sprintf(text,"/rotors/%s/roll-deg", _name);
244 _roll = ( ((Rotorpart*)getRotorpart(0))->getrealAlpha()
245 -((Rotorpart*)getRotorpart(2*(_number_of_parts>>2)))->getrealAlpha()
252 sprintf(text,"/rotors/%s/yaw-deg", _name);
253 _yaw=( ((Rotorpart*)getRotorpart(1*(_number_of_parts>>2)))->getrealAlpha()
254 -((Rotorpart*)getRotorpart(3*(_number_of_parts>>2)))->getrealAlpha()
261 sprintf(text,"/rotors/%s/rpm", _name);
267 sprintf(text,"/rotors/%s/debug/debugfge",_name);
272 sprintf(text,"/rotors/%s/debug/debugfvs",_name);
277 sprintf(text,"/rotors/%s/debug/debugftl",_name);
282 sprintf(text,"/rotors/%s/debug/vortexstate",_name);
287 sprintf(text,"/rotors/%s/stall",_name);
288 *f=getOverallStall();
292 sprintf(text,"/rotors/%s/torque",_name);
298 if (b>=_number_of_blades)
303 sprintf(text,"/rotors/%s/blade[%i]/%s",
305 w==0?"position-deg":(w==1?"flap-deg":"incidence-deg"));
306 *f=((Rotorpart*)getRotorpart(0))->getPhi()*180/pi
307 +360*b/_number_of_blades*(_ccw?1:-1);
316 rk=Math::clamp(rk,0,1);//Delete this
318 if(w==2) {k+=2;l+=2;}
320 if(w==1) {k+=1;l+=1;}
323 if (w==1) *f=rk*((Rotorpart*) getRotorpart(k*(_number_of_parts>>2)))->getrealAlpha()*180/pi
324 +rl*((Rotorpart*) getRotorpart(l*(_number_of_parts>>2)))->getrealAlpha()*180/pi;
325 else if(w==2) *f=rk*((Rotorpart*)getRotorpart(k*(_number_of_parts>>2)))->getIncidence()*180/pi
326 +rl*((Rotorpart*)getRotorpart(l*(_number_of_parts>>2)))->getIncidence()*180/pi;
331 void Rotorgear::setEngineOn(int value)
336 void Rotor::setAlpha0(float f)
341 void Rotor::setAlphamin(float f)
346 void Rotor::setAlphamax(float f)
351 void Rotor::setAlpha0factor(float f)
356 int Rotor::numRotorparts()
358 return _rotorparts.size();
361 Rotorpart* Rotor::getRotorpart(int n)
363 return ((Rotorpart*)_rotorparts.get(n));
366 int Rotorgear::getEngineon()
371 float Rotor::getTorqueOfInertia()
373 return _torque_of_inertia;
376 void Rotor::setTorque(float f)
381 void Rotor::addTorque(float f)
386 void Rotor::strncpy(char *dest,const char *src,int maxlen)
389 while(src[n]&&n<(maxlen-1))
397 void Rotor::setNormal(float* normal)
400 float invsum,sqrsum=0;
401 for(i=0; i<3; i++) { sqrsum+=normal[i]*normal[i];}
403 invsum=1/Math::sqrt(sqrsum);
408 _normal_with_yaw_roll[i]=_normal[i] = normal[i]*invsum;
412 void Rotor::setForward(float* forward)
415 float invsum,sqrsum=0;
416 for(i=0; i<3; i++) { sqrsum+=forward[i]*forward[i];}
418 invsum=1/Math::sqrt(sqrsum);
421 for(i=0; i<3; i++) { _forward[i] = forward[i]*invsum; }
424 void Rotor::setForceAtPitchA(float force)
426 _force_at_pitch_a=force;
429 void Rotor::setPowerAtPitch0(float value)
431 _power_at_pitch_0=value;
434 void Rotor::setPowerAtPitchB(float value)
436 _power_at_pitch_b=value;
439 void Rotor::setPitchA(float value)
441 _pitch_a=value/180*pi;
444 void Rotor::setPitchB(float value)
446 _pitch_b=value/180*pi;
449 void Rotor::setBase(float* base)
452 for(i=0; i<3; i++) _base[i] = base[i];
455 void Rotor::setMaxCyclicail(float value)
457 _maxcyclicail=value/180*pi;
460 void Rotor::setMaxCyclicele(float value)
462 _maxcyclicele=value/180*pi;
465 void Rotor::setMinCyclicail(float value)
467 _mincyclicail=value/180*pi;
470 void Rotor::setMinCyclicele(float value)
472 _mincyclicele=value/180*pi;
475 void Rotor::setMinCollective(float value)
477 _min_pitch=value/180*pi;
480 void Rotor::setMaxCollective(float value)
482 _max_pitch=value/180*pi;
485 void Rotor::setDiameter(float value)
490 void Rotor::setWeightPerBlade(float value)
492 _weight_per_blade=value;
495 void Rotor::setNumberOfBlades(float value)
497 _number_of_blades=int(value+.5);
500 void Rotor::setRelBladeCenter(float value)
502 _rel_blade_center=value;
505 void Rotor::setDynamic(float value)
510 void Rotor::setDelta3(float value)
515 void Rotor::setDelta(float value)
520 void Rotor::setTranslift(float value)
525 void Rotor::setC2(float value)
530 void Rotor::setStepspersecond(float steps)
532 _stepspersecond=steps;
535 void Rotor::setRPM(float value)
540 void Rotor::setRelLenHinge(float value)
542 _rel_len_hinge=value;
545 void Rotor::setAlphaoutput(int i, const char *text)
547 strncpy(_alphaoutput[i],text,255);
550 void Rotor::setName(const char *text)
552 strncpy(_name,text,256);//256: some space needed for settings
555 void Rotor::setCcw(int ccw)
560 void Rotor::setNotorque(int value)
565 void Rotor::setRelLenTeeterHinge(float f)
567 _rellenteeterhinge=f;
570 void Rotor::setTeeterdamp(float f)
575 void Rotor::setMaxteeterdamp(float f)
580 void Rotor::setGlobalGround(double *global_ground, float* global_vel)
583 for(i=0; i<4; i++) _global_ground[i] = global_ground[i];
586 void Rotor::setParameter(char *parametername, float value)
588 #define p(a,b) if (strcmp(parametername,#a)==0) _##a = (value * (b)); else
590 p(translift_maxfactor,1)
591 p(ground_effect_constant,1)
592 p(vortex_state_lift_factor,1)
600 p(number_of_segments,1)
602 p(rel_len_where_incidence_is_measured,1)
605 p(airfoil_incidence_no_lift,pi/180.)
606 p(rel_len_blade_start,1)
607 p(incidence_stall_zero_speed,pi/180.)
608 p(incidence_stall_half_sonic_speed,pi/180.)
609 p(lift_factor_stall,1)
610 p(stall_change_over,pi/180.)
611 p(drag_factor_stall,1)
612 p(airfoil_lift_coefficient,1)
613 p(airfoil_drag_coefficient0,1)
614 p(airfoil_drag_coefficient1,1)
616 p(rotor_correction_factor,1)
617 SG_LOG(SG_INPUT, SG_ALERT,
618 "internal error in parameter set up for rotor: '" <<
619 parametername <<"'" << endl);
623 float Rotor::getLiftFactor()
628 void Rotorgear::setRotorBrake(float lval)
630 lval = Math::clamp(lval, 0, 1);
634 void Rotor::setCollective(float lval)
636 lval = Math::clamp(lval, -1, 1);
638 for(i=0; i<_rotorparts.size(); i++) {
639 ((Rotorpart*)_rotorparts.get(i))->setCollective(lval);
641 _collective=_min_pitch+(lval+1)/2*(_max_pitch-_min_pitch);
644 void Rotor::setCyclicele(float lval,float rval)
646 lval = Math::clamp(lval, -1, 1);
647 _cyclicele=_mincyclicele+(lval+1)/2*(_maxcyclicele-_mincyclicele);
650 void Rotor::setCyclicail(float lval,float rval)
652 lval = Math::clamp(lval, -1, 1);
654 _cyclicail=-(_mincyclicail+(lval+1)/2*(_maxcyclicail-_mincyclicail));
657 void Rotor::getPosition(float* out)
660 for(i=0; i<3; i++) out[i] = _base[i];
663 void Rotor::calcLiftFactor(float* v, float rho, State *s)
665 //calculates _lift_factor, which is a foactor for the lift of the rotor
667 //- ground effect (_f_ge)
668 //- vortex state (_f_vs)
669 //- translational lift (_f_tl)
674 // Calculate ground effect
675 _f_ge=1+_diameter/_ground_effect_altitude*_ground_effect_constant;
677 // Now calculate translational lift
678 float v_vert = Math::dot3(v,_normal);
680 Math::cross3(v,_normal,help);
681 float v_horiz = Math::mag3(help);
682 _f_tl = ((1-Math::pow(2.7183,-v_horiz/_translift_ve))
683 *(_translift_maxfactor-1)+1)/_translift_maxfactor;
685 _lift_factor = _f_ge*_f_tl*_f_vs;
688 void Rotor::findGroundEffectAltitude(Ground * ground_cb,State *s)
690 _ground_effect_altitude=findGroundEffectAltitude(ground_cb,s,
691 _groundeffectpos[0],_groundeffectpos[1],
692 _groundeffectpos[2],_groundeffectpos[3]);
695 float Rotor::findGroundEffectAltitude(Ground * ground_cb,State *s,
696 float *pos0,float *pos1,float *pos2,float *pos3,
697 int iteration,float a0,float a1,float a2,float a3)
711 Math::add3(p[0],p[2],p[4]);
712 Math::mul3(0.5,p[4],p[4]);//the center
714 float mina=100*_diameter;
716 for (int i=0;i<5;i++)
718 if (a[i]==-1)//in the first iteration,(iteration==0) no height is
719 //passed to this function, these missing values are
723 s->posLocalToGlobal(p[i], pt);
725 // Ask for the ground plane in the global coordinate system
726 double global_ground[4];
728 ground_cb->getGroundPlane(pt, global_ground, global_vel);
729 // find h, the distance to the ground
730 // The ground plane transformed to the local frame.
732 s->planeGlobalToLocal(global_ground, ground);
734 a[i] = ground[3] - Math::dot3(p[i], ground);
735 // Now a[i] is the distance from p[i] to ground
741 if (mina>=10*_diameter)
742 return mina; //the ground effect will be zero
744 //check if further recursion is neccessary
745 //if the height does not differ more than 20%, than
746 //we can return then mean height, if not split
747 //zhe square to four parts and calcualte the height
749 //suma * 0.2 is the mean
750 //0.15 is the maximum allowed difference from the mean
751 //to the height at the center
753 ||(Math::abs(suma*0.2-a[4])<(0.15*0.2*suma*(1<<iteration))))
756 float pc[4][3],ac[4]; //pc[i]=center of pos[i] and pos[(i+1)&3]
757 for (int i=0;i<4;i++)
759 Math::add3(p[i],p[(i+1)&3],pc[i]);
760 Math::mul3(0.5,pc[i],pc[i]);
762 s->posLocalToGlobal(pc[i], pt);
764 // Ask for the ground plane in the global coordinate system
765 double global_ground[4];
767 ground_cb->getGroundPlane(pt, global_ground, global_vel);
768 // find h, the distance to the ground
769 // The ground plane transformed to the local frame.
771 s->planeGlobalToLocal(global_ground, ground);
773 ac[i] = ground[3] - Math::dot3(p[i], ground);
774 // Now ac[i] is the distance from pc[i] to ground
777 (findGroundEffectAltitude(ground_cb,s,p[0],pc[1],p[4],pc[3],
778 iteration+1,a[0],ac[0],a[4],ac[3])
779 +findGroundEffectAltitude(ground_cb,s,p[1],pc[0],p[4],pc[1],
780 iteration+1,a[1],ac[0],a[4],ac[1])
781 +findGroundEffectAltitude(ground_cb,s,p[2],pc[1],p[4],pc[2],
782 iteration+1,a[2],ac[1],a[4],ac[2])
783 +findGroundEffectAltitude(ground_cb,s,p[3],pc[2],p[4],pc[3],
784 iteration+1,a[3],ac[2],a[4],ac[3])
788 void Rotor::getDownWash(float *pos, float *v_heli, float *downwash)
790 float pos2rotor[3],tmp[3];
791 Math::sub3(_base,pos,pos2rotor);
792 float dist=Math::dot3(pos2rotor,_normal_with_yaw_roll);
793 //calculate incidence at 0.7r;
794 float inc = _collective+_twist *0.7
795 - _twist*_rel_len_where_incidence_is_measured;
798 if (dist<0) // we are not in the downwash region
800 downwash[0]=downwash[1]=downwash[2]=0.;
804 //calculate the mean downwash speed directly beneath the rotor disk
805 float v1bar = Math::sin(inc) *_omega * 0.35 * _diameter * 0.8;
806 //0.35 * d = 0.7 *r, a good position to calcualte the mean downwashd
807 //0.8 the slip of the rotor.
809 //calculate the time the wind needed from thr rotor to here
810 if (v1bar< 1) v1bar = 1;
811 float time=dist/v1bar;
813 //calculate the pos2rotor, where the rotor was, "time" ago
814 Math::mul3(time,v_heli,tmp);
815 Math::sub3(pos2rotor,tmp,pos2rotor);
817 //and again calculate dist
818 dist=Math::dot3(pos2rotor,_normal_with_yaw_roll);
819 //missing the normal is offen not pointing to the normal of the rotor
820 //disk. Rotate the normal by yaw and tilt angle
824 if (dist<0) // we are not in the downwash region
826 downwash[0]=downwash[1]=downwash[2]=0.;
829 //of course this could be done in a runge kutta integrator, but it's such
830 //a approximation that I beleave, it would'nt be more realistic
832 //calculate the dist to the rotor-axis
833 Math::cross3(pos2rotor,_normal_with_yaw_roll,tmp);
834 float r= Math::mag3(tmp);
835 //calculate incidence at r;
836 float rel_r = r *2 /_diameter;
837 float inc_r = _collective+_twist * r /_diameter * 2
838 - _twist*_rel_len_where_incidence_is_measured;
840 //calculate the downwash speed directly beneath the rotor disk
843 v1 = Math::sin(inc_r) *_omega * r * 0.8;
845 //calcualte the downwash speed in a distance "dist" to the rotor disc,
846 //for large dist. The speed is assumed do follow a gausian distribution
847 //with sigma increasing with dist^2:
848 //sigma is assumed to be half of the rotor diameter directly beneath the
849 //disc and is assumed to the rotor diameter at dist = (diameter * sqrt(2))
851 float sigma=_diameter/2 + dist * dist / _diameter /4.;
852 float v2 = v1bar*_diameter/ (Math::sqrt(2 * pi) * sigma)
853 * Math::pow(2.7183,-.5*r*r/(sigma*sigma))*_diameter/2/sigma;
855 //calculate the weight of the two downwash velocities.
856 //Directly beneath the disc it is v1, far away it is v2
857 float g = Math::pow(2.7183,-2*dist/_diameter);
858 //at dist = rotor radius it is assumed to be 1/e * v1 + (1-1/e)* v2
860 float v = g * v1 + (1-g) * v2;
861 Math::mul3(-v,_normal_with_yaw_roll,downwash);
862 //the downwash is calculated in the opposite direction of the normal
865 void Rotor::compile()
867 // Have we already been compiled?
868 if(_rotorparts.size() != 0) return;
870 //rotor is divided into _number_of_parts parts
871 //each part is calcualted at _number_of_segments points
874 //and make it a factor of 4
875 _number_of_parts=(int(Math::clamp(_number_of_parts,4,256))>>2)<<2;
877 _dynamic=_dynamic*(1/ //inverse of the time
878 ( (60/_rotor_rpm)/4 //for rotating 90 deg
879 +(60/_rotor_rpm)/(2*_number_of_blades) //+ meantime a rotorblade
880 //will pass a given point
882 float directions[5][3];
883 //pointing forward, right, ... the 5th is ony for calculation
884 directions[0][0]=_forward[0];
885 directions[0][1]=_forward[1];
886 directions[0][2]=_forward[2];
891 Math::cross3(directions[i-1],_normal,directions[i]);
893 Math::cross3(_normal,directions[i-1],directions[i]);
894 Math::unit3(directions[i],directions[i]);
896 Math::set3(directions[4],directions[0]);
897 // now directions[0] is perpendicular to the _normal.and has a length
898 // of 1. if _forward is already normalized and perpendicular to the
899 // normal, directions[0] will be the same
902 Math::mul3(_diameter*0.7,directions[i],_groundeffectpos[i]);
903 Math::add3(_base,_groundeffectpos[i],_groundeffectpos[i]);
905 float rotorpartmass = _weight_per_blade*_number_of_blades/_number_of_parts*.453;
906 //was pounds -> now kg
908 _torque_of_inertia = 1/12. * ( _number_of_parts * rotorpartmass) * _diameter
909 * _diameter * _rel_blade_center * _rel_blade_center /(0.5*0.5);
910 float speed=_rotor_rpm/60*_diameter*_rel_blade_center*pi;
911 float lentocenter=_diameter*_rel_blade_center*0.5;
912 float lentoforceattac=_diameter*_rel_len_hinge*0.5;
913 float zentforce=rotorpartmass*speed*speed/lentocenter;
914 float pitchaforce=_force_at_pitch_a/_number_of_parts*.453*9.81;
915 // was pounds of force, now N, devided by _number_of_parts
916 //(so its now per rotorpart)
918 float torque0=0,torquemax=0,torqueb=0;
919 float omega=_rotor_rpm/60*2*pi;
921 float omega0=omega*Math::sqrt(1/(1-_rel_len_hinge));
922 _delta*=pitchaforce/(_pitch_a*omega*lentocenter*2*rotorpartmass);
924 float phi=Math::atan2(2*omega*_delta,omega0*omega0-omega*omega);
925 float relamp=(omega*omega/(2*_delta*Math::sqrt(sqr(omega0*omega0-omega*omega)
926 +4*_delta*_delta*omega*omega)))*_cyclic_factor;
929 torque0=_power_at_pitch_0/_number_of_parts*1000/omega;
930 // f*r=p/w ; p=f*s/t; r=s/t/w ; r*w*t = s
931 torqueb=_power_at_pitch_b/_number_of_parts*1000/omega;
932 torquemax=_power_at_pitch_b/_number_of_parts*1000/omega/_pitch_b*_max_pitch;
943 for (i=0;i<_number_of_parts;i++)
945 float lpos[3],lforceattac[3],lspeed[3],dirzentforce[3];
946 float s = Math::sin(2*pi*i/_number_of_parts);
947 float c = Math::cos(2*pi*i/_number_of_parts);
948 float direction[3],nextdirection[3],help[3];
949 Math::mul3(c ,directions[0],help);
950 Math::mul3(s ,directions[1],direction);
951 Math::add3(help,direction,direction);
953 Math::mul3(c ,directions[1],help);
954 Math::mul3(s ,directions[2],nextdirection);
955 Math::add3(help,nextdirection,nextdirection);
957 Math::mul3(lentocenter,direction,lpos);
958 Math::add3(lpos,_base,lpos);
959 Math::mul3(lentoforceattac,nextdirection,lforceattac);
960 //nextdirection: +90deg (gyro)!!!
962 Math::add3(lforceattac,_base,lforceattac);
963 Math::mul3(speed,nextdirection,lspeed);
964 Math::mul3(1,nextdirection,dirzentforce);
967 float maxcyclic=(i&1)?_maxcyclicele:_maxcyclicail;
968 float mincyclic=(i&1)?_mincyclicele:_mincyclicail;
970 Rotorpart* rp=rps[i]=newRotorpart(lpos, lforceattac, _normal,
971 lspeed,dirzentforce,zentforce,pitchaforce, _max_pitch,_min_pitch,
972 mincyclic,maxcyclic,_delta3,rotorpartmass,_translift,
973 _rel_len_hinge,lentocenter);
974 int k = i*4/_number_of_parts;
975 rp->setAlphaoutput(_alphaoutput[k&1?k:(_ccw?k^2:k)],0);
976 rp->setAlphaoutput(_alphaoutput[4+(k&1?k:(_ccw?k^2:k))],1+(k>1));
978 rp->setTorque(torquemax,torque0);
979 rp->setRelamp(relamp);
980 rp->setDirectionofRotorPart(direction);
981 rp->setTorqueOfInertia(_torque_of_inertia/_number_of_parts);
983 for (i=0;i<_number_of_parts;i++)
985 rps[i]->setlastnextrp(rps[(i-1+_number_of_parts)%_number_of_parts],
986 rps[(i+1)%_number_of_parts],
987 rps[(i+_number_of_parts/2)%_number_of_parts],
988 rps[(i-_number_of_parts/4+_number_of_parts)%_number_of_parts],
989 rps[(i+_number_of_parts/4)%_number_of_parts]);
991 for (i=0;i<_number_of_parts;i++)
993 rps[i]->setCompiled();
995 float lift[4],torque[4], v_wind[3];
996 v_wind[0]=v_wind[1]=v_wind[2]=0;
997 rps[0]->setOmega(_omegan);
999 if (_airfoil_lift_coefficient==0)
1001 //calculate the lift and drag coefficients now
1005 rps[0]->calculateAlpha(v_wind,rho_null,_pitch_a,0,0,
1006 &(torque[0]),&(lift[0])); //max_pitch a
1007 _liftcoef = pitchaforce/lift[0];
1010 rps[0]->calculateAlpha(v_wind,rho_null,0,0,0,&(torque[0]),&(lift[0]));
1015 rps[0]->calculateAlpha(v_wind,rho_null,0,0,0,&(torque[1]),&(lift[1]));
1020 rps[0]->calculateAlpha(v_wind,rho_null,_pitch_b,0,0,&(torque[2]),&(lift[2]));
1025 rps[0]->calculateAlpha(v_wind,rho_null,_pitch_b,0,0,&(torque[3]),&(lift[3]));
1030 _dragcoef1=torque0/torque[1];
1031 _dragcoef0=(torqueb-_dragcoef1*torque[3])/torque[2];
1035 _dragcoef1=(torque0/torque[0]-torqueb/torque[2])
1036 /(torque[1]/torque[0]-torque[3]/torque[2]);
1037 _dragcoef0=(torqueb-_dragcoef1*torque[3])/torque[2];
1042 _liftcoef=_airfoil_lift_coefficient/_number_of_parts*_number_of_blades;
1043 _dragcoef0=_airfoil_drag_coefficient0/_number_of_parts*_number_of_blades*_c2;
1044 _dragcoef1=_airfoil_drag_coefficient1/_number_of_parts*_number_of_blades*_c2;
1048 rps[0]->calculateAlpha(v_wind,rho_null,_pitch_a,0,0,
1049 &(torque[0]),&(lift[0])); //pitch a
1050 rps[0]->calculateAlpha(v_wind,rho_null,_pitch_b,0,0,
1051 &(torque[1]),&(lift[1])); //pitch b
1052 rps[0]->calculateAlpha(v_wind,rho_null,0,0,0,
1053 &(torque[3]),&(lift[3])); //pitch 0
1054 SG_LOG(SG_GENERAL, SG_INFO,
1055 "Rotor: coefficients for airfoil:" << endl << setprecision(6)
1056 << " drag0: " << _dragcoef0*_number_of_parts/_number_of_blades/_c2
1057 << " drag1: " << _dragcoef1*_number_of_parts/_number_of_blades/_c2
1058 << " lift: " << _liftcoef*_number_of_parts/_number_of_blades
1060 << "at 10 deg:" << endl
1061 << "drag: " << (Math::sin(10./180*pi)*_dragcoef1+_dragcoef0)
1062 *_number_of_parts/_number_of_blades/_c2
1063 << " lift: " << Math::sin(10./180*pi)*_liftcoef*_number_of_parts/_number_of_blades
1065 << "Some results (Pitch [degree], Power [kW], Lift [N])" << endl
1066 << 0.0f << "deg " << Math::abs(torque[3]*_number_of_parts*_omegan/1000) << "kW "
1067 << lift[3]*_number_of_parts << endl
1068 << _pitch_a*180/pi << "deg " << Math::abs(torque[0]*_number_of_parts*_omegan/1000)
1069 << "kW " << lift[0]*_number_of_parts << endl
1070 << _pitch_b*180/pi << "deg " << Math::abs(torque[1]*_number_of_parts*_omegan/1000)
1071 << "kW " << lift[1]*_number_of_parts << endl << endl );
1073 //first calculation of relamp is wrong
1074 //it used pitchaforce, but this was unknown and
1075 //on the default value
1076 _delta*=lift[0]/pitchaforce;
1077 relamp=(omega*omega/(2*_delta*Math::sqrt(sqr(omega0*omega0-omega*omega)
1078 +4*_delta*_delta*omega*omega)))*_cyclic_factor;
1079 for (i=0;i<_number_of_parts;i++)
1081 rps[i]->setRelamp(relamp);
1083 rps[0]->setOmega(0);
1086 std::ostream & operator<<(std::ostream & out, /*const*/ Rotor& r)
1088 #define i(x) << #x << ":" << r.x << endl
1089 #define iv(x) << #x << ":" << r.x[0] << ";" << r.x[1] << ";" <<r.x[2] << ";" << endl
1090 out << "Writing Info on Rotor "
1093 i(_omega) i(_omegan) i(_omegarel) i(_ddt_omega) i(_omegarelneu)
1096 i( _airfoil_incidence_no_lift)
1098 i( _airfoil_lift_coefficient)
1099 i( _airfoil_drag_coefficient0)
1100 i( _airfoil_drag_coefficient1)
1102 i( _number_of_segments)
1103 i( _number_of_parts)
1105 iv( _groundeffectpos[0])iv( _groundeffectpos[1])iv( _groundeffectpos[2])iv( _groundeffectpos[3])
1106 i( _ground_effect_altitude)
1108 iv( _normal_with_yaw_roll)
1111 i( _number_of_blades)
1112 i( _weight_per_blade)
1113 i( _rel_blade_center)
1116 i( _force_at_pitch_a)
1118 i( _power_at_pitch_0)
1119 i( _power_at_pitch_b)
1136 i( _alphamin) i(_alphamax) i(_alpha0) i(_alpha0factor)
1137 i( _teeterdamp) i(_maxteeterdamp)
1138 i( _rellenteeterhinge)
1140 i( _translift_maxfactor)
1141 i( _ground_effect_constant)
1142 i( _vortex_state_lift_factor)
1143 i( _vortex_state_c1)
1144 i( _vortex_state_c2)
1145 i( _vortex_state_c3)
1146 i( _vortex_state_e1)
1147 i( _vortex_state_e2)
1148 i( _vortex_state_e3)
1149 i( _lift_factor) i(_f_ge) i(_f_vs) i(_f_tl)
1154 i( _twist) //outer incidence = inner inner incidence + _twist
1155 i( _rel_len_where_incidence_is_measured)
1156 i( _torque_of_inertia)
1157 i( _rel_len_blade_start)
1158 i( _incidence_stall_zero_speed)
1159 i( _incidence_stall_half_sonic_speed)
1160 i( _lift_factor_stall)
1161 i( _stall_change_over)
1162 i( _drag_factor_stall)
1169 i( _cyclic_factor) <<endl;
1171 for(j=0; j<r._rotorparts.size(); j++) {
1172 out << *((Rotorpart*)r._rotorparts.get(j));
1179 void Rotor:: writeInfo()
1182 std::ostringstream buffer;
1184 FILE*f=fopen("c:\\fgmsvc\\bat\\log.txt","at");
1185 if (!f) f=fopen("c:\\fgmsvc\\bat\\log.txt","wt");
1188 fprintf(f,"%s",(const char *)buffer.str().c_str());
1193 Rotorpart* Rotor::newRotorpart(float* pos, float *posforceattac, float *normal,
1194 float* speed,float *dirzentforce, float zentforce,float maxpitchforce,
1195 float maxpitch, float minpitch, float mincyclic,float maxcyclic,
1196 float delta3,float mass,float translift,float rellenhinge,float len)
1198 Rotorpart *r = new Rotorpart();
1199 r->setPosition(pos);
1200 r->setNormal(normal);
1201 r->setZentipetalForce(zentforce);
1202 r->setPositionForceAttac(posforceattac);
1204 r->setDirectionofZentipetalforce(dirzentforce);
1205 r->setMaxpitch(maxpitch);
1206 r->setMinpitch(minpitch);
1207 r->setMaxcyclic(maxcyclic);
1208 r->setMincyclic(mincyclic);
1209 r->setDelta3(delta3);
1210 r->setDynamic(_dynamic);
1211 r->setTranslift(_translift);
1214 r->setRelLenHinge(rellenhinge);
1215 r->setOmegaN(_omegan);
1216 r->setAlpha0(_alpha0);
1217 r->setAlphamin(_alphamin);
1218 r->setAlphamax(_alphamax);
1219 r->setAlpha0factor(_alpha0factor);
1221 r->setDiameter(_diameter);
1223 #define p(a) r->setParameter(#a,_##a);
1225 p(number_of_segments)
1226 p(rel_len_where_incidence_is_measured)
1227 p(rel_len_blade_start)
1228 p(rotor_correction_factor)
1233 void Rotor::interp(float* v1, float* v2, float frac, float* out)
1235 out[0] = v1[0] + frac*(v2[0]-v1[0]);
1236 out[1] = v1[1] + frac*(v2[1]-v1[1]);
1237 out[2] = v1[2] + frac*(v2[2]-v1[2]);
1240 void Rotorgear::initRotorIteration(float *lrot,float dt)
1244 if (!_rotors.size()) return;
1245 Rotor* r0 = (Rotor*)_rotors.get(0);
1246 omegarel= r0->getOmegaRelNeu();
1247 for(i=0; i<_rotors.size(); i++) {
1248 Rotor* r = (Rotor*)_rotors.get(i);
1249 r->inititeration(dt,omegarel,0,lrot);
1253 void Rotorgear::calcForces(float* torqueOut)
1256 torqueOut[0]=torqueOut[1]=torqueOut[2]=0;
1257 // check,<if the engine can handle the torque of the rotors.
1258 // If not reduce the torque to the fueselage and change rotational
1259 // speed of the rotors instead
1262 float omegarel,omegan;
1263 Rotor* r0 = (Rotor*)_rotors.get(0);
1264 omegarel= r0->getOmegaRel();
1266 float total_torque_of_inertia=0;
1267 float total_torque=0;
1268 for(i=0; i<_rotors.size(); i++) {
1269 Rotor* r = (Rotor*)_rotors.get(i);
1270 omegan=r->getOmegan();
1271 total_torque_of_inertia+=r->getTorqueOfInertia()*omegan*omegan;
1272 //FIXME: this is constant, so this can be done in compile
1274 total_torque+=r->getTorque()*omegan;
1276 float max_torque_of_engine=0;
1279 max_torque_of_engine=_max_power_engine;
1280 float df=1-omegarel;
1281 df/=_engine_prop_factor;
1282 df = Math::clamp(df, 0, 1);
1283 max_torque_of_engine = df * _max_power_engine;
1287 float rel_torque_engine=1;
1288 if (total_torque<=0)
1289 rel_torque_engine=0;
1291 if (max_torque_of_engine>0)
1292 rel_torque_engine=1/max_torque_of_engine*total_torque;
1294 rel_torque_engine=0;
1296 //add the rotor brake and the gear fritcion
1298 if (r0->_rotorparts.size()) dt=((Rotorpart*)r0->_rotorparts.get(0))->getDt();
1300 float rotor_brake_torque;
1301 rotor_brake_torque=_rotorbrake*_max_power_rotor_brake+_rotorgear_friction;
1302 //clamp it to the value you need to stop the rotor
1303 //to avod accelerate the rotor to neagtive rpm:
1304 rotor_brake_torque=Math::clamp(rotor_brake_torque,0,
1305 total_torque_of_inertia/dt*omegarel);
1306 max_torque_of_engine-=rotor_brake_torque;
1308 //change the rotation of the rotors
1309 if ((max_torque_of_engine<total_torque) //decreasing rotation
1310 ||((max_torque_of_engine>total_torque)&&(omegarel<1))
1311 //increasing rotation due to engine
1312 ||(total_torque<0) ) //increasing rotation due to autorotation
1314 _ddt_omegarel=(max_torque_of_engine-total_torque)/total_torque_of_inertia;
1315 if(max_torque_of_engine>total_torque)
1317 //check if the acceleration is due to the engine. If yes,
1318 //the engine self limits the accel.
1319 float lim1=-total_torque/total_torque_of_inertia;
1320 //accel. by autorotation
1322 if (lim1<_engine_accel_limit) lim1=_engine_accel_limit;
1323 //if the accel by autorotation greater than the max. engine
1324 //accel, then this is the limit, if not: the engine is the limit
1325 if (_ddt_omegarel>lim1) _ddt_omegarel=lim1;
1327 if (_ddt_omegarel>5.5)_ddt_omegarel=5.5;
1328 //clamp it to avoid overflow. Should never be reached
1329 if (_ddt_omegarel<-5.5)_ddt_omegarel=-5.5;
1331 if (_max_power_engine<0.001) {omegarel=1;_ddt_omegarel=0;}
1332 //for debug: negative or no maxpower will result
1333 //in permanet 100% rotation
1335 omegarel+=dt*_ddt_omegarel;
1337 if (omegarel>2.5) omegarel=2.5;
1338 //clamp it to avoid overflow. Should never be reached
1339 if (omegarel<-.5) omegarel=-.5;
1341 r0->setOmegaRelNeu(omegarel);
1342 //calculate the torque, which is needed to accelerate the rotors.
1343 //Add this additional torque to the body
1344 for(j=0; j<_rotors.size(); j++) {
1345 Rotor* r = (Rotor*)_rotors.get(j);
1346 for(i=0; i<r->_rotorparts.size(); i++) {
1347 float torque_scalar=0;
1348 Rotorpart* rp = (Rotorpart*)r->_rotorparts.get(i);
1350 rp->getAccelTorque(_ddt_omegarel,torque);
1351 Math::add3(torque,torqueOut,torqueOut);
1355 _total_torque_on_engine=total_torque+_ddt_omegarel*total_torque_of_inertia;
1359 void Rotorgear::addRotor(Rotor* rotor)
1365 float Rotorgear::compile(RigidBody* body)
1368 for(int j=0; j<_rotors.size(); j++) {
1369 Rotor* r = (Rotor*)_rotors.get(j);
1372 for(i=0; i<r->numRotorparts(); i++) {
1373 Rotorpart* rp = (Rotorpart*)r->getRotorpart(i);
1374 float mass = rp->getWeight();
1375 mass = mass * Math::sqrt(mass);
1377 rp->getPosition(pos);
1378 body->addMass(mass, pos);
1385 void Rotorgear::getDownWash(float *pos, float * v_heli, float *downwash)
1388 downwash[0]=downwash[1]=downwash[2]=0;
1389 for(int i=0; i<_rotors.size(); i++) {
1390 Rotor* ro = (Rotor*)_rotors.get(i);
1391 ro->getDownWash(pos,v_heli,tmp);
1392 Math::add3(downwash,tmp,downwash); // + downwash
1396 void Rotorgear::setParameter(char *parametername, float value)
1398 #define p(a,b) if (strcmp(parametername,#a)==0) _##a = (value * (b)); else
1399 p(max_power_engine,1000)
1400 p(engine_prop_factor,1)
1401 p(yasimdragfactor,1)
1402 p(yasimliftfactor,1)
1403 p(max_power_rotor_brake,1000)
1404 p(rotorgear_friction,1000)
1405 p(engine_accel_limit,0.01)
1406 SG_LOG(SG_INPUT, SG_ALERT,
1407 "internal error in parameter set up for rotorgear: '"
1408 << parametername <<"'" << endl);
1411 int Rotorgear::getValueforFGSet(int j,char *text,float *f)
1415 sprintf(text,"/rotors/gear/total-torque");
1416 *f=_total_torque_on_engine;
1420 Rotorgear::Rotorgear()
1425 _max_power_rotor_brake=1;
1426 _rotorgear_friction=1;
1427 _max_power_engine=1000*450;
1428 _engine_prop_factor=0.05f;
1432 _engine_accel_limit=0.05f;
1433 _total_torque_on_engine=0;
1436 Rotorgear::~Rotorgear()
1438 for(int i=0; i<_rotors.size(); i++)
1439 delete (Rotor*)_rotors.get(i);
1442 }; // namespace yasim