FlightGear next
Rotor.cpp
Go to the documentation of this file.
1
2#ifdef HAVE_CONFIG_H
3# include "config.h"
4#endif
5
6#include <simgear/debug/logstream.hxx>
7
8#include "Math.hpp"
9#include <Main/fg_props.hxx>
10#include "Rotorpart.hpp"
11#include "Glue.hpp"
12#include "Ground.hpp"
13#include "Rotor.hpp"
14
15#include <iostream>
16#include <iomanip>
17
18
19#include <stdio.h>
20#include <string.h>
21#include <iostream>
22#include <sstream>
23
24using std::setprecision;
25using std::endl;
26
27namespace yasim {
28
29const float pi=3.14159;
30
31static inline float sqr(float a) { return a * a; }
32
33Rotor::Rotor()
34{
35 int i;
36 _alpha0=-.05;
37 _alpha0factor=1;
38 _alphamin=-.1;
39 _alphamax= .1;
40 _alphaoutput[0][0]=0;
41 _alphaoutput[1][0]=0;
42 _alphaoutput[2][0]=0;
43 _alphaoutput[3][0]=0;
44 _alphaoutput[4][0]=0;
45 _alphaoutput[5][0]=0;
46 _alphaoutput[6][0]=0;
47 _alphaoutput[7][0]=0;
48 _base[0] = _base[1] = _base[2] = 0;
49 _ccw=0;
50 _delta=1;
51 _delta3=0;
52 _diameter =10;
53 _dynamic=1;
54 _engineon=0;
55 _force_at_pitch_a=0;
56 _forward[0]=1;
57 _forward[1]=_forward[2]=0;
58 _max_pitch=13./180*pi;
59 _maxcyclicail=10./180*pi;
60 _maxcyclicele=10./180*pi;
61 _maxteeterdamp=0;
62 _mincyclicail=-10./180*pi;
63 _mincyclicele=-10./180*pi;
64 _min_pitch=-.5/180*pi;
65 _cyclicele=0;
66 _cyclicail=0;
67 _name[0] = 0;
68 _normal[0] = _normal[1] = 0;
69 _normal[2] = 1;
70 _normal_with_yaw_roll[0]= _normal_with_yaw_roll[1]=0;
71 _normal_with_yaw_roll[2]=1;
72 _number_of_blades=4;
73 _omega=_omegan=_omegarel=_omegarelneu=0;
74 _phi_null=0;
75 _ddt_omega=0;
76 _pitch_a=0;
77 _pitch_b=0;
78 _power_at_pitch_0=0;
79 _power_at_pitch_b=0;
80 _no_torque=0;
81 _rel_blade_center=.7;
82 _rel_len_hinge=0.01;
83 _shared_flap_hinge=false;
84 _rellenteeterhinge=0.01;
85 _rotor_rpm=442;
86 _sim_blades=0;
87 _teeterdamp=0.00001;
88 _translift=0.05;
89 _weight_per_blade=42;
90 _translift_ve=20;
91 _translift_maxfactor=1.3;
92 _ground_effect_constant=0.1;
93 _vortex_state_lift_factor=0.4;
94 _vortex_state_c1=0.1;
95 _vortex_state_c2=0;
96 _vortex_state_c3=0;
97 _vortex_state_e1=1;
98 _vortex_state_e2=1;
99 _vortex_state_e3=1;
100 _vortex_state=0;
101 _lift_factor=1;
102 _liftcoef=0.1;
103 _dragcoef0=0.1;
104 _dragcoef1=0.1;
105 _twist=0;
106 _number_of_segments=1;
107 _number_of_parts=4;
108 _rel_len_where_incidence_is_measured=0.7;
109 _torque_of_inertia=1;
110 _torque=0;
111 _chord=0.3;
112 _taper=1;
113 _airfoil_incidence_no_lift=0;
114 _rel_len_blade_start=0;
115 _airfoil_lift_coefficient=0;
116 _airfoil_drag_coefficient0=0;
117 _airfoil_drag_coefficient1=0;
118 for(i=0; i<2; i++)
119 _global_ground[i] = _tilt_center[i] = 0;
120 _global_ground[2] = 1;
121 _global_ground[3] = -1e3;
122 _incidence_stall_zero_speed=18*pi/180.;
123 _incidence_stall_half_sonic_speed=14*pi/180.;
124 _lift_factor_stall=0.28;
125 _stall_change_over=2*pi/180.;
126 _drag_factor_stall=8;
127 _stall_sum=1;
128 _stall_v2sum=1;
129 _collective=0;
130 _yaw=_roll=0;
131 for (int k=0;k<4;k++)
132 for (i=0;i<3;i++)
133 _groundeffectpos[k][i]=0;
134 _ground_effect_altitude=1;
135 _cyclic_factor=1;
136 _lift_factor=_f_ge=_f_vs=_f_tl=1;
137 _rotor_correction_factor=.65;
138 _balance1=1;
139 _balance2=1;
140 _properties_tied=0;
141 _num_ground_contact_pos=0;
142 _directions_and_postions_dirty=true;
143 _tilt_yaw=0;
144 _tilt_roll=0;
145 _tilt_pitch=0;
146 _old_tilt_roll=0;
147 _old_tilt_pitch=0;
148 _old_tilt_yaw=0;
149 _min_tilt_yaw=0;
150 _min_tilt_pitch=0;
151 _min_tilt_roll=0;
152 _max_tilt_yaw=0;
153 _max_tilt_pitch=0;
154 _max_tilt_roll=0;
155 _downwash_factor=1;
156}
157
158Rotor::~Rotor()
159{
160 int i;
161 for(i=0; i<_rotorparts.size(); i++) {
162 Rotorpart* r = (Rotorpart*)_rotorparts.get(i);
163 delete r;
164 }
165 //untie the properties
166 if(_properties_tied)
167 {
168 SGPropertyNode * node = fgGetNode("/rotors", true)->getNode(_name,true);
169 node->untie("balance-ext");
170 node->untie("balance-int");
171 _properties_tied=0;
172 }
173}
174
175void Rotor::inititeration(float dt,float omegarel,float ddt_omegarel,float *rot)
176{
177 _stall_sum=0;
178 _stall_v2sum=0;
179 _omegarel=omegarel;
180 _omega=_omegan*_omegarel;
181 _ddt_omega=_omegan*ddt_omegarel;
182 int i;
183 float drot[3];
184 updateDirectionsAndPositions(drot);
185 Math::add3(rot,drot,drot);
186 for(i=0; i<_rotorparts.size(); i++) {
187 float s = Math::sin(float(2*pi*i/_number_of_parts+(_phi-pi/2.)*(_ccw?1:-1)));
188 float c = Math::cos(float(2*pi*i/_number_of_parts+(_phi-pi/2.)*(_ccw?1:-1)));
189 Rotorpart* r = (Rotorpart*)_rotorparts.get(i);
190 r->setOmega(_omega);
191 r->setDdtOmega(_ddt_omega);
192 r->inititeration(dt,drot);
193 r->setCyclic(_cyclicail*c+_cyclicele*s);
194 }
195
196 //calculate the normal of the rotor disc, for calcualtion of the downwash
197 float side[3],help[3];
198 Math::cross3(_normal,_forward,side);
199 Math::mul3(Math::cos(_yaw)*Math::cos(_roll),_normal,_normal_with_yaw_roll);
200
201 Math::mul3(Math::sin(_yaw),_forward,help);
202 Math::add3(_normal_with_yaw_roll,help,_normal_with_yaw_roll);
203
204 Math::mul3(Math::sin(_roll),side,help);
205 Math::add3(_normal_with_yaw_roll,help,_normal_with_yaw_roll);
206
207 //update balance
208 if ((_balance1*_balance2 < 0.97) && (_balance1>-1))
209 {
210 _balance1-=(0.97-_balance1*_balance2)*(0.97-_balance1*_balance2)*0.005;
211 if (_balance1<-1) _balance1=-1;
212 }
213}
214
215float Rotor::calcStall(float incidence,float speed)
216{
217 float stall_incidence=_incidence_stall_zero_speed
218 +(_incidence_stall_half_sonic_speed
219 -_incidence_stall_zero_speed)*speed/(343./2);
220 //missing: Temeperature dependency of sonic speed
221 incidence = Math::abs(incidence);
222 if (incidence > (90./180.*pi))
223 incidence = pi-incidence;
224 float stall = (incidence-stall_incidence)/_stall_change_over;
225 stall = Math::clamp(stall,0,1);
226
227 _stall_sum+=stall*speed*speed;
228 _stall_v2sum+=speed*speed;
229
230 return stall;
231}
232
233float Rotor::getLiftCoef(float incidence,float speed)
234{
235 float stall=calcStall(incidence,speed);
236 /* the next shold look like this, but this is the inner loop of
237 the rotor simulation. For small angles (and we hav only small
238 angles) the first order approximation works well
239 float c1= Math::sin(incidence-_airfoil_incidence_no_lift)*_liftcoef;
240 for c2 we would need higher order, because in stall the angle can be large
241 */
242 float i2;
243 if (incidence > (pi/2))
244 i2 = incidence-pi;
245 else if (incidence <-(pi/2))
246 i2 = (incidence+pi);
247 else
248 i2 = incidence;
249 float c1= (i2-_airfoil_incidence_no_lift)*_liftcoef;
250 if (stall > 0)
251 {
252 float c2= Math::sin(2*(incidence-_airfoil_incidence_no_lift))
253 *_liftcoef*_lift_factor_stall;
254 return (1-stall)*c1 + stall *c2;
255 }
256 else
257 return c1;
258}
259
260float Rotor::getDragCoef(float incidence,float speed)
261{
262 float stall=calcStall(incidence,speed);
263 float c1= (Math::abs(Math::sin(incidence-_airfoil_incidence_no_lift))
264 *_dragcoef1+_dragcoef0);
265 float c2= c1*_drag_factor_stall;
266 return (1-stall)*c1 + stall *c2;
267}
268
269int Rotor::getValueforFGSet(int j,char *text,float *f)
270{
271 if (_name[0]==0) return 0;
272 if (4>numRotorparts()) return 0; //compile first!
273
274 if (j==0)
275 {
276 snprintf(text, 300, "/rotors/%s/cone-deg", _name);
277 *f=(_balance1>-1)?( ((Rotorpart*)getRotorpart(0))->getrealAlpha()
278 +((Rotorpart*)getRotorpart(1*(_number_of_parts>>2)))->getrealAlpha()
279 +((Rotorpart*)getRotorpart(2*(_number_of_parts>>2)))->getrealAlpha()
280 +((Rotorpart*)getRotorpart(3*(_number_of_parts>>2)))->getrealAlpha()
281 )/4*180/pi:0;
282 }
283 else if (j==1)
284 {
285 snprintf(text, 300, "/rotors/%s/roll-deg", _name);
286 _roll = ( ((Rotorpart*)getRotorpart(0))->getrealAlpha()
287 -((Rotorpart*)getRotorpart(2*(_number_of_parts>>2)))->getrealAlpha()
288 )/2*(_ccw?-1:1);
289 *f=(_balance1>-1)?_roll *180/pi:0;
290 }
291 else if (j==2)
292 {
293 snprintf(text, 300, "/rotors/%s/yaw-deg", _name);
294 _yaw=( ((Rotorpart*)getRotorpart(1*(_number_of_parts>>2)))->getrealAlpha()
295 -((Rotorpart*)getRotorpart(3*(_number_of_parts>>2)))->getrealAlpha()
296 )/2;
297 *f=(_balance1>-1)?_yaw*180/pi:0;
298 }
299 else if (j==3)
300 {
301 snprintf(text, 300, "/rotors/%s/rpm", _name);
302 *f=(_balance1>-1)?_omega/2/pi*60:0;
303 }
304 else if (j==4)
305 {
306 snprintf(text, 300, "/rotors/%s/tilt/pitch-deg",_name);
307 *f=_tilt_pitch*180/pi;
308 }
309 else if (j==5)
310 {
311 snprintf(text, 300, "/rotors/%s/tilt/roll-deg",_name);
312 *f=_tilt_roll*180/pi;
313 }
314 else if (j==6)
315 {
316 snprintf(text, 300, "/rotors/%s/tilt/yaw-deg",_name);
317 *f=_tilt_yaw*180/pi;
318 }
319 else if (j==7)
320 {
321 snprintf(text, 300, "/rotors/%s/balance", _name);
322 *f=_balance1;
323 }
324 else if (j==8)
325 {
326 snprintf(text, 300, "/rotors/%s/stall",_name);
327 *f=getOverallStall();
328 }
329 else if (j==9)
330 {
331 snprintf(text, 300, "/rotors/%s/torque",_name);
332 *f=-_torque;;
333 }
334 else
335 {
336 int b=(j-10)/3;
337 if (b>=_number_of_blades)
338 {
339 return 0;
340 }
341 int w=j%3;
342 snprintf(text, 300, "/rotors/%s/blade[%i]/%s",
343 _name,b,
344 w==0?"position-deg":(w==1?"flap-deg":"incidence-deg"));
345 *f=((Rotorpart*)getRotorpart(0))->getPhi()*180/pi
346 +360*b/_number_of_blades*(_ccw?1:-1);
347 if (*f>360) *f-=360;
348 if (*f<0) *f+=360;
349 if (_balance1<=-1) *f=0;
350 int k,l;
351 float rk,rl,p;
352 p=(*f/90);
353 k=int(p);
354 l=k+1;
355 rk=l-p;
356 rk=Math::clamp(rk,0,1);//Delete this
357 rl=1-rk;
358 if(w==2) {k+=2;l+=2;}
359 else if(w==1) {k+=1;l+=1;}
360
361 k%=4;
362 l%=4;
363 if (w==1) *f=rk*((Rotorpart*) getRotorpart(k*(_number_of_parts>>2)))->getrealAlpha()*180/pi
364 +rl*((Rotorpart*) getRotorpart(l*(_number_of_parts>>2)))->getrealAlpha()*180/pi;
365 else if(w==2) *f=rk*((Rotorpart*)getRotorpart(k*(_number_of_parts>>2)))->getIncidence()*180/pi
366 +rl*((Rotorpart*)getRotorpart(l*(_number_of_parts>>2)))->getIncidence()*180/pi;
367 }
368 return j+1;
369}
370
371void Rotorgear::setEngineOn(int value)
372{
373 _engineon=value;
374}
375
376void Rotorgear::setRotorEngineMaxRelTorque(float lval)
377{
378 _max_rel_torque=lval;
379}
380
381void Rotorgear::setRotorRelTarget(float lval)
382{
383 _target_rel_rpm=lval;
384}
385
386void Rotor::setAlpha0(float f)
387{
388 _alpha0=f;
389}
390
391void Rotor::setAlphamin(float f)
392{
393 _alphamin=f;
394}
395
396void Rotor::setAlphamax(float f)
397{
398 _alphamax=f;
399}
400
401void Rotor::setAlpha0factor(float f)
402{
403 _alpha0factor=f;
404}
405
406int Rotor::numRotorparts()
407{
408 return _rotorparts.size();
409}
410
411Rotorpart* Rotor::getRotorpart(int n)
412{
413 return ((Rotorpart*)_rotorparts.get(n));
414}
415
416int Rotorgear::getEngineon()
417{
418 return _engineon;
419}
420
421float Rotor::getTorqueOfInertia()
422{
423 return _torque_of_inertia;
424}
425
426void Rotor::setTorque(float f)
427{
428 _torque=f;
429}
430
431void Rotor::addTorque(float f)
432{
433 _torque+=f;
434}
435
436void Rotor::strncpy(char *dest,const char *src,int maxlen)
437{
438 int n=0;
439 while(src[n]&&n<(maxlen-1))
440 {
441 dest[n]=src[n];
442 n++;
443 }
444 dest[n]=0;
445}
446
447void Rotor::setNormal(float* normal)
448{
449 int i;
450 float invsum,sqrsum=0;
451 for(i=0; i<3; i++) { sqrsum+=normal[i]*normal[i];}
452 if (sqrsum!=0)
453 invsum=1/Math::sqrt(sqrsum);
454 else
455 invsum=1;
456 for(i=0; i<3; i++)
457 {
458 _normal_with_yaw_roll[i]=_normal[i] = normal[i]*invsum;
459 }
460}
461
462void Rotor::setForward(float* forward)
463{
464 int i;
465 float invsum,sqrsum=0;
466 for(i=0; i<3; i++) { sqrsum+=forward[i]*forward[i];}
467 if (sqrsum!=0)
468 invsum=1/Math::sqrt(sqrsum);
469 else
470 invsum=1;
471 for(i=0; i<3; i++) { _forward[i] = forward[i]*invsum; }
472}
473
474void Rotor::setForceAtPitchA(float force)
475{
476 _force_at_pitch_a=force;
477}
478
479void Rotor::setPowerAtPitch0(float value)
480{
481 _power_at_pitch_0=value;
482}
483
484void Rotor::setPowerAtPitchB(float value)
485{
486 _power_at_pitch_b=value;
487}
488
489void Rotor::setPitchA(float value)
490{
491 _pitch_a=value/180*pi;
492}
493
494void Rotor::setPitchB(float value)
495{
496 _pitch_b=value/180*pi;
497}
498
499void Rotor::setBase(float* base)
500{
501 int i;
502 for(i=0; i<3; i++) _base[i] = base[i];
503}
504
505void Rotor::setMaxCyclicail(float value)
506{
507 _maxcyclicail=value/180*pi;
508}
509
510void Rotor::setMaxCyclicele(float value)
511{
512 _maxcyclicele=value/180*pi;
513}
514
515void Rotor::setMinCyclicail(float value)
516{
517 _mincyclicail=value/180*pi;
518}
519
520void Rotor::setMinCyclicele(float value)
521{
522 _mincyclicele=value/180*pi;
523}
524
525void Rotor::setMinCollective(float value)
526{
527 _min_pitch=value/180*pi;
528}
529
530void Rotor::setMinTiltYaw(float value)
531{
532 _min_tilt_yaw=value/180*pi;
533}
534
535void Rotor::setMinTiltPitch(float value)
536{
537 _min_tilt_pitch=value/180*pi;
538}
539
540void Rotor::setMinTiltRoll(float value)
541{
542 _min_tilt_roll=value/180*pi;
543}
544
545void Rotor::setMaxTiltYaw(float value)
546{
547 _max_tilt_yaw=value/180*pi;
548}
549
550void Rotor::setMaxTiltPitch(float value)
551{
552 _max_tilt_pitch=value/180*pi;
553}
554
555void Rotor::setMaxTiltRoll(float value)
556{
557 _max_tilt_roll=value/180*pi;
558}
559
560void Rotor::setTiltCenterX(float value)
561{
562 _tilt_center[0] = value;
563}
564
565void Rotor::setTiltCenterY(float value)
566{
567 _tilt_center[1] = value;
568}
569
570void Rotor::setTiltCenterZ(float value)
571{
572 _tilt_center[2] = value;
573}
574
575void Rotor::setMaxCollective(float value)
576{
577 _max_pitch=value/180*pi;
578}
579
580void Rotor::setDiameter(float value)
581{
582 _diameter=value;
583}
584
585void Rotor::setWeightPerBlade(float value)
586{
587 _weight_per_blade=value;
588}
589
590void Rotor::setNumberOfBlades(float value)
591{
592 _number_of_blades=int(value+.5);
593}
594
595void Rotor::setRelBladeCenter(float value)
596{
597 _rel_blade_center=value;
598}
599
600void Rotor::setDynamic(float value)
601{
602 _dynamic=value;
603}
604
605void Rotor::setDelta3(float value)
606{
607 _delta3=value;
608}
609
610void Rotor::setDelta(float value)
611{
612 _delta=value;
613}
614
615void Rotor::setTranslift(float value)
616{
617 _translift=value;
618}
619
620void Rotor::setSharedFlapHinge(bool s)
621{
622 _shared_flap_hinge=s;
623}
624
625void Rotor::setBalance(float b)
626{
627 _balance1=b;
628}
629
630void Rotor::setC2(float value)
631{
632 _c2=value;
633}
634
635void Rotor::setStepspersecond(float steps)
636{
637 _stepspersecond=steps;
638}
639
640void Rotor::setRPM(float value)
641{
642 _rotor_rpm=value;
643}
644
645void Rotor::setPhiNull(float value)
646{
647 _phi_null=value;
648}
649
650void Rotor::setRelLenHinge(float value)
651{
652 _rel_len_hinge=value;
653}
654
655void Rotor::setDownwashFactor(float value)
656{
657 _downwash_factor=value;
658}
659
660void Rotor::setAlphaoutput(int i, const char *text)
661{
662 strncpy(_alphaoutput[i],text,255);
663}
664
665void Rotor::setName(const char *text)
666{
667 strncpy(_name,text,256);//256: some space needed for settings
668}
669
670void Rotor::setCcw(int ccw)
671{
672 _ccw=ccw;
673}
674
675void Rotor::setNotorque(int value)
676{
677 _no_torque=value;
678}
679
680void Rotor::setRelLenTeeterHinge(float f)
681{
682 _rellenteeterhinge=f;
683}
684
685void Rotor::setTeeterdamp(float f)
686{
687 _teeterdamp=f;
688}
689
690void Rotor::setMaxteeterdamp(float f)
691{
692 _maxteeterdamp=f;
693}
694
695void Rotor::setGlobalGround(double *global_ground, float* global_vel)
696{
697 int i;
698 for(i=0; i<4; i++) _global_ground[i] = global_ground[i];
699}
700
701void Rotor::setParameter(const char *parametername, float value)
702{
703#define p(a,b) if (strcmp(parametername,#a)==0) _##a = (value * (b)); else
704 p(translift_ve,1)
705 p(translift_maxfactor,1)
706 p(ground_effect_constant,1)
707 p(vortex_state_lift_factor,1)
708 p(vortex_state_c1,1)
709 p(vortex_state_c2,1)
710 p(vortex_state_c3,1)
711 p(vortex_state_e1,1)
712 p(vortex_state_e2,1)
713 p(vortex_state_e3,1)
714 p(twist,pi/180.)
715 p(number_of_segments,1)
716 p(number_of_parts,1)
717 p(rel_len_where_incidence_is_measured,1)
718 p(chord,1)
719 p(taper,1)
720 p(airfoil_incidence_no_lift,pi/180.)
721 p(rel_len_blade_start,1)
722 p(incidence_stall_zero_speed,pi/180.)
723 p(incidence_stall_half_sonic_speed,pi/180.)
724 p(lift_factor_stall,1)
725 p(stall_change_over,pi/180.)
726 p(drag_factor_stall,1)
727 p(airfoil_lift_coefficient,1)
728 p(airfoil_drag_coefficient0,1)
729 p(airfoil_drag_coefficient1,1)
730 p(cyclic_factor,1)
731 p(rotor_correction_factor,1)
732 SG_LOG(SG_INPUT, SG_ALERT,
733 "internal error in parameter set up for rotor: '" <<
734 parametername <<"'" << std::endl);
735#undef p
736}
737
738float Rotor::getLiftFactor()
739{
740 return _lift_factor;
741}
742
743void Rotorgear::setRotorBrake(float lval)
744{
745 lval = Math::clamp(lval, 0, 1);
746 _rotorbrake=lval;
747}
748
749void Rotor::setTiltYaw(float lval)
750{
751 lval = Math::clamp(lval, -1, 1);
752 _tilt_yaw = _min_tilt_yaw+(lval+1)/2*(_max_tilt_yaw-_min_tilt_yaw);
753 _directions_and_postions_dirty = true;
754}
755
756void Rotor::setTiltPitch(float lval)
757{
758 lval = Math::clamp(lval, -1, 1);
759 _tilt_pitch = _min_tilt_pitch+(lval+1)/2*(_max_tilt_pitch-_min_tilt_pitch);
760 _directions_and_postions_dirty = true;
761}
762
763void Rotor::setTiltRoll(float lval)
764{
765 lval = Math::clamp(lval, -1, 1);
766 _tilt_roll = _min_tilt_roll+(lval+1)/2*(_max_tilt_roll-_min_tilt_roll);
767 _directions_and_postions_dirty = true;
768}
769
770void Rotor::setCollective(float lval)
771{
772 lval = Math::clamp(lval, -1, 1);
773 int i;
774 _collective=_min_pitch+(lval+1)/2*(_max_pitch-_min_pitch);
775 for(i=0; i<_rotorparts.size(); i++) {
776 ((Rotorpart*)_rotorparts.get(i))->setCollective(_collective);
777 }
778}
779
780void Rotor::setCyclicele(float lval,float rval)
781{
782 lval = Math::clamp(lval, -1, 1);
783 _cyclicele=_mincyclicele+(lval+1)/2*(_maxcyclicele-_mincyclicele);
784}
785
786void Rotor::setCyclicail(float lval,float rval)
787{
788 lval = Math::clamp(lval, -1, 1);
789 if (_ccw) lval *=-1;
790 _cyclicail=-(_mincyclicail+(lval+1)/2*(_maxcyclicail-_mincyclicail));
791}
792
793void Rotor::setRotorBalance(float lval)
794{
795 lval = Math::clamp(lval, -1, 1);
796 _balance2 = lval;
797}
798
799void Rotor::getPosition(float* out)
800{
801 int i;
802 for(i=0; i<3; i++) out[i] = _base[i];
803}
804
805void Rotor::calcLiftFactor(float* v, float rho, State *s)
806{
807 //calculates _lift_factor, which is a foactor for the lift of the rotor
808 //due to
809 //- ground effect (_f_ge)
810 //- vortex state (_f_vs)
811 //- translational lift (_f_tl)
812 _f_ge=1;
813 _f_tl=1;
814 _f_vs=1;
815
816 // Calculate ground effect
817 _f_ge=1+_diameter/_ground_effect_altitude*_ground_effect_constant;
818
819 // Now calculate translational lift
820 // float v_vert = Math::dot3(v,_normal);
821 float help[3];
822 Math::cross3(v,_normal,help);
823 float v_horiz = Math::mag3(help);
824 _f_tl = ((1-Math::pow(2.7183,-v_horiz/_translift_ve))
825 *(_translift_maxfactor-1)+1)/_translift_maxfactor;
826
827 _lift_factor = _f_ge*_f_tl*_f_vs;
828
829 //store the gravity direction
830 Glue::geodUp(s->pos, _grav_direction);
831 s->globalToLocal(_grav_direction, _grav_direction);
832}
833
834void Rotor::findGroundEffectAltitude(Ground * ground_cb,State *s)
835{
836 _ground_effect_altitude=findGroundEffectAltitude(ground_cb,s,
837 _groundeffectpos[0],_groundeffectpos[1],
838 _groundeffectpos[2],_groundeffectpos[3]);
839 testForRotorGroundContact(ground_cb,s);
840}
841
842void Rotor::testForRotorGroundContact(Ground * ground_cb,State *s)
843{
844 int i;
845 for (i=0;i<_num_ground_contact_pos;i++)
846 {
847 double pt[3],h;
848 s->posLocalToGlobal(_ground_contact_pos[i], pt);
849
850 // Ask for the ground plane in the global coordinate system
851 double global_ground[4];
852 float global_vel[3];
853 unsigned int body;
854 ground_cb->getGroundPlane(pt, global_ground, global_vel, body);
855 // find h, the distance to the ground
856 // The ground plane transformed to the local frame.
857 float ground[4];
858 s->planeGlobalToLocal(global_ground, ground);
859
860 h = ground[3] - Math::dot3(_ground_contact_pos[i], ground);
861 // Now h is the distance from _ground_contact_pos[i] to ground
862 if (h<0)
863 {
864 _balance1 -= (-h)/_diameter/_num_ground_contact_pos;
865 _balance1 = (_balance1<-1)?-1:_balance1;
866 }
867 }
868}
869float Rotor::findGroundEffectAltitude(Ground * ground_cb,State *s,
870 float *pos0,float *pos1,float *pos2,float *pos3,
871 int iteration,float a0,float a1,float a2,float a3)
872{
873 float a[5];
874 float *p[5],pos4[3];
875 a[0]=a0;
876 a[1]=a1;
877 a[2]=a2;
878 a[3]=a3;
879 a[4]=-1;
880 p[0]=pos0;
881 p[1]=pos1;
882 p[2]=pos2;
883 p[3]=pos3;
884 p[4]=pos4;
885 Math::add3(p[0],p[2],p[4]);
886 Math::mul3(0.5,p[4],p[4]);//the center
887
888 float mina=100*_diameter;
889 float suma=0;
890 for (int i=0;i<5;i++)
891 {
892 if (a[i]==-1)//in the first iteration,(iteration==0) no height is
893 //passed to this function, these missing values are
894 //marked by ==-1
895 {
896 double pt[3];
897 s->posLocalToGlobal(p[i], pt);
898
899 // Ask for the ground plane in the global coordinate system
900 double global_ground[4];
901 float global_vel[3];
902 unsigned int body;
903 ground_cb->getGroundPlane(pt, global_ground, global_vel, body);
904 // find h, the distance to the ground
905 // The ground plane transformed to the local frame.
906 float ground[4];
907 s->planeGlobalToLocal(global_ground, ground);
908
909 a[i] = ground[3] - Math::dot3(p[i], ground);
910 // Now a[i] is the distance from p[i] to ground
911 }
912 suma+=a[i];
913 if (a[i]<mina)
914 mina=a[i];
915 }
916 if (mina>=10*_diameter)
917 return mina; //the ground effect will be zero
918
919 //check if further recursion is neccessary
920 //if the height does not differ more than 20%, than
921 //we can return then mean height, if not split
922 //zhe square to four parts and calcualte the height
923 //for each part
924 //suma * 0.2 is the mean
925 //0.15 is the maximum allowed difference from the mean
926 //to the height at the center
927 if ((iteration>2)
928 ||(Math::abs(suma*0.2-a[4])<(0.15*0.2*suma*(1<<iteration))))
929 return suma*0.2;
930 suma=0;
931 float pc[4][3],ac[4]; //pc[i]=center of pos[i] and pos[(i+1)&3]
932 for (int i=0;i<4;i++)
933 {
934 Math::add3(p[i],p[(i+1)&3],pc[i]);
935 Math::mul3(0.5,pc[i],pc[i]);
936 double pt[3];
937 s->posLocalToGlobal(pc[i], pt);
938
939 // Ask for the ground plane in the global coordinate system
940 double global_ground[4];
941 float global_vel[3];
942 unsigned int body;
943 ground_cb->getGroundPlane(pt, global_ground, global_vel, body);
944 // find h, the distance to the ground
945 // The ground plane transformed to the local frame.
946 float ground[4];
947 s->planeGlobalToLocal(global_ground, ground);
948
949 ac[i] = ground[3] - Math::dot3(p[i], ground);
950 // Now ac[i] is the distance from pc[i] to ground
951 }
952 return 0.25*
953 (findGroundEffectAltitude(ground_cb,s,p[0],pc[1],p[4],pc[3],
954 iteration+1,a[0],ac[0],a[4],ac[3])
955 +findGroundEffectAltitude(ground_cb,s,p[1],pc[0],p[4],pc[1],
956 iteration+1,a[1],ac[0],a[4],ac[1])
957 +findGroundEffectAltitude(ground_cb,s,p[2],pc[1],p[4],pc[2],
958 iteration+1,a[2],ac[1],a[4],ac[2])
959 +findGroundEffectAltitude(ground_cb,s,p[3],pc[2],p[4],pc[3],
960 iteration+1,a[3],ac[2],a[4],ac[3])
961 );
962}
963
964void Rotor::getDownWash(const float *pos, const float *v_heli, float *downwash)
965{
966 float pos2rotor[3],tmp[3];
967 Math::sub3(_base,pos,pos2rotor);
968 float dist=Math::dot3(pos2rotor,_normal_with_yaw_roll);
969 //calculate incidence at 0.7r;
970 float inc = _collective+_twist *0.7
971 - _twist*_rel_len_where_incidence_is_measured;
972 if (inc < 0)
973 dist *=-1;
974 if (dist<0) // we are not in the downwash region
975 {
976 downwash[0]=downwash[1]=downwash[2]=0.;
977 return;
978 }
979
980 //calculate the mean downwash speed directly beneath the rotor disk
981 float v1bar = Math::sin(inc) *_omega * 0.35 * _diameter * 0.8;
982 //0.35 * d = 0.7 *r, a good position to calcualte the mean downwashd
983 //0.8 the slip of the rotor.
984
985 //calculate the time the wind needed from thr rotor to here
986 if (v1bar< 1) v1bar = 1;
987 float time=dist/v1bar;
988
989 //calculate the pos2rotor, where the rotor was, "time" ago
990 Math::mul3(time,v_heli,tmp);
991 Math::sub3(pos2rotor,tmp,pos2rotor);
992
993 //and again calculate dist
994 dist=Math::dot3(pos2rotor,_normal_with_yaw_roll);
995 //missing the normal is offen not pointing to the normal of the rotor
996 //disk. Rotate the normal by yaw and tilt angle
997
998 if (inc < 0)
999 dist *=-1;
1000 if (dist<0) // we are not in the downwash region
1001 {
1002 downwash[0]=downwash[1]=downwash[2]=0.;
1003 return;
1004 }
1005 //of course this could be done in a runge kutta integrator, but it's such
1006 //a approximation that I beleave, it would'nt be more realistic
1007
1008 //calculate the dist to the rotor-axis
1009 Math::cross3(pos2rotor,_normal_with_yaw_roll,tmp);
1010 float r= Math::mag3(tmp);
1011 //calculate incidence at r;
1012 float rel_r = r *2 /_diameter;
1013 float inc_r = _collective+_twist * r /_diameter * 2
1014 - _twist*_rel_len_where_incidence_is_measured;
1015
1016 //calculate the downwash speed directly beneath the rotor disk
1017 float v1=0;
1018 if (rel_r<1)
1019 v1 = Math::sin(inc_r) *_omega * r * 0.8;
1020
1021 //calcualte the downwash speed in a distance "dist" to the rotor disc,
1022 //for large dist. The speed is assumed do follow a gausian distribution
1023 //with sigma increasing with dist^2:
1024 //sigma is assumed to be half of the rotor diameter directly beneath the
1025 //disc and is assumed to the rotor diameter at dist = (diameter * sqrt(2))
1026
1027 float sigma=_diameter/2 + dist * dist / _diameter /4.;
1028 float v2 = v1bar*_diameter/ (Math::sqrt(2 * pi) * sigma)
1029 * Math::pow(2.7183,-.5*r*r/(sigma*sigma))*_diameter/2/sigma;
1030
1031 //calculate the weight of the two downwash velocities.
1032 //Directly beneath the disc it is v1, far away it is v2
1033 float g = Math::pow(2.7183,-2*dist/_diameter);
1034 //at dist = rotor radius it is assumed to be 1/e * v1 + (1-1/e)* v2
1035
1036 float v = g * v1 + (1-g) * v2;
1037 Math::mul3(-v*_downwash_factor,_normal_with_yaw_roll,downwash);
1038 //the downwash is calculated in the opposite direction of the normal
1039}
1040
1041void Rotor::euler2orient(float roll, float pitch, float hdg, float* out)
1042{
1043 // the Glue::euler2orient, inverts y<z due to different bases
1044 // therefore the negation of all "y" and "z" coeffizients
1045 Glue::euler2orient(roll,pitch,hdg,out);
1046 for (int i=3;i<9;i++) out[i]*=-1.0;
1047}
1048
1049
1050void Rotor::updateDirectionsAndPositions(float *rot)
1051{
1052 if (!_directions_and_postions_dirty)
1053 {
1054 rot[0]=rot[1]=rot[2]=0;
1055 return;
1056 }
1057 rot[0]=_old_tilt_roll-_tilt_roll;
1058 rot[1]=_old_tilt_pitch-_tilt_pitch;
1059 rot[2]=_old_tilt_yaw-_tilt_yaw;
1060 _old_tilt_roll=_tilt_roll;
1061 _old_tilt_pitch=_tilt_pitch;
1062 _old_tilt_yaw=_tilt_yaw;
1063 float orient[9];
1064 euler2orient(_tilt_roll, _tilt_pitch, _tilt_yaw, orient);
1065 float forward[3];
1066 float normal[3];
1067 float base[3];
1068 Math::sub3(_base,_tilt_center,base);
1069 Math::vmul33(orient, base, base);
1070 Math::add3(base,_tilt_center,base);
1071 Math::vmul33(orient, _forward, forward);
1072 Math::vmul33(orient, _normal, normal);
1073#define _base base
1074#define _forward forward
1075#define _normal normal
1076 float directions[5][3];
1077 //pointing forward, right, ... the 5th is ony for calculation
1078 directions[0][0]=_forward[0];
1079 directions[0][1]=_forward[1];
1080 directions[0][2]=_forward[2];
1081 int i;
1082 for (i=1;i<5;i++)
1083 {
1084 if (!_ccw)
1085 Math::cross3(directions[i-1],_normal,directions[i]);
1086 else
1087 Math::cross3(_normal,directions[i-1],directions[i]);
1088 }
1089 Math::set3(directions[4],directions[0]);
1090 // now directions[0] is perpendicular to the _normal.and has a length
1091 // of 1. if _forward is already normalized and perpendicular to the
1092 // normal, directions[0] will be the same
1093 //_num_ground_contact_pos=(_number_of_parts<16)?_number_of_parts:16;
1094 for (i=0;i<_num_ground_contact_pos;i++)
1095 {
1096 float help[3];
1097 float s = Math::sin(pi*2*i/_num_ground_contact_pos);
1098 float c = Math::cos(pi*2*i/_num_ground_contact_pos);
1099 Math::mul3(c*_diameter*0.5,directions[0],_ground_contact_pos[i]);
1100 Math::mul3(s*_diameter*0.5,directions[1],help);
1101 Math::add3(help,_ground_contact_pos[i],_ground_contact_pos[i]);
1102 Math::add3(_base,_ground_contact_pos[i],_ground_contact_pos[i]);
1103 }
1104 for (i=0;i<4;i++)
1105 {
1106 Math::mul3(_diameter*0.7,directions[i],_groundeffectpos[i]);
1107 Math::add3(_base,_groundeffectpos[i],_groundeffectpos[i]);
1108 }
1109 for (i=0;i<_number_of_parts;i++)
1110 {
1111 Rotorpart* rp = getRotorpart(i);
1112 float lpos[3],lforceattac[3],lspeed[3],dirzentforce[3];
1113 float s = Math::sin(2*pi*i/_number_of_parts);
1114 float c = Math::cos(2*pi*i/_number_of_parts);
1115 float sp = Math::sin(float(2*pi*i/_number_of_parts-pi/2.+_phi));
1116 float cp = Math::cos(float(2*pi*i/_number_of_parts-pi/2.+_phi));
1117 float direction[3],nextdirection[3],help[3],direction90deg[3];
1118 float rotorpartmass = _weight_per_blade*_number_of_blades/_number_of_parts*.453;
1119 float speed=_rotor_rpm/60*_diameter*_rel_blade_center*pi;
1120 float lentocenter=_diameter*_rel_blade_center*0.5;
1121 float lentoforceattac=_diameter*_rel_len_hinge*0.5;
1122 float zentforce=rotorpartmass*speed*speed/lentocenter;
1123
1124 Math::mul3(c ,directions[0],help);
1125 Math::mul3(s ,directions[1],direction);
1126 Math::add3(help,direction,direction);
1127
1128 Math::mul3(c ,directions[1],help);
1129 Math::mul3(s ,directions[2],direction90deg);
1130 Math::add3(help,direction90deg,direction90deg);
1131
1132 Math::mul3(cp ,directions[1],help);
1133 Math::mul3(sp ,directions[2],nextdirection);
1134 Math::add3(help,nextdirection,nextdirection);
1135
1136 Math::mul3(lentocenter,direction,lpos);
1137 Math::add3(lpos,_base,lpos);
1138 Math::mul3(lentoforceattac,nextdirection,lforceattac);
1139 //nextdirection: +90deg (gyro)!!!
1140
1141 Math::add3(lforceattac,_base,lforceattac);
1142 Math::mul3(speed,direction90deg,lspeed);
1143 Math::mul3(1,nextdirection,dirzentforce);
1144 rp->setPosition(lpos);
1145 rp->setNormal(_normal);
1146 rp->setZentipetalForce(zentforce);
1147 rp->setPositionForceAttac(lforceattac);
1148 rp->setSpeed(lspeed);
1149 rp->setDirectionofZentipetalforce(dirzentforce);
1150 rp->setDirectionofRotorPart(direction);
1151 }
1152#undef _base
1153#undef _forward
1154#undef _normal
1155 _directions_and_postions_dirty=false;
1156}
1157
1158void Rotor::compile()
1159{
1160 // Have we already been compiled?
1161 if(! _rotorparts.empty()) return;
1162
1163 //rotor is divided into _number_of_parts parts
1164 //each part is calcualted at _number_of_segments points
1165
1166 //clamp to 4..256
1167 //and make it a factor of 4
1168 _number_of_parts=(int(Math::clamp(_number_of_parts,4,256))>>2)<<2;
1169
1170 _dynamic=_dynamic*(1/ //inverse of the time
1171 ( (60/_rotor_rpm)/4 //for rotating 90 deg
1172 +(60/_rotor_rpm)/(2*_number_of_blades) //+ meantime a rotorblade
1173 //will pass a given point
1174 ));
1175 //normalize the directions
1176 Math::unit3(_forward,_forward);
1177 Math::unit3(_normal,_normal);
1178 _num_ground_contact_pos=(_number_of_parts<16)?_number_of_parts:16;
1179 float rotorpartmass = _weight_per_blade*_number_of_blades/_number_of_parts*.453;
1180 //was pounds -> now kg
1181
1182 _torque_of_inertia = 1/12. * ( _number_of_parts * rotorpartmass) * _diameter
1183 * _diameter * _rel_blade_center * _rel_blade_center /(0.5*0.5);
1184 float speed=_rotor_rpm/60*_diameter*_rel_blade_center*pi;
1185 float lentocenter=_diameter*_rel_blade_center*0.5;
1186 // float lentoforceattac=_diameter*_rel_len_hinge*0.5;
1187 float zentforce=rotorpartmass*speed*speed/lentocenter;
1188 float pitchaforce=_force_at_pitch_a/_number_of_parts*.453*9.81;
1189 // was pounds of force, now N, devided by _number_of_parts
1190 //(so its now per rotorpart)
1191
1192 float torque0=0,torquemax=0,torqueb=0;
1193 float omega=_rotor_rpm/60*2*pi;
1194 _omegan=omega;
1195 float omega0=omega*Math::sqrt(1/(1-_rel_len_hinge));
1196 float delta_theoretical=pitchaforce/(_pitch_a*omega*lentocenter*2*rotorpartmass);
1197 _delta*=delta_theoretical;
1198
1199 float relamp=(omega*omega/(2*_delta*Math::sqrt(sqr(omega0*omega0-omega*omega)
1200 +4*_delta*_delta*omega*omega)))*_cyclic_factor;
1201 //float relamp_theoretical=(omega*omega/(2*delta_theoretical*Math::sqrt(sqr(omega0*omega0-omega*omega)
1202 // +4*delta_theoretical*delta_theoretical*omega*omega)))*_cyclic_factor;
1203 _phi=Math::acos(_rel_len_hinge);
1204 _phi-=Math::atan(_delta3);
1205 if (!_no_torque)
1206 {
1207 torque0=_power_at_pitch_0/_number_of_parts*1000/omega;
1208 // f*r=p/w ; p=f*s/t; r=s/t/w ; r*w*t = s
1209 torqueb=_power_at_pitch_b/_number_of_parts*1000/omega;
1210 torquemax=_power_at_pitch_b/_number_of_parts*1000/omega/_pitch_b*_max_pitch;
1211
1212 if(_ccw)
1213 {
1214 torque0*=-1;
1215 torquemax*=-1;
1216 torqueb*=-1;
1217 }
1218 }
1219
1220 Rotorpart* rps[256];
1221 int i;
1222 for (i=0;i<_number_of_parts;i++)
1223 {
1224 Rotorpart* rp=rps[i]=newRotorpart(zentforce,pitchaforce,_delta3,rotorpartmass,
1225 _translift,_rel_len_hinge,lentocenter);
1226 int k = i*4/_number_of_parts;
1227 rp->setAlphaoutput(_alphaoutput[k&1?k:(_ccw?k^2:k)],0);
1228 rp->setAlphaoutput(_alphaoutput[4+(k&1?k:(_ccw?k^2:k))],1+(k>1));
1229 _rotorparts.add(rp);
1230 rp->setTorque(torquemax,torque0);
1231 rp->setRelamp(relamp);
1232 rp->setTorqueOfInertia(_torque_of_inertia/_number_of_parts);
1233 rp->setDirection(2*pi*i/_number_of_parts);
1234 }
1235 for (i=0;i<_number_of_parts;i++)
1236 {
1237 rps[i]->setlastnextrp(rps[(i-1+_number_of_parts)%_number_of_parts],
1238 rps[(i+1)%_number_of_parts],
1239 rps[(i+_number_of_parts/2)%_number_of_parts],
1240 rps[(i-_number_of_parts/4+_number_of_parts)%_number_of_parts],
1241 rps[(i+_number_of_parts/4)%_number_of_parts]);
1242 }
1243 float drot[3];
1244 updateDirectionsAndPositions(drot);
1245 for (i=0;i<_number_of_parts;i++)
1246 {
1247 rps[i]->setCompiled();
1248 }
1249 float lift[4],torque[4], v_wind[3];
1250 v_wind[0]=v_wind[1]=v_wind[2]=0;
1251 rps[0]->setOmega(_omegan);
1252
1253 if (_airfoil_lift_coefficient==0)
1254 {
1255 //calculate the lift and drag coefficients now
1256 _dragcoef0=1;
1257 _dragcoef1=1;
1258 _liftcoef=1;
1259 rps[0]->calculateAlpha(v_wind,rho_null,_pitch_a,0,0,
1260 &(torque[0]),&(lift[0])); //max_pitch a
1261 _liftcoef = pitchaforce/lift[0];
1262 _dragcoef0=1;
1263 _dragcoef1=0;
1264 rps[0]->calculateAlpha(v_wind,rho_null,0,0,0,&(torque[0]),&(lift[0]));
1265 //0 degree, c0
1266
1267 _dragcoef0=0;
1268 _dragcoef1=1;
1269 rps[0]->calculateAlpha(v_wind,rho_null,0,0,0,&(torque[1]),&(lift[1]));
1270 //0 degree, c1
1271
1272 _dragcoef0=1;
1273 _dragcoef1=0;
1274 rps[0]->calculateAlpha(v_wind,rho_null,_pitch_b,0,0,&(torque[2]),&(lift[2]));
1275 //picth b, c0
1276
1277 _dragcoef0=0;
1278 _dragcoef1=1;
1279 rps[0]->calculateAlpha(v_wind,rho_null,_pitch_b,0,0,&(torque[3]),&(lift[3]));
1280 //picth b, c1
1281
1282 if (torque[0]==0)
1283 {
1284 _dragcoef1=torque0/torque[1];
1285 _dragcoef0=(torqueb-_dragcoef1*torque[3])/torque[2];
1286 }
1287 else
1288 {
1289 _dragcoef1=(torque0/torque[0]-torqueb/torque[2])
1290 /(torque[1]/torque[0]-torque[3]/torque[2]);
1291 _dragcoef0=(torqueb-_dragcoef1*torque[3])/torque[2];
1292 }
1293 }
1294 else
1295 {
1296 _liftcoef=_airfoil_lift_coefficient/_number_of_parts*_number_of_blades;
1297 _dragcoef0=_airfoil_drag_coefficient0/_number_of_parts*_number_of_blades*_c2;
1298 _dragcoef1=_airfoil_drag_coefficient1/_number_of_parts*_number_of_blades*_c2;
1299 }
1300
1301 //Check
1302 rps[0]->calculateAlpha(v_wind,rho_null,_pitch_a,0,0,
1303 &(torque[0]),&(lift[0])); //pitch a
1304 rps[0]->calculateAlpha(v_wind,rho_null,_pitch_b,0,0,
1305 &(torque[1]),&(lift[1])); //pitch b
1306 rps[0]->calculateAlpha(v_wind,rho_null,0,0,0,
1307 &(torque[3]),&(lift[3])); //pitch 0
1308 SG_LOG(SG_FLIGHT, SG_INFO,
1309 "Rotor: coefficients for airfoil:" << endl << setprecision(6)
1310 << " drag0: " << _dragcoef0*_number_of_parts/_number_of_blades/_c2
1311 << " drag1: " << _dragcoef1*_number_of_parts/_number_of_blades/_c2
1312 << " lift: " << _liftcoef*_number_of_parts/_number_of_blades
1313 << endl
1314 << "at 10 deg:" << endl
1315 << "drag: " << (Math::sin(10./180*pi)*_dragcoef1+_dragcoef0)
1316 *_number_of_parts/_number_of_blades/_c2
1317 << " lift: " << Math::sin(10./180*pi)*_liftcoef*_number_of_parts/_number_of_blades
1318 << endl
1319 << "Some results (Pitch [degree], Power [kW], Lift [N])" << endl
1320 << 0.0f << "deg " << Math::abs(torque[3]*_number_of_parts*_omegan/1000) << "kW "
1321 << lift[3]*_number_of_parts << endl
1322 << _pitch_a*180/pi << "deg " << Math::abs(torque[0]*_number_of_parts*_omegan/1000)
1323 << "kW " << lift[0]*_number_of_parts << endl
1324 << _pitch_b*180/pi << "deg " << Math::abs(torque[1]*_number_of_parts*_omegan/1000)
1325 << "kW " << lift[1]*_number_of_parts << endl << endl );
1326
1327 //first calculation of relamp is wrong
1328 //it used pitchaforce, but this was unknown and
1329 //on the default value
1330 _delta*=lift[0]/pitchaforce;
1331 relamp=(omega*omega/(2*_delta*Math::sqrt(sqr(omega0*omega0-omega*omega)
1332 +4*_delta*_delta*omega*omega)))*_cyclic_factor;
1333 for (i=0;i<_number_of_parts;i++)
1334 {
1335 rps[i]->setRelamp(relamp);
1336 }
1337 rps[0]->setOmega(0);
1338 setCollective(0);
1339 setCyclicail(0,0);
1340 setCyclicele(0,0);
1341
1342 writeInfo();
1343
1344 //tie the properties
1345 /* After reset these values are totally wrong. I have to find out why
1346 SGPropertyNode * node = fgGetNode("/rotors", true)->getNode(_name,true);
1347 node->tie("balance_ext",SGRawValuePointer<float>(&_balance2),false);
1348 node->tie("balance_int",SGRawValuePointer<float>(&_balance1));
1349 _properties_tied=1;
1350 */
1351}
1352std::ostream & operator<<(std::ostream & out, Rotor& r)
1353{
1354#define i(x) << #x << ":" << r.x << endl
1355#define iv(x) << #x << ":" << r.x[0] << ";" << r.x[1] << ";" <<r.x[2] << ";" << endl
1356 out << "Writing Info on Rotor "
1357 i(_name)
1358 i(_torque)
1359 i(_omega) i(_omegan) i(_omegarel) i(_ddt_omega) i(_omegarelneu)
1360 i (_chord)
1361 i( _taper)
1362 i( _airfoil_incidence_no_lift)
1363 i( _collective)
1364 i( _airfoil_lift_coefficient)
1365 i( _airfoil_drag_coefficient0)
1366 i( _airfoil_drag_coefficient1)
1367 i( _ccw)
1368 i( _number_of_segments)
1369 i( _number_of_parts)
1370 iv( _base)
1371 iv( _groundeffectpos[0])iv( _groundeffectpos[1])iv( _groundeffectpos[2])iv( _groundeffectpos[3])
1372 i( _ground_effect_altitude)
1373 iv( _normal)
1374 iv( _normal_with_yaw_roll)
1375 iv( _forward)
1376 i( _diameter)
1377 i( _number_of_blades)
1378 i( _weight_per_blade)
1379 i( _rel_blade_center)
1380 i( _min_pitch)
1381 i( _max_pitch)
1382 i( _force_at_pitch_a)
1383 i( _pitch_a)
1384 i( _power_at_pitch_0)
1385 i( _power_at_pitch_b)
1386 i( _no_torque)
1387 i( _sim_blades)
1388 i( _pitch_b)
1389 i( _rotor_rpm)
1390 i( _rel_len_hinge)
1391 i( _maxcyclicail)
1392 i( _maxcyclicele)
1393 i( _mincyclicail)
1394 i( _mincyclicele)
1395 i( _delta3)
1396 i( _delta)
1397 i( _dynamic)
1398 i( _translift)
1399 i( _c2)
1400 i( _stepspersecond)
1401 i( _engineon)
1402 i( _alphamin) i(_alphamax) i(_alpha0) i(_alpha0factor)
1403 i( _teeterdamp) i(_maxteeterdamp)
1404 i( _rellenteeterhinge)
1405 i( _translift_ve)
1406 i( _translift_maxfactor)
1407 i( _ground_effect_constant)
1408 i( _vortex_state_lift_factor)
1409 i( _vortex_state_c1)
1410 i( _vortex_state_c2)
1411 i( _vortex_state_c3)
1412 i( _vortex_state_e1)
1413 i( _vortex_state_e2)
1414 i( _vortex_state_e3)
1415 i( _lift_factor) i(_f_ge) i(_f_vs) i(_f_tl)
1416 i( _vortex_state)
1417 i( _liftcoef)
1418 i( _dragcoef0)
1419 i( _dragcoef1)
1420 i( _twist) //outer incidence = inner inner incidence + _twist
1421 i( _rel_len_where_incidence_is_measured)
1422 i( _torque_of_inertia)
1423 i( _rel_len_blade_start)
1424 i( _incidence_stall_zero_speed)
1425 i( _incidence_stall_half_sonic_speed)
1426 i( _lift_factor_stall)
1427 i( _stall_change_over)
1428 i( _drag_factor_stall)
1429 i( _stall_sum)
1430 i( _stall_v2sum)
1431 i( _yaw)
1432 i( _roll)
1433 i( _cyclicail)
1434 i( _cyclicele)
1435 i( _cyclic_factor) <<endl;
1436 int j;
1437 for(j=0; j<r._rotorparts.size(); j++) {
1438 out << *((Rotorpart*)r._rotorparts.get(j));
1439 }
1440 out <<endl << endl;
1441#undef i
1442#undef iv
1443 return out;
1444}
1445void Rotor:: writeInfo()
1446{
1447#ifdef TEST_DEBUG
1448 std::ostringstream buffer;
1449 buffer << *this;
1450 FILE*f=fopen("c:\\fgmsvc\\bat\\log.txt","at");
1451 if (!f) f=fopen("c:\\fgmsvc\\bat\\log.txt","wt");
1452 if (f)
1453 {
1454 fprintf(f,"%s",(const char *)buffer.str().c_str());
1455 fclose (f);
1456 }
1457#endif
1458}
1459Rotorpart* Rotor::newRotorpart(float zentforce,float maxpitchforce,
1460 float delta3,float mass,float translift,float rellenhinge,float len)
1461{
1462 Rotorpart *r = new Rotorpart();
1463 r->setDelta3(delta3);
1464 r->setDynamic(_dynamic);
1465 r->setTranslift(_translift);
1466 r->setC2(_c2);
1467 r->setWeight(mass);
1468 r->setRelLenHinge(rellenhinge);
1469 r->setSharedFlapHinge(_shared_flap_hinge);
1470 r->setOmegaN(_omegan);
1471 r->setPhi(_phi_null);
1472 r->setAlpha0(_alpha0);
1473 r->setAlphamin(_alphamin);
1474 r->setAlphamax(_alphamax);
1475 r->setAlpha0factor(_alpha0factor);
1476 r->setLen(len);
1477 r->setDiameter(_diameter);
1478 r->setRotor(this);
1479#define p(a) r->setParameter(#a,_##a);
1480 p(twist)
1481 p(number_of_segments)
1482 p(rel_len_where_incidence_is_measured)
1483 p(rel_len_blade_start)
1484 p(rotor_correction_factor)
1485#undef p
1486 return r;
1487}
1488
1489void Rotor::interp(float* v1, float* v2, float frac, float* out)
1490{
1491 out[0] = v1[0] + frac*(v2[0]-v1[0]);
1492 out[1] = v1[1] + frac*(v2[1]-v1[1]);
1493 out[2] = v1[2] + frac*(v2[2]-v1[2]);
1494}
1495
1496void Rotorgear::initRotorIteration(float *lrot,float dt)
1497{
1498 int i;
1499 float omegarel;
1500 if (_rotors.empty()) return;
1501 Rotor* r0 = (Rotor*)_rotors.get(0);
1502 omegarel= r0->getOmegaRelNeu();
1503 for(i=0; i<_rotors.size(); i++) {
1504 Rotor* r = (Rotor*)_rotors.get(i);
1505 r->inititeration(dt,omegarel,0,lrot);
1506 }
1507}
1508
1509void Rotorgear::calcForces(float* torqueOut)
1510{
1511 int i,j;
1512 torqueOut[0]=torqueOut[1]=torqueOut[2]=0;
1513 // check,<if the engine can handle the torque of the rotors.
1514 // If not reduce the torque to the fueselage and change rotational
1515 // speed of the rotors instead
1516 if (! _rotors.empty())
1517 {
1518 float omegarel,omegan;
1519 Rotor* r0 = (Rotor*)_rotors.get(0);
1520 omegarel= r0->getOmegaRel();
1521
1522 float total_torque_of_inertia=0;
1523 float total_torque=0;
1524 for(i=0; i<_rotors.size(); i++) {
1525 Rotor* r = (Rotor*)_rotors.get(i);
1526 omegan=r->getOmegan();
1527 total_torque_of_inertia+=r->getTorqueOfInertia()*omegan*omegan;
1528 //FIXME: this is constant, so this can be done in compile
1529
1530 total_torque+=r->getTorque()*omegan;
1531 }
1532 float max_torque_of_engine=0;
1533 // SGPropertyNode * node=fgGetNode("/rotors/gear", true);
1534 if (_engineon)
1535 {
1536 max_torque_of_engine=_max_power_engine*_max_rel_torque;
1537 float df=_target_rel_rpm-omegarel;
1538 df/=_engine_prop_factor;
1539 df = Math::clamp(df, 0, 1);
1540 max_torque_of_engine = df * _max_power_engine*_max_rel_torque;
1541 }
1542 total_torque*=-1;
1543 _ddt_omegarel=0;
1544
1545#if 0
1546 float rel_torque_engine=1;
1547 if (total_torque<=0)
1548 rel_torque_engine=0;
1549 else
1550 if (max_torque_of_engine>0)
1551 rel_torque_engine=1/max_torque_of_engine*total_torque;
1552 else
1553 rel_torque_engine=0;
1554#endif
1555
1556 //add the rotor brake and the gear fritcion
1557 float dt=0.1f;
1558 if (! r0->_rotorparts.empty()) dt=((Rotorpart*)r0->_rotorparts.get(0))->getDt();
1559
1560 float rotor_brake_torque;
1561 rotor_brake_torque=_rotorbrake*_max_power_rotor_brake+_rotorgear_friction;
1562 //clamp it to the value you need to stop the rotor
1563 //to avod accelerate the rotor to neagtive rpm:
1564 rotor_brake_torque=Math::clamp(rotor_brake_torque,0,
1565 total_torque_of_inertia/dt*omegarel);
1566 max_torque_of_engine-=rotor_brake_torque;
1567
1568 //change the rotation of the rotors
1569 if ((max_torque_of_engine<total_torque) //decreasing rotation
1570 ||((max_torque_of_engine>total_torque)&&(omegarel<_target_rel_rpm))
1571 //increasing rotation due to engine
1572 ||(total_torque<0) ) //increasing rotation due to autorotation
1573 {
1574 _ddt_omegarel=(max_torque_of_engine-total_torque)/total_torque_of_inertia;
1575 if(max_torque_of_engine>total_torque)
1576 {
1577 //check if the acceleration is due to the engine. If yes,
1578 //the engine self limits the accel.
1579 float lim1=-total_torque/total_torque_of_inertia;
1580 //accel. by autorotation
1581
1582 if (lim1<_engine_accel_limit) lim1=_engine_accel_limit;
1583 //if the accel by autorotation greater than the max. engine
1584 //accel, then this is the limit, if not: the engine is the limit
1585 if (_ddt_omegarel>lim1) _ddt_omegarel=lim1;
1586 }
1587 if (_ddt_omegarel>5.5)_ddt_omegarel=5.5;
1588 //clamp it to avoid overflow. Should never be reached
1589 if (_ddt_omegarel<-5.5)_ddt_omegarel=-5.5;
1590
1591 if (_max_power_engine<0.001) {omegarel=1;_ddt_omegarel=0;}
1592 //for debug: negative or no maxpower will result
1593 //in permanet 100% rotation
1594
1595 omegarel+=dt*_ddt_omegarel;
1596
1597 if (omegarel>2.5) omegarel=2.5;
1598 //clamp it to avoid overflow. Should never be reached
1599 if (omegarel<-.5) omegarel=-.5;
1600
1601 r0->setOmegaRelNeu(omegarel);
1602 //calculate the torque, which is needed to accelerate the rotors.
1603 //Add this additional torque to the body
1604 for(j=0; j<_rotors.size(); j++) {
1605 Rotor* r = (Rotor*)_rotors.get(j);
1606 for(i=0; i<r->_rotorparts.size(); i++) {
1607 // float torque_scalar=0;
1608 Rotorpart* rp = (Rotorpart*)r->_rotorparts.get(i);
1609 float torque[3];
1610 rp->getAccelTorque(_ddt_omegarel,torque);
1611 Math::add3(torque,torqueOut,torqueOut);
1612 }
1613 }
1614 }
1615 _total_torque_on_engine=total_torque+_ddt_omegarel*total_torque_of_inertia;
1616 }
1617}
1618
1619void Rotorgear::addRotor(Rotor* rotor)
1620{
1621 _rotors.add(rotor);
1622 _in_use = 1;
1623}
1624
1625void Rotorgear::compile()
1626{
1627 // float wgt = 0;
1628 for(int j=0; j<_rotors.size(); j++) {
1629 Rotor* r = (Rotor*)_rotors.get(j);
1630 r->compile();
1631 }
1632}
1633
1634void Rotorgear::getDownWash(const float *pos, const float * v_heli, float *downwash)
1635{
1636 float tmp[3];
1637 downwash[0]=downwash[1]=downwash[2]=0;
1638 for(int i=0; i<_rotors.size(); i++) {
1639 Rotor* ro = (Rotor*)_rotors.get(i);
1640 ro->getDownWash(pos,v_heli,tmp);
1641 Math::add3(downwash,tmp,downwash); // + downwash
1642 }
1643}
1644
1645void Rotorgear::setParameter(char *parametername, float value)
1646{
1647#define p(a,b) if (strcmp(parametername,#a)==0) _##a = (value * (b)); else
1648 p(max_power_engine,1000)
1649 p(engine_prop_factor,1)
1650 p(yasimdragfactor,1)
1651 p(yasimliftfactor,1)
1652 p(max_power_rotor_brake,1000)
1653 p(rotorgear_friction,1000)
1654 p(engine_accel_limit,0.01)
1655 SG_LOG(SG_INPUT, SG_ALERT,
1656 "internal error in parameter set up for rotorgear: '"
1657 << parametername <<"'" << endl);
1658#undef p
1659}
1660int Rotorgear::getValueforFGSet(int j,char *text,float *f)
1661{
1662 if (j==0)
1663 {
1664 snprintf(text, 300, "/rotors/gear/total-torque");
1665 *f=_total_torque_on_engine;
1666 } else return 0;
1667 return j+1;
1668}
1669Rotorgear::Rotorgear()
1670{
1671 _in_use=0;
1672 _engineon=0;
1673 _rotorbrake=0;
1674 _max_power_rotor_brake=1;
1675 _rotorgear_friction=1;
1676 _max_power_engine=1000*450;
1677 _engine_prop_factor=0.05f;
1678 _yasimdragfactor=1;
1679 _yasimliftfactor=1;
1680 _ddt_omegarel=0;
1681 _engine_accel_limit=0.05f;
1682 _total_torque_on_engine=0;
1683 _target_rel_rpm=1;
1684 _max_rel_torque=1;
1685}
1686
1687Rotorgear::~Rotorgear()
1688{
1689 for(int i=0; i<_rotors.size(); i++)
1690 delete (Rotor*)_rotors.get(i);
1691}
1692
1693}; // namespace yasim
#define p(x)
#define _normal
#define i(x)
#define _forward
#define iv(x)
#define _base
const double g(9.80665)
static float sqr(float a)
Definition Rotor.cpp:31
std::ostream & operator<<(std::ostream &out, Rotor &r)
Definition Rotor.cpp:1352
const float pi
Definition Rotor.cpp:29
SGPropertyNode * fgGetNode(const char *path, bool create)
Get a property node.
Definition proptest.cpp:27
static const double pi
Definition sview.cxx:59