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] = 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;
145 for(i=0; i<_rotorparts.size(); i++) {
146 Rotorpart* r = (Rotorpart*)_rotorparts.get(i);
149 //untie the properties
152 SGPropertyNode * node = fgGetNode("/rotors", true)->getNode(_name,true);
153 node->untie("balance-ext");
154 node->untie("balance-int");
159 void Rotor::inititeration(float dt,float omegarel,float ddt_omegarel,float *rot)
164 _omega=_omegan*_omegarel;
165 _ddt_omega=_omegan*ddt_omegarel;
167 for(i=0; i<_rotorparts.size(); i++) {
168 float s = Math::sin(float(2*pi*i/_number_of_parts+(_phi-pi/2.)*(_ccw?1:-1)));
169 float c = Math::cos(float(2*pi*i/_number_of_parts+(_phi-pi/2.)*(_ccw?1:-1)));
170 Rotorpart* r = (Rotorpart*)_rotorparts.get(i);
172 r->setDdtOmega(_ddt_omega);
173 r->inititeration(dt,rot);
174 r->setCyclic(_cyclicail*c+_cyclicele*s);
177 //calculate the normal of the rotor disc, for calcualtion of the downwash
178 float side[3],help[3];
179 Math::cross3(_normal,_forward,side);
180 Math::mul3(Math::cos(_yaw)*Math::cos(_roll),_normal,_normal_with_yaw_roll);
182 Math::mul3(Math::sin(_yaw),_forward,help);
183 Math::add3(_normal_with_yaw_roll,help,_normal_with_yaw_roll);
185 Math::mul3(Math::sin(_roll),side,help);
186 Math::add3(_normal_with_yaw_roll,help,_normal_with_yaw_roll);
189 if ((_balance1*_balance2 < 0.97) && (_balance1>-1))
191 _balance1-=(0.97-_balance1*_balance2)*(0.97-_balance1*_balance2)*0.00001;
192 if (_balance1<-1) _balance1=-1;
196 float Rotor::calcStall(float incidence,float speed)
198 float stall_incidence=_incidence_stall_zero_speed
199 +(_incidence_stall_half_sonic_speed
200 -_incidence_stall_zero_speed)*speed/(343./2);
201 //missing: Temeperature dependency of sonic speed
202 incidence = Math::abs(incidence);
203 if (incidence > (90./180.*pi))
204 incidence = pi-incidence;
205 float stall = (incidence-stall_incidence)/_stall_change_over;
206 stall = Math::clamp(stall,0,1);
208 _stall_sum+=stall*speed*speed;
209 _stall_v2sum+=speed*speed;
214 float Rotor::getLiftCoef(float incidence,float speed)
216 float stall=calcStall(incidence,speed);
217 /* the next shold look like this, but this is the inner loop of
218 the rotor simulation. For small angles (and we hav only small
219 angles) the first order approximation works well
220 float c1= Math::sin(incidence-_airfoil_incidence_no_lift)*_liftcoef;
221 for c2 we would need higher order, because in stall the angle can be large
224 if (incidence > (pi/2))
226 else if (incidence <-(pi/2))
230 float c1= (i2-_airfoil_incidence_no_lift)*_liftcoef;
233 float c2= Math::sin(2*(incidence-_airfoil_incidence_no_lift))
234 *_liftcoef*_lift_factor_stall;
235 return (1-stall)*c1 + stall *c2;
241 float Rotor::getDragCoef(float incidence,float speed)
243 float stall=calcStall(incidence,speed);
244 float c1= (Math::abs(Math::sin(incidence-_airfoil_incidence_no_lift))
245 *_dragcoef1+_dragcoef0);
246 float c2= c1*_drag_factor_stall;
247 return (1-stall)*c1 + stall *c2;
250 int Rotor::getValueforFGSet(int j,char *text,float *f)
252 if (_name[0]==0) return 0;
253 if (4>numRotorparts()) return 0; //compile first!
256 sprintf(text,"/rotors/%s/cone-deg", _name);
257 *f=(_balance1>-1)?( ((Rotorpart*)getRotorpart(0))->getrealAlpha()
258 +((Rotorpart*)getRotorpart(1*(_number_of_parts>>2)))->getrealAlpha()
259 +((Rotorpart*)getRotorpart(2*(_number_of_parts>>2)))->getrealAlpha()
260 +((Rotorpart*)getRotorpart(3*(_number_of_parts>>2)))->getrealAlpha()
266 sprintf(text,"/rotors/%s/roll-deg", _name);
267 _roll = ( ((Rotorpart*)getRotorpart(0))->getrealAlpha()
268 -((Rotorpart*)getRotorpart(2*(_number_of_parts>>2)))->getrealAlpha()
270 *f=(_balance1>-1)?_roll *180/pi:0;
275 sprintf(text,"/rotors/%s/yaw-deg", _name);
276 _yaw=( ((Rotorpart*)getRotorpart(1*(_number_of_parts>>2)))->getrealAlpha()
277 -((Rotorpart*)getRotorpart(3*(_number_of_parts>>2)))->getrealAlpha()
279 *f=(_balance1>-1)?_yaw*180/pi:0;
284 sprintf(text,"/rotors/%s/rpm", _name);
285 *f=(_balance1>-1)?_omega/2/pi*60:0;
290 sprintf(text,"/rotors/%s/debug/debugfge",_name);
295 sprintf(text,"/rotors/%s/debug/debugfvs",_name);
300 sprintf(text,"/rotors/%s/debug/debugftl",_name);
305 sprintf(text,"/rotors/%s/balance", _name);
310 sprintf(text,"/rotors/%s/stall",_name);
311 *f=getOverallStall();
315 sprintf(text,"/rotors/%s/torque",_name);
321 if (b>=_number_of_blades)
326 sprintf(text,"/rotors/%s/blade[%i]/%s",
328 w==0?"position-deg":(w==1?"flap-deg":"incidence-deg"));
329 *f=((Rotorpart*)getRotorpart(0))->getPhi()*180/pi
330 +360*b/_number_of_blades*(_ccw?1:-1);
333 if (_balance1<=-1) *f=0;
340 rk=Math::clamp(rk,0,1);//Delete this
342 if(w==2) {k+=2;l+=2;}
344 if(w==1) {k+=1;l+=1;}
347 if (w==1) *f=rk*((Rotorpart*) getRotorpart(k*(_number_of_parts>>2)))->getrealAlpha()*180/pi
348 +rl*((Rotorpart*) getRotorpart(l*(_number_of_parts>>2)))->getrealAlpha()*180/pi;
349 else if(w==2) *f=rk*((Rotorpart*)getRotorpart(k*(_number_of_parts>>2)))->getIncidence()*180/pi
350 +rl*((Rotorpart*)getRotorpart(l*(_number_of_parts>>2)))->getIncidence()*180/pi;
355 void Rotorgear::setEngineOn(int value)
360 void Rotorgear::setRotorEngineMaxRelTorque(float lval)
362 fgGetNode("/rotors/gear/max-rel-torque", true)->setDoubleValue(lval);
365 void Rotorgear::setRotorRelTarget(float lval)
367 fgGetNode("/rotors/gear/target-rel-rpm", true)->setDoubleValue(lval);
370 void Rotor::setAlpha0(float f)
375 void Rotor::setAlphamin(float f)
380 void Rotor::setAlphamax(float f)
385 void Rotor::setAlpha0factor(float f)
390 int Rotor::numRotorparts()
392 return _rotorparts.size();
395 Rotorpart* Rotor::getRotorpart(int n)
397 return ((Rotorpart*)_rotorparts.get(n));
400 int Rotorgear::getEngineon()
405 float Rotor::getTorqueOfInertia()
407 return _torque_of_inertia;
410 void Rotor::setTorque(float f)
415 void Rotor::addTorque(float f)
420 void Rotor::strncpy(char *dest,const char *src,int maxlen)
423 while(src[n]&&n<(maxlen-1))
431 void Rotor::setNormal(float* normal)
434 float invsum,sqrsum=0;
435 for(i=0; i<3; i++) { sqrsum+=normal[i]*normal[i];}
437 invsum=1/Math::sqrt(sqrsum);
442 _normal_with_yaw_roll[i]=_normal[i] = normal[i]*invsum;
446 void Rotor::setForward(float* forward)
449 float invsum,sqrsum=0;
450 for(i=0; i<3; i++) { sqrsum+=forward[i]*forward[i];}
452 invsum=1/Math::sqrt(sqrsum);
455 for(i=0; i<3; i++) { _forward[i] = forward[i]*invsum; }
458 void Rotor::setForceAtPitchA(float force)
460 _force_at_pitch_a=force;
463 void Rotor::setPowerAtPitch0(float value)
465 _power_at_pitch_0=value;
468 void Rotor::setPowerAtPitchB(float value)
470 _power_at_pitch_b=value;
473 void Rotor::setPitchA(float value)
475 _pitch_a=value/180*pi;
478 void Rotor::setPitchB(float value)
480 _pitch_b=value/180*pi;
483 void Rotor::setBase(float* base)
486 for(i=0; i<3; i++) _base[i] = base[i];
489 void Rotor::setMaxCyclicail(float value)
491 _maxcyclicail=value/180*pi;
494 void Rotor::setMaxCyclicele(float value)
496 _maxcyclicele=value/180*pi;
499 void Rotor::setMinCyclicail(float value)
501 _mincyclicail=value/180*pi;
504 void Rotor::setMinCyclicele(float value)
506 _mincyclicele=value/180*pi;
509 void Rotor::setMinCollective(float value)
511 _min_pitch=value/180*pi;
514 void Rotor::setMaxCollective(float value)
516 _max_pitch=value/180*pi;
519 void Rotor::setDiameter(float value)
524 void Rotor::setWeightPerBlade(float value)
526 _weight_per_blade=value;
529 void Rotor::setNumberOfBlades(float value)
531 _number_of_blades=int(value+.5);
534 void Rotor::setRelBladeCenter(float value)
536 _rel_blade_center=value;
539 void Rotor::setDynamic(float value)
544 void Rotor::setDelta3(float value)
549 void Rotor::setDelta(float value)
554 void Rotor::setTranslift(float value)
559 void Rotor::setSharedFlapHinge(bool s)
561 _shared_flap_hinge=s;
564 void Rotor::setBalance(float b)
569 void Rotor::setC2(float value)
574 void Rotor::setStepspersecond(float steps)
576 _stepspersecond=steps;
579 void Rotor::setRPM(float value)
584 void Rotor::setPhiNull(float value)
589 void Rotor::setRelLenHinge(float value)
591 _rel_len_hinge=value;
594 void Rotor::setAlphaoutput(int i, const char *text)
596 strncpy(_alphaoutput[i],text,255);
599 void Rotor::setName(const char *text)
601 strncpy(_name,text,256);//256: some space needed for settings
604 void Rotor::setCcw(int ccw)
609 void Rotor::setNotorque(int value)
614 void Rotor::setRelLenTeeterHinge(float f)
616 _rellenteeterhinge=f;
619 void Rotor::setTeeterdamp(float f)
624 void Rotor::setMaxteeterdamp(float f)
629 void Rotor::setGlobalGround(double *global_ground, float* global_vel)
632 for(i=0; i<4; i++) _global_ground[i] = global_ground[i];
635 void Rotor::setParameter(char *parametername, float value)
637 #define p(a,b) if (strcmp(parametername,#a)==0) _##a = (value * (b)); else
639 p(translift_maxfactor,1)
640 p(ground_effect_constant,1)
641 p(vortex_state_lift_factor,1)
649 p(number_of_segments,1)
651 p(rel_len_where_incidence_is_measured,1)
654 p(airfoil_incidence_no_lift,pi/180.)
655 p(rel_len_blade_start,1)
656 p(incidence_stall_zero_speed,pi/180.)
657 p(incidence_stall_half_sonic_speed,pi/180.)
658 p(lift_factor_stall,1)
659 p(stall_change_over,pi/180.)
660 p(drag_factor_stall,1)
661 p(airfoil_lift_coefficient,1)
662 p(airfoil_drag_coefficient0,1)
663 p(airfoil_drag_coefficient1,1)
665 p(rotor_correction_factor,1)
666 SG_LOG(SG_INPUT, SG_ALERT,
667 "internal error in parameter set up for rotor: '" <<
668 parametername <<"'" << endl);
672 float Rotor::getLiftFactor()
677 void Rotorgear::setRotorBrake(float lval)
679 lval = Math::clamp(lval, 0, 1);
683 void Rotor::setCollective(float lval)
685 lval = Math::clamp(lval, -1, 1);
687 _collective=_min_pitch+(lval+1)/2*(_max_pitch-_min_pitch);
688 for(i=0; i<_rotorparts.size(); i++) {
689 ((Rotorpart*)_rotorparts.get(i))->setCollective(_collective);
693 void Rotor::setCyclicele(float lval,float rval)
695 lval = Math::clamp(lval, -1, 1);
696 _cyclicele=_mincyclicele+(lval+1)/2*(_maxcyclicele-_mincyclicele);
699 void Rotor::setCyclicail(float lval,float rval)
701 lval = Math::clamp(lval, -1, 1);
703 _cyclicail=-(_mincyclicail+(lval+1)/2*(_maxcyclicail-_mincyclicail));
706 void Rotor::getPosition(float* out)
709 for(i=0; i<3; i++) out[i] = _base[i];
712 void Rotor::calcLiftFactor(float* v, float rho, State *s)
714 //calculates _lift_factor, which is a foactor for the lift of the rotor
716 //- ground effect (_f_ge)
717 //- vortex state (_f_vs)
718 //- translational lift (_f_tl)
723 // Calculate ground effect
724 _f_ge=1+_diameter/_ground_effect_altitude*_ground_effect_constant;
726 // Now calculate translational lift
727 float v_vert = Math::dot3(v,_normal);
729 Math::cross3(v,_normal,help);
730 float v_horiz = Math::mag3(help);
731 _f_tl = ((1-Math::pow(2.7183,-v_horiz/_translift_ve))
732 *(_translift_maxfactor-1)+1)/_translift_maxfactor;
734 _lift_factor = _f_ge*_f_tl*_f_vs;
736 //store the gravity direction
737 Glue::geodUp(s->pos, _grav_direction);
740 void Rotor::findGroundEffectAltitude(Ground * ground_cb,State *s)
742 _ground_effect_altitude=findGroundEffectAltitude(ground_cb,s,
743 _groundeffectpos[0],_groundeffectpos[1],
744 _groundeffectpos[2],_groundeffectpos[3]);
745 testForRotorGroundContact(ground_cb,s);
748 void Rotor::testForRotorGroundContact(Ground * ground_cb,State *s)
751 for (i=0;i<_num_ground_contact_pos;i++)
754 s->posLocalToGlobal(_ground_contact_pos[i], pt);
756 // Ask for the ground plane in the global coordinate system
757 double global_ground[4];
759 ground_cb->getGroundPlane(pt, global_ground, global_vel);
760 // find h, the distance to the ground
761 // The ground plane transformed to the local frame.
763 s->planeGlobalToLocal(global_ground, ground);
765 h = ground[3] - Math::dot3(_ground_contact_pos[i], ground);
766 // Now h is the distance from _ground_contact_pos[i] to ground
769 _balance1 -= (-h)/_diameter/_num_ground_contact_pos;
770 _balance1 = (_balance1<-1)?-1:_balance1;
774 float Rotor::findGroundEffectAltitude(Ground * ground_cb,State *s,
775 float *pos0,float *pos1,float *pos2,float *pos3,
776 int iteration,float a0,float a1,float a2,float a3)
790 Math::add3(p[0],p[2],p[4]);
791 Math::mul3(0.5,p[4],p[4]);//the center
793 float mina=100*_diameter;
795 for (int i=0;i<5;i++)
797 if (a[i]==-1)//in the first iteration,(iteration==0) no height is
798 //passed to this function, these missing values are
802 s->posLocalToGlobal(p[i], pt);
804 // Ask for the ground plane in the global coordinate system
805 double global_ground[4];
807 ground_cb->getGroundPlane(pt, global_ground, global_vel);
808 // find h, the distance to the ground
809 // The ground plane transformed to the local frame.
811 s->planeGlobalToLocal(global_ground, ground);
813 a[i] = ground[3] - Math::dot3(p[i], ground);
814 // Now a[i] is the distance from p[i] to ground
820 if (mina>=10*_diameter)
821 return mina; //the ground effect will be zero
823 //check if further recursion is neccessary
824 //if the height does not differ more than 20%, than
825 //we can return then mean height, if not split
826 //zhe square to four parts and calcualte the height
828 //suma * 0.2 is the mean
829 //0.15 is the maximum allowed difference from the mean
830 //to the height at the center
832 ||(Math::abs(suma*0.2-a[4])<(0.15*0.2*suma*(1<<iteration))))
835 float pc[4][3],ac[4]; //pc[i]=center of pos[i] and pos[(i+1)&3]
836 for (int i=0;i<4;i++)
838 Math::add3(p[i],p[(i+1)&3],pc[i]);
839 Math::mul3(0.5,pc[i],pc[i]);
841 s->posLocalToGlobal(pc[i], pt);
843 // Ask for the ground plane in the global coordinate system
844 double global_ground[4];
846 ground_cb->getGroundPlane(pt, global_ground, global_vel);
847 // find h, the distance to the ground
848 // The ground plane transformed to the local frame.
850 s->planeGlobalToLocal(global_ground, ground);
852 ac[i] = ground[3] - Math::dot3(p[i], ground);
853 // Now ac[i] is the distance from pc[i] to ground
856 (findGroundEffectAltitude(ground_cb,s,p[0],pc[1],p[4],pc[3],
857 iteration+1,a[0],ac[0],a[4],ac[3])
858 +findGroundEffectAltitude(ground_cb,s,p[1],pc[0],p[4],pc[1],
859 iteration+1,a[1],ac[0],a[4],ac[1])
860 +findGroundEffectAltitude(ground_cb,s,p[2],pc[1],p[4],pc[2],
861 iteration+1,a[2],ac[1],a[4],ac[2])
862 +findGroundEffectAltitude(ground_cb,s,p[3],pc[2],p[4],pc[3],
863 iteration+1,a[3],ac[2],a[4],ac[3])
867 void Rotor::getDownWash(float *pos, float *v_heli, float *downwash)
869 float pos2rotor[3],tmp[3];
870 Math::sub3(_base,pos,pos2rotor);
871 float dist=Math::dot3(pos2rotor,_normal_with_yaw_roll);
872 //calculate incidence at 0.7r;
873 float inc = _collective+_twist *0.7
874 - _twist*_rel_len_where_incidence_is_measured;
877 if (dist<0) // we are not in the downwash region
879 downwash[0]=downwash[1]=downwash[2]=0.;
883 //calculate the mean downwash speed directly beneath the rotor disk
884 float v1bar = Math::sin(inc) *_omega * 0.35 * _diameter * 0.8;
885 //0.35 * d = 0.7 *r, a good position to calcualte the mean downwashd
886 //0.8 the slip of the rotor.
888 //calculate the time the wind needed from thr rotor to here
889 if (v1bar< 1) v1bar = 1;
890 float time=dist/v1bar;
892 //calculate the pos2rotor, where the rotor was, "time" ago
893 Math::mul3(time,v_heli,tmp);
894 Math::sub3(pos2rotor,tmp,pos2rotor);
896 //and again calculate dist
897 dist=Math::dot3(pos2rotor,_normal_with_yaw_roll);
898 //missing the normal is offen not pointing to the normal of the rotor
899 //disk. Rotate the normal by yaw and tilt angle
903 if (dist<0) // we are not in the downwash region
905 downwash[0]=downwash[1]=downwash[2]=0.;
908 //of course this could be done in a runge kutta integrator, but it's such
909 //a approximation that I beleave, it would'nt be more realistic
911 //calculate the dist to the rotor-axis
912 Math::cross3(pos2rotor,_normal_with_yaw_roll,tmp);
913 float r= Math::mag3(tmp);
914 //calculate incidence at r;
915 float rel_r = r *2 /_diameter;
916 float inc_r = _collective+_twist * r /_diameter * 2
917 - _twist*_rel_len_where_incidence_is_measured;
919 //calculate the downwash speed directly beneath the rotor disk
922 v1 = Math::sin(inc_r) *_omega * r * 0.8;
924 //calcualte the downwash speed in a distance "dist" to the rotor disc,
925 //for large dist. The speed is assumed do follow a gausian distribution
926 //with sigma increasing with dist^2:
927 //sigma is assumed to be half of the rotor diameter directly beneath the
928 //disc and is assumed to the rotor diameter at dist = (diameter * sqrt(2))
930 float sigma=_diameter/2 + dist * dist / _diameter /4.;
931 float v2 = v1bar*_diameter/ (Math::sqrt(2 * pi) * sigma)
932 * Math::pow(2.7183,-.5*r*r/(sigma*sigma))*_diameter/2/sigma;
934 //calculate the weight of the two downwash velocities.
935 //Directly beneath the disc it is v1, far away it is v2
936 float g = Math::pow(2.7183,-2*dist/_diameter);
937 //at dist = rotor radius it is assumed to be 1/e * v1 + (1-1/e)* v2
939 float v = g * v1 + (1-g) * v2;
940 Math::mul3(-v,_normal_with_yaw_roll,downwash);
941 //the downwash is calculated in the opposite direction of the normal
944 void Rotor::compile()
946 // Have we already been compiled?
947 if(_rotorparts.size() != 0) return;
949 //rotor is divided into _number_of_parts parts
950 //each part is calcualted at _number_of_segments points
953 //and make it a factor of 4
954 _number_of_parts=(int(Math::clamp(_number_of_parts,4,256))>>2)<<2;
956 _dynamic=_dynamic*(1/ //inverse of the time
957 ( (60/_rotor_rpm)/4 //for rotating 90 deg
958 +(60/_rotor_rpm)/(2*_number_of_blades) //+ meantime a rotorblade
959 //will pass a given point
961 float directions[5][3];
962 //pointing forward, right, ... the 5th is ony for calculation
963 directions[0][0]=_forward[0];
964 directions[0][1]=_forward[1];
965 directions[0][2]=_forward[2];
970 Math::cross3(directions[i-1],_normal,directions[i]);
972 Math::cross3(_normal,directions[i-1],directions[i]);
973 Math::unit3(directions[i],directions[i]);
975 Math::set3(directions[4],directions[0]);
976 // now directions[0] is perpendicular to the _normal.and has a length
977 // of 1. if _forward is already normalized and perpendicular to the
978 // normal, directions[0] will be the same
979 _num_ground_contact_pos=(_number_of_parts<16)?_number_of_parts:16;
980 for (i=0;i<_num_ground_contact_pos;i++)
983 float s = Math::sin(pi*2*_num_ground_contact_pos/i);
984 float c = Math::cos(pi*2*_num_ground_contact_pos/i);
985 Math::mul3(c*_diameter*0.5,directions[0],_ground_contact_pos[i]);
986 Math::mul3(s*_diameter*0.5,directions[1],help);
987 Math::add3(help,_ground_contact_pos[i],_ground_contact_pos[i]);
988 Math::add3(_base,_ground_contact_pos[i],_ground_contact_pos[i]);
992 Math::mul3(_diameter*0.7,directions[i],_groundeffectpos[i]);
993 Math::add3(_base,_groundeffectpos[i],_groundeffectpos[i]);
995 float rotorpartmass = _weight_per_blade*_number_of_blades/_number_of_parts*.453;
996 //was pounds -> now kg
998 _torque_of_inertia = 1/12. * ( _number_of_parts * rotorpartmass) * _diameter
999 * _diameter * _rel_blade_center * _rel_blade_center /(0.5*0.5);
1000 float speed=_rotor_rpm/60*_diameter*_rel_blade_center*pi;
1001 float lentocenter=_diameter*_rel_blade_center*0.5;
1002 float lentoforceattac=_diameter*_rel_len_hinge*0.5;
1003 float zentforce=rotorpartmass*speed*speed/lentocenter;
1004 float pitchaforce=_force_at_pitch_a/_number_of_parts*.453*9.81;
1005 // was pounds of force, now N, devided by _number_of_parts
1006 //(so its now per rotorpart)
1008 float torque0=0,torquemax=0,torqueb=0;
1009 float omega=_rotor_rpm/60*2*pi;
1011 float omega0=omega*Math::sqrt(1/(1-_rel_len_hinge));
1012 float delta_theoretical=pitchaforce/(_pitch_a*omega*lentocenter*2*rotorpartmass);
1013 _delta*=delta_theoretical;
1015 float relamp=(omega*omega/(2*_delta*Math::sqrt(sqr(omega0*omega0-omega*omega)
1016 +4*_delta*_delta*omega*omega)))*_cyclic_factor;
1017 float relamp_theoretical=(omega*omega/(2*delta_theoretical*Math::sqrt(sqr(omega0*omega0-omega*omega)
1018 +4*delta_theoretical*delta_theoretical*omega*omega)))*_cyclic_factor;
1019 _phi=Math::acos(_rel_len_hinge);
1020 _phi-=Math::atan(_delta3);
1023 torque0=_power_at_pitch_0/_number_of_parts*1000/omega;
1024 // f*r=p/w ; p=f*s/t; r=s/t/w ; r*w*t = s
1025 torqueb=_power_at_pitch_b/_number_of_parts*1000/omega;
1026 torquemax=_power_at_pitch_b/_number_of_parts*1000/omega/_pitch_b*_max_pitch;
1036 Rotorpart* rps[256];
1037 for (i=0;i<_number_of_parts;i++)
1039 float lpos[3],lforceattac[3],lspeed[3],dirzentforce[3];
1040 float s = Math::sin(2*pi*i/_number_of_parts);
1041 float c = Math::cos(2*pi*i/_number_of_parts);
1042 float sp = Math::sin(float(2*pi*i/_number_of_parts-pi/2.+_phi));
1043 float cp = Math::cos(float(2*pi*i/_number_of_parts-pi/2.+_phi));
1044 float direction[3],nextdirection[3],help[3],direction90deg[3];
1045 Math::mul3(c ,directions[0],help);
1046 Math::mul3(s ,directions[1],direction);
1047 Math::add3(help,direction,direction);
1049 Math::mul3(c ,directions[1],help);
1050 Math::mul3(s ,directions[2],direction90deg);
1051 Math::add3(help,direction90deg,direction90deg);
1053 Math::mul3(cp ,directions[1],help);
1054 Math::mul3(sp ,directions[2],nextdirection);
1055 Math::add3(help,nextdirection,nextdirection);
1057 Math::mul3(lentocenter,direction,lpos);
1058 Math::add3(lpos,_base,lpos);
1059 Math::mul3(lentoforceattac,nextdirection,lforceattac);
1060 //nextdirection: +90deg (gyro)!!!
1062 Math::add3(lforceattac,_base,lforceattac);
1063 Math::mul3(speed,direction90deg,lspeed);
1064 Math::mul3(1,nextdirection,dirzentforce);
1066 Rotorpart* rp=rps[i]=newRotorpart(lpos, lforceattac, _normal,
1067 lspeed,dirzentforce,zentforce,pitchaforce,_delta3,rotorpartmass,
1068 _translift,_rel_len_hinge,lentocenter);
1069 int k = i*4/_number_of_parts;
1070 rp->setAlphaoutput(_alphaoutput[k&1?k:(_ccw?k^2:k)],0);
1071 rp->setAlphaoutput(_alphaoutput[4+(k&1?k:(_ccw?k^2:k))],1+(k>1));
1072 _rotorparts.add(rp);
1073 rp->setTorque(torquemax,torque0);
1074 rp->setRelamp(relamp);
1075 rp->setDirectionofRotorPart(direction);
1076 rp->setTorqueOfInertia(_torque_of_inertia/_number_of_parts);
1077 rp->setDirection(2*pi*i/_number_of_parts);
1079 for (i=0;i<_number_of_parts;i++)
1081 rps[i]->setlastnextrp(rps[(i-1+_number_of_parts)%_number_of_parts],
1082 rps[(i+1)%_number_of_parts],
1083 rps[(i+_number_of_parts/2)%_number_of_parts],
1084 rps[(i-_number_of_parts/4+_number_of_parts)%_number_of_parts],
1085 rps[(i+_number_of_parts/4)%_number_of_parts]);
1087 for (i=0;i<_number_of_parts;i++)
1089 rps[i]->setCompiled();
1091 float lift[4],torque[4], v_wind[3];
1092 v_wind[0]=v_wind[1]=v_wind[2]=0;
1093 rps[0]->setOmega(_omegan);
1095 if (_airfoil_lift_coefficient==0)
1097 //calculate the lift and drag coefficients now
1101 rps[0]->calculateAlpha(v_wind,rho_null,_pitch_a,0,0,
1102 &(torque[0]),&(lift[0])); //max_pitch a
1103 _liftcoef = pitchaforce/lift[0];
1106 rps[0]->calculateAlpha(v_wind,rho_null,0,0,0,&(torque[0]),&(lift[0]));
1111 rps[0]->calculateAlpha(v_wind,rho_null,0,0,0,&(torque[1]),&(lift[1]));
1116 rps[0]->calculateAlpha(v_wind,rho_null,_pitch_b,0,0,&(torque[2]),&(lift[2]));
1121 rps[0]->calculateAlpha(v_wind,rho_null,_pitch_b,0,0,&(torque[3]),&(lift[3]));
1126 _dragcoef1=torque0/torque[1];
1127 _dragcoef0=(torqueb-_dragcoef1*torque[3])/torque[2];
1131 _dragcoef1=(torque0/torque[0]-torqueb/torque[2])
1132 /(torque[1]/torque[0]-torque[3]/torque[2]);
1133 _dragcoef0=(torqueb-_dragcoef1*torque[3])/torque[2];
1138 _liftcoef=_airfoil_lift_coefficient/_number_of_parts*_number_of_blades;
1139 _dragcoef0=_airfoil_drag_coefficient0/_number_of_parts*_number_of_blades*_c2;
1140 _dragcoef1=_airfoil_drag_coefficient1/_number_of_parts*_number_of_blades*_c2;
1144 rps[0]->calculateAlpha(v_wind,rho_null,_pitch_a,0,0,
1145 &(torque[0]),&(lift[0])); //pitch a
1146 rps[0]->calculateAlpha(v_wind,rho_null,_pitch_b,0,0,
1147 &(torque[1]),&(lift[1])); //pitch b
1148 rps[0]->calculateAlpha(v_wind,rho_null,0,0,0,
1149 &(torque[3]),&(lift[3])); //pitch 0
1150 SG_LOG(SG_GENERAL, SG_INFO,
1151 "Rotor: coefficients for airfoil:" << endl << setprecision(6)
1152 << " drag0: " << _dragcoef0*_number_of_parts/_number_of_blades/_c2
1153 << " drag1: " << _dragcoef1*_number_of_parts/_number_of_blades/_c2
1154 << " lift: " << _liftcoef*_number_of_parts/_number_of_blades
1156 << "at 10 deg:" << endl
1157 << "drag: " << (Math::sin(10./180*pi)*_dragcoef1+_dragcoef0)
1158 *_number_of_parts/_number_of_blades/_c2
1159 << " lift: " << Math::sin(10./180*pi)*_liftcoef*_number_of_parts/_number_of_blades
1161 << "Some results (Pitch [degree], Power [kW], Lift [N])" << endl
1162 << 0.0f << "deg " << Math::abs(torque[3]*_number_of_parts*_omegan/1000) << "kW "
1163 << lift[3]*_number_of_parts << endl
1164 << _pitch_a*180/pi << "deg " << Math::abs(torque[0]*_number_of_parts*_omegan/1000)
1165 << "kW " << lift[0]*_number_of_parts << endl
1166 << _pitch_b*180/pi << "deg " << Math::abs(torque[1]*_number_of_parts*_omegan/1000)
1167 << "kW " << lift[1]*_number_of_parts << endl << endl );
1169 //first calculation of relamp is wrong
1170 //it used pitchaforce, but this was unknown and
1171 //on the default value
1172 _delta*=lift[0]/pitchaforce;
1173 relamp=(omega*omega/(2*_delta*Math::sqrt(sqr(omega0*omega0-omega*omega)
1174 +4*_delta*_delta*omega*omega)))*_cyclic_factor;
1175 for (i=0;i<_number_of_parts;i++)
1177 rps[i]->setRelamp(relamp);
1179 rps[0]->setOmega(0);
1182 //tie the properties
1183 /* After reset these values are totally wrong. I have to find out why
1184 SGPropertyNode * node = fgGetNode("/rotors", true)->getNode(_name,true);
1185 node->tie("balance_ext",SGRawValuePointer<float>(&_balance2),false);
1186 node->tie("balance_int",SGRawValuePointer<float>(&_balance1));
1190 std::ostream & operator<<(std::ostream & out, Rotor& r)
1192 #define i(x) << #x << ":" << r.x << endl
1193 #define iv(x) << #x << ":" << r.x[0] << ";" << r.x[1] << ";" <<r.x[2] << ";" << endl
1194 out << "Writing Info on Rotor "
1197 i(_omega) i(_omegan) i(_omegarel) i(_ddt_omega) i(_omegarelneu)
1200 i( _airfoil_incidence_no_lift)
1202 i( _airfoil_lift_coefficient)
1203 i( _airfoil_drag_coefficient0)
1204 i( _airfoil_drag_coefficient1)
1206 i( _number_of_segments)
1207 i( _number_of_parts)
1209 iv( _groundeffectpos[0])iv( _groundeffectpos[1])iv( _groundeffectpos[2])iv( _groundeffectpos[3])
1210 i( _ground_effect_altitude)
1212 iv( _normal_with_yaw_roll)
1215 i( _number_of_blades)
1216 i( _weight_per_blade)
1217 i( _rel_blade_center)
1220 i( _force_at_pitch_a)
1222 i( _power_at_pitch_0)
1223 i( _power_at_pitch_b)
1240 i( _alphamin) i(_alphamax) i(_alpha0) i(_alpha0factor)
1241 i( _teeterdamp) i(_maxteeterdamp)
1242 i( _rellenteeterhinge)
1244 i( _translift_maxfactor)
1245 i( _ground_effect_constant)
1246 i( _vortex_state_lift_factor)
1247 i( _vortex_state_c1)
1248 i( _vortex_state_c2)
1249 i( _vortex_state_c3)
1250 i( _vortex_state_e1)
1251 i( _vortex_state_e2)
1252 i( _vortex_state_e3)
1253 i( _lift_factor) i(_f_ge) i(_f_vs) i(_f_tl)
1258 i( _twist) //outer incidence = inner inner incidence + _twist
1259 i( _rel_len_where_incidence_is_measured)
1260 i( _torque_of_inertia)
1261 i( _rel_len_blade_start)
1262 i( _incidence_stall_zero_speed)
1263 i( _incidence_stall_half_sonic_speed)
1264 i( _lift_factor_stall)
1265 i( _stall_change_over)
1266 i( _drag_factor_stall)
1273 i( _cyclic_factor) <<endl;
1275 for(j=0; j<r._rotorparts.size(); j++) {
1276 out << *((Rotorpart*)r._rotorparts.get(j));
1283 void Rotor:: writeInfo()
1286 std::ostringstream buffer;
1288 FILE*f=fopen("c:\\fgmsvc\\bat\\log.txt","at");
1289 if (!f) f=fopen("c:\\fgmsvc\\bat\\log.txt","wt");
1292 fprintf(f,"%s",(const char *)buffer.str().c_str());
1297 Rotorpart* Rotor::newRotorpart(float* pos, float *posforceattac, float *normal,
1298 float* speed,float *dirzentforce, float zentforce,float maxpitchforce,
1299 float delta3,float mass,float translift,float rellenhinge,float len)
1301 Rotorpart *r = new Rotorpart();
1302 r->setPosition(pos);
1303 r->setNormal(normal);
1304 r->setZentipetalForce(zentforce);
1305 r->setPositionForceAttac(posforceattac);
1307 r->setDirectionofZentipetalforce(dirzentforce);
1308 r->setDelta3(delta3);
1309 r->setDynamic(_dynamic);
1310 r->setTranslift(_translift);
1313 r->setRelLenHinge(rellenhinge);
1314 r->setSharedFlapHinge(_shared_flap_hinge);
1315 r->setOmegaN(_omegan);
1316 r->setPhi(_phi_null);
1317 r->setAlpha0(_alpha0);
1318 r->setAlphamin(_alphamin);
1319 r->setAlphamax(_alphamax);
1320 r->setAlpha0factor(_alpha0factor);
1322 r->setDiameter(_diameter);
1324 #define p(a) r->setParameter(#a,_##a);
1326 p(number_of_segments)
1327 p(rel_len_where_incidence_is_measured)
1328 p(rel_len_blade_start)
1329 p(rotor_correction_factor)
1334 void Rotor::interp(float* v1, float* v2, float frac, float* out)
1336 out[0] = v1[0] + frac*(v2[0]-v1[0]);
1337 out[1] = v1[1] + frac*(v2[1]-v1[1]);
1338 out[2] = v1[2] + frac*(v2[2]-v1[2]);
1341 void Rotorgear::initRotorIteration(float *lrot,float dt)
1345 if (!_rotors.size()) return;
1346 Rotor* r0 = (Rotor*)_rotors.get(0);
1347 omegarel= r0->getOmegaRelNeu();
1348 for(i=0; i<_rotors.size(); i++) {
1349 Rotor* r = (Rotor*)_rotors.get(i);
1350 r->inititeration(dt,omegarel,0,lrot);
1354 void Rotorgear::calcForces(float* torqueOut)
1357 torqueOut[0]=torqueOut[1]=torqueOut[2]=0;
1358 // check,<if the engine can handle the torque of the rotors.
1359 // If not reduce the torque to the fueselage and change rotational
1360 // speed of the rotors instead
1363 float omegarel,omegan;
1364 Rotor* r0 = (Rotor*)_rotors.get(0);
1365 omegarel= r0->getOmegaRel();
1367 float total_torque_of_inertia=0;
1368 float total_torque=0;
1369 for(i=0; i<_rotors.size(); i++) {
1370 Rotor* r = (Rotor*)_rotors.get(i);
1371 omegan=r->getOmegan();
1372 total_torque_of_inertia+=r->getTorqueOfInertia()*omegan*omegan;
1373 //FIXME: this is constant, so this can be done in compile
1375 total_torque+=r->getTorque()*omegan;
1377 float max_torque_of_engine=0;
1378 SGPropertyNode * node=fgGetNode("/rotors/gear", true);
1379 float target_rel_rpm=node->getDoubleValue("target-rel-rpm",1);
1382 float max_rel_torque=node->getDoubleValue("max-rel-torque",1);
1383 max_torque_of_engine=_max_power_engine*max_rel_torque;
1384 float df=target_rel_rpm-omegarel;
1385 df/=_engine_prop_factor;
1386 df = Math::clamp(df, 0, 1);
1387 max_torque_of_engine = df * _max_power_engine*max_rel_torque;
1391 float rel_torque_engine=1;
1392 if (total_torque<=0)
1393 rel_torque_engine=0;
1395 if (max_torque_of_engine>0)
1396 rel_torque_engine=1/max_torque_of_engine*total_torque;
1398 rel_torque_engine=0;
1400 //add the rotor brake and the gear fritcion
1402 if (r0->_rotorparts.size()) dt=((Rotorpart*)r0->_rotorparts.get(0))->getDt();
1404 float rotor_brake_torque;
1405 rotor_brake_torque=_rotorbrake*_max_power_rotor_brake+_rotorgear_friction;
1406 //clamp it to the value you need to stop the rotor
1407 //to avod accelerate the rotor to neagtive rpm:
1408 rotor_brake_torque=Math::clamp(rotor_brake_torque,0,
1409 total_torque_of_inertia/dt*omegarel);
1410 max_torque_of_engine-=rotor_brake_torque;
1412 //change the rotation of the rotors
1413 if ((max_torque_of_engine<total_torque) //decreasing rotation
1414 ||((max_torque_of_engine>total_torque)&&(omegarel<target_rel_rpm))
1415 //increasing rotation due to engine
1416 ||(total_torque<0) ) //increasing rotation due to autorotation
1418 _ddt_omegarel=(max_torque_of_engine-total_torque)/total_torque_of_inertia;
1419 if(max_torque_of_engine>total_torque)
1421 //check if the acceleration is due to the engine. If yes,
1422 //the engine self limits the accel.
1423 float lim1=-total_torque/total_torque_of_inertia;
1424 //accel. by autorotation
1426 if (lim1<_engine_accel_limit) lim1=_engine_accel_limit;
1427 //if the accel by autorotation greater than the max. engine
1428 //accel, then this is the limit, if not: the engine is the limit
1429 if (_ddt_omegarel>lim1) _ddt_omegarel=lim1;
1431 if (_ddt_omegarel>5.5)_ddt_omegarel=5.5;
1432 //clamp it to avoid overflow. Should never be reached
1433 if (_ddt_omegarel<-5.5)_ddt_omegarel=-5.5;
1435 if (_max_power_engine<0.001) {omegarel=1;_ddt_omegarel=0;}
1436 //for debug: negative or no maxpower will result
1437 //in permanet 100% rotation
1439 omegarel+=dt*_ddt_omegarel;
1441 if (omegarel>2.5) omegarel=2.5;
1442 //clamp it to avoid overflow. Should never be reached
1443 if (omegarel<-.5) omegarel=-.5;
1445 r0->setOmegaRelNeu(omegarel);
1446 //calculate the torque, which is needed to accelerate the rotors.
1447 //Add this additional torque to the body
1448 for(j=0; j<_rotors.size(); j++) {
1449 Rotor* r = (Rotor*)_rotors.get(j);
1450 for(i=0; i<r->_rotorparts.size(); i++) {
1451 float torque_scalar=0;
1452 Rotorpart* rp = (Rotorpart*)r->_rotorparts.get(i);
1454 rp->getAccelTorque(_ddt_omegarel,torque);
1455 Math::add3(torque,torqueOut,torqueOut);
1459 _total_torque_on_engine=total_torque+_ddt_omegarel*total_torque_of_inertia;
1463 void Rotorgear::addRotor(Rotor* rotor)
1469 void Rotorgear::compile()
1472 for(int j=0; j<_rotors.size(); j++) {
1473 Rotor* r = (Rotor*)_rotors.get(j);
1476 fgGetNode("/rotors/gear/target-rel-rpm", true)->setDoubleValue(1);
1477 fgGetNode("/rotors/gear/max-rel-torque", true)->setDoubleValue(1);
1480 void Rotorgear::getDownWash(float *pos, float * v_heli, float *downwash)
1483 downwash[0]=downwash[1]=downwash[2]=0;
1484 for(int i=0; i<_rotors.size(); i++) {
1485 Rotor* ro = (Rotor*)_rotors.get(i);
1486 ro->getDownWash(pos,v_heli,tmp);
1487 Math::add3(downwash,tmp,downwash); // + downwash
1491 void Rotorgear::setParameter(char *parametername, float value)
1493 #define p(a,b) if (strcmp(parametername,#a)==0) _##a = (value * (b)); else
1494 p(max_power_engine,1000)
1495 p(engine_prop_factor,1)
1496 p(yasimdragfactor,1)
1497 p(yasimliftfactor,1)
1498 p(max_power_rotor_brake,1000)
1499 p(rotorgear_friction,1000)
1500 p(engine_accel_limit,0.01)
1501 SG_LOG(SG_INPUT, SG_ALERT,
1502 "internal error in parameter set up for rotorgear: '"
1503 << parametername <<"'" << endl);
1506 int Rotorgear::getValueforFGSet(int j,char *text,float *f)
1510 sprintf(text,"/rotors/gear/total-torque");
1511 *f=_total_torque_on_engine;
1515 Rotorgear::Rotorgear()
1520 _max_power_rotor_brake=1;
1521 _rotorgear_friction=1;
1522 _max_power_engine=1000*450;
1523 _engine_prop_factor=0.05f;
1527 _engine_accel_limit=0.05f;
1528 _total_torque_on_engine=0;
1531 Rotorgear::~Rotorgear()
1533 for(int i=0; i<_rotors.size(); i++)
1534 delete (Rotor*)_rotors.get(i);
1537 }; // namespace yasim