JSBSim Flight Dynamics Model 1.3.3 (16 Oct 2026)
An Open Source Flight Dynamics and Control Software Library in C++
Loading...
Searching...
No Matches
FGBrushLessDCMotor.cpp
1/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2
3 Module: FGBrushLessDCMotor.cpp
4 Autor Paolo Becchi
5 1st release 1/1/2022
6 Purpose: This module models an BLDC electric motor
7
8 ------------- Copyright (C) 2022 Paolo Becchi (pbecchi@aerobusinees.it) -------------
9
10 This program is free software; you can redistribute it and/or modify it under
11 the terms of the GNU Lesser General Public License as published by the Free
12 Software Foundation; either version 2 of the License, or (at your option) any
13 later version.
14
15 This program is distributed in the hope that it will be useful, but WITHOUT
16 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
17 FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
18 details.
19
20 You should have received a copy of the GNU Lesser General Public License along
21 with this program; if not, write to the Free Software Foundation, Inc., 59
22 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
23
24 Further information about the GNU Lesser General Public License can also be
25 found on the world wide web at http://www.gnu.org
26
27FUNCTIONAL DESCRIPTION
28--------------------------------------------------------------------------------
29Following code represent a new BrushLess DC motor to be used as alternative
30to basic electric motor.
31BLDC motor code is based on basic "3 constant motor equations"
32It require 3 basic physical motor properties:
33Kv speed motor constant [RPM/Volt]
34Rm internal coil resistance [Ohms]
35I0 no load current [Amperes]
36
37REFERENCE:
38http://web.mit.edu/drela/Public/web/qprop/motor1_theory.pdf
39
40HISTORY
41--------------------------------------------------------------------------------
421/01/2022 Created
43
44%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
45INCLUDES
46%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
47
48#include <iostream>
49#include <sstream>
50#include <math.h>
51
52#include "FGFDMExec.h"
53#include "FGBrushLessDCMotor.h"
54#include "FGPropeller.h"
55#include "input_output/FGXMLElement.h"
56
57using namespace std;
58
59namespace JSBSim {
60
61/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
62CLASS IMPLEMENTATION
63%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
64
65FGBrushLessDCMotor::FGBrushLessDCMotor(FGFDMExec* exec, Element* el, int engine_number, struct FGEngine::Inputs& input)
66 : FGEngine(engine_number, input)
67{
68 Load(exec, el);
69
70 Type = etElectric;
71
72 if (el->FindElement("maxvolts"))
73 MaxVolts = el->FindElementValueAsNumberConvertTo("maxvolts", "VOLTS");
74 else {
75 XMLLogException err(el);
76 err << "<maxvolts> is a mandatory parameter\n";
77 throw err;
78 }
79
80 if (el->FindElement("velocityconstant"))
81 Kv = el->FindElementValueAsNumber("velocityconstant");
82 else {
83 XMLLogException err(el);
84 err << "<velocityconstant> is a mandatory parameter\n";
85 throw err;
86 }
87
88 if (el->FindElement("coilresistance"))
89 CoilResistance = el->FindElementValueAsNumberConvertTo("coilresistance", "OHMS");
90 else {
91 XMLLogException err(el);
92 err << "<coilresistance> is a mandatory parameter\n";
93 throw err;
94 }
95 if (el->FindElement("noloadcurrent"))
96 ZeroTorqueCurrent = el->FindElementValueAsNumberConvertTo("noloadcurrent", "AMPERES");
97 else {
98 XMLLogException err(el);
99 err << "<noloadcurrent> is a mandatory parameter\n";
100 throw err;
101 }
102
103 double MaxCurrent = MaxVolts / CoilResistance + ZeroTorqueCurrent;
104
105 PowerWatts = MaxCurrent * MaxVolts;
106
107 string base_property_name = CreateIndexedPropertyName("propulsion/engine", EngineNumber);
108 auto pm = exec->GetPropertyManager();
109 pm->Tie(base_property_name + "/power-hp", &HP);
110 pm->Tie(base_property_name + "/current-amperes", &Current);
111
112 Debug(0); // Call Debug() routine from constructor if needed
113}
114
115//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
116
118{
119 Debug(1); // Call Debug() routine from constructor if needed
120}
121
122//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
123
125{
126 RunPreFunctions();
127
128 if (Thruster->GetType() == FGThruster::ttPropeller) {
129 ((FGPropeller*)Thruster)->SetAdvance(in.PropAdvance[EngineNumber]);
130 ((FGPropeller*)Thruster)->SetFeather(in.PropFeather[EngineNumber]);
131 }
132
133 double RPM = Thruster->GetRPM();
134 double V = MaxVolts * in.ThrottlePos[EngineNumber];
135
136 Current = (V - RPM / Kv) / CoilResistance; // Equation (4) from Drela's document
137
138 // Compute torque from current with Kq=1/Kv, equation (5) from Drela's
139 // document. The "zero torque current" is the current necessary for the motor
140 // to overcome its internal friction: the friction resists the rotation,
141 // whatever the sign of the current, and holds a motor at rest until the
142 // current overcomes it.
143 double FrictionCurrent;
144
145 if (RPM > 0.0)
146 FrictionCurrent = ZeroTorqueCurrent;
147 else if (RPM < 0.0)
148 FrictionCurrent = -ZeroTorqueCurrent;
149 else
150 FrictionCurrent = Constrain(-ZeroTorqueCurrent, Current, ZeroTorqueCurrent);
151
152 double Torque = (Current - FrictionCurrent) / Kv * WattperRPMtoftpound;
153
154 // EnginePower must be non zero when accelerating from RPM == 0.0
155 double EnginePower = ((2 * M_PI) * max(RPM, 0.0001) * Torque) / 60; //units [#*ft/s]
156 HP = EnginePower / hptowatts * NMtoftpound; // units[HP]
157 LoadThrusterInputs();
158 Thruster->Calculate(EnginePower);
159
160 RunPostFunctions();
161}
162
163//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
164
165string FGBrushLessDCMotor::GetEngineLabels(const string& delimiter)
166{
167 std::ostringstream buf;
168
169 buf << Name << " HP (engine " << EngineNumber << ")" << delimiter
170 << Thruster->GetThrusterLabels(EngineNumber, delimiter);
171
172 return buf.str();
173}
174
175//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
176
177string FGBrushLessDCMotor::GetEngineValues(const string& delimiter)
178{
179 std::ostringstream buf;
180
181 buf << HP << delimiter
182 << Thruster->GetThrusterValues(EngineNumber, delimiter);
183
184 return buf.str();
185}
186
187//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
188//
189// The bitmasked value choices are as follows:
190// unset: In this case (the default) JSBSim would only print
191// out the normally expected messages, essentially echoing
192// the config files as they are read. If the environment
193// variable is not set, debug_lvl is set to 1 internally
194// 0: This requests JSBSim not to output any messages
195// whatsoever.
196// 1: This value explicity requests the normal JSBSim
197// startup messages
198// 2: This value asks for a message to be printed out when
199// a class is instantiated
200// 4: When this value is set, a message is displayed when a
201// FGModel object executes its Run() method
202// 8: When this value is set, various runtime state variables
203// are printed out periodically
204// 16: When set various parameters are sanity checked and
205// a message is printed out when they go out of bounds
206
207void FGBrushLessDCMotor::Debug(int from)
208{
209 if (debug_lvl <= 0) return;
210
211 if (debug_lvl & 1) { // Standard console startup message output
212 if (from == 0) { // Constructor
213 FGLogging log(LogLevel::DEBUG);
214 log << "\n Engine Name: " << Name << "\n";
215 log << " Power Watts: " << PowerWatts << "\n";
216 log << " Speed Factor: " << Kv << "\n";
217 log << " Coil Resistance: " << CoilResistance << "\n";
218 log << " NoLoad Current: " << ZeroTorqueCurrent << "\n";
219 }
220 }
221 if (debug_lvl & 2 ) { // Instantiation/Destruction notification
222 FGLogging log(LogLevel::DEBUG);
223 if (from == 0) log << "Instantiated: FGBrushLessDCMotor\n";
224 if (from == 1) log << "Destroyed: FGBrushLessDCMotor\n";
225 }
226 if (debug_lvl & 4 ) { // Run() method entry print for FGModel-derived objects
227 }
228 if (debug_lvl & 8 ) { // Runtime state variables
229 }
230 if (debug_lvl & 16) { // Sanity checking
231 }
232 if (debug_lvl & 64) {
233 if (from == 0) { // Constructor
234 }
235 }
236}
237
238} // namespace JSBSim
Element * FindElement(const std::string &el="")
Searches for a specified element.
double FindElementValueAsNumberConvertTo(const std::string &el, const std::string &target_units)
Searches for the named element and converts and returns the data belonging to it.
double FindElementValueAsNumber(const std::string &el="")
Searches for the named element and returns the data belonging to it as a number.
void Calculate(void)
Calculates the thrust of the engine, and other engine functions.
FGBrushLessDCMotor(FGFDMExec *exec, Element *el, int engine_number, FGEngine::Inputs &input)
Constructor.
Base class for all engines.
Definition FGEngine.h:104
Encapsulates the JSBSim simulation executive.
Definition FGFDMExec.h:185
std::shared_ptr< FGPropertyManager > GetPropertyManager(void) const
Returns a pointer to the property manager object.
Definition FGFDMExec.h:422
static constexpr double Constrain(double min, double value, double max)
Constrain a value between a minimum and a maximum value.
Definition FGJSBBase.h:258
FGPropeller models a propeller given the tabular data for Ct (thrust) and Cp (power),...
Main namespace for the JSBSim Flight Dynamics Model.
Definition FGFDMExec.cpp:73
constexpr double NMtoftpound
Models an electric brushless DC motor or more appropriately permanent magnet synchronous motor.