JSBSim源代码分析

JSBSim是一个开源跨平台的飞行动力学模型(FDM)软件库,用于模拟航空航天飞行器的飞行动力学。 该库已被纳入飞行模拟软件包FlightGear和OpenEaagles。JSBSim可以独立运行,通过命令行参数指定飞行器和初始状态,进行简单情境下的飞行动力学仿真,也可以将JSBSim作为代码库,编程实现飞行器模型加载,设置输入,获得输出。下面将通过分析JSBSim源代码,研究其实现通用飞行动力学模型的方法。

入口分析

下面是JSBSim参考手册中的最简单实例,因JSBSim的不断开发,JSBSim参考手册中该编程实例有点过时,因此进行了少量修改。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
#include <FGFDMExec.h>
#include <sg_path.hxx>

using namespace std;

int main(int argc, char **argv)
{
JSBSim::FGFDMExec FDMExec;
bool result = true;

FDMExec.LoadScript(SGPath::fromUtf8(argv[1]));

while (result) result = FDMExec.Run();
}

从上述代码可知,调用JSBSim的主要方法是利用FGFDMExec类,通过实例化一个FGFDMExec类,就相当于获得了一个运行JSBSim仿真的工具箱,通过这个工具箱就可以调用JSBSim的大部分功能,实现我们要的仿真目标。同时FGFDMExec类通过加载外部飞机的XML脚本,实现飞行动力学模型的通用性。

JSBSim初始化流程

上述JSBSim最简仿真示例中已包含其初始化流程,采用图示如下:

JSBSim初始化流程

图1 JSBSim初始化流程

FGFDMExec初始化

FGFDMExec类在其构造函数中对各个模型进行初始化,具体代码在Allocate函数中:

1
2
3
4
5
6
7
8
9
10
11
12
13
FGFDMExec::FGFDMExec(FGPropertyManager* root, unsigned int* fdmctr)
: Root(root), RandomEngine(new default_random_engine), FDMctr(fdmctr)
{
...
try {
Allocate();
} catch (const string& msg ) {
cout << "Caught error: " << msg << endl;
exit(1);
}

...
}

Allocate函数代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
bool FGFDMExec::Allocate(void)
{
bool result=true;

Models.resize(eNumStandardModels);

// First build the inertial model since some other models are relying on
// the inertial model and the ground callback to build themselves.
// Note that this does not affect the order in which the models will be
// executed later.
Models[eInertial] = new FGInertial(this);

// See the eModels enum specification in the header file. The order of the
// enums specifies the order of execution. The Models[] vector is the primary
// storage array for the list of models.
Models[ePropagate] = new FGPropagate(this);
Models[eInput] = new FGInput(this);
Models[eAtmosphere] = new FGStandardAtmosphere(this);
...

// Assign the Model shortcuts for internal executive use only.
Propagate = (FGPropagate*)Models[ePropagate];
Inertial = (FGInertial*)Models[eInertial];
Atmosphere = (FGAtmosphere*)Models[eAtmosphere];
...

// Initialize planet (environment) constants
LoadPlanetConstants();

// Initialize models
for (unsigned int i = 0; i < Models.size(); i++) {
// The Input/Output models must not be initialized prior to IC loading
if (i == eInput || i == eOutput) continue;

LoadInputs(i);
Models[i]->InitModel();
}

...
return result;
}

Allocate函数代码中需要注意LoadInputs函数,该函数决定各个子模型的初始化顺序,确定各个子模型的输入输出,具体代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
void FGFDMExec::LoadInputs(unsigned int idx)
{
switch(idx) {
case ePropagate:
Propagate->in.vPQRidot = Accelerations->GetPQRidot();
Propagate->in.vUVWidot = Accelerations->GetUVWidot();
Propagate->in.DeltaT = dT;
break;
case eInput:
break;
case eInertial:
Inertial->in.Position = Propagate->GetLocation();
break;
case eAtmosphere:
Atmosphere->in.altitudeASL = Propagate->GetAltitudeASL();
break;
case eWinds:
Winds->in.AltitudeASL = Propagate->GetAltitudeASL();
Winds->in.DistanceAGL = Propagate->GetDistanceAGL();
...
break;
case eAuxiliary:
Auxiliary->in.Pressure = Atmosphere->GetPressure();
Auxiliary->in.Density = Atmosphere->GetDensity();
...
break;
case eSystems:
// Dynamic inputs come into the components that FCS manages through properties
break;
case ePropulsion:
Propulsion->in.Pressure = Atmosphere->GetPressure();
...

break;
case eAerodynamics:
Aerodynamics->in.Alpha = Auxiliary->Getalpha();
...
break;
case eGroundReactions:
// There are no external inputs to this model.
GroundReactions->in.Vground = Auxiliary->GetVground();
...
break;
case eExternalReactions:
// There are no external inputs to this model.
break;
case eBuoyantForces:
BuoyantForces->in.Density = Atmosphere->GetDensity();
...
break;
case eMassBalance:
MassBalance->in.GasInertia = BuoyantForces->GetGasMassInertia();
MassBalance->in.GasMass = BuoyantForces->GetGasMass();
...
break;
case eAircraft:
Aircraft->in.AeroForce = Aerodynamics->GetForces();
Aircraft->in.PropForce = Propulsion->GetForces();
...
break;
case eAccelerations:
Accelerations->in.J = MassBalance->GetJ();
Accelerations->in.Jinv = MassBalance->GetJinv();
...
break;
default:
break;
}
}

FGFDMExec加载飞机配置

FGFDMExec的LoadScript函数在初始化时负责加载飞机配置,用于初始化各个子模型。

1
2
3
4
5
6
7
8
9
10
bool FGFDMExec::LoadScript(const SGPath& script, double deltaT,
const SGPath& initfile)
{
bool result;

Script = new FGScript(this);
result = Script->LoadScript(GetFullPath(script), deltaT, initfile);

return result;
}

FGFDMExec运行

FGFDMExec的Run函数负责飞行动力学模型的计算,其代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
bool FGFDMExec::Run(void)
{
bool success=true;

...

// returns true if success, false if complete
if (Script != 0 && !IntegrationSuspended()) success = Script->RunScript();

for (unsigned int i = 0; i < Models.size(); i++) {
LoadInputs(i);
Models[i]->Run(holding);
}

...

return success;
}

FGFDMExec的Run函数将依次调用各个子模型的Run函数。

参考链接

  1. JSBSim编程实践之入门,by jackhuang.