1 // dme.cxx - distance-measuring equipment.
2 // Written by David Megginson, started 2003.
3 //
4 // This file is in the Public Domain and comes with no warranty.
5 
6 #ifdef HAVE_CONFIG_H
7 #  include <config.h>
8 #endif
9 
10 #include <simgear/compiler.h>
11 #include <simgear/sg_inlines.h>
12 #include <simgear/math/sg_geodesy.hxx>
13 #include <simgear/math/sg_random.h>
14 #include <simgear/sound/sample_group.hxx>
15 
16 #include <Main/fg_props.hxx>
17 #include <Navaids/navlist.hxx>
18 #include <Sound/audioident.hxx>
19 
20 #include "dme.hxx"
21 
22 #include <cstdio>
23 
24 
25 /**
26  * Adjust the range.
27  *
28  * Start by calculating the radar horizon based on the elevation
29  * difference, then clamp to the maximum, then add a fudge for
30  * borderline reception.
31  */
32 static double
adjust_range(double transmitter_elevation_ft,double aircraft_altitude_ft,double max_range_nm)33 adjust_range (double transmitter_elevation_ft, double aircraft_altitude_ft,
34               double max_range_nm)
35 {
36     double delta_elevation_ft =
37         fabs(aircraft_altitude_ft - transmitter_elevation_ft);
38     double range_nm = 1.23 * sqrt(delta_elevation_ft);
39     if (range_nm > max_range_nm)
40         range_nm = max_range_nm;
41     else if (range_nm < 20.0)
42         range_nm = 20.0;
43     double rand = sg_random();
44     return range_nm + (range_nm * rand * rand);
45 }
46 
47 namespace {
48 
49   class DMEFilter : public FGNavList::TypeFilter
50   {
51   public:
DMEFilter()52     DMEFilter() :
53       TypeFilter(FGPositioned::DME),
54       _locEnabled(fgGetBool("/sim/realism/dme-fallback-to-loc", true))
55     {
56       if (_locEnabled) {
57         _mintype = FGPositioned::ILS;
58       }
59     }
60 
pass(FGPositioned * pos) const61     virtual bool pass(FGPositioned* pos) const
62     {
63       switch (pos->type()) {
64       case FGPositioned::DME: return true;
65       case FGPositioned::ILS:
66       case FGPositioned::LOC: return _locEnabled;
67       default: return false;
68       }
69     }
70 
71   private:
72     const bool _locEnabled;
73   };
74 
75 } // of anonymous namespace
76 
77 
DME(SGPropertyNode * node)78 DME::DME ( SGPropertyNode *node )
79     : _last_distance_nm(0),
80       _last_frequency_mhz(-1),
81       _time_before_search_sec(0),
82       _navrecord(NULL),
83       _audioIdent(NULL)
84 {
85     readConfig(node, "dme");
86 }
87 
~DME()88 DME::~DME ()
89 {
90     delete _audioIdent;
91 }
92 
93 void
init()94 DME::init ()
95 {
96     std::string branch = nodePath();
97     SGPropertyNode *node = fgGetNode(branch, true );
98     initServicePowerProperties(node);
99 
100     SGPropertyNode *fnode = node->getChild("frequencies", 0, true);
101     _source_node = fnode->getChild("source", 0, true);
102     _frequency_node = fnode->getChild("selected-mhz", 0, true);
103     _in_range_node = node->getChild("in-range", 0, true);
104     _distance_node = node->getChild("indicated-distance-nm", 0, true);
105     _speed_node = node->getChild("indicated-ground-speed-kt", 0, true);
106     _time_node = node->getChild("indicated-time-min", 0, true);
107 
108     double d = node->getDoubleValue( "volume", 1.0 );
109     _volume_node = node->getChild("volume", 0, true);
110     _volume_node->setDoubleValue( d );
111 
112     bool b = node->getBoolValue( "ident", false );
113     _ident_btn_node = node->getChild("ident", 0, true);
114     _ident_btn_node->setBoolValue( b );
115 
116     SGPropertyNode *subnode = node->getChild("KDI572-574", 0, true);
117 
118     _distance_string = subnode->getChild("nm",0, true);
119     _distance_string->setStringValue("---");
120     _speed_string = subnode->getChild("kt", 0, true);
121     _speed_string->setStringValue("---");
122     _time_string = subnode->getChild("min",0, true);
123     _time_string->setStringValue("--");
124 
125     std::ostringstream temp;
126     temp << name() << "-ident-" << number();
127     if( NULL == _audioIdent )
128         _audioIdent = new DMEAudioIdent(temp.str());
129     _audioIdent->init();
130 
131     reinit();
132 }
133 
134 void
reinit()135 DME::reinit ()
136 {
137     _time_before_search_sec = 0;
138 	clear();
139 }
140 
141 void
update(double delta_time_sec)142 DME::update (double delta_time_sec)
143 {
144     if( delta_time_sec < SGLimitsd::min() )
145         return;  //paused
146     char tmp[16];
147 
148     // Figure out the source
149     const char * source = _source_node->getStringValue();
150     if (source[0] == '\0') {
151         std::string branch;
152         branch = "/instrumentation/" + name() + "/frequencies/selected-mhz";
153         _source_node->setStringValue(branch.c_str());
154         source = _source_node->getStringValue();
155     }
156                                 // Get the frequency
157 
158     double frequency_mhz = fgGetDouble(source, 108.0);
159     if (frequency_mhz != _last_frequency_mhz) {
160         _time_before_search_sec = 0;
161         _last_frequency_mhz = frequency_mhz;
162     }
163     _frequency_node->setDoubleValue(frequency_mhz);
164 
165                                 // Get the aircraft position
166     // On timeout, scan again
167     _time_before_search_sec -= delta_time_sec;
168     if (_time_before_search_sec < 0) {
169         _time_before_search_sec = 1.0;
170 
171       SGGeod pos(globals->get_aircraft_position());
172       DMEFilter filter;
173       _navrecord = FGNavList::findByFreq(frequency_mhz, pos, &filter);
174     }
175 
176     // If it's off, don't bother.
177     if (!isServiceableAndPowered()) {
178         clear();
179         return;
180     }
181 
182     // If it's on, but invalid source,don't bother.
183 	if (nullptr == _navrecord) {
184 		clear();
185         return;
186     }
187 
188     // Calculate the distance to the transmitter
189     double distance_nm = dist(_navrecord->cart(),
190                   globals->get_aircraft_position_cart()) * SG_METER_TO_NM;
191 
192     double range_nm = adjust_range(_navrecord->get_elev_ft(),
193                                    globals->get_aircraft_position().getElevationFt(),
194                                    _navrecord->get_range());
195 
196     if (distance_nm <= range_nm) {
197         double volume = _volume_node->getDoubleValue();
198         if( false == _ident_btn_node->getBoolValue() )
199             volume = 0.0;
200 
201         _audioIdent->setIdent(_navrecord->ident(), volume );
202 
203         double speed_kt = (fabs(distance_nm - _last_distance_nm) *
204                            ((1 / delta_time_sec) * 3600.0));
205         _last_distance_nm = distance_nm;
206 
207         _in_range_node->setBoolValue(true);
208         double tmp_dist = distance_nm - _navrecord->get_multiuse();
209         if ( tmp_dist < 0.0 ) {
210             tmp_dist = 0.0;
211         }
212         _distance_node->setDoubleValue( tmp_dist );
213         if ( tmp_dist >389 ) tmp_dist = 389;
214         if ( tmp_dist >= 100.0) {
215             snprintf ( tmp,16,"%3.0f",tmp_dist);
216         } else {
217             snprintf ( tmp,16,"%2.1f",tmp_dist);
218         }
219         _distance_string->setStringValue(tmp);
220 
221         _speed_node->setDoubleValue(speed_kt);
222         double spd = speed_kt;
223         if(spd>999) spd=999;
224         snprintf ( tmp,16,"%3.0f",spd);
225         _speed_string->setStringValue(tmp);
226 
227 
228         if (SGLimitsd::min() < fabs(speed_kt)){
229             double tm = distance_nm/speed_kt*60.0;
230             _time_node->setDoubleValue(tm);
231             if (tm >99) tm= 99;
232             snprintf ( tmp,16,"%2.0f",tm);
233             _time_string->setStringValue(tmp);
234         }
235 
236     } else {
237 		clear();
238     }
239 
240     _audioIdent->update( delta_time_sec );
241 }
242 
clear()243 void DME::clear()
244 {
245 	_last_distance_nm = 0;
246 	_in_range_node->setBoolValue(false);
247 	_distance_node->setDoubleValue(0);
248 	_distance_string->setStringValue("---");
249 	_speed_node->setDoubleValue(0);
250 	_speed_string->setStringValue("---");
251 	_time_node->setDoubleValue(0);
252 	_time_string->setStringValue("--");
253 	_audioIdent->setIdent("", 0.0);
254 }
255 
256 // Register the subsystem.
257 #if 0
258 SGSubsystemMgr::InstancedRegistrant<DME> registrantDME(
259     SGSubsystemMgr::FDM,
260     {{"instrumentation", SGSubsystemMgr::Dependency::HARD}},
261     1.0);
262 #endif
263 
264 // end of dme.cxx
265