JSBSim Flight Dynamics Model  1.1.11 (13 Feb 2022)
An Open Source Flight Dynamics and Control Software Library in C++
FGPID.cpp
1 /*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2 
3  Module: FGPID.cpp
4  Author: Jon S. Berndt
5  Date started: 6/17/2006
6 
7  ------------- Copyright (C) 2006 Jon S. Berndt (jon@jsbsim.org) -------------
8 
9  This program is free software; you can redistribute it and/or modify it under
10  the terms of the GNU Lesser General Public License as published by the Free
11  Software Foundation; either version 2 of the License, or (at your option) any
12  later version.
13 
14  This program is distributed in the hope that it will be useful, but WITHOUT
15  ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
16  FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
17  details.
18 
19  You should have received a copy of the GNU Lesser General Public License along
20  with this program; if not, write to the Free Software Foundation, Inc., 59
21  Temple Place - Suite 330, Boston, MA 02111-1307, USA.
22 
23  Further information about the GNU Lesser General Public License can also be
24  found on the world wide web at http://www.gnu.org.
25 
26 HISTORY
27 --------------------------------------------------------------------------------
28 Initial code 6/17/2006 JSB
29 
30 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
31 COMMENTS, REFERENCES, and NOTES
32 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
33 
34 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
35 INCLUDES
36 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
37 
38 #include "FGPID.h"
39 #include "math/FGParameterValue.h"
40 
41 using namespace std;
42 
43 namespace JSBSim {
44 
45 /*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
46 CLASS IMPLEMENTATION
47 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
48 
49 FGPID::FGPID(FGFCS* fcs, Element* element) : FGFCSComponent(fcs, element)
50 {
51  Element *el;
52 
53  I_out_total = 0.0;
54  Input_prev = Input_prev2 = 0.0;
55  Trigger = nullptr;
56  ProcessVariableDot = nullptr;
57  IsStandard = false;
58  IntType = eNone; // No integrator initially defined.
59 
60  CheckInputNodes(1, 1, element);
61 
62  string pid_type = element->GetAttributeValue("type");
63 
64  if (pid_type == "standard") IsStandard = true;
65 
66  el = element->FindElement("kp");
67  if (el)
68  Kp = new FGParameterValue(el, PropertyManager);
69  else
70  Kp = new FGRealValue(0.0);
71 
72  el = element->FindElement("ki");
73  if (el) {
74  string integ_type = el->GetAttributeValue("type");
75  if (integ_type == "rect") { // Use rectangular integration
76  IntType = eRectEuler;
77  } else if (integ_type == "trap") { // Use trapezoidal integration
78  IntType = eTrapezoidal;
79  } else if (integ_type == "ab2") { // Use Adams Bashforth 2nd order integration
80  IntType = eAdamsBashforth2;
81  } else if (integ_type == "ab3") { // Use Adams Bashforth 3rd order integration
82  IntType = eAdamsBashforth3;
83  } else { // Use default Adams Bashforth 2nd order integration
84  IntType = eAdamsBashforth2;
85  }
86 
87  Ki = new FGParameterValue(el, PropertyManager);
88  }
89  else
90  Ki = new FGRealValue(0.0);
91 
92 
93  el = element->FindElement("kd");
94  if (el)
95  Kd = new FGParameterValue(el, PropertyManager);
96  else
97  Kd = new FGRealValue(0.0);
98 
99  el = element->FindElement("pvdot");
100  if (el)
101  ProcessVariableDot = new FGPropertyValue(el->GetDataLine(), PropertyManager);
102 
103  el = element->FindElement("trigger");
104  if (el)
105  Trigger = new FGPropertyValue(el->GetDataLine(), PropertyManager);
106 
107  bind(el);
108 }
109 
110 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
111 
112 void FGPID::bind(Element *el)
113 {
114  FGFCSComponent::bind(el);
115 
116  string tmp;
117  if (Name.find("/") == string::npos) {
118  tmp = "fcs/" + PropertyManager->mkPropertyName(Name, true);
119  } else {
120  tmp = Name;
121  }
122  typedef double (FGPID::*PMF)(void) const;
123  PropertyManager->Tie(tmp+"/initial-integrator-value", this, (PMF)nullptr,
124  &FGPID::SetInitialOutput);
125 
126  Debug(0);
127 }
128 
129 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
130 
131 FGPID::~FGPID()
132 {
133  delete Kp;
134  delete Ki;
135  delete Kd;
136  delete Trigger;
137  delete ProcessVariableDot;
138  Debug(1);
139 }
140 
141 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
142 
143 void FGPID::ResetPastStates(void)
144 {
145  FGFCSComponent::ResetPastStates();
146 
147  Input_prev = Input_prev2 = Output = I_out_total = 0.0;
148 }
149 
150 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
151 
152 bool FGPID::Run(void )
153 {
154  double I_out_delta = 0.0;
155  double Dval = 0;
156 
157  Input = InputNodes[0]->getDoubleValue();
158 
159  if (ProcessVariableDot) {
160  Dval = ProcessVariableDot->getDoubleValue();
161  } else {
162  Dval = (Input - Input_prev)/dt;
163  }
164 
165  // Do not continue to integrate the input to the integrator if a wind-up
166  // condition is sensed - that is, if the property pointed to by the trigger
167  // element is non-zero. Reset the integrator to 0.0 if the Trigger value
168  // is negative.
169 
170  double test = 0.0;
171  if (Trigger) test = Trigger->getDoubleValue();
172 
173  if (fabs(test) < 0.000001) {
174  switch(IntType) {
175  case eRectEuler:
176  I_out_delta = Input; // Normal rectangular integrator
177  break;
178  case eTrapezoidal:
179  I_out_delta = 0.5 * (Input + Input_prev); // Trapezoidal integrator
180  break;
181  case eAdamsBashforth2:
182  I_out_delta = 1.5*Input - 0.5*Input_prev; // 2nd order Adams Bashforth integrator
183  break;
184  case eAdamsBashforth3: // 3rd order Adams Bashforth integrator
185  I_out_delta = (23.0*Input - 16.0*Input_prev + 5.0*Input_prev2) / 12.0;
186  break;
187  case eNone:
188  // No integrator is defined or used.
189  I_out_delta = 0.0;
190  break;
191  }
192  }
193 
194  if (test < 0.0) I_out_total = 0.0; // Reset integrator to 0.0
195 
196  I_out_total += Ki->GetValue() * dt * I_out_delta;
197 
198  if (IsStandard)
199  Output = Kp->GetValue() * (Input + I_out_total + Kd->GetValue()*Dval);
200  else
201  Output = Kp->GetValue()*Input + I_out_total + Kd->GetValue()*Dval;
202 
203  Input_prev2 = test < 0.0 ? 0.0:Input_prev;
204  Input_prev = Input;
205 
206  Clip();
207  SetOutput();
208 
209  return true;
210 }
211 
212 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
213 // The bitmasked value choices are as follows:
214 // unset: In this case (the default) JSBSim would only print
215 // out the normally expected messages, essentially echoing
216 // the config files as they are read. If the environment
217 // variable is not set, debug_lvl is set to 1 internally
218 // 0: This requests JSBSim not to output any messages
219 // whatsoever.
220 // 1: This value explicity requests the normal JSBSim
221 // startup messages
222 // 2: This value asks for a message to be printed out when
223 // a class is instantiated
224 // 4: When this value is set, a message is displayed when a
225 // FGModel object executes its Run() method
226 // 8: When this value is set, various runtime state variables
227 // are printed out periodically
228 // 16: When set various parameters are sanity checked and
229 // a message is printed out when they go out of bounds
230 
231 void FGPID::Debug(int from)
232 {
233  if (debug_lvl <= 0) return;
234 
235  if (debug_lvl & 1) { // Standard console startup message output
236  if (from == 0) { // Constructor
237  cout << " INPUT: " << InputNodes[0]->GetNameWithSign() << endl;
238 
239  for (auto node: OutputNodes)
240  cout << " OUTPUT: " << node->getNameString() << endl;
241  }
242  }
243  if (debug_lvl & 2 ) { // Instantiation/Destruction notification
244  if (from == 0) cout << "Instantiated: FGPID" << endl;
245  if (from == 1) cout << "Destroyed: FGPID" << endl;
246  }
247  if (debug_lvl & 4 ) { // Run() method entry print for FGModel-derived objects
248  }
249  if (debug_lvl & 8 ) { // Runtime state variables
250  }
251  if (debug_lvl & 16) { // Sanity checking
252  }
253  if (debug_lvl & 64) {
254  if (from == 0) { // Constructor
255  }
256  }
257 }
258 }
JSBSim::FGPropertyManager::mkPropertyName
std::string mkPropertyName(std::string name, bool lowercase)
Property-ify a name replaces spaces with '-' and, optionally, makes name all lower case.
Definition: FGPropertyManager.cpp:64
JSBSim::FGPropertyManager::Tie
void Tie(const std::string &name, T *pointer)
Tie a property to an external variable.
Definition: FGPropertyManager.h:449