1 // transponder.cxx -- class to impliment a transponder
3 // Written by Roy Vegard Ovesen, started September 2004.
5 // Copyright (C) 2004 Roy Vegard Ovesen - rvovesen@tiscali.no
6 // Copyright (C) 2013 Clement de l'Hamaide - clemaez@hotmail.fr
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.
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.
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.
22 // Example invocation, in the instrumentation.xml file:
24 // <name>encoder</name>
26 // <mode>0</mode> // Mode A = 0, Mode C = 1, Mode S = 2
33 #include "transponder.hxx"
35 #include <simgear/compiler.h>
36 #include <simgear/sg_inlines.h>
37 #include <simgear/debug/logstream.hxx>
39 #include <Main/fg_props.hxx>
45 const double IDENT_TIMEOUT = 18.0; // 18 seconds
46 const int INVALID_ALTITUDE = -9999;
47 const int INVALID_ID = -9999;
49 Transponder::Transponder(SGPropertyNode *node)
52 _name(node->getStringValue("name", "transponder")),
53 _num(node->getIntValue("number", 0)),
54 _mode((Mode) node->getIntValue("mode", 1)),
57 _requiredBusVolts = node->getDoubleValue("bus-volts", 8.0);
58 _altitudeSourcePath = node->getStringValue("encoder-path", "/instrumentation/altimeter");
59 _kt70Compat = node->getBoolValue("kt70-compatibility", false);
63 Transponder::~Transponder()
68 void Transponder::init()
70 SGPropertyNode *node = fgGetNode("/instrumentation/" + _name, _num, true );
73 _busPower_node = fgGetNode("/systems/electrical/outputs/transponder", true);
74 _pressureAltitude_node = fgGetNode(_altitudeSourcePath, true);
76 SGPropertyNode *in_node = node->getChild("inputs", 0, true);
77 for (int i=0; i<4;++i) {
78 _digit_node[i] = in_node->getChild("digit", i, true);
79 _digit_node[i]->addChangeListener(this);
82 _knob_node = in_node->getChild("knob-mode", 0, true);
83 if (!_knob_node->hasValue()) {
85 // default to, if aircraft wants to start dark, it can do this
87 _knob_node->setIntValue(_knob);
89 _knob = static_cast<KnobPosition>(_knob_node->getIntValue());
92 _knob_node->addChangeListener(this);
94 _mode_node = in_node->getChild("mode", 0, true);
95 _mode_node->setIntValue(_mode);
96 _mode_node->addChangeListener(this);
98 _identBtn_node = in_node->getChild("ident-btn", 0, true);
99 _identBtn_node->setBoolValue(false);
100 _identBtn_node->addChangeListener(this);
102 _serviceable_node = node->getChild("serviceable", 0, true);
103 _serviceable_node->setBoolValue(true);
105 _idCode_node = node->getChild("id-code", 0, true);
106 _idCode_node->addChangeListener(this);
107 // set default, but don't overwrite value from preferences.xml or -set.xml
108 if (!_idCode_node->hasValue()) {
109 _idCode_node->setIntValue(1200);
113 _altitude_node = node->getChild("altitude", 0, true);
114 _altitudeValid_node = node->getChild("altitude-valid", 0, true);
115 _ident_node = node->getChild("ident", 0, true);
116 _transmittedId_node = node->getChild("transmitted-id", 0, true);
119 // alias the properties through
120 SGPropertyNode_ptr output = node->getChild("outputs", 0, true);
121 output->getChild("flight-level", 0, true)->alias(_altitude_node);
122 output->getChild("id-code", 0, true)->alias(_idCode_node);
123 in_node->getChild("func-knob", 0, true)->alias(_knob_node);
127 void Transponder::bind()
130 SGPropertyNode *node = fgGetNode("/instrumentation/" + _name, _num, true );
131 _tiedProperties.setRoot(node);
133 _tiedProperties.Tie("annunciators/fl", this,
134 &Transponder::getFLAnnunciator );
135 _tiedProperties.Tie("annunciators/alt", this,
136 &Transponder::getAltAnnunciator );
137 _tiedProperties.Tie("annunciators/gnd", this,
138 &Transponder::getGroundAnnuciator );
139 _tiedProperties.Tie("annunciators/on", this,
140 &Transponder::getOnAnnunciator );
141 _tiedProperties.Tie("annunciators/sby", this,
142 &Transponder::getStandbyAnnunciator );
143 _tiedProperties.Tie("annunciators/reply", this,
144 &Transponder::getReplyAnnunciator );
145 } // of kt70 backwards compatibility
148 void Transponder::unbind()
150 _tiedProperties.Untie();
154 void Transponder::update(double dt)
156 if (has_power() && _serviceable_node->getBoolValue())
158 // Mode C & S send also altitude
159 Mode effectiveMode = (_knob == KNOB_ALT) ? _mode : MODE_A;
160 SGPropertyNode* altitudeSource = NULL;
162 switch (effectiveMode) {
164 altitudeSource = _pressureAltitude_node->getChild("mode-c-alt-ft");
167 altitudeSource = _pressureAltitude_node->getChild("mode-s-alt-ft");
173 int alt = INVALID_ALTITUDE;
174 if (effectiveMode != MODE_A) {
175 if (altitudeSource) {
176 alt = altitudeSource->getIntValue();
178 // warn if altitude input is misconfigured
179 SG_LOG(SG_INSTR, SG_INFO, "transponder altitude input for mode " << _mode << " is missing");
183 _altitude_node->setIntValue(alt);
184 _altitudeValid_node->setBoolValue(alt != INVALID_ALTITUDE);
188 if ( _identTime > IDENT_TIMEOUT ) {
190 _ident_node->setBoolValue(false);
195 if (_knob >= KNOB_ON) {
196 _transmittedId_node->setIntValue(_idCode_node->getIntValue());
198 _transmittedId_node->setIntValue(INVALID_ID);
203 _altitude_node->setIntValue(INVALID_ALTITUDE);
204 _altitudeValid_node->setBoolValue(false);
205 _ident_node->setBoolValue(false);
206 _transmittedId_node->setIntValue(INVALID_ID);
210 static int powersOf10[4] = {1, 10, 100, 1000};
212 static int extractCodeDigit(int code, int index)
214 return (code / powersOf10[index]) % 10;
217 static int modifyCodeDigit(int code, int index, int digitValue)
219 assert(digitValue >= 0 && digitValue < 8);
220 int p = powersOf10[index];
221 int codeWithoutDigit = code - (extractCodeDigit(code, index) * p);
222 return codeWithoutDigit + (digitValue * p);
225 void Transponder::valueChanged(SGPropertyNode *prop)
227 // Ident button pressed
228 if (prop == _identBtn_node) {
229 if (prop->getBoolValue()) {
231 _ident_node->setBoolValue(true);
234 // don't cancel state on release
239 if (prop == _mode_node) {
240 _mode = static_cast<Mode>(prop->getIntValue());
244 if (prop == _knob_node) {
245 _knob = static_cast<KnobPosition>(prop->getIntValue());
249 if (_listener_active)
254 if (prop == _idCode_node) {
255 // keep the digits in-sync
256 for (int i=0; i<4; ++i) {
257 _digit_node[i]->setIntValue(extractCodeDigit(prop->getIntValue(), i));
261 int index = prop->getIndex();
262 int digitValue = prop->getIntValue();
263 SG_CLAMP_RANGE<int>(digitValue, 0, 7);
264 _idCode_node->setIntValue(modifyCodeDigit(_idCode_node->getIntValue(), index, digitValue));
265 prop->setIntValue(digitValue);
271 bool Transponder::has_power() const
273 return (_knob_node->getIntValue() > KNOB_STANDBY) && (_busPower_node->getDoubleValue() > _requiredBusVolts);
276 bool Transponder::getFLAnnunciator() const
278 return (_knob == KNOB_ALT) || (_knob == KNOB_GROUND) || (_knob == KNOB_TEST);
281 bool Transponder::getAltAnnunciator() const
283 return (_knob == KNOB_ALT) || (_knob == KNOB_TEST);
286 bool Transponder::getGroundAnnuciator() const
288 return (_knob == KNOB_GROUND) || (_knob == KNOB_TEST);
291 bool Transponder::getOnAnnunciator() const
293 return (_knob == KNOB_ON) || (_knob == KNOB_TEST);
296 bool Transponder::getStandbyAnnunciator() const
298 return (_knob == KNOB_STANDBY) || (_knob == KNOB_TEST);
301 bool Transponder::getReplyAnnunciator() const
303 return _identMode || (_knob == KNOB_TEST);