@@ -130,7 +130,13 @@ TABS.gps.initialize = function (callback) {
130130 let vehiclesCursorInitialized = false ;
131131 let arrowIcon ;
132132
133- function process_html ( ) {
133+ async function process_html ( settingsPromise ) {
134+ // Wait for settings to finish loading to avoid race conditions
135+ // where user changes are overwritten by background setting loads
136+ if ( settingsPromise ) {
137+ await settingsPromise ;
138+ }
139+
134140 i18n . localize ( ) ;
135141
136142 var fcFeatures = FC . getFeatures ( ) ;
@@ -200,6 +206,163 @@ TABS.gps.initialize = function (callback) {
200206
201207 gps_ubx_sbas_e . val ( FC . MISC . gps_ubx_sbas ) ;
202208
209+ // GPS Preset Configuration
210+ const GPS_PRESETS = {
211+ m8 : {
212+ name : "u-blox M8" ,
213+ galileo : true ,
214+ glonass : true ,
215+ beidou : true ,
216+ rate : 8 ,
217+ description : [
218+ "4 GNSS constellations for maximum accuracy" ,
219+ "8Hz update rate (conservative for M8)" ,
220+ "Best for: Navigation, position hold, slower aircraft"
221+ ]
222+ } ,
223+ 'm9-precision' : {
224+ name : "u-blox M9 (Precision Mode)" ,
225+ galileo : true ,
226+ glonass : false ,
227+ beidou : true ,
228+ rate : 5 ,
229+ description : [
230+ "3 GNSS constellations (GPS+Galileo+Beidou) → 32 satellites" ,
231+ "5Hz update rate, HDOP ~1.0-1.3" ,
232+ "Best for: Long-range cruise, position hold, navigation missions"
233+ ]
234+ } ,
235+ 'm9-sport' : {
236+ name : "u-blox M9 (Sport Mode)" ,
237+ galileo : true ,
238+ glonass : false ,
239+ beidou : true ,
240+ rate : 10 ,
241+ description : [
242+ "3 GNSS constellations (GPS+Galileo+Beidou) → 16 satellites" ,
243+ "10Hz update rate (hardware limit), HDOP ~2.0-2.5" ,
244+ "Best for: Fast flying, racing, acrobatics, quick response"
245+ ]
246+ } ,
247+ m10 : {
248+ name : "u-blox M10" ,
249+ galileo : true ,
250+ glonass : false ,
251+ beidou : true ,
252+ rate : 8 ,
253+ description : [
254+ "3 GNSS constellations (GPS+Galileo+Beidou)" ,
255+ "8Hz update rate (safe for M10 default CPU clock)" ,
256+ "Best for: General use, balanced performance"
257+ ]
258+ } ,
259+ 'm10-highperf' : {
260+ name : "u-blox M10 (High-Performance)" ,
261+ galileo : true ,
262+ glonass : true ,
263+ beidou : true ,
264+ rate : 10 ,
265+ description : [
266+ "4 GNSS constellations for maximum satellites" ,
267+ "10Hz update rate (requires high-performance CPU clock)" ,
268+ "Only use if you KNOW your M10 has high-performance clock enabled"
269+ ]
270+ } ,
271+ manual : {
272+ name : "Manual Settings" ,
273+ description : [
274+ "Full control over constellation selection and update rate" ,
275+ "For advanced users and special requirements"
276+ ]
277+ }
278+ } ;
279+
280+ function detectGPSPreset ( hwVersion ) {
281+ switch ( hwVersion ) {
282+ case 800 : return 'm8' ;
283+ case 900 : return 'm9-precision' ; // Default to precision mode for better accuracy
284+ case 1000 : return 'm10' ;
285+ default : return 'manual' ;
286+ }
287+ }
288+
289+ function applyGPSPreset ( presetId ) {
290+ // Handle special cases first (before checking GPS_PRESETS)
291+ if ( presetId === 'manual' ) {
292+ // Enable all controls
293+ $ ( '.preset-controlled' ) . prop ( 'disabled' , false ) ;
294+ $ ( '#gps_ublox_nav_hz' ) . prop ( 'disabled' , false ) ;
295+ $ ( '#preset_info' ) . hide ( ) ;
296+ return ;
297+ }
298+
299+ if ( presetId === 'auto' ) {
300+ // Try to auto-detect from FC
301+ if ( FC . GPS_DATA && FC . GPS_DATA . hwVersion ) {
302+ const detectedPreset = detectGPSPreset ( FC . GPS_DATA . hwVersion ) ;
303+ applyGPSPreset ( detectedPreset ) ;
304+ $ ( '#gps_preset_mode' ) . val ( detectedPreset ) ;
305+ GUI . log ( i18n . getMessage ( 'gpsAutoDetectSuccess' ) + ' ' + GPS_PRESETS [ detectedPreset ] . name ) ;
306+ } else {
307+ // Fall back to manual if can't detect
308+ applyGPSPreset ( 'manual' ) ;
309+ $ ( '#gps_preset_mode' ) . val ( 'manual' ) ;
310+ GUI . log ( i18n . getMessage ( 'gpsAutoDetectFailed' ) ) ;
311+ }
312+ return ;
313+ }
314+
315+ // Normal preset application
316+ const preset = GPS_PRESETS [ presetId ] ;
317+ if ( ! preset ) return ;
318+
319+ // Apply preset values (user can still adjust after applying)
320+ $ ( '#gps_use_galileo' ) . prop ( 'checked' , preset . galileo ) ;
321+ $ ( '#gps_use_glonass' ) . prop ( 'checked' , preset . glonass ) ;
322+ $ ( '#gps_use_beidou' ) . prop ( 'checked' , preset . beidou ) ;
323+ $ ( '#gps_ublox_nav_hz' ) . val ( preset . rate ) ;
324+
325+ // Show preset info
326+ $ ( '#preset_name' ) . text ( preset . name ) ;
327+ $ ( '#preset_details' ) . html ( preset . description . map ( d => `<li>${ d } </li>` ) . join ( '' ) ) ;
328+ $ ( '#preset_info' ) . show ( ) ;
329+ }
330+
331+ // Set up preset mode handler (namespaced to prevent memory leaks)
332+ $ ( '#gps_preset_mode' ) . on ( 'change.gpsTab' , function ( ) {
333+ applyGPSPreset ( $ ( this ) . val ( ) ) ;
334+ } ) ;
335+
336+ // Hardware detection status indicator
337+ function updateHardwareStatus ( ) {
338+ if ( FC . GPS_DATA && FC . GPS_DATA . hwVersion && FC . GPS_DATA . hwVersion > 0 ) {
339+ const detectedPreset = detectGPSPreset ( FC . GPS_DATA . hwVersion ) ;
340+ if ( detectedPreset && detectedPreset !== 'manual' && GPS_PRESETS [ detectedPreset ] ) {
341+ $ ( '#gps_hardware_name' ) . text ( GPS_PRESETS [ detectedPreset ] . name + ' detected' ) ;
342+ $ ( '#gps_hardware_status' ) . show ( ) ;
343+ }
344+ }
345+ }
346+
347+ // Handler for "Use optimal settings" link (namespaced)
348+ $ ( '#gps_apply_optimal' ) . on ( 'click.gpsTab' , function ( e ) {
349+ e . preventDefault ( ) ;
350+ if ( FC . GPS_DATA && FC . GPS_DATA . hwVersion ) {
351+ const detectedPreset = detectGPSPreset ( FC . GPS_DATA . hwVersion ) ;
352+ if ( detectedPreset && detectedPreset !== 'manual' ) {
353+ $ ( '#gps_preset_mode' ) . val ( detectedPreset ) . trigger ( 'change' ) ;
354+ GUI . log ( 'Applied recommended settings for ' + GPS_PRESETS [ detectedPreset ] . name ) ;
355+ }
356+ }
357+ } ) ;
358+
359+ // Initialize - default to manual mode to preserve user's existing settings
360+ // User can explicitly select a preset or use "Auto-detect" if desired
361+ applyGPSPreset ( 'manual' ) ;
362+
363+ // Check for hardware detection after a short delay to allow GPS data to arrive
364+ setTimeout ( updateHardwareStatus , 500 ) ;
365+
203366 let mapView = new View ( {
204367 center : [ 0 , 0 ] ,
205368 zoom : 15
@@ -240,7 +403,7 @@ TABS.gps.initialize = function (callback) {
240403 } ) ) ;
241404 }
242405
243- $ ( "#center_button" ) . on ( 'click' , function ( ) {
406+ $ ( "#center_button" ) . on ( 'click.gpsTab ' , function ( ) {
244407 let lat = FC . GPS_DATA . lat / 10000000 ;
245408 let lon = FC . GPS_DATA . lon / 10000000 ;
246409 let center = fromLonLat ( [ lon , lat ] ) ;
@@ -466,7 +629,7 @@ TABS.gps.initialize = function (callback) {
466629 } ) ;
467630 }
468631
469- $ ( 'a.save' ) . on ( 'click' , function ( ) {
632+ $ ( 'a.save' ) . on ( 'click.gpsTab ' , function ( ) {
470633 serialPortHelper . set ( $port . val ( ) , 'GPS' , $baud . val ( ) ) ;
471634 features . reset ( ) ;
472635 features . fromUI ( $ ( '.tab-gps' ) ) ;
@@ -517,15 +680,15 @@ TABS.gps.initialize = function (callback) {
517680 }
518681 }
519682
520- $ ( 'a.loadAssistnowOnline' ) . on ( 'click' , function ( ) {
683+ $ ( 'a.loadAssistnowOnline' ) . on ( 'click.gpsTab ' , function ( ) {
521684 if ( globalSettings . assistnowApiKey != null && globalSettings . assistnowApiKey != '' ) {
522685 ublox . loadAssistnowOnline ( processUbloxData ) ;
523686 } else {
524687 dialog . alert ( "Assistnow Token not set!" ) ;
525688 }
526689 } ) ;
527690
528- $ ( 'a.loadAssistnowOffline' ) . on ( 'click' , function ( ) {
691+ $ ( 'a.loadAssistnowOffline' ) . on ( 'click.gpsTab ' , function ( ) {
529692 if ( globalSettings . assistnowApiKey != null && globalSettings . assistnowApiKey != '' ) {
530693 ublox . loadAssistnowOffline ( processUbloxData ) ;
531694 } else {
@@ -539,6 +702,14 @@ TABS.gps.initialize = function (callback) {
539702} ;
540703
541704TABS . gps . cleanup = function ( callback ) {
705+ // Remove all namespaced event handlers to prevent memory leaks
706+ $ ( '#gps_preset_mode' ) . off ( '.gpsTab' ) ;
707+ $ ( '#gps_apply_optimal' ) . off ( '.gpsTab' ) ;
708+ $ ( '#center_button' ) . off ( '.gpsTab' ) ;
709+ $ ( 'a.save' ) . off ( '.gpsTab' ) ;
710+ $ ( 'a.loadAssistnowOnline' ) . off ( '.gpsTab' ) ;
711+ $ ( 'a.loadAssistnowOffline' ) . off ( '.gpsTab' ) ;
712+
542713 if ( callback ) callback ( ) ;
543714 if ( TABS . gps . toolboxAdsbVehicle ) {
544715 TABS . gps . toolboxAdsbVehicle . close ( ) ;
0 commit comments