/******************************************************************************
//	param.c			handles the configuration parameters	
//	
//*****************************************************************************
//	
//	Dec2008, created
//
//  Oct11 updated writing eeprom param
//  jan16 updated for non-volatile ram for profile, surface counters
//  may16 update for more flash card properly mounted.
//
//==== Future Expansion =======================================================
//		last update: Jan2009
//
//	1. 
//  2. getting/setting parameters in the msp430 modules
//  3. break out the mission id into 2 words: YYMM and id#
//  4. parse command '2'
//  5. ability to update Eeprom params from menus and shore commands
//  6. variations on passwords?
//  7. read in old config file and just use names to update.
//
*******************************************************************************/
#include	<cfxbios.h>		// Persistor BIOS and I/O Definitions
#include	<cfxpico.h>		// Persistor PicoDOS Definitions

#include	<assert.h>
#include	<ctype.h>
#include	<errno.h>
#include	<float.h>
#include	<limits.h>
#include	<locale.h>
#include	<math.h>
#include	<setjmp.h>
#include	<signal.h>
#include	<stdarg.h>
#include	<stddef.h>
#include	<stdio.h>
#include	<stdlib.h>
#include	<string.h>
#include	<time.h>


#include    "cf2_qsm332.h"
#include    "spi_cmd.h"
#include    "nav.h"
#include    "lifo.h"
#include    "setup.h"

// globals
extern char Verbose; // switch for displaying more info
extern char IRQ_Flag; // interrupt flag: says what caused the interrupt
extern short iparam [ MAX_PARAM ]; // global parameter settings
extern struct all_param all;
//=== jan16 moving Surf_N, Prof_N from eeprom to non-volatile RAM ==========
ulong NvRamSurf_N : 0xC010; // surface counter
ulong NvRamProf_N : 0xC014; // profile counter

#define END_GROUP 126 // END of table has group setting character =126

struct group_param group[] = //==============================================
{ // table with group char linked to label
  // this is used strictly for display purposes, 
  // and for individually modifying paramaters within a group
  //      123456789012345678901234567890
  { 'V', "Vehicle_ID,Sensor_Settings"     },
  { 'A', "Attitude-Control(pitch)"        },
  { 'B', "Attitude-Control(roll)"         },
  { 'C', "Compiler_settings"              },
  { 'D', "Doppler_settings"               },
  { 'E', "Exception_settings"             },
  { 'H', "Hydraulic_settings"             },
  { 'M', "Mission_settings"               },
  { 'N', "Nav_Compass_settings"           },
  { 'R', "Route_settings"                 },
  { 'S', "Steering_Control"               },
  { 'T', "Communication,Timing"           },
  { 'X', "Request_Reply"                  },
  { 'U', "NOT ASSIGNED"                   },
  { 'W', "Watchdog settings"              },
#if BENTHOS==1 || NPS==1
  { 'L', "Acoustic_Link_settings"         },
#endif
  
  { END_GROUP, "END"                      }
}; //========================================================================
#if BENTHOS==1 || NPS==1
  #define NGROUP 17 //max # of groups in the above+1
#else
  #define NGROUP 16 //max # of groups in the above+1
#endif
struct shore_cmd shore[] = //================================================
{ // shore command assignments
  { '0',  0, 1, 'E', 1, 2, "Spray_RESET"      },
  { '1',  0, 2, 'E', 1, 1, "END_MISSION"      },
  { '2',  0, 1, 'V', 2, 2, "Change_Val[addr]" },
  { '3',  0, 2, 'R', 3, 3, "Change_Waypt[i]"  },
  { '4',  0, 0, 'M', 3, 3, "Drift_Timing"     },
  { '5',  0, 0, 'H', 1, 2, "Air_Vent_Control" },
  { '5','1', 0, 'H', 3, 3, "Auto-Degas Loop"  }, // jun15
#if BENTHOS==1 || NPS==1
  { '6',  0, 2, 'L', 0, 0, "Acoustic_Link"    },
  { '6','0', 2, 'L', 0, 0, "Acoustic_Link"    },
  { '6','1', 2, 'L', 0, 0, "Acoustic_Link"    },
  { '6','2', 2, 'L', 0, 0, "Acoustic_Link"    },
  { '6','3', 2, 'L', 0, 0, "Acoustic_Link"    },
  { '6','4', 2, 'L', 0, 0, "Acoustic_Link"    },
#else
  { '6',  0, 0, 'U', 0, 0, "NOT_ASSIGNED"     },
#endif
  { '7',  0, 2, 'R', 2, 5, "Change_Route[i]"  },
  { '8',  0, 0, 'M', 3, 3, "Max_Depth"        },
  { '9',  0, 0, 'T', 1, 1, "Min_Dive_Cycle"   },
  { 'A',  0, 0, 'V', 1, 1, "Optical_On/Off"   },
  { 'B',  0, 0, 'M', 1, 1, "Max_Dives"        },
  { 'C',  0, 0, 'H', 1, 7, "Hyd_Pump_Control" }, //dec14, added 50m pump time
  { 'D',  0, 1, 'S', 1, 5, "Set_Correction"   }, //dec11, fnx=1; turn off manual heading
  { 'E',  0, 0, 'S', 1, 2, "Manual_steering"  },
  { 'F',  0, 0, 'D', 1, 6, "Doppler"          },
#if NTK==1
  { 'F','1', 0, 'D', 1, 7, "NTK"              }, //aug14 update from 6 to 7 params
#endif
#if ZOOG==1 //=== oct16 zooGlider shore commands ================
  { 'F','2', 2, 'D', 3, 3, "zooNar Pings"     }, 
  { 'F','3', 2, 'D', 3, 3, "zooNar Beam"      }, 
#endif
  { 'G',  0, 0, 'M', 1, 1, "Glide_Angle"      },
  { 'H',  0, 0, 'V', 2, 2, "Eng_Time_Series"  },
  { 'I',  0, 0, 'A', 1, 5, "Pitch_Control"    },
  { 'J',  0, 0, 'B', 1, 5, "Roll_Control"     },
  { 'K',  0, 0, 'T', 1, 1, "Min_Tm_Btwn_Sat"  },
  { 'L',  0, 1, 'T', 1, 3, "Argos_Timing"     },
  { 'M',  0, 1, 'T', 1, 3, "GPS_Timing"       }, // dec11, add ability to do cold reset
  { 'N',  0, 0, 'U', 0, 0, "NOT_ASSIGNED"     }, // N is used in the ISUS code
  { 'O',  0, 2, 'O', 4, 4, "PP_cell_info"     }, // mar11: sets general PP_DAQ param
  { 'O','1', 2, 'O', 4, 4, "PP_load[i]_info"  }, // mar11: sets specific PP load vals
  { 'P',  0, 0, 'E', 1, 4, "Exception_Action" }, // dec11 added op_mode
  { 'R',  0, 2, 'X', 1, 1, "Request_Resend"   },
  { 'S',  0, 0, 'T', 2, 2, "Wing_Ant_Select"  },
  { 'T',  0, 0, 'S', 1, 2, "Steering_Point"   },
  { 'U',  0, 0, 'T', 1, 2, "SBD_Timing"       },
  { 'V',  0, 0, 'N', 1, 2, "Compass_Declin."  },
  { 'W',  0, 1, 'R', 4, 4, "Route_Settings"   },
  { 'Z',  0, 0, 'V', 1, 3, "Altimeter_Cntl"   },
  { '@',  0, 2, 'X', 0, 0, "Request_Route"    },
  { '!',  0, 2, 'X', 0, 0, "Request_Params"   },
  
  { END_GROUP, 0,  0, END_GROUP, 0, 0, "End_shore_commands" }
}; //========================================================================
#define N_SHORE 43 // # shore commands+1 //jan14, take into account BENTHOS,NTK

// parameter addr-name link, plus default settings
 struct lookup Param_table[] =  //===========================================
{   //table to look up IA_addr given ascii name
    //table entries are:
    //   name (for linking address to config file)
    //   group (group ID, used for display, sorting only)
    //   set_type: flag of the variable status:
    //   default value (in case of reset & cfg file is corrupted)
    //   addr (pointer) into the global iparam.

    // values SET by eeprom/compile, NOT by .cfg or shore commands
    // 0902.5 feb11, moved sensor_cfg to compile version= installed sensor suite
    {"MajVer",SA_major_ver, 'C',SET_NO ,MAJ_VER,  0, 0, 0, 0, 0, "EEPROM_Version"     },
    {"MinVer",SA_minor_ver, 'C',SET_NO ,MIN_VER,  0, 0, 0, 0, 0, "Minor_Revision"     },
    {"ShoVer",SA_shore_ver, 'C',SET_NO ,SHORE_VER,0, 0, 0, 0, 0, "Shore_Cmd_Version"  }, 
    {"SENVER",SA_sensor_cfg,'C',SET_NO ,SENSE_CFG,0, 0, 0, 0, 0, "Sensor_Config"      },   
    {"GIOVer",SA_gpio_ver,  'C',SET_NO ,0,        0, 0, 0, 0, 0, "M430_GPIO_Version"  },
    // SBE sensor offsets and gains
    {"PR_off",SA_pr_off,   'C',SET_NO , PR_OFF,  0, 0, 0, 0, 0, "SBE_Poff[dBar]"     },
    {"PR_gn", SA_pr_gain,  'C',SET_NO , PR_GAIN, 0, 0, 0, 0, 0, "SBE_Pgain*1000"     },
    {"T_off", SA_temp_off, 'C',SET_NO , T_OFF,   0, 0, 0, 0, 0, "SBE_Toff[degC]"     },
    {"T_gn" , SA_temp_gain,'C',SET_NO , T_GAIN,  0, 0, 0, 0, 0, "SBE_Tgain*1000"     },
    {"S_off", SA_sal_off,  'C',SET_NO , S_OFF,   0, 0, 0, 0, 0, "SBE_Soff[PSU]"      },
    {"S_gn" , SA_sal_gain, 'C',SET_NO , S_GAIN,  0, 0, 0, 0, 0, "SBE_Sgain*1000"     },
    // 0903: feb11, include sample timing, so documented in config file
    {"Dt_dn", SA_dt_dn,    'C',SET_NO , SAMP_DT_DN,  0, 0, 0, 0, 0, "Samp_Secs_Down" },
    {"Dt_up", SA_dt_up,    'C',SET_NO , SAMP_DT_UP,  0, 0, 0, 0, 0, "Samp_Secs_Up"   },

    // values that reside in eeprom --------------------------------
	{"SERNO" ,EA_serno,     'V',SET_EE ,  99,   1,  999, 0, 0,0, "Spray_Ser_No"    },    
    // dec11 0903.6, let op_mode be modified in the exceptions command.
	{"OPMODE",EA_op_mode,   'V',SET_EE ,   0,  -9,   99,'P',0,3, "Operation_Mode"  },  
	{"OPTGN" ,EA_opt_gain,  'V',SET_EE ,   0,   0, 9999, 0, 0,0, "Optical_Gain"    },
	
    // jan16 values that reside in non-volatile RAM ----------------
    // change from SET_NV to SET_NO to keep config, shore commands from updating
	{"SURF_N",EA_surf_num,  'V',SET_NV ,   0,   0, 9999, 0, 0,0, "Present_Surf_Num"},
	{"PROF_N",EA_prof_num,  'V',SET_NV ,   0,   0, 9999, 0, 0,0, "Present_Profile_Num"},
	//values in RAM, but read in from the .cfg file ----------------
	
	// pitch-specific control settings Mar12, expanded bounds on min, max settings
	{"Pi_0",  IA_pitch_0,    'A',SET_OK,2048,  100, 4000, 0, 0,0, "Zero_Pitch"  }, 
	{"Pi_xmt",IA_pitch_xmt,  'A',SET_OK,1700,  100, 4000, 0, 0,0, "Pitch_Surface_Xmit"  }, 
	
	{"Pi_10d",IA_pitch_10d,  'A',SET_OK,  60,   10,  200, 0, 0,0, "Pitch_Cnts_for_10deg"  },   
	{"P_dcdt",MA_pitch_dcdt, 'A',SET_OK,  23,   15,  100, 0, 0,0, "Pitch_Cnts_Per_S"  }, 
	
	{"Pi_wdn",IA_pi_wdn,     'A',SET_OK,   0,   -15,    1, 'I', 0, 0, "Descent_Wmin[cm/s]"   },
	{"Pi_use",IA_pi_stat,    'A',SET_OK,   1,     0,    2, 'I', 0, 1, "Pitch_Status"   },
	{"Pi_dn", IA_pi_dn,      'A',SET_OK, 120, -1000, 3000, 'I', 0, 3, "Pitch_Down_Offset" },
	{"Pi_up", IA_pi_up,      'A',SET_OK,-300, -3000, 3000, 'I', 0, 2, "Pitch_Up_Offset" },
	{"Pi_pmp",IA_pi_pmp,     'A',SET_OK,   9,     0,   50, 'I', 0, 4, "Pitch_Pump_Cnts" },
	//msp430 eeprom settings
	{"Pi_min",MA_pitch_min,  'A',SET_ME ,1000,  500, 4000,  0,0,0, "Min_Pitch_Cnts" },    
	{"Pi_max",MA_pitch_max,  'A',SET_ME ,3200,  500, 4000,  0,0,0, "Max_Pitch_Cnts" },  
    {"Pi_c2i",MA_pi_c2i,     'A',SET_ME,   1,     1, 4000,  0,0,0, "Pi_Cnts_to_Amp"   },
    {"Pi_c2v",MA_pi_c2v,     'A',SET_ME,   1,     1, 4000,  0,0,0, "Pi_Cnts_to_Volt"  },
    {"Pi_mxI",MA_pi_max_amps,'A',SET_ME, 150,    50, 1000,  0,0,0, "Pitch_Max_Amps"   },
    {"Pi_SN", MA_pi_sn,      'A',SET_ME,   1,     1, 9999,  0,0,0, "Pitch_Ser_No"     },
    {"Pi_Ver",MA_pi_ver,     'A',SET_ME,   0,     0, 9999,  0,0,0, "Pi_Firmware_Ver"  },

	// roll-specific control settings ---------------------------
	{"Ro_0",  IA_roll_0,     'B',SET_OK,2048,  100, 4000, 0, 0,0, "Zero_Roll"  },
	{"Rport", IA_roll_port,  'B',SET_OK,1500,  100, 4000, 0, 0,0, "Port_Wing_Up"  },   
	{"Rstbd", IA_roll_stbd,  'B',SET_OK,2500,  100, 4000, 0, 0,0, "Stbd_Wing_Up"  }, 
	
	{"Ro_10d",IA_roll_10d,   'B',SET_OK,  70,   10,  200, 0, 0,0, "Roll_Cnts_for_10deg"  },    
	{"R_dcdt",MA_roll_dcdt,  'B',SET_OK,  17,   15,  100, 0, 0,0, "Roll_Cnts_Per_S"  },    
	 //0902.5 feb11 increased max limit on Ro_use
	{"Ro_use",IA_ro_use,     'B',SET_OK,   1,    0,  999,'J',0,0, "Use_Roll_Angle"   },
	{"Ro_up", IA_ro_up,      'B',SET_OK,   0,  -30,   30,'J',0,1, "Roll_Angle_Up"    },
	{"Ro_dn", IA_ro_dn,      'B',SET_OK,   0,  -30,   30,'J',0,2, "Roll_Angle_Down"  },
	{"Ro_tau",IA_ro_rtau,    'B',SET_OK,  10, -200,  200,'J',0,3, "Roll_PI_Ptau"     }, //jan16 allow <0
	{"Ro_eta",IA_ro_etau,    'B',SET_OK,  10,-2000, 2000,'J',0,4, "Roll_PI_Itau"     },
	// msp430 eeprom settings
	{"Ro_min",MA_roll_min,   'B',SET_ME ,1400,  500, 4000, 0,0,0, "Min_Roll_Cnts" }, 
	{"Ro_max",MA_roll_max,   'B',SET_ME ,2700,  500, 4000, 0,0,0, "Max_Roll_Cnts" },  
    {"Ro_c2i",MA_ro_c2i,     'B',SET_ME,   1,    1, 4000,  0,0,0, "Roll_Cnts_to_Amp" },
    {"Ro_c2v",MA_ro_c2v,     'B',SET_ME,   1,    1, 4000,  0,0,0, "Roll_Cnts_to_Volt"},
    {"Ro_mxI",MA_ro_max_amps,'B',SET_ME, 150,   50, 1000,  0,0,0, "Roll_Max_Amps"    },
    {"Ro_SN", MA_ro_sn,      'B',SET_ME,   1,    1, 9999,  0,0,0, "Roll_Ser_No"      },
    {"Ro_Ver",MA_ro_ver,     'B',SET_ME,   0,    0, 9999,  0,0,0, "Roll_Firmware_Ver"},
	
    // hydraulic settings ---------------------------------------
    {"MinPmp",IA_min_pump,   'H',SET_OK, 360,  300,  900,  0,0,0, "Min_Pump_Sec"     },
    {"MaxPmp",IA_max_pump,   'H',SET_OK, 600,  300, 1200,'C',0,0, "Max_Pump_Sec"     },
    {"Pmp50m",IA_pmp_50m,    'H',SET_OK,   8,    2,  300,'C',0,1, "Pump_50m"         }, //dec14
    {"BuoyPm",IA_buoy_pump,  'H',SET_OK, 150,   50,  400,'C',0,2, "Buoy_Pump_Sec"    },
    {"DtPmp", IA_delta_pump, 'H',SET_OK,  35,   10,  100,'C',0,3, "Delta_Pump_Sec"   },
    {"wUpmin",IA_wmin_up,    'H',SET_OK,  10,    5,   30,'C',0,4, "Wmin_Up[cm/s]"    },
    {"MaxAmp",IA_max_amp,    'H',SET_OK, 300,  100,  500,'C',0,5, "Max_Pump_Amps"    },
    {"AmpAct",IA_amp_action, 'H',SET_OK,   1,    0,    2,'C',0,6, "Bad_Amp_Action"   },
    {"OilMax",IA_oil_max,    'H',SET_OK,2000,  200, 4000,  0,0,0, "Max_Oil_Counts"   },
    // vent control
    {"VntCtl",IA_cntl_vent,  'H',SET_OK,   0,    0, 1000,'5',0,0, "Vent_Control"     },
    {"Vnt_Tm",IA_tm_vent,    'H',SET_OK,   0,    0,   40,'5',0,1, "Vent_Evac_Sec"   },
    // msp430 eeprom settings
    {"H_c2i", MA_hyd_c2i,    'H',SET_ME,  61,    1, 4000,  0,0,0, "Hyd_Cnts_to_Amps" },
    {"H_c2v", MA_hyd_c2v,    'H',SET_ME,  67,    1, 4000,  0,0,0, "Hyd_Cnts_to_Volt" },
    {"HydSN", MA_hyd_sn,     'H',SET_ME,   1,    1, 9999,  0,0,0, "Hyd_Ser_No"       },
    {"HydVer",MA_hyd_ver,    'H',SET_ME,   0,    0, 9999,  0,0,0, "Hyd_Firmware_Ver" },
    
    // route settings -------------------------------------------
    {"Nroute",IA_num_route,  'R',SET_OK, 1, 1,MAX_WAYPTS,'W',0,0, "Num_Route"        },
    {"Rnow",  IA_route_now,  'R',SET_OK, 1, 1,MAX_WAYPTS,'W',0,1, "Route_Now"        },
    {"R_end", IA_route_end,  'R',SET_OK,   1,    1,    3,'W',0,2, "End_Route"        },
    {"Rdir",  IA_route_dir,  'R',SET_OK,   1,   -1,    1,'W',0,3, "Route_Dir"        },
    //route list is stored separately
    
    // Compass settings -----------------------------------------
    {"MagVar",IA_mag_var,    'N',SET_OK,   0,  -90,   90,'V',0,0, "Mag_Declination"  },
    {"UseMC", IA_use_moff,   'N',SET_OK,   1,    0,    2,'V',0,1, "User_Mag_Cal"     },
    {"Moff_X",IA_moff_x,     'N',SET_OK,   0, -999,  999, 0, 0,0, "Mag_Offset_X"     },
    {"Moff_Y",IA_moff_y,     'N',SET_OK,   0, -999,  999, 0, 0,0, "Mag_Offset_Y"     },
    {"Moff_Z",IA_moff_z,     'N',SET_OK,   0, -999,  999, 0, 0,0, "Mag_Offset_Z"     },
    
    // Steering control --------------------------------------
    {"UseSet",IA_use_set,    'S',SET_OK,   0,    0,    1,'D',0,0, "Use_Set_Corr."    },
    {"Cross", IA_cross,      'S',SET_OK,   0, -180,  180,'D',0,1, "Steer_Current"    },
    {"Set_ND",IA_set_nprof,  'S',SET_OK,   0,    0, 9999,'D',0,2, "Set_Nprofs"       },
    {"SetMin",IA_set_min,    'S',SET_OK,   0, -360,  360,'D',0,3, "Set_Min_Correct"  },
    {"SetMax",IA_set_max,    'S',SET_OK,   0, -360,  360,'D',0,4, "Set_Max_Correct"  },
    {"ManAng",IA_man_theta,  'S',SET_OK,   0,   -2,  360,'E',0,0, "Manual_Heading"   },
    {"Man_ND",IA_man_nprof,  'S',SET_OK,   0,    0, 9999,'E',0,1, "Manual_Nprofs"    },
    {"Str_Pt",IA_steer_pt,   'S',SET_OK,   0,    0, 1000,'T',0,0, "Steering_Pt_km"   },
    {"Str_ND",IA_steer_nprof,'S',SET_OK,   0,    0, 9999,'T',0,1, "Steer_Nprofs"     },
    
    // Mission settings -----------------------------------------
    {"MisID" ,IA_miss_id,    'M',SET_OK,   0,    0,32565, 0 ,0,0, "Mission_ID"       },
    {"Nprofs",IA_nprofs,     'M',SET_OK, 200,    1, 9999,'B',0,0, "Num_Profiles"     },
    {"Zmax0" ,IA_first_zmax, 'M',SET_OK, 100,   20, 1100,'0',0,1, "First_Dive_Zmax"  },
    {"Zmax",  IA_zmax,       'M',SET_OK, 100,   20, 1100,'8',0,0, "Max_Depth[dBar]"  },
    {"Tglide",IA_max_dive_tm,'M',SET_OK,  25,   10,  600,'8',0,1, "Max_Descent_Time" },
    {"N_Avg", IA_num_avg,    'M',SET_OK,   1,    1,   50,'8',0,2, "Npts_to_Avg"      },
    {"G_Ang", IA_glide_angle,'M',SET_OK,  17,    6,   40,'G',0,0, "Pitch_Angle[deg]" },
    // mission drift settings
    {"DrftTm",IA_drift_tm,   'M',SET_OK,   0,    0,32766,'4',0,0, "Drift_Minutes"    },
    {"DrftNS",IA_drift_ns,   'M',SET_OK,   1,    1,32766,'4',0,1, "Drift_N_Sample"   },
    // 0903 feb11 add drift initial pump time
    {"Tpmp0" ,IA_drift_pmp,  'M',SET_OK,  40,    1,  900,'4',0,2, "Drift_T_pump[s]"  },
    
    // Timing settings ------------------------------------------
    {"GPS_Tm",IA_gps_tm,     'T',SET_OK,  15,    0,   20,'M',0,0, "GPS_Time[m]"      },
    {"Argos", IA_argos_tm,   'T',SET_OK,  15,    0, 1440,'L',0,0, "Argos_Time[m]"    },
    {"Xmit0", IA_min_xmit_tm,'T',SET_OK,  15,   10,  600,'K',0,0, "Min_Xmit_Time[m]" },
      // mission start-up timing
    {"T_Dly", IA_delay_start,'T',SET_OK,  10,    0,32767,  0,0,0, "Start_Delay_Tm[m]"},
    {"T_strt",IA_try_start,  'T',SET_OK,   0,    0,32767,  0,0,0, "Start_Roll_Try"   },
    {"P_strt",IA_try_press,  'T',SET_OK,   0,    0, 9999,  0,0,0, "Start_Press_Hrs"  },
      // sbd modem timing
    {"SBD_Tm",IA_sbd_max_tm, 'T',SET_OK, 900,   60, 1800,'U',0,0, "Max_SBD_Time[s]"  },
    {"SBD_Pk",IA_sbd_pack_tm,'T',SET_OK,  60,   30,  200,'U',0,1, "SBD_Pkt_Wait[s]"  },

    // Exception settings ---------------------------------------
    {"No_GPS",IA_no_gps,     'E',SET_OK,  -1,   -3,  360,'M',0,1, "No_GPS_Heading"   },
    {"SatMax",IA_abort_max_sat,'E',SET_OK,24,    0,  100,  0,0,0, "Sat_Fail_Hr"      },
    {"GpsMax",IA_abort_max_gps,'E',SET_OK,24,    0,  100,  0,0,0, "GPS_Fail_Hr"      },
    {"PMP_?", IA_pmp_rcvry,  'E',SET_OK,   0,    0,    3,'P',0,0, "Pump_Exception"   },
    {"SBD_?", IA_sbd_rcvry,  'E',SET_OK,   1,    0,    3,'P',0,1, "SBD_Exception"    },
    {"Mx_RST",IA_max_reset,  'E',SET_OK,   0,    0,   10,'P',0,2, "Max_Num_Resets"   },
    // dec11 0903.6, op_mode added to end of cmd 'P'
    {"AMult", IA_argos_mult, 'E',SET_OK,   0,    0,   10,'L',0,1, "Argos_Multiplier" },
    {"AbtPmp",IA_abort_pump, 'E',SET_OK,   0,    0,    1,'L',0,2, "Abort_Pump_Try"   },
    
    //Vehicle/sensor settings -----------------------------------
    {"UseOpt",IA_use_opt,    'V',SET_OK,   1,    0,    1,'A',0,0, "Use_Optical"      },
    {"UseAlt",IA_use_alt,    'V',SET_OK,   0,    0,    1,'Z',0,0, "Use_Altimeter"    },
    {"AltZ1", IA_alt_z_1st,  'V',SET_OK,  50,    0, 1100,'Z',0,1, "Alt_1st_Use_Z"    },
    {"Alt_Z", IA_alt_z,      'V',SET_OK,  20,    0,   50,'Z',0,2, "Alt_Min_2_Bot"    },
    {"EngDec",IA_eng_ndec,   'V',SET_OK,   2,    0,  500,'H',0,0, "Eng_Decimate"     },
    {"EngMsk",IA_eng_mask,   'V',SET_OK, 255,    0,  255,'H',0,1, "Eng_mask"         },
    {"W0_gps",IA_wing0_GPS,  'V',SET_OK,   2,    0,    2,'S',0,0, "GPS_Wing_Ant_SW"  },
    {"W0_sbd",IA_wing0_SBD,  'V',SET_OK,   2,    0,    2,'S',0,1, "SBD_Wing_Ant_SW"  },
 #if ADP==1    
    //Doppler settings ------------------------------------------
    {"A_Zmax",IA_adp_zmax,    'D',SET_OK,500,    0,  600,'F',0,0, "ADP_Max_Depth"   },
    {"A_Cell",IA_adp_ncell,   'D',SET_OK,  5,    2,    5,'F',0,1, "ADP_Num_Cells"   },
    {"A_Clen",IA_adp_cellsize,'D',SET_OK,  4,    2,   10,'F',0,2, "ADP_Cell_Size"   },
    {"APulse",IA_adp_pulse,   'D',SET_OK,  8,    2,   10,'F',0,3, "ADP_Pulse_Len"   },
    {"A_SNR" ,IA_adp_minsnr,  'D',SET_OK, -1,   -1,    9,'F',0,4, "ADP_Min_SNR"     },
    {"ADP_ns",IA_adp_nsh,     'D',SET_OK,  0,    0,   28,'F',0,5, "ADP_N_Shallow"   },
    //Doppler, hard-coded
    {"ADP_k1",SA_adp_k1,      'D',SET_NO, K1_ABS,0,    0, 0, 0,0, "ADP_Bin1_ABScat" },
    {"ADP_k2",SA_adp_k2,      'D',SET_NO, K2_ABS,0,    0, 0, 0,0, "ADP_Bin2_ABScat" },
    {"ADP_BD",SA_adp_bd,      'D',SET_NO, BD,    0,    0, 0, 0,0, "ADP_Blank_Dist"  },
    {"Aalpha",SA_adp_alpha,   'D',SET_NO, ALPHA, 0,    0, 0, 0,0, "ADP_alpha"       },
 #endif
 #if NTK==1
    //NTK settings ----------------------------------------------
    {"N_Zmax",IA_ntk_zmax,    'D',SET_OK,500,   -1, 1000,'F','1',0, "NTK_Zmax"        },
    {"N_nc"  ,IA_ntk_nc,      'D',SET_OK, 15,    1,   60,'F','1',1, "NTK_#cells"      },
    {"N_cs"  ,IA_ntk_cs,      'D',SET_OK, 20,    5,   50,'F','1',2, "NTK_Cell_size"   },//may16 
    {"N_tmDZ",IA_ntk_tmDZ,    'D',SET_OK,  4,    1,  100,'F','1',3, "NTK_Ping_decim"  },
    {"N_tmCD",IA_ntk_tmCD,    'D',SET_OK,  2,    1,   60,'F','1',4, "NTK_#cells_avg"  },
    {"N_sr"  ,IA_ntk_sr,      'D',SET_OK,180,    1,  600,'F','1',5, "NTK_Sample_rate" },
    {"N_fwd" ,IA_ntk_fwd,     'D',SET_OK,  0,    0,    1,'F','1',6, "NTK_beam_orient" }, //aug14 added
 #endif
    
    //MSP430 watchdog settings ----------------------------------
    {"WD_Act",MA_wdt_action,  'W',SET_ME,  7,    0,  255,  0,0,0, "Wdog_Action"     },
    {"WDAttn",MA_wdt_attn,    'W',SET_ME,600,   10, 9999,  0,0,0, "Wdog_CF2_Attn_Ti"},
    {"WDtCf2",MA_wdt_cf2_rst, 'W',SET_ME,900,   10, 9999,  0,0,0, "Wdog_CF2_Rst_Ti" },
    {"WDtRst",MA_wdt_430_rst, 'W',SET_ME,920,   10, 9999,  0,0,0, "Wdog_430_Rst_Ti" },
    {"WDtMlt",MA_wdt_mult,    'W',SET_ME,  1,    0, 9999,  0,0,0, "Wdog_Ti_Mult"    },
    //MSP430 watchdog analog settings ---------------------------
    {"Wc2v14",MA_wdt_c2v_14,  'W',SET_ME, 66,    1, 9999,  0,0,0, "Wdog_C_to_V_14"  },
    {"Wc2v07",MA_wdt_c2v_07,  'W',SET_ME, 40,    1, 9999,  0,0,0, "Wdog_C_to_V_07"  },
    {"Wc2vac",MA_wdt_c2vac,   'W',SET_ME,967,    1, 9999,  0,0,0, "Wdog_Vac_gain"   },
    {"W_Voff",MA_wdt_Voff,    'W',SET_ME,3000,   1, 9999,  0,0,0, "Wdog_Vac_off"    },
    {"Wc2mv", MA_wdt_c2mv,    'W',SET_NO, IC2mV, 0,    0,  0,0,0, "Wdog_C_to_mV"    },
    //oct14, add sn, ver
    {"WD_SN", MA_wdt_sn,      'W',SET_ME,   1,    1, 9999,  0,0,0, "GPIO_Ser_No"     },
    {"WD_Ver",MA_wdt_ver,     'W',SET_ME,   0,    0, 9999,  0,0,0, "GPIO_Firm_Ver"   },

#if BENTHOS==1 || NPS==1
    //Benthos Acoustic modem settings ----------------------------------
    {"ACcomm",IA_acm_tm,      'L',SET_OK, 60,    0, 1440,'6',0,0, "ACM_comm[min]"   },
    {"ACRtry",IA_acm_try,     'L',SET_OK,  1,    0,    3,'6',0,0, "ACM_Rtries"      },
    {"AC_inc",IA_acm_binc,    'L',SET_OK,  0,    0,   10,'6',0,0, "ACM_auto_inc"    },
    {"ACIdle",IA_acm_rmdly,   'L',SET_OK, 60,    0,  180,'6',0,0, "ACM_idle_dly[s]" },
    {"ACXtry",IA_acm_mxtry,   'L',SET_OK,  3,    0,   10,'6',0,0, "ACM_Xtries"      },
    {"ACmode",IA_acm_mode,    'L',SET_OK,  0,    0,    2,'6',0,0, "ACM_mode"        },
    {"ACdpth",IA_acm_dpth,    'L',SET_OK, 20,    0,  800,'6',0,0, "ACM_depth"       },
    {"ACLpwr",IA_acm_lxmt,    'L',SET_OK,  8,    1,    8,'6',0,0, "ACM_Loc_pwr"     },
    {"ACptch",IA_acm_ptch,    'L',SET_OK,  0,-1000, 1000,'6',0,0, "ACM_ptch_chng"   },
#endif
/*
    //=== added jun15 for auto-degassing (adg) ===========================
    {"adgCyc",IA_adg_ncycle, 'H',SET_OK,   0,    0,   25,'5','1',0, "ADG_N_Cycles"  },
    {"adgInc",IA_adg_incr,   'H',SET_OK,   1,    1,   60,'5','1',1, "ADG_N_Incr"    }, 
    {"adgMod",IA_adg_modulo, 'H',SET_OK,  99,    0,  999,'5','1',2, "ADG_Modulo"    },
*/
    
    {"!END!!",IA_END, END_GROUP, SET_NO, 99,  99, 99, 0, 0,0, "End_Params" }   
}; //==========================================================================

short  set_default_param( void ) //********************************************
{ // set the Param_table to the default values
  // then read in the .cfg file to set to the most current values
  // dec11, set default value to 1, and eeprom values are updated after cfg read.
  // oct14, return 1 if config-file error,
  //        else return 0
  short j=0, addr ;  // start at beginning of the table
  short file_err;    // oct14 error if no file is present
  
  while ( Param_table[j].group != END_GROUP  &&  j< MAX_PARAM ) //=====
  { // set all to their default values, or value stored in eeprom
    addr = Param_table[j].addr; // address for this variable
    
    if ( addr>=0 && addr < MAX_PARAM) //-------------------
    { // then it is legal
      iparam[ addr ] = Param_table[j].x0; // =default value
      if ( Param_table[j].set_type == SET_EE )
      // then set the value from the EEPROM lookup
         set_eeprom_param( j, 0, 1 ); //if not found, force to the default

    } //---------------------------------------------------

    j++;
  } //=================================================================
  file_err = read_config( -1, TRUE ); // read in the config file settings
  //===the above does NOT update eeprom values, nor NV-RAM values
  
  // dec11, make sure that the eeprom values are as stored, not as in the .cfg
  j = 0; // step through all parameters
  while ( Param_table[j].group != END_GROUP  &&  j< MAX_PARAM ) //=====
  {
    addr = Param_table[j].addr; // address for this variable
    
    if ( addr>=0 && addr < MAX_PARAM) //-------------------
    { // then it is legal
      if ( Param_table[j].set_type == SET_EE )
         set_eeprom_param( j, 0, 1 ); //read in the value
         //dec11, forcing default to 1, so if bad eeprom, op-mode
         //will cause spurious interrupt
    } //---------------------------------------------------
    j++;
  } //=================================================================
  //===jan16, make sure the profile, surf # agree with non-volatile ram
  //=== this overrides anything that may have been in the .cfg file
  //=== thus maintaining counters across resets.
  iparam[ EA_surf_num ] = NvRamSurf_N;
  iparam[ EA_prof_num ] = NvRamProf_N;
  
  return (file_err);
} //***************************************************************************


void  set_eeprom_param( short j, short update, short xnew ) //*****************
{ // set a parameter value from a eeprom lookup
  // j = pointer into the Param_table
  // update = flag=TRUE to update the eeprom
  //          if FALSE, then will set it to the default value IF it is not in EEPROM yet.
  // xnew = new value to use
  // mods Oct11 for clearer logic, diagnostics 
  // dec11, make sure RAM value is always the same as EEPROM value
  short addr,  x, x0,   op_mode, write_ok;
  char name[LKNAMSIZ+1];
  
    strcpy(name, Param_table[j].name );  // name copy
    //n = strlen( name );
    addr   = Param_table[j].addr;   // address for this variable
    x = x0 = Param_table[j].x0;     // default value
    cprintf("EEPROM check for %s[%3d]  ", name, addr );
    write_ok = addr>=0 && addr < MAX_PARAM;
    if (!write_ok)
    { //--catch bad addr
       cprintf("ILLEGAL ADDRESS !!!\n");
       return;
    } //----------------
    
    op_mode = (short) VEEFetchLong( "OPMODE", NO_VEE_VAL); // Spray mode of op
    x       = (short) VEEFetchLong(     name, NO_VEE_VAL );
    //if (Verbose)  cprintf("OPMODE = %d\n", op_mode);
    
    //===catch special case of setting to default param===
    if (x == NO_VEE_VAL && !update && op_mode == 0 ) 
    {  // eeprom was NOT found, and NOT want to update ---
       // this is the set_default_param() special case
       // it is OK to update to the default value
       cprintf("%s NOT FOUND in EEPROM; forcing to default value\n",name);
       xnew = x0; // force it to the default value 
    } //--------------------------------------------------
    else
    { // NOT special case, only update if desired
      write_ok = update; // this would be false on RESET w/op_mode!=0
    } //--------------------------------------------------
    
    if (write_ok)
    { //-- then write the new value ------------
        VEEStoreLong( name, (long) xnew ); //write it
        x = (short) VEEFetchLong( name, NO_VEE_VAL ); //read it back
        cprintf("...EEPROM update... " );
        iparam[ addr ] = x; // store the value to ram as well
    } //----------------------------------------
    cprintf("= %d\n",x); // print the value
    
    //dec11, make sure no matter what, the eeprom value and ram agree
    x = (short) VEEFetchLong( name, NO_VEE_VAL ); //read it back
    iparam[ addr ] = x; // store the value to ram as well
    
  return;
} //***************************************************************************

void  set_surf_num ( short n ) //**********************************************
{  // set both the eeprom and iparam surfacing num to value n
  // updated jan16 to use non-volatile ram instead of eeprom
   // short x;
   // char name[] = "SURF_N";
   
  iparam[ EA_surf_num ] = n; // save
  NvRamSurf_N           = n; // save in non-volatile area
  
        /*
        x = (short) VEEFetchLong( name, NO_VEE_VAL );
        if (x == NO_VEE_VAL)
        {
          cprintf("SURF_N was NOT found??? Setting=1\n");
          iparam [ EA_surf_num ] = 1;
          x=1;
        }
        else
        {
           VEEStoreLong( name, (long) n );
           x = (short) VEEFetchLong( name, NO_VEE_VAL );
           iparam [ EA_surf_num ] = n;
        }
        */
      
   return;
} //***************************************************************************

void  set_prof_num ( short n ) //**********************************************
{  // set both the eeprom and iparam profile num to value n
  // updated jan16 to use non-volatile ram instead of eeprom
   // short x;
   // char name[] = "PROF_N";
   
  iparam[ EA_prof_num ] = n; // save
  NvRamProf_N           = n; // also save in non-volatile area
  
        /*
        x = (short) VEEFetchLong( name, NO_VEE_VAL );
        if (x == NO_VEE_VAL)
        {
          cprintf("PROF_N was NOT found??? Setting=1\n");
          iparam [ EA_prof_num ] = 1;
          x=1;
        }
        else
        {
           VEEStoreLong( name, (long) n );
           x = (short) VEEFetchLong( name, NO_VEE_VAL );
           iparam [ EA_prof_num ] = n;
        }
        */
      
   return;
} //***************************************************************************

void  set_op_mode ( short n ) //***********************************************
{  // set both the eeprom and iparam op_mode to value n
   short x;
   char name[] = "OPMODE";
   
        x = (short) VEEFetchLong( name, NO_VEE_VAL );
        if (x == NO_VEE_VAL)
        {
          cprintf("OPMODE was NOT found??? Setting=1\n");
          iparam [ EA_op_mode ] = 1;
          x=1;
        }
        else
        {
           VEEStoreLong( name, (long) n );
           x = (short) VEEFetchLong( name, NO_VEE_VAL );
           iparam [ EA_op_mode ] = n;
        }
   cprintf("OPMODE = %d\n",x);
   
   return;
} //***************************************************************************

short incr_surf_num ( void ) //************************************************
{ // increment the surfacing# by 1
  // updated jan16 to use non-volatile ram instead of eeprom
  short x, y, xbad, ybad;
  //char surf_n[] = "SURF_N";
  
  x = iparam[ EA_surf_num ]; // present RAM value
  y = NvRamSurf_N; // non-volatile backup value
  xbad = ( x<0 || x>32000 ); // TRUE if BAD
  ybad = ( y<0 || y>32000 ); // TRUE if BAD
  
  if ( xbad ) //======================================
  { // then local ram has been corrupted!
    if ( ybad )
    { // both are bad!
      x = 0; // reset to 0
    } else // else we think y is OK
    { x = y; } 
  } //================================================
  //=== no matter what, we now believe x
  x++; // increment the local copy
  iparam[ EA_surf_num ] = x; // save
  NvRamSurf_N           = x; // save in non-volatile area
  
     /*
     x = (short) VEEFetchLong( surf_n, NO_VEE_VAL );
      
      if (x == NO_VEE_VAL) //------------------------------------
      { // then NOT found !!
        cprintf("SURF_N NOT FOUND in EEPROM. Setting to 1 \n" );
        x = 1;
      } //-------------------------------------------------------
           
      // increment and store the value
      x++; // increment
      VEEStoreLong( surf_n, (long) x ); // store in eeprom
      iparam[ EA_surf_num ] = x; // store the value
      */
  
  return (x);
} //***************************************************************************

short incr_prof_num ( void ) //************************************************
{ // increment the dive# by 1
  // updated jan16 to use non-volatile ram instead of eeprom
  // char prof_n[] = "PROF_N";
  short x, y, xbad, ybad;
  
  x = iparam[ EA_prof_num ]; // present RAM value
  y = NvRamProf_N; // non-volatile backup value
  xbad = ( x<0 || x>32000 ); // TRUE if BAD
  ybad = ( y<0 || y>32000 ); // TRUE if BAD
  
  if ( xbad ) //======================================
  { // then local ram has been corrupted!
    if ( ybad )
    { // both are bad!
      x = 0; // reset to 0
    } else // else we think y is OK
    { x = y; } 
  } //================================================
  //=== no matter what, we now believe x
  x++; // increment the local copy
  iparam[ EA_prof_num ] = x; // save
  NvRamProf_N           = x; // also save in non-volatile area
  
  
      /*
      x = (short) VEEFetchLong( prof_n, NO_VEE_VAL );
      
      if (x == NO_VEE_VAL) //------------------------------------
      { // then NOT found !!
        cprintf("PROF_N NOT FOUND in EEPROM. Setting to 1 \n" );
        x = 1;
      } //-------------------------------------------------------
           
      // increment and store the value
      x++; // increment
      VEEStoreLong( prof_n, (long) x ); // store in eeprom
      iparam[ EA_prof_num ] = x; // store the value
      */
  
  return (x);
} //***************************************************************************

short find_name (char name[], struct lookup *t ) //****************************
{ // search for name in the lookup table
  // returns the lookup index if found,
  // returns -1 if NOT FOUND
  short j=0, k, done, n;
  
  k = strlen(name);
  if (k>LKNAMSIZ) k=LKNAMSIZ;
  
  do //================================================================
  { // look for a comparison
    n = strlen(  t[j].name );
    if (n == k) //+++++++++++++++++++++++++++++
    { // then the correct length
       if ( !strncmp(t[j].name, name, k) )
          return( j );//found it !!!
    } //+++++++++++++++++++++++++++++++++++++++
    // else not a match, go to the next entry
    j++;
    done = !(strncmp( t[j].name,"!END!!",LKNAMSIZ));
      
  } while ( !done  && j<MAX_PARAM ); //================================
     
  return(-1); // else unable to find
}  //**************************************************************************

short   check_nshore     ( void    ) //****************************************
{ //check to make sure NSHORE is large enough JAN14
  //return:
  //  -1  -> end not found
  //   0  -> NSHORE not long enough
  //   1  -> NSHORE long enough
 short i=-1;
 

 //===  make sure NSHORE is big enough ==================
 while (shore[i].cmd != END_GROUP )i++;

 if (i>N_SHORE)
  {
   cprintf("!!!! N_SHORE=%d commands is LESS THAN %d commands defined !!!!\n",
             N_SHORE,i);
   return(0);
   }
 if (i<=N_SHORE)
   { 
    //cprintf("N_SHORE=%d commands is >= %d commands defined\n",
    //         N_SHORE,i);
    return(1);
   }
   
  return(-1);
} //**************************************************************************

short find_addr( short addr, struct lookup *t ) //*****************************
{ // Find the index that matches the desired address

  // returns the index into the lookup table
  // returns -1 if it was not found
  short j=0;
  
  do //++++++++++++++++++++++++++++++++++++++++++++++++++++
  {  // search for a match
    if ( t[j].addr == addr ) return(j); //match found!
  } while (j++ < MAX_PARAM); //++++++++++++++++++++++++++++
  
  return(-1);  // here if no match was found
}  //**************************************************************************

void display_info( short addr, struct lookup *t ) //***************************
{ // display addr, value and label name for addr in lookup
  // searches the  lookup table for the same addr and then displays label
  short j;
  char name[LKNAMSIZ+1];
  
  j = find_addr( addr, t ); // look for the address
  if (j>=0 && j<MAX_PARAM ) //===================================
  {  // then it was found,
     cprintf("  %3d : %5d ", addr, iparam[addr] ); // index & value
     fill_name ( t[j].name, name );
     cprintf("%s %s\n", name, t[j].label );
  } //===========================================================
  else // NOT FOUND
  {  printf("Addr %3d was NOT found \n", addr ); }
  
  return;
} //***************************************************************************

void fill_name( char *in, char *out ) //***************************************
{ // blank-fill after the end of name
  // updates the out string
  short  k, n;  // start at beginning of the table

       strcpy( out, in );
       n = strlen(out);
       if (n>LKNAMSIZ) n=LKNAMSIZ;

       for (k=n; k<LKNAMSIZ; k++) out[k] = ' ';
       out[LKNAMSIZ] = 0;  //end-str char
       
  return;
} //***************************************************************************

void write_header (FILE *fp ) //***********************************************
{ // display header info = various version info
  // used for output files
  char s[90];
  
  sprintf(s,"#sn%04d, Ver. %5d,%3d, Shore %3d, Prof# %4d, Ux_Ti %10ld",
    iparam[EA_serno], iparam[SA_major_ver], iparam[SA_minor_ver],
    iparam[SA_shore_ver], iparam[EA_prof_num], RTCGetTime(0,0)  );

    if (fp == NULL || fp == stdin )
      cprintf("%s\n", s);
    else
      fprintf(fp,"%s\r\n", s);

  return;
} //***************************************************************************

void  display_all (FILE *fp, struct lookup *t ) //*****************************
{ // display all parameters, by group
  short i=0;
  char line[80];
  
  
  write_header( fp );
  
  while ( i<NGROUP && group[i].group != END_GROUP )
  {
    sprintf(line,"> Parameter group[%02d] %c= %s",i, group[i].group, group[i].label );
    
    if (fp == NULL || fp == stdin )
      cprintf("%s\n", line);
    else
      fprintf(fp,"%s\r\n", line);
      
    display_group(fp, group[i].group, t);
    i++;
  }
  
  //0902, display waypoint and route file as well
  display_route( FALSE );
  waypt_display( FALSE );
  
  return;
} //***************************************************************************


void  display_group(FILE *fp, short igroup, struct lookup *t ) //**************
{ // display all lookup parameters in group i
  short j=0, addr, k, n ;  // start at beginning of the table
  char line[80]; // output line
  char name[LKNAMSIZ+1];
  
  while ( t[j].group != END_GROUP  &&  j< MAX_PARAM ) //================
  {
    if ( t[j].group == igroup ) //------------------------------
    {  // then it's part of the  group, display
       addr = t[j].addr;
       
       if (addr>=0 && addr < MAX_PARAM ) 
       { // then legal!
         line[0] = '!'; // delimiter
         strcpy( name, t[j].name );
         n = strlen(name);
         if (n>LKNAMSIZ) n=LKNAMSIZ;
         for (k=0; k<n; k++) line[k+1]= name[k];
         for (k=n; k<LKNAMSIZ; k++) line[k+1] =' ';
         sprintf(&line[LKNAMSIZ+1],"%4d %7d %s",
                  addr, iparam[ addr ], t[j].label  );
         if (fp == NULL || fp == stdin )
           cprintf("%s\n", line);
         else
           fprintf(fp,"%s\r\n", line);
       }
       else // !not legal,,,what gives??
       {
         cprintf("Illegal addr=%d at j=%d\n",addr,j);
       }
    } //---------------------------------------------------------

    j++;
  } //=================================================================
  
  return;
} //***************************************************************************

void modify_group ( struct lookup *t ) //**************************************
{ // modify values within a group until done
  // since only called via the user menu, 
  // it DOES allow changing an eeprom value
  short i, ig=-1, addr, j, n, reply, ok;
  char gc;

  do // ===================================================================
  { //display group selections until done
    i=0;
    while ( i<NGROUP && group[i].group != END_GROUP )
    { //----------------------------------------------
      cprintf("> Parameter group[%02d] %c= %s\n",
               i, group[i].group, group[i].label );
      i++;
    } //----------------------------------------------
    
     cprintf("give group character to modify (q to quit) >\n");
	 SerInFlush();  // flush any characters
	    gc = -1; // illegal value - means it timed out
	    gc = SCIRxGetCharWithTimeout(18000 ); // get a new character
        gc = ch_l2u( gc ); // upper-case

     //--- see if gc is legal -------------------------
     ig = -1; // illegal group index
     i = 0; // search for char gc
     while ( i<NGROUP && group[i].group != END_GROUP )
     { //----------------------------------------------
       if ( group[i].group == gc ) // MATCH!
       { ig = i;  i=NGROUP; }
       i++;
     } //----------------------------------------------
     if ( ig== -1 ) return; // NOT legal, quit --------
     // else, legal, proceed with mods

     do //++++++++++++++++++++++++++++++++++++++++++++++++++++
     { // keep working with this group until illegal addr
       display_group(stdin, group[ig].group, t);
       cprintf("Give addr # to modify > \n");
       n = get_num(&addr); //addr to modify
       j = find_addr( addr, t ); // look for corresponding index
       ok = ( j>=0 && t[j].group == gc );
       
       if (ok )
       { // then legal & in right group -----------------
         display_info( addr, t );
         if ( addr == IA_miss_id ) //,,,,,,,,,,,,,,,,
         { change_miss_id ( j, t ); }
         else //,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
         { // get the new value
           cprintf("Give the new value > \n");
           n = get_num(&reply);
           if (n==1) // then legal
             change_param(j, reply, TRUE, t, FALSE );
         } //,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
       } //----------------------------------------------  
       
     } while ( ok ); //+++++++++++++++++++++++++++++++++++++++   
       
  } while (ig>=0); //======================================================
  
  return;
} //***************************************************************************

void  change_miss_id  ( short j,  struct lookup *t ) //************************
{ // the mission ID is an encoded word of yy:mm:id  : get info here
  short  n1, n2, n3, m1=0, m2=0, m3=0, x;

	  cprintf("Give new year value (0..100) >\n");
	  n1 = get_num(&x);
	  if (n1==1) m1 = x;
	  
	  cprintf("Give new month value (1..12) >\n");
	  n2 = get_num(&x);
	  if (n2==1) m2 = x;
	  
	  printf("Give mission id (0..15) >\n");
	  n3 = get_num(&x);
	  
	  if ( n1==1 && n2==1 && n3==1 ) //----------------
	  { // create new mission id
	    m3 =  x & 0x000f;  // save lsnibble
	    m2 = m2 & 0x000f;  // save lsnibble
	    m1 = m1 & 0x00ff;  // save lsb
	    x =  (m1 << 8) +  (m2 <<4) + m3;
	    cprintf("writing out %d = %02d/%02d:%02d\n",
	        x, m1, m2, m3);

        change_param( j, x, TRUE, t, FALSE );
      } //---------------------------------------------
      else
      { cprintf("NOT VALID - mission ID was not updated\n"); }
              
  return;
} //***************************************************************************

short find_name_addr(char name[], struct lookup *t )  //***********************
 //look up name and return the address.
{//returns -1 if unable to find name
  short i;
  
  i=find_name(name, t);//get index, -1=not found
  if (i > -1) i = t[i].addr; // set i to the address
  
  return(i);  
} //***************************************************************************

short change_param( short j, short x, short ee_update, struct lookup *t,
                    short display ) //***
{ // change parameter in lookup table[j] to value x
  // if it resides in eeprom and want to update, ee_update must=TRUE
  // if display==true, then the update is displayed if valid

  // returns the new value : -1 if not valid
  short val =-1; // init to not-valid number
  short addr, set_type;
  short imin, imax;
  char str[80], name[LKNAMSIZ+1];
  
    set_type = t[j].set_type;
    if ( set_type>= SET_OK ) //------------------------------
    { // then OK to change the value
      addr = t[j].addr; // address
      imin = t[j].imin; // min bound
      imax = t[j].imax; // max bound
      if ( x>=imin  && x <=imax ) //..................
      { // yes, we can change the parameter
        iparam[ addr ] = x; // set to the new value
        fill_name( t[j].name, name);
        if (display) cprintf("%5d = %s\n", x, name ); // display the update
        sprintf(str,"!%s %4d %7d %d %ld",
            name, addr, x, iparam[ EA_prof_num ], RTCGetTime(0,0) );
        if ( all.fp_sco !=NULL )
          fprintf(all.fp_sco,"%s\r\n", str );
        // jan16, if non-volatile, update the NV_RAM as well
        if ( set_type == SET_NV && ee_update) //,,,,
        { // ee_update must also ==true to update values
          if ( addr == EA_prof_num ) NvRamProf_N = x;
          if ( addr == EA_surf_num ) NvRamSurf_N = x;
        } //,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
      } //............................................
      val = iparam [ addr ];
    } //-----------------------------------------------------
    else if ( set_type == SET_EE && ee_update)
    { // eeprom-type and we want to modify
      addr = t[j].addr; // address
      imin = t[j].imin; // min bound
      imax = t[j].imax; // max bound
      if ( x>=imin  && x <=imax ) //..................
      { //dec11, checked bounds, update
        set_eeprom_param( j, ee_update, x );
        val = iparam [ addr ]; //dec11, updated value
        //--dec11, write to the change-file
        fill_name( t[j].name, name);
        if (display) cprintf("%5d = %s\n", val, name ); // display the update
        sprintf(str,"!%s %4d %7d %d %ld",
            name, addr, val, iparam[ EA_prof_num ], RTCGetTime(0,0) );
        if ( all.fp_sco !=NULL )
          fprintf(all.fp_sco,"%s\r\n", str );
      } //............................................
    } //-----------------------------------------------------
      

  return( val ); // and return
} //***************************************************************************


short change_name_param( char name[], short x, struct lookup *t ) //***********
{ // change variable 'name' value to x
  // this is only called from the user menu, so it WILL allow
  // changing an eeprom value

  // returns the new value : -1 if not valid
  short val =-1; // init to not-valid number
  short  j;
  
  j = find_name( name, t ); // find the name in the lookup table
  
  if (   j>-1) //================================================
  { // then it was found 
    val = change_param( j, x, TRUE, t, FALSE );
    
  } //===========================================================
  

  return( val ); // and return
} //***************************************************************************

void  lifo_store_param  ( void )  //*******************************************
{ //** store Spray parameters
  // in param.c so has access to the Param_table
  short i, j, addr, k, n=0;  
  uchar dat[512];
    
  // MAJ_VER serves as the version for this packet type
  dat[0] = SBD_PARAM; //assign id = 0xd3 = Spray settings
  // dat[1], dat[2] = #bytes in the message; set at end of fnx
  // dat[3], dat[4] = #of entries in the parameter list
  i = b2_store(5,dat,MAJ_VER );
  // i = 7; // eeprom entries follow
    
  j=0; // start @ 1st entry ---------------------------------------
  while ( Param_table[j].group != END_GROUP  && j<IA_END  ) //-----
  { // go thru the whole table ------------------------------------

    { // could send only a subset if desired.=============
        addr = Param_table[j].addr;
        k    = iparam [ addr ]; // value
        dat[i++] = addr; // store the address
        i = b2_store(i,dat, k ); // store the value & increment i+=2;
        n++; // keep track of # written out
    } //===================================================
    
    j++; // look at next entry
  } // end while --------------------------------------------------
  
  dat[i++] = ';' ; // end-of-data char

  // n= # out
  j = i; //=# of bytes to transmit : includes 0,1,2 bytes + ';'
  i = b2_store(1,dat,j ); //=#bytes to transmit
  i = b2_store(i,dat,n ); //=# of entries
  
  append_lifo( j, dat ); // append to present lifo
  all.sbd->send_param = FALSE; // no longer request to send eeprom

  return;
} //***************************************************************************



// shore command section ******************************************************
//*****************************************************************************

void  display_shore_cmd ( void ) //********************************************
{ // display all parameters, by group
  short i=0, sub;
  
  while ( i<N_SHORE && shore[i].cmd != END_GROUP )
  {
    sub = shore[i].sub; // sub-cmd
    if (sub<' ') sub=' '; // make legal value
    cprintf("> Cmd %c%c: [Narg=%1d-%1d] %s \n",
       shore[i].cmd, sub,  shore[i].imin, shore[i].imax, shore[i].label );

    i++;
  }
  return;
} //***************************************************************************


short  test_shore_cmd ( void ) //**********************************************
{ // display all parameters, by group
  short i=0, c0=0, sub=0, n, err;
  char str[80];
  
  cprintf("give command >\n");
  gets(str); // read in the name
  n = strlen(str);
  if (n==0) return ( CMD_NO );
  
  c0= str[0]; // cmd
  if (n>1) sub = str[1]; // sub-cmd
  
  if (n<3) display_cmd( c0, sub ); // display the command 
  else err = parse_cmd(  str );
  

  return ( err );
} //***************************************************************************

void  display_cmd( char c1, char sub ) //**************************************
{ // display the shore command c0, sub-command sub

  short i=0, done=0, match, k, imin, imax, def;
  
  //make case-insensitive
  c1 = ch_l2u( c1 );
  sub = ch_l2u( sub);
  
  while ( i<N_SHORE && !done ) //==========================================
  { // look for a match in the list of shore commands
    match = ( c1 == shore[i].cmd ) && ( sub == shore[i].sub);
    
    if (match)
       cprintf("> Cmd %c%c: %s, N_Arg_Min=%d, N_Arg_Max=%d, \n",
       c1, sub, shore[i].label, shore[i].imin, shore[i].imax );
       
    done = ( shore[i].cmd == END_GROUP) || match;
    i++;
  } //=====================================================================
  
  if (!match) cprintf("Command was not found\n");
  
  // else found a match
  i=0; // reset index to now search the lookup table Param_table[]
  while ( Param_table[i].group != END_GROUP  &&  i< MAX_PARAM ) //=========
  { // search for the correct cmd/sub-cmd
    match = ( c1 == Param_table[i].c1 ) && ( sub== Param_table[i].c2 );
    if (match) //--------------------------------------------
    { // then display the argument parameters
      k =   Param_table[i].i1; // argument index
      imin= Param_table[i].imin; // minimum value
      imax= Param_table[i].imax; // maximum value
      def = Param_table[i].x0;   // default value
      cprintf("arg[%d] = %s:  range= %d<=value<=%d : default=%d,  %s\n",
         k, Param_table[i].name, imin, imax, def, Param_table[i].label );
    } //-----------------------------------------------------
    i++;
  } //=====================================================================
  
  return;
} //***************************************************************************

short  parse_cmd( char str[] ) //**********************************************
{ // parse the shore command c1, sub-command sub
  // str = full command
  
  // returns an error status
  // 0=CMD_OK = no errors
  // 1=CMD_NO = command not recognized
  // 2=CMD_NARG = not the correct # of arguments
  // 3=CMD_BOUND = value is out-of-range.
  // 4=CMD_WRITE = error trying to change the value
  

  short i=0, done=0, match, k, imin, imax, x0, narg, val;
  short err=0, ee_update = 0;
  short x[20]; // argument array
  char c2=' '; // display-character for the sub-command
  char c1, sub;
  
  //First character = main command char
  c1  = str[0];
  //2nd character = sub-command (' ' = none )
  sub = str[1];
  
  //make case-insensitive
  c1 =  ch_l2u( c1 );
  sub = ch_l2u( sub);
  // catch illegal sub-commands
  if (sub <=' ' || sub>'~' || sub==';' || sub=='$') sub=0;
  if (sub>0) c2=sub; // display char
  
  if (Verbose) cprintf("...searching for %c%c  \n", c1,c2  );
  
  while ( i<N_SHORE && !done ) //==========================================
  { // look for a match in the list of shore commands
    match = ( c1 == shore[i].cmd ) && ( sub == shore[i].sub);
    
    
    if (match)
    { // get the min and max # of arguments
       imin = shore[i].imin;
       imax = shore[i].imax;
    }
       
    done = ( shore[i].cmd == END_GROUP) || match;
    if (!done) i++;
  } //=====================================================================
  
  if (Verbose)
     cprintf("cmd %c%c ,match=%d, min=%d max=%d\n",c1,c2 ,match, imin,imax);
  if (!match) return( CMD_NO ); // return that it was NOT found
  
  
  narg = read_shorts( &str[2], imax, x ); // read in the params
  
  if (Verbose)
  { // print out the param list
    cprintf("Read in %d args\n",narg);
    for (k=0;k<narg;k++) cprintf("%d %4d\n",k,x[k] );
  }
  
  if (narg<imin )
  { // then NOT ENOUGH arguments
    cprintf("bad #arg: %s\n", str);
    return ( CMD_NARG );
  }
  
  if ( shore[i].fnx >0 ) //================================================
  { // then this cmd requires a specific function call
    switch( c1 ) //++++++++++++++++++++++++++++++++++++++++++++++++++++
    { // call the correct function dependent upon cmd
      case '0'   : // RESET
              err = check_passwd(  x[0] );
              if (!err) 
              { // then password was OK, set the RESET flag.
                cprintf("Mission reset flag is set\n");
                set_op_mode(-1); // nov10 flag that issuing reset
                all.exc->reset = TRUE;  // set flag to reset
              }
              break;
      case '1'   : // ABORT
              err = check_passwd(  x[0] );
              if (!err) 
              { // then password was OK, set the ABORT flag,
                cprintf("Mission abort flag is set\n");
                all.exc->abort = 9;  // aug15, 9=remote-abort cmd
              }
              break;
      case '2'   : // change a specific address entry
              err = cmd_2( sub, narg, x );
              break;
      case '3'   : // change a waypoint entry : floating point #
              err = parse_waypt( sub, &str[2] );
              all.sbd->save_waypt = TRUE; // flag to write new waypt file
              all.sbd->calc_route = TRUE; // set flag to recalculate
              break;
      case '6'   : // change Acoustic Modem parameters
              err = cmd_6( &str[0] );
              break;
      case '7'   : // change a route entry
              err = cmd_7( sub, narg, x );
              all.sbd->save_route = TRUE; // flag to save new route file
              all.sbd->calc_route = TRUE; // set flag to recalculate
              break;
      case 'D'   : // dec11, current-crossing; turn off manual
              val = x[1];  // current-crossing angle; if>0, turn OFF manual heading
              if (val != 0) iparam [ IA_man_nprof ] = 0; //command to turn ON, not off
              break;
#if ZOOG==1  //=== oct16 add shore commands for zooGlider
      case 'F'   : // mar11, parse polyplus data
              if      (sub=='2') { err = cmd_F2( narg, x ); }
              else if (sub=='3') { err = cmd_F3( narg, x ); }
              break;
#endif
      case 'L'   : // request to change Argos parameters
              all.exc->nose_down = 0; // reset nose_down=0
              all.exc->rfx       = 1; // reset multiplier
              break;
      case 'M'   : // dec11 request to change GPS params
              if ( narg==3 ) //----------------
              { //then includes cold-reset param
                all.exc->gps_cold = x[2];
                narg=2; //so below won't try to parse this one
                cprintf(" gps_cold = %d\n", x[2] );
              } //-----------------------------
              break;
#if PPLUS==1  //add polyplus shore cmd fnxs
      case 'O'   : // mar11, parse polyplus data
              if      (sub== 0 ) { err = cmd_O_( narg, x ); }
              else if (sub=='1') { err = cmd_O1( narg, x ); }
              break;
#endif
      case 'R'   : // request to resend a dive #
              retransmit_lifo( x[0] ); // x[0]=dive #
              break;
      case 'W'   : // change the route parameters
              all.sbd->save_route = TRUE; // flag to save new route file
              all.sbd->calc_route = TRUE; // set flag to recalculate
              // the below while loop will update the params
              break;
      case '@'   : // request the route to be queued
              all.sbd->send_waypts = TRUE; // set flag to send waypoints
              break;
      case '!'   : // request the parameter list
              all.sbd->send_param = TRUE; // set flag to send all param
              //dec11, update the file as well
              write_config (-1); // rewrite .cfg, saving params if reset
              cprintf("updated the cfg file\n");
              break;
      
      default: err = CMD_NO_FNX; // NO function
    } //+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
    
    
    if (shore[i].fnx==2  || err>0) return( err );
    // return if there is a parsing error, or no other params to parse
  } //=====================================================================
  
  // else time to parse ===================================================
  err = 0; // no errors so far
  i=0; // reset index to search the lookup table Param_table[]
  while ( Param_table[i].group != END_GROUP  &&  i< MAX_PARAM ) //=========
  { // search for the correct cmd/sub-cmd
    //cprintf("table[%d].group = %c\n",i,Param_table[i].group);
    //DelayMilliSecs(5);
    match = ( c1 == Param_table[i].c1 ) && ( sub== Param_table[i].c2 );
    //DelayMilliSecs(5);
    //cprintf("%d %04x %04x %04x %04x\n",match, c1,Param_table[i].c1,sub,Param_table[i].c2);
    if (match) //--------------------------------------------
    { // then read the argument parameters
      k =   Param_table[i].i1; // argument index
      imin= Param_table[i].imin; // minimum value
      imax= Param_table[i].imax; // maximum value
      
      if (k<narg) //kkkkkkkkkkkkkkkkkkkkkkkkkkk
      { // then legal index
        x0 = x[k]; // input value
        if ( imin<=x0  && x0<=imax )
        { // then it is legal
          ee_update =  ( c1 == 'P' ); 
          val = change_param( i, x0, ee_update, Param_table, TRUE );
          // dec11 only allows 'P' to update eeprom
          if (val != x0) err = CMD_WRITE;
        }
        else err = CMD_BOUND;
      } //kkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkk

    } //-----------------------------------------------------
    i++;
  } //=====================================================================
  
  return ( err );
} //***************************************************************************

short  check_passwd( short x ) //**********************************************
{ // check the password value
  // return an error if bad, 0=if OK
  short err;
  
  err = ( x != PASSWORD );
  if (err) err = CMD_BOUND; //set flag that bad bounds
  
  return( err );
} //***************************************************************************

short  parse_waypt ( short sub, char str[] ) //********************************
{ // parse shore command 3 = change a value in the waypoint list
  // sub = sub-command..presently not used
  // shore command 3 form = 3 <iw> <lat> <lon>
  // <iw> = waypoint index, <lat>, <lon> are decimal degrees
  
  short i, iw, err=0;
  float xlat, xlon;
  struct s_waypt *waypt;
  
  waypt = all.nav->waypt; // point to the waypoint structure
  //route = all.nav->route; // point to the route structure

  i = sub; // dummy to keep compiler from complaining 
  i = sscanf(str,"%d %f %f",&iw, &xlat,&xlon); // read in values
  
  if (i==3) //=========================================================
  {  // right # of arguments: try processing

     err != insert_waypt(waypt, iw, xlat, xlon); // err=0 if good
     
     if (err) //-------------------------------------------------
     { err = CMD_BOUND; } // set to bounds problem
     else
     { // no error, update what's needed
        if ( iw== iparam[ IA_route_now ] ) // updated present waypt
           all.nav->bear = -999;  // = Gets updated in check_waypt.
        if (!verify_route() ) // called to update all values
           err = CMD_ROUTE; // flag as something bad with the route
     } //---------------------------------------------------------
     
  }  
  else err = CMD_NARG; //==============================================

  return( err );
} //***************************************************************************

short cmd_7( short sub, short narg, short x[] ) //*****************************
{ // parse shore command 7 = change individual route info
  // sub = sub-command..presently not used
  // narg = # of arguments in x
  // x = array of data
  // shore command 7 form = 7 <ir> <iw> <app_angle> <detect> <range>
   short ir, iw, a, d, r, err=0;
   struct route_param *route;
   
   route = all.nav->route; // point to the route structure
   a = sub; // use to keep compiler from complaining
   
   ir = x[0];  // route entry index
   iw = x[1];  // waypoint index to link to ir
   
   //========================================================================
   if (     ( narg >=2 )
        &&  ( ir>=0  && ir <= iparam[ IA_num_route ]  ) // legal
        &&  ( iw>=0  && iw  < MAX_WAYPTS ) // legal index
      ) // in bounds, OK to process
   { // assign the values //================================================
      // assign the defaults first
      route->rlist [ir] = iw; // link route entry ir to waypoint iw
      route->blist [ir] = 0; // approach angle: 0 = auto-range
      route->detect[ir] = 1; // watch-circle or finish line detect
      route->circle[ir] = 0; // 0 = auto-range on the watch-circle
      
      switch( narg ) //++++++++++++++++++++++++++++++++++++++++++++++++
      { // use what ever settings are present
        case 5 : r = x[4]; // watch-circle radius
          if  ( r>=0 && r<20 )  route->circle[ir] = r;
          // no break: process the next as well
        case 4 : d = x[3]; // detect mode
         // detect decides when near a waypoint
         // 0=range only, 1=range or finish line, 2=finish line only
          if  ( d>=0 && d<3 )  route->detect[ir] = d;
          // no break: process the next as well
        case 3 : a = x[2]; // approach angle
          if  ( a>=0 && a<361 )  route->blist[ir] = a;
          break;
      } //+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
      
      if ( ir == iparam[ IA_route_now] ) 
            // we're updating the present waypt info
            all.nav->bear = -999; // set to a value so a new bearing
            // to this waypt is updated correctly in check_waypt
   } //====================================================================
   else
   {  err = CMD_BOUND; }// set that it failed due to error
     
      cprintf("@r%d: Waypt %d,approach=%d  detect= %d range= %d, err=%d\n",
              ir, iw, a, d, r, err );
  
  return( err );
} //***************************************************************************

#if PPLUS==1  //add polyplus shore cmd fnxs
short cmd_O_(  short narg, short x[] ) //*****************************
{ // parse shore command O = polyplus cell general settings : mar11
  // narg = # of arguments in x
  // x = array of data
  // shore command O form = O <icell> <nload> <tstart> <idecimate> 
   short icell, nload, tstart, kdec, err=0;

   
   icell = x[0];  // which cell we're setting (0 or 1)
   nload = x[1];  // #of load settings
   tstart= x[2];  // start delay before applying the first load
   kdec  = x[3];  // decimation for sending the data back
   
   //========================================================================
   if (     ( narg ==4 )
        &&  ( icell>=0  && icell  <= 1       )  // legal
        &&  ( nload>=0  && nload  <= PP_LMAX )  // legal 
      ) // in bounds, OK to process
   { // assign the values //================================================
     all.pp->cell[icell].nload  = nload;
     all.pp->cell[icell].tstart = tstart;
     all.pp->cell[icell].kdec   = kdec;
   } //====================================================================
   else
   {  err = CMD_BOUND; }// set that it failed due to error
     
      cprintf("cmd_0_, icell=%d, Nload=%d, Tstart=%d, kdec=%d, err=%d\n",
              icell, nload, tstart, kdec, err );
  
  return( err );
} //***************************************************************************
#endif // end pplus

#if PPLUS==1  //add polyplus shore cmd fnxs
short cmd_O1(  short narg, short x[] ) //**************************************
{ // parse shore command O1 = polyplus cell specific settings : mar11
  // narg = # of arguments in x
  // x = array of data
  // shore command O form = O1 <icell> <iload> <load> <secs> 
   short icell, iload, load, secs, nload=0, err=0;

   
   icell = x[0];  // which cell we're setting (0 or 1)
   iload = x[1];  // which load index
   load  = x[2];  // the actual load setting
   secs  = x[3];  // #of seconds to apply the load
   
   if (icell==0 || icell==1 ) nload = all.pp->cell[icell].nload;
   // else nload = 0
   
   //========================================================================
   if (     ( narg ==4 )
        &&  ( icell>=0  && icell  <= 1       )  // legal
        &&  ( iload>=0  && iload  <  nload   )  // legal 
        &&  (  load>=0  &&  load  <= 4       )  // legal 
        &&  (  secs>=0                       )  // legal 
      ) // in bounds, OK to process
   { // assign the values //================================================
     all.pp->cell[icell].t[iload]   = secs;
     all.pp->cell[icell].L[iload]   = load;
   } //====================================================================
   else
   {  err = CMD_BOUND; }// set that it failed due to error
     
      cprintf("cmd_01, icell=%d, iload=%d, load=%d, secs=%d, err=%d\n",
              icell, iload, load, secs, err );
  
  return( err );
} //***************************************************************************
#endif

// ****************************************************************************
#if ZOOG==1  // **** oct16 add zooGlider shore commands ***********************
short cmd_F2(  short narg, short x[] ) // *************************************
{ // parse shore command F2 = zonar ping parameters
  // narg = # of arguments in x
  // x = array of data
  // shore command F2 form = F2 <nPings> <tBin> <dtBurst>
  short nPings, tBin, dtBurst, err=0;
  struct zonar_param *zs;
  
  zs = all.zonar; // point to the zonar struct

   
   nPings  = x[0];  // # pings per burst
   tBin    = x[1];  // ms of data to average into 1 satellite output bin.
   dtBurst = x[2];  // seconds between burst (0=manually triggered)
   
   //=== could do error checking here.
   if (narg>3) narg=3; // limit
   
   //=== store new variables ========
   if (narg>0) zs->nPings  = nPings;
   if (narg>1) zs->tBin    = tBin;
   if (narg>2) zs->dtBurst = dtBurst;
   zs->f2Num = narg; // value>0 tells zonar code to update these parameters.
   
     
  return( err );
} // **************************************************************************

short cmd_F3(  short narg, short x[] ) // *************************************
{ // parse shore command F3 = zonar beam parameters
  // narg = # of arguments in x
  // x = array of data
  // shore command F3 form = F3 <iBeam> <pulse> <scan> <ping> <blank>
  short iBeam, pulse, scan, ping, blank, err=0, n;
  struct zonar_param *zs;
  
  zs = all.zonar; // point to the zonar struct

   
   iBeam   = x[0];  // beam 1 = 200 kHz, beam 2 = 1 MHz
   pulse   = x[1];  // ms duration of ping
   scan    = x[2];  // ms duration of acquiring data (range[ms])
   ping    = x[3];  // ms between pings
   blank   = x[4];  // ms blanking distance
   
   if (iBeam<1 || iBeam>2 )
   { //== out of range =====
     err = CMD_BOUND;
     zs->f3Num = 0;
     return ( err );
   } //=====================

   //=== could do error checking here.
   if (narg>5) narg=5; // limit; narg = lsn of output char
   n = ( iBeam * 0x10 )  + narg;  // msn is beam #
   
   //=== store new variables ========
   if ( iBeam == 1 ) //++++++++++++++++++++++++++++
   { // change beam 1 values
     if (narg>1) zs->pulse1  = pulse;
     if (narg>2) zs->tScan1  = scan;
     if (narg>3) zs->tPing1  = ping;
     if (narg>4) zs->blank1  = blank;
   } else //+++++++++++++++++++++++++++++++++++++++
   { // change beam 2 values
     if (narg>1) zs->pulse2  = pulse;
     if (narg>2) zs->tScan2  = scan;
     if (narg>3) zs->tPing2  = ping;
     if (narg>4) zs->blank2  = blank;
   }  //+++++++++++++++++++++++++++++++++++++++++++
   zs->f3Num = n; // value>0 tells zonar code to update these parameters.

       
  return( err );
} // **************************************************************************
#endif // *** end if ZOOG *****************************************************
// ****************************************************************************



short cmd_2( short sub, short narg, short x[] ) //*****************************
{ // parse shore command 2 = change individual address
  // sub = sub-command..presently not used
  // narg = # of arguments in x
  // x = array of data
  // shore command 2 form = 2 <addr> <val>
  // dec11: this DOES allow updating eeprom value = op_mode
  
   short a, addr, x0, j, err=0, val;
   short imin, imax;
   short ee_update = FALSE; //dec11, do NOT allow eeprom updates
   a = sub; // use to keep compiler from complaining
   
   addr = x[0];  // iparam addr
   x0   = x[1];  // new value
   
   if ( narg != 2 ) return ( CMD_NARG );
   
    j = find_addr( addr, Param_table ); // look for corresponding index
    if (j>0) //==============================================================
    { // found the addr in the table,  read the bounds

      imin= Param_table[j].imin; // minimum value
      imax= Param_table[j].imax; // maximum value
      
        if ( imin<=x0  && x0<=imax )
        { // then it is legal
          ee_update =  ( addr == EA_op_mode );
          //dec11 v0903.6, allow eeprom change to the op mode
          val = change_param( j, x0, ee_update, Param_table, TRUE );
          if (val != x0) err = CMD_WRITE;
        }
        else err = CMD_BOUND;


   } //====================================================================
   else
   {  err = CMD_ADDR; }// set that it failed due to no addr
     
  
  return( err );
} //***************************************************************************

#if BENTHOS==1 || NPS==1
  #include  "BenthCF2.h"
  extern struct acm_command modem_cmd_str; //command string for remote modem

  short cmd_6(char *ch) //************************
  { // parse shore command 6 to alter params and/or ops of acoustic modem for CORC shuttle
    //GS sends cmdfmt <command string>
    //                   where cmdfmt specifies the format of command in <command string>
    //Command format was changed 28Mar10 for cmdfmt=1,2,3,4 
    //If cmdfmt = 0 <command string> is sent to the LOCAL modem without alteration
    //
    //#if BENTHOS ==1
    //If cmdfmt = 1 <command string> = IA_drift_tm IA_drift_ns btmsk[0-3]
    //   30Nov15 -- LAR removed parsing of this sub-command.  Since use of btmsk has been
    //                 removed, there is no need for this command. 
    //#endif
    //
    //#if NPS ==1
    //If cmdfmt = 1 <command string> = modem_on/off dummy[0-4] 
    //       modem_on/off    1 = turn on modem immediately
    //                       0 = turn off modem immediately
    //
    // retaining dummy var 1-4 so groundstation doesnt complain
    //#endif
    //
    //If cmdfmt = 2 <command string> = IA_acm_tm IA_acm_mode IA_acm_dpth IA_acm_ptch IA_acm_lxmt
    //      IA_acm_tm     = max minutes to drift trying acoustic xfer    
    //      IA_acm_mode   = Acoustic communication mode
    //                        0 = no acoustic communications
    //                        1 = acoustic communications at bottom of dive
    //                        2 = acoustic communications after ascending to acm_dpth meters 
    //                            below the surface
    //      IA_acm_dpth   = Depth (dbar) at which to start surface comms (only if acm_mode=2)
    //      IA_acm_ptch   = number of counts to move pitch battery at start of 
    //                      drift before starting ac comms.zero=no change
    //                      Positive moves batt FWD and puts nose down, neg=nose up
    //                      Spray5 pitch range is ~1050 to ~2710 counts
    //      IA_acm_lxmt   = power setting for xmit of Local modem  (1,..,8)
    //If cmdfmt = 3 <command string> = IA_acm_rmdly IA_acm_mxtry IA_acm_try IA_acm_binc
    //       IA_acm_rmdly = secs delay between waits for REM to be idle (60ish)
    //       IA_acm_mxtry = max tries to start a transfer in XferBlk or XferSubBlk (3ish)
    //       IA_acm_try   = number of calls to XferBlk or XferSubBlk per block 
    //                       (use 0,1 with autoretry)
    //       IA_acm_binc  = 0 for no block auto-increment, 1=auto-inc, 2=skip block on next
    //                      dive
    //If cmdfmt = 4 <command string>=modindx,id,baud,ib_new,nb_new,ib_rpt,nb_rpt,
    //                               minpwr,pmargn,nsubs
    //       modindx      = {0,..,4} is index in modem database
    //       id           = address of remote modem (legal 0 thru 99)
    //                      no attempt to link if id is outside legal range
    //       baud         = baud rate of remote 
    //                      legal values 140,300,600,800,1066,1200,2400
    //                      use baud =  0 to use current setting of the remote
    //       ib_new       = first 4K byte block of new data to transfer to GS (0-175)
    //                      If ib_new < 0 then leave ib_new unchanged
    //                      (this allows auto-incrementation of block number when sent so
    //                      next dive starts with first block not sent on previous dive)
    //       nb_new       = maximum number of 4K blocks of new data to transfer starting
    //                      with block ib_new
    //                      If nb_new=0 then transfer no data.
    //   --After nb_new of new data have been transferred, a chance to repeat
    //   --the transfer of old data is given.
    //       ib_rpt       = index of first 4K block to repeat (ignored if nb_rpt=0) 
    //       nb_rpt       = number of blocks to repeat
    //                      no repeat data is sent if nb_rpt=0
    //       minpwr       = min power setting of REMOTE (8 disables auto-adjust)
    //       pmargn       = power margin added to lowest successful power setting
    //       nsubs        = # 256-byte sub=blocks per chunk {1,2,4,8} use AT$BS
    //                      nsub=16 uses AT$BB regardless of baud rate
    short i,k1,k2,k3,k4,k5,k6,k7,k8,k9,mindx;
    char cmdfmt,cmdstr[41],*gptr,*sptr;
    uchar dbg[80];
    struct acm_param *p_acm;
   
    p_acm = all.acm;
    //extract one char format and remainder of string
    i=strcspn(ch,"01234");//find 1st char that is either 0,1,2,or 3 
    if(i>=strlen(ch)) return(1); //no legal format char found
    cmdfmt=ch[i];  //got the format spec
    strcpy(cmdstr,&ch[i+1]);  //get the command string
    if( cmdfmt =='0' )
    { //this is a command string to be sent to LOCAL
    
      //Because the X command parsing function parse_X_reply uses '$' to identify the 
      //start of the command checksum, the command string can't contain any embedded '$'.
      //Since Benthos modem strings use $ to identify a command to a REMOTE modem, the
      //Iridium message has every '$' replaced by '%'.
      
      gptr=sptr=cmdstr;//init get and store pointers
      while( *gptr != '\0')
      { if( *gptr != '%' )
        { //transfer everything but % unaltered
          *sptr++ = *gptr++; //xfer and bump pointers
        }
        else
        { //found a % at gptr
          if ( *(gptr+1) == '%' )
          { //2 % in a row translate to a single % so just shift left by 1 char
            gptr++;
            *sptr++ = *gptr++;  //xfer and bump pointers
          }
          else
          { //a single % translates to a $
            *sptr++ = '$';  //store $ and bump store pointer
            gptr++;  //bump get pointer
          }
        }
      }
      *sptr='\0';//null terminate
            
      modem_cmd_str.remID = -1;    
      modem_cmd_str.nbytes = strlen(cmdstr);  //length of the command string
      strcpy(modem_cmd_str.command,cmdstr);  //Move to queuing area 
      printf("Cmnd str to LOCAL=%s\n",cmdstr);//debug  
      return(0); 
    }
   #if BENTHOS==1 //-----------------------------------------------------
    else if ( cmdfmt=='1')
    { //parse cmdstr to get parameters of loitering at bottom of dive
      //removed 30Nov2015 by LAR. Removed use of btmsk so no need to 
      //fill the array
      return(0);  //always a success since does nothing
    }
   #endif //-------------------------------------------------------------
   #if NPS==1 //---------------------------------------------------------
    else if ( cmdfmt=='1')
    { //parse cmdstr to get modem power setting
      k1=0;  //default to OFF
      i = sscanf(cmdstr,"%d",&k1);//i=#items parsed ok 
      if( i<1 )return(1);  //nothing to parse
      switch (i) {
        case 1:  //read new power setting value
          if (k1) //turn ON modem
           {
             start_acm();
           }
          else //turn OFF modem
           {
             acm_off();
           }
          break;
        default:  return(1);  
      }  //end switch
      //annotate the parameter change to CF1
      sprintf(dbg,"CHPAR    %6ld modem power=%d\n",
                   RTCGetTime(0,0),k1); 
      printf("%s",dbg);
      buf_to_cf1V( strlen(dbg),dbg);
      return(0);  //success
     }
   #endif //---------------------------------------------------------------
    else if ( cmdfmt=='2')
    { //parse cmdstr to change duration of comms and local power setting
      k1=60;  //minutes to spend in acoustic communications
      k2=k4=0;k3=20;
      k5=7;  //default to power level 7 (down 3 db from max power)
      i = sscanf(cmdstr,"%d %d %d %d %d",&k1,&k2,&k3,&k4,&k5);//i=#items parsed ok 
      if( i<1 )return(1);  //nothing to parse
      switch (i) {
        case 5:  //read new ee_acm_lxmt value
          iparam[IA_acm_lxmt]= k5;//8=max power,..1=min power  -3db per step
        case 4:  //read new acm_pitch value
          iparam[IA_acm_ptch]= k4;//number cts to move pit batt FWD for ac comms (0 is OK)
        case 3:  //read new ee_acm_dpth value
          iparam[IA_acm_dpth]= k3;//dpth of surface Ac comms
        case 2:  //read new ee_acm_mode value
          iparam[IA_acm_mode]= k2;//0=no ac comms, 1=@bottom, 2=@surface
        case 1:  //read new ee_acm_tm value
          iparam[IA_acm_tm]= k1;
          break;
        default:  return(1);  
      }  //end switch
      //annotate the parameter change to CF1
      sprintf(dbg,"CHPAR    %6ld acm_tm=%d acm_mode=%d acm_dpth=%d acm_ptch=%d acm_lxmt=%d\n",
                   RTCGetTime(0,0),k1,k2,k3,k4,k5); 
      printf("%s",dbg);
      buf_to_cf1V( strlen(dbg),dbg);
      return(0);  //success
    }
    else if ( cmdfmt=='3')
    { //parse XferBlk params to start REMOTE xfer
      k1=60;k2=3;k3=0;k4=0;
      i = sscanf(cmdstr,"%d %d %d %d",&k1,&k2,&k3,&k4);//i=#items parsed ok //debug
      if( i<1 )return(1);  //nothing to parse
      switch (i) {
        case 4:  //read new ee_acm_binc value
          iparam[IA_acm_binc]= k4;//0=no auto-inc, 1= auto inc block on
                                         //next dive, 2=skip block on next dive
        case 3:  //read new ee_acm_try value
          iparam[IA_acm_try]= k3;
        case 2:  //read new ee_acm_mxtry value
          iparam[IA_acm_mxtry]= k2;//max Xfer tries to make
        case 1:  //read new ee_acm_rmdly value
          iparam[IA_acm_rmdly]= k1;//secs bet REM idle tries
          break;
        default:  return(1);  
      }  //end switch
      //annotate the parameter change to CF1
      sprintf(dbg,"CHPAR    %6ld acm_rmdly=%d acm_mxtry=%d acm_try=%d acm_binc=%d\n",
                   RTCGetTime(0,0),k1,k2,k3,k4);
      printf("%s",dbg);
      buf_to_cf1V( strlen(dbg),dbg);
      return(0);  //success
    }
    else if ( cmdfmt=='4')
    { //modem database configuration
      i = sscanf(cmdstr,"%d %d %d %d %d %d %d %d %d %d",
                 &mindx,&k1,&k2,&k3,&k4,&k5,&k6,&k7,&k8,&k9);//i=#items parsed ok
      if( i<1 )return(1);  //nothing to parse
      switch (i) {
        case 10:  //read new nsub value
          p_acm->modlist[mindx].nsub = k9; //# 256-byte sub=blocks per chunk
        case 9:  //read new pmargn value
          p_acm->modlist[mindx].pmargn = k8; //pmargn= # levels added to lowest successful power
        case 8:  //read new minpwr value
          p_acm->modlist[mindx].minpwr = k7; //min power setting of REMOTE
        case 7:  //read new nb_rpt value
          p_acm->modlist[mindx].nb_rpt = k6; //number of 4K blocks of old data to get (0 is OK)
        case 6:  //read new ib_rpt value
          if( k5 >= 0 )  //15Feb09--no change if command has negative value
             p_acm->modlist[mindx].ib_rpt = k5; //index of 1st old block of data to get(0-175)
        case 5:  //read new nb_new value
          p_acm->modlist[mindx].nb_new = k4; //number of 4K blocks of new data to get
                                    // -1 to get all new data, 0 to get no new data
        case 4:  //read new ib_new value
          if( k3 >= 0 )  //15Feb09--no change if command has negative value
             p_acm->modlist[mindx].ib_new = k3;  //index (in REMOTE flash) of first 4K block of
                                        //new data to get
        case 3:  //read new acoustic baud rate
          p_acm->modlist[mindx].baud = k2; //legal -1,0,140,300,600,800,1066,1200,2400
        case 2:  //read new modem ID
          p_acm->modlist[mindx].id = k1; //=modem address ID.  No checks allows setting >99
                               //so Spray ignores modem at the waypoint.
          break;
        default:  return(1);  
      }  //end switch
      
     return(0);  //success
    }  //end database configure
    
    return(1);//unknown command
  } //end cmd_6 *********************************************************
#else
  //cmd_ for NON-Benthos returns 1
  short cmd_6( char *ch) //************************
  {
    *ch = '0'; //-- dummy
    return(1);
  } //*** end cmd_6 for Non-Benthos
#endif


// configuration file routines ************************************************
//*****************************************************************************

/*
short  get_ee_val ( char name[] ) //*******************************************
{ // get a short value from the eeprom
  // name[] = variable name
  short x;
      x = (short) VEEFetchLong( name, NO_VEE_VAL );
      
      if (x == NO_VEE_VAL) //------------------------------------
      { // then NOT found !!
        // can handle exception here
      } //-------------------------------------------------------
  return ( x );
}//****************************************************************************
*/
FILE*  open_sco  ( void ) //***************************************************
{ // opens the .sco = Spray Change Order 
  // n = suffix index: valid range = 0<=n<=9,
  // otherwise writes to <XXXX.cfg> where XXXX is the serial #
  // returns error ( 0=no error )
  short err = 0;
  short sn;
  FILE *fp;
  char name[20];
  

      sn = (short) VEEFetchLong( "SERNO", NO_VEE_VAL );
      
      if (sn == NO_VEE_VAL) //------------------------------------
      { // then NOT found !!
          sn = 999; // set to default
          cprintf("Illegal s/n, setting to default = %d", sn);
      } //-------------------------------------------------------
  
  // compose the sco name ---------------------------------------
  { sprintf(name,"%04d.sco",sn); }
  
  //--- open the file -------------------------------------------
  fp = fopen(name, "a+"); // append if needed
   
  return ( fp );
} //***************************************************************************
    
short  write_config( short n ) //**********************************************
{ // write out the parameter list to a configuration file
  // n = suffix index: valid range = 0<=n<=9,
  // otherwise writes to <XXXX.cfg> where XXXX is the serial #
  // returns error ( 0=no error )
  short err = 0;
  short sn;
  FILE *fp;
  char name[20];
  

      sn = (short) VEEFetchLong( "SERNO", NO_VEE_VAL );
      
      if (sn == NO_VEE_VAL) //------------------------------------
      { // then NOT found !!
          sn = 999; // set to default
          cprintf("Illegal s/n, setting to default = %d", sn);
      } //-------------------------------------------------------
  
  // make the config name ---------------------------------------
  if (n >=0 && n<=9 )
  { // then legal
    sprintf(name,"%04d.cf%1d",sn,n);
  } else // not legal
  { sprintf(name,"%04d.cfg",sn); }
  
  //--- open the file -------------------------------------------
  fp = fopen(name, "w"); // write over if already exists
  if (fp) //-----------------------------------------------------
  { // then it is valid, we can write out the information
    display_all( fp, Param_table );
  }
  else err=1; // open err
  fclose( fp );
   
  return ( err );
} //***************************************************************************
    
short  read_config( short n, short force ) //**********************************
{ // read the parameter list from a configuration file
  // n = suffix index: valid range = 0<=n<=9,
  // otherwise reads  <XXXX.cfg> where XXXX is the serial #
  // force = TRUE = force the config file address to agree with iparam
  //       this would normally be true, except when updating from a previous
  //       compilation, and still want to load the majority of the values.
  
  // returns error ( 0=no error )
  // err=1 = bad update
  // err=2 = bad address
  // err=3 = incorrect #arg
  // err=4 = file could not be opened.
  
  short err = 0;
  short sn;
  FILE *fp;
  char name[20];
  

      sn = (short) VEEFetchLong( "SERNO", NO_VEE_VAL );
      
      if (sn == NO_VEE_VAL) //------------------------------------
      { // then NOT found !!
          sn = 999; // set to default
          cprintf("Illegal s/n, setting to default = %d", sn);
      } //-------------------------------------------------------
  
  // compose the config name ------------------------------------
  if (n >=0 && n<=9 )
  { // then legal
    sprintf(name,"%04d.cf%1d",sn,n);
  } else // not legal
  { sprintf(name,"%04d.cfg",sn); }
  
  //--- open the file -------------------------------------------
  fp = fopen(name, "r"); // read
  //--- may16, try to mount the flash card if not good
  if (!fp) //-------------------------------------
  {
    printf("trying to mount the flash card\n");
    execstr("ccc 1"); // may16 try to re-mount
    fp = fopen(name, "r"); // try again
  } //--------------------------------------------
    
  if (fp) //-----------------------------------------------------
  { // then it is valid, we can read in
    err = read_cfg( fp, force ); // read in all lines
  }
  else 
  {
    cprintf(">>>> File %s could not be opened \n",name);
    err=4; // open err
  }
  
  fclose( fp );
   
  return ( err );
} //***************************************************************************
    
short  read_cfg (FILE *fp, short force ) //************************************
{ // read in from file until no more lines
  // force = TRUE = force the config file address to agree with iparam
  //       this would normally be true, except when updating from a previous
  //       compilation, and still want to load the majority of the values.

  // returns 0 if OK, else error status
  // err=1 = bad update: is set for eeprom values trying to be changed.
  // err=2 = bad address
  // err=3 = incorrect #arg
  
  short err = 0;
  short n, num, use;
  short addr, val, j, a;
  char str[80], name[20];
  
  do //====================================================================
  {  // read until EOF
    n = fget_line( fp, str, 80 ); // read up to 80 char
    
    if (n>6 && str[0] == '!' ) //++++++++++++++++++++++++++++++++
    { // then could be a legal line
      num = sscanf(str,"!%s %d %d",name,&addr,&val );
      
      if (num==3) //333333333333333333333333333333333333
      { // then read in correct # of arguments
        j = find_name( name, Param_table );
        a = Param_table[j].addr;
        use = TRUE; // use this value...
        // unless want to force the addresses to agree...
        if (force) use = ( a == addr );
        
        if ( use ) //--------------------------------
        { // then proceed
           num = change_param( j, val, FALSE, Param_table, FALSE );
           // first FALSE does NOT allow changing an eeprom or NV-RAM value
           err=0;
           if (num != val ) 
           {  
              err=1;  //FLAG bad update
              //cprintf("could not update addr=%3d name= %s\n", addr, name);
           }
        } // else addresses do not match, set err==2
        else err=2; //-------------------------------
        if (err==2) cprintf("Err %d:j=%d, addr[j]=%d, %s %d %d\n",
                          err,j, a, name,addr,val );
        
      }  
      else err=3; //333333333333333333333333333333333333
      
    } //+++++++++++++++++++++++++++++++++++++++++++++++++++++++++
    
  } while (n>=0); //=======================================================
  
  return ( err );
} //***************************************************************************


//*****************************************************************************
//*****************************************************************************
//*****************************************************************************
void param_menu(void);
void test_param( void ) //*****************************************************
{
  char ch, name[20];  //input char
  short  i, done, n, val, tmo, k;
  long tnow;

	cprintf("         Mission/Configuration Parameters : %s \n", __DATE__);
	
	all.fp_sco = open_sco (); // open the change-order file
	param_menu();
  
	done = false;
	tnow = RTCGetTime(0,0);  // gets present time
	tmo = 0;
	while (!done) //+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
	{  //cycle until we get a quit command

		
		while ( SCIRxQueuedCount() )
		    SCIRxFlush(); // delete any old stuff 
        if (ch !=' ' && ch>0 ) printf("E_1>\n"); // display directory prompt
	    ch = -1; // illegal value - means it timed out
	    ch = SCIRxGetCharWithTimeout( 1000 ); // get a new character
	    tmo = check_tmo ( &ch, tmo );
	    // increments the tmo counter if ch<0, and
	    // goes to sleep mode after max_tmo.  Returns *ch as upper-case
	    
	    //================================================================
	    switch(ch) //====================================================
	    { // and depending upon the cmd, do the appropriate action =======
	    
        case 'Q' :  done=1; break; //quit function
        case '?' :  break; // causes directory prompt to be displayed
        case ' ' :  break; // wait for another character
        case 'U' :  param_menu(); break;
        case  -1 :  break; // wait for another character
	    case '+' :  toggle_verbose(); // toggle verbose mode
				    break;



		case 'D' : // display all parameters
			display_all( stdin, Param_table );
			break;
			
		case 'F' : // Find a specific name
			cprintf("give variable name to find >\n");
			gets(name); // read in the name
			i = find_name_addr(name, Param_table );
			if (i>-1)
			{ // then the address was found;
			  display_info( i, Param_table );
			  cprintf("\n");
			}
			else cprintf("%s not found\n",name);
			break;

		case 'M' : // modify values within a group
			modify_group ( Param_table );
			param_menu();
			break;
			
		case 'R' : // read a config file
		    cprintf("Give cfg index 0<=i<=9 to read\n");
		    n = get_num(&val);
		    if ( n>0 ) // then valid
		    {  cprintf("Force address values in file to agree with ram? ");
		       k = YesNo();
		       
			   read_config (val, k );  // read the config file
			}
			break;
			
		case 'S' : // display all shore commands
			display_shore_cmd( );
			break;
                    
		case 'T' : // display all shore commands
			n = test_shore_cmd( );
			cprintf("shore_cmd_err = %d\n", n);
			break;
                    
		case 'W' : // write out a config file
		    cprintf("Give index 0<=i<=9 if want a copy, -1 to overwrite .cfg\n");
		    n = get_num(&val);
			write_config (val );  // write out the config file
			break;
			
		case 'X' : // get name and new value
			printf("Give variable name to change >\n");
			gets(name);
			printf("Give the new value for %s >\n",name);
			n = get_num(&val);
			if (n==1) //then a legal read of value
				val = change_name_param(name, val, Param_table );
			printf("New value = %d \n", val);
			break;
                   
		case '8' : 
		    cprintf("Give #times to increment surface, profile numbers\n");
		    n = get_num(&val);
		    if ( n>0 ) // then valid
		    {  
		       for (i=0;i<val;i++)
		       {
		         incr_surf_num();
		         incr_prof_num();
		         cprintf("s=%5d p=%5d\n",iparam[EA_surf_num], iparam[EA_prof_num] );
		       }
			}
			break;

		case '9' : 
		    cprintf("Give new value for op_mod, surface#\n");
		    n = get_num(&val);
		    if ( n>0 ) // then valid
		    {  
		       set_op_mode(val);
		       set_surf_num(val);
			}
			break;
        default  : printf(" input unknown \n");
	    } //==============================================================
	     
	  
	} // end while !done
	//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
	//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
	
	//--jun11, add auto-request to re-write the config file
	cprintf("IF you have changed any values, SAVE to the cfg file\n");
	cprintf("Save to the cfg file now? (Y/N)\n");
	if ( YesNo() ) 	
	{  // dec11 display line that actually saved
	   write_config (-1 );  // write out the config file
	   cprintf("Saved to the .cfg file\n");
	}

	fclose ( all.fp_sco );
	
  return;
} //***************************************************************************




void param_menu(void) //*******************************************************
{ //print out menu selections  

 cprintf("                  Parameter and Shore Command Menu\n"
 		"Q.uit             :   U.pdate menu\n"
        "D.isplay All      :   F.ind Name     : X.change value \n"
        "M.odify group     :   R.ead Config   : W.rite Config \n"
        "S.hore cmd summary:   T.est shore cmd: 9.change opmode,dive#\n" );

  return;
} //***************************************************************************


//*** END *********************************************************************