]> git.mxchange.org Git - flightgear.git/blob - src/Cockpit/navcom.cxx
Begin interfacing the navcom's to the electrical model.
[flightgear.git] / src / Cockpit / navcom.cxx
1 // navcom.cxx -- class to manage a navcom instance
2 //
3 // Written by Curtis Olson, started April 2000.
4 //
5 // Copyright (C) 2000 - 2002  Curtis L. Olson - curt@flightgear.org
6 //
7 // This program is free software; you can redistribute it and/or
8 // modify it under the terms of the GNU General Public License as
9 // published by the Free Software Foundation; either version 2 of the
10 // License, or (at your option) any later version.
11 //
12 // This program is distributed in the hope that it will be useful, but
13 // WITHOUT ANY WARRANTY; without even the implied warranty of
14 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15 // General Public License for more details.
16 //
17 // You should have received a copy of the GNU General Public License
18 // along with this program; if not, write to the Free Software
19 // Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
20 //
21 // $Id$
22
23
24 #ifdef HAVE_CONFIG_H
25 #  include <config.h>
26 #endif
27
28 #include <stdio.h>      // snprintf
29
30 #include <simgear/compiler.h>
31 #include <simgear/math/sg_random.h>
32
33 #include <Aircraft/aircraft.hxx>
34 #include <Navaids/ilslist.hxx>
35 #include <Navaids/navlist.hxx>
36 #include <Time/FGEventMgr.hxx>
37
38 #include "navcom.hxx"
39
40 #include <string>
41 SG_USING_STD(string);
42
43
44 // Constructor
45 FGNavCom::FGNavCom() :
46     lon_node(fgGetNode("/position/longitude-deg", true)),
47     lat_node(fgGetNode("/position/latitude-deg", true)),
48     alt_node(fgGetNode("/position/altitude-ft", true)),
49     last_nav_id(""),
50     last_nav_vor(false),
51     nav_play_count(0),
52     nav_last_time(0),
53     need_update(true),
54     power_btn(true),
55     comm_freq(0.0),
56     comm_alt_freq(0.0),
57     comm_vol_btn(0.0),
58     nav_freq(0.0),
59     nav_alt_freq(0.0),
60     nav_radial(0.0),
61     nav_vol_btn(0.0),
62     nav_ident_btn(true)
63 {
64     SGPath path( globals->get_fg_root() );
65     SGPath term = path;
66     term.append( "Navaids/range.term" );
67     SGPath low = path;
68     low.append( "Navaids/range.low" );
69     SGPath high = path;
70     high.append( "Navaids/range.high" );
71
72     term_tbl = new SGInterpTable( term.str() );
73     low_tbl = new SGInterpTable( low.str() );
74     high_tbl = new SGInterpTable( high.str() );
75
76 }
77
78
79 // Destructor
80 FGNavCom::~FGNavCom() 
81 {
82     delete term_tbl;
83     delete low_tbl;
84     delete high_tbl;
85 }
86
87
88 void
89 FGNavCom::init ()
90 {
91     morse.init();
92
93     search();
94
95     update(0);                  // FIXME: use dt
96 }
97
98 void
99 FGNavCom::bind ()
100 {
101     char propname[256];
102
103     // we know index is valid now so lets bind to the bus property
104     // here.
105     sprintf( propname, "/systems/electrical/outputs/navcomm[%d]", index );
106     // default to true in case no electrical system defined.
107     fgSetDouble( propname, 60.0 );
108     bus_power = fgGetNode( propname, true );
109
110                                 // User inputs
111     sprintf( propname, "/radios/comm[%d]/inputs/power-btn", index );
112     fgTie( propname, this,
113            &FGNavCom::get_power_btn, &FGNavCom::set_power_btn );
114     fgSetArchivable( propname );
115
116     sprintf( propname, "/radios/comm[%d]/frequencies/selected-mhz", index );
117     fgTie( propname, this, &FGNavCom::get_comm_freq, &FGNavCom::set_comm_freq );
118     fgSetArchivable( propname );
119
120     sprintf( propname, "/radios/comm[%d]/frequencies/standby-mhz", index );
121     fgTie( propname, this,
122            &FGNavCom::get_comm_alt_freq, &FGNavCom::set_comm_alt_freq );
123     fgSetArchivable( propname );
124
125     sprintf( propname, "/radios/comm[%d]/volume", index );
126     fgTie( propname, this,
127            &FGNavCom::get_comm_vol_btn, &FGNavCom::set_comm_vol_btn );
128     fgSetArchivable( propname );
129
130     sprintf( propname, "/radios/nav[%d]/frequencies/selected-mhz", index );
131     fgTie( propname, this,
132           &FGNavCom::get_nav_freq, &FGNavCom::set_nav_freq );
133     fgSetArchivable( propname );
134
135     sprintf( propname, "/radios/nav[%d]/frequencies/standby-mhz", index );
136     fgTie( propname , this,
137            &FGNavCom::get_nav_alt_freq, &FGNavCom::set_nav_alt_freq);
138     fgSetArchivable( propname );
139
140     sprintf( propname, "/radios/nav[%d]/radials/selected-deg", index );
141     fgTie( propname, this,
142            &FGNavCom::get_nav_sel_radial, &FGNavCom::set_nav_sel_radial );
143     fgSetArchivable( propname );
144
145     sprintf( propname, "/radios/nav[%d]/volume", index );
146     fgTie( propname, this,
147            &FGNavCom::get_nav_vol_btn, &FGNavCom::set_nav_vol_btn );
148     fgSetArchivable( propname );
149
150     sprintf( propname, "/radios/nav[%d]/ident", index );
151     fgTie( propname, this,
152            &FGNavCom::get_nav_ident_btn, &FGNavCom::set_nav_ident_btn );
153     fgSetArchivable( propname );
154
155                                 // Radio outputs
156     sprintf( propname, "/radios/nav[%d]/radials/actual-deg", index );
157     fgTie( propname,  this, &FGNavCom::get_nav_radial );
158
159     sprintf( propname, "/radios/nav[%d]/to-flag", index );
160     fgTie( propname, this, &FGNavCom::get_nav_to_flag );
161
162     sprintf( propname, "/radios/nav[%d]/from-flag", index );
163     fgTie( propname, this, &FGNavCom::get_nav_from_flag );
164
165     sprintf( propname, "/radios/nav[%d]/in-range", index );
166     fgTie( propname, this, &FGNavCom::get_nav_inrange );
167
168     sprintf( propname, "/radios/nav[%d]/heading-needle-deflection", index );
169     fgTie( propname, this, &FGNavCom::get_nav_heading_needle_deflection );
170
171     sprintf( propname, "/radios/nav[%d]/gs-needle-deflection", index );
172     fgTie( propname, this, &FGNavCom::get_nav_gs_needle_deflection );
173 }
174
175
176 void
177 FGNavCom::unbind ()
178 {
179     char propname[256];
180
181     sprintf( propname, "/radios/comm[%d]/inputs/power-btn", index );
182     fgUntie( propname );
183     sprintf( propname, "/radios/comm[%d]/frequencies/selected-mhz", index );
184     fgUntie( propname );
185     sprintf( propname, "/radios/comm[%d]/frequencies/standby-mhz", index );
186     fgUntie( propname );
187
188     sprintf( propname, "/radios/nav[%d]/frequencies/selected-mhz", index );
189     fgUntie( propname );
190     sprintf( propname, "/radios/nav[%d]/frequencies/standby-mhz", index );
191     fgUntie( propname );
192     sprintf( propname, "/radios/nav[%d]/radials/actual-deg", index );
193     fgUntie( propname );
194     sprintf( propname, "/radios/nav[%d]/radials/selected-deg", index );
195     fgUntie( propname );
196     sprintf( propname, "/radios/nav[%d]/ident", index );
197     fgUntie( propname );
198     sprintf( propname, "/radios/nav[%d]/to-flag", index );
199     fgUntie( propname );
200     sprintf( propname, "/radios/nav[%d]/from-flag", index );
201     fgUntie( propname );
202     sprintf( propname, "/radios/nav[%d]/in-range", index );
203     fgUntie( propname );
204     sprintf( propname, "/radios/nav[%d]/heading-needle-deflection", index );
205     fgUntie( propname );
206     sprintf( propname, "/radios/nav[%d]/gs-needle-deflection", index );
207     fgUntie( propname );
208 }
209
210
211 // model standard VOR/DME/TACAN service volumes as per AIM 1-1-8
212 double FGNavCom::adjustNavRange( double stationElev, double aircraftElev,
213                               double nominalRange )
214 {
215     // extend out actual usable range to be 1.3x the published safe range
216     const double usability_factor = 1.3;
217
218     // assumptions we model the standard service volume, plus
219     // ... rather than specifying a cylinder, we model a cone that
220     // contains the cylinder.  Then we put an upside down cone on top
221     // to model diminishing returns at too-high altitudes.
222
223     // altitude difference
224     double alt = ( aircraftElev * SG_METER_TO_FEET - stationElev );
225     // cout << "aircraft elev = " << aircraftElev * SG_METER_TO_FEET
226     //      << " station elev = " << stationElev << endl;
227
228     if ( nominalRange < 25.0 + SG_EPSILON ) {
229         // Standard Terminal Service Volume
230         return term_tbl->interpolate( alt ) * usability_factor;
231     } else if ( nominalRange < 50.0 + SG_EPSILON ) {
232         // Standard Low Altitude Service Volume
233         // table is based on range of 40, scale to actual range
234         return low_tbl->interpolate( alt ) * nominalRange / 40.0
235             * usability_factor;
236     } else {
237         // Standard High Altitude Service Volume
238         // table is based on range of 130, scale to actual range
239         return high_tbl->interpolate( alt ) * nominalRange / 130.0
240             * usability_factor;
241     }
242 }
243
244
245 // model standard ILS service volumes as per AIM 1-1-9
246 double FGNavCom::adjustILSRange( double stationElev, double aircraftElev,
247                                      double offsetDegrees, double distance )
248 {
249     // assumptions we model the standard service volume, plus
250
251     // altitude difference
252     // double alt = ( aircraftElev * SG_METER_TO_FEET - stationElev );
253     double offset = fabs( offsetDegrees );
254
255     if ( offset < 10 ) {
256         return FG_ILS_DEFAULT_RANGE;
257     } else if ( offset < 35 ) {
258         return 10 + (35 - offset) * (FG_ILS_DEFAULT_RANGE - 10) / 25;
259     } else if ( offset < 45 ) {
260         return (45 - offset);
261     } else if ( offset > 170 ) {
262         return FG_ILS_DEFAULT_RANGE;
263     } else if ( offset > 145 ) {
264         return 10 + (offset - 145) * (FG_ILS_DEFAULT_RANGE - 10) / 25;
265     } else if ( offset > 135 ) {
266         return (offset - 135);
267     } else {
268         return 0;
269     }
270 }
271
272
273 // Update the various nav values based on position and valid tuned in navs
274 void 
275 FGNavCom::update(double dt) 
276 {
277     double lon = lon_node->getDoubleValue() * SGD_DEGREES_TO_RADIANS;
278     double lat = lat_node->getDoubleValue() * SGD_DEGREES_TO_RADIANS;
279     double elev = alt_node->getDoubleValue() * SG_FEET_TO_METER;
280
281     need_update = false;
282
283     Point3D aircraft = sgGeodToCart( Point3D( lon, lat, elev ) );
284     Point3D station;
285     double az1, az2, s;
286
287     ////////////////////////////////////////////////////////////////////////
288     // Nav.
289     ////////////////////////////////////////////////////////////////////////
290
291     if ( nav_valid && power_btn && (bus_power->getDoubleValue() > 1.0) ) {
292         station = Point3D( nav_x, nav_y, nav_z );
293         nav_loc_dist = aircraft.distance3D( station );
294
295         if ( nav_has_gs ) {
296             station = Point3D( nav_gs_x, nav_gs_y, nav_gs_z );
297             nav_gs_dist = aircraft.distance3D( station );
298         } else {
299             nav_gs_dist = 0.0;
300         }
301         
302         // wgs84 heading
303         geo_inverse_wgs_84( elev, lat * SGD_RADIANS_TO_DEGREES, lon * SGD_RADIANS_TO_DEGREES, 
304                             nav_loclat, nav_loclon,
305                             &az1, &az2, &s );
306         // cout << "az1 = " << az1 << " magvar = " << nav_magvar << endl;
307         nav_heading = az1 - nav_magvar;
308         // cout << " heading = " << nav_heading
309         //      << " dist = " << nav_dist << endl;
310
311         if ( nav_loc ) {
312             double offset = nav_heading - nav_radial;
313             while ( offset < -180.0 ) { offset += 360.0; }
314             while ( offset > 180.0 ) { offset -= 360.0; }
315             // cout << "ils offset = " << offset << endl;
316             nav_effective_range = adjustILSRange(nav_elev, elev, offset,
317                                                   nav_loc_dist * SG_METER_TO_NM );
318         } else {
319             nav_effective_range = adjustNavRange(nav_elev, elev, nav_range);
320         }
321         // cout << "nav range = " << nav_effective_range
322         //      << " (" << nav_range << ")" << endl;
323
324         if ( nav_loc_dist < nav_effective_range * SG_NM_TO_METER ) {
325             nav_inrange = true;
326         } else if ( nav_loc_dist < 2 * nav_effective_range * SG_NM_TO_METER ) {
327             nav_inrange = sg_random() < 
328                 ( 2 * nav_effective_range * SG_NM_TO_METER - nav_loc_dist ) /
329                 (nav_effective_range * SG_NM_TO_METER);
330         } else {
331             nav_inrange = false;
332         }
333
334         if ( !nav_loc ) {
335             nav_radial = nav_sel_radial;
336         }
337     } else {
338         nav_inrange = false;
339         // cout << "not picking up vor. :-(" << endl;
340     }
341
342 #ifdef ENABLE_AUDIO_SUPPORT
343     if ( nav_valid && nav_inrange ) {
344         // play station ident via audio system if on + ident,
345         // otherwise turn it off
346         if ( power_btn && (bus_power->getDoubleValue() > 1.0)
347              && nav_ident_btn )
348         {
349             FGSimpleSound *sound;
350             sound = globals->get_soundmgr()->find( nav_fx_name );
351             if ( sound != NULL ) {
352                 sound->set_volume( nav_vol_btn );
353             } else {
354                 SG_LOG( SG_COCKPIT, SG_ALERT,
355                         "Can't find nav-vor-ident sound" );
356             }
357             sound = globals->get_soundmgr()->find( dme_fx_name );
358             if ( sound != NULL ) {
359                 sound->set_volume( nav_vol_btn );
360             } else {
361                 SG_LOG( SG_COCKPIT, SG_ALERT,
362                         "Can't find nav-dme-ident sound" );
363             }
364             // cout << "nav_last_time = " << nav_last_time << " ";
365             // cout << "cur_time = "
366             //      << globals->get_time_params()->get_cur_time();
367             if ( nav_last_time <
368                  globals->get_time_params()->get_cur_time() - 30 ) {
369                 nav_last_time = globals->get_time_params()->get_cur_time();
370                 nav_play_count = 0;
371             }
372             // cout << " nav_play_count = " << nav_play_count << endl;
373             // cout << "playing = "
374             //      << globals->get_soundmgr()->is_playing(nav_fx_name)
375             //      << endl;
376             if ( nav_play_count < 4 ) {
377                 // play VOR ident
378                 if ( !globals->get_soundmgr()->is_playing(nav_fx_name) ) {
379                     globals->get_soundmgr()->play_once( nav_fx_name );
380                     ++nav_play_count;
381                 }
382             } else if ( nav_play_count < 5 && nav_has_dme ) {
383                 // play DME ident
384                 if ( !globals->get_soundmgr()->is_playing(nav_fx_name) &&
385                      !globals->get_soundmgr()->is_playing(dme_fx_name) ) {
386                     globals->get_soundmgr()->play_once( dme_fx_name );
387                     ++nav_play_count;
388                 }
389             }
390         } else {
391             globals->get_soundmgr()->stop( nav_fx_name );
392             globals->get_soundmgr()->stop( dme_fx_name );
393         }
394     }
395 #endif
396
397 }
398
399
400 // Update current nav/adf radio stations based on current postition
401 void FGNavCom::search() 
402 {
403     double lon = lon_node->getDoubleValue() * SGD_DEGREES_TO_RADIANS;
404     double lat = lat_node->getDoubleValue() * SGD_DEGREES_TO_RADIANS;
405     double elev = alt_node->getDoubleValue() * SG_FEET_TO_METER;
406
407     FGILS ils;
408     FGNav nav;
409
410     ////////////////////////////////////////////////////////////////////////
411     // Nav.
412     ////////////////////////////////////////////////////////////////////////
413
414     if ( current_ilslist->query( lon, lat, elev, nav_freq, &ils ) ) {
415         nav_id = ils.get_locident();
416         nav_valid = true;
417         if ( last_nav_id != nav_id || last_nav_vor ) {
418             nav_trans_ident = ils.get_trans_ident();
419             last_nav_id = nav_id;
420             last_nav_vor = false;
421             nav_loc = true;
422             nav_has_dme = ils.get_has_dme();
423             nav_has_gs = ils.get_has_gs();
424
425             nav_loclon = ils.get_loclon();
426             nav_loclat = ils.get_loclat();
427             nav_gslon = ils.get_gslon();
428             nav_gslat = ils.get_gslat();
429             nav_elev = ils.get_gselev();
430             nav_magvar = 0;
431             nav_range = FG_ILS_DEFAULT_RANGE;
432             nav_effective_range = nav_range;
433             nav_target_gs = ils.get_gsangle();
434             nav_radial = ils.get_locheading();
435             while ( nav_radial <   0.0 ) { nav_radial += 360.0; }
436             while ( nav_radial > 360.0 ) { nav_radial -= 360.0; }
437             nav_x = ils.get_x();
438             nav_y = ils.get_y();
439             nav_z = ils.get_z();
440             nav_gs_x = ils.get_gs_x();
441             nav_gs_y = ils.get_gs_y();
442             nav_gs_z = ils.get_gs_z();
443
444 #ifdef ENABLE_AUDIO_SUPPORT
445             if ( globals->get_soundmgr()->exists( nav_fx_name ) ) {
446                 globals->get_soundmgr()->remove( nav_fx_name );
447             }
448             FGSimpleSound *sound;
449             sound = morse.make_ident( nav_trans_ident, LO_FREQUENCY );
450             sound->set_volume( 0.3 );
451             globals->get_soundmgr()->add( sound, nav_fx_name );
452
453             if ( globals->get_soundmgr()->exists( dme_fx_name ) ) {
454                 globals->get_soundmgr()->remove( dme_fx_name );
455             }
456             sound = morse.make_ident( nav_trans_ident, HI_FREQUENCY );
457             sound->set_volume( 0.3 );
458             globals->get_soundmgr()->add( sound, dme_fx_name );
459
460             int offset = (int)(sg_random() * 30.0);
461             nav_play_count = offset / 4;
462             nav_last_time = globals->get_time_params()->get_cur_time() -
463                 offset;
464             // cout << "offset = " << offset << " play_count = "
465             //      << nav_play_count
466             //      << " nav_last_time = " << nav_last_time
467             //      << " current time = "
468             //      << globals->get_time_params()->get_cur_time() << endl;
469 #endif
470
471             // cout << "Found an ils station in range" << endl;
472             // cout << " id = " << ils.get_locident() << endl;
473         }
474     } else if ( current_navlist->query( lon, lat, elev, nav_freq, &nav ) ) {
475         nav_id = nav.get_ident();
476         nav_valid = true;
477         if ( last_nav_id != nav_id || !last_nav_vor ) {
478             last_nav_id = nav_id;
479             last_nav_vor = true;
480             nav_trans_ident = nav.get_trans_ident();
481             nav_loc = false;
482             nav_has_dme = nav.get_has_dme();
483             nav_has_gs = false;
484             nav_loclon = nav.get_lon();
485             nav_loclat = nav.get_lat();
486             nav_elev = nav.get_elev();
487             nav_magvar = nav.get_magvar();
488             nav_range = nav.get_range();
489             nav_effective_range = adjustNavRange(nav_elev, elev, nav_range);
490             nav_target_gs = 0.0;
491             nav_radial = nav_sel_radial;
492             nav_x = nav.get_x();
493             nav_y = nav.get_y();
494             nav_z = nav.get_z();
495
496 #ifdef ENABLE_AUDIO_SUPPORT
497             if ( globals->get_soundmgr()->exists( nav_fx_name ) ) {
498                 globals->get_soundmgr()->remove( nav_fx_name );
499             }
500             FGSimpleSound *sound;
501             sound = morse.make_ident( nav_trans_ident, LO_FREQUENCY );
502             sound->set_volume( 0.3 );
503             if ( globals->get_soundmgr()->add( sound, nav_fx_name ) ) {
504                 // cout << "Added nav-vor-ident sound" << endl;
505             } else {
506                 cout << "Failed to add v1-vor-ident sound" << endl;
507             }
508
509             if ( globals->get_soundmgr()->exists( dme_fx_name ) ) {
510                 globals->get_soundmgr()->remove( dme_fx_name );
511             }
512             sound = morse.make_ident( nav_trans_ident, HI_FREQUENCY );
513             sound->set_volume( 0.3 );
514             globals->get_soundmgr()->add( sound, dme_fx_name );
515
516             int offset = (int)(sg_random() * 30.0);
517             nav_play_count = offset / 4;
518             nav_last_time = globals->get_time_params()->get_cur_time() -
519                 offset;
520             // cout << "offset = " << offset << " play_count = "
521             //      << nav_play_count << " nav_last_time = "
522             //      << nav_last_time << " current time = "
523             //      << globals->get_time_params()->get_cur_time() << endl;
524 #endif
525
526             // cout << "Found a vor station in range" << endl;
527             // cout << " id = " << nav.get_ident() << endl;
528         }
529     } else {
530         nav_valid = false;
531         nav_id = "";
532         nav_radial = 0;
533         nav_trans_ident = "";
534         last_nav_id = "";
535 #ifdef ENABLE_AUDIO_SUPPORT
536         if ( ! globals->get_soundmgr()->remove( nav_fx_name ) ) {
537             cout << "Failed to remove nav-vor-ident sound" << endl;
538         }
539         globals->get_soundmgr()->remove( dme_fx_name );
540 #endif
541         // cout << "not picking up vor1. :-(" << endl;
542     }
543 }
544
545
546 // return the amount of heading needle deflection, returns a value
547 // clamped to the range of ( -10 , 10 )
548 double FGNavCom::get_nav_heading_needle_deflection() const {
549     double r;
550
551     if ( nav_inrange ) {
552         r = nav_heading - nav_radial;
553         // cout << "Radial = " << nav_radial 
554         //      << "  Bearing = " << nav_heading << endl;
555     
556         while ( r >  180.0 ) { r -= 360.0;}
557         while ( r < -180.0 ) { r += 360.0;}
558         if ( fabs(r) > 90.0 ) {
559             r = ( r<0.0 ? -r-180.0 : -r+180.0 );
560             if ( nav_loc ) {
561                 r = -r;
562             }
563         }
564
565         // According to Robin Peel, the ILS is 4x more sensitive than a vor
566         if ( nav_loc ) { r *= 4.0; }
567         if ( r < -10.0 ) { r = -10.0; }
568         if ( r >  10.0 ) { r = 10.0; }
569     } else {
570         r = 0.0;
571     }
572
573     return r;
574 }
575
576
577 // return the amount of glide slope needle deflection (.i.e. the
578 // number of degrees we are off the glide slope * 5.0
579 double FGNavCom::get_nav_gs_needle_deflection() const {
580     if ( nav_inrange && nav_has_gs ) {
581         double x = nav_gs_dist;
582         double y = (fgGetDouble("/position/altitude-ft") - nav_elev)
583             * SG_FEET_TO_METER;
584         double angle = atan2( y, x ) * SGD_RADIANS_TO_DEGREES;
585         return (nav_target_gs - angle) * 5.0;
586     } else {
587         return 0.0;
588     }
589 }
590
591
592 /**
593  * Return true if the NAV TO flag should be active.
594  */
595 bool 
596 FGNavCom::get_nav_to_flag () const
597 {
598   if (nav_inrange) {
599     double offset = fabs(nav_heading - nav_radial);
600     if (nav_loc)
601       return true;
602     else
603       return (offset <= 90.0 || offset >= 270.0);
604   } else {
605     return false;
606   }
607 }
608
609
610 /**
611  * Return true if the NAV FROM flag should be active.
612  */
613 bool
614 FGNavCom::get_nav_from_flag () const
615 {
616   if (nav_inrange) {
617     double offset = fabs(nav_heading - nav_radial);
618     if (nav_loc)
619       return false;
620     else
621       return (offset > 90.0 && offset < 270.0);
622   } else {
623     return false;
624   }
625 }