FlightGear next
transponder.cxx
Go to the documentation of this file.
1// transponder.cxx -- class to implement a transponder
2//
3// Written by Roy Vegard Ovesen, started September 2004.
4//
5// Copyright (C) 2004 Roy Vegard Ovesen - rvovesen@tiscali.no
6// Copyright (C) 2013 Clement de l'Hamaide - clemaez@hotmail.fr
7//
8// This program is free software; you can redistribute it and/or
9// modify it under the terms of the GNU General Public License as
10// published by the Free Software Foundation; either version 2 of the
11// License, or (at your option) any later version.
12//
13// This program is distributed in the hope that it will be useful, but
14// WITHOUT ANY WARRANTY; without even the implied warranty of
15// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16// General Public License for more details.
17//
18// You should have received a copy of the GNU General Public License
19// along with this program; if not, write to the Free Software
20// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
21//
22// Example invocation, in the instrumentation.xml file:
23// <transponder>
24// <name>encoder</name>
25// <number>0</number>
26// <mode>2</mode> // Mode A = 0, Mode C = 1, Mode S = 2
27// <auto-ground>...</auto-ground>
28// <airspeed-path>...</airspeed-path>
29// <mach-path>...</mach-path>
30// </altimeter>
31//
32// Mode-S transponders (configured with mode = 2) can transmit a ground bit to
33// indicate ground operation. If auto-ground is not defined, the ground bit is
34// switched manually by setting the transponder knob to GND. If auto-ground is
35// defined, the transponder simulates automatic switching of the ground bit
36// using the value of the property defined in auto-ground.
37//
38// For a squat switch, use "/gear/gear/wow". For automatic switching based on
39// airspeed, use a property rule to make a new boolean property that indicates,
40// for example, airspeed < 80, and reference your new property in auto-ground.
41//
42// Note that Mode-A and Mode-C transponders do not transmit a ground bit, even
43// if the transponder knob is set to the GND position.
44//
45// Mode-S transponders also transmit indicated airspeed and Mach number. The
46// default sources are "/instrumentation/airspeed-indicator/indicated-speed-kt"
47// and ".../indicated-mach", but this can be changed by setting "airspeed-path"
48// and "mach-path" properties respectively as shown above.
49
50#include <config.h>
51
52#include "transponder.hxx"
53
54#include <simgear/compiler.h>
55#include <simgear/sg_inlines.h>
56#include <simgear/debug/logstream.hxx>
57
58#include <Main/fg_props.hxx>
59
60#include <cstdio>
61
62using std::string;
63
64const double IDENT_TIMEOUT = 18.0; // 18 seconds
65const int INVALID_ALTITUDE = -9999;
66const int INVALID_AIRSPEED = -9999;
67const float INVALID_MACH_NUM = -1.0;
68const int INVALID_ID = -9999;
69
70Transponder::Transponder(SGPropertyNode *node) :
71 _mode((Mode) node->getIntValue("mode", 1))
72{
73 readConfig(node, "transponder");
74
75 // ensure older power-supply name works
76 if (node->hasChild("bus-volts")) {
77 SG_LOG(SG_INSTR, SG_DEV_WARN, "Transponder: use new supply-volatge prop: 'minimum-supply-volts' instead of 'bus-volts'");
78 setMinimumSupplyVolts(node->getDoubleValue("bus-volts", 8.0));
79 }
80
81 _altitudeSourcePath = node->getStringValue("encoder-path", "/instrumentation/altimeter");
82 _autoGroundPath = node->getStringValue("auto-ground");
83 _airspeedSourcePath = node->getStringValue("airspeed-path", "/instrumentation/airspeed-indicator/indicated-speed-kt");
84 _machSourcePath = node->getStringValue("mach-path", "/instrumentation/airspeed-indicator/indicated-mach");
85 _kt70Compat = node->getBoolValue("kt70-compatibility", false);
86}
87
88
92
93
95{
96 SGPropertyNode *node = fgGetNode(nodePath(), true);
98
99 // Inputs
100 _pressureAltitude_node = fgGetNode(_altitudeSourcePath, true);
101 _autoGround_node = fgGetNode(_autoGroundPath, true);
102 _airspeedIndicator_node = fgGetNode(_airspeedSourcePath, true);
103 _machSource_node = fgGetNode(_machSourcePath, true);
104
105 SGPropertyNode *in_node = node->getChild("inputs", 0, true);
106 for (int i=0; i<4;++i) {
107 _digit_node[i] = in_node->getChild("digit", i, true);
108 _digit_node[i]->addChangeListener(this);
109 }
110
111 _knob_node = in_node->getChild("knob-mode", 0, true);
112 if (!_knob_node->hasValue()) {
113 _knob = KNOB_ON;
114 // default to, if aircraft wants to start dark, it can do this
115 // in its -set.xml
116 _knob_node->setIntValue(_knob);
117 } else {
118 _knob = static_cast<KnobPosition>(_knob_node->getIntValue());
119 }
120
121 _knob_node->addChangeListener(this);
122
123 _mode_node = in_node->getChild("mode", 0, true);
124 _mode_node->setIntValue(_mode);
125 _mode_node->addChangeListener(this);
126
127 _identBtn_node = in_node->getChild("ident-btn", 0, true);
128 _identBtn_node->setBoolValue(false);
129 _identBtn_node->addChangeListener(this);
130
131 _idCode_node = node->getChild("id-code", 0, true);
132 _idCode_node->addChangeListener(this);
133 // set default, but don't overwrite value from defaults.xml or -set.xml
134 if (!_idCode_node->hasValue()) {
135 _idCode_node->setIntValue(1200);
136 }
137
138 // Outputs
139 _altitude_node = node->getChild("altitude", 0, true);
140 _altitudeValid_node = node->getChild("altitude-valid", 0, true);
141 _ident_node = node->getChild("ident", 0, true);
142 _transmittedId_node = node->getChild("transmitted-id", 0, true);
143 _ground_node = node->getChild("ground-bit", 0, true);
144 _airspeed_node = node->getChild("airspeed-kt", 0, true);
145 _mach_node = node->getChild("mach-number", 0, true);
146
147 if (_kt70Compat) {
148 // alias the properties through
149 SGPropertyNode_ptr output = node->getChild("outputs", 0, true);
150 output->getChild("flight-level", 0, true)->alias(_altitude_node, true);
151 output->getChild("id-code", 0, true)->alias(_idCode_node, true);
152 in_node->getChild("func-knob", 0, true)->alias(_knob_node, true);
153 }
154}
155
157{
158 if (_kt70Compat) {
159 SGPropertyNode *node = fgGetNode(nodePath(), true );
160 _tiedProperties.setRoot(node);
161
162 _tiedProperties.Tie("annunciators/fl", this,
163 &Transponder::getFLAnnunciator );
164 _tiedProperties.Tie("annunciators/alt", this,
165 &Transponder::getAltAnnunciator );
166 _tiedProperties.Tie("annunciators/gnd", this,
167 &Transponder::getGroundAnnuciator );
168 _tiedProperties.Tie("annunciators/on", this,
169 &Transponder::getOnAnnunciator );
170 _tiedProperties.Tie("annunciators/sby", this,
171 &Transponder::getStandbyAnnunciator );
172 _tiedProperties.Tie("annunciators/reply", this,
173 &Transponder::getReplyAnnunciator );
174 } // of kt70 backwards compatibility
175}
176
178{
179 _tiedProperties.Untie();
181}
182
183void Transponder::update(double dt)
184{
186 {
187 // Mode C & S send also altitude
188 Mode effectiveMode = (_knob == KNOB_ALT || _knob == KNOB_GROUND) ? _mode : MODE_A;
189 SGPropertyNode* altitudeSource = NULL;
190
191 switch (effectiveMode) {
192 case MODE_C:
193 altitudeSource = _pressureAltitude_node->getChild("mode-c-alt-ft");
194 break;
195 case MODE_S:
196 altitudeSource = _pressureAltitude_node->getChild("mode-s-alt-ft");
197 break;
198 default:
199 break;
200 }
201
202 int alt = INVALID_ALTITUDE;
203 if (effectiveMode != MODE_A) {
204 if (altitudeSource) {
205 alt = altitudeSource->getIntValue();
206 } else {
207 // warn if altitude input is misconfigured
208 SG_LOG(SG_INSTR, SG_INFO, "transponder altitude input for mode " << _mode << " is missing");
209 }
210 }
211
212 _altitude_node->setIntValue(alt);
213 _altitudeValid_node->setBoolValue(alt != INVALID_ALTITUDE);
214
215 if ( _identMode ) {
216 _identTime += dt;
217 if ( _identTime > IDENT_TIMEOUT ) {
218 // reset ident mode
219 _ident_node->setBoolValue(false);
220 _identMode = false;
221 }
222 }
223
224 if (_knob >= KNOB_GROUND) {
225 _transmittedId_node->setIntValue(_idCode_node->getIntValue());
226 } else {
227 _transmittedId_node->setIntValue(INVALID_ID);
228 }
229
230 if (_mode == MODE_S && _knob >= KNOB_GROUND) {
231 if (_autoGround_node->hasValue()) {
232 // Automatic ground bit based on the auto-ground property
233 _ground_node->setBoolValue(_autoGround_node->getBoolValue());
234 } else {
235 // Manual ground bit based on the transponder knob
236 _ground_node->setBoolValue(_knob == KNOB_GROUND);
237 }
238 } else {
239 _ground_node->setBoolValue(false);
240 }
241
242 if (_mode == MODE_S && _knob >= KNOB_GROUND && _airspeedIndicator_node->hasValue()) {
243 _airspeed_node->setIntValue(_airspeedIndicator_node->getIntValue());
244 } else {
245 _airspeed_node->setIntValue(INVALID_AIRSPEED);
246 }
247
248 if (_mode == MODE_S && _machSource_node->hasValue()) {
249 _mach_node->setDoubleValue(_machSource_node->getDoubleValue());
250 } else {
251 _mach_node->setDoubleValue(INVALID_MACH_NUM);
252 }
253 }
254 else
255 { // un-powered or u/s
256 _altitude_node->setIntValue(INVALID_ALTITUDE);
257 _altitudeValid_node->setBoolValue(false);
258 _ident_node->setBoolValue(false);
259 _ground_node->setBoolValue(false);
260 _airspeed_node->setIntValue(INVALID_AIRSPEED);
261 _mach_node->setDoubleValue(INVALID_MACH_NUM);
262 _transmittedId_node->setIntValue(INVALID_ID);
263 }
264}
265
266static int powersOf10[4] = {1, 10, 100, 1000};
267
268static int extractCodeDigit(int code, int index)
269{
270 return (code / powersOf10[index]) % 10;
271}
272
273static int modifyCodeDigit(int code, int index, int digitValue)
274{
275 assert(digitValue >= 0 && digitValue < 8);
276 int p = powersOf10[index];
277 int codeWithoutDigit = code - (extractCodeDigit(code, index) * p);
278 return codeWithoutDigit + (digitValue * p);
279}
280
281void Transponder::valueChanged(SGPropertyNode *prop)
282{
283 // Ident button pressed
284 if (prop == _identBtn_node) {
285 if (prop->getBoolValue()) {
286 _identTime = 0.0;
287 _ident_node->setBoolValue(true);
288 _identMode = true;
289 } else {
290 // don't cancel state on release
291 }
292 return;
293 }
294
295 if (prop == _mode_node) {
296 _mode = static_cast<Mode>(prop->getIntValue());
297 return;
298 }
299
300 if (prop == _knob_node) {
301 _knob = static_cast<KnobPosition>(prop->getIntValue());
302 return;
303 }
304
305 if (_listener_active)
306 return;
307
308 _listener_active++;
309
310 if (prop == _idCode_node) {
311 // keep the digits in-sync
312 for (int i=0; i<4; ++i) {
313 _digit_node[i]->setIntValue(extractCodeDigit(prop->getIntValue(), i));
314 }
315 } else {
316 // digit node
317 int index = prop->getIndex();
318 int digitValue = prop->getIntValue();
319 SG_CLAMP_RANGE<int>(digitValue, 0, 7);
320 _idCode_node->setIntValue(modifyCodeDigit(_idCode_node->getIntValue(), index, digitValue));
321 prop->setIntValue(digitValue);
322 }
323
324 _listener_active--;
325}
326
327bool Transponder::getFLAnnunciator() const
328{
329 return (_knob == KNOB_ALT) || (_knob == KNOB_GROUND) || (_knob == KNOB_TEST);
330}
331
332bool Transponder::getAltAnnunciator() const
333{
334 return (_knob == KNOB_ALT) || (_knob == KNOB_TEST);
335}
336
337bool Transponder::getGroundAnnuciator() const
338{
339 return (_knob == KNOB_GROUND) || (_knob == KNOB_TEST);
340}
341
342bool Transponder::getOnAnnunciator() const
343{
344 return (_knob == KNOB_ON) || (_knob == KNOB_TEST);
345}
346
347bool Transponder::getStandbyAnnunciator() const
348{
349 return (_knob == KNOB_STANDBY) || (_knob == KNOB_TEST);
350}
351
352bool Transponder::getReplyAnnunciator() const
353{
354 return _identMode || (_knob == KNOB_TEST);
355}
356
358{
359 return (_knob_node->getIntValue() >= KNOB_STANDBY);
360}
361
362
363// Register the subsystem.
364#if 0
365SGSubsystemMgr::InstancedRegistrant<Transponder> registrantTransponder(
366 SGSubsystemMgr::FDM,
367 {{"instrumentation", SGSubsystemMgr::Dependency::HARD}},
368 0.2);
369#endif
#define p(x)
#define i(x)
void initServicePowerProperties(SGPropertyNode *node)
void setMinimumSupplyVolts(double v)
void readConfig(SGPropertyNode *config, std::string defaultName)
std::string nodePath() const
bool isServiceableAndPowered() const
void bind() override
Transponder(SGPropertyNode *node)
virtual ~Transponder()
void unbind() override
void update(double dt) override
void init() override
bool isPowerSwitchOn() const override
SGPropertyNode * fgGetNode(const char *path, bool create)
Get a property node.
Definition proptest.cpp:27
const int INVALID_ID
static int powersOf10[4]
const double IDENT_TIMEOUT
static int extractCodeDigit(int code, int index)
const float INVALID_MACH_NUM
const int INVALID_AIRSPEED
static int modifyCodeDigit(int code, int index, int digitValue)
const int INVALID_ALTITUDE