![]() |
JSBSim Flight Dynamics Model 1.0 (23 February 2013)
An Open Source Flight Dynamics and Control Software Library in C++
|
00001 /*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 00002 00003 Header: FGTrimAnalysis.cpp 00004 Author: Agostino De Marco 00005 Date started: Dec/14/2006 00006 00007 ------------- Copyright (C) 2006 Agostino De Marco (agodemar@unina.it) ------- 00008 00009 This program is free software; you can redistribute it and/or modify it under 00010 the terms of the GNU Lesser General Public License as published by the Free Software 00011 Foundation; either version 2 of the License, or (at your option) any later 00012 version. 00013 00014 This program is distributed in the hope that it will be useful, but WITHOUT 00015 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS 00016 FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more 00017 details. 00018 00019 You should have received a copy of the GNU Lesser General Public License along with 00020 this program; if not, write to the Free Software Foundation, Inc., 59 Temple 00021 Place - Suite 330, Boston, MA 02111-1307, USA. 00022 00023 Further information about the GNU Lesser General Public License can also be found on 00024 the world wide web at http://www.gnu.org. 00025 00026 HISTORY 00027 -------------------------------------------------------------------------------- 00028 12/14/06 ADM Created 00029 00030 FUNCTIONAL DESCRIPTION 00031 -------------------------------------------------------------------------------- 00032 00033 This class takes the given set of IC's and analyzes the possible trim states 00034 of the aircraft, i.e. finds the aircraft state required to 00035 maintain a specified flight condition. This flight condition can be 00036 steady-level, a steady turn, a pull-up or pushover. 00037 It is implemented using an iterative, direct search of a cost function minimum. 00038 00039 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 00040 SENTRY 00041 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/ 00042 00043 // !!!!!!! BEWARE ALL YE WHO ENTER HERE !!!!!!! 00044 00045 /*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 00046 INCLUDES 00047 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/ 00048 00049 #include <stdlib.h> 00050 00051 #include "FGFDMExec.h" 00052 #include "models/FGAtmosphere.h" 00053 #include "initialization/FGInitialCondition.h" 00054 #include "FGTrimAnalysis.h" 00055 #include "models/FGAircraft.h" 00056 #include "models/FGMassBalance.h" 00057 #include "models/FGGroundReactions.h" 00058 #include "models/FGInertial.h" 00059 #include "models/FGAerodynamics.h" 00060 #include "math/FGColumnVector3.h" 00061 00062 #include "input_output/FGPropertyManager.h" 00063 #include "input_output/FGXMLParse.h" 00064 00065 #include "models/propulsion/FGPiston.h" 00066 #include "models/propulsion/FGPropeller.h" 00067 #include "models/propulsion/FGTurbine.h" 00068 00069 #include "math/FGTable.h" 00070 00071 #if defined(sgi) && !defined(__GNUC__) 00072 # include <math.h> 00073 #else 00074 # include <cmath> 00075 #endif 00076 #include <sstream> 00077 #include <map> 00078 00079 #include "math/direct_search/SMDSearch.h" 00080 #include "math/direct_search/NMSearch.h" 00081 #include "math/direct_search/vec.h" 00082 00083 #if _MSC_VER 00084 #pragma warning (disable : 4786 4788) 00085 #endif 00086 00087 namespace JSBSim { 00088 00089 static const char *IdSrc = "$Id: FGTrimAnalysis.cpp,v 1.14 2011/03/23 11:58:29 jberndt Exp $"; 00090 static const char *IdHdr = ID_FGTRIMANALYSIS; 00091 00092 00105 //friend void find_CostFunctionFull(long vars, Vector<double> &v, double & f, 00106 void find_CostFunctionFull(long vars, Vector<double> &v, double & f, 00107 bool & success, void* t_ptr) 00108 { 00109 (*(Objective*)t_ptr).CostFunctionFull(vars, v, f); 00110 success = true; 00111 } 00124 //friend void find_CostFunctionFullWingsLevel(long vars, Vector<double> &v, double & f, 00125 void find_CostFunctionFullWingsLevel(long vars, Vector<double> &v, double & f, 00126 bool & success, void* t_ptr) 00127 { 00128 (*(Objective*)t_ptr).CostFunctionFullWingsLevel(vars, v, f); 00129 success = true; 00130 } 00143 //friend void find_CostFunctionLongitudinal(long vars, Vector<double> &v, double & f, 00144 void find_CostFunctionLongitudinal(long vars, Vector<double> &v, double & f, 00145 bool & success, void* t_ptr) 00146 { 00147 (*(Objective*)t_ptr).CostFunctionLongitudinal(vars, v, f); 00148 success = true; 00149 } 00162 //friend void find_CostFunctionFullCoordinatedTurn(long vars, Vector<double> &v, double & f, 00163 void find_CostFunctionFullCoordinatedTurn(long vars, Vector<double> &v, double & f, 00164 bool & success, void* t_ptr) 00165 { 00166 (*(Objective*)t_ptr).CostFunctionFullCoordinatedTurn(vars, v, f); 00167 success = true; 00168 } 00181 //fryyiend void find_CostFunctionFullTurn(long vars, Vector<double> &v, double & f, 00182 void find_CostFunctionFullTurn(long vars, Vector<double> &v, double & f, 00183 bool & success, void* t_ptr) 00184 { 00185 (*(Objective*)t_ptr).CostFunctionFullTurn(vars, v, f); 00186 success = true; 00187 } 00200 //friend void find_CostFunctionPullUp(long vars, Vector<double> &v, double & f, 00201 void find_CostFunctionPullUp(long vars, Vector<double> &v, double & f, 00202 bool & success, void* t_ptr) 00203 { 00204 (*(Objective*)t_ptr).CostFunctionPullUp(vars, v, f); 00205 success = true; 00206 } 00207 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 00208 //public: 00209 00210 Objective::Objective(FGFDMExec* fdmex, FGTrimAnalysis* ta, double x) : _x(x), TrimAnalysis(ta), FDMExec(fdmex) 00211 { 00212 // populate the map:type-of-trim/function-pointer 00213 mpCostFunctions[taFull ] = find_CostFunctionFull; 00214 mpCostFunctions[taFullWingsLevel ] = find_CostFunctionFullWingsLevel; 00215 mpCostFunctions[taLongitudinal ] = find_CostFunctionLongitudinal; 00216 mpCostFunctions[taTurn ] = find_CostFunctionFullCoordinatedTurn; 00217 mpCostFunctions[taPullup ] = find_CostFunctionPullUp; 00218 } 00219 00220 void Objective::CostFunctionFull(long vars, Vector<double> &v, double & f) 00221 { 00222 if (vars != 7) { 00223 cerr << "\nError: (Cost function for taFull mode) Dimension must be 7 !!\n"; 00224 exit(1); 00225 } 00226 if (TrimAnalysis->GetMode()!=taFull){ 00227 cerr << "\nError: must be taFull mode !!\n"; 00228 exit(1); 00229 } 00230 00231 f = myCostFunctionFull(v); 00232 return; 00233 } 00234 00235 void Objective::CostFunctionFullWingsLevel(long vars, Vector<double> &v, double & f) 00236 { 00237 if (vars != 6) { 00238 cerr << "\nError: (Cost function for taFullWingsLevel mode) Dimension must be 6 !!\n"; 00239 exit(1); 00240 } 00241 if (TrimAnalysis->GetMode()!=taFullWingsLevel){ 00242 cerr << "\nError: must be taFull mode !!\n"; 00243 exit(1); 00244 } 00245 00246 f = myCostFunctionFullWingsLevel(v); 00247 return; 00248 } 00249 00250 void Objective::CostFunctionLongitudinal(long vars, Vector<double> &v, double & f) 00251 { 00252 if (vars != 3) { 00253 cerr << "\nError: (Cost function for taLongitudinal mode) Dimension must be 3 !!\n"; 00254 exit(1); 00255 } 00256 if (TrimAnalysis->GetMode()!=taLongitudinal){ 00257 cerr << "\nError: trim mode must be taLongitudinal mode !!\n"; 00258 exit(1); 00259 } 00260 00261 f = myCostFunctionLongitudinal(v); 00262 return; 00263 } 00264 00265 void Objective::CostFunctionFullCoordinatedTurn(long vars, Vector<double> &v, double & f) 00266 { 00267 if (vars != 5) { 00268 cerr << "\nError: (Cost function for taTurn mode) Dimension must be 5 !!\n"; 00269 exit(1); 00270 } 00271 if (TrimAnalysis->GetMode()!=taTurn){ 00272 cerr << "\nError: trim mode must be taTurn mode !!\n"; 00273 exit(1); 00274 } 00275 00276 f = myCostFunctionFullCoordinatedTurn(v); 00277 return; 00278 } 00279 00280 void Objective::CostFunctionFullTurn(long vars, Vector<double> &v, double & f) 00281 { 00282 if (vars != 6) { 00283 cerr << "\nError: (Cost function for taTurn mode) Dimension must be 6 !!\n"; 00284 exit(1); 00285 } 00286 if (TrimAnalysis->GetMode()!=taTurnFull){ 00287 cerr << "\nError: trim mode must be taTurnFull ("<< (int)taTurnFull << ") mode !!\n"; 00288 exit(1); 00289 } 00290 00291 f = myCostFunctionFullTurn(v); 00292 return; 00293 } 00294 00295 void Objective::CostFunctionPullUp(long vars, Vector<double> &v, double & f) 00296 { 00297 if (vars != 5) { 00298 cerr << "\nError: (Cost function for taPullup mode) Dimension must be 5 !!\n"; 00299 exit(1); 00300 } 00301 if (TrimAnalysis->GetMode()!=taPullup){ 00302 cerr << "\nError: trim mode must be taPullup mode !!\n"; 00303 exit(1); 00304 } 00305 00306 f = myCostFunctionPullUp(v); 00307 return; 00308 } 00309 00310 void Objective::Set_x_val(double new_x) 00311 { 00312 _x = new_x; 00313 } 00314 00315 //private: 00316 // see the end of the file 00317 00318 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 00319 00320 FGTrimAnalysis::FGTrimAnalysis(FGFDMExec *FDMExec,TrimAnalysisMode tt) 00321 { 00322 SetDebug(2); 00323 00324 N=0; 00325 00326 trim_failed = true; 00327 00328 max_iterations=2500; 00329 stop_criterion="Stop-On-Delta"; 00330 00331 Debug=0;DebugLevel=0; 00332 00333 fdmex=FDMExec; 00334 00335 fgic=fdmex->GetIC(); 00336 total_its=0; 00337 trimudot=true; 00338 gamma_fallback=true; 00339 ctrl_count=0; 00340 mode=tt; 00341 _targetNlf=1.0; 00342 _targetNlf=fgic->GetTargetNlfIC(); 00343 00344 _vtIC = fgic->GetVtrueFpsIC(); 00345 _hIC = fgic->GetAltitudeFtIC(); 00346 _gamma = fgic->GetFlightPathAngleRadIC(); 00347 _rocIC = _vtIC*cos(_gamma); 00348 _vdownIC = _rocIC; 00349 00350 // state variables 00351 _u = fgic->GetUBodyFpsIC(); 00352 _v = fgic->GetVBodyFpsIC(); 00353 _w = fgic->GetWBodyFpsIC(); 00354 _p = fgic->GetPRadpsIC(); 00355 _q = fgic->GetQRadpsIC(); 00356 _r = fgic->GetRRadpsIC(); 00357 _alpha = fgic->GetAlphaRadIC(); 00358 _beta = fgic->GetBetaRadIC(); 00359 _theta = fgic->GetThetaRadIC(); 00360 _phi = fgic->GetPhiRadIC(); 00361 _psiIC = fgic->GetPsiRadIC(); 00362 _psi = _psiIC; 00363 _psigtIC = _psi; 00364 00365 _phiW = _psiW = 0.0; 00366 00367 _vgIC = _vtIC*cos(_gamma); 00368 _vnorthIC = _vgIC * cos(_psigtIC); 00369 _veastIC = _vgIC * sin(_psigtIC); 00370 wnorthIC = _weastIC = _wdownIC = 0.; 00371 00372 _udot=_vdot=_wdot=_pdot=_qdot=_rdot=0.; 00373 _psidot=_thetadot=0.; 00374 _psiWdot = _phiWdot = _gammadot = 0.; 00375 00376 C1 = C2 = C3 = 1.0; 00377 _cbeta = cos(_beta); 00378 _sbeta = sin(_beta); 00379 _sphi = sin(_phi); 00380 00381 SetMode(tt); // creates vTrimAnalysisControls 00382 fdmex->SetTrimMode( (int)tt ); 00383 00384 trim_id = "default-trim"; 00385 00386 // direct search stuff 00387 search_type = "Nelder-Mead"; 00388 sigma_nm = 0.5; alpha_nm = 1.0; beta_nm = 0.5; gamma_nm = 2.0; 00389 initial_step = 0.01; 00390 tolerance = 1.0E-10; // 0.0000000001 00391 cost_function_value = 9999.0; 00392 00393 rf_name = ""; 00394 if (rf.is_open()) rf.close(); 00395 00396 Auxiliary = fdmex->GetAuxiliary(); 00397 Aerodynamics = fdmex->GetAerodynamics(); 00398 Propulsion = fdmex->GetPropulsion(); 00399 FCS = fdmex->GetFCS(); 00400 } 00401 00402 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 00403 00404 FGTrimAnalysis::~FGTrimAnalysis(void) { 00405 00406 ClearControls(); 00407 00408 vAlphaDeg.clear(); 00409 vCL.clear(); vCD.clear(); vCm.clear(); 00410 vThrottleCmd.clear(); vElevatorCmd.clear(); 00411 vVn.clear(); vTn.clear(); 00412 00413 if (rf.is_open()) rf.close(); 00414 00415 fdmex->SetTrimStatus(false); 00416 fdmex->SetTrimMode( 99 ); 00417 } 00418 00419 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 00420 00421 void FGTrimAnalysis::SetState(double u0, double v0, double w0, double p0, double q0, double r0, 00422 double alpha0, double beta0, double phi0, double theta0, double psi0, double gamma0) 00423 { 00424 _u=u0; _v=v0; _w=w0; 00425 _p=p0; _q=q0; _r=r0; 00426 _alpha=alpha0; _beta=beta0; _gamma=gamma0; 00427 _theta=theta0; _phi=phi0; _psi=psi0; 00428 } 00429 00430 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 00431 00432 void FGTrimAnalysis::SetEulerAngles(double phi, double theta, double psi) 00433 { 00434 // feed into private variables 00435 _phi = phi; _cphi = cos(_phi); _sphi = sin(_phi); 00436 _theta = theta; _ctheta = cos(_theta); _stheta = sin(_theta); 00437 _psi = psi; _cpsi = cos(_psi); _spsi = sin(_psi); 00438 00439 } 00440 00441 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 00442 00443 void FGTrimAnalysis::SetDottedValues(double udot, double vdot, double wdot, double pdot, double qdot, double rdot) 00444 { 00445 _udot=udot; _vdot=vdot; _wdot=wdot; _pdot=pdot; _qdot=qdot; _rdot=rdot; 00446 } 00447 00448 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 00449 00450 bool FGTrimAnalysis::Load(string fname, bool useStoredPath) 00451 { 00452 string name="", type=""; 00453 string trimDef; 00454 Element *element=0, *trimCfg=0, *search_element=0, *output_element=0; 00455 00456 string sep = "/"; 00457 # ifdef macintosh 00458 sep = ";"; 00459 # endif 00460 00461 if( useStoredPath ) { 00462 trimDef = fdmex->GetFullAircraftPath() + sep + fname + ".xml"; 00463 } else { 00464 trimDef = fname; 00465 } 00466 00467 document = this->LoadXMLDocument(trimDef); 00468 00469 trimCfg = document->FindElement("trim_config"); 00470 if (!trimCfg) { 00471 cerr << "File: " << trimDef << " does not contain a trim configuration tag" << endl; 00472 return false; 00473 } 00474 00475 name = trimCfg->GetAttributeValue("name"); 00476 trim_id = name; 00477 00478 // First, find "search" element that specifies the type of cost function minimum search 00479 00480 search_element = trimCfg->FindElement("search"); 00481 if (!search_element) { 00482 cerr << "Using the Nelder-Mead search algorithm (default)." << endl; 00483 } else { 00484 type = search_element->GetAttributeValue("type"); 00485 if (type.size() > 0) search_type = type; // if search type is not set, default is already Nelder-Mead 00486 if (search_type == "Nelder-Mead") { 00487 // Read settings from search 00488 // Note: all of these have defaults set above 00489 if ( search_element->FindElement("sigma_nm") ) 00490 sigma_nm = search_element->FindElementValueAsNumber("sigma_nm"); 00491 if ( search_element->FindElement("alpha_nm") ) 00492 alpha_nm = search_element->FindElementValueAsNumber("alpha_nm"); 00493 if ( search_element->FindElement("beta_nm") ) 00494 beta_nm = search_element->FindElementValueAsNumber("beta_nm"); 00495 if ( search_element->FindElement("gamma_nm") ) 00496 gamma_nm = search_element->FindElementValueAsNumber("gamma_nm"); 00497 } 00498 // ToDo: manage all the other possible choices here 00499 // if (search_type == "Sequential-Multiple-Nelder-Mead") { } 00500 // if (search_type == "Multicompass") { } 00501 // etc ... 00502 if ( search_element->FindElement("tolerance") ) { 00503 tolerance = search_element->FindElement("tolerance")->GetAttributeValueAsNumber("value"); 00504 } 00505 if ( search_element->FindElement("max_iterations") ) { 00506 max_iterations = (unsigned int)search_element->FindElement("max_iterations")->GetAttributeValueAsNumber("value"); 00507 } 00508 if ( search_element->FindElement("stop_criterion") ) { 00509 stop_criterion = search_element->FindElement("stop_criterion")->GetAttributeValue("type"); 00510 } 00511 } 00512 00513 // Initialize trim controls based on what is in the trim config file. This 00514 // includes initial trim values (or defaults from the IC file) and step 00515 // size values. 00516 00517 element = trimCfg->FindElement("phi"); 00518 InitializeTrimControl(fgic->GetPhiRadIC(), element, "RAD", JSBSim::taPhi); 00519 if ( ( fabs(_phi) < 89.5*(FGJSBBase::degtorad ) ) && ( mode == taTurn )) 00520 _targetNlf = 1./cos(_phi); 00521 00522 element = trimCfg->FindElement("theta"); 00523 InitializeTrimControl(fgic->GetThetaRadIC(), element, "RAD", JSBSim::taTheta); 00524 00525 element = trimCfg->FindElement("psi"); 00526 InitializeTrimControl(fgic->GetPsiRadIC(), element, "RAD", JSBSim::taHeading); 00527 00528 element = trimCfg->FindElement("gamma"); 00529 _gamma = fgic->GetFlightPathAngleRadIC(); 00530 if (element) 00531 if (element->GetNumDataLines() > 0) _gamma = element->GetDataAsNumber(); 00532 00533 element = trimCfg->FindElement("nlf"); 00534 if (element) { 00535 if (element->GetNumDataLines() > 0) _targetNlf = element->GetDataAsNumber(); 00536 CalculatePhiWFromTargetNlfTurn(_targetNlf); 00537 } 00538 00539 element = trimCfg->FindElement("throttle_cmd"); 00540 if (element) InitializeTrimControl(0, element, "", JSBSim::taThrottle); 00541 00542 element = trimCfg->FindElement("elevator_cmd"); 00543 if (element) InitializeTrimControl(0, element, "", JSBSim::taElevator); 00544 00545 element = trimCfg->FindElement("rudder_cmd"); 00546 if (element) InitializeTrimControl(0, element, "", JSBSim::taRudder); 00547 00548 element = trimCfg->FindElement("aileron_cmd"); 00549 if (element) InitializeTrimControl(0, element, "", JSBSim::taAileron); 00550 00551 output_element = trimCfg->FindElement("output_file"); 00552 if (output_element) { 00553 rf_name = output_element->GetAttributeValue("name"); 00554 if (rf_name.empty()) { 00555 cerr << "name must be specified in output_file \"name\" attribute."<< endl; 00556 } else { 00557 if ( !SetResultsFile(rf_name) ) 00558 cerr << "Unable to use output file "<< rf_name << endl; 00559 } 00560 } 00561 return true; 00562 } 00563 00564 00565 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 00566 00567 bool FGTrimAnalysis::InitializeTrimControl(double default_value, Element* el, 00568 string unit, TaControl type) 00569 { 00570 Element *step_size_element=0, *trim_config=0; 00571 double iv = 0.0; 00572 double step = 0.0; // default step value 00573 bool set_override = false; 00574 00575 iv = default_value; 00576 00577 if (el != 0) { 00578 string name = el->GetName(); 00579 trim_config = el->GetParent(); 00580 set_override = el->GetNumDataLines() != 0; 00581 if (set_override) { 00582 if (unit.empty()) iv = trim_config->FindElementValueAsNumber(name); 00583 else iv = trim_config->FindElementValueAsNumberConvertTo(name, unit); 00584 } 00585 if (el->GetAttributeValueAsNumber("step_size") != HUGE_VAL) 00586 step = el->GetAttributeValueAsNumber("step_size"); 00587 } 00588 00589 for (unsigned int i=0; i<vTrimAnalysisControls.size(); i++) { 00590 if (vTrimAnalysisControls[i]->GetControlType() == type) { 00591 vTrimAnalysisControls[i]->SetControlInitialValue(iv); 00592 vTrimAnalysisControls[i]->SetControlStep(step); 00593 break; 00594 } 00595 } 00596 00597 return set_override; 00598 } 00599 00600 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 00601 00602 void FGTrimAnalysis::TrimStats() { 00603 int run_sum=0; 00604 cout << endl << " Trim Statistics: " << endl; 00605 cout << " Total Iterations: " << total_its << endl; 00606 } 00607 00608 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 00609 00610 void FGTrimAnalysis::Report(void) { 00611 00612 cout << "---------------------------------------------------------------------\n"; 00613 00614 cout << "Trim report: " << endl; 00615 cout << "\tTrim algorithm terminated with the following values:" << endl; 00616 cout << "\tu, v, w (ft/s): " << _u <<", "<< _v <<", "<< _w << endl 00617 << "\tp, q, r (rad/s): " << _p <<", "<< _q <<", "<< _r << endl 00618 << "\talpha, beta (deg): " << _alpha*57.3 <<", "<< _beta*57.3 << endl 00619 << "\tphi, theta, psi (deg): " << _phi*57.3 <<", "<< _theta*57.3 << ", " << _psi*57.3 << endl 00620 << "\tCost function value : " << cost_function_value << endl 00621 << "\tCycles executed : " << total_its << endl << endl; 00622 00623 cout << "\tTrim variables adjusted:" << endl; 00624 for (unsigned int i=0; i<vTrimAnalysisControls.size();i++){ 00625 cout << "\t\t" << vTrimAnalysisControls[i]->GetControlName() <<": "; 00626 cout << vTrimAnalysisControls[i]->GetControl() << endl; 00627 } 00628 //... 00629 00630 cout << endl; 00631 00632 cout << "\t** Initial -> Final Conditions **" << endl; 00633 cout << "\tAlpha IC: " << fgic->GetAlphaDegIC() << " Degrees" << endl; 00634 cout << "\t Final: " << Auxiliary->Getalpha()*57.3 << " Degrees" << endl; 00635 cout << "\tBeta IC: " << fgic->GetBetaDegIC() << " Degrees" << endl; 00636 cout << "\t Final: " << Auxiliary->Getbeta()*57.3 << " Degrees" << endl; 00637 cout << "\tGamma IC: " << fgic->GetFlightPathAngleDegIC() << " Degrees" << endl; 00638 cout << "\t Final: " << Auxiliary->GetGamma()*57.3 << " Degrees" << endl; 00639 cout << "\tPhi IC : " << fgic->GetPhiDegIC() << " Degrees" << endl; 00640 cout << "\t Final: " << fdmex->GetPropagate()->GetEuler(1)*57.3 << " Degrees" << endl; 00641 cout << "\tTheta IC: " << fgic->GetThetaDegIC() << " Degrees" << endl; 00642 cout << "\t Final: " << fdmex->GetPropagate()->GetEuler(2)*57.3 << " Degrees" << endl; 00643 cout << "\tPsi IC : " << fgic->GetPsiDegIC() << " Degrees" << endl; 00644 cout << "\t Final: " << fdmex->GetPropagate()->GetEuler(3)*57.3 << " Degrees" << endl; 00645 cout << endl; 00646 cout << "--------------------------------------------------------------------- \n\n"; 00647 00648 fdmex->EnableOutput(); 00649 } 00650 00651 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 00652 00653 void FGTrimAnalysis::ClearControls(void) { 00654 00655 FGTrimAnalysisControl* tac; 00656 00657 mode=taCustom; 00658 vector<FGTrimAnalysisControl*>::iterator iControls; 00659 iControls = vTrimAnalysisControls.begin(); 00660 while (iControls != vTrimAnalysisControls.end()) { 00661 tac=*iControls; 00662 delete tac; 00663 00664 iControls++; 00665 } 00666 00667 vTrimAnalysisControls.clear(); 00668 00669 } 00670 00671 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 00672 00673 bool FGTrimAnalysis::AddControl( TaControl control ) { 00674 00675 FGTrimAnalysisControl* tac; 00676 bool result=true; 00677 00678 mode = taCustom; 00679 vector <FGTrimAnalysisControl*>::iterator iControls = vTrimAnalysisControls.begin(); 00680 while (iControls != vTrimAnalysisControls.end()) { 00681 tac=*iControls; 00682 if( tac->GetControlType() == control ) 00683 result=false; 00684 iControls++; 00685 } 00686 if(result) { 00687 vTrimAnalysisControls.push_back(new FGTrimAnalysisControl(fdmex,fgic,control)); 00688 } 00689 return result; 00690 } 00691 00692 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 00693 00694 bool FGTrimAnalysis::RemoveControl( TaControl control ) { 00695 00696 FGTrimAnalysisControl* tac; 00697 bool result=false; 00698 00699 mode = taCustom; 00700 vector <FGTrimAnalysisControl*>::iterator iControls = vTrimAnalysisControls.begin(); 00701 while (iControls != vTrimAnalysisControls.end()) { 00702 tac=*iControls; 00703 if( tac->GetControlType() == control ) { 00704 delete tac; 00705 vTrimAnalysisControls.erase(iControls); 00706 result=true; 00707 continue; 00708 } 00709 iControls++; 00710 } 00711 00712 return result; 00713 } 00714 00715 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 00716 00717 bool FGTrimAnalysis::EditState( TaControl new_control, double new_initvalue, double new_step, double new_min, double new_max ) { 00718 FGTrimAnalysisControl* tac; 00719 bool result=false; 00720 00721 mode = taCustom; 00722 vector <FGTrimAnalysisControl*>::iterator iControls = vTrimAnalysisControls.begin(); 00723 while (iControls != vTrimAnalysisControls.end()) { 00724 tac=*iControls; 00725 if( tac->GetControlType() == new_control ) { 00726 vTrimAnalysisControls.insert(iControls,1,new FGTrimAnalysisControl(fdmex,fgic,new_control)); 00727 delete tac; 00728 vTrimAnalysisControls.erase(iControls+1); 00729 result=true; 00730 break; 00731 } 00732 iControls++; 00733 } 00734 return result; 00735 } 00736 00737 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 00738 00739 void FGTrimAnalysis::setupPullup() { 00740 double g,q,cgamma; 00741 g=fdmex->GetInertial()->gravity(); 00742 cgamma=cos(fgic->GetFlightPathAngleRadIC()); 00743 q=g*(_targetNlf-cgamma)/fgic->GetVtrueFpsIC(); 00744 cout << _targetNlf << ", " << q << endl; 00745 fgic->SetQRadpsIC(q); 00746 updateRates(); 00747 00748 } 00749 00750 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 00751 00752 void FGTrimAnalysis::CalculatePhiWFromTargetNlfTurn(double nlf){ 00753 if ( ( mode == taTurn ) || ( mode == taTurnFull ) ) { 00754 // target Nlf is given 00755 // set _phiW according to given Nlf 00756 _targetNlf = nlf; 00757 _phiW = atan2( sqrt(_targetNlf*_targetNlf-cos(_gamma)*cos(_gamma)), cos(_gamma) ); 00758 } 00759 } 00760 00761 //ToDo: check formulas for nonzero gamma 00762 void FGTrimAnalysis::setupTurn(void){ 00763 00764 if ( mode == taTurn ) { 00765 // target Nlf is given 00766 // set _phiW according to given Nlf 00767 _phiW = atan2( sqrt(_targetNlf*_targetNlf-cos(_gamma)*cos(_gamma)), cos(_gamma) ); 00768 C1 = cos(_phiW)*sin(_gamma)*cos(_theta)*sin(_psi)+ 00769 sin(_phiW) *cos(_theta)*cos(_psi) ; 00770 C2 = cos(_phiW)*cos(_gamma)*cos(_theta)*cos(_psi)+ 00771 cos(_phiW)*sin(_gamma)*sin(_theta) ; 00772 C3 = sin(_phiW) *sin(_theta)+ 00773 cos(_phiW)*cos(_gamma)*cos(_theta)*sin(_psi) ; 00774 _cbeta = ( C1*sin(_phiW)*cos(_gamma)+C2*cos(_phiW) + C3*sin(_phiW)*sin(_gamma) ) 00775 /( sqrt(C1*C1 + C2*C2 + C3*C3) ); 00776 _sbeta = sqrt(1.-_cbeta*_cbeta ); 00777 _sphi = ( _cbeta*sin(_phiW) * cos(_gamma) - _sbeta*sin(_gamma) )/cos(_theta); 00778 _phi = asin(_sphi); 00779 00780 // theta_underlined (see paper on Trim) for coordinated turn 00781 _theta = atan2( sin(_psi)*cos(_gamma)+cos(_psi)*sin(_gamma), cos(_gamma) ); 00782 00783 double g,V; 00784 V = sqrt( _u*_u +_v*_v + _w*_w ); 00785 g=fdmex->GetInertial()->gravity(); 00786 _psiWdot = (g/V)*tan(_phiW); 00787 updateRates(); 00788 } 00789 if ( mode == taTurnFull ) { 00790 // target Nlf is given 00791 C1 = cos(_phiW)*sin(_gamma)*cos(_theta)*sin(_psi)+ 00792 sin(_phiW) *cos(_theta)*cos(_psi) ; 00793 C2 = cos(_phiW)*cos(_gamma)*cos(_theta)*cos(_psi)+ 00794 cos(_phiW)*sin(_gamma)*sin(_theta) ; 00795 C3 = sin(_phiW) *sin(_theta)+ 00796 cos(_phiW)*cos(_gamma)*cos(_theta)*sin(_psi) ; 00797 _cbeta = ( C1*sin(_phiW)*cos(_gamma)+C2*cos(_phiW) + C3*sin(_phiW)*sin(_gamma) ) 00798 /( sqrt(C1*C1 + C2*C2 + C3*C3) ); 00799 _sbeta = sqrt(1.-_cbeta*_cbeta ); 00800 _sphi = ( _cbeta*sin(_phiW) * cos(_gamma) - _sbeta*sin(_gamma) )/cos(_theta); 00801 _phi = asin(_sphi); 00802 00803 double g,V; 00804 V = sqrt( _u*_u +_v*_v + _w*_w ); 00805 g=fdmex->GetInertial()->gravity(); 00806 _psiWdot = (g/V)*tan(_phiW); 00807 updateRates(); 00808 } 00809 } 00810 00811 void FGTrimAnalysis::setupTurn(double phiW){ 00812 if ( mode == taTurn ) { 00813 _phiW = phiW; 00814 // recalculate target Nlf 00815 _targetNlf = sqrt( cos(_gamma)*cos(_gamma)*tan(_phiW)*tan(_phiW) + cos(_gamma)*cos(_gamma) ); 00816 00817 C1 = cos(_phiW)*sin(_gamma)*cos(_theta)*sin(_psi)+ 00818 sin(_phiW) *cos(_theta)*cos(_psi) ; 00819 C2 = cos(_phiW)*cos(_gamma)*cos(_theta)*cos(_psi)+ 00820 cos(_phiW)*sin(_gamma)*sin(_theta) ; 00821 C3 = sin(_phiW) *sin(_theta)+ 00822 cos(_phiW)*cos(_gamma)*cos(_theta)*sin(_psi) ; 00823 _cbeta = ( C1*sin(_phiW)*cos(_gamma)+C2*cos(_phiW) + C3*sin(_phiW)*sin(_gamma) ) 00824 /( sqrt(C1*C1 + C2*C2 + C3*C3) ); 00825 _sbeta = sqrt(1.-_cbeta*_cbeta ); 00826 _sphi = ( _cbeta*sin(_phiW) * cos(_gamma) - _sbeta*sin(_gamma) )/cos(_theta); 00827 _phi = asin(_sphi); 00828 00829 _cphi = cos(_phi); 00830 00831 // theta_underlined (see paper on Trim) for coordinated turn 00832 //_theta = atan2( sin(_psi)*cos(_gamma)+cos(_psi)*sin(_gamma), cos(_gamma) ); 00833 00834 double g,V; 00835 V = sqrt( _u*_u +_v*_v + _w*_w ); 00836 g=fdmex->GetInertial()->gravity(); 00837 _psiWdot = (g/V)*tan(_phiW); 00838 updateRates(); 00839 } 00840 if ( mode == taTurnFull ) { 00841 _phiW = phiW; 00842 // recalculate target Nlf 00843 _targetNlf = sqrt( cos(_gamma)*cos(_gamma)*tan(_phiW)*tan(_phiW) + cos(_gamma)*cos(_gamma) ); 00844 00845 C1 = cos(_phiW)*sin(_gamma)*cos(_theta)*sin(_psi)+ 00846 sin(_phiW) *cos(_theta)*cos(_psi) ; 00847 C2 = cos(_phiW)*cos(_gamma)*cos(_theta)*cos(_psi)+ 00848 cos(_phiW)*sin(_gamma)*sin(_theta) ; 00849 C3 = sin(_phiW) *sin(_theta)+ 00850 cos(_phiW)*cos(_gamma)*cos(_theta)*sin(_psi) ; 00851 _cbeta = ( C1*sin(_phiW)*cos(_gamma)+C2*cos(_phiW) + C3*sin(_phiW)*sin(_gamma) ) 00852 /( sqrt(C1*C1 + C2*C2 + C3*C3) ); 00853 _sbeta = sqrt(1.-_cbeta*_cbeta ); 00854 _sphi = ( _cbeta*sin(_phiW) * cos(_gamma) - _sbeta*sin(_gamma) )/cos(_theta); 00855 _phi = asin(_sphi); 00856 00857 _cphi = cos(_phi); 00858 00859 // theta_underlined (see paper on Trim) for coordinated turn 00860 //_theta = atan2( sin(_psi)*cos(_gamma)+cos(_psi)*sin(_gamma), cos(_gamma) ); 00861 00862 double g,V; 00863 V = sqrt( _u*_u +_v*_v + _w*_w ); 00864 g=fdmex->GetInertial()->gravity(); 00865 _psiWdot = (g/V)*tan(_phiW); 00866 updateRates(); 00867 } 00868 00869 } 00870 00871 //ToDo: check formulas for nonzero gamma 00872 void FGTrimAnalysis::setupTurnPhi(double psi, double theta){ 00873 if ( mode == taTurn ) { 00874 _psi = psi; _cpsi = cos(_psi); _spsi = sin(_psi); 00875 _theta = theta; _ctheta = cos(_theta); _stheta = sin(_theta); 00876 00877 C1 = cos(_phiW)*sin(_gamma)*cos(_theta)*sin(_psi)+ 00878 sin(_phiW) *cos(_theta)*cos(_psi) ; 00879 C2 = cos(_phiW)*cos(_gamma)*cos(_theta)*cos(_psi)+ 00880 cos(_phiW)*sin(_gamma)*sin(_theta) ; 00881 C3 = sin(_phiW) *sin(_theta)+ 00882 cos(_phiW)*cos(_gamma)*cos(_theta)*sin(_psi) ; 00883 _cbeta = ( C1*sin(_phiW)*cos(_gamma)+C2*cos(_phiW) + C3*sin(_phiW)*sin(_gamma) ) 00884 /( sqrt(C1*C1 + C2*C2 + C3*C3) ); 00885 _sbeta = sqrt(1.-_cbeta*_cbeta ); 00886 00887 _sphi = ( _cbeta*sin(_phiW) * cos(_gamma) - _sbeta*sin(_gamma) )/cos(_theta); 00888 _phi = asin(_sphi); 00889 _cphi = cos(_phi); 00890 } 00891 if ( mode == taTurnFull ) { 00892 _psi = psi; _cpsi = cos(_psi); _spsi = sin(_psi); 00893 _theta = theta; _ctheta = cos(_theta); _stheta = sin(_theta); 00894 00895 C1 = cos(_phiW)*sin(_gamma)*cos(_theta)*sin(_psi)+ 00896 sin(_phiW) *cos(_theta)*cos(_psi) ; 00897 C2 = cos(_phiW)*cos(_gamma)*cos(_theta)*cos(_psi)+ 00898 cos(_phiW)*sin(_gamma)*sin(_theta) ; 00899 C3 = sin(_phiW) *sin(_theta)+ 00900 cos(_phiW)*cos(_gamma)*cos(_theta)*sin(_psi) ; 00901 _cbeta = ( C1*sin(_phiW)*cos(_gamma)+C2*cos(_phiW) + C3*sin(_phiW)*sin(_gamma) ) 00902 /( sqrt(C1*C1 + C2*C2 + C3*C3) ); 00903 _sbeta = sqrt(1.-_cbeta*_cbeta ); 00904 00905 _sphi = ( _cbeta*sin(_phiW) * cos(_gamma) - _sbeta*sin(_gamma) )/cos(_theta); 00906 _phi = asin(_sphi); 00907 _cphi = cos(_phi); 00908 00909 } 00910 } 00911 00912 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 00913 00914 FGColumnVector3 FGTrimAnalysis::UpdateRatesTurn(double psi, double theta, double phi, double phiW){ 00915 _psi = psi; 00916 setupTurn(phiW); // calls updateRates: updates _p,_q,_r 00917 return FGColumnVector3(_p,_q,_r); 00918 } 00919 00920 FGColumnVector3 FGTrimAnalysis::UpdateRatesPullup(void){ 00921 double g,cgamma; 00922 g=fdmex->GetInertial()->gravity(); 00923 cgamma=cos(fgic->GetFlightPathAngleRadIC()); 00924 _p = 0; 00925 _q = g*(_targetNlf-cgamma)/fgic->GetVtrueFpsIC(); 00926 _r = 0.; 00927 00928 fgic->SetQRadpsIC(_q); 00929 return FGColumnVector3(_p,_q,_r); 00930 } 00931 00932 void FGTrimAnalysis::updateRates(void){ 00933 if ( ( mode == taTurn ) || ( mode == taTurnFull ) ){ 00934 // this is the result of a Matlab symbolic expression 00935 double cth2 = cos(_theta)*cos(_theta), 00936 sth2 = sin(_theta)*sin(_theta), 00937 scth = sin(_theta)*cos(_theta), 00938 cph2 = cos(_phiW)*cos(_phiW), 00939 sph2 = sin(_phiW)*sin(_phiW), 00940 scph = sin(_phiW)*cos(_phiW), 00941 cga2 = cos(_gamma)*cos(_gamma), 00942 scga = sin(_gamma)*cos(_gamma), 00943 cps2 = cos(_psi)*cos(_psi), 00944 scps = sin(_psi)*cos(_psi); 00945 _calpha = 00946 sqrt( 00947 1-cth2 00948 +cph2*cth2 00949 -2*scph*scth*cos(_gamma)*sin(_psi)+cph2*cga2*cth2 00950 +cph2*cga2*cth2*(1-cps2) 00951 +2*cph2*scga*scth*cos(_psi) 00952 -cga2*cph2 00953 -2*cph2*cth2*cps2 00954 +2*scph*sin(_gamma)*cth2*scps 00955 +cps2*cth2 ), 00956 _salpha = sqrt( 1. - _calpha*_calpha ); 00957 00958 _p = -_psiWdot* 00959 ( sin(_gamma)*_calpha*_cbeta 00960 +cos(_gamma)*sin(_phiW)*_calpha*_sbeta 00961 +cos(_gamma)*cos(_phiW)*_salpha 00962 ); 00963 00964 _q = -_psiWdot* 00965 ( sin(_gamma)*_sbeta 00966 -cos(_gamma)*sin(_phiW)*_cbeta 00967 ); 00968 00969 _r = -_psiWdot* 00970 ( sin(_gamma)*_salpha*_cbeta 00971 +cos(_gamma)*sin(_phiW)*_salpha*_sbeta 00972 -cos(_gamma)*cos(_phiW)*_calpha 00973 ); 00974 00975 } else if( mode == taPullup && fabs(_targetNlf-1) > 0.01) { 00976 00977 double g,q,cgamma; 00978 g=fdmex->GetInertial()->gravity(); 00979 cgamma=cos(fgic->GetFlightPathAngleRadIC()); 00980 q=g*(_targetNlf-cgamma)/fgic->GetVtrueFpsIC(); 00981 _q=q; 00982 fgic->SetQRadpsIC(q); 00983 } 00984 } 00985 00986 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 00987 00988 void FGTrimAnalysis::setDebug(void) { 00989 Debug=0; 00990 return; 00991 } 00992 00993 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 00994 00995 void FGTrimAnalysis::SetMode(TrimAnalysisMode tt) { 00996 00997 ClearControls(); 00998 00999 cout << "---------------------------------------------------------------------" << endl; 01000 cout << "Trim analysis performed: "; 01001 mode=tt; 01002 switch(tt) { 01003 case taLongitudinal: 01004 if (debug_lvl > 0) 01005 cout << " Longitudinal Trim" << endl; 01006 vTrimAnalysisControls.push_back(new FGTrimAnalysisControl(fdmex,fgic,taThrottle )); 01007 vTrimAnalysisControls.push_back(new FGTrimAnalysisControl(fdmex,fgic,taElevator )); // TODO: taPitchTrim 01008 vTrimAnalysisControls.push_back(new FGTrimAnalysisControl(fdmex,fgic,taTheta )); 01009 break; 01010 case taFull: 01011 if (debug_lvl > 0) 01012 cout << " Full Trim" << endl; 01013 vTrimAnalysisControls.push_back(new FGTrimAnalysisControl(fdmex,fgic,taThrottle )); 01014 vTrimAnalysisControls.push_back(new FGTrimAnalysisControl(fdmex,fgic,taElevator )); // TODO: taPitchTrim 01015 vTrimAnalysisControls.push_back(new FGTrimAnalysisControl(fdmex,fgic,taAileron )); // TODO: taRollTrim 01016 vTrimAnalysisControls.push_back(new FGTrimAnalysisControl(fdmex,fgic,taRudder )); // TODO: taYawTrim 01017 vTrimAnalysisControls.push_back(new FGTrimAnalysisControl(fdmex,fgic,taPhi )); 01018 vTrimAnalysisControls.push_back(new FGTrimAnalysisControl(fdmex,fgic,taTheta )); 01019 vTrimAnalysisControls.push_back(new FGTrimAnalysisControl(fdmex,fgic,taHeading )); 01020 break; 01021 case taFullWingsLevel: 01022 if (debug_lvl > 0) 01023 cout << " Full Trim, Wings-Level" << endl; 01024 vTrimAnalysisControls.push_back(new FGTrimAnalysisControl(fdmex,fgic,taThrottle )); 01025 vTrimAnalysisControls.push_back(new FGTrimAnalysisControl(fdmex,fgic,taElevator )); // TODO: taPitchTrim 01026 vTrimAnalysisControls.push_back(new FGTrimAnalysisControl(fdmex,fgic,taAileron )); // TODO: taRollTrim 01027 vTrimAnalysisControls.push_back(new FGTrimAnalysisControl(fdmex,fgic,taRudder )); // TODO: taYawTrim 01028 vTrimAnalysisControls.push_back(new FGTrimAnalysisControl(fdmex,fgic,taTheta )); 01029 vTrimAnalysisControls.push_back(new FGTrimAnalysisControl(fdmex,fgic,taHeading )); 01030 break; 01031 case taTurn: 01032 // ToDo: set target NLF here !!! 01033 // ToDo: assign psiDot here !! 01034 if (debug_lvl > 0) 01035 cout << " Full Trim, Coordinated turn" << endl; 01036 vTrimAnalysisControls.push_back(new FGTrimAnalysisControl(fdmex,fgic,taThrottle )); 01037 vTrimAnalysisControls.push_back(new FGTrimAnalysisControl(fdmex,fgic,taElevator )); // TODO: taPitchTrim 01038 vTrimAnalysisControls.push_back(new FGTrimAnalysisControl(fdmex,fgic,taAileron )); // TODO: taRollTrim 01039 vTrimAnalysisControls.push_back(new FGTrimAnalysisControl(fdmex,fgic,taRudder )); // TODO: taYawTrim 01040 //vTrimAnalysisControls.push_back(new FGTrimAnalysisControl(fdmex,fgic,taPhi )); 01041 //vTrimAnalysisControls.push_back(new FGTrimAnalysisControl(fdmex,fgic,taTheta )); 01042 vTrimAnalysisControls.push_back(new FGTrimAnalysisControl(fdmex,fgic,taHeading )); 01043 break; 01044 case taTurnFull: 01045 if (debug_lvl > 0) 01046 cout << " Non-coordinated Turn Trim" << endl; 01047 vTrimAnalysisControls.push_back(new FGTrimAnalysisControl(fdmex,fgic,taThrottle )); 01048 vTrimAnalysisControls.push_back(new FGTrimAnalysisControl(fdmex,fgic,taElevator )); // TODO: taPitchTrim 01049 vTrimAnalysisControls.push_back(new FGTrimAnalysisControl(fdmex,fgic,taAileron )); // TODO: taRollTrim 01050 vTrimAnalysisControls.push_back(new FGTrimAnalysisControl(fdmex,fgic,taRudder )); // TODO: taYawTrim 01051 //vTrimAnalysisControls.push_back(new FGTrimAnalysisControl(fdmex,fgic,taPhi )); 01052 // calculate this from target nlf 01053 vTrimAnalysisControls.push_back(new FGTrimAnalysisControl(fdmex,fgic,taTheta )); 01054 vTrimAnalysisControls.push_back(new FGTrimAnalysisControl(fdmex,fgic,taHeading )); 01055 break; 01056 case taPullup: 01057 // ToDo: set target NLF here !!! 01058 // ToDo: assign qDot here !! 01059 if (debug_lvl > 0) 01060 cout << " Full Trim, Pullup" << endl; 01061 vTrimAnalysisControls.push_back(new FGTrimAnalysisControl(fdmex,fgic,taThrottle )); 01062 vTrimAnalysisControls.push_back(new FGTrimAnalysisControl(fdmex,fgic,taElevator )); // TODO: taPitchTrim 01063 vTrimAnalysisControls.push_back(new FGTrimAnalysisControl(fdmex,fgic,taAileron )); 01064 vTrimAnalysisControls.push_back(new FGTrimAnalysisControl(fdmex,fgic,taRudder )); 01065 //vTrimAnalysisControls.push_back(new FGTrimAnalysisControl(fdmex,fgic,taPhi )); 01066 vTrimAnalysisControls.push_back(new FGTrimAnalysisControl(fdmex,fgic,taTheta )); 01067 //vTrimAnalysisControls.push_back(new FGTrimAnalysisControl(fdmex,fgic,taHeading )); 01068 break; 01069 case taGround: 01070 if (debug_lvl > 0) 01071 cout << " Ground Trim" << endl; 01072 vTrimAnalysisControls.push_back(new FGTrimAnalysisControl(fdmex,fgic,taAltAGL )); 01073 vTrimAnalysisControls.push_back(new FGTrimAnalysisControl(fdmex,fgic,taTheta )); 01074 //vTrimAnalysisControls.push_back(new FGTrimAnalysisControl(fdmex,fgic,taPhi )); 01075 break; 01076 case taCustom: 01077 // agodemar... 01078 // ...agodemar 01079 case taNone: 01080 break; 01081 } 01082 01083 current_ctrl=0; 01084 } 01085 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 01086 01087 bool FGTrimAnalysis::SetResultsFile(string name) 01088 { 01089 if ( rf.is_open() ) return false; 01090 01091 rf_name = name; 01092 rf.open(rf_name.c_str(), ios::out); 01093 if ( !rf.is_open() ) { 01094 cerr << "Unable to open " << rf_name << endl; 01095 return false; 01096 } 01097 //rf << "# ... complete this " << endl; 01098 //rf << "# iteration, CostFunc, size, dT, dE, dA, dR, Psi (rad), Theta (rad), Phi (rad)\n"; 01099 return true; 01100 } 01101 01102 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 01103 01104 bool FGTrimAnalysis::ensureRunning() 01105 { 01106 bool success = false; 01107 01108 if ( Propulsion == 0L ) return false; 01109 01110 for(unsigned i=0;i<Propulsion->GetNumEngines();i++) 01111 { 01112 if (!Propulsion->GetEngine(i)->GetRunning() ) { 01113 Propulsion->GetEngine(i)->SetStarter( true ); 01114 if ( Propulsion->GetEngine(i)->GetType() == JSBSim::FGEngine::etPiston ) 01115 { 01116 FGPiston * Piston = (FGPiston*)Propulsion->GetEngine(i); 01117 Piston->SetMagnetos(3); 01118 } 01119 else if ( Propulsion->GetEngine(i)->GetType() == FGEngine::etTurbine ) 01120 { 01121 FGTurbine * Turbine = (FGTurbine*)Propulsion->GetEngine(i); 01122 Turbine->SetCutoff(false); 01123 Turbine->SetStarter(true); 01124 Turbine->Calculate(); 01125 } 01126 Propulsion->GetEngine(i)->SetRunning( true ); 01127 Propulsion->Run(); 01128 } else { 01129 success = true; // at least one engine is found in a running state 01130 Propulsion->SetActiveEngine(i); 01131 } 01132 } 01133 return success; 01134 } 01135 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 01136 01137 bool FGTrimAnalysis::ensureRunning(unsigned int i) 01138 { 01139 bool success = false; 01140 01141 if ( Propulsion == 0L ) return false; 01142 01143 if ( i < Propulsion->GetNumEngines() ) 01144 { 01145 if (!Propulsion->GetEngine(i)->GetRunning() ) { 01146 Propulsion->GetEngine(i)->SetStarter( true ); 01147 if ( Propulsion->GetEngine(i)->GetType() == JSBSim::FGEngine::etPiston ) 01148 { 01149 FGPiston * Piston = (FGPiston*)Propulsion->GetEngine(i); 01150 Piston->SetMagnetos(3); 01151 } 01152 else if ( Propulsion->GetEngine(i)->GetType() == FGEngine::etTurbine ) 01153 { 01154 FGTurbine * Turbine = (FGTurbine*)Propulsion->GetEngine(i); 01155 Turbine->SetCutoff(false); 01156 Turbine->SetStarter(true); 01157 Turbine->Calculate(); 01158 } 01159 Propulsion->GetEngine(i)->SetRunning( true ); 01160 Propulsion->Run(); 01161 01162 } else { 01163 success = true; // at least one engine is found in a running state 01164 Propulsion->SetActiveEngine(i); 01165 } 01166 } 01167 else success=false; // i not in range 01168 01169 return success; 01170 } 01171 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 01172 01173 bool FGTrimAnalysis::runForAWhile(int nruns) 01174 { 01175 bool result = fdmex->Run(); // MAKE AN INITIAL RUN 01176 01177 int counter = 0; 01178 01179 // *** CYCLIC EXECUTION LOOP, AND MESSAGE READING *** // 01180 while ( counter < nruns ) { // (result && (counter < nruns)) 01181 counter++; 01182 result = fdmex->Run(); 01183 } 01184 return result; 01185 } 01186 01187 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 01188 bool FGTrimAnalysis::populateVecAlphaDeg(double vmin, double vmax, int n) 01189 { 01190 if ( !vAlphaDeg.empty() ) return false; 01191 for(int i=0; i<n; i++) 01192 vAlphaDeg.push_back( vmin + double(i)*(vmax - vmin)/double(n-1) ); 01193 return true; 01194 } 01195 01196 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 01197 bool FGTrimAnalysis::populateVecThrottleCmd(double vmin, double vmax, int n) 01198 { 01199 if ( !vThrottleCmd.empty() ) return false; 01200 for(int i=0; i<n; i++) 01201 vThrottleCmd.push_back( vmin + double(i)*(vmax - vmin)/double(n-1) ); 01202 return true; 01203 } 01204 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 01205 bool FGTrimAnalysis::populateVecElevatorCmd(double vmin, double vmax, int n) 01206 { 01207 if ( !vElevatorCmd.empty() ) return false; 01208 for(int i=0; i<n; i++) 01209 vElevatorCmd.push_back( vmin + double(i)*(vmax - vmin)/double(n-1) ); 01210 return true; 01211 } 01212 01213 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 01214 bool FGTrimAnalysis::calculateAerodynamics( 01215 double dE_cmd, 01216 double Vt, 01217 double alpha_deg, 01218 double altitude, 01219 double rho, 01220 double S, double mac, double bw, 01221 double& CL, double& CD, double& Cm) 01222 { 01223 double qBar = 0.5 * rho * Vt*Vt; 01224 double qBar_S = qBar * S; 01225 01226 Auxiliary->SetVt( Vt ); 01227 Auxiliary->Setalpha( alpha_deg*degtorad ); // ! note these are DEC converted to RAD!! 01228 01229 Auxiliary->Setbeta( 0.0 ); 01230 01231 // ensure zero rates 01232 Auxiliary->Setadot( 0.0 ); 01233 Auxiliary->Setbdot( 0.0 ); 01234 Auxiliary->SetAeroPQR( FGColumnVector3(0.,0.,0.) ) ; // note assumes that trim_mode is triggered 01235 // note: do not Auxiliary->Run(), otherwise dotted values 01236 // _and_ aerodynamic angles are recalculated !!! 01237 01238 // ensure the desired dE before aerodynamics 01239 FCS->SetDeCmd( dE_cmd ); 01240 FCS->Run(); 01241 01242 Aerodynamics->Run(); 01243 01244 // get the Aerodynamics internat data 01245 vector <FGFunction*> * Coeff = Aerodynamics->GetAeroFunctions(); 01246 01247 if ( Coeff->empty() ) return false; 01248 01249 CL = 0.; 01250 unsigned int axis_ctr = 2; // means LIFT, 0=DRAG, 1=SIDE, 3,4,5=ROLL,PITCH,YAW 01251 for (unsigned int ctr=0; ctr < Coeff[axis_ctr].size(); ctr++) 01252 CL += Coeff[axis_ctr][ctr]->GetValue(); 01253 CL /= qBar_S; 01254 01255 CD = 0.; 01256 axis_ctr = 0; // means DRAG 01257 for (unsigned int ctr=0; ctr < Coeff[axis_ctr].size(); ctr++) 01258 CD += Coeff[axis_ctr][ctr]->GetValue(); 01259 CD /= qBar_S; 01260 01261 Cm=0.; 01262 axis_ctr = 4; // means PITCH 0=DRAG, 1=SIDE, 2=LIFT, 3=ROLL, 4=PITCH, 5=YAW 01263 for (unsigned int ctr=0; ctr < Coeff[axis_ctr].size(); ctr++) 01264 Cm += Coeff[axis_ctr][ctr]->GetValue(); 01265 01266 Cm /= ( qBar_S*mac ); 01267 01268 return true; 01269 01270 } 01271 01272 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 01273 01274 bool FGTrimAnalysis::DoTrim(void) { 01275 01276 // retrieve initial conditions 01277 fdmex->RunIC(); 01278 01279 cout << endl << "Numerical trim algorithm: constrained optimization of a cost function" << endl; 01280 01281 Objective* obj_ptr = new Objective(this->fdmex, this, 999.0); 01282 01283 fdmex->SetTrimStatus( true ); 01284 01285 //################################### 01286 // run for a while 01287 //################################### 01288 01289 double tMin,tMax; 01290 double throttle = 1.0; 01291 for (unsigned i=0;i<Propulsion->GetNumEngines();i++) 01292 { 01293 tMin=Propulsion->GetEngine(i)->GetThrottleMin(); 01294 tMax=Propulsion->GetEngine(i)->GetThrottleMax(); 01295 FCS->SetThrottleCmd(i,tMin + throttle *(tMax-tMin)); 01296 if (Propulsion->GetEngine(i)->GetType()==FGEngine::etPiston) 01297 { 01298 FCS->SetMixtureCmd(i,0.87); 01299 } 01300 if (Propulsion->GetEngine(i)->GetType()==FGEngine::etTurbine) 01301 { 01302 ((FGTurbine*)Propulsion->GetEngine(i))->SetCutoff(false); 01303 ((FGTurbine*)Propulsion->GetEngine(i))->SetPhase(FGTurbine::tpRun); // tpStart 01304 } 01305 01306 FCS->Run(); // apply throttle change 01307 } 01308 Propulsion->GetSteadyState(); // GetSteadyState processes all engines 01309 01310 //-------------------------------------------------------- 01311 01312 for(unsigned i=0;i<Propulsion->GetNumEngines();i++) 01313 { 01314 int engineStartCount = 0; 01315 bool engine_started = false; 01316 01317 int n_attempts = 100; 01318 if (Propulsion->GetEngine(i)->GetType()==FGEngine::etTurbine) 01319 n_attempts = 5000; // a turbine engine, e.g. 737's cfm56, takes a little longer to get running 01320 01321 while ( !engine_started && (engineStartCount < n_attempts) ) 01322 { 01323 engine_started = ensureRunning(i); 01324 fdmex->Run(); 01325 engineStartCount++; 01326 } 01327 01328 } 01329 01330 //-------------------------------------------------------- 01331 01332 Propulsion->GetSteadyState(); 01333 01334 fdmex->SetDebugLevel(0); // 4 01335 01336 //######################################################################### 01337 01338 trim_failed=false; 01339 01340 for(int i=0;i < fdmex->GetGroundReactions()->GetNumGearUnits();i++){ 01341 fdmex->GetGroundReactions()->GetGearUnit(i)->SetReport(false); 01342 } 01343 01344 fdmex->DisableOutput(); 01345 01346 fgic->SetPRadpsIC(0.0); 01347 fgic->SetQRadpsIC(0.0); 01348 fgic->SetRRadpsIC(0.0); 01349 01350 if(mode == taPullup ) { 01351 setupPullup(); // also calls updateRates() 01352 } else if (mode == taTurn) { 01353 setupTurn(); // also calls updateRates() 01354 } else if (mode == taTurnFull) { 01355 setupTurn(); // also calls updateRates() 01356 } else { 01357 fgic->SetPRadpsIC(0.0); 01358 fgic->SetQRadpsIC(0.0); 01359 fgic->SetRRadpsIC(0.0); 01360 _p = _q = _r = 0.; 01361 } 01362 01363 // ** DO HERE THE TRIM ** // 01364 01365 //--------------------------------------------------------------------------- 01366 // REMINDER: 01367 // n. of control variables for full trim (taFull): 7 01368 // ordering: the four commands first, then the three Euler angles, 01369 // [0] throttle cmd, [1] elevator cmd, 2) aileron cmd, [3] rudder cmd 01370 // [4] phi, [5] theta, [6] psi (alias taHeading) 01371 //----------------------------------------------------------------------------------------------------------------- 01372 01373 // re-run ICs 01374 fdmex->RunIC(); 01375 01376 // write trim results on file, rf=results file 01377 if ( rf.is_open() ) 01378 rf << 01379 "# iteration, costf, dT, dE, dA, dR, Phi (rad), Theta (rad), Psi (rad), uDot (fps2), vDot (fps2), wDot (fps2), pDot (rad/s2), qDot (rad/s2), rDot (rad/s2), u (fps), v (fps), w (fps), p (rad/s), q (rad/s), r (rad/s), alpha (rad), beta (rad), alphaDot (rad/s), betaDot (rad/s), Thrust" 01380 << endl; 01381 01382 long n = vTrimAnalysisControls.size(); // number of variables (dimension of the search) 01383 01384 /* we'll initialize an n-entry Vector whose value is startVal, 01385 * and use it as our starting point. 01386 */ 01387 01388 // a Vec, needed in the search constructor 01389 Vector<double>* minVec = new Vector<double>(n, 0.0); 01390 01391 // a Vec needed to store the minimum point later 01392 Vector<double>* Sminimum; 01393 new_Vector_Init(Sminimum,*minVec); 01394 01395 /* 01396 * now construct the search object: 01397 * NMS = Nelder-Mead Search algorithm; 01398 * if n parameters have to be adjusted, then user has to provide 01399 * n+1 initial combinations where the cost function is going to be sampled. 01400 * The class has the capability of automatically initialize those points, 01401 * but here I prefer giving them explicitly so that the initial steps in 01402 * all parameter "directions" are well defined. 01403 */ 01404 01405 NMSearch NMS( 01406 n, *minVec, /* minVec, */ 01407 sigma_nm, alpha_nm, beta_nm, gamma_nm, 01408 initial_step, tolerance, 01409 //find_CostFunctionFull, // gets the proper member function from the 01410 // object of class Objective (see main_obj.cc from DirectSearch examples) 01411 0L, // gets the proper member function from the 01412 (void *)obj_ptr 01413 ); 01414 01415 if ( GetMode()==taLongitudinal ) { 01416 NMS.SetFcnName(find_CostFunctionLongitudinal); 01417 } 01418 if ( GetMode()==taFull ) { 01419 NMS.SetFcnName(find_CostFunctionFull); 01420 } 01421 if ( GetMode()==taFullWingsLevel ) { 01422 NMS.SetFcnName(find_CostFunctionFullWingsLevel); 01423 } 01424 if ( GetMode()==taTurn ) { 01425 NMS.SetFcnName(find_CostFunctionFullCoordinatedTurn); 01426 } 01427 if ( GetMode()==taTurnFull ) { 01428 NMS.SetFcnName(find_CostFunctionFullTurn); 01429 } 01430 if ( GetMode()==taPullup ) { 01431 NMS.SetFcnName(find_CostFunctionPullUp); 01432 } 01433 01434 //----------------------------------------- 01435 // initialize simplex (n+1 conditions) 01436 //----------------------------------------- 01437 // InitGeneralSimplex(plex) where... 01438 // Matrix<double> *plex = NULL; 01439 // new_Matrix(plex, n+1,n); // create one more row, see Dyn_alloc.h 01440 // ... 01441 01442 //------------------------------------------------------------------------------ 01443 // In this stringstream we I put the n+1 sequences, each of n values (n-ples) 01444 // The stream is then used to initialize the simplex by a call to the method 01445 // ReadInFile 01446 //------------------------------------------------------------------------------ 01447 stringstream ss (stringstream::in | stringstream::out); 01448 01449 // first feed the trial minimizer, zeroth point of the simplex 01450 for (unsigned int k=0; k<vTrimAnalysisControls.size();k++) 01451 ss << vTrimAnalysisControls[k]->GetControlInitialValue() << " "; 01452 01453 // then the rest of n-ples 01454 for (unsigned int k=0; k<vTrimAnalysisControls.size();k++) 01455 { 01456 for(vector<FGTrimAnalysisControl*>::iterator vi=vTrimAnalysisControls.begin(); 01457 vi!=vTrimAnalysisControls.end();vi++) { 01458 if ( (*vi)->GetControlType()==vTrimAnalysisControls[k]->GetControlType() ) 01459 ss << (*vi)->GetControlInitialValue() + (*vi)->GetControlStep() << " "; 01460 else 01461 ss << (*vi)->GetControlInitialValue() << " "; 01462 }// this complets the k-th n-ple 01463 }// this completes the (N+1) n-ples 01464 01465 NMS.ReadInFile(ss); // assign the initial combinations of parameters 01466 01467 /* 01468 If Stop_on_std is set to false in an NMSearch, the standard-deviation test is used until that test is met. 01469 Then (and in subsequent iterations of the same search) the more expensive ``delta'' test will be used. 01470 We realize that there will be times when the standard deviation test will actually take more function calls than 01471 the ``delta'' test, but this is not typically the case, and we make this compromise for the sake of efficiency. 01472 */ 01473 01474 NMS.SetMaxCalls(max_iterations); 01475 //NMS.SetMaxCallsExact(20000); 01476 if ( stop_criterion=="Stop-On-Std" ) 01477 NMS.Set_Stop_on_std(); 01478 if ( stop_criterion=="Stop-On-Delta" ) 01479 NMS.Set_Stop_on_delta(); 01480 01481 double SMinVal; 01482 long Scalls; 01483 01484 // NMS.PrintDesign(); 01485 01486 fdmex->SetDebugLevel(0); 01487 01488 /* start searching */ 01489 NMS.BeginSearch(); // stops when the simplex shrinks to a point 01490 01491 // WATCH this !! 01492 delete obj_ptr; 01493 01494 /* recover information about the search */ 01495 NMS.GetMinPoint(*Sminimum); 01496 NMS.GetMinVal(SMinVal); 01497 Scalls = NMS.GetFunctionCalls(); 01498 01499 // Apply the set of controls found by the minimization procedure 01500 01501 if ( ( mode == taFull ) || 01502 ( mode == taTurnFull ) ) 01503 { 01504 FCS->SetDeCmd( (*Sminimum)[1] ); // elevator 01505 FCS->SetDaCmd( (*Sminimum)[2] ); // ailerons 01506 FCS->SetDrCmd( (*Sminimum)[3] ); // rudder 01507 double tMin,tMax; 01508 for(unsigned i=0;i<Propulsion->GetNumEngines();i++) 01509 { 01510 tMin=Propulsion->GetEngine(i)->GetThrottleMin(); 01511 tMax=Propulsion->GetEngine(i)->GetThrottleMax(); 01512 FCS->SetThrottleCmd(i,tMin + (*Sminimum)[0] *(tMax-tMin)); 01513 FCS->Run(); // apply throttle change 01514 } 01515 Propulsion->GetSteadyState(); // GetSteadyState processes all engines 01516 FCS->Run(); // apply throttle, yoke & pedal changes 01517 01518 FGQuaternion quat( (*Sminimum)[4], (*Sminimum)[5], (*Sminimum)[6] ); // phi, theta, psi 01519 quat.Normalize(); 01520 01521 fgic->ResetIC(_u, _v, _w, _p, _q, _r, _alpha, _beta, _phi, _theta, _psi, _gamma); 01522 01523 FGPropagate::VehicleState vstate = fdmex->GetPropagate()->GetVState(); 01524 vstate.vQtrn = FGQuaternion(_phi,_theta,_psi); 01525 fdmex->GetPropagate()->SetVState(vstate); 01526 Auxiliary->Setalpha( _alpha ); // need to get Auxiliary updated 01527 Auxiliary->Setbeta ( _beta ); 01528 01529 // NOTE: _do not_ fdmex->RunIC() here ! We just reset the state 01530 /* */ 01531 //fdmex->RunIC(); 01532 fdmex->Run(); 01533 01534 } // end taFull 01535 01536 if ( mode == taLongitudinal ) 01537 { 01538 FCS->SetDeCmd( (*Sminimum)[1] ); // elevator 01539 double tMin,tMax; 01540 for(unsigned i=0;i<Propulsion->GetNumEngines();i++) 01541 { 01542 tMin=Propulsion->GetEngine(i)->GetThrottleMin(); 01543 tMax=Propulsion->GetEngine(i)->GetThrottleMax(); 01544 FCS->SetThrottleCmd(i,tMin + (*Sminimum)[0] *(tMax-tMin)); 01545 FCS->Run(); // apply throttle change 01546 } 01547 Propulsion->GetSteadyState(); // GetSteadyState processes all engines 01548 FCS->Run(); // apply throttle, yoke & pedal changes 01549 01550 FGQuaternion quat( 0, (*Sminimum)[2], fgic->GetPsiRadIC() ); // phi, theta, psi 01551 quat.Normalize(); 01552 01553 // enforce the current state to IC object 01554 01555 fgic->ResetIC(_u, _v, _w, _p, _q, _r, _alpha, _beta, _phi, _theta, _psi, _gamma); 01556 01557 // NOTE: _do not_ fdmex->RunIC() here ! We just reset the state 01558 // which means that we populate IC private variables with our set of values 01559 //fdmex->RunIC(); 01560 01561 FGPropagate::VehicleState vstate = fdmex->GetPropagate()->GetVState(); 01562 vstate.vQtrn = FGQuaternion(_phi,_theta,_psi); 01563 fdmex->GetPropagate()->SetVState(vstate); 01564 Auxiliary->Setalpha( _alpha ); // need to get Auxiliary updated 01565 Auxiliary->Setbeta ( _beta ); 01566 01567 fdmex->Run(); 01568 01569 } // end taLongitudinal 01570 01571 if ( mode == taFullWingsLevel) 01572 { 01573 FCS->SetDeCmd( (*Sminimum)[1] ); // elevator 01574 FCS->SetDaCmd( (*Sminimum)[2] ); // ailerons 01575 FCS->SetDrCmd( (*Sminimum)[3] ); // rudder 01576 01577 double tMin,tMax; 01578 for(unsigned i=0;i<Propulsion->GetNumEngines();i++) 01579 { 01580 tMin=Propulsion->GetEngine(i)->GetThrottleMin(); 01581 tMax=Propulsion->GetEngine(i)->GetThrottleMax(); 01582 FCS->SetThrottleCmd(i,tMin + (*Sminimum)[0] *(tMax-tMin)); 01583 FCS->Run(); // apply throttle change 01584 } 01585 Propulsion->GetSteadyState(); // GetSteadyState processes all engines 01586 FCS->Run(); // apply throttle, yoke & pedal changes 01587 01588 FGQuaternion quat( 0, (*Sminimum)[2], fgic->GetPsiRadIC() ); // phi, theta, psi 01589 quat.Normalize(); 01590 01591 //... 01592 // enforce the current state to IC object 01593 01594 fgic->ResetIC(_u, _v, _w, _p, _q, _r, _alpha, _beta, _phi, _theta, _psi, _gamma); 01595 01596 // NOTE: _do not_ fdmex->RunIC() here ! We just reset the state 01597 // which means that we populate IC private variables with our set of values 01598 //fdmex->RunIC(); 01599 01600 FGPropagate::VehicleState vstate = fdmex->GetPropagate()->GetVState(); 01601 vstate.vQtrn = FGQuaternion(_phi,_theta,_psi); 01602 fdmex->GetPropagate()->SetVState(vstate); 01603 Auxiliary->Setalpha( _alpha ); // need to get Auxiliary updated 01604 Auxiliary->Setbeta ( _beta ); 01605 01606 fdmex->Run(); 01607 }// end taFullWingsLevel 01608 01609 if ( mode == taTurn ) 01610 { 01611 FCS->SetDeCmd( (*Sminimum)[1] ); // elevator 01612 FCS->SetDaCmd( (*Sminimum)[2] ); // ailerons 01613 FCS->SetDrCmd( (*Sminimum)[3] ); // rudder 01614 01615 double tMin,tMax; 01616 for(unsigned i=0;i<Propulsion->GetNumEngines();i++) 01617 { 01618 tMin=Propulsion->GetEngine(i)->GetThrottleMin(); 01619 tMax=Propulsion->GetEngine(i)->GetThrottleMax(); 01620 FCS->SetThrottleCmd(i,tMin + (*Sminimum)[0] *(tMax-tMin)); 01621 FCS->Run(); // apply throttle change 01622 } 01623 Propulsion->GetSteadyState(); // GetSteadyState processes all engines 01624 FCS->Run(); // apply throttle, yoke & pedal changes 01625 01626 FGQuaternion quat( fgic->GetPhiRadIC(), (*Sminimum)[2], fgic->GetPsiRadIC() ); // phi, theta, psi 01627 quat.Normalize(); 01628 01629 //... 01630 // enforce the current state to IC object 01631 01632 fgic->ResetIC(_u, _v, _w, _p, _q, _r, _alpha, _beta, _phi, _theta, _psi, _gamma); 01633 01634 // NOTE: _do not_ fdmex->RunIC() here ! We just reset the state 01635 // which means that we populate IC private variables with our set of values 01636 //fdmex->RunIC(); 01637 01638 FGPropagate::VehicleState vstate = fdmex->GetPropagate()->GetVState(); 01639 vstate.vQtrn = FGQuaternion(_phi,_theta,_psi); 01640 fdmex->GetPropagate()->SetVState(vstate); 01641 Auxiliary->Setalpha( _alpha ); // need to get Auxiliary updated 01642 Auxiliary->Setbeta ( 0.0 ); 01643 Auxiliary->SetGamma( fgic->GetFlightPathAngleRadIC() ); 01644 01645 fdmex->Run(); 01646 }// end ta turn 01647 01648 if ( mode == taPullup ) 01649 { 01650 FCS->SetDeCmd( (*Sminimum)[1] ); // elevator 01651 FCS->SetDaCmd( (*Sminimum)[2] ); // ailerons 01652 FCS->SetDrCmd( (*Sminimum)[3] ); // rudder 01653 01654 double tMin,tMax; 01655 for(unsigned i=0;i<Propulsion->GetNumEngines();i++) 01656 { 01657 tMin=Propulsion->GetEngine(i)->GetThrottleMin(); 01658 tMax=Propulsion->GetEngine(i)->GetThrottleMax(); 01659 FCS->SetThrottleCmd(i,tMin + (*Sminimum)[0] *(tMax-tMin)); 01660 FCS->Run(); // apply throttle change 01661 } 01662 Propulsion->GetSteadyState(); // GetSteadyState processes all engines 01663 FCS->Run(); // apply throttle, yoke & pedal changes 01664 01665 FGQuaternion quat( 0, (*Sminimum)[2], fgic->GetPRadpsIC() ); // phi, theta, psi 01666 quat.Normalize(); 01667 01668 //... 01669 // enforce the current state to IC object 01670 01671 fgic->ResetIC(_u, _v, _w, _p, _q, _r, _alpha, _beta, _phi, _theta, _psi, _gamma); 01672 01673 // NOTE: _do not_ fdmex->RunIC() here ! We just reset the state 01674 // which means that we populate IC private variables with our set of values 01675 //fdmex->RunIC(); 01676 01677 FGPropagate::VehicleState vstate = fdmex->GetPropagate()->GetVState(); 01678 vstate.vQtrn = FGQuaternion(_phi,_theta,_psi); 01679 fdmex->GetPropagate()->SetVState(vstate); 01680 Auxiliary->Setalpha( _alpha ); // need to get Auxiliary updated 01681 Auxiliary->Setbeta ( 0.0 ); 01682 Auxiliary->SetGamma( fgic->GetFlightPathAngleRadIC() ); 01683 01684 fdmex->Run(); 01685 }//end taPullup 01686 01687 delete minVec; 01688 delete Sminimum; 01689 01690 //----------------------------------------------------------------------------------------------------------------- 01691 01692 total_its = NMS.GetFunctionCalls(); 01693 01694 if( !trim_failed ) { 01695 if (debug_lvl > 0) { 01696 cout << endl << " Trim successful. (Cost function value: " << cost_function_value << ")" << endl; 01697 } 01698 } else { 01699 if (debug_lvl > 0) 01700 cout << endl << " Trim failed" << endl; 01701 } 01702 01703 01704 for (unsigned int i=0; i<(unsigned int)fdmex->GetGroundReactions()->GetNumGearUnits();i++){ 01705 fdmex->GetGroundReactions()->GetGearUnit(i)->SetReport(true); 01706 } 01707 01708 return !trim_failed; 01709 } 01710 01711 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 01712 01713 double Objective::myCostFunctionFull(Vector<double> & x) // x variations come from the search algorithm 01714 { 01715 01716 //---------------------------------------------------------------------------- 01717 // NOTE: 01718 // when the program flow arrives here, it is assumed that a successful 01719 // instantiation of a TrimAnalysis object took place with a mode set 01720 // to taFull, i.e. 7 independent variables to be adjusted 01721 //---------------------------------------------------------------------------- 01722 01723 //---------------------------------------------------------------------------- 01724 // NOTE: 01725 // rely on the fact that IC quantities have been fed at least once into 01726 // their correspondent data structure in TrimAnalysis->fdmex tree 01727 //---------------------------------------------------------------------------- 01728 01729 double delta_cmd_T, delta_cmd_E, delta_cmd_A, delta_cmd_R; 01730 01731 double theta, phi, psi; 01732 01733 double alpha = 0.0, beta = 0.0, gamma = 0.0; 01734 01735 FGQuaternion Quat; 01736 FGColumnVector3 vPQRdot(0.0,0.0,0.0), vUVWdot(0.0,0.0,0.0); 01737 01738 FGPropagate::VehicleState VState; 01739 VState.vLocation = FGColumnVector3(0.0,0.0,0.0); 01740 VState.vUVW = FGColumnVector3(0.0,0.0,0.0); 01741 VState.vPQR = FGColumnVector3(0.0,0.0,0.0); 01742 VState.vQtrn = FGQuaternion (0.0,0.0,0.0); 01743 01744 //---------------------------------------------------------------------------- 01745 // Minimization control vector, i.e. independent variables 01746 //---------------------------------------------------------------------------- 01747 01748 // CHECK the trim mode 01749 01750 //----------------------------------------------------------------------------------------------------------------- 01751 // REMINDER: 01752 // n. of control variables for full trim (taFull): 7 01753 // ordering: the four commands first, then the three Euler angles, 01754 // [0] throttle cmd, [1] elevator cmd, 2) aileron cmd, [3] rudder cmd 01755 // [4] phi, [5] theta, [6] psi (alias taHeading) 01756 //----------------------------------------------------------------------------------------------------------------- 01757 01758 delta_cmd_T = x[0]; // throttle, cmd 01759 delta_cmd_E = x[1]; // elevator, cmd 01760 delta_cmd_A = x[2]; // aileron, cmd 01761 delta_cmd_R = x[3]; // rudder, cmd 01762 01763 phi = x[4]; // rad 01764 theta = x[5]; 01765 psi = x[6]; 01766 01767 TrimAnalysis->SetEulerAngles(phi, theta, psi); 01768 01769 //---------------------------------------------------------------------------- 01770 // parameter bound check 01771 // ToDo: manage bounds via the FGTrimAnalysisControl class 01772 //---------------------------------------------------------------------------- 01773 bool penalty = ( (delta_cmd_T < 0) || (delta_cmd_T > 1) ) 01774 || ( (delta_cmd_E < -1) || (delta_cmd_E > 1) ) 01775 || ( (delta_cmd_A < -1) || (delta_cmd_A > 1) ) 01776 || ( (delta_cmd_R < -1) || (delta_cmd_R > 1) ) 01777 || ( (psi < 0.0 ) || (psi > 2.0*M_PI) ) 01778 || ( (theta < -0.5*M_PI) || (theta > 0.5*M_PI) ) 01779 || ( (phi < - M_PI) || (phi > M_PI) ) 01780 ; 01781 01782 if ( penalty ) { 01783 //------------------------------------------------- 01784 // just return an "infinite" value 01785 return HUGE_VAL; 01786 //------------------------------------------------- 01787 } else {calculateDottedStates(delta_cmd_T, delta_cmd_E, delta_cmd_A, delta_cmd_R, 01788 phi, theta, psi, 01789 TrimAnalysis->GetMode(), 01790 alpha, beta, gamma, 01791 VState, 01792 vUVWdot, vPQRdot ); 01793 } 01794 01795 double u , v , w , 01796 p , q , r , 01797 uDot, vDot, wDot, 01798 pDot, qDot, rDot; 01799 u = VState.vUVW (1); v = VState.vUVW (2); w = VState.vUVW (3); 01800 p = VState.vPQR (1); q = VState.vPQR (2); r = VState.vPQR (3); 01801 uDot = vUVWdot(1); vDot = vUVWdot(2); wDot = vUVWdot(3); 01802 pDot = vPQRdot(1); qDot = vPQRdot(2); rDot = vPQRdot(3); 01803 01804 double 01805 f = 1.000*uDot*uDot 01806 + vDot*vDot 01807 + 1.000*wDot*wDot 01808 + 0.010*pDot*pDot 01809 + 0.010*qDot*qDot 01810 + 0.010*rDot*rDot; 01811 01812 static int count = 0; 01813 count++; 01814 01815 if ( f < TrimAnalysis->GetCostFunctionValue() ) 01816 { 01817 // feed into the vector of TrimAnalysis Controls the current values 01818 vector<FGTrimAnalysisControl*>::iterator vi; 01819 for(vi=TrimAnalysis->GetControls()->begin(); 01820 vi!=TrimAnalysis->GetControls()->end();vi++) 01821 { 01822 if ( (*vi)->GetControlType()==JSBSim::taThrottle ) (*vi)->SetControl(delta_cmd_T); 01823 if ( (*vi)->GetControlType()==JSBSim::taElevator ) (*vi)->SetControl(delta_cmd_E); // TODO: taPitchTrim 01824 if ( (*vi)->GetControlType()==JSBSim::taAileron ) (*vi)->SetControl(delta_cmd_A); // TODO: taRollTrim 01825 if ( (*vi)->GetControlType()==JSBSim::taRudder ) (*vi)->SetControl(delta_cmd_R); // TODO: taYawTrim 01826 if ( (*vi)->GetControlType()==JSBSim::taPhi ) (*vi)->SetControl(phi); 01827 if ( (*vi)->GetControlType()==JSBSim::taTheta ) (*vi)->SetControl(theta); 01828 if ( (*vi)->GetControlType()==JSBSim::taHeading ) (*vi)->SetControl(psi); 01829 } 01830 01831 if ( f<=TrimAnalysis->GetTolerance() ) TrimAnalysis->SetTrimSuccessfull(); // SetTrimFailed(false); 01832 01833 // write on file, if open 01834 01835 // "# iteration, costf, 01836 // dT, dE, dA, dR, dPsi, dTheta, dPhi, Psi (rad), Theta (rad), Phi (rad), 01837 // uDot (fps2), vDot (fps2), wDot (fps2), pDot (rad/s2), qDot (rad/s2), rDot (rad/s2), 01838 // u (fps), v (fps), w (fps), p (rad/s), q (rad/s), r (rad/s), alpha (rad), beta (rad), 01839 // alphaDot (rad/s), betaDot (rad/s), Thrust" 01840 01841 ofstream* rfp = TrimAnalysis->GetResultsFile(); 01842 if (rfp) 01843 (*rfp) 01844 << count <<", "<< f <<", " // 1, 2 01845 << delta_cmd_T <<", "<< delta_cmd_E <<", "<< delta_cmd_A <<", "<< delta_cmd_R <<", " 01846 // 3, 4, 5, 6 01847 << phi <<", "<< theta <<", "<< psi <<", " // 7, 8, 9 01848 << uDot <<", "<< vDot <<", "<< wDot <<", " // 10, 11, 12 01849 << pDot <<", "<< qDot <<", "<< rDot <<", " // 13, 14, 15 01850 << u <<", "<< v <<", "<< w << ", " // 16, 17, 18 01851 << VState.vPQR(1) <<", "<< VState.vPQR(2) <<", "<< VState.vPQR(3) << ", " 01852 // 19, 20, 21 01853 << FDMExec->GetAuxiliary()->Getalpha() << ", " // 22 01854 << FDMExec->GetAuxiliary()->Getbeta() << ", " // 23 01855 << FDMExec->GetAuxiliary()->Getadot() << ", " // 24 01856 << FDMExec->GetAuxiliary()->Getbdot() << ", " // 25 01857 01858 << FDMExec->GetPropulsion()->GetEngine(0)->GetThrust() << "\n"; //26 01859 01860 // this really goes into the integration process 01861 FDMExec->GetPropagate()->SetVState( VState ); 01862 01863 // update in TrimAnalysis some private variables, i.e. the state _u, _v, _w, ... 01864 TrimAnalysis->SetState(u, v, w, p, q, r, alpha, beta, phi, theta, psi, gamma); 01865 01866 TrimAnalysis->SetCostFunctionValue(f); 01867 } 01868 01869 return f; 01870 } 01871 01872 01873 double Objective::myCostFunctionFullWingsLevel(Vector<double> & x) // x variations come from the search algorithm 01874 { 01875 01876 //---------------------------------------------------------------------------- 01877 // NOTE: 01878 // when the program flow arrives here, it is assumed that a successful 01879 // instantiation of a TrimAnalysis object took place with a mode set 01880 // to taFullWingsLevel, i.e. 6 independent variables to be adjusted 01881 // ___phi is set to zero because we want wings leveled___ 01882 //---------------------------------------------------------------------------- 01883 01884 //---------------------------------------------------------------------------- 01885 // NOTE: 01886 // rely on the fact that IC quantities have been fed at least once into 01887 // their correspondent data structure in TrimAnalysis->fdmex tree 01888 //---------------------------------------------------------------------------- 01889 01890 double delta_cmd_T, delta_cmd_E, delta_cmd_A, delta_cmd_R; 01891 01892 double theta, phi, psi; 01893 01894 double alpha = 0.0, beta = 0.0, gamma = 0.0; 01895 01896 FGQuaternion Quat; 01897 FGColumnVector3 vPQRdot(0.0,0.0,0.0), vUVWdot(0.0,0.0,0.0); 01898 01899 FGPropagate::VehicleState VState; 01900 VState.vLocation = FGColumnVector3(0.0,0.0,0.0); 01901 VState.vUVW = FGColumnVector3(0.0,0.0,0.0); 01902 VState.vPQR = FGColumnVector3(0.0,0.0,0.0); 01903 VState.vQtrn = FGQuaternion (0.0,0.0,0.0); 01904 01905 //---------------------------------------------------------------------------- 01906 // Minimization control vector, i.e. independent variables 01907 //---------------------------------------------------------------------------- 01908 01909 // CHECK the trim mode 01910 01911 //----------------------------------------------------------------------------------------------------------------- 01912 // REMINDER: 01913 // n. of control variables for full trim (taFullWingsLevel): 6 01914 // ordering: the four commands first, then the three Euler angles, 01915 // [0] throttle cmd, [1] elevator cmd, 2) aileron cmd, [3] rudder cmd 01916 // [4] theta, [5] psi (alias taHeading) 01917 //----------------------------------------------------------------------------------------------------------------- 01918 01919 delta_cmd_T = x[0]; // throttle, cmd 01920 delta_cmd_E = x[1]; // elevator, cmd 01921 delta_cmd_A = x[2]; // aileron, cmd 01922 delta_cmd_R = x[3]; // rudder, cmd 01923 01924 phi = 0.0 ; // rad, this enforces the wings-level condition 01925 theta = x[4]; 01926 psi = x[5]; 01927 01928 TrimAnalysis->SetEulerAngles(phi, theta, psi); 01929 01930 //---------------------------------------------------------------------------- 01931 // parameter bound check 01932 // ToDo: manage bounds via the FGTrimAnalysisControl class 01933 //---------------------------------------------------------------------------- 01934 bool penalty = ( (delta_cmd_T < 0) || (delta_cmd_T > 1) ) 01935 || ( (delta_cmd_E < -1) || (delta_cmd_E > 1) ) 01936 || ( (delta_cmd_A < -1) || (delta_cmd_A > 1) ) 01937 || ( (delta_cmd_R < -1) || (delta_cmd_R > 1) ) 01938 || ( (psi < 0.0 ) || (psi > 2.0*M_PI) ) 01939 || ( (theta < -0.5*M_PI) || (theta > 0.5*M_PI) ) 01940 // || ( (phi < - M_PI) || (phi > M_PI) ) ...always zero here 01941 ; 01942 01943 if ( penalty ) { 01944 //------------------------------------------------- 01945 // just return an "infinite" value 01946 return HUGE_VAL; 01947 //------------------------------------------------- 01948 } else { 01949 calculateDottedStates(delta_cmd_T, delta_cmd_E, delta_cmd_A, delta_cmd_R, 01950 phi, theta, psi, 01951 TrimAnalysis->GetMode(), 01952 alpha, beta, gamma, 01953 VState, 01954 vUVWdot, vPQRdot ); 01955 } 01956 01957 double u , v , w , 01958 p , q , r , 01959 uDot, vDot, wDot, 01960 pDot, qDot, rDot; 01961 u = VState.vUVW (1); v = VState.vUVW (2); w = VState.vUVW (3); 01962 p = VState.vPQR (1); q = VState.vPQR (2); r = VState.vPQR (3); 01963 uDot = vUVWdot(1); vDot = vUVWdot(2); wDot = vUVWdot(3); 01964 pDot = vPQRdot(1); qDot = vPQRdot(2); rDot = vPQRdot(3); 01965 01966 double 01967 f = 1.000*uDot*uDot 01968 + vDot*vDot 01969 + 1.000*wDot*wDot 01970 + 0.010*pDot*pDot 01971 + 0.010*qDot*qDot 01972 + 0.010*rDot*rDot; 01973 01974 static int count = 0; 01975 count++; 01976 01977 if ( f < TrimAnalysis->GetCostFunctionValue() ) 01978 { 01979 // feed into the vector of TrimAnalysis Controls the current values 01980 vector<FGTrimAnalysisControl*>::iterator vi; 01981 for(vi=TrimAnalysis->GetControls()->begin(); 01982 vi!=TrimAnalysis->GetControls()->end();vi++) 01983 { 01984 if ( (*vi)->GetControlType()==JSBSim::taThrottle ) (*vi)->SetControl(delta_cmd_T); 01985 if ( (*vi)->GetControlType()==JSBSim::taElevator ) (*vi)->SetControl(delta_cmd_E); // TODO: taPitchTrim 01986 if ( (*vi)->GetControlType()==JSBSim::taAileron ) (*vi)->SetControl(delta_cmd_A); // TODO: taRollTrim 01987 if ( (*vi)->GetControlType()==JSBSim::taRudder ) (*vi)->SetControl(delta_cmd_R); // TODO: taYawTrim 01988 if ( (*vi)->GetControlType()==JSBSim::taPhi ) (*vi)->SetControl(phi); 01989 if ( (*vi)->GetControlType()==JSBSim::taTheta ) (*vi)->SetControl(theta); 01990 if ( (*vi)->GetControlType()==JSBSim::taHeading ) (*vi)->SetControl(psi); 01991 } 01992 01993 if ( f<=TrimAnalysis->GetTolerance() ) TrimAnalysis->SetTrimSuccessfull(); // SetTrimFailed(false); 01994 01995 // write on file, if open 01996 01997 // "# iteration, costf, 01998 // dT, dE, dA, dR, dPsi, dTheta, dPhi, Psi (rad), Theta (rad), Phi (rad), 01999 // uDot (fps2), vDot (fps2), wDot (fps2), pDot (rad/s2), qDot (rad/s2), rDot (rad/s2), 02000 // u (fps), v (fps), w (fps), p (rad/s), q (rad/s), r (rad/s), alpha (rad), beta (rad), 02001 // alphaDot (rad/s), betaDot (rad/s), Thrust" 02002 02003 ofstream* rfp = TrimAnalysis->GetResultsFile(); 02004 if (rfp) 02005 (*rfp) 02006 << count <<", "<< f <<", " // 1, 2 02007 << delta_cmd_T <<", "<< delta_cmd_E <<", "<< delta_cmd_A <<", "<< delta_cmd_R <<", " 02008 // 3, 4, 5, 6 02009 << phi <<", "<< theta <<", "<< psi <<", " // 7, 8, 9 02010 << uDot <<", "<< vDot <<", "<< wDot <<", " // 10, 11, 12 02011 << pDot <<", "<< qDot <<", "<< rDot <<", " // 13, 14, 15 02012 << u <<", "<< v <<", "<< w << ", " // 16, 17, 18 02013 << VState.vPQR(1) <<", "<< VState.vPQR(2) <<", "<< VState.vPQR(3) << ", " 02014 // 19, 20, 21 02015 << FDMExec->GetAuxiliary()->Getalpha() << ", " // 22 02016 << FDMExec->GetAuxiliary()->Getbeta() << ", " // 23 02017 << FDMExec->GetAuxiliary()->Getadot() << ", " // 24 02018 << FDMExec->GetAuxiliary()->Getbdot() << ", " // 25 02019 02020 << FDMExec->GetPropulsion()->GetEngine(0)->GetThrust() << "\n"; // 26 02021 02022 // this influences really into the further integration process 02023 FDMExec->GetPropagate()->SetVState( VState ); 02024 02025 // update in TrimAnalysis some private variables, i.e. the state _u, _v, _w, ... 02026 TrimAnalysis->SetState(u, v, w, p, q, r, alpha, beta, phi, theta, psi, gamma); 02027 02028 TrimAnalysis->SetCostFunctionValue(f); 02029 } 02030 02031 return f; 02032 } 02033 02034 double Objective::myCostFunctionLongitudinal(Vector<double> & x) 02035 { 02036 //---------------------------------------------------------------------------- 02037 // NOTE: 02038 // when the program flow arrives here, it is assumed that a successful 02039 // instantiation of a TrimAnalysis object took place with a mode set 02040 // to taLongitudinal, i.e. 3 independent variables to be adjusted 02041 //---------------------------------------------------------------------------- 02042 02043 //---------------------------------------------------------------------------- 02044 // NOTE: 02045 // rely on the fact that IC quantities have been fed at least once into 02046 // their correspondent data structure in TrimAnalysis->fdmex tree 02047 // 02048 // A perfect longitudinal trim has to be possible! 02049 // (no asymmetric effect should be present) 02050 //---------------------------------------------------------------------------- 02051 02052 double delta_cmd_T, delta_cmd_E, delta_cmd_A = 0.0, delta_cmd_R = 0.0; 02053 02054 double phi, theta, psi; 02055 02056 double alpha = 0.0, beta = 0.0, gamma = 0.0; 02057 02058 FGQuaternion Quat; 02059 FGColumnVector3 vPQRdot(0.0,0.0,0.0), vUVWdot(0.0,0.0,0.0); 02060 02061 FGPropagate::VehicleState VState; 02062 VState.vLocation = FGColumnVector3(0.0,0.0,0.0); 02063 VState.vUVW = FGColumnVector3(0.0,0.0,0.0); 02064 VState.vPQR = FGColumnVector3(0.0,0.0,0.0); 02065 VState.vQtrn = FGQuaternion (0.0,0.0,0.0); 02066 02067 //---------------------------------------------------------------------------- 02068 // Minimization control vector, i.e. independent variables 02069 //---------------------------------------------------------------------------- 02070 02071 // CHECK the trim mode 02072 02073 //----------------------------------------------------------------------------------------------------------------- 02074 // REMINDER: 02075 // n. of control variables for longitudinal trim (taLongitudinal): 3 02076 // ordering: the two commands first, then the Euler angle theta, 02077 // [0] throttle cmd, [1] elevator cmd, [2] theta 02078 //----------------------------------------------------------------------------------------------------------------- 02079 02080 delta_cmd_T = x[0]; // throttle, cmd 02081 delta_cmd_E = x[1]; // elevator, cmd 02082 theta = x[2]; // pitch attitude 02083 02084 double psiIC = FDMExec->GetIC()->GetPsiRadIC(); 02085 02086 psi = psiIC; 02087 phi = 0.0; 02088 02089 TrimAnalysis->SetEulerAngles(phi, theta, psi); 02090 02091 //---------------------------------------------------------------------------- 02092 // parameter bound check 02093 // ToDo: manage bounds via the FGTrimAnalysisControl class 02094 //---------------------------------------------------------------------------- 02095 bool penalty = ( (delta_cmd_T < 0) || (delta_cmd_T > 1) ) 02096 || ( (delta_cmd_E < -1) || (delta_cmd_E > 1) ) 02097 || ( (theta < -0.5*M_PI) || (theta > 0.5*M_PI) ) 02098 ; 02099 02100 if ( penalty ) { 02101 //------------------------------------------------- 02102 // just return an "infinite" value 02103 return HUGE_VAL; 02104 //------------------------------------------------- 02105 } else { 02106 calculateDottedStates(delta_cmd_T, delta_cmd_E, delta_cmd_A, delta_cmd_R, 02107 phi, theta, psi, 02108 TrimAnalysis->GetMode(), 02109 alpha, beta, gamma, 02110 VState, 02111 vUVWdot, vPQRdot ); 02112 } 02113 02114 double u , v , w , 02115 p , q , r , 02116 uDot, vDot, wDot, 02117 pDot, qDot, rDot; 02118 u = VState.vUVW (1); v = VState.vUVW (2); w = VState.vUVW (3); 02119 p = VState.vPQR (1); q = VState.vPQR (2); r = VState.vPQR (3); 02120 uDot = vUVWdot(1); vDot = vUVWdot(2); wDot = vUVWdot(3); 02121 pDot = vPQRdot(1); qDot = vPQRdot(2); rDot = vPQRdot(3); 02122 02123 double 02124 f = 1.000*uDot*uDot 02125 + 1.000*wDot*wDot 02126 + //1.0 02127 0.010 02128 *qDot*qDot; 02129 02130 static int count = 0; 02131 count++; 02132 02133 if ( f < TrimAnalysis->GetCostFunctionValue() ) 02134 { 02135 // feed into the vector of TrimAnalysis Controls the current values 02136 vector<FGTrimAnalysisControl*>::iterator vi; 02137 for(vi=TrimAnalysis->GetControls()->begin(); 02138 vi!=TrimAnalysis->GetControls()->end();vi++) 02139 { 02140 if ( (*vi)->GetControlType()==JSBSim::taThrottle ) (*vi)->SetControl(delta_cmd_T); 02141 if ( (*vi)->GetControlType()==JSBSim::taElevator ) (*vi)->SetControl(delta_cmd_E); // TODO: taPitchTrim 02142 if ( (*vi)->GetControlType()==JSBSim::taAileron ) (*vi)->SetControl(0); // TODO: taRollTrim 02143 if ( (*vi)->GetControlType()==JSBSim::taRudder ) (*vi)->SetControl(0); // TODO: taYawTrim 02144 if ( (*vi)->GetControlType()==JSBSim::taPhi ) (*vi)->SetControl(0); 02145 if ( (*vi)->GetControlType()==JSBSim::taTheta ) (*vi)->SetControl(theta); 02146 if ( (*vi)->GetControlType()==JSBSim::taHeading ) (*vi)->SetControl( FDMExec->GetIC()->GetPsiRadIC()); 02147 } 02148 02149 if ( f<=TrimAnalysis->GetTolerance() ) TrimAnalysis->SetTrimSuccessfull(); // SetTrimFailed(false); 02150 02151 // write on file, if open 02152 02153 // "# iteration, costf, 02154 // dT, dE, dA, dR, dPsi, dTheta, dPhi, Psi (rad), Theta (rad), Phi (rad), 02155 // uDot (fps2), vDot (fps2), wDot (fps2), pDot (rad/s2), qDot (rad/s2), rDot (rad/s2), 02156 // u (fps), v (fps), w (fps), p (rad/s), q (rad/s), r (rad/s), alpha (rad), beta (rad), 02157 // alphaDot (rad/s), betaDot (rad/s), Thrust" 02158 02159 ofstream* rfp = TrimAnalysis->GetResultsFile(); 02160 if (rfp) 02161 (*rfp) 02162 << count <<", "<< f <<", " // 1, 2 02163 << delta_cmd_T <<", " // 3 02164 << delta_cmd_E <<", " <<0.0 <<", "<< 0.0 <<", " // 4, 5, 6 02165 << phi <<", "<< theta <<", "<< psi <<", " // 7, 8, 9 02166 << uDot <<", "<< vDot <<", "<< wDot <<", " // 10, 11, 12 02167 << pDot <<", "<< qDot <<", "<< rDot <<", " // 13, 14, 15 02168 << u <<", "<< v <<", "<< w << ", " // 16, 17, 18 02169 << VState.vPQR(1) <<", "<< VState.vPQR(2) <<", "<< VState.vPQR(3) << ", " 02170 // 19, 20, 21 02171 << FDMExec->GetAuxiliary()->Getalpha() << ", " // 22 02172 << FDMExec->GetAuxiliary()->Getbeta() << ", " // 23 02173 << FDMExec->GetAuxiliary()->Getadot() << ", " // 24 02174 << FDMExec->GetAuxiliary()->Getbdot() << ", " // 25 02175 02176 << FDMExec->GetPropulsion()->GetEngine(0)->GetThrust() << "\n"; // 26 02177 02178 FDMExec->GetPropagate()->SetVState( VState ); // ?? is this really necessary 02179 02180 // update in TrimAnalysis some private variables, i.e. the state _u, _v, _w, ... 02181 TrimAnalysis->SetState(u, v, w, p, q, r, alpha, beta, phi, theta, psi, gamma); 02182 02183 TrimAnalysis->SetCostFunctionValue(f); // update the current minimum 02184 } 02185 02186 return f; 02187 } // end of myCostFunctionLongitudinal 02188 02189 double Objective::myCostFunctionFullCoordinatedTurn(Vector<double> & x) 02190 { 02191 //---------------------------------------------------------------------------- 02192 // NOTE: 02193 // when the program flow arrives here, it is assumed that a successful 02194 // instantiation of a TrimAnalysis object took place with a mode set 02195 // to taTurn, i.e. 4 independent variables to be adjusted 02196 //---------------------------------------------------------------------------- 02197 02198 //---------------------------------------------------------------------------- 02199 // NOTE: 02200 // rely on the fact that IC quantities have been fed at least once into 02201 // their correspondent data structure in TrimAnalysis->fdmex tree 02202 //---------------------------------------------------------------------------- 02203 02204 double delta_cmd_T, delta_cmd_E, delta_cmd_A, delta_cmd_R; 02205 02206 double phi, theta, psi; 02207 02208 double alpha = 0.0, beta = 0.0; 02209 double gamma = 0.0; 02210 02211 // for coordinated turns with nonzero rate of climb 02212 gamma = FDMExec->GetIC()->GetFlightPathAngleRadIC(); 02213 02214 FGQuaternion Quat; 02215 FGColumnVector3 vPQRdot(0.0,0.0,0.0), vUVWdot(0.0,0.0,0.0); 02216 02217 FGPropagate::VehicleState VState; 02218 VState.vLocation = FGColumnVector3(0.0,0.0,0.0); 02219 VState.vUVW = FGColumnVector3(0.0,0.0,0.0); 02220 VState.vPQR = FGColumnVector3(0.0,0.0,0.0); 02221 VState.vQtrn = FGQuaternion (0.0,0.0,0.0); 02222 02223 //---------------------------------------------------------------------------- 02224 // Minimization control vector, i.e. independent variables 02225 //---------------------------------------------------------------------------- 02226 02227 // CHECK the trim mode 02228 02229 //----------------------------------------------------------------------------------------------------------------- 02230 // REMINDER: 02231 // n. of control variables for longitudinal trim (taTurn): 5 02232 // [0] throttle cmd, [1] elevator cmd, 2) aileron cmd, [3] rudder cmd 02233 // [4] psi 02234 //----------------------------------------------------------------------------------------------------------------- 02235 02236 delta_cmd_T = x[0]; // throttle, cmd 02237 delta_cmd_E = x[1]; // elevator, cmd 02238 delta_cmd_A = x[2]; // aileron, cmd 02239 delta_cmd_R = x[3]; // rudder, cmd 02240 02241 psi = x[4]; 02242 02243 // theta_underlined (see paper on Trim) for coordinated turn 02244 theta = atan2( sin(psi)*cos(gamma)+cos(psi)*sin(gamma), cos(gamma) ); 02245 02246 TrimAnalysis->setupTurnPhi(psi,theta); // calculates also phi 02247 02248 double psiIC = FDMExec->GetIC()->GetPsiRadIC(); 02249 double phiIC = FDMExec->GetIC()->GetPhiRadIC(); 02250 02251 phi = TrimAnalysis->GetPhiRad(); // this one is calculated by setupTurnPhi() above 02252 02253 TrimAnalysis->SetEulerAngles(phi, theta, psi); // recalculates sin and cosines 02254 02255 //---------------------------------------------------------------------------- 02256 // parameter bound check 02257 // ToDo: manage bounds via the FGTrimAnalysisControl class 02258 //---------------------------------------------------------------------------- 02259 bool penalty = ( (delta_cmd_T < 0) || (delta_cmd_T > 1) ) 02260 || ( (delta_cmd_E < -1) || (delta_cmd_E > 1) ) 02261 || ( (delta_cmd_A < -1) || (delta_cmd_A > 1) ) 02262 || ( (delta_cmd_R < -1) || (delta_cmd_R > 1) ) 02263 || ( (psi < 0.0 ) || (psi > 2.0*M_PI) ) 02264 || ( (theta < -0.5*M_PI) || (theta > 0.5*M_PI) ) // may go out of range 02265 ; 02266 02267 if ( penalty ) { 02268 //------------------------------------------------- 02269 // just return an "infinite" value 02270 return HUGE_VAL; 02271 //------------------------------------------------- 02272 } else { 02273 calculateDottedStates(delta_cmd_T, delta_cmd_E, delta_cmd_A, delta_cmd_R, 02274 phi, theta, psi, 02275 TrimAnalysis->GetMode(), 02276 alpha, beta, gamma, 02277 VState, 02278 vUVWdot, vPQRdot ); 02279 } 02280 02281 double u , v , w , 02282 p , q , r , 02283 uDot, vDot, wDot, 02284 pDot, qDot, rDot; 02285 u = VState.vUVW (1); v = VState.vUVW (2); w = VState.vUVW (3); 02286 p = VState.vPQR (1); q = VState.vPQR (2); r = VState.vPQR (3); 02287 uDot = vUVWdot(1); vDot = vUVWdot(2); wDot = vUVWdot(3); 02288 pDot = vPQRdot(1); qDot = vPQRdot(2); rDot = vPQRdot(3); 02289 02290 double 02291 f = 1.000*uDot*uDot 02292 + vDot*vDot 02293 + 1.000*wDot*wDot 02294 + 0.010*pDot*pDot 02295 + 0.010*qDot*qDot 02296 + 0.010*rDot*rDot; 02297 02298 static int count = 0; 02299 count++; 02300 02301 if ( f < TrimAnalysis->GetCostFunctionValue() ) 02302 { 02303 // feed into the vector of TrimAnalysis Controls the current values 02304 vector<FGTrimAnalysisControl*>::iterator vi; 02305 for(vi=TrimAnalysis->GetControls()->begin(); 02306 vi!=TrimAnalysis->GetControls()->end();vi++) 02307 { 02308 if ( (*vi)->GetControlType()==JSBSim::taThrottle ) (*vi)->SetControl(delta_cmd_T); 02309 if ( (*vi)->GetControlType()==JSBSim::taElevator ) (*vi)->SetControl(delta_cmd_E); // TODO: taPitchTrim 02310 if ( (*vi)->GetControlType()==JSBSim::taAileron ) (*vi)->SetControl(delta_cmd_A); // TODO: taRollTrim 02311 if ( (*vi)->GetControlType()==JSBSim::taRudder ) (*vi)->SetControl(delta_cmd_R); // TODO: taYawTrim 02312 if ( (*vi)->GetControlType()==JSBSim::taPhi ) (*vi)->SetControl(phi); 02313 if ( (*vi)->GetControlType()==JSBSim::taTheta ) (*vi)->SetControl(theta); 02314 if ( (*vi)->GetControlType()==JSBSim::taHeading ) (*vi)->SetControl(psi); 02315 } 02316 02317 if ( f<=TrimAnalysis->GetTolerance() ) TrimAnalysis->SetTrimSuccessfull(); // SetTrimFailed(false); 02318 02319 // write on file, if open 02320 02321 // "# iteration, costf, 02322 // dT, dE, dA, dR, dPsi, dTheta, dPhi, Psi (rad), Theta (rad), Phi (rad), 02323 // uDot (fps2), vDot (fps2), wDot (fps2), pDot (rad/s2), qDot (rad/s2), rDot (rad/s2), 02324 // u (fps), v (fps), w (fps), p (rad/s), q (rad/s), r (rad/s), alpha (rad), beta (rad), 02325 // alphaDot (rad/s), betaDot (rad/s), Thrust" 02326 02327 ofstream* rfp = TrimAnalysis->GetResultsFile(); 02328 if (rfp) 02329 (*rfp) 02330 << count <<", "<< f <<", " // 1, 2 02331 << delta_cmd_T <<", " // 3 02332 << delta_cmd_E <<", " << delta_cmd_A <<", "<< delta_cmd_R <<", " // 4, 5, 6 02333 << phi <<", "<< theta <<", "<< psi <<", " // 7, 8, 9 02334 << uDot <<", "<< vDot <<", "<< wDot <<", " // 10, 11, 12 02335 << pDot <<", "<< qDot <<", "<< rDot <<", " // 13, 14, 15 02336 << u <<", "<< v <<", "<< w << ", " // 16, 17, 18 02337 << VState.vPQR(1) <<", "<< VState.vPQR(2) <<", "<< VState.vPQR(3) << ", " 02338 // 19, 20, 21 02339 << FDMExec->GetAuxiliary()->Getalpha() << ", " // 22 02340 << FDMExec->GetAuxiliary()->Getbeta() << ", " // 23 02341 << FDMExec->GetAuxiliary()->Getadot() << ", " // 24 02342 << FDMExec->GetAuxiliary()->Getbdot() << ", " // 25 02343 02344 << FDMExec->GetPropulsion()->GetEngine(0)->GetThrust() << "\n"; // 26 02345 02346 FDMExec->GetPropagate()->SetVState( VState ); // ?? is this really necessary 02347 02348 // update in TrimAnalysis some private variables, i.e. the state _u, _v, _w, ... 02349 TrimAnalysis->SetState(u, v, w, p, q, r, alpha, beta, phi, theta, psi, gamma); 02350 02351 TrimAnalysis->SetCostFunctionValue(f); // update the current minimum 02352 } 02353 02354 return f; 02355 } // end of myCostFunctionFullCoordinatedTurn 02356 02357 double Objective::myCostFunctionFullTurn(Vector<double> & x) 02358 { 02359 //---------------------------------------------------------------------------- 02360 // NOTE: 02361 // when the program flow arrives here, it is assumed that a successful 02362 // instantiation of a TrimAnalysis object took place with a mode set 02363 // to taTurnFull, i.e. 5 independent variables to be adjusted 02364 //---------------------------------------------------------------------------- 02365 02366 //---------------------------------------------------------------------------- 02367 // NOTE: 02368 // rely on the fact that IC quantities have been fed at least once into 02369 // their correspondent data structure in TrimAnalysis->fdmex tree 02370 //---------------------------------------------------------------------------- 02371 02372 double delta_cmd_T, delta_cmd_E, delta_cmd_A, delta_cmd_R; 02373 02374 double phi, theta, psi; 02375 02376 double alpha = 0.0, beta = 0.0; 02377 double gamma = 0.0; 02378 02379 // for coordinated turns with nonzero rate of climb 02380 gamma = FDMExec->GetIC()->GetFlightPathAngleRadIC(); 02381 02382 FGQuaternion Quat; 02383 FGColumnVector3 vPQRdot(0.0,0.0,0.0), vUVWdot(0.0,0.0,0.0); 02384 02385 FGPropagate::VehicleState VState; 02386 VState.vLocation = FGColumnVector3(0.0,0.0,0.0); 02387 VState.vUVW = FGColumnVector3(0.0,0.0,0.0); 02388 VState.vPQR = FGColumnVector3(0.0,0.0,0.0); 02389 VState.vQtrn = FGQuaternion (0.0,0.0,0.0); 02390 02391 //---------------------------------------------------------------------------- 02392 // Minimization control vector, i.e. independent variables 02393 //---------------------------------------------------------------------------- 02394 02395 // CHECK the trim mode 02396 02397 //----------------------------------------------------------------------------------------------------------------- 02398 // REMINDER: 02399 // n. of control variables for longitudinal trim (taTurn): 5 02400 // [0] throttle cmd, [1] elevator cmd, 2) aileron cmd, [3] rudder cmd 02401 // [4] psi, [5] theta 02402 //----------------------------------------------------------------------------------------------------------------- 02403 02404 delta_cmd_T = x[0]; // throttle, cmd 02405 delta_cmd_E = x[1]; // elevator, cmd 02406 delta_cmd_A = x[2]; // aileron, cmd 02407 delta_cmd_R = x[3]; // rudder, cmd 02408 02409 psi = x[4]; 02410 theta = x[5]; 02411 02412 TrimAnalysis->setupTurn(); 02413 02414 double psiIC = FDMExec->GetIC()->GetPsiRadIC(); 02415 double phiIC = FDMExec->GetIC()->GetPhiRadIC(); 02416 02417 phi = TrimAnalysis->GetPhiRad(); // this one is calculated by sutupTurnPhi() above 02418 02419 TrimAnalysis->SetEulerAngles(phi, theta, psi); // recalculates sin and cosines 02420 02421 //---------------------------------------------------------------------------- 02422 // parameter bound check 02423 // ToDo: manage bounds via the FGTrimAnalysisControl class 02424 //---------------------------------------------------------------------------- 02425 bool penalty = ( (delta_cmd_T < 0) || (delta_cmd_T > 1) ) 02426 || ( (delta_cmd_E < -1) || (delta_cmd_E > 1) ) 02427 || ( (delta_cmd_A < -1) || (delta_cmd_A > 1) ) 02428 || ( (delta_cmd_R < -1) || (delta_cmd_R > 1) ) 02429 || ( (psi < 0.0 ) || (psi > 2.0*M_PI) ) 02430 || ( (theta < -0.5*M_PI) || (theta > 0.5*M_PI) ) // may go out of range 02431 ; 02432 02433 if ( penalty ) { 02434 //------------------------------------------------- 02435 // just return an "infinite" value 02436 return HUGE_VAL; 02437 //------------------------------------------------- 02438 } else { 02439 calculateDottedStates(delta_cmd_T, delta_cmd_E, delta_cmd_A, delta_cmd_R, 02440 phi, theta, psi, 02441 TrimAnalysis->GetMode(), 02442 alpha, beta, gamma, 02443 VState, 02444 vUVWdot, vPQRdot ); 02445 } 02446 02447 double u , v , w , 02448 p , q , r , 02449 uDot, vDot, wDot, 02450 pDot, qDot, rDot; 02451 u = VState.vUVW (1); v = VState.vUVW (2); w = VState.vUVW (3); 02452 p = VState.vPQR (1); q = VState.vPQR (2); r = VState.vPQR (3); 02453 uDot = vUVWdot(1); vDot = vUVWdot(2); wDot = vUVWdot(3); 02454 pDot = vPQRdot(1); qDot = vPQRdot(2); rDot = vPQRdot(3); 02455 02456 double 02457 f = 1.000*uDot*uDot 02458 + vDot*vDot 02459 + 1.000*wDot*wDot 02460 + 0.010*pDot*pDot 02461 + 0.010*qDot*qDot 02462 + 0.010*rDot*rDot; 02463 02464 static int count = 0; 02465 count++; 02466 02467 if ( f < TrimAnalysis->GetCostFunctionValue() ) 02468 { 02469 // feed into the vector of TrimAnalysis Controls the current values 02470 vector<FGTrimAnalysisControl*>::iterator vi; 02471 for(vi=TrimAnalysis->GetControls()->begin(); 02472 vi!=TrimAnalysis->GetControls()->end();vi++) 02473 { 02474 if ( (*vi)->GetControlType()==JSBSim::taThrottle ) (*vi)->SetControl(delta_cmd_T); 02475 if ( (*vi)->GetControlType()==JSBSim::taElevator ) (*vi)->SetControl(delta_cmd_E); // TODO: taPitchTrim 02476 if ( (*vi)->GetControlType()==JSBSim::taAileron ) (*vi)->SetControl(delta_cmd_A); // TODO: taRollTrim 02477 if ( (*vi)->GetControlType()==JSBSim::taRudder ) (*vi)->SetControl(delta_cmd_R); // TODO: taYawTrim 02478 if ( (*vi)->GetControlType()==JSBSim::taPhi ) (*vi)->SetControl(phi); 02479 if ( (*vi)->GetControlType()==JSBSim::taTheta ) (*vi)->SetControl(theta); 02480 if ( (*vi)->GetControlType()==JSBSim::taHeading ) (*vi)->SetControl(psi); 02481 } 02482 02483 if ( f<=TrimAnalysis->GetTolerance() ) TrimAnalysis->SetTrimSuccessfull(); // SetTrimFailed(false); 02484 02485 // write on file, if open 02486 02487 // "# iteration, costf, 02488 // dT, dE, dA, dR, dPsi, dTheta, dPhi, Psi (rad), Theta (rad), Phi (rad), 02489 // uDot (fps2), vDot (fps2), wDot (fps2), pDot (rad/s2), qDot (rad/s2), rDot (rad/s2), 02490 // u (fps), v (fps), w (fps), p (rad/s), q (rad/s), r (rad/s), alpha (rad), beta (rad), 02491 // alphaDot (rad/s), betaDot (rad/s), Thrust" 02492 02493 ofstream* rfp = TrimAnalysis->GetResultsFile(); 02494 if (rfp) 02495 (*rfp) 02496 << count <<", "<< f <<", " // 1, 2 02497 << delta_cmd_T <<", " // 3 02498 << delta_cmd_E <<", " << delta_cmd_A <<", "<< delta_cmd_R <<", " // 4, 5, 6 02499 << phi <<", "<< theta <<", "<< psi <<", " // 7, 8, 9 02500 << uDot <<", "<< vDot <<", "<< wDot <<", " // 10, 11, 12 02501 << pDot <<", "<< qDot <<", "<< rDot <<", " // 13, 14, 15 02502 << u <<", "<< v <<", "<< w << ", " // 16, 17, 18 02503 << VState.vPQR(1) <<", "<< VState.vPQR(2) <<", "<< VState.vPQR(3) << ", " 02504 // 19, 20, 21 02505 << FDMExec->GetAuxiliary()->Getalpha() << ", " // 22 02506 << FDMExec->GetAuxiliary()->Getbeta() << ", " // 23 02507 << FDMExec->GetAuxiliary()->Getadot() << ", " // 24 02508 << FDMExec->GetAuxiliary()->Getbdot() << ", " // 25 02509 02510 << FDMExec->GetPropulsion()->GetEngine(0)->GetThrust() << "\n"; // 02511 02512 FDMExec->GetPropagate()->SetVState( VState ); // ?? is this really necessary 02513 02514 // update in TrimAnalysis some private variables, i.e. the state _u, _v, _w, ... 02515 TrimAnalysis->SetState(u, v, w, p, q, r, alpha, beta, phi, theta, psi, gamma); 02516 02517 TrimAnalysis->SetCostFunctionValue(f); // update the current minimum 02518 } 02519 02520 return f; 02521 } // end of myCostFunctionFullCoordinatedTurn 02522 02523 02524 double Objective::myCostFunctionPullUp(Vector<double> & x) 02525 { 02526 //---------------------------------------------------------------------------- 02527 // NOTE: 02528 // when the program flow arrives here, it is assumed that a successful 02529 // instantiation of a TrimAnalysis object took place with a mode set 02530 // to taTurn, i.e. 6 independent variables to be adjusted 02531 //---------------------------------------------------------------------------- 02532 02533 //---------------------------------------------------------------------------- 02534 // NOTE: 02535 // rely on the fact that IC quantities have been fed at least once into 02536 // their correspondent data structure in TrimAnalysis->fdmex tree 02537 //---------------------------------------------------------------------------- 02538 02539 double delta_cmd_T, delta_cmd_E, delta_cmd_A, delta_cmd_R; 02540 02541 double phi, theta, psi; 02542 02543 double alpha = 0.0, beta = 0.0, gamma = 0.0; 02544 02545 FGQuaternion Quat; 02546 FGColumnVector3 vPQRdot(0.0,0.0,0.0), vUVWdot(0.0,0.0,0.0); 02547 02548 FGPropagate::VehicleState VState; 02549 VState.vLocation = FGColumnVector3(0.0,0.0,0.0); 02550 VState.vUVW = FGColumnVector3(0.0,0.0,0.0); 02551 VState.vPQR = FGColumnVector3(0.0,0.0,0.0); 02552 VState.vQtrn = FGQuaternion (0.0,0.0,0.0); 02553 02554 //---------------------------------------------------------------------------- 02555 // Minimization control vector, i.e. independent variables 02556 //---------------------------------------------------------------------------- 02557 02558 // CHECK the trim mode 02559 02560 //----------------------------------------------------------------------------------------------------------------- 02561 // REMINDER: 02562 // n. of control variables for longitudinal trim (taPullup): 5 02563 // [0] throttle cmd, [1] elevator cmd, 2) aileron cmd, [3] rudder cmd 02564 // [4] theta 02565 //----------------------------------------------------------------------------------------------------------------- 02566 02567 delta_cmd_T = x[0]; // throttle, cmd 02568 delta_cmd_E = x[1]; // elevator, cmd 02569 delta_cmd_A = x[2]; // aileron, cmd 02570 delta_cmd_R = x[3]; // rudder, cmd 02571 02572 theta = x[4]; 02573 02574 double psiIC = FDMExec->GetIC()->GetPsiRadIC(); 02575 02576 phi = 0.0; // assume a wing level pull-up 02577 psi = psiIC; 02578 02579 TrimAnalysis->SetEulerAngles(phi, theta, psi); 02580 02581 //---------------------------------------------------------------------------- 02582 // parameter bound check 02583 // ToDo: manage bounds via the FGTrimAnalysisControl class 02584 //---------------------------------------------------------------------------- 02585 bool penalty = ( (delta_cmd_T < 0) || (delta_cmd_T > 1) ) 02586 || ( (delta_cmd_E < -1) || (delta_cmd_E > 1) ) 02587 || ( (delta_cmd_A < -1) || (delta_cmd_A > 1) ) 02588 || ( (delta_cmd_R < -1) || (delta_cmd_R > 1) ) 02589 || ( (theta < -0.5*M_PI) || (theta > 0.5*M_PI) ); 02590 02591 if ( penalty ) { 02592 //------------------------------------------------- 02593 // just return an "infinite" value 02594 return HUGE_VAL; 02595 //------------------------------------------------- 02596 } else { 02597 calculateDottedStates(delta_cmd_T, delta_cmd_E, delta_cmd_A, delta_cmd_R, 02598 phi, theta, psi, 02599 TrimAnalysis->GetMode(), 02600 alpha, beta, gamma, 02601 VState, 02602 vUVWdot, vPQRdot ); 02603 } 02604 02605 double u , v , w , 02606 p , q , r , 02607 uDot, vDot, wDot, 02608 pDot, qDot, rDot; 02609 u = VState.vUVW (1); v = VState.vUVW (2); w = VState.vUVW (3); 02610 p = VState.vPQR (1); q = VState.vPQR (2); r = VState.vPQR (3); 02611 uDot = vUVWdot(1); vDot = vUVWdot(2); wDot = vUVWdot(3); 02612 pDot = vPQRdot(1); qDot = vPQRdot(2); rDot = vPQRdot(3); 02613 02614 double 02615 f = 1.000*uDot*uDot 02616 + vDot*vDot 02617 + 1.000*wDot*wDot 02618 + 0.010*pDot*pDot 02619 + 0.010*qDot*qDot 02620 + 0.010*rDot*rDot; 02621 02622 static int count = 0; 02623 count++; 02624 02625 if ( f < TrimAnalysis->GetCostFunctionValue() ) 02626 { 02627 // feed into the vector of TrimAnalysis Controls the current values 02628 vector<FGTrimAnalysisControl*>::iterator vi; 02629 for(vi=TrimAnalysis->GetControls()->begin(); 02630 vi!=TrimAnalysis->GetControls()->end();vi++) 02631 { 02632 if ( (*vi)->GetControlType()==JSBSim::taThrottle ) (*vi)->SetControl(delta_cmd_T); 02633 if ( (*vi)->GetControlType()==JSBSim::taElevator ) (*vi)->SetControl(delta_cmd_E); // TODO: taPitchTrim 02634 if ( (*vi)->GetControlType()==JSBSim::taAileron ) (*vi)->SetControl(delta_cmd_A); // TODO: taRollTrim 02635 if ( (*vi)->GetControlType()==JSBSim::taRudder ) (*vi)->SetControl(delta_cmd_R); // TODO: taYawTrim 02636 if ( (*vi)->GetControlType()==JSBSim::taPhi ) (*vi)->SetControl(phi); 02637 if ( (*vi)->GetControlType()==JSBSim::taTheta ) (*vi)->SetControl(theta); 02638 if ( (*vi)->GetControlType()==JSBSim::taHeading ) (*vi)->SetControl(psi); 02639 } 02640 02641 if ( f<=TrimAnalysis->GetTolerance() ) TrimAnalysis->SetTrimSuccessfull(); // SetTrimFailed(false); 02642 02643 // write on file, if open 02644 02645 // "# iteration, costf, 02646 // dT, dE, dA, dR, dPsi, dTheta, dPhi, Psi (rad), Theta (rad), Phi (rad), 02647 // uDot (fps2), vDot (fps2), wDot (fps2), pDot (rad/s2), qDot (rad/s2), rDot (rad/s2), 02648 // u (fps), v (fps), w (fps), p (rad/s), q (rad/s), r (rad/s), alpha (rad), beta (rad), 02649 // alphaDot (rad/s), betaDot (rad/s), Thrust" 02650 02651 ofstream* rfp = TrimAnalysis->GetResultsFile(); 02652 if (rfp) 02653 (*rfp) 02654 << count <<", "<< f <<", " // 1, 2 02655 << delta_cmd_T <<", " // 3 02656 << delta_cmd_E <<", " <<delta_cmd_A <<", "<< delta_cmd_R <<", " // 4, 5, 6 02657 << phi <<", "<< theta <<", "<< psi <<", " // 7, 8, 9 02658 << uDot <<", "<< vDot <<", "<< wDot <<", " // 10, 11, 12 02659 << pDot <<", "<< qDot <<", "<< rDot <<", " // 13, 14, 15 02660 << u <<", "<< v <<", "<< w << ", " // 16, 17, 18 02661 << VState.vPQR(1) <<", "<< VState.vPQR(2) <<", "<< VState.vPQR(3) << ", " 02662 // 19, 20, 21 02663 << FDMExec->GetAuxiliary()->Getalpha() << ", " // 22 02664 << FDMExec->GetAuxiliary()->Getbeta() << ", " // 23 02665 << FDMExec->GetAuxiliary()->Getadot() << ", " // 24 02666 << FDMExec->GetAuxiliary()->Getbdot() << ", " // 25 02667 02668 << FDMExec->GetPropulsion()->GetEngine(0)->GetThrust() << "\n"; // 26 02669 02670 FDMExec->GetPropagate()->SetVState( VState ); // ?? is this really necessary 02671 02672 // update in TrimAnalysis some private variables, i.e. the state _u, _v, _w, ... 02673 TrimAnalysis->SetState(u, v, w, p, q, r, alpha, beta, phi, theta, psi, gamma); 02674 02675 TrimAnalysis->SetCostFunctionValue(f); // update the current minimum 02676 } 02677 02678 return f; 02679 } // end of myCostFunctionPullUp 02680 02681 02682 void Objective::calculateDottedStates(double delta_cmd_T, double delta_cmd_E, double delta_cmd_A, double delta_cmd_R, 02683 double phi, double theta, double psi, 02684 TrimAnalysisMode trimMode, 02685 double& alpha, double& beta, double& gamma, 02686 FGPropagate::VehicleState& VState, 02687 FGColumnVector3& vUVWdot, FGColumnVector3& vPQRdot ) 02688 { 02689 double stheta,sphi,spsi; 02690 double ctheta,cphi,cpsi; 02691 double phiW = 0.; 02692 FGPropulsion* Propulsion = FDMExec->GetPropulsion(); 02693 FGFCS* FCS = FDMExec->GetFCS(); 02694 FGAuxiliary* Auxiliary = FDMExec->GetAuxiliary(); 02695 02696 if ( ( trimMode == taTurn ) || ( trimMode == taTurnFull ) )//... Coordinated turn: p,q,r not zero, beta=0, gamma=0 02697 { 02698 // setupTurn has already made its job 02699 phiW = TrimAnalysis->GetPhiWRad(); 02700 } 02701 02702 cphi = cos(phi); sphi = sin(phi); // phi, rad 02703 ctheta = cos(theta); stheta = sin(theta); // theta, rad 02704 cpsi = cos(psi); spsi = sin(psi); // psi, rad 02705 02706 TrimAnalysis->SetEulerAngles(phi, theta, psi); 02707 02708 //------------------------------------------------- 02709 // apply controls 02710 //------------------------------------------------- 02711 02712 // make sure the engines are running 02713 for (unsigned int i=0; i<Propulsion->GetNumEngines(); i++) { 02714 Propulsion->GetEngine(i)->SetRunning(true); 02715 } 02716 02717 double tMin,tMax; 02718 for(unsigned i=0;i<Propulsion->GetNumEngines();i++) 02719 { 02720 tMin=Propulsion->GetEngine(i)->GetThrottleMin(); 02721 tMax=Propulsion->GetEngine(i)->GetThrottleMax(); 02722 FCS->SetThrottleCmd(i,tMin + delta_cmd_T *(tMax-tMin)); 02723 FCS->Run(); // apply throttle change 02724 } 02725 Propulsion->GetSteadyState(); // GetSteadyState processes all engines 02726 02727 // apply commands 02728 // ToDo: apply aerosurface deflections, 02729 // to override control system authority 02730 02731 FCS->SetDeCmd( delta_cmd_E ); // elevator 02732 FCS->SetDaCmd( delta_cmd_A ); // ailerons 02733 FCS->SetDrCmd( delta_cmd_R ); // rudder 02734 02735 FCS->Run(); // apply yoke & pedal changes 02736 02737 //................................................ 02738 // set also euler angles 02739 //................................................ 02740 02741 // new euler angles -> new quaternion Quat 02742 02743 FGQuaternion Quat1( phi, theta, psi ); // values coming fro search algorithm 02744 02745 VState.vQtrn = Quat1; 02746 VState.vQtrn.Normalize(); 02747 02748 //------------------------------------------ 02749 // reconstruct NED velocity components from 02750 // initial conditions 02751 //------------------------------------------ 02752 02753 // initial altitude (never changed) 02754 double hIC = FDMExec->GetIC()->GetAltitudeFtIC(); 02755 02756 // re-apply the desired altitude; 02757 // FGAtmosphere wants the altitude from FGPropagate 02758 // (currently there's no Seth method in FGAtmosphere) 02759 FDMExec->GetPropagate()->Seth( hIC ); 02760 FDMExec->GetAtmosphere()->Run(); 02761 02762 // initial speed (never changed) 02763 double vtIC = FDMExec->GetIC()->GetVtrueFpsIC(); 02764 02765 // initial flight-path angle (never changed) 02766 double gammaIC = FDMExec->GetIC()->GetFlightPathAngleRadIC(); 02767 02768 // initial climb-rate (never changed) 02769 // ??? 02770 double rocIC = FDMExec->GetIC()->GetClimbRateFpsIC(); 02771 // ??? 02772 gammaIC = TrimAnalysis->GetGamma(); // from file of from IC 02773 rocIC = vtIC * tan(gammaIC); 02774 gamma = gammaIC; // <--------------------------------------------- this goes out to the caller 02775 02776 double vdownIC = - rocIC; 02777 02778 if ( ( trimMode == taTurn ) || ( trimMode == taTurnFull ) ) //... turn: p,q,r, gamma not zero 02779 { 02780 gamma = TrimAnalysis->GetGammaRad(); //0.0; 02781 vdownIC = TrimAnalysis->GetVtFps() * tan(gamma); //0.0; 02782 } 02783 Auxiliary->SetGamma(gamma); 02784 02785 // euler angles from the IC 02786 double psiIC = FDMExec->GetIC()->GetPsiRadIC(); // this is the desired ground track direction 02787 02788 // assume that the desired ground-track heading coincides with the 02789 // aircraft initial heading given in IC 02790 double psigtIC = psiIC; 02791 02792 // NED velocity components 02793 //double vgIC = FDMExec->GetIC()->GetVgroundFpsIC(); 02794 double vgIC = vtIC * cos(gammaIC); 02795 02796 double vnorthIC = vgIC * cos(psigtIC); 02797 double veastIC = vgIC * sin(psigtIC); 02798 02799 // Wind components in NED frame 02800 double wnorthIC = FDMExec->GetIC()->GetWindNFpsIC(); 02801 double weastIC = FDMExec->GetIC()->GetWindEFpsIC(); 02802 double wdownIC = FDMExec->GetIC()->GetWindDFpsIC(); 02803 02804 // Velocity components in body-frame (from NED) 02805 double u, v, w; 02806 02807 u=vnorthIC*ctheta*cpsi + 02808 veastIC*ctheta*spsi - 02809 vdownIC*stheta; 02810 v=vnorthIC*( sphi*stheta*cpsi - cphi*spsi ) + 02811 veastIC*( sphi*stheta*spsi + cphi*cpsi ) + 02812 vdownIC*sphi*ctheta; 02813 w=vnorthIC*( cphi*stheta*cpsi + sphi*spsi ) + 02814 veastIC*( cphi*stheta*spsi - sphi*cpsi ) + 02815 vdownIC*cphi*ctheta; 02816 02817 // Wind domponents in body-frame (from NED) 02818 double uw, vw, ww; 02819 02820 uw=wnorthIC*ctheta*cpsi + 02821 weastIC*ctheta*spsi - 02822 wdownIC*stheta; 02823 vw=wnorthIC*( sphi*stheta*cpsi - cphi*spsi ) + 02824 weastIC*( sphi*stheta*spsi + cphi*cpsi ) + 02825 wdownIC*sphi*ctheta; 02826 ww=wnorthIC*(cphi*stheta*cpsi + sphi*spsi) + 02827 weastIC*(cphi*stheta*spsi - sphi*cpsi) + 02828 wdownIC*cphi*ctheta; 02829 02830 /********************************************************************************************** 02831 P R O P A G A T I O N start . . . 02832 **********************************************************************************************/ 02833 02834 // now that we have the velocities we can imitate the chain 02835 // in FDMexec->Run()... Propagate::Run() 02836 02837 //++++++++++++++++++++++++++++++++++++++++++++ 02838 // recalculate some important auxiliary data 02839 // imitate here Auxiliary::Run() 02840 //++++++++++++++++++++++++++++++++++++++++++++ 02841 02842 double ua, va, wa; 02843 double adot, bdot; 02844 02845 Auxiliary->SetVt(vtIC); // re-apply the desired velocity 02846 02847 if ((trimMode==taTurn)||(trimMode==taPullup)) 02848 { 02849 v=0.0; // no sideslip 02850 } 02851 02852 ua = u + uw; va = v + vw; wa = w + ww; 02853 02854 // from FGAuxiliary::Run() 02855 if ( vtIC > 0.05) { 02856 if ( wa != 0.0 ) alpha = ua*ua > 0.0 ? atan2(wa, ua) : 0.0; // this goes out to the caller 02857 if ( va != 0.0 ) beta = ua*ua+wa*wa > 0.0 ? atan2(va,sqrt(ua*ua+wa*wa)) : 0.0; // this goes out to the caller 02858 double mUW = (ua*ua + wa*wa); 02859 double signU=1; 02860 if (ua != 0.0) signU = ua/fabs(ua); 02861 adot = bdot = 0; // enforce zero aerodyn. angle rates 02862 } else { 02863 alpha = beta = adot = bdot = 0; // this goes out to the caller 02864 } 02865 02866 //------------------------------------------------------------------------------------ 02867 // APPLY constraints to (p,q,r) 02868 //------------------------------------------------------------------------------------ 02869 02870 // constraint applied to p,q,r 02871 double p, q, r; 02872 02873 // for NON TURNING straight FLIGHT, these will remain zero 02874 p = q = r = 0.0; 02875 02876 if ( ( trimMode == taTurn ) || ( trimMode == taTurnFull ) ) //... Coordinated turn: p,q,r not zero 02877 { 02878 double g=FDMExec->GetInertial()->gravity(); 02879 //double cgamma=cos(FDMExec->GetIC()->GetFlightPathAngleRadIC()); 02880 02881 // assume that the given phiIC is the desired phi_Wind 02882 // phiW ... see above at the beginning 02883 double turnRate = g*tan(phiW) / FDMExec->GetIC()->GetVtrueFpsIC(); //FDMExec->GetIC()->GetUBodyFpsIC(); 02884 double pW = 0.0; 02885 double qW = turnRate*sin(phiW); 02886 double rW = turnRate*cos(phiW); 02887 02888 FGColumnVector3 pqr = TrimAnalysis->UpdateRatesTurn(psi, theta, phi, phiW); 02889 02890 // finally calculate the body frame angular rates 02891 p = pqr(1); 02892 q = pqr(2); 02893 r = pqr(3); 02894 02895 Auxiliary->SetGamma(0.); 02896 02897 } 02898 if ( trimMode == taPullup ) //... then p,q,r not zero 02899 { 02900 double g=FDMExec->GetInertial()->gravity(); 02901 double cgamma=cos(FDMExec->GetIC()->GetFlightPathAngleRadIC()); 02902 //double targetNlf=FDMExec->GetIC()->GetTargetNlfIC(); 02903 double targetNlf = TrimAnalysis->GetTargetNlf(); 02904 02905 FGColumnVector3 pqr = TrimAnalysis->UpdateRatesPullup(); 02906 q = pqr(2); 02907 02908 // assume levelled wings 02909 } 02910 //if ( trimMode == taRoll ) ... then p,q,r not zero 02911 // may assign theta rate (=q if wings are leveled) 02912 02913 double qbar = 0.5*FDMExec->GetAtmosphere()->GetDensity()*vtIC*vtIC; 02914 double qbarUW = 0.5*FDMExec->GetAtmosphere()->GetDensity()*(ua*ua + wa*wa); 02915 double qbarUV = 0.5*FDMExec->GetAtmosphere()->GetDensity()*(ua*ua + va*va); 02916 double Mach = vtIC / FDMExec->GetAtmosphere()->GetSoundSpeed(); 02917 02918 double MachU = ua / FDMExec->GetAtmosphere()->GetSoundSpeed(); 02919 double MachV = va / FDMExec->GetAtmosphere()->GetSoundSpeed(); 02920 double MachW = wa / FDMExec->GetAtmosphere()->GetSoundSpeed(); 02921 02922 //++++++++++++++++++++++++++++++++++++++++++++ 02923 // now feed the above values into Auxiliary data structure 02924 //++++++++++++++++++++++++++++++++++++++++++++ 02925 02926 Auxiliary->Setalpha( alpha ); 02927 Auxiliary->Setbeta ( beta ); 02928 02929 if ((trimMode==taTurn)||(trimMode==taPullup)) Auxiliary->Setbeta( 0.0 ); 02930 02931 // ensure zero rates 02932 Auxiliary->Setadot( 0.0 ); 02933 Auxiliary->Setbdot( 0.0 ); 02934 02935 // ToDo: take into account here the wind and other desired trim conditions 02936 Auxiliary->SetAeroPQR( FGColumnVector3(p,q,r)) ; 02937 02938 // note assumes that trim_mode is triggered 02939 // assumes that p=q=r=0, or set by the appropriate taTrimMode context 02940 02941 if ((trimMode==taTurn)||(trimMode==taPullup)) 02942 { 02943 v=0.0; // no sideslip 02944 } 02945 02946 JSBSim::FGColumnVector3 vUVWAero( ua,va,wa ); 02947 Auxiliary->SetAeroUVW( vUVWAero ); 02948 02949 Auxiliary->Setqbar ( qbar ); 02950 Auxiliary->SetqbarUV( qbarUV ); 02951 Auxiliary->SetqbarUW( qbarUW ); 02952 02953 Auxiliary->SetVt ( vtIC ); 02954 02955 Auxiliary->SetMach ( Mach ); 02956 Auxiliary->SetGamma ( gammaIC ); 02957 02958 // note: do not Auxiliary->Run(), otherwise dotted values 02959 // _and_ aerodynamic angles are recalculated !!! 02960 02961 //++++++++++++++++++++++++++++++++++++++++++++ 02962 // Recalculate aerodynamics by taking into 02963 // account the above changes 02964 //++++++++++++++++++++++++++++++++++++++++++++ 02965 FDMExec->GetAerodynamics()->Run(); 02966 02967 //++++++++++++++++++++++++++++++++++++++++++++ 02968 // Recalculate propulsion forces & moments 02969 //++++++++++++++++++++++++++++++++++++++++++++ 02970 //Propulsion->Run(); 02971 Propulsion->GetSteadyState(); 02972 02973 //++++++++++++++++++++++++++++++++++++++++++++ 02974 // Recalculate GroundReaction forces & moments 02975 //++++++++++++++++++++++++++++++++++++++++++++ 02976 FDMExec->GetGroundReactions()->Run(); 02977 02978 //++++++++++++++++++++++++++++++++++++++++++++ 02979 // Recalculate TOTAL Forces & Moments 02980 //++++++++++++++++++++++++++++++++++++++++++++ 02981 FDMExec->GetAircraft()->Run(); 02982 02983 //++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 02984 // now we have the forces & moments 02985 // we need some Propagate-like statements ... i.e. replicate what Propagate::Run() does 02986 // to finally get the "dotted" state 02987 //++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 02988 02989 VState.vLocation = JSBSim::FGLocation( FDMExec->GetPropagate()->GetLongitude(), // location 02990 FDMExec->GetPropagate()->GetLatitude(), 02991 hIC + FDMExec->GetIC()->GetSeaLevelRadiusFtIC() ); 02992 02993 // _do not_ feed into Propagate at this level!! 02994 //FDMExec->GetPropagate()->SetLocation( VState.vLocation ); 02995 // but pass to the caller the calculated state and dotted state 02996 02997 VState.vUVW(1) = u; // u,v,w 02998 VState.vUVW(2) = v; 02999 VState.vUVW(3) = w; 03000 03001 VState.vPQR(1) = p; // p,q,r 03002 VState.vPQR(2) = q; 03003 VState.vPQR(3) = r; 03004 03005 //VState.vQtrn = Quat; // already fed above 03006 03007 const FGColumnVector3 omega( 0.0, 0.0, FDMExec->GetInertial()->omega() ); // earth rotation 03008 const FGColumnVector3& vForces = FDMExec->GetAircraft()->GetForces(); // current forces 03009 const FGColumnVector3& vMoments = FDMExec->GetAircraft()->GetMoments(); // current moments 03010 03011 double mass = FDMExec->GetMassBalance()->GetMass(); // mass 03012 const FGMatrix33& J = FDMExec->GetMassBalance()->GetJ(); // inertia matrix 03013 const FGMatrix33& Jinv = FDMExec->GetMassBalance()->GetJinv(); // inertia matrix inverse 03014 03015 double rd = FDMExec->GetPropagate()->GetRadius(); // radius 03016 if (rd == 0.0) {cerr << "radius = 0 !" << endl; rd = 1e-16;} // radius check 03017 03018 double rdInv = 1.0/rd; 03019 FGColumnVector3 gAccel( 0.0, 0.0, FDMExec->GetInertial()->GetGAccel(rd) ); 03020 03021 // The rotation matrices: 03022 const FGMatrix33& Tl2b = VState.vQtrn.GetT(); // local to body frame 03023 const FGMatrix33& Tb2l = VState.vQtrn.GetTInv(); // body to local frame 03024 const FGMatrix33& Tec2l = VState.vLocation.GetTec2l(); // earth centered to local frame 03025 const FGMatrix33& Tl2ec = VState.vLocation.GetTl2ec(); // local to earth centered frame 03026 03027 // Inertial angular velocity measured in the body frame. 03028 //... const FGColumnVector3 pqri = VState.vPQR + Tl2b*(Tec2l*omega); 03029 03030 // NOTE: the trim is valid in flat-earth hypothesis, 03031 // do not take into account the motion relative to the e.c., 03032 // consider only the motion wrt local frame 03033 03034 const FGColumnVector3 pqri = VState.vPQR; 03035 03036 const FGColumnVector3 vVel = Tb2l * VState.vUVW; 03037 03038 //+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 03039 // Finally compute the time derivatives of the vehicle state values: 03040 //+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 03041 03042 // Compute body frame rotational accelerations based on the current body moments 03043 vPQRdot = Jinv*(vMoments - pqri*(J*pqri)); // <------------------------ this goes out to the caller 03044 03045 // Compute body frame accelerations based on the current body forces 03046 vUVWdot = VState.vUVW*VState.vPQR + vForces/mass; // <----------------- this goes out to the caller 03047 03048 // Coriolis acceleration. 03049 FGColumnVector3 ecVel = Tl2ec*vVel; 03050 FGColumnVector3 ace = 2.0*omega*ecVel; 03051 //... vUVWdot -= Tl2b*(Tec2l*ace); 03052 03053 if (!FDMExec->GetGroundReactions()->GetWOW()) { 03054 // Centrifugal acceleration. 03055 FGColumnVector3 aeec = omega*(omega*VState.vLocation); 03056 } 03057 03058 // Gravitation accel 03059 vUVWdot += Tl2b*gAccel; 03060 03061 // Compute vehicle velocity wrt EC frame, expressed in EC frame 03062 FGColumnVector3 vLocationDot = Tl2ec * vVel; 03063 03064 FGColumnVector3 omegaLocal( rdInv*vVel(2), // East 03065 -rdInv*vVel(1), // North 03066 -rdInv*vVel(2)*VState.vLocation.GetTanLatitude() ); 03067 03068 // Compute quaternion orientation derivative on current body rates 03069 //... FGQuaternion vQtrndot = VState.vQtrn.GetQDot( VState.vPQR - Tl2b*omegaLocal ); 03070 FGQuaternion vQtrndot = VState.vQtrn.GetQDot( VState.vPQR ); 03071 03072 /********************************************************************************************** 03073 end of.... P R O P A G A T I O N 03074 **********************************************************************************************/ 03075 03076 } 03077 03078 bool FGTrimAnalysis::getSteadyState(int nrepeat ) 03079 { 03080 double currentThrust = 0, lastThrust=-1; 03081 int steady_count=0; 03082 bool steady=false; 03083 03084 while ( !steady && steady_count <= nrepeat ) 03085 { 03086 steady_count++; 03087 steady = Propulsion->GetSteadyState(); 03088 } 03089 return steady; 03090 } 03091 03092 //................................................................................ 03093 } // JSBSim namespace