JSBSim Flight Dynamics Model  1.1.11 (13 Feb 2022)
An Open Source Flight Dynamics and Control Software Library in C++
FGAngles.cpp
1 /*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2 
3  Module: FGAngles.cpp
4  Author: Jon S. Berndt
5  Date started: 6/2013
6 
7  ------------- Copyright (C) 2013 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 FUNCTIONAL DESCRIPTION
27 --------------------------------------------------------------------------------
28 
29 HISTORY
30 --------------------------------------------------------------------------------
31 Created: 6/2013 Jon S. Berndt
32 
33 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
34 COMMENTS, REFERENCES, and NOTES
35 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
36 
37  The Included Angle to Heading algorithm is used to find the smallest included
38  angle (the angle less than or equal to 180 degrees) to a specified heading
39  from the current heading. The sense of the rotation to get to that angle is
40  also calculated (positive 1 for a clockwise rotation, negative 1 for counter-
41  clockwise).
42 
43  The angle to the heading is calculated as follows:
44 
45  Given an angle phi:
46 
47  V = cos(phi)i + sin(phi)j (this is a unit vector)
48 
49  The dot product for two, 2D vectors is written:
50 
51  V1*V2 = |V1||V2|cos(phi)
52 
53  Since the magnitude of a unit vector is 1, we can write the equation as
54  follows:
55 
56  V1*V2 = cos(phi)
57 
58  or,
59 
60  phi = acos(V1*V2)
61 
62  or,
63 
64  phi = acos[ cos(phi1)cos(phi2) + sin(phi1)sin(phi2) ]
65 
66 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
67 INCLUDES
68 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
69 
70 #include "FGAngles.h"
71 #include "input_output/FGXMLElement.h"
72 
73 using namespace std;
74 
75 namespace JSBSim {
76 
77 /*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
78 CLASS IMPLEMENTATION
79 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
80 
81 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
82 
83 FGAngles::FGAngles(FGFCS* fcs, Element* element) : FGFCSComponent(fcs, element)
84 {
85  source_angle = 0.0;
86  target_angle = 0.0;
87  source_angle_unit = 1.0;
88  target_angle_unit = 1.0;
89  output_unit = 1.0;
90 
91  if (element->FindElement("target_angle") ) {
92  target_angle_pNode = PropertyManager->GetNode(element->FindElementValue("target_angle"));
93  if (element->FindElement("target_angle")->HasAttribute("unit")) {
94  if (element->FindElement("target_angle")->GetAttributeValue("unit") == "DEG") {
95  target_angle_unit = 0.017453293;
96  }
97  }
98  } else {
99  throw("Target angle is required for component: "+Name);
100  }
101 
102  if (element->FindElement("source_angle") ) {
103  source_angle_pNode = PropertyManager->GetNode(element->FindElementValue("source_angle"));
104  if (element->FindElement("source_angle")->HasAttribute("unit")) {
105  if (element->FindElement("source_angle")->GetAttributeValue("unit") == "DEG") {
106  source_angle_unit = 0.017453293;
107  }
108  }
109  } else {
110  throw("Source latitude is required for Angles component: "+Name);
111  }
112 
113  unit = element->GetAttributeValue("unit");
114  if (!unit.empty()) {
115  if (unit == "DEG") output_unit = 180.0/M_PI;
116  else if (unit == "RAD") output_unit = 1.0;
117  else throw("Unknown unit "+unit+" in angle component, "+Name);
118  } else {
119  output_unit = 1.0; // Default is radians (1.0) if unspecified
120  }
121 
122  bind(element);
123  Debug(0);
124 }
125 
126 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
127 
128 FGAngles::~FGAngles()
129 {
130  Debug(1);
131 }
132 
133 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
134 
135 bool FGAngles::Run(void )
136 {
137  source_angle = source_angle_pNode->getDoubleValue() * source_angle_unit;
138  target_angle = target_angle_pNode->getDoubleValue() * target_angle_unit;
139 
140  double x1 = cos(source_angle);
141  double y1 = sin(source_angle);
142  double x2 = cos(target_angle);
143  double y2 = sin(target_angle);
144 
145  double x1x2_y1y2 = max(-1.0, min(x1*x2 + y1*y2, 1.0));
146  double angle_to_heading_rad = acos(x1x2_y1y2);
147  double x1y2 = x1*y2;
148  double x2y1 = x2*y1;
149 
150  if (x1y2 >= x2y1) Output = angle_to_heading_rad * output_unit;
151  else Output = -angle_to_heading_rad * output_unit;
152 
153  Clip();
154  SetOutput();
155 
156  return true;
157 }
158 
159 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
160 // The bitmasked value choices are as follows:
161 // unset: In this case (the default) JSBSim would only print
162 // out the normally expected messages, essentially echoing
163 // the config files as they are read. If the environment
164 // variable is not set, debug_lvl is set to 1 internally
165 // 0: This requests JSBSim not to output any messages
166 // whatsoever.
167 // 1: This value explicity requests the normal JSBSim
168 // startup messages
169 // 2: This value asks for a message to be printed out when
170 // a class is instantiated
171 // 4: When this value is set, a message is displayed when a
172 // FGModel object executes its Run() method
173 // 8: When this value is set, various runtime state variables
174 // are printed out periodically
175 // 16: When set various parameters are sanity checked and
176 // a message is printed out when they go out of bounds
177 
178 void FGAngles::Debug(int from)
179 {
180  if (debug_lvl <= 0) return;
181 
182  if (debug_lvl & 1) { // Standard console startup message output
183  if (from == 0) { // Constructor
184  }
185  }
186  if (debug_lvl & 2 ) { // Instantiation/Destruction notification
187  if (from == 0) cout << "Instantiated: FGAngles" << endl;
188  if (from == 1) cout << "Destroyed: FGAngles" << endl;
189  }
190  if (debug_lvl & 4 ) { // Run() method entry print for FGModel-derived objects
191  }
192  if (debug_lvl & 8 ) { // Runtime state variables
193  }
194  if (debug_lvl & 16) { // Sanity checking
195  }
196  if (debug_lvl & 64) {
197  if (from == 0) { // Constructor
198  }
199  }
200 }
201 }