Incorporated latest JSBsim updates.
This commit is contained in:
parent
bc91f0ce1c
commit
1153be7545
22 changed files with 455 additions and 190 deletions
|
@ -65,8 +65,8 @@ int fgJSBsimInit(double dt) {
|
|||
FGPath engine_path( current_options.get_fg_root() );
|
||||
engine_path.append( "Engine" );
|
||||
|
||||
FDMExec.GetAircraft()->LoadAircraft(aircraft_path.str(),
|
||||
engine_path.str(), "X15");
|
||||
FDMExec.GetAircraft()->LoadAircraftEx(aircraft_path.str(),
|
||||
engine_path.str(), "X15");
|
||||
FG_LOG( FG_FLIGHT, FG_INFO, " loaded aircraft" );
|
||||
|
||||
FDMExec.GetState()->Reset(aircraft_path.str(), "Reset00");
|
||||
|
|
|
@ -36,6 +36,7 @@ HISTORY
|
|||
12/12/98 JSB Created
|
||||
04/03/99 JSB Changed Aero() method to correct body axis force calculation
|
||||
from wind vector. Fix provided by Tony Peden.
|
||||
05/03/99 JSB Changed (for the better?) the way configurations are read in.
|
||||
|
||||
********************************************************************************
|
||||
COMMENTS, REFERENCES, and NOTES
|
||||
|
@ -133,12 +134,14 @@ stability derivatives for the aircraft.
|
|||
********************************************************************************
|
||||
INCLUDES
|
||||
*******************************************************************************/
|
||||
#include <dirent.h>
|
||||
|
||||
#include <sys/stat.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
#ifdef FGFS
|
||||
# ifndef __BORLANDC__
|
||||
# include <Include/compiler.h>
|
||||
# endif
|
||||
# ifdef FG_HAVE_STD_INCLUDES
|
||||
# include <cmath>
|
||||
# else
|
||||
|
@ -170,13 +173,6 @@ FGAircraft::FGAircraft(FGFDMExec* fdmex) : FGModel(fdmex)
|
|||
Name = "FGAircraft";
|
||||
|
||||
for (i=0;i<6;i++) coeff_ctr[i] = 0;
|
||||
|
||||
Axis[LiftCoeff] = "CLIFT";
|
||||
Axis[DragCoeff] = "CDRAG";
|
||||
Axis[SideCoeff] = "CSIDE";
|
||||
Axis[RollCoeff] = "CROLL";
|
||||
Axis[PitchCoeff] = "CPITCH";
|
||||
Axis[YawCoeff] = "CYAW";
|
||||
}
|
||||
|
||||
|
||||
|
@ -184,124 +180,185 @@ FGAircraft::~FGAircraft(void)
|
|||
{
|
||||
}
|
||||
|
||||
|
||||
bool FGAircraft::LoadAircraft(string aircraft_path, string engine_path, string fname)
|
||||
bool FGAircraft::LoadAircraftEx(string aircraft_path, string engine_path, string fname)
|
||||
{
|
||||
string path;
|
||||
string fullpath;
|
||||
string filename;
|
||||
string aircraftDef;
|
||||
string tag;
|
||||
DIR* dir;
|
||||
DIR* coeffdir;
|
||||
struct dirent* dirEntry;
|
||||
struct dirent* coeffdirEntry;
|
||||
struct stat st;
|
||||
struct stat st2;
|
||||
string holding_string;
|
||||
char scratch[128];
|
||||
ifstream coeffInFile;
|
||||
|
||||
aircraftDef = aircraft_path + "/" + fname + "/" + fname + ".dat";
|
||||
aircraftDef = aircraft_path + "/" + fname + "/" + fname + ".cfg";
|
||||
ifstream aircraftfile(aircraftDef.c_str());
|
||||
cout << "Reading Aircraft Configuration File: " << aircraftDef << endl;
|
||||
|
||||
if (aircraftfile) {
|
||||
aircraftfile >> AircraftName; // String with no embedded spaces
|
||||
aircraftfile >> WingArea; // square feet
|
||||
aircraftfile >> WingSpan; // feet
|
||||
aircraftfile >> cbar; // feet
|
||||
aircraftfile >> Ixx; // slug ft^2
|
||||
aircraftfile >> Iyy; // "
|
||||
aircraftfile >> Izz; // "
|
||||
aircraftfile >> Ixz; // "
|
||||
aircraftfile >> EmptyWeight; // pounds
|
||||
EmptyMass = EmptyWeight / GRAVITY;
|
||||
aircraftfile >> tag;
|
||||
numTanks = numEngines = 0;
|
||||
numSelectedOxiTanks = numSelectedFuelTanks = 0;
|
||||
|
||||
numTanks = numEngines = 0;
|
||||
numSelectedOxiTanks = numSelectedFuelTanks = 0;
|
||||
while (!aircraftfile.fail()) {
|
||||
holding_string.erase();
|
||||
aircraftfile >> holding_string;
|
||||
if (holding_string.compare("//",0,2) != 0) {
|
||||
// if (holding_string.compare(0, 2, "//") != 0) {
|
||||
|
||||
while ( !(tag == "EOF") ) {
|
||||
if (tag == "CGLOC") {
|
||||
aircraftfile >> Xcg; // inches
|
||||
aircraftfile >> Ycg; // inches
|
||||
aircraftfile >> Zcg; // inches
|
||||
} else if (tag == "EYEPOINTLOC") {
|
||||
aircraftfile >> Xep; // inches
|
||||
aircraftfile >> Yep; // inches
|
||||
aircraftfile >> Zep; // inches
|
||||
} else if (tag == "TANK") {
|
||||
if (holding_string == "AIRCRAFT") {
|
||||
cout << "Reading in Aircraft parameters ..." << endl;
|
||||
} else if (holding_string == "AERODYNAMICS") {
|
||||
cout << "Reading in Aerodynamic parameters ..." << endl;
|
||||
} else if (holding_string == "AC_NAME") {
|
||||
aircraftfile >> AircraftName; // String with no embedded spaces
|
||||
cout << "Aircraft Name: " << AircraftName << endl;
|
||||
} else if (holding_string == "AC_WINGAREA") {
|
||||
aircraftfile >> WingArea;
|
||||
cout << "Aircraft Wing Area: " << WingArea << endl;
|
||||
} else if (holding_string == "AC_WINGSPAN") {
|
||||
aircraftfile >> WingSpan;
|
||||
cout << "Aircraft WingSpan: " << WingSpan << endl;
|
||||
} else if (holding_string == "AC_CHORD") {
|
||||
aircraftfile >> cbar;
|
||||
cout << "Aircraft Chord: " << cbar << endl;
|
||||
} else if (holding_string == "AC_IXX") {
|
||||
aircraftfile >> Ixx;
|
||||
cout << "Aircraft Ixx: " << Ixx << endl;
|
||||
} else if (holding_string == "AC_IYY") {
|
||||
aircraftfile >> Iyy;
|
||||
cout << "Aircraft Iyy: " << Iyy << endl;
|
||||
} else if (holding_string == "AC_IZZ") {
|
||||
aircraftfile >> Izz;
|
||||
cout << "Aircraft Izz: " << Izz << endl;
|
||||
} else if (holding_string == "AC_IXZ") {
|
||||
aircraftfile >> Ixz;
|
||||
cout << "Aircraft Ixz: " << Ixz << endl;
|
||||
} else if (holding_string == "AC_EMPTYWT") {
|
||||
aircraftfile >> EmptyWeight;
|
||||
EmptyMass = EmptyWeight / GRAVITY;
|
||||
cout << "Aircraft Empty Weight: " << EmptyWeight << endl;
|
||||
} else if (holding_string == "AC_CGLOC") {
|
||||
aircraftfile >> Xcg >> Ycg >> Zcg;
|
||||
cout << "Aircraft C.G.: " << Xcg << " " << Ycg << " " << Zcg << endl;
|
||||
} else if (holding_string == "AC_EYEPTLOC") {
|
||||
aircraftfile >> Xep >> Yep >> Zep;
|
||||
cout << "Pilot Eyepoint: " << Xep << " " << Yep << " " << Zep << endl;
|
||||
} else if (holding_string == "AC_TANK") {
|
||||
Tank[numTanks] = new FGTank(aircraftfile);
|
||||
switch(Tank[numTanks]->GetType()) {
|
||||
case FGTank::ttFUEL:
|
||||
numSelectedFuelTanks++;
|
||||
cout << "Reading in Fuel Tank #" << numSelectedFuelTanks << " parameters ..." << endl;
|
||||
break;
|
||||
case FGTank::ttOXIDIZER:
|
||||
numSelectedOxiTanks++;
|
||||
cout << "Reading in Oxidizer Tank #" << numSelectedOxiTanks << " parameters ..." << endl;
|
||||
break;
|
||||
}
|
||||
numTanks++;
|
||||
} else if (tag == "ENGINE") {
|
||||
|
||||
} else if (holding_string == "AC_ENGINE") {
|
||||
|
||||
aircraftfile >> tag;
|
||||
cout << "Reading in " << tag << " Engine parameters ..." << endl;
|
||||
Engine[numEngines] = new FGEngine(FDMExec, engine_path, tag, numEngines);
|
||||
numEngines++;
|
||||
}
|
||||
aircraftfile >> tag;
|
||||
}
|
||||
aircraftfile.close();
|
||||
PutState();
|
||||
|
||||
// Read subdirectory for this aircraft for stability derivative lookup tables:
|
||||
//
|
||||
// Build up the path name to the aircraft file by appending the aircraft
|
||||
// name to the "aircraft/" initial path. Initialize the directory entry
|
||||
// structure dirEntry in preparation for reading through the directory.
|
||||
// Build up a path to each file in the directory sequentially and "stat" it
|
||||
// to see if the entry is a directory or a file. If the entry is a file, then
|
||||
// compare it to each string in the Axis[] array to see which axis the
|
||||
// directory represents: Lift, Drag, Side, Roll, Pitch, Yaw. When the match
|
||||
// is found, go into that directory and search for any coefficient files.
|
||||
// Build a new coefficient by passing the full pathname to the coefficient
|
||||
// file to the FGCoefficient constructor.
|
||||
//
|
||||
// Note: axis_ctr=0 for the Lift "axis", 1 for Drag, 2 for Side force, 3 for
|
||||
// Roll, 4 for Pitch, and 5 for Yaw. The term coeff_ctr merely keeps
|
||||
// track of the number of coefficients registered for each of the
|
||||
// previously mentioned axis.
|
||||
} else if (holding_string == "}") {
|
||||
|
||||
path = aircraft_path + "/" + AircraftName + "/";
|
||||
if (dir = opendir(path.c_str())) {
|
||||
} else if (holding_string == "{") {
|
||||
|
||||
} else if (holding_string == "LIFT") {
|
||||
|
||||
cout << " Lift Coefficients ..." << endl;
|
||||
aircraftfile >> tag;
|
||||
streampos gpos = aircraftfile.tellg();
|
||||
aircraftfile >> tag;
|
||||
if (tag != "}" ) {
|
||||
aircraftfile.seekg(gpos);
|
||||
Coeff[LiftCoeff][coeff_ctr[LiftCoeff]] = new FGCoefficient(FDMExec, aircraftfile);
|
||||
coeff_ctr[LiftCoeff]++;
|
||||
} else {
|
||||
cout << " None found ..." << endl;
|
||||
}
|
||||
|
||||
} else if (holding_string == "DRAG") {
|
||||
|
||||
cout << " Drag Coefficients ..." << endl;
|
||||
aircraftfile >> tag;
|
||||
streampos gpos = aircraftfile.tellg();
|
||||
aircraftfile >> tag;
|
||||
if (tag != "}" ) {
|
||||
aircraftfile.seekg(gpos);
|
||||
Coeff[DragCoeff][coeff_ctr[DragCoeff]] = new FGCoefficient(FDMExec, aircraftfile);
|
||||
coeff_ctr[DragCoeff]++;
|
||||
} else {
|
||||
cout << " None found ..." << endl;
|
||||
}
|
||||
|
||||
} else if (holding_string == "SIDE") {
|
||||
|
||||
cout << " Side Coefficients ..." << endl;
|
||||
aircraftfile >> tag;
|
||||
streampos gpos = aircraftfile.tellg();
|
||||
aircraftfile >> tag;
|
||||
if (tag != "}" ) {
|
||||
aircraftfile.seekg(gpos);
|
||||
Coeff[SideCoeff][coeff_ctr[SideCoeff]] = new FGCoefficient(FDMExec, aircraftfile);
|
||||
coeff_ctr[SideCoeff]++;
|
||||
} else {
|
||||
cout << " None found ..." << endl;
|
||||
}
|
||||
|
||||
} else if (holding_string == "ROLL") {
|
||||
|
||||
cout << " Roll Coefficients ..." << endl;
|
||||
aircraftfile >> tag;
|
||||
streampos gpos = aircraftfile.tellg();
|
||||
aircraftfile >> tag;
|
||||
if (tag != "}" ) {
|
||||
aircraftfile.seekg(gpos);
|
||||
Coeff[RollCoeff][coeff_ctr[RollCoeff]] = new FGCoefficient(FDMExec, aircraftfile);
|
||||
coeff_ctr[RollCoeff]++;
|
||||
} else {
|
||||
cout << " None found ..." << endl;
|
||||
}
|
||||
|
||||
} else if (holding_string == "PITCH") {
|
||||
|
||||
cout << " Pitch Coefficients ..." << endl;
|
||||
aircraftfile >> tag;
|
||||
streampos gpos = aircraftfile.tellg();
|
||||
aircraftfile >> tag;
|
||||
if (tag != "}" ) {
|
||||
aircraftfile.seekg(gpos);
|
||||
Coeff[PitchCoeff][coeff_ctr[PitchCoeff]] = new FGCoefficient(FDMExec, aircraftfile);
|
||||
coeff_ctr[PitchCoeff]++;
|
||||
} else {
|
||||
cout << " None found ..." << endl;
|
||||
}
|
||||
|
||||
} else if (holding_string == "YAW") {
|
||||
|
||||
cout << " Yaw Coefficients ..." << endl;
|
||||
aircraftfile >> tag;
|
||||
streampos gpos = aircraftfile.tellg();
|
||||
aircraftfile >> tag;
|
||||
if (tag != "}" ) {
|
||||
aircraftfile.seekg(gpos);
|
||||
Coeff[YawCoeff][coeff_ctr[YawCoeff]] = new FGCoefficient(FDMExec, aircraftfile);
|
||||
coeff_ctr[YawCoeff]++;
|
||||
} else {
|
||||
cout << " None found ..." << endl;
|
||||
}
|
||||
|
||||
} else {
|
||||
}
|
||||
|
||||
while (dirEntry = readdir(dir)) {
|
||||
fullpath = path + dirEntry->d_name;
|
||||
stat(fullpath.c_str(),&st);
|
||||
if ((st.st_mode & S_IFMT) == S_IFDIR) {
|
||||
for (int axis_ctr=0; axis_ctr < 6; axis_ctr++) {
|
||||
if (dirEntry->d_name == Axis[axis_ctr]) {
|
||||
if (coeffdir = opendir(fullpath.c_str())) {
|
||||
while (coeffdirEntry = readdir(coeffdir)) {
|
||||
if (coeffdirEntry->d_name[0] != '.') {
|
||||
filename = path + Axis[axis_ctr] + "/" + coeffdirEntry->d_name;
|
||||
stat(filename.c_str(),&st2);
|
||||
if (st2.st_size > 6) {
|
||||
Coeff[axis_ctr][coeff_ctr[axis_ctr]] = new FGCoefficient(FDMExec, filename);
|
||||
coeff_ctr[axis_ctr]++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
cerr << "Could not open directory " << path << " for reading" << endl;
|
||||
aircraftfile.getline(scratch, 127);
|
||||
}
|
||||
return true;
|
||||
|
||||
} else {
|
||||
cerr << "Unable to open aircraft definition file " << fname << endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
cout << "End of Configuration File Parsing." << endl;
|
||||
}
|
||||
|
||||
|
||||
|
@ -390,7 +447,7 @@ void FGAircraft::FAero(void)
|
|||
|
||||
for (int axis_ctr = 0; axis_ctr < 3; axis_ctr++)
|
||||
for (int ctr=0; ctr < coeff_ctr[axis_ctr]; ctr++)
|
||||
F[axis_ctr] += Coeff[axis_ctr][ctr]->Value();
|
||||
F[axis_ctr] += Coeff[axis_ctr][ctr]->TotalValue();
|
||||
|
||||
Forces[0] += F[DragCoeff]*cos(alpha)*cos(beta) - F[SideCoeff]*cos(alpha)*sin(beta) - F[LiftCoeff]*sin(alpha);
|
||||
Forces[1] += F[DragCoeff]*sin(beta) + F[SideCoeff]*cos(beta);
|
||||
|
@ -424,9 +481,13 @@ void FGAircraft::FProp(void)
|
|||
|
||||
void FGAircraft::MAero(void)
|
||||
{
|
||||
for (int axis_ctr = 0; axis_ctr < 3; axis_ctr++)
|
||||
for (int ctr = 0; ctr < coeff_ctr[axis_ctr+3]; ctr++)
|
||||
Moments[axis_ctr] += Coeff[axis_ctr+3][ctr]->Value();
|
||||
int axis_ctr, ctr;
|
||||
|
||||
for (axis_ctr = 0; axis_ctr < 3; axis_ctr++) {
|
||||
for (ctr = 0; ctr < coeff_ctr[axis_ctr+3]; ctr++) {
|
||||
Moments[axis_ctr] += Coeff[axis_ctr+3][ctr]->TotalValue();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
@ -463,3 +524,4 @@ void FGAircraft::GetState(void)
|
|||
void FGAircraft::PutState(void)
|
||||
{
|
||||
}
|
||||
|
||||
|
|
|
@ -145,6 +145,8 @@ INCLUDES
|
|||
# else
|
||||
# include <fstream.h>
|
||||
# endif
|
||||
# include STL_STRING
|
||||
FG_USING_STD(string);
|
||||
#else
|
||||
# include <fstream>
|
||||
#endif
|
||||
|
@ -158,6 +160,10 @@ INCLUDES
|
|||
DEFINITIONS
|
||||
*******************************************************************************/
|
||||
|
||||
#ifndef FGFS
|
||||
using namespace std;
|
||||
#endif
|
||||
|
||||
/*******************************************************************************
|
||||
CLASS DECLARATION
|
||||
*******************************************************************************/
|
||||
|
@ -182,7 +188,7 @@ public:
|
|||
// ***************************************************************************
|
||||
/** This function must be called with the name of an aircraft which
|
||||
has an associated .dat file in the appropriate subdirectory. The paths
|
||||
to the appropriate subdirectories are given as the first two parameters.
|
||||
to the appropriate subdirectories are given as the first two parameters.
|
||||
@memo Loads the given aircraft.
|
||||
@param string Path to the aircraft files
|
||||
@param string Path to the engine files
|
||||
|
@ -191,6 +197,18 @@ public:
|
|||
*/
|
||||
bool LoadAircraft(string, string, string);
|
||||
|
||||
// ***************************************************************************
|
||||
/** This function must be called with the name of an aircraft which
|
||||
has an associated .dat file in the appropriate subdirectory. The paths
|
||||
to the appropriate subdirectories are given as the first two parameters.
|
||||
@memo Loads the given aircraft.
|
||||
@param string Path to the aircraft files
|
||||
@param string Path to the engine files
|
||||
@param string The name of the aircraft to be loaded, e.g. "X15".
|
||||
@return True - if successful
|
||||
*/
|
||||
bool LoadAircraftEx(string, string, string);
|
||||
|
||||
// ***************************************************************************
|
||||
/** @memo Gets the aircraft name as defined in the aircraft config file.
|
||||
@param
|
||||
|
|
|
@ -53,6 +53,8 @@ Excel. The data is from the ICAO atmosphere model.
|
|||
CLASS DECLARATION
|
||||
*******************************************************************************/
|
||||
|
||||
using namespace std;
|
||||
|
||||
class FGAtmosphere : public FGModel
|
||||
{
|
||||
public:
|
||||
|
|
|
@ -43,6 +43,8 @@ INCLUDES
|
|||
DEFINES
|
||||
*******************************************************************************/
|
||||
|
||||
using namespace std;
|
||||
|
||||
/*******************************************************************************
|
||||
CLASS DECLARATION
|
||||
*******************************************************************************/
|
||||
|
|
|
@ -123,27 +123,11 @@ INCLUDES
|
|||
************************************ CODE **************************************
|
||||
*******************************************************************************/
|
||||
|
||||
FGCoefficient::FGCoefficient(FGFDMExec* fdex)
|
||||
{
|
||||
FDMExec = fdex;
|
||||
State = FDMExec->GetState();
|
||||
Atmosphere = FDMExec->GetAtmosphere();
|
||||
FCS = FDMExec->GetFCS();
|
||||
Aircraft = FDMExec->GetAircraft();
|
||||
Translation = FDMExec->GetTranslation();
|
||||
Rotation = FDMExec->GetRotation();
|
||||
Position = FDMExec->GetPosition();
|
||||
Auxiliary = FDMExec->GetAuxiliary();
|
||||
Output = FDMExec->GetOutput();
|
||||
|
||||
rows = columns = 0;
|
||||
}
|
||||
|
||||
|
||||
FGCoefficient::FGCoefficient(FGFDMExec* fdex, string fname)
|
||||
FGCoefficient::FGCoefficient(FGFDMExec* fdex, ifstream& coeffDefFile)
|
||||
{
|
||||
int r, c;
|
||||
float ftrashcan;
|
||||
string strashcan;
|
||||
|
||||
FDMExec = fdex;
|
||||
State = FDMExec->GetState();
|
||||
|
@ -156,13 +140,15 @@ FGCoefficient::FGCoefficient(FGFDMExec* fdex, string fname)
|
|||
Auxiliary = FDMExec->GetAuxiliary();
|
||||
Output = FDMExec->GetOutput();
|
||||
|
||||
ifstream coeffDefFile(fname.c_str());
|
||||
|
||||
if (coeffDefFile) {
|
||||
if (!coeffDefFile.fail()) {
|
||||
coeffDefFile >> name;
|
||||
cout << " " << name << endl;
|
||||
coeffDefFile >> strashcan;
|
||||
coeffDefFile >> description;
|
||||
cout << " " << description << endl;
|
||||
coeffDefFile >> method;
|
||||
cout << " " << method << endl;
|
||||
|
||||
if (method == "EQUATION") type = EQUATION;
|
||||
else if (method == "TABLE") type = TABLE;
|
||||
|
@ -172,84 +158,107 @@ FGCoefficient::FGCoefficient(FGFDMExec* fdex, string fname)
|
|||
|
||||
if (type == VECTOR || type == TABLE) {
|
||||
coeffDefFile >> rows;
|
||||
cout << " Rows: " << rows << " ";
|
||||
if (type == TABLE) {
|
||||
coeffDefFile >> columns;
|
||||
cout << "Cols: " << columns;
|
||||
}
|
||||
coeffDefFile >> LookupR;
|
||||
cout << endl;
|
||||
cout << " Row indexing parameter: " << LookupR << endl;
|
||||
}
|
||||
|
||||
if (type == TABLE) {
|
||||
coeffDefFile >> LookupC;
|
||||
cout << " Column indexing parameter: " << LookupC << endl;
|
||||
}
|
||||
|
||||
coeffDefFile >> multipliers;
|
||||
|
||||
cout << " Non-Dimensionalized by: ";
|
||||
|
||||
mult_count = 0;
|
||||
if (multipliers & FG_QBAR) {
|
||||
mult_idx[mult_count] = FG_QBAR;
|
||||
mult_count++;
|
||||
cout << "qbar ";
|
||||
}
|
||||
if (multipliers & FG_WINGAREA) {
|
||||
mult_idx[mult_count] = FG_WINGAREA;
|
||||
mult_count++;
|
||||
cout << "S ";
|
||||
}
|
||||
if (multipliers & FG_WINGSPAN) {
|
||||
mult_idx[mult_count] = FG_WINGSPAN;
|
||||
mult_count++;
|
||||
cout << "b ";
|
||||
}
|
||||
if (multipliers & FG_CBAR) {
|
||||
mult_idx[mult_count] = FG_CBAR;
|
||||
mult_count++;
|
||||
cout << "c ";
|
||||
}
|
||||
if (multipliers & FG_ALPHA) {
|
||||
mult_idx[mult_count] = FG_ALPHA;
|
||||
mult_count++;
|
||||
cout << "alpha ";
|
||||
}
|
||||
if (multipliers & FG_ALPHADOT) {
|
||||
mult_idx[mult_count] = FG_ALPHADOT;
|
||||
mult_count++;
|
||||
cout << "alphadot ";
|
||||
}
|
||||
if (multipliers & FG_BETA) {
|
||||
mult_idx[mult_count] = FG_BETA;
|
||||
mult_count++;
|
||||
cout << "beta ";
|
||||
}
|
||||
if (multipliers & FG_BETADOT) {
|
||||
mult_idx[mult_count] = FG_BETADOT;
|
||||
mult_count++;
|
||||
cout << "betadot ";
|
||||
}
|
||||
if (multipliers & FG_PITCHRATE) {
|
||||
mult_idx[mult_count] = FG_PITCHRATE;
|
||||
mult_count++;
|
||||
cout << "q ";
|
||||
}
|
||||
if (multipliers & FG_ROLLRATE) {
|
||||
mult_idx[mult_count] = FG_ROLLRATE;
|
||||
mult_count++;
|
||||
cout << "p ";
|
||||
}
|
||||
if (multipliers & FG_YAWRATE) {
|
||||
mult_idx[mult_count] = FG_YAWRATE;
|
||||
mult_count++;
|
||||
cout << "r ";
|
||||
}
|
||||
if (multipliers & FG_ELEVATOR) {
|
||||
mult_idx[mult_count] = FG_ELEVATOR;
|
||||
mult_count++;
|
||||
cout << "De ";
|
||||
}
|
||||
if (multipliers & FG_AILERON) {
|
||||
mult_idx[mult_count] = FG_AILERON;
|
||||
mult_count++;
|
||||
cout << "Da ";
|
||||
}
|
||||
if (multipliers & FG_RUDDER) {
|
||||
mult_idx[mult_count] = FG_RUDDER;
|
||||
mult_count++;
|
||||
cout << "Dr ";
|
||||
}
|
||||
if (multipliers & FG_MACH) {
|
||||
mult_idx[mult_count] = FG_MACH;
|
||||
mult_count++;
|
||||
cout << "Mach ";
|
||||
}
|
||||
if (multipliers & FG_ALTITUDE) {
|
||||
mult_idx[mult_count] = FG_ALTITUDE;
|
||||
mult_count++;
|
||||
cout << "h ";
|
||||
}
|
||||
|
||||
cout << endl;
|
||||
|
||||
switch(type) {
|
||||
case VALUE:
|
||||
coeffDefFile >> StaticValue;
|
||||
|
@ -261,6 +270,15 @@ FGCoefficient::FGCoefficient(FGFDMExec* fdex, string fname)
|
|||
coeffDefFile >> Table3D[r][0];
|
||||
coeffDefFile >> Table3D[r][1];
|
||||
}
|
||||
|
||||
for (r=0;r<=rows;r++) {
|
||||
cout << " ";
|
||||
for (c=0;c<=columns;c++) {
|
||||
cout << Table3D[r][c] << " ";
|
||||
}
|
||||
cout << endl;
|
||||
}
|
||||
|
||||
break;
|
||||
case TABLE:
|
||||
Allocate(rows, columns);
|
||||
|
@ -273,54 +291,24 @@ FGCoefficient::FGCoefficient(FGFDMExec* fdex, string fname)
|
|||
coeffDefFile >> Table3D[r][c];
|
||||
}
|
||||
}
|
||||
|
||||
for (r=0;r<=rows;r++) {
|
||||
cout << " ";
|
||||
for (c=0;c<=columns;c++) {
|
||||
cout << Table3D[r][c] << " ";
|
||||
}
|
||||
cout << endl;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
cerr << "Empty file" << endl;
|
||||
}
|
||||
coeffDefFile.close();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
FGCoefficient::FGCoefficient(FGFDMExec* fdex, int r, int c)
|
||||
{
|
||||
FDMExec = fdex;
|
||||
State = FDMExec->GetState();
|
||||
Atmosphere = FDMExec->GetAtmosphere();
|
||||
FCS = FDMExec->GetFCS();
|
||||
Aircraft = FDMExec->GetAircraft();
|
||||
Translation = FDMExec->GetTranslation();
|
||||
Rotation = FDMExec->GetRotation();
|
||||
Position = FDMExec->GetPosition();
|
||||
Auxiliary = FDMExec->GetAuxiliary();
|
||||
Output = FDMExec->GetOutput();
|
||||
|
||||
rows = r;
|
||||
columns = c;
|
||||
Allocate(r,c);
|
||||
}
|
||||
|
||||
|
||||
FGCoefficient::FGCoefficient(FGFDMExec* fdex, int n)
|
||||
{
|
||||
FDMExec = fdex;
|
||||
State = FDMExec->GetState();
|
||||
Atmosphere = FDMExec->GetAtmosphere();
|
||||
FCS = FDMExec->GetFCS();
|
||||
Aircraft = FDMExec->GetAircraft();
|
||||
Translation = FDMExec->GetTranslation();
|
||||
Rotation = FDMExec->GetRotation();
|
||||
Position = FDMExec->GetPosition();
|
||||
Auxiliary = FDMExec->GetAuxiliary();
|
||||
Output = FDMExec->GetOutput();
|
||||
|
||||
rows = n;
|
||||
columns = 0;
|
||||
Allocate(n);
|
||||
}
|
||||
|
||||
|
||||
FGCoefficient::~FGCoefficient(void)
|
||||
{
|
||||
}
|
||||
|
@ -365,11 +353,15 @@ float FGCoefficient::Value(float rVal, float cVal)
|
|||
col2temp = rFactor*(Table3D[r][c] - Table3D[r-1][c]) + Table3D[r-1][c];
|
||||
|
||||
Value = col1temp + cFactor*(col2temp - col1temp);
|
||||
|
||||
|
||||
//cout << "Value for " << description << " is " << Value;
|
||||
|
||||
for (midx=0;midx<mult_count;midx++) {
|
||||
Value *= GetCoeffVal(mult_idx[midx]);
|
||||
}
|
||||
|
||||
//cout << " after multipliers it is: " << Value << endl;
|
||||
|
||||
return Value;
|
||||
}
|
||||
|
||||
|
@ -390,23 +382,46 @@ float FGCoefficient::Value(float Val)
|
|||
} else {
|
||||
Factor = 1.0;
|
||||
}
|
||||
|
||||
Value = Factor*(Table3D[r][1] - Table3D[r-1][1]) + Table3D[r-1][1];
|
||||
|
||||
// cout << "Value for " << description << " is " << Value;
|
||||
|
||||
for (midx=0;midx<mult_count;midx++) {
|
||||
Value *= GetCoeffVal(mult_idx[midx]);
|
||||
}
|
||||
|
||||
//cout << " after multipliers it is: " << Value << endl;
|
||||
|
||||
return Value;
|
||||
}
|
||||
|
||||
|
||||
float FGCoefficient::Value()
|
||||
float FGCoefficient::Value(void)
|
||||
{
|
||||
float Value;
|
||||
int midx;
|
||||
|
||||
Value = StaticValue;
|
||||
|
||||
// cout << "Value for " << description << " is " << Value << endl;
|
||||
|
||||
for (midx=0;midx<mult_count;midx++) {
|
||||
Value *= GetCoeffVal(mult_idx[midx]);
|
||||
}
|
||||
|
||||
// cout << " after multipliers it is: " << Value << endl;
|
||||
|
||||
return Value;
|
||||
}
|
||||
|
||||
float FGCoefficient::TotalValue()
|
||||
{
|
||||
switch(type) {
|
||||
case 0:
|
||||
return -1;
|
||||
case 1:
|
||||
return (StaticValue);
|
||||
return (Value());
|
||||
case 2:
|
||||
return (Value(GetCoeffVal(LookupR)));
|
||||
case 3:
|
||||
|
@ -421,36 +436,52 @@ float FGCoefficient::GetCoeffVal(int val_idx)
|
|||
{
|
||||
switch(val_idx) {
|
||||
case FG_QBAR:
|
||||
//cout << "Qbar: " << State->Getqbar() << endl;
|
||||
return State->Getqbar();
|
||||
case FG_WINGAREA:
|
||||
//cout << "S: " << Aircraft->GetWingArea() << endl;
|
||||
return Aircraft->GetWingArea();
|
||||
case FG_WINGSPAN:
|
||||
//cout << "b: " << Aircraft->GetWingSpan() << endl;
|
||||
return Aircraft->GetWingSpan();
|
||||
case FG_CBAR:
|
||||
//cout << "Cbar: " << Aircraft->Getcbar() << endl;
|
||||
return Aircraft->Getcbar();
|
||||
case FG_ALPHA:
|
||||
//cout << "Alpha: " << Translation->Getalpha() << endl;
|
||||
return Translation->Getalpha();
|
||||
case FG_ALPHADOT:
|
||||
//cout << "Adot: " << State->Getadot() << endl;
|
||||
return State->Getadot();
|
||||
case FG_BETA:
|
||||
//cout << "Beta: " << Translation->Getbeta() << endl;
|
||||
return Translation->Getbeta();
|
||||
case FG_BETADOT:
|
||||
//cout << "Bdot: " << State->Getbdot() << endl;
|
||||
return State->Getbdot();
|
||||
case FG_PITCHRATE:
|
||||
//cout << "Q: " << Rotation->GetQ() << endl;
|
||||
return Rotation->GetQ();
|
||||
case FG_ROLLRATE:
|
||||
//cout << "P: " << Rotation->GetP() << endl;
|
||||
return Rotation->GetP();
|
||||
case FG_YAWRATE:
|
||||
//cout << "R: " << Rotation->GetR() << endl;
|
||||
return Rotation->GetR();
|
||||
case FG_ELEVATOR:
|
||||
//cout << "De: " << FCS->GetDe() << endl;
|
||||
return FCS->GetDe();
|
||||
case FG_AILERON:
|
||||
//cout << "Da: " << FCS->GetDa() << endl;
|
||||
return FCS->GetDa();
|
||||
case FG_RUDDER:
|
||||
//cout << "Dr: " << FCS->GetDr() << endl;
|
||||
return FCS->GetDr();
|
||||
case FG_MACH:
|
||||
//cout << "Mach: " << State->GetMach() << endl;
|
||||
return State->GetMach();
|
||||
case FG_ALTITUDE:
|
||||
//cout << "h: " << State->Geth() << endl;
|
||||
return State->Geth();
|
||||
}
|
||||
return 0;
|
||||
|
|
|
@ -54,6 +54,9 @@ INCLUDES
|
|||
/*******************************************************************************
|
||||
DEFINES
|
||||
*******************************************************************************/
|
||||
|
||||
using namespace std;
|
||||
|
||||
#define FG_QBAR 1
|
||||
#define FG_WINGAREA 2
|
||||
#define FG_WINGSPAN 4
|
||||
|
@ -189,32 +192,12 @@ CLASS DECLARATION
|
|||
class FGCoefficient
|
||||
{
|
||||
public:
|
||||
// ***************************************************************************
|
||||
/** @memo Constructor
|
||||
@param FGFDMExec* - pointer to owning simulation executive
|
||||
*/
|
||||
FGCoefficient(FGFDMExec*);
|
||||
|
||||
// ***************************************************************************
|
||||
/** @memo Constructor for two independent variable table
|
||||
@param
|
||||
@return
|
||||
*/
|
||||
FGCoefficient(FGFDMExec*, int, int);
|
||||
|
||||
// ***************************************************************************
|
||||
/** @memo
|
||||
@param
|
||||
@return
|
||||
*/
|
||||
FGCoefficient(FGFDMExec*, int);
|
||||
|
||||
// ***************************************************************************
|
||||
/** @memo
|
||||
@param
|
||||
@return
|
||||
*/
|
||||
FGCoefficient(FGFDMExec*, string);
|
||||
FGCoefficient(FGFDMExec*, ifstream&);
|
||||
|
||||
// ***************************************************************************
|
||||
/** @memo
|
||||
|
@ -258,6 +241,13 @@ public:
|
|||
*/
|
||||
float Value(void);
|
||||
|
||||
// ***************************************************************************
|
||||
/** @memo
|
||||
@param
|
||||
@return
|
||||
*/
|
||||
float TotalValue(void);
|
||||
|
||||
// ***************************************************************************
|
||||
/** @memo
|
||||
@param
|
||||
|
|
|
@ -19,9 +19,10 @@
|
|||
// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
//
|
||||
// $Id$
|
||||
// (Log is kept at end of this file)
|
||||
|
||||
|
||||
#include "controls.hxx"
|
||||
#include "FGControls.h"
|
||||
|
||||
|
||||
FGControls controls;
|
||||
|
@ -49,3 +50,51 @@ FGControls::~FGControls() {
|
|||
}
|
||||
|
||||
|
||||
// $Log$
|
||||
// Revision 1.3 1999/05/08 03:19:15 curt
|
||||
// Incorporated latest JSBsim updates.
|
||||
//
|
||||
// Revision 1.1 1999/02/13 01:12:03 curt
|
||||
// Initial Revision.
|
||||
//
|
||||
// Revision 1.1 1999/02/09 04:51:32 jon
|
||||
// Initial revision
|
||||
//
|
||||
// Revision 1.3 1998/12/05 16:13:12 curt
|
||||
// Renamed class fgCONTROLS to class FGControls.
|
||||
//
|
||||
// Revision 1.2 1998/10/25 14:08:41 curt
|
||||
// Turned "struct fgCONTROLS" into a class, with inlined accessor functions.
|
||||
//
|
||||
// Revision 1.1 1998/10/18 01:51:05 curt
|
||||
// c++-ifying ...
|
||||
//
|
||||
// Revision 1.8 1998/09/29 02:01:31 curt
|
||||
// Added a brake.
|
||||
//
|
||||
// Revision 1.7 1998/02/07 15:29:36 curt
|
||||
// Incorporated HUD changes and struct/typedef changes from Charlie Hotchkiss
|
||||
// <chotchkiss@namg.us.anritsu.com>
|
||||
//
|
||||
// Revision 1.6 1998/01/19 19:27:02 curt
|
||||
// Merged in make system changes from Bob Kuehne <rpk@sgi.com>
|
||||
// This should simplify things tremendously.
|
||||
//
|
||||
// Revision 1.5 1998/01/19 18:40:22 curt
|
||||
// Tons of little changes to clean up the code and to remove fatal errors
|
||||
// when building with the c++ compiler.
|
||||
//
|
||||
// Revision 1.4 1997/12/10 22:37:41 curt
|
||||
// Prepended "fg" on the name of all global structures that didn't have it yet.
|
||||
// i.e. "struct WEATHER {}" became "struct fgWEATHER {}"
|
||||
//
|
||||
// Revision 1.3 1997/08/27 03:30:01 curt
|
||||
// Changed naming scheme of basic shared structures.
|
||||
//
|
||||
// Revision 1.2 1997/06/21 17:12:48 curt
|
||||
// Capitalized subdirectory names.
|
||||
//
|
||||
// Revision 1.1 1997/05/31 19:24:04 curt
|
||||
// Initial revision.
|
||||
//
|
||||
|
||||
|
|
|
@ -19,6 +19,7 @@
|
|||
// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
//
|
||||
// $Id$
|
||||
// (Log is kept at end of this file)
|
||||
|
||||
|
||||
#ifndef _CONTROLS_HXX
|
||||
|
@ -29,6 +30,7 @@
|
|||
# error This library requires C++
|
||||
#endif
|
||||
|
||||
//using namespace std;
|
||||
|
||||
// Define a structure containing the control parameters
|
||||
|
||||
|
@ -174,3 +176,75 @@ extern FGControls controls;
|
|||
#endif // _CONTROLS_HXX
|
||||
|
||||
|
||||
// $Log$
|
||||
// Revision 1.3 1999/05/08 03:19:16 curt
|
||||
// Incorporated latest JSBsim updates.
|
||||
//
|
||||
// Revision 1.1 1999/02/13 01:12:03 curt
|
||||
// Initial Revision.
|
||||
//
|
||||
// Revision 1.3 1998/12/05 16:13:13 curt
|
||||
// Renamed class fgCONTROLS to class FGControls.
|
||||
//
|
||||
// Revision 1.2 1998/10/25 14:08:42 curt
|
||||
// Turned "struct fgCONTROLS" into a class, with inlined accessor functions.
|
||||
//
|
||||
// Revision 1.1 1998/10/18 01:51:07 curt
|
||||
// c++-ifying ...
|
||||
//
|
||||
// Revision 1.17 1998/09/29 14:57:00 curt
|
||||
// c++-ified some comments.
|
||||
//
|
||||
// Revision 1.16 1998/09/29 02:01:32 curt
|
||||
// Added a brake.
|
||||
//
|
||||
// Revision 1.15 1998/04/25 22:06:27 curt
|
||||
// Edited cvs log messages in source files ... bad bad bad!
|
||||
//
|
||||
// Revision 1.14 1998/04/22 13:26:19 curt
|
||||
// C++ - ifing the code a bit.
|
||||
//
|
||||
// Revision 1.13 1998/04/21 17:02:35 curt
|
||||
// Prepairing for C++ integration.
|
||||
//
|
||||
// Revision 1.12 1998/02/09 22:56:48 curt
|
||||
// Removed "depend" files from cvs control. Other minor make tweaks.
|
||||
//
|
||||
// Revision 1.11 1998/02/07 15:29:36 curt
|
||||
// Incorporated HUD changes and struct/typedef changes from Charlie Hotchkiss
|
||||
// <chotchkiss@namg.us.anritsu.com>
|
||||
//
|
||||
// Revision 1.10 1998/01/27 00:47:52 curt
|
||||
// Incorporated Paul Bleisch's <pbleisch@acm.org> new debug message
|
||||
// system and commandline/config file processing code.
|
||||
//
|
||||
// Revision 1.9 1998/01/22 02:59:31 curt
|
||||
// Changed #ifdef FILE_H to #ifdef _FILE_H
|
||||
//
|
||||
// Revision 1.8 1998/01/19 18:40:22 curt
|
||||
// Tons of little changes to clean up the code and to remove fatal errors
|
||||
// when building with the c++ compiler.
|
||||
//
|
||||
// Revision 1.7 1997/12/15 23:54:36 curt
|
||||
// Add xgl wrappers for debugging.
|
||||
// Generate terrain normals on the fly.
|
||||
//
|
||||
// Revision 1.6 1997/12/10 22:37:41 curt
|
||||
// Prepended "fg" on the name of all global structures that didn't have it yet.
|
||||
// i.e. "struct WEATHER {}" became "struct fgWEATHER {}"
|
||||
//
|
||||
// Revision 1.5 1997/08/27 03:30:02 curt
|
||||
// Changed naming scheme of basic shared structures.
|
||||
//
|
||||
// Revision 1.4 1997/07/23 21:52:18 curt
|
||||
// Put comments around the text after an #endif for increased portability.
|
||||
//
|
||||
// Revision 1.3 1997/05/31 19:16:27 curt
|
||||
// Elevator trim added.
|
||||
//
|
||||
// Revision 1.2 1997/05/23 15:40:33 curt
|
||||
// Added GNU copyright headers.
|
||||
//
|
||||
// Revision 1.1 1997/05/16 15:59:48 curt
|
||||
// Initial revision.
|
||||
//
|
||||
|
|
|
@ -56,6 +56,8 @@ INCLUDES
|
|||
DEFINES
|
||||
*******************************************************************************/
|
||||
|
||||
using namespace std;
|
||||
|
||||
/*******************************************************************************
|
||||
CLASS DECLARATION
|
||||
*******************************************************************************/
|
||||
|
|
|
@ -44,6 +44,7 @@ INCLUDES
|
|||
CLASS DECLARATION
|
||||
*******************************************************************************/
|
||||
|
||||
using namespace std;
|
||||
|
||||
class FGFCS : public FGModel
|
||||
{
|
||||
|
|
|
@ -40,6 +40,8 @@ INCLUDES
|
|||
|
||||
#include "FGModel.h"
|
||||
|
||||
using namespace std;
|
||||
|
||||
/*******************************************************************************
|
||||
CLASS DECLARATION
|
||||
*******************************************************************************/
|
||||
|
|
|
@ -28,11 +28,21 @@ void main(int argc, char** argv)
|
|||
|
||||
FDMExec = new FGFDMExec();
|
||||
|
||||
FDMExec->GetAircraft()->LoadAircraft("aircraft", "engine", string(argv[1]));
|
||||
FDMExec->GetAircraft()->LoadAircraftEx("aircraft", "engine", string(argv[1]));
|
||||
FDMExec->GetState()->Reset("aircraft", string(argv[2]));
|
||||
|
||||
while (FDMExec->GetState()->Getsim_time() <= 25.0)
|
||||
{
|
||||
//
|
||||
// fake an aileron, rudder and elevator kick here after 20 seconds
|
||||
//
|
||||
|
||||
if (FDMExec->GetState()->Getsim_time() > 5.0) {
|
||||
FDMExec->GetFCS()->SetDa(0.05);
|
||||
FDMExec->GetFCS()->SetDr(0.05);
|
||||
FDMExec->GetFCS()->SetDe(0.05);
|
||||
}
|
||||
|
||||
FDMExec->Run();
|
||||
nanosleep(&short_wait,&no_wait);
|
||||
}
|
||||
|
|
|
@ -58,6 +58,8 @@ INCLUDES
|
|||
DEFINES
|
||||
*******************************************************************************/
|
||||
|
||||
using namespace std;
|
||||
|
||||
/*******************************************************************************
|
||||
CLASS DECLARATION
|
||||
*******************************************************************************/
|
||||
|
|
|
@ -191,6 +191,9 @@ void FGOutput::DelimitedOutput(void)
|
|||
cout << "Longitude,";
|
||||
cout << "QBar,";
|
||||
cout << "Alpha";
|
||||
cout << "L";
|
||||
cout << "M";
|
||||
cout << "N";
|
||||
cout << endl;
|
||||
FirstPass = false;
|
||||
} else {
|
||||
|
@ -216,7 +219,10 @@ void FGOutput::DelimitedOutput(void)
|
|||
cout << State->Getlatitude() << ",";
|
||||
cout << State->Getlongitude() << ",";
|
||||
cout << State->Getqbar() << ",";
|
||||
cout << Translation->Getalpha() << "";
|
||||
cout << Translation->Getalpha() << ",";
|
||||
cout << Aircraft->GetL() << ",";
|
||||
cout << Aircraft->GetM() << ",";
|
||||
cout << Aircraft->GetN() << "";
|
||||
cout << endl;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -40,6 +40,8 @@ INCLUDES
|
|||
|
||||
#include "FGModel.h"
|
||||
|
||||
using namespace std;
|
||||
|
||||
/*******************************************************************************
|
||||
CLASS DECLARATION
|
||||
*******************************************************************************/
|
||||
|
|
|
@ -43,6 +43,8 @@ INCLUDES
|
|||
*******************************************************************************/
|
||||
#include "FGModel.h"
|
||||
|
||||
using namespace std;
|
||||
|
||||
/*******************************************************************************
|
||||
CLASS DECLARATION
|
||||
*******************************************************************************/
|
||||
|
|
|
@ -69,6 +69,8 @@ INCLUDES
|
|||
|
||||
#include "FGModel.h"
|
||||
|
||||
using namespace std;
|
||||
|
||||
/*******************************************************************************
|
||||
CLASS DECLARATION
|
||||
*******************************************************************************/
|
||||
|
|
|
@ -64,6 +64,8 @@ INCLUDES
|
|||
DEFINES
|
||||
*******************************************************************************/
|
||||
|
||||
using namespace std;
|
||||
|
||||
/*******************************************************************************
|
||||
CLASS DECLARATION
|
||||
*******************************************************************************/
|
||||
|
|
|
@ -60,6 +60,8 @@ INCLUDES
|
|||
/*******************************************************************************
|
||||
DEFINES
|
||||
*******************************************************************************/
|
||||
|
||||
using namespace std;
|
||||
|
||||
/*******************************************************************************
|
||||
CLASS DECLARATION
|
||||
|
|
|
@ -69,6 +69,8 @@ INCLUDES
|
|||
|
||||
#include "FGModel.h"
|
||||
|
||||
using namespace std;
|
||||
|
||||
/*******************************************************************************
|
||||
CLASS DECLARATION
|
||||
*******************************************************************************/
|
||||
|
|
|
@ -42,6 +42,8 @@ INCLUDES
|
|||
DEFINES
|
||||
*******************************************************************************/
|
||||
|
||||
using namespace std;
|
||||
|
||||
/*******************************************************************************
|
||||
CLASS DECLARATION
|
||||
*******************************************************************************/
|
||||
|
|
Loading…
Reference in a new issue