#include "device-functions.h"
#include "globals.h"
#include "version.h"
#include "error_utils.h"
#include "i2c.h"
#include "bus.h"
#include "lcd.h"
#include "flash.h"
#include "menus.h"
#include <math.h>
#include <glib.h>
#include <libuser/user.h>
#include <libuser/config.h>
#include <security/pam_appl.h>
#include <security/pam_misc.h>


static void start_gate_override ();
static void stop_gate_override ();
static void set_shiftreg_bits(int shiftreg, int start_at_bit, int numbits, int value);


#define ONE_BIT 1
#define TWO_BITS 2
#define THREE_BITS 3
#define FOUR_BITS 4
#define FIVE_BITS 5
#define SIX_BITS 6
#define SEVEN_BITS 7
#define EIGHT_BITS 8

#define SR_0 0
#define SR_1 1
#define SR_2 2
#define SR_3 3

#define POS_0 0
#define POS_1 1
#define POS_2 2
#define POS_3 3
#define POS_4 4
#define POS_5 5
#define POS_6 6
#define POS_7 7
#define POS_8 8
#define POS_9 9
#define POS_10 10
#define POS_11 11
#define POS_12 12
#define POS_13 13
#define POS_14 14
#define POS_15 15
#define POS_16 16
#define POS_17 17
#define POS_18 18
#define POS_19 19

#define XTR_POS 14

#define BIT_HIGH 1
#define BIT_LOW 0

void idn_string(gchar** response)
{
	*response = g_strdup_printf ("AVTECH ELECTROSYSTEMS,%s,SN:%s,v%s",
	                             globals.Flash.model_num,
	                             globals.Flash.serial_num,
	                             FW_VERSION);
}


void Main_Rst (void)
{
	int i;

	globals.Flags.extended_ampl_min_max=NO;

	// go backwards, so channel 0 overrides everything
	for (i=globals.Flash.channels-1; i>=0; --i) {

		globals.ChannelState[i].route_primary=1;
		globals.ChannelState[i].route_secondary=1;
		globals.ChannelState[i].frequency=globals.Flash.min_freq[i];
		globals.ChannelState[i].delay=globals.Flash.min_delay[i];

		if (globals.Flash.min_pw[i] > 0.0) {
			globals.ChannelState[i].pw=globals.Flash.min_pw[i];
		} else {
			globals.ChannelState[i].pw=0.0;
		}

		if (globals.Flash.ampl_min_max_only[i] || (globals.Flash.min_ampl[i] > 0.0)) {
			globals.ChannelState[i].amplitude = globals.Flash.min_ampl[i];
		} else {
			globals.ChannelState[i].amplitude=0.0;
		}
		globals.ChannelState[i].offset=0.0;

		globals.ChannelState[i].zout=globals.Flash.zout_min[i];
		globals.ChannelState[i].hold_setting = hold_width;
		globals.ChannelState[i].double_pulse = double_off;
		globals.ChannelState[i].pw_ctrl_mode = pw_normal;
		globals.ChannelState[i].func_mode = pulse_mode_on;
		globals.ChannelState[i].polarity = globals.Flash.invert_by_default[i];
		globals.ChannelState[i].output_state = output_off;
		globals.ChannelState[i].gate_type = gate_sync;
		globals.ChannelState[i].trigger_source = source_internal;
		globals.ChannelState[i].amp_mode = amp_mode_normal;
		globals.ChannelState[i].os_mode = os_mode_normal;
		globals.ChannelState[i].gate_level = gate_low;
		globals.ChannelState[i].load_type= globals.Flash.low_load_type[i];
		globals.ChannelState[i].test_delay_mode = NO;
		globals.ChannelState[i].logic_level = logic_ttl;
		globals.ChannelState[i].burst_count = 1 && !globals.Flash.burst_func[i];
		globals.ChannelState[i].burst_time = globals.Constraints.composite_min_burst_time[i];
		globals.ChannelState[i].rise_time = globals.Flash.min_rise_time[i];
		globals.ChannelState[i].soft_current_limit = globals.Flash.max_soft_current_limit[i];
		globals.ChannelState[i].vcc1 = 0.0;
		globals.ChannelState[i].vcc2 = globals.Flash.vcc2_min[i];
		globals.ChannelState[i].vlogic = 0.0;
		globals.ChannelState[i].slew = globals.Flash.min_slew[i];

		globals.Flags.do_check_settings=NO;   /* don't check for conflicting settings */
		Set_Pw(0,0,0,i,globals.ChannelState[i].pw,0);

		Set_Route(i,ROUTE_PRIMARY,globals.ChannelState[i].route_primary);
		Set_Route(i,ROUTE_SECONDARY,globals.ChannelState[i].route_secondary);

		Set_frequency(0,0,0,i,globals.ChannelState[i].frequency);
		Set_Delay(0,0,0,i,globals.ChannelState[i].delay);
		Set_Double(i,globals.ChannelState[i].double_pulse);
		Set_Pwmode(i,globals.ChannelState[i].pw_ctrl_mode);
		Set_Func(i,globals.ChannelState[i].func_mode);
		Set_Pol(i,globals.ChannelState[i].polarity);
		Set_Gate_Sync(i,globals.ChannelState[i].gate_type);
		Set_Gate_Level(i,globals.ChannelState[i].gate_level);
		Set_EA(i,globals.ChannelState[i].amp_mode);
		Set_EO(i,globals.ChannelState[i].os_mode);
		if (globals.Flash.switchable_zout[i]) {
			Set_zout(i,globals.ChannelState[i].zout,1);
		}

		Set_Load(i,globals.ChannelState[i].load_type);
		Set_Logic_Level(i,globals.ChannelState[i].logic_level);
		Set_rise_time(0,0,0,i,globals.ChannelState[i].rise_time);
		Set_slew(0,0,0,i,globals.ChannelState[i].slew);
		Set_current_limit(0,i,globals.ChannelState[i].soft_current_limit);

		if (globals.Flash.max_burst_count[i]>1) {
			Set_Burst_Count(i,globals.ChannelState[i].burst_count,globals.ChannelState[i].burst_time);
		}

		Set_Trig_Source(i,globals.ChannelState[i].trigger_source);

		Set_Amplitude(0,0,0,0,0,0,i,globals.ChannelState[i].amplitude,0);
		Set_Offset(0,0,0,0,i,globals.ChannelState[i].offset);
		Set_Output_State(i,globals.ChannelState[i].output_state);

		Set_current_limit(0,i,globals.ChannelState[i].soft_current_limit);

		Set_avrq_ampl(0,0,0,2,globals.ChannelState[i].vcc1);
		Set_avrq_ampl(0,0,0,3,globals.ChannelState[i].vcc2);
		Set_avrq_ampl(0,0,0,4,globals.ChannelState[i].vlogic);
		globals.Flags.do_check_settings=YES;  /* check for conflicting settings */
	}

	// establishes min/max values for queries, but reports no errors
	Error_check(globals.ChannelState);

	Ctrl_PRF_Limiter(1);

	globals.Timers.normal_relay_bounce_time_in_milliseconds=1.0e3 * globals.Flash.relay_delay_in_sec;
	globals.Timers.Relay_Switching_Delay_in_Milliseconds=globals.Timers.normal_relay_bounce_time_in_milliseconds;

	// reset all transient flags
	globals.Flags = globals.DefaultFlags;

	Menu_Clear_Buttons();
}


void set_dac(int dac, int word)
{
	/* allows dacs to be disabled (using dac=-1, for example) */
	if ((dac >= 0) && (dac < 8)) {
		globals.Registers.parallel_DAC_reg[dac]=word;
	}
}


int Set_Amplitude(int check_possible_only,int pol_override,int override_on,int word_override,int range_override,
                  int switch_range_only,int channel,float new_ampl,int called_from_set_pw)
{
	int word_out;	  /* what is sent to the DAC */
	int relay_range,atten_range,old_range,old_actual_pol;	  /* selects relay range */
	int UseNegData;	/* if polarity is negative and separate piece-wise linear data is available for neg */
	int point_found;
	int entry;
	int actual_pol;

	// for CH2 of AVR-D3-B-MS1
	if (globals.Flash.ignore_ampl_polarity[channel]) {
		new_ampl = fabs(new_ampl);
	}

	if (globals.Flash.enable_avrq_extra_ampls && channel) {
		return Set_avrq_ampl(check_possible_only,word_override,range_override,channel,new_ampl);
	}
	if (globals.Flash.enable_avrq_extra_ampls && !channel) {
		if ((new_ampl>=0.0) && (new_ampl<globals.Flash.ampl_zero_equiv[channel])) {
			new_ampl=globals.Flash.ampl_zero_equiv[channel];
		}
		if ((new_ampl<0.0) && (new_ampl>-globals.Flash.ampl_zero_equiv[channel])) {
			new_ampl=-globals.Flash.ampl_zero_equiv[channel];
		}
	}

	if (!globals.Flash.voltage_enabled[channel] && !globals.Flash.current_enabled[channel]) {
		return Unrecognized;
	}

	/* abandon if high channel selected by user but not enabled by firmware */
	if (channel && !globals.Flash.ChanKey_amplitude) {
		return InvalidChannel;
	}

	/* keep "-0" in negative area, to avoid using zeroed positive data */
	if (	new_ampl<=smallest_allowed_number
	                && new_ampl>=-smallest_allowed_number
	                && !(globals.Flash.max_ampl[channel]>0) 	) {
		new_ampl=-smallest_allowed_number;
	}

	/* Check for min abs value, step size */
	if (globals.Flash.ampl_min_abs_value[channel] > 0.0) {
		if ((new_ampl < 0.0) &&
		                (new_ampl > -globals.Flash.ampl_min_abs_value[channel])) {
			new_ampl = -globals.Flash.ampl_min_abs_value[channel];
		}
		if ((new_ampl >= 0.0) &&
		                (new_ampl < globals.Flash.ampl_min_abs_value[channel])) {
			new_ampl = globals.Flash.ampl_min_abs_value[channel];
		}
	}

	/* For units with large amplitude step sizes (AVRQ-2-B-PN-EVE) */
	if (globals.Flash.ampl_step_size[channel] > 0.0) {
		new_ampl = ((int) (new_ampl / globals.Flash.ampl_step_size[channel])) * globals.Flash.ampl_step_size[channel];
	}

	if (!check_possible_only) {
		int i, error_num;
		for (i=0; i<max_channels; ++i) {
			TestState[i]=globals.ChannelState[i];
		}
		TestState[channel].amplitude=new_ampl;
		if ((error_num=Error_check(TestState))) {
			return error_num;
		}

		/* determine the previous relay range */
		Set_VI_Control(pwl_ampl_values,channel,globals.ChannelState[channel].amplitude,&point_found,
		               &old_range,&atten_range,&UseNegData,&entry,&word_out,&old_actual_pol);
	}

	word_out = -1;
	relay_range = 0;


	/* find appropriate range/fine settings from piece-wise linear data in flash memory */

	if (override_on) {
		word_out=word_override;
		relay_range=range_override;
		actual_pol=pol_override;
	} else {
		/* set the amplitude controls now. */
		int status;
		if ((status=Set_VI_Control(pwl_ampl_values,channel,new_ampl,&point_found,
		                           &relay_range,&atten_range,&UseNegData,&entry,&word_out,&actual_pol))) {
			return status;
		}
	}

	if (check_possible_only) {
		if (point_found) {
			return OK;
		} else {
			return CalibrationMinMaxError;
		}
	}

	/* when switching to EA mode, need to switch range but keep DAC voltage zero */
	if (switch_range_only) {
		word_out=0;
	}


	/* if switching to a higher range with the same polarity, an extended delay may be needed to allow */
	/* capacitor banks to discharge (1011, 1015 series especially) */
	if ((relay_range>old_range) && (actual_pol==old_actual_pol) ) {
		globals.Timers.Relay_Switching_Delay_in_Milliseconds=(long) (1000L * globals.Flash.extended_relay_delay_in_sec);
	}


	set_dac(globals.Flash.ampl_DAC[channel],word_out);


	if (!channel) {
		if (!globals.Flash.ampl_ranges_for_ch2_only) {
			if ((globals.Flash.ChanKey_amplitude?globals.Flash.channels:1)<=1) {
				/* for regular, single-channel units use AMPL RANGE pins 0-4 */
				set_shiftreg_bits(SR_3, POS_7, FIVE_BITS, 1<<relay_range);
			} else {
				/* for CH1 of dual-channel units, use AMPL RANGE pin 0-2 for CH1, and 3-4 for CH2 */
				set_shiftreg_bits(SR_3, POS_7, THREE_BITS, 1<<relay_range);
			}
		}

		if (	(!actual_pol && globals.Flash.pol_relay_high_for_pos[channel])
		                ||	(actual_pol && !globals.Flash.pol_relay_high_for_pos[channel])	) {
			set_shiftreg_bits(SR_3, POS_13, ONE_BIT, BIT_HIGH);	/* set O.POL line high to switch pol relay to +, normally */
		} else {
			set_shiftreg_bits(SR_3, POS_13, ONE_BIT, BIT_LOW);	/* set O.POL line low to switch pol relay to -, normally */
		}

		// Are attenuators used?
		if (globals.Flash.attenuators[channel][0] > 0.0) {
			// deal with attenuator range - haven't done for CH2 yet

			// only implement one attenuator so far (range 0), out of possible max_attens (8!)
			// use I2C driver in future?
			if (atten_range == 0) {
				set_shiftreg_bits(SR_2, XTR_POS + 1, ONE_BIT, BIT_HIGH);
			} else {
				set_shiftreg_bits(SR_2, XTR_POS + 1, ONE_BIT, BIT_LOW);
			}
		}

	} else {

		if (!globals.Flash.ampl_ranges_for_ch2_only) {
			/* for CH2 of dual-channel units, use AMPL RANGE pin 2-3 */
			set_shiftreg_bits(SR_3, POS_10, TWO_BITS, 1<<relay_range);
		} else {
			/* sometimes CH2 can use pins 0-4 */
			set_shiftreg_bits(SR_3, POS_7, FIVE_BITS, 1<<relay_range);
		}

		int pol_bit = globals.Flash.polarity_xtra_rly[channel] + XTR_POS;
		if (new_ampl<0.0) {
			set_shiftreg_bits(SR_2, pol_bit, ONE_BIT, BIT_LOW);	/* set O.POL line low to switch pol relay to - */
		} else {
			set_shiftreg_bits(SR_2, pol_bit, ONE_BIT, BIT_HIGH);	/* set O.POL line high to switch pol relay to + */
		}
	}

	globals.Changes.update_amp=YES;

	globals.ChannelState[channel].amplitude=new_ampl;
	Set_Update_Chans();

	if ((relay_range!=old_range) && (globals.ChannelState[channel].output_state==output_on)) {
		globals.Flags.force_output_fully_off=YES;
	}

	/* update offset */
	if (globals.Flash.ampl_os_ranges_related[channel] && !globals.Changes.update_os) {
		Set_Offset(0,0,0,0,channel,globals.ChannelState[channel].offset);
	}

	/* ampl / pw tweaks */
	if (!called_from_set_pw) {
		Set_Pw(0,0,0,channel,globals.ChannelState[channel].pw,1);
	}

	/* ampl / delay tweaks */
	Set_Delay(0,0,0,channel,globals.ChannelState[channel].delay);
	Set_rise_time(0,0,0,channel,globals.ChannelState[channel].rise_time);

	return OK;
}


int Set_Pw(int check_possible_only,int word_override,int range_override,int channel,float set_pw,int called_from_set_ampl)
{
	int word_out;  /* what is sent to the DAC */
	int cap_range_control;
	int point_found,relay_range,dummy,UseNegData,entry;
	int status,actual_pol;

	/* abandon if high channel selected by user but not enabled by firmware */
	if (channel && !globals.Flash.ChanKey_pw) {
		return InvalidChannel;
	}

	/* for fixed PW units, set actual trigger PW to a reasonable minimum */
	if (globals.Flash.fixed_pw[channel]
	                && !check_possible_only
	                && !word_override
	                && !range_override
	                && (globals.Flash.fully_programmed==All_Programmed)) {
		set_pw=globals.Flash.min_pw[channel];
	}

	if (!check_possible_only) {
		int i, check_valid;
		for (i=0; i<max_channels; ++i) {
			TestState[i]=globals.ChannelState[i];
		}
		TestState[channel].pw=set_pw;
		if ((check_valid=Error_check(TestState))) {
			return check_valid;
		}
	}


	/* find appropriate range/fine settings from piece-wise linear data in flash memory */
	if (word_override) {
		word_out=word_override;
		relay_range=range_override;
	} else {
		/* set the pw controls now. */
		if ((status=Set_VI_Control(pwl_pw_values,channel,set_pw,&point_found,
		                           &relay_range,&dummy,&UseNegData,&entry,&word_out,&actual_pol))) {
			return status;
		}
	}


	if (check_possible_only) {
		if (point_found) {
			return OK;
		} else {
			return CalibrationMinMaxError;
		}
	}

	if (relay_range==0) {
		cap_range_control=0;
	} else {
		cap_range_control = 1 << (relay_range-1);
	}

	/* if this is for CH1, use the standard output chain */
	if (channel==0) {
		if (globals.Flash.special_pw_range_minimum[channel] > 0.0) {
			if (set_pw > globals.Flash.special_pw_range_minimum[channel]) {
				set_shiftreg_bits(SR_2, XTR_POS + 2, ONE_BIT, BIT_HIGH);
			} else {
				set_shiftreg_bits(SR_2, XTR_POS + 2, ONE_BIT, BIT_LOW);
			}
		}

		/* AVPP-style: lower PW range is voltage-controlled */
		if (globals.Flash.volt_ctrl_pw[channel]) {

			// reset PG B line (see below)
			set_shiftreg_bits(SR_2, XTR_POS + 5, ONE_BIT, BIT_LOW);

			/* use DAC8420 to control PW in lowest PW range */
			if (!relay_range) {
				// set the voltage to the actual PW-control circuit (normally in a module)
				set_dac(globals.Flash.pw_dac[channel],word_out);

				// This word is fed to the PW circuit on the OP1B board, not the module
				// Set to a fixed value. Isn't actually used to control PW in this mode,
				// but triggers following stages.
				word_out=globals.Flash.fix_pw_dac_val[channel];

				// Also, boost the internal PW range during calibration
				// so that PRF calibration works at
				// low PRFs (i.e., reasonable duty cycle for scope to see).
				// Bit of a hack based on the period.
				if (globals.Flash.fully_programmed==Being_Programmed) {
					int rough_range = ((int) log10 (1.0/globals.ChannelState[channel].frequency)) + 6;
					if (rough_range < 0) {
						rough_range = 0;
					}
					if (rough_range >= timing_ranges) {
						rough_range = timing_ranges-1;
					}
			        	if (rough_range==0) {
				                cap_range_control=0;
				        } else {
				                cap_range_control = 1 << (rough_range-1);
				        }
				}

				/* Zout hardware/software is used in AVPP to control PW ranges in the
 * 				   external circuitry, not Zout */
				Set_zout(channel,globals.Flash.zout_min[channel],0);

			} else {
				Set_zout(channel,globals.Flash.zout_max[channel],0);

				/* In AVPP units, the bottom range is voltage-controlled, and the upper range(s) are
					PWin=PWout. Therefore, shift the relay used by the upper ranges by one, to avoid
					changing capacitors. */

				if (relay_range==1) {
					cap_range_control=0;
					// set XTRA RLY 5 high in this range for AVR-E3-B-R5-N-M5, and other
					// units with PG A, B, and C. This corresponds to PG B.
					set_shiftreg_bits(SR_2, XTR_POS + 5, ONE_BIT, BIT_HIGH);
				} else {
					cap_range_control = 1 << (relay_range-2);
				}
			}
		}

		/* set fine controls */
		set_dac(4,word_out);

		/* set ranges */
		set_shiftreg_bits(SR_3, POS_0, SEVEN_BITS, cap_range_control);
	}

	if (channel==1) {
		/* if this is for CH2, use the standard 1021D dual-PW board, which has a PCF8574 for I/O, */
		/* unless it is a voltage-controlled scheme like the ISI units. */
		if (!globals.Flash.volt_ctrl_pw[channel]) {
			control_pcb107(Second_PW_Port,globals.Flash.pw_dac[channel],word_out,relay_range);
		} else {
			/* ISI-style units have voltage-controlled PW and two ranges */
			set_dac(globals.Flash.pw_dac[channel],word_out);

			/* two ranges controlled by XTRA-RLY2 line */
			set_shiftreg_bits(SR_2, XTR_POS + 2, ONE_BIT, relay_range);
		}
	}

	/* need a center-frequency control voltage for monocycle generators, to space the + and - parts of the monocycle. */
	/* use CH2 calibration to do this */
	if (globals.Flash.is_monocycle[0] && channel==0) {
		if ((status=Set_VI_Control(pwl_pw_values,1,set_pw,&point_found,
		                           &relay_range,&dummy,&UseNegData,&entry,&word_out,&actual_pol))) {
			return status;
		}
		set_dac(globals.Flash.monocycle_dac[0],word_out);
	}

	globals.ChannelState[channel].pw=set_pw;
	Set_Update_Chans();

	if (!called_from_set_ampl) {
		Set_Amplitude(0,0,0,0,0,0,channel,globals.ChannelState[channel].amplitude,1);
	}

	return OK;
}


int Set_Offset(int check_possible_only,int override_on,int word_override,int range_override,int channel,float new_offset)
{
	int word_out;	  /* what is sent to the DAC */
	int point_found;
	int entry,dummy0,dummy1,dummy2,dummy3;
	int actual_pol;
	int old_range,relay_range;

	if (!globals.Flash.voltage_offset_enabled[channel] && !globals.Flash.current_offset_enabled[channel]) {
		return Unrecognized;
	}

	/* abandon if high channel selected by user but not enabled by firmware */
	if (channel && !globals.Flash.ChanKey_offset) {
		return InvalidChannel;
	}

	word_out = -1;
	point_found = 0;

	if (!check_possible_only) {
		int i, error_num;
		for (i=0; i<max_channels; ++i) {
			TestState[i]=globals.ChannelState[i];
		}
		TestState[channel].offset=new_offset;
		if ((error_num=Error_check(TestState))) {
			return error_num;
		}

		/* determine the previous relay range */
		Set_VI_Control(pwl_os_values,channel,globals.ChannelState[channel].offset,&point_found,
		               &old_range,&dummy0,&dummy1,&dummy2,&word_out,&dummy3);
	}

	if (override_on) {
		relay_range=range_override;
		word_out=word_override;
	} else {
		int status;
		if ((status=Set_VI_Control(pwl_os_values,channel,new_offset,&point_found,
		                           &relay_range,&dummy0,&dummy2,&entry,&word_out,&actual_pol))) {
			return status;
		}
	}

	if (check_possible_only) {
		if (point_found) {
			return OK;
		} else {
			return CalibrationMinMaxError;
		}
	}

	set_dac(globals.Flash.os_DAC[channel],word_out);

	globals.Changes.update_os=YES;

	globals.ChannelState[channel].offset=new_offset;
	Set_Update_Chans();

	if ((relay_range!=old_range) && (globals.ChannelState[channel].output_state==output_on)) {
		globals.Flags.force_output_fully_off=YES;
	}

	if ((globals.Flash.ampl_coupled_to_os[channel] || globals.Flash.ampl_os_ranges_related[channel]) && !globals.Changes.update_amp) {
		Set_Amplitude(0,0,0,0,0,0,channel,globals.ChannelState[channel].amplitude,0);
	}

	/* change amplitude range if required for calibration */
	if (override_on) {
		Set_Amplitude(0,0,1,0,relay_range,0,channel,0.0,0);
	}

	/* increase PW if required to compensate for series output diode turn-on time */
	if (globals.Flash.distort_Y[channel] != 0.0) {
		Set_Pw(0,0,0,channel,globals.ChannelState[channel].pw,0);
	}

	return OK;
}


int Set_frequency(int check_possible_only,int word_override,int range_override,int channel,float set_freq)
{
	float new_pw;
	int point_found,relay_range,UseNegData,entry,actual_pol,old_range,dummy;
	int word_out;	/* what is sent to the DAC */

	/* abandon if high channel selected by user but not enabled by firmware */
	if (channel && !globals.Flash.ChanKey_frequency) {
		return InvalidChannel;
	}

	word_out = -1;

	/* check new duty cycle, if pw fixed */
	if (globals.ChannelState[channel].hold_setting==hold_width) {
		new_pw=globals.ChannelState[channel].pw;
	} else {
		new_pw=globals.ChannelState[channel].pw * globals.ChannelState[channel].frequency/set_freq;
	}

	if (!check_possible_only) {
		int i, check_valid;
		for (i=0; i<max_channels; ++i) {
			TestState[i]=globals.ChannelState[i];
		}
		TestState[channel].pw=new_pw;
		TestState[channel].frequency=set_freq;
		if ((check_valid=Error_check(TestState))) {
			return check_valid;
		}

		/* added Feb 23/05 */
		/* determine the previous relay range */
		Set_VI_Control(pwl_period_values,channel,1.0/globals.ChannelState[channel].frequency,&point_found,
		               &old_range,&dummy,&UseNegData,&entry,&word_out,&actual_pol);
	}

	/* find appropriate range/fine settings from piece-wise linear data in flash memory */
	if (word_override) {
		word_out=word_override;
		relay_range=range_override;
	} else {
		/* set the amplitude controls now. */
		int status;
		if ((status=Set_VI_Control(pwl_period_values,channel,1.0/set_freq,&point_found,
		                           &relay_range,&dummy,&UseNegData,&entry,&word_out,&actual_pol))) {
			return status;
		}
	}

	if (check_possible_only) {
		if (point_found) {
			return OK;
		} else {
			return CalibrationMinMaxError;
		}
	}

	if (globals.Flash.is_func_gen[channel]) {
		set_dac(globals.Flash.freq_dac[channel],word_out);

		/* Function generator PRF range controlled by XTRA-RLY 1-3 lines. */
		/* Range=0 disables oscillator, for amplifier mode. */

		if (globals.ChannelState[channel].func_mode==amp_mode_on) {
			relay_range=0;
		}

		set_shiftreg_bits(SR_2, XTR_POS + 1, THREE_BITS, relay_range);

		if ((relay_range!=old_range) && (globals.ChannelState[channel].output_state==output_on)) {
			globals.Flags.force_output_fully_off=YES;
		}
	}

	else {
		int cap_range_control = 0;

		if (relay_range) {
			cap_range_control = 1 << (relay_range-1);
		}

		set_dac(globals.Flash.freq_dac[channel],word_out);
		set_shiftreg_bits(SR_2, POS_0, SEVEN_BITS, cap_range_control);
	}

	g_print_debug("freq range %d, word %d\n",relay_range,word_out);

	globals.ChannelState[channel].frequency=set_freq;

	Set_Pw(0,0,0,channel,new_pw,0);
	Set_Update_Chans();

	return OK;
}


int Set_Delay(int check_possible_only,int word_override,int range_override,int channel,float set_delay)
{
	int word_out;  /* what is sent to the DAC */
	int cap_range_control;	/* what is actually sent to shift register */
	float adj_setting, setting, min_one_shot_delay;
	int point_found,relay_range,UseNegData,entry,actual_pol,dummy;

	/* abandon if high channel selected by user but not enabled by firmware */
	if (channel && !globals.Flash.ChanKey_delay) {
		return InvalidChannel;
	}

	if (!check_possible_only) {
		int i, check_valid;
		for (i=0; i<max_channels; ++i) {
			TestState[i]=globals.ChannelState[i];
		}
		TestState[channel].delay=set_delay;
		if ((check_valid=Error_check(TestState))) {
			return check_valid;
		}
	}

	/* won't work for all values if apparent fixed delay is less than minimum variable delay */
	min_one_shot_delay = globals.Flash.delay_pwl_time[channel][0][0][0];
	if (   (globals.Flash.delay_shrink[channel] > 0.0) &&
		(globals.Flash.delay_shrink[channel] < min_one_shot_delay)) {
		return DelayRangeError;
	}

	/* If we need the advance mode for all positive values of delay (for the external trigger
 	   mode to work as expected), then we must have 

		( Flash.delay_shrink - Flash.propagation_delay ) >= min_one_shot_delay

		shrink ~ fixed DDU delay (around 25 ns)
		prop ~ propagation delay of main PG - prog delay of SYNC

	   This is not normally the case, because Flash.propagation_delay can be large.

	   If necessary, delay_shrink can be increased by using a larger DDU, or the
	   propagation_delay difference can be reduced by increasing the delay of the
	   SYNC pulse.
	*/

	if (	 globals.Flash.force_monotonic_ext_trig_delay[channel] &&	// specials mostly
		(globals.Flash.delay_shrink[channel] > 0.0) &&			// ignore if delay shrink cal not set
		(globals.Flash.propagation_delay[channel] > 0.0) &&
		((globals.Flash.delay_shrink[channel] - globals.Flash.propagation_delay[channel]) < min_one_shot_delay)) {
		return ExternalModeDelayError;
	}

	
	/* tweak depending on polarity */
	adj_setting = set_delay;
	if (globals.ChannelState[channel].amplitude<0.0) {
		adj_setting -= globals.Flash.delay_pol_tweak[channel][1];
	} else {
		adj_setting -= globals.Flash.delay_pol_tweak[channel][0];
	}

	setting = adj_setting + globals.Flash.delay_shrink[channel] - globals.Flash.propagation_delay[channel];

	if (setting >= min_one_shot_delay) {
                Set_AdvDel(channel,to_Advance);
        } else {
		setting = globals.Flash.delay_shrink[channel] + globals.Flash.propagation_delay[channel] - adj_setting;
		Set_AdvDel(channel,to_Delay);
	}

	/* protect against impossible settings during first turn-on */
	if (setting<zero_equiv_timing) {
		return HardwareError;
	}

	/* find appropriate range/fine settings from piece-wise linear data in flash memory */
	if (word_override) {
		word_out=word_override;
		relay_range=range_override;
	} else {
		/* set the amplitude controls now. */
		int status;
		if ((status=Set_VI_Control(pwl_delay_values,channel,setting,&point_found,
		                           &relay_range,&dummy,&UseNegData,&entry,&word_out,&actual_pol))) {
			return status;
		}
	}

	if (check_possible_only) {
		if (point_found) {
			return OK;
		} else {
			return CalibrationMinMaxError;
		}
	}


	if (relay_range==0) {
		cap_range_control=0;
	} else {
		cap_range_control = 1 << (relay_range-1);
	}

	if (channel==0) {
		set_dac(globals.Flash.delay_dac[channel],word_out);
		set_shiftreg_bits(SR_2, POS_8, SEVEN_BITS, cap_range_control);
	}

	if (channel==1) {
		control_pcb107(globals.Flash.I2C_port_for_CH2_delay,globals.Flash.delay_dac[channel],word_out,relay_range);
	}

	globals.ChannelState[channel].delay=set_delay;
	Set_Update_Chans();

	return OK;
}


int Set_Double(int channel,int new_setting)
{
	int error_num;
	int i;

	/* abandon if high channel selected by user but not enabled by firmware */
	if (channel && !globals.Flash.ChanKey_double_pulse) {
		return InvalidChannel;
	}

	for (i=0; i<max_channels; ++i) {
		TestState[i]=globals.ChannelState[i];
	}
	TestState[channel].double_pulse=new_setting;
	if ((error_num=Error_check(TestState))) {
		return error_num;
	}

	if (new_setting == double_off) {
		set_shiftreg_bits(SR_0, POS_5, ONE_BIT, BIT_LOW);	// turn double-pulse off
		Ctrl_PRF_Limiter (1);				// normal prf limiting
	} else {
		set_shiftreg_bits(SR_0, POS_5, ONE_BIT, BIT_HIGH);	// turn double-pulse on
		Ctrl_PRF_Limiter (0);					// disable prf limiter
	}

	globals.ChannelState[channel].double_pulse=new_setting;
	Set_Update_Chans();

	return OK;
}


void Ctrl_PRF_Limiter(int enable)
{
	if (enable && globals.Flash.prf_limiter) {
		set_shiftreg_bits(SR_0, POS_7, ONE_BIT, BIT_HIGH);	/* turn prf limiter on */
	} else {
		set_shiftreg_bits(SR_0, POS_7, ONE_BIT, BIT_LOW);		/* turn prf limiter off */
	}

	Main_update_shift_registers();

	return;
}


void Set_Mux(int channel)
{
	int mux_out;

	if (globals.ChannelState[channel].func_mode==dc_mode_on && globals.ChannelState[channel].polarity==pol_norm) {
		mux_out=4;
	} else if (globals.ChannelState[channel].func_mode==dc_mode_on && globals.ChannelState[channel].polarity!=pol_norm) {
		mux_out=5;
	} else if (globals.ChannelState[channel].pw_ctrl_mode!=pw_in_out && globals.ChannelState[channel].polarity==pol_norm) {
		mux_out=2;
	} else if (globals.ChannelState[channel].pw_ctrl_mode!=pw_in_out && globals.ChannelState[channel].polarity!=pol_norm) {
		mux_out=3;
	} else if (globals.ChannelState[channel].polarity==pol_norm) {
		mux_out=0;
	} else if (globals.ChannelState[channel].polarity!=pol_norm) {
		mux_out=1;
	}

	if (globals.ChannelState[channel].test_delay_mode==YES) {
		mux_out=6;
	}

	set_shiftreg_bits(SR_1, POS_0, THREE_BITS, mux_out);
}


int Set_Func(int channel,int mode)
{
	int i;
	int check_valid;

	/* abandon if high channel selected by user but not enabled by firmware */
	if (channel && !globals.Flash.ChanKey_func_mode) {
		return InvalidChannel;
	}

	if ( (mode==dc_mode_on) && !globals.Flash.dc_mode_allowed[channel]) {
		return SyntaxError;
	}

	/* some modes are for function generators only */
	if ( ((mode==sin_mode_on) || (mode==squ_mode_on) || (mode==tri_mode_on) || (mode==amp_mode_on))
	                && !globals.Flash.is_func_gen[channel]) {
		return SyntaxError;
	}

	for (i=0; i<max_channels; ++i) {
		TestState[i]=globals.ChannelState[i];
	}
	TestState[channel].func_mode=mode;
	if ((check_valid=Error_check(TestState))) {
		return check_valid;
	}

	if (globals.Flash.is_func_gen[channel]) {
		/* set sin, tri, squ lines */
		set_shiftreg_bits(SR_3, POS_17, THREE_BITS, mode);

		/* set amplify mode line */

		int amplify_bit = XTR_POS + globals.Flash.ext_amplify_xtra_rly[channel];
		if (mode==amp_mode_on) {
			set_shiftreg_bits(SR_2, amplify_bit, ONE_BIT, BIT_HIGH);
		} else {
			set_shiftreg_bits(SR_2, amplify_bit, ONE_BIT, BIT_LOW);
		}
	}


	/* reset pulse width and delay to safe values */
	if (globals.ChannelState[channel].func_mode!=mode) {
		globals.ChannelState[channel].func_mode=mode;

		Set_Pw(0,0,0,channel,globals.Flash.min_pw[channel],0);
		Set_Delay(0,0,0,channel,globals.Flash.min_delay[channel]);

		/* update frequency, to reset func gen control lines */
		Set_frequency(0,0,0,channel,globals.ChannelState[channel].frequency);

		Set_current_limit(0,channel,globals.ChannelState[channel].soft_current_limit);
	}

	Set_Mux(channel);
	Set_Update_Chans();

	return OK;
}


int Set_Pol(int channel,int mode)
{
	int error_num, i;

	/* abandon if high channel selected by user but not enabled by firmware */
	if (channel && !globals.Flash.ChanKey_polarity) {
		return InvalidChannel;
	}

	/* duty cycle check */
	for (i=0; i<max_channels; ++i) {
		TestState[i]=globals.ChannelState[i];
	}
	TestState[channel].polarity=mode;
	if ((error_num=Error_check(TestState))) {
		return error_num;
	}

	globals.ChannelState[channel].polarity=mode;

	Set_Mux(channel);
	Set_Update_Chans();

	return OK;
}


int Set_Hold(int channel,int mode)
{
	/* abandon if high channel selected by user but not enabled by firmware */
	if (channel && !globals.Flash.ChanKey_hold_setting) {
		return InvalidChannel;
	}

	globals.ChannelState[channel].hold_setting=mode;
	Set_Update_Chans();

	return OK;
}


int Set_Output_State(int channel,int mode)
{
	int i, error_num;

	/* abandon if high channel selected by user but not enabled by firmware */
	if (channel && !globals.Flash.ChanKey_output_state) {
		return InvalidChannel;
	}

	for (i=0; i<max_channels; ++i) {
		TestState[i]=globals.ChannelState[i];
	}
	TestState[channel].output_state=mode;
	if ((error_num=Error_check(TestState))) {
		return error_num;
	}

	/* suppress triggering during relay bounce */
	/* set gate bit high, if not already high */

	for (i=0; i<(globals.Flash.ChanKey_frequency?globals.Flash.channels:1); ++i)
		if (globals.ChannelState[i].trigger_source!=source_hold) {
			bus_setpin(O_GATE, 1);
		}

	if (mode==output_on || !globals.Flash.on_off_used) {
		/* do not permit enabling of output during overload condition */
		if ( (I2C_Read(PCF8574A+Button_Press_Port) & 0x40) == 0) {
			return Overload_Detected;
		}

		bus_setpin(OUTPUT_RELAY, 1);	 /* turn output on */
		bus_setpin(PW_ENABLE, 1);	  /* enable PW circuit */

		g_usleep (800e3);		/* wait for extended-off circuit to work */

		globals.Timers.last_activity_at[channel] = sec_timer();
	} else {
		bus_setpin(PW_ENABLE, 0);	 /* disable PW circuit */
		bus_setpin(OUTPUT_RELAY, 0);	 /* turn output off */
		globals.Timers.last_activity_at[channel] = 0L;
	}

	globals.ChannelState[channel].output_state=mode;
	Set_Update_Chans();


	/* keep trigger suppressed during output relay update */
	/* trigger gate will be released at end of following Main_update_shift_registers(); */

	return OK;
}


int Set_Trig_Source(int channel,int mode)
{
	int gate_mode;
	int use_pw_ctrl_mode;
	int reset_freq;
	float use_freq;

	/* abandon if high channel selected by user but not enabled by firmware */
	if (channel && !globals.Flash.ChanKey_trigger_source) {
		return InvalidChannel;
	}

	use_pw_ctrl_mode = globals.ChannelState[channel].pw_ctrl_mode;
	use_freq = globals.ChannelState[channel].frequency;
	reset_freq = 0;

	if (globals.ChannelState[channel].trigger_source!=mode) {
		int i, error_num;
		for (i=0; i<max_channels; ++i) {
			TestState[i]=globals.ChannelState[i];
		}
		TestState[channel].trigger_source = mode;

		if ((globals.ChannelState[channel].trigger_source != source_hold) && (mode != source_hold)) {
			reset_freq = 1;
			use_freq = globals.Flash.min_freq[channel];
			// turn ab mode off when switching trigger source
			if (use_pw_ctrl_mode == pw_in_out) {
				use_pw_ctrl_mode = pw_normal;
			}
		}

		TestState[channel].pw_ctrl_mode = use_pw_ctrl_mode;
		TestState[channel].frequency = use_freq;

		if ((error_num=Error_check(TestState))) {
			return error_num;
		}

		if (reset_freq) {
			Set_frequency(0,0,0,channel,use_freq);
			Set_Pwmode(channel,use_pw_ctrl_mode);
		}
	}

	globals.ChannelState[channel].trigger_source=mode;
	Set_Update_Chans();


	/* Function generator operate in the "external" mode only. The internal oscillator is never used. */
	if (globals.Flash.is_func_gen[channel]) {
		mode=source_external;
	}

	gate_mode = 0;
	if (mode==source_internal) {
		/* frequency is automatically lowered if pw or delay have been changed to conflicting */
		/* settings while in a non-internal mode */
		set_shiftreg_bits(SR_0, POS_2, THREE_BITS, 0x0);
	} else if (mode==source_external) {
		globals.ChannelState[channel].hold_setting=hold_width;
		set_shiftreg_bits(SR_0, POS_2, THREE_BITS, 0x3);
	} else if (mode==source_manual) {
		globals.ChannelState[channel].hold_setting=hold_width;
		set_shiftreg_bits(SR_0, POS_2, THREE_BITS, 0x1);
	} else if (mode==source_hold) {
		gate_mode=1;
	} else if (mode==source_immediate) {
		set_shiftreg_bits(SR_0, POS_2, THREE_BITS, 0x4);
		Main_update_shift_registers();
		set_shiftreg_bits(SR_0, POS_2, THREE_BITS, 0x7);
	}

	Main_update_shift_registers();
	bus_setpin(O_GATE, gate_mode);	/* change gate only after new source set */

	return OK;
}


int Set_Gate_Sync(int channel,int mode)
{
	/* abandon if high channel selected by user but not enabled by firmware */
	if (channel && !globals.Flash.ChanKey_gate_type) {
		return InvalidChannel;
	}

	if (mode==gate_sync) {
		set_shiftreg_bits(SR_0, POS_0, ONE_BIT, BIT_LOW);
	} else {
		set_shiftreg_bits(SR_0, POS_0, ONE_BIT, BIT_HIGH);
	}

	globals.ChannelState[channel].gate_type=mode;
	Set_Update_Chans();

	return OK;
}


int Set_Gate_Level(int channel,int mode)
{
	/* abandon if high channel selected by user but not enabled by firmware */
	if (channel && !globals.Flash.ChanKey_gate_level) {
		return InvalidChannel;
	}

	if (mode==0) {
		set_shiftreg_bits(SR_0, POS_1, ONE_BIT, BIT_HIGH);
	} else {
		set_shiftreg_bits(SR_0, POS_1, ONE_BIT, BIT_LOW);
	}

	globals.ChannelState[channel].gate_level=mode;
	Set_Update_Chans();

	return OK;
}


static void start_gate_override ()
{
	/* just set gate bit high, if not already high */
	int i;
	for (i=0; i<(globals.Flash.ChanKey_frequency?globals.Flash.channels:1); ++i)
		if (globals.ChannelState[i].trigger_source!=source_hold) {
			bus_setpin(O_GATE, 1);
		}
}


static void stop_gate_override ()
{
	/* release gate (if not in hold mode) */
	int i;
	for (i=0; i<(globals.Flash.ChanKey_frequency?globals.Flash.channels:1); ++i)
		if (globals.ChannelState[i].trigger_source!=source_hold) {
			bus_setpin(O_GATE, 0);
		}
}


int Set_zout(int channel,int setting,int really_for_zout)
{
	int update_zout_slowly;

	/* abandon if high channel selected by user but not enabled by firmware */
	if (channel && !globals.Flash.ChanKey_zout) {
		return InvalidChannel;
	}

	update_zout_slowly=NO;

	/* The Zout hardware and software is controlled by PW commands in the AVPP. */
	/* This mode is signalled by really_for_zout=0 */

	/* first channel uses dedicated Zout line */
	if (channel==0) {
		if (setting!=globals.Flash.zout_max[channel]) {
			set_shiftreg_bits(SR_3, POS_12, ONE_BIT, BIT_HIGH);	/* power zout relay */
		} else {
			set_shiftreg_bits(SR_3, POS_12, ONE_BIT, BIT_LOW);	/* de-power zout relay */
		}
	}

	/* second channel uses dedicated xtra-rly2 line */
	if (channel==1) {
		if (setting!=globals.Flash.zout_max[channel]) {
			set_shiftreg_bits(SR_2, XTR_POS + 2, ONE_BIT, BIT_HIGH);	/* power zout relay */
		} else {
			set_shiftreg_bits(SR_2, XTR_POS + 2, ONE_BIT, BIT_LOW);	/* de-power zout relay */
		}
	}

	if (setting!=globals.ChannelState[channel].zout) {
		globals.Changes.update_zout=YES;

		/* allow extra time for cap banks to discharge if going from
			50 Ohms to 2 Ohm Zout */
		if (setting<globals.ChannelState[channel].zout) {
			update_zout_slowly=YES;
		}
	}

	globals.ChannelState[channel].zout=setting;
	Set_Update_Chans();

	/* Skip this stuff if this is actually for AVPP PW control */
	if (really_for_zout) {
		if (globals.Changes.update_zout) {
			/* change of Zout may require change in amplitude controls */
			Set_Amplitude(0,0,0,0,0,0,channel,globals.ChannelState[channel].amplitude,0);
		}

		if (update_zout_slowly) {
			globals.Timers.Relay_Switching_Delay_in_Milliseconds=(long) (1000L * globals.Flash.extended_relay_delay_in_sec);
		}
	}

	return OK;
}


int Set_Load(int channel, float value)
{
	int error_num;

	/* abandon if high channel selected by user but not enabled by firmware */
	if (channel && !globals.Flash.ChanKey_load_type) {
		return InvalidChannel;
	}

	if (globals.Flash.enable_avrq_extra_ampls) {
		return Set_avrq_res(0,0,0,channel,value);
	}

	/* duty cycle check */
	int i;
	for (i=0; i<max_channels; ++i) {
		TestState[i]=globals.ChannelState[i];
	}
	TestState[channel].load_type=value;
	if ((error_num=Error_check(TestState))) {
		return error_num;
	}

	if (globals.ChannelState[channel].load_type!=value) {
		globals.Changes.update_load=YES;
	}

	globals.ChannelState[channel].load_type=value;
	Set_Update_Chans();

	if (globals.Changes.update_load) {
		/* change of load may require change in amplitude controls */
		Set_Amplitude(0,0,0,0,0,0,channel,globals.ChannelState[channel].amplitude,0);
	}

	return OK;
}


int Set_EA(int channel,int mode)
{
	char set_DAC_to_zero;

	/* abandon if high channel selected by user but not enabled by firmware */
	if (channel && !globals.Flash.ChanKey_amplitude) {
		return InvalidChannel;
	}

	if (!(globals.Flash.ea_enabled[channel]) && (mode==amp_mode_ea) ) {
		return SyntaxError;
	}
	if (!(globals.Flash.ext_amplify_enabled[channel]) && (mode==amp_mode_amplify) ) {
		return SyntaxError;
	}

	set_DAC_to_zero=YES;
	if (mode==amp_mode_amplify) {
		set_DAC_to_zero=NO;
	}

	/* set to max amplitude range when switching from normal to EA or Ext Amplify */
	if ( (globals.ChannelState[channel].amp_mode==amp_mode_normal) && (mode!=amp_mode_normal) ) {
		if (globals.ChannelState[channel].amplitude<0.0)
			Set_Amplitude(0,0,0,0,0,set_DAC_to_zero,channel,
			              globals.ChannelState[channel].amplitude=globals.Flash.min_ampl[channel],0); /* most negative */
		else
			Set_Amplitude(0,0,0,0,0,set_DAC_to_zero,channel,
			              globals.ChannelState[channel].amplitude=globals.Flash.max_ampl[channel],0); /* most positive */
	}


	/* protect when switching from EA or Ext Amplify back to normal */
	if ( (globals.ChannelState[channel].amp_mode!=amp_mode_normal) && (mode==amp_mode_normal) ) {
		if (globals.ChannelState[channel].amplitude<0.0) {
			Set_Amplitude(0,0,0,0,0,0,channel,globals.ChannelState[channel].amplitude=-smallest_allowed_number,0);
		} else {
			Set_Amplitude(0,0,0,0,0,0,channel,globals.ChannelState[channel].amplitude=smallest_allowed_number,0);
		}
	}


	if (globals.Flash.ea_enabled[channel]) {
		int ea_reg, ea_bit;

		if (!channel) {
			ea_bit = 16;
			ea_reg = SR_3;
		} else {
			ea_bit = globals.Flash.ea_xtra_rly[channel] + XTR_POS;
			ea_reg = SR_2;
		}

		if (mode==amp_mode_ea) {
			set_shiftreg_bits(ea_reg, ea_bit, ONE_BIT, BIT_HIGH);
		} else {
			set_shiftreg_bits(ea_reg, ea_bit, ONE_BIT, BIT_LOW);
		}
	}


	if (globals.Flash.ext_amplify_enabled[channel]) {
		int amplify_bit = XTR_POS + globals.Flash.ext_amplify_xtra_rly[channel];

		if (mode==amp_mode_amplify) {
			set_shiftreg_bits(SR_2, amplify_bit, ONE_BIT, BIT_HIGH);
		} else {
			set_shiftreg_bits(SR_2, amplify_bit, ONE_BIT, BIT_LOW);
		}
	}


	if (globals.ChannelState[channel].amp_mode!=mode) {
		globals.Changes.update_amp=YES;
	}

	globals.ChannelState[channel].amp_mode=mode;
	Set_Update_Chans();

	return OK;
}


int Set_AdvDel(int channel,int setting)
{
	if (channel == 0) {
		if (setting==to_Advance) {
			set_shiftreg_bits(SR_0, POS_6, ONE_BIT, BIT_HIGH);	/* turn advance on */
		} else {
			set_shiftreg_bits(SR_0, POS_6, ONE_BIT, BIT_LOW);	/* turn delay on */
		}
		Set_Update_Chans();
	}

	// no equivalent for CH2

	return OK;
}


int Set_Pwmode(int channel,int mode)
{
	/* abandon if high channel selected by user but not enabled by firmware */
	if (channel && !globals.Flash.ChanKey_pw) {
		return InvalidChannel;
	}

	if (!(globals.Flash.pw_ab_mode_enabled[channel]) && (mode==pw_in_out)) {
		return SyntaxError;
	}

        if (!(globals.Flash.ew_enabled[channel]) && (mode==pw_ew_ext)) {
                return SyntaxError;
        }

	if ((mode==pw_in_out) && (globals.ChannelState[channel].trigger_source!=source_external)) {
		return AB_Mode_Error;
	}

	globals.ChannelState[channel].pw_ctrl_mode=mode;

	if (globals.Flash.ew_enabled[channel]) {
		if (mode==pw_ew_ext) {
			set_shiftreg_bits(SR_2, globals.Flash.ew_xtra_rly[channel] + XTR_POS, ONE_BIT, BIT_HIGH);
		} else {
			set_shiftreg_bits(SR_2, globals.Flash.ew_xtra_rly[channel] + XTR_POS, ONE_BIT, BIT_LOW);
		}
	}

	Set_Mux(0);
	Set_Update_Chans();

	return OK;
}


int Set_Update_Chans(void)
{
	int num_of_chan;

	num_of_chan=globals.Flash.channels;
	if (num_of_chan==0) {
		num_of_chan=1;
	}

	if (num_of_chan>1) {
		int i;
		if (!globals.Flash.ChanKey_frequency) for (i=1; i<num_of_chan; ++i) {
				globals.ChannelState[i].frequency=globals.ChannelState[0].frequency;
			}
		if (!globals.Flash.ChanKey_delay) for (i=1; i<num_of_chan; ++i) {
				globals.ChannelState[i].delay=globals.ChannelState[0].delay;
			}
		if (!globals.Flash.ChanKey_pw) for (i=1; i<num_of_chan; ++i) {
				globals.ChannelState[i].pw=globals.ChannelState[0].pw;
			}
		if (!globals.Flash.ChanKey_amplitude) for (i=1; i<num_of_chan; ++i) {
				globals.ChannelState[i].amplitude=globals.ChannelState[0].amplitude;
			}
		if (!globals.Flash.ChanKey_offset) for (i=1; i<num_of_chan; ++i) {
				globals.ChannelState[i].offset=globals.ChannelState[0].offset;
			}

		if (!globals.Flash.ChanKey_Curr_Mon_value) for (i=1; i<num_of_chan; ++i) {
				globals.ChannelState[i].Curr_Mon_value=globals.ChannelState[0].Curr_Mon_value;
			}
		if (!globals.Flash.ChanKey_Curr_Mon_offset) for (i=1; i<num_of_chan; ++i) {
				globals.ChannelState[i].Curr_Mon_offset=globals.ChannelState[0].Curr_Mon_offset;
			}

		if (!globals.Flash.ChanKey_zout) for (i=1; i<num_of_chan; ++i) {
				globals.ChannelState[i].zout=globals.ChannelState[0].zout;
			}
		if (!globals.Flash.ChanKey_hold_setting) for (i=1; i<num_of_chan; ++i) {
				globals.ChannelState[i].hold_setting=globals.ChannelState[0].hold_setting;
			}
		if (!globals.Flash.ChanKey_double_pulse) for (i=1; i<num_of_chan; ++i) {
				globals.ChannelState[i].double_pulse=globals.ChannelState[0].double_pulse;
			}
		if (!globals.Flash.ChanKey_pw) for (i=1; i<num_of_chan; ++i) {
				globals.ChannelState[i].pw_ctrl_mode=globals.ChannelState[0].pw_ctrl_mode;
			}
		if (!globals.Flash.ChanKey_func_mode) for (i=1; i<num_of_chan; ++i) {
				globals.ChannelState[i].func_mode=globals.ChannelState[0].func_mode;
			}
		if (!globals.Flash.ChanKey_polarity) for (i=1; i<num_of_chan; ++i) {
				globals.ChannelState[i].polarity=globals.ChannelState[0].polarity;
			}
		if (!globals.Flash.ChanKey_output_state) for (i=1; i<num_of_chan; ++i) {
				globals.ChannelState[i].output_state=globals.ChannelState[0].output_state;
			}
		if (!globals.Flash.ChanKey_gate_type) for (i=1; i<num_of_chan; ++i) {
				globals.ChannelState[i].gate_type=globals.ChannelState[0].gate_type;
			}
		if (!globals.Flash.ChanKey_trigger_source) for (i=1; i<num_of_chan; ++i) {
				globals.ChannelState[i].trigger_source=globals.ChannelState[0].trigger_source;
			}
		if (!globals.Flash.ChanKey_amp_mode) for (i=1; i<num_of_chan; ++i) {
				globals.ChannelState[i].amp_mode=globals.ChannelState[0].amp_mode;
			}
		if (!globals.Flash.ChanKey_gate_level) for (i=1; i<num_of_chan; ++i) {
				globals.ChannelState[i].gate_level=globals.ChannelState[0].gate_level;
			}
		if (!globals.Flash.ChanKey_load_type) for (i=1; i<num_of_chan; ++i) {
				globals.ChannelState[i].load_type=globals.ChannelState[0].load_type;
			}
		if (!globals.Flash.ChanKey_test_delay_mode) for (i=1; i<num_of_chan; ++i) {
				globals.ChannelState[i].test_delay_mode=globals.ChannelState[0].test_delay_mode;
			}
		if (!globals.Flash.ChanKey_os_mode) for (i=1; i<num_of_chan; ++i) {
				globals.ChannelState[i].os_mode=globals.ChannelState[0].os_mode;
			}
		if (!globals.Flash.ChanKey_Burst_Count) for (i=1; i<num_of_chan; ++i) {
				globals.ChannelState[i].burst_count=globals.ChannelState[0].burst_count;
			}
		if (!globals.Flash.ChanKey_Burst_Time) for (i=1; i<num_of_chan; ++i) {
				globals.ChannelState[i].burst_time=globals.ChannelState[0].burst_time;
			}
		if (!globals.Flash.ChanKey_rise_time) for (i=1; i<num_of_chan; ++i) {
				globals.ChannelState[i].rise_time=globals.ChannelState[0].rise_time;
			}
		if (!globals.Flash.ChanKey_slew) for (i=1; i<num_of_chan; ++i) {
				globals.ChannelState[i].slew=globals.ChannelState[0].slew;
			}

		for (i=1; i<num_of_chan; ++i) {
			globals.ChannelState[i].vcc1=globals.ChannelState[0].vcc1;
			globals.ChannelState[i].vcc2=globals.ChannelState[0].vcc2;
			globals.ChannelState[i].vlogic=globals.ChannelState[0].vlogic;
		}

	}

	return OK;
}


int Set_Amp_Calib(int channel,float meas_ampl)
{
	float change_ratio;
	int i,status;
	int point_found,relay_range,UseNegData,entry,word_out,actual_pol,atten_range;
	int eprom_loc;

	/* use for all channels */

	/* get current range and polarity settings */
	Set_VI_Control(pwl_ampl_values,channel,globals.ChannelState[channel].amplitude,&point_found,
	               &relay_range,&atten_range,&UseNegData,&entry,&word_out,&actual_pol);

	if (fabs(globals.ChannelState[channel].amplitude)<globals.Flash.ampl_zero_equiv[channel]) {
		return NeedNonZeroAmpl;
	}

	change_ratio = meas_ampl/globals.ChannelState[channel].amplitude;

	if (change_ratio>1.3 || change_ratio < 0.7) {
		return CalibrationPercentError;
	}

	for (i=0; i<10; ++i)
		globals.Flash.ampl_pwl_amp[channel][relay_range][UseNegData][i]=
		        globals.Flash.ampl_pwl_amp[channel][relay_range][UseNegData][i]*change_ratio;


	/* see if new data prevents min/max from being obtained */
	status=Check_MinMax_Cal(channel,pwl_ampl_values);

	if (status) {
		/* revert to original calibration */
		for (i=0; i<10; ++i)
			globals.Flash.ampl_pwl_amp[channel][relay_range][UseNegData][i]=
			        globals.Flash.ampl_pwl_amp[channel][relay_range][UseNegData][i]/change_ratio;
	}

	eprom_loc = (char *) &(globals.Flash.ampl_pwl_amp) - (char *) &(globals.Flash.flash_start);
	writeUserBlock(&globals.Flash, eprom_loc, sizeof(globals.Flash.ampl_pwl_amp));

	return status;
}


int Set_Mon_Calib(int channel,float meas_ampl)
{
	float change_ratio;
	int point_found,relay_range,UseNegData,entry,word_out,actual_pol,eprom_loc,atten_range;

	/* use for all channels */

	/* get current range and polarity settings */
	Set_VI_Control(pwl_ampl_values,channel,globals.ChannelState[channel].amplitude,&point_found,
	               &relay_range,&atten_range,&UseNegData,&entry,&word_out,&actual_pol);

	if (fabs(globals.ChannelState[channel].Curr_Mon_value)< (5.0 * globals.Flash.monitor_step[channel])) {
		return NeedNonZeroAmpl;
	}

	if (globals.Flash.sep_posneg_mon_ratio[channel] && globals.ChannelState[channel].amplitude<0.0) {
		UseNegData=1;
	} else {
		UseNegData=0;
	}

	change_ratio = meas_ampl/globals.ChannelState[channel].Curr_Mon_value;

	if (change_ratio>1.3 || change_ratio < 0.7) {
		return CalibrationPercentError;
	}

	globals.Flash.mon_vi_ratio[channel][relay_range][UseNegData]=globals.Flash.mon_vi_ratio[channel][relay_range][UseNegData]/change_ratio;

	eprom_loc = (char *) &(globals.Flash.mon_vi_ratio) - (char *) &(globals.Flash.flash_start);
	writeUserBlock(&globals.Flash, eprom_loc, sizeof(globals.Flash.mon_vi_ratio));

	return OK;
}


int Set_Logic_Level(int channel,int mode)
{
	if (channel==0) {
		if (mode==logic_ecl) {
			set_shiftreg_bits(SR_3, POS_14, ONE_BIT, BIT_HIGH);
		} else {
			set_shiftreg_bits(SR_3, POS_14, ONE_BIT, BIT_LOW);
		}
	}

	/* second channel uses xtra-rly3 */
	if (channel==1) {
		if (mode==logic_ecl) {
			set_shiftreg_bits(SR_3, XTR_POS + 3, ONE_BIT, BIT_HIGH);
		} else {
			set_shiftreg_bits(SR_3, XTR_POS + 3, ONE_BIT, BIT_LOW);
		}
	}

	globals.ChannelState[channel].logic_level=mode;
	Set_Update_Chans();

	return OK;
}


int Set_OS_Calib(int channel,float meas_ampl)
{
	float change_ratio;
	int i,status,eprom_loc;

	if (fabs(globals.ChannelState[channel].offset)<globals.Flash.ampl_zero_equiv[channel]) {
		return NeedNonZeroAmpl;
	}

	change_ratio = meas_ampl/globals.ChannelState[channel].offset;

	if (change_ratio>1.3 || change_ratio < 0.7) {
		return CalibrationPercentError;
	}

	for (i=0; i<10; ++i) {
		globals.Flash.os_pwl_amp[channel][0][0][i]=globals.Flash.os_pwl_amp[channel][0][0][i]*change_ratio;
	}


	/* see if new data prevents min/max from being obtained */
	status=Check_MinMax_Cal(channel,pwl_os_values);

	if (status) {
		/* revert to original calibration */
		for (i=0; i<10; ++i) {
			globals.Flash.os_pwl_amp[channel][0][0][i]=globals.Flash.os_pwl_amp[channel][0][0][i]/change_ratio;
		}
	}

	eprom_loc = (char *) &(globals.Flash.os_pwl_amp) - (char *) &(globals.Flash.flash_start);
	writeUserBlock(&globals.Flash, eprom_loc, sizeof(globals.Flash.os_pwl_amp));

	return status;
}


float Get_PW_shift(int channel)
{
	int UseNegData;

	UseNegData = 0;
	if (globals.ChannelState[channel].amplitude < 0.0) {
		UseNegData = 1;
	}

	return globals.Flash.pulse_width_pol_tweak[channel][UseNegData];
}


int Set_PW_shift(int channel,float new_shift)
{
	float orig_shift;
	int UseNegData, status, eprom_loc;

	UseNegData = 0;
	if (globals.ChannelState[channel].amplitude < 0.0) {
		UseNegData = 1;
	}

	orig_shift = globals.Flash.pulse_width_pol_tweak[channel][UseNegData];
	globals.Flash.pulse_width_pol_tweak[channel][UseNegData] = new_shift;

	status=Check_MinMax_Cal(channel,pwl_pw_values);

	if	(status) {
		globals.Flash.pulse_width_pol_tweak[channel][UseNegData] = orig_shift;
		return status;
	}

	eprom_loc = (char *) &(globals.Flash.pulse_width_pol_tweak[channel][UseNegData]) - (char *) &(globals.Flash.flash_start);
	writeUserBlock(&globals.Flash, eprom_loc, sizeof(globals.Flash.pulse_width_pol_tweak[channel][UseNegData]));

	return Set_Pw(0,0,0,channel,globals.ChannelState[channel].pw,0);
}


int Set_Route(int channel, int module, int mode)
{
	int error_num;
	int word_out;

	if (channel && !globals.Flash.ChanKey_route) {
		return InvalidChannel;
	}

	if (!globals.Flash.routing_required[channel]) {
		return Unrecognized;
	}

	if (module >= globals.Flash.routing_required[channel]) {
		return IllegalParameter;
	}

	if ((mode < 1) || (mode > globals.Flash.routing_max_pins[channel])) {
		return OutOfRange;
	}

	int i;
	for (i=0; i<max_channels; ++i) {
		TestState[i]=globals.ChannelState[i];
	}

	if (globals.Flash.routing_max_pins[channel] > 2) {
		if (module == ROUTE_PRIMARY) {
			TestState[channel].route_primary=mode;
			if ((error_num=Error_check(TestState))) {
				return error_num;
			}

			if (mode!=globals.ChannelState[channel].route_primary) {
				globals.ChannelState[channel].route_primary=mode;
			}
		}

		if (module == ROUTE_SECONDARY) {
			TestState[channel].route_secondary=mode;
			if ((error_num=Error_check(TestState))) {
				return error_num;
			}

			if (mode!=globals.ChannelState[channel].route_secondary) {
				globals.ChannelState[channel].route_secondary=mode;
			}
		}
		word_out =  (globals.ChannelState[channel].route_primary-1) |
		            ((globals.ChannelState[channel].route_secondary-1) << 4);
		I2C_Write(PCF8574+Second_PW_Port,word_out);
	} else {
		/* AVD-D2-B */
		if (mode==2) {
			set_shiftreg_bits(SR_2, XTR_POS + 1, ONE_BIT, BIT_HIGH);
		} else {
			set_shiftreg_bits(SR_2, XTR_POS + 1, ONE_BIT, BIT_LOW);
		}
		globals.ChannelState[channel].route_primary=mode;
	}

	Set_Update_Chans();

	return OK;
}


int Set_EO(int channel,int mode)
{
	/* abandon if high channel selected by user but not enabled by firmware */
	if (channel && !globals.Flash.ChanKey_offset) {
		return InvalidChannel;
	}

	if (!globals.Flash.eo_enabled[channel]) {
		return SyntaxError;
	}

	if (globals.ChannelState[channel].os_mode != mode) {
		Set_Offset(0,0,0,0,channel,0.0);
	}

	if (mode) {
		set_shiftreg_bits(SR_2, XTR_POS + 4, ONE_BIT, BIT_HIGH);
	} else {
		set_shiftreg_bits(SR_2, XTR_POS + 4, ONE_BIT, BIT_LOW);
	}

	if (globals.ChannelState[channel].os_mode!=mode) {
		globals.Changes.update_os=YES;
	}

	globals.ChannelState[channel].os_mode=mode;
	Set_Update_Chans();
	Main_update_shift_registers();

	return OK;
}


int Set_Dly_Shr_Nom(int channel,int calibration_point_number)
{
	float temp1, temp2, ampl;

	Main_Rst();
	Main_update_shift_registers();

	Set_frequency(0,0,0,globals.Flash.ChanKey_frequency?channel:0,5.0e0);

	if (globals.Flash.max_ampl[channel] > 0.0) {
		ampl = globals.Flash.max_ampl[channel];
	} else {
		ampl = globals.Flash.min_ampl[channel];
	}
	Set_Amplitude(0,0,0,0,0,0,channel,ampl,0);


	/* Set delay constants to zero, temporarily, for nulling purposes. */
	/* Restore the constants after the delay is set, so that the unit */
	/* will not suffer from an interrupted calibration. */

	temp1=globals.Flash.propagation_delay[channel];
	temp2=globals.Flash.delay_shrink[channel];
	globals.Flash.propagation_delay[channel]=0.0;
	globals.Flash.delay_shrink[channel]=0.0;

	if (calibration_point_number==1) {
		Set_Delay(0,0,0,channel,75e-9);
	} else {
		Set_Delay(0,0,0,channel,-75e-9);
	}

	/* user must set output on, when ready */

	globals.Flash.propagation_delay[channel]=temp1;
	globals.Flash.delay_shrink[channel]=temp2;

	Main_update_shift_registers();

	Show_Main_Menu();

	return OK;
}


int Set_Dly_Shr_Cal(int channel,int calibration_point_number,float cal_point)
{
	int eprom_loc;
	float delay1,delay2;

	delay1=globals.Flash.propagation_delay[channel]-globals.Flash.delay_shrink[channel]+75e-9;
	delay2=globals.Flash.propagation_delay[channel]+globals.Flash.delay_shrink[channel]-75e-9;

	if (calibration_point_number==1) {
		delay1=cal_point;
	} else {
		delay2=cal_point;
	}

	if (delay1>50e-6 || delay2>50e-6) {
		return CalibrationPercentError;
	}

	globals.Flash.propagation_delay[channel]=(delay1+delay2)/2;
	globals.Flash.delay_shrink[channel]=( (150e-9)-delay1+delay2)/2;

	eprom_loc = (char *) &(globals.Flash.propagation_delay) - (char *) &(globals.Flash.flash_start);
	writeUserBlock(&globals.Flash, eprom_loc, sizeof(globals.Flash.propagation_delay));

	eprom_loc = (char *) &(globals.Flash.delay_shrink) - (char *) &(globals.Flash.flash_start);
	writeUserBlock(&globals.Flash, eprom_loc, sizeof(globals.Flash.delay_shrink));

	return OK;
}



int Set_Cal_Nom(int channel,int calibration_point_number,int parameter, float *nom_val)
{
	int nominal_wordout,polarity,range,entry,status,num_in_range,num_of_ranges;
	float nominal_val,other_setting,temp1,temp2;
	int reset_state;
	int disable_errors;
	int true_channel;

	true_channel=channel;

	Get_VI_Rng_Info(parameter,channel,calibration_point_number,&range,&polarity,&entry,&num_in_range,&num_of_ranges);
	status=reset_state=disable_errors=0;

	switch (parameter) {
	case (pwl_ampl_values):
		nominal_val=globals.Flash.ampl_pwl_amp[channel][range][polarity][entry];
		if (polarity) {
			nominal_val=-nominal_val;
		}
		nominal_wordout=globals.Flash.ampl_pwl_Vc_norm4095[channel][range][polarity][entry];
		if (Set_Amplitude(0,0,0,0,0,0,channel,globals.Flash.min_ampl[channel],0) || Set_Amplitude(0,0,0,0,0,0,channel,globals.Flash.max_ampl[channel],0)) {
			/* confirm that timing settings can support min/max ampl settings */
			return CalibrationTimingProblem;
		}
		/* auto-set output impedance, as required */
		if (globals.Flash.switchable_zout[channel]) {
			/* if the cal point is in the top used range, then set Zout to min */
			if (range==num_of_ranges) {
				Set_zout(channel,globals.Flash.zout_min[channel],1);
			} else {
				Set_zout(channel,globals.Flash.zout_max[channel],1);
			}
		}
		break;
	case (pwl_os_values):
		nominal_val=globals.Flash.os_pwl_amp[channel][range][polarity][entry];
		nominal_wordout=globals.Flash.os_pwl_Vc_norm4095[channel][range][polarity][entry];
		break;
	case pwl_pw_values:
		nominal_val=globals.Flash.pw_pwl_time[channel][range][polarity][entry];
		nominal_wordout=globals.Flash.pw_pwl_Vc_norm4095[channel][range][polarity][entry];
		reset_state=disable_errors=1;
		break;
	case pwl_delay_values:
		nominal_val=globals.Flash.delay_pwl_time[channel][range][polarity][entry];
		nominal_wordout=globals.Flash.delay_pwl_Vc_norm4095[channel][range][polarity][entry];
		reset_state=disable_errors=1;
		break;
	case pwl_period_values:
		nominal_val=globals.Flash.period_pwl_time[channel][range][polarity][entry];
		nominal_wordout=globals.Flash.period_pwl_Vc_norm4095[channel][range][polarity][entry];
		reset_state=disable_errors=1;
		break;
	case pwl_burst_values:
		nominal_val=globals.Flash.burst_pwl_time[channel][range][polarity][entry];
		nominal_wordout=globals.Flash.burst_pwl_Vc_norm4095[channel][range][polarity][entry];
		reset_state=disable_errors=1;
		break;
	case pwl_rise_time_values:
		nominal_val=globals.Flash.rise_time_pwl_time[channel][range][polarity][entry];
		nominal_wordout=globals.Flash.rise_time_pwl_Vc_norm4095[channel][range][polarity][entry];
		reset_state=disable_errors=1;
		break;
	case pwl_slew_values:
		nominal_val=globals.Flash.slew_pwl_time[channel][range][polarity][entry];
		nominal_wordout=globals.Flash.slew_pwl_Vc_norm4095[channel][range][polarity][entry];
		break;
	case pwl_vcc1_values:
		nominal_val=globals.Flash.vcc1_pwl_amp[0][0][0][entry];
		nominal_wordout=globals.Flash.vcc1_pwl_Vc_norm4095[0][0][0][entry];
		true_channel=0;
		break;
	case pwl_vcc2_values:
		nominal_val=globals.Flash.vcc2_pwl_amp[0][0][0][entry];
		nominal_wordout=globals.Flash.vcc2_pwl_Vc_norm4095[0][0][0][entry];
		true_channel=0;
		break;
	case pwl_load_type_values:
		nominal_val=globals.Flash.load_type_pwl_time[channel][range][polarity][entry];
		nominal_wordout=globals.Flash.load_type_pwl_Vc_norm4095[channel][range][polarity][entry];
		reset_state=0;
		disable_errors=1;
		break;
	}

	/* just get nominal value, don't activate */
	if (nom_val) {
		*nom_val = nominal_val;
		return OK;
	}

	/* For timing parameters, turn off output and error-checking, and change prf/pw */
	/* For ampl/os parameters, use existing timing and output state, and use error-checking */

	if (reset_state) {
		Main_Rst();
		Set_Output_State(true_channel,output_off);
		Main_update_shift_registers();
		bus_setpin(PW_ENABLE, 1);	/* enable PW circuit (but output relay still off) */
	}

	if (disable_errors) {
		globals.Flash.fully_programmed=Being_Programmed;    /* disable error-checking */
		Ctrl_PRF_Limiter(0);						/* re-enables after *rst */
	}

	globals.Flags.extended_ampl_min_max=YES;
	globals.ChannelState[true_channel].test_delay_mode=NO;

	switch (parameter) {
	case (pwl_ampl_values):
		status=Set_Amplitude(0,polarity,1,nominal_wordout,range,0,channel,nominal_val,0);
		break;
	case (pwl_os_values):
		status=Set_Offset(0,1,nominal_wordout,range,channel,nominal_val);
		break;
	case pwl_pw_values:
		other_setting=0.08/nominal_val;
		if (other_setting > 500e3) {
			other_setting=500e3;
		}

		Set_Pol(channel,pol_norm);

		Set_Delay(0,0,0,channel,0.0);
		status=Set_frequency(0,0,0,globals.Flash.ChanKey_frequency?channel:0,other_setting);

		if (!status) {
			status=Set_Pw(0,nominal_wordout,range,channel,nominal_val,0);
		}
		break;
	case pwl_delay_values:
		other_setting=0.08/nominal_val;
		if (other_setting > 500e3) {
			other_setting=500e3;
		}

		/* set delay constants to zero, temporarily */
		temp1=globals.Flash.propagation_delay[channel];
		temp2=globals.Flash.delay_shrink[channel];
		globals.Flash.propagation_delay[channel]=0.0;
		globals.Flash.delay_shrink[channel]=0.0;

		globals.ChannelState[channel].test_delay_mode=YES;

		status=Set_frequency(0,0,0,globals.Flash.ChanKey_frequency?channel:0,other_setting);
		if (!status) {
			status=Set_Delay(0,nominal_wordout,range,channel,nominal_val);
		}

		globals.Flash.propagation_delay[channel]=temp1;
		globals.Flash.delay_shrink[channel]=temp2;

		break;
	case pwl_period_values:
		other_setting=0.4*nominal_val;
		if (other_setting>1.0) {
			other_setting=1.0;    /* reduce pulse width if required */
		}

		Set_Delay(0,0,0,channel,0.0);
		status=Set_frequency(0,nominal_wordout,range,channel,1.0/nominal_val);

		if (!status) {
			status=Set_Pw(0,0,0,globals.Flash.ChanKey_pw?channel:0,other_setting,0);
		}
		break;

	case pwl_burst_values:
		other_setting=20.0*nominal_val;

		if (other_setting>10.0) {
			other_setting=10.0;    /* reduce pulse width if required */
		}
		status=Set_frequency(0,0,0,channel,1.0/other_setting);
		Main_update_shift_registers();

		if (!status) {
			status=Set_Pw(0,0,0,globals.Flash.ChanKey_pw?channel:0,nominal_val,0);
		}
		if (!status) {
			status=Set_Burst_Count(channel,4,nominal_val);
		}
		if (!status) {
			status=Set_Burst_Time(0,nominal_wordout,range,channel,nominal_val);
		}

		break;

	case pwl_rise_time_values:
		other_setting=10;	/* reduce test frequency if required */
		status=Set_frequency(0,0,0,globals.Flash.ChanKey_frequency?channel:0,other_setting);
		if (!status) {
			status=Set_rise_time(0,nominal_wordout,range,channel,nominal_val);
		}
		break;

	case pwl_slew_values:
		other_setting=10;	/* reduce test frequency if required */
		status=Set_frequency(0,0,0,globals.Flash.ChanKey_frequency?channel:0,other_setting);
		if (!status) {
			status=Set_slew(0,nominal_wordout,range,channel,nominal_val);
		}
		break;

	case pwl_load_type_values:
		Set_avrq_res(0,nominal_wordout,range,channel,nominal_val);
		break;

	case pwl_vcc1_values:
		status=Set_Amplitude(0,polarity,1,nominal_wordout,range,0,channel,nominal_val,0);
		break;

	case pwl_vcc2_values:
		status=Set_Amplitude(0,polarity,1,nominal_wordout,range,0,channel,nominal_val,0);
		break;

	}

	/* enables or disables delay diagnostic mode */
	Set_Mux(true_channel);

	Main_update_shift_registers();
	Show_Main_Menu();

	/* re-enable error-checking */
	globals.Flash.fully_programmed=All_Programmed;

	/* rise time requires more values to be set before it can be measured */
	if (parameter != pwl_rise_time_values) {
		globals.Flags.extended_ampl_min_max=NO;
	}

	return status;
}


int Set_VI_Cal_Pnt(int parameter,int channel,int calibration_point_number,float cal_point)
{
	int polarity,range,entry,status,num_in_range,num_of_ranges,eprom_loc;
	float nom_ampl;
	int index;
	int max_points,max_polarity,max_ranges;
	float *pwl_amp;
	int true_channel;

	true_channel=channel;

	Get_VI_Rng_Info(parameter,channel,calibration_point_number,&range,&polarity,&entry,&num_in_range,&num_of_ranges);

	max_points=points_in_range;
	switch (parameter) {
	case (pwl_ampl_values):
		max_polarity=ampl_polarities;
		max_ranges=ampl_ranges;
		pwl_amp=&globals.Flash.ampl_pwl_amp[0][0][0][0];
		break;
	case (pwl_os_values):
		max_polarity=os_polarities;
		max_ranges=os_ranges;
		pwl_amp=&globals.Flash.os_pwl_amp[0][0][0][0];
		break;
	case pwl_pw_values:
		max_polarity=timing_polarities;
		max_ranges=timing_ranges;
		pwl_amp=&globals.Flash.pw_pwl_time[0][0][0][0];
		break;
	case pwl_delay_values:
		max_polarity=timing_polarities;
		max_ranges=timing_ranges;
		pwl_amp=&globals.Flash.delay_pwl_time[0][0][0][0];
		break;
	case pwl_period_values:
		max_polarity=timing_polarities;
		max_ranges=timing_ranges;
		pwl_amp=&globals.Flash.period_pwl_time[0][0][0][0];
		break;
	case pwl_burst_values:
		max_polarity=timing_polarities;
		max_ranges=timing_ranges;
		pwl_amp=&globals.Flash.burst_pwl_time[0][0][0][0];
		break;
	case pwl_rise_time_values:
		max_polarity=ampl_polarities;
		max_ranges=ampl_ranges;
		pwl_amp=&globals.Flash.rise_time_pwl_time[0][0][0][0];
		break;
	case pwl_slew_values:
		max_polarity=timing_polarities;
		max_ranges=timing_ranges;
		pwl_amp=&globals.Flash.slew_pwl_time[0][0][0][0];
		break;
	case pwl_vcc1_values:
		max_polarity=1;
		max_ranges=1;
		true_channel=0;
		pwl_amp=&globals.Flash.vcc1_pwl_amp[0][0][0][0];
		break;
	case pwl_vcc2_values:
		max_polarity=1;
		max_ranges=1;
		true_channel=0;
		pwl_amp=&globals.Flash.vcc2_pwl_amp[0][0][0][0];
		break;
	case pwl_load_type_values:
		max_polarity=load_type_polarities;
		max_ranges=load_type_ranges;
		pwl_amp=&globals.Flash.load_type_pwl_time[0][0][0][0];
		break;
	}
	index=true_channel*max_ranges*max_polarity*max_points
	      +range*max_polarity*max_points
	      +polarity*max_points
	      +entry;

	nom_ampl=pwl_amp[index];

	switch (parameter) {
	case (pwl_ampl_values):
		if (globals.ChannelState[channel].amplitude<0.0) {
			nom_ampl=-nom_ampl;
		}
		if ( (cal_point>0.0 && nom_ampl<0.0) || (cal_point<0.0 && nom_ampl>0.0)) {
			return CalibrationPolarityError;
		}
		if (fabs(nom_ampl) < smallest_allowed_number) {
			return CalibrationZeroError;
		}
		if (fabs( (cal_point-nom_ampl)/nom_ampl) >0.3) {
			return CalibrationPercentError;
		}
		break;

	case (pwl_os_values):
		/* check for divide-by-zero error */
		if (fabs(nom_ampl) > smallest_allowed_number) {
			/* exclude relative changes of 30% or more */
			if (fabs( (cal_point-nom_ampl)/nom_ampl) > 0.3) {
				return CalibrationPercentError;
			}
		}
		/* exclude changes greater than 20% of entire range */
		if (fabs(cal_point-nom_ampl) > (0.2*fabs(globals.Flash.max_offset[channel]-globals.Flash.min_offset[channel]))) {
			return CalibrationPercentError;
		}
		break;

	case pwl_pw_values:
	case pwl_period_values:
	case pwl_burst_values:
	case pwl_rise_time_values:
	case pwl_slew_values:
	case pwl_load_type_values:
		/* exclude relative changes of 45% or more unless < 15 ns */
		if ( (fabs(cal_point-nom_ampl)/nom_ampl > 0.45) && (fabs(cal_point-nom_ampl)>15e-9)) {
			return CalibrationPercentError;
		}
		if (cal_point<0.0) {
			return Negative_Not_Allowed;
		}
		break;

	case pwl_delay_values:
		/* exclude relative changes of 45% or more unless < 15 ns */
		if ( (fabs(cal_point-nom_ampl)/nom_ampl > 0.45) && (fabs(cal_point-nom_ampl)>15e-9)) {
			return CalibrationPercentError;
		}
		break;

	case pwl_vcc1_values:
	case pwl_vcc2_values:
		if (fabs(nom_ampl) > smallest_allowed_number) {
			/* exclude relative changes of 30% or more */
			if (fabs( (cal_point-nom_ampl)/nom_ampl) > 0.3) {
				return CalibrationPercentError;
			}
		}
		break;
	}



	status=OK;
	globals.Flags.extended_ampl_min_max=YES;

	if (parameter==pwl_ampl_values) {
		cal_point=fabs(cal_point);
	}

	*(float *)(&pwl_amp[index])=cal_point;

	/* see if new point prevents min/max from being obtained */
	status=Check_MinMax_Cal(channel,parameter);

	if	(status) {
		/* revert to original value if required */
		*(float *)(&pwl_amp[index])=nom_ampl;
	}

	Set_Cal_Nom(channel,calibration_point_number,parameter,NULL);

	Main_update_shift_registers();
	Show_Main_Menu();

	eprom_loc = (char *) (&pwl_amp[index]) - (char *) &(globals.Flash.flash_start);
	writeUserBlock(&globals.Flash, eprom_loc, sizeof(nom_ampl));

	globals.Flags.extended_ampl_min_max=NO;
	return status;
}


int Set_VI_Del_Cal(int parameter,int channel,int calibration_point_number)
{
	int polarity,range,entry,status,i,num_in_range,num_of_ranges;
	float temp1[10];
	short temp2[10];
	int index;
	int max_points,max_polarity,max_ranges;
	short *pointer_short1;
	float *pointer_float1;
	int eprom_loc,size_of_float1,size_of_short1;
	int true_channel;

	true_channel=channel;

	max_points=points_in_range;

	switch (parameter) {
	case (pwl_ampl_values):
		max_polarity=ampl_polarities;
		max_ranges=ampl_ranges;
		pointer_short1=&globals.Flash.ampl_pwl_Vc_norm4095[0][0][0][0];
		pointer_float1=&globals.Flash.ampl_pwl_amp[0][0][0][0];
		size_of_short1=sizeof(globals.Flash.ampl_pwl_Vc_norm4095);
		size_of_float1=sizeof(globals.Flash.ampl_pwl_amp);
		break;
	case (pwl_os_values):
		max_polarity=os_polarities;
		max_ranges=os_ranges;
		pointer_short1=&globals.Flash.os_pwl_Vc_norm4095[0][0][0][0];
		pointer_float1=&globals.Flash.os_pwl_amp[0][0][0][0];
		size_of_short1=sizeof(globals.Flash.os_pwl_Vc_norm4095);
		size_of_float1=sizeof(globals.Flash.os_pwl_amp);
		break;
	case pwl_pw_values:
		max_polarity=timing_polarities;
		max_ranges=timing_ranges;
		pointer_short1=&globals.Flash.pw_pwl_Vc_norm4095[0][0][0][0];
		pointer_float1=&globals.Flash.pw_pwl_time[0][0][0][0];
		size_of_short1=sizeof(globals.Flash.pw_pwl_Vc_norm4095);
		size_of_float1=sizeof(globals.Flash.pw_pwl_time);
		break;
	case pwl_delay_values:
		max_polarity=timing_polarities;
		max_ranges=timing_ranges;
		pointer_short1=&globals.Flash.delay_pwl_Vc_norm4095[0][0][0][0];
		pointer_float1=&globals.Flash.delay_pwl_time[0][0][0][0];
		size_of_short1=sizeof(globals.Flash.delay_pwl_Vc_norm4095);
		size_of_float1=sizeof(globals.Flash.delay_pwl_time);
		break;
	case pwl_period_values:
		max_polarity=timing_polarities;
		max_ranges=timing_ranges;
		pointer_short1=&globals.Flash.period_pwl_Vc_norm4095[0][0][0][0];
		pointer_float1=&globals.Flash.period_pwl_time[0][0][0][0];
		size_of_short1=sizeof(globals.Flash.period_pwl_Vc_norm4095);
		size_of_float1=sizeof(globals.Flash.period_pwl_time);
		break;
	case pwl_burst_values:
		max_polarity=timing_polarities;
		max_ranges=timing_ranges;
		pointer_short1=&globals.Flash.burst_pwl_Vc_norm4095[0][0][0][0];
		pointer_float1=&globals.Flash.burst_pwl_time[0][0][0][0];
		size_of_short1=sizeof(globals.Flash.burst_pwl_Vc_norm4095);
		size_of_float1=sizeof(globals.Flash.burst_pwl_time);
		break;
	case pwl_rise_time_values:
		max_polarity=ampl_polarities;
		max_ranges=ampl_ranges;
		pointer_short1=&globals.Flash.rise_time_pwl_Vc_norm4095[0][0][0][0];
		pointer_float1=&globals.Flash.rise_time_pwl_time[0][0][0][0];
		size_of_short1=sizeof(globals.Flash.rise_time_pwl_Vc_norm4095);
		size_of_float1=sizeof(globals.Flash.rise_time_pwl_time);
		break;
	case pwl_slew_values:
		max_polarity=timing_polarities;
		max_ranges=timing_ranges;
		pointer_short1=&globals.Flash.slew_pwl_Vc_norm4095[0][0][0][0];
		pointer_float1=&globals.Flash.slew_pwl_time[0][0][0][0];
		size_of_short1=sizeof(globals.Flash.slew_pwl_Vc_norm4095);
		size_of_float1=sizeof(globals.Flash.slew_pwl_time);
		break;
	case pwl_vcc1_values:
		max_polarity=1;
		max_ranges=1;
		pointer_short1=&globals.Flash.vcc1_pwl_Vc_norm4095[0][0][0][0];
		pointer_float1=&globals.Flash.vcc1_pwl_amp[0][0][0][0];
		size_of_short1=sizeof(globals.Flash.vcc1_pwl_Vc_norm4095);
		size_of_float1=sizeof(globals.Flash.vcc1_pwl_amp);
		true_channel=0;
		break;
	case pwl_vcc2_values:
		max_polarity=1;
		max_ranges=1;
		pointer_short1=&globals.Flash.vcc2_pwl_Vc_norm4095[0][0][0][0];
		pointer_float1=&globals.Flash.vcc2_pwl_amp[0][0][0][0];
		size_of_short1=sizeof(globals.Flash.vcc2_pwl_Vc_norm4095);
		size_of_float1=sizeof(globals.Flash.vcc2_pwl_amp);
		true_channel=0;
		break;
	case pwl_load_type_values:
		max_polarity=load_type_polarities;
		max_ranges=load_type_ranges;
		pointer_short1=&globals.Flash.load_type_pwl_Vc_norm4095[0][0][0][0];
		pointer_float1=&globals.Flash.load_type_pwl_time[0][0][0][0];
		size_of_short1=sizeof(globals.Flash.load_type_pwl_Vc_norm4095);
		size_of_float1=sizeof(globals.Flash.load_type_pwl_time);
		break;
	}

	Get_VI_Rng_Info(parameter,channel,calibration_point_number,&range,&polarity,&entry,&num_in_range,&num_of_ranges);

	index=true_channel*max_ranges*max_polarity*max_points
	      +range*max_polarity*max_points
	      +polarity*max_points;


	/* can not delete top or bottom of a range */
	if ((entry==0) || (entry==(num_in_range-1))) {
		return CalibrationRangeError;
	}

	/* backup calibration values */
	for (i=0; i<max_points; i++) {
		temp1[i]=pointer_float1[index+i];
		temp2[i]=pointer_short1[index+i];
	}

	/* move the data, overwriting the deleted point */
	for (i=entry; i<max_points-1; i++) {
		*(float *)(&pointer_float1[index+i])=pointer_float1[index+i+1];
		*(short *)(&pointer_short1[index+i])=pointer_short1[index+i+1];
	}

	*(float *)(&pointer_float1[index+points_in_range-1])=0.0;
	*(short *)(&pointer_short1[index+points_in_range-1])=0;

	/* see if new point prevents min/max from being obtained */
	status=Check_MinMax_Cal(channel,parameter);

	if (status) {
		/* revert to original calibration */
		for (i=0; i<max_points; i++) {
			*(float *)(&pointer_float1[index+i])=temp1[i];
			*(short *)(&pointer_short1[index+i])=temp2[i];
		}
	}

	Set_Amplitude(0,0,0,0,0,0,channel,0.0,0);
	Main_update_shift_registers();

	Set_Offset(0,0,0,0,channel,0.0);
	Main_update_shift_registers();

	Show_Main_Menu();

	eprom_loc = (char *) pointer_short1 - (char *) &(globals.Flash.flash_start);
	writeUserBlock(&globals.Flash, eprom_loc, size_of_short1);

	eprom_loc = (char *) pointer_float1 - (char *) &(globals.Flash.flash_start);
	writeUserBlock(&globals.Flash, eprom_loc, size_of_float1);

	return status;
}


int Set_VI_Control(int parameter,int channel,float new_ampl,int *point_found,int *relay_range,int *atten_range,
                   int *UseNegData,int *entry,int *word_out,int *actual_pol)
{
	float use_ampl,tweaked_use_ampl,fraction;
	int i;
	float min_os_in_range,max_os_in_range;
	float min_ampl_in_range,max_ampl_in_range;

	int max_points,max_polarity,max_ranges;
	float *pwl_amp;
	short *pwl_vc;
	int index,use_range;
	int range_i,entry_i;
	float inverse_word_out;
	int timing_extrapolation_allowed;
	int pw_polarity;

	float pw_distort_val;
	float ampl_for_distort_calc;

	int top_range_only;
	int starting_range;

	int decreasing_values_allowed;
	int reciprocal_relationship;

	int true_channel;

	true_channel=channel;

	top_range_only=0;
	starting_range=0;
	max_points=points_in_range;
	timing_extrapolation_allowed=NO;
	reciprocal_relationship=NO;
	decreasing_values_allowed=NO;
	min_os_in_range=max_os_in_range=0.0;
	min_ampl_in_range=max_ampl_in_range=0.0;


	switch (parameter) {
	case pwl_ampl_values:

		if (globals.Flash.ampl_coupled_to_os[channel]) {
			new_ampl+=globals.ChannelState[channel].offset;	/* for 1011-style offset circuit */
		}

		max_polarity=ampl_polarities;
		max_ranges=ampl_ranges;
		pwl_vc=&globals.Flash.ampl_pwl_Vc_norm4095[0][0][0][0];
		pwl_amp=&globals.Flash.ampl_pwl_amp[0][0][0][0];
		if (new_ampl<0.0) {
			*actual_pol=1;
			*UseNegData=1;
		} else {
			*actual_pol=0;
			*UseNegData=0;
		}
		if (globals.Flash.use_pos_ampl_data_only[channel] && new_ampl<0.0) {
			*UseNegData=0;
		}
		use_ampl=fabs(new_ampl);

		/* use top amplitude range in AV-1011 if Zout=2 */
		top_range_only = globals.Flash.switchable_zout[channel]
		                 && (globals.ChannelState[channel].zout==globals.Flash.zout_min[channel]);

		/* if high_impedance_mode the relay range is automatically set to 3 (for 1011, 1015 only),
		   if such a range exists */

		if ( globals.Flash.switchable_load[channel] &&
		                (globals.ChannelState[channel].load_type>globals.Flash.low_load_type[channel]) &&
		                globals.Flash.ampl_pwl_Vc_norm4095[channel][3][*actual_pol][1] > 0) {
			starting_range=3;
			top_range_only=0;
		}

		/* if 1011-OT offset is non-zero, the relay range is automatically set to 3 or 4 (for 1011, 1015 only) */

		if (globals.Flash.ampl_coupled_to_os[channel]
		                && globals.Flash.switchable_zout[channel]
		                && (fabs(globals.ChannelState[channel].offset)>(globals.Flash.max_offset[channel]/max_v_dymanic_range))) {
			starting_range=3;
		}

		/* for AVMP-4-B: lower ampl range used for first PW range only */
		if (globals.Flash.use_high_ampl_ranges_for_high_pw_ranges[channel] &&
		                ((globals.Registers.shift_reg_out[3] & 0x7f) > 0)) {
			top_range_only=1;
		}

		if (globals.Flash.couple_first_N_pw_ranges_to_ampl_ranges[channel]) {
			int curr_pw_range = 0;
			int parse_sr = globals.Registers.shift_reg_out[3] & 0x7f;

			while (parse_sr) {
				++curr_pw_range;
				parse_sr = parse_sr >> 1;
			}

			if (globals.Flash.volt_ctrl_pw[channel]) {
				// ranges are shifted in these units
				++curr_pw_range;
				// except for minimum range
				if (globals.Registers.shift_reg_out[3] & (long)0x01000) {
					curr_pw_range = 0;
				}
			}

			starting_range = curr_pw_range;
			if (starting_range > globals.Flash.couple_first_N_pw_ranges_to_ampl_ranges[channel]) {
				starting_range = globals.Flash.couple_first_N_pw_ranges_to_ampl_ranges[channel];
			}

			top_range_only = 0;
		}

		break;
	case pwl_os_values:
		decreasing_values_allowed=YES;		/* allows Vc=0 to corresponds to most + OS, and Vc=10V to give most - OS */
		max_polarity=os_polarities;
		max_ranges=os_ranges;
		pwl_vc=&globals.Flash.os_pwl_Vc_norm4095[0][0][0][0];
		pwl_amp=&globals.Flash.os_pwl_amp[0][0][0][0];
		*UseNegData=0;
		use_ampl=new_ampl;
		break;
	case pwl_pw_values:
		max_polarity=timing_polarities;
		max_ranges=timing_ranges;
		pwl_vc=&globals.Flash.pw_pwl_Vc_norm4095[0][0][0][0];
		pwl_amp=&globals.Flash.pw_pwl_time[0][0][0][0];
		*UseNegData=0;
		use_ampl=new_ampl;

		/* increase PW if required to compensate for series output diode turn-on time */
		if (		(globals.Flash.fully_programmed != Being_Programmed)
		                &&	(globals.Flash.distort_Y[channel] != 0.0)
		                && (fabs(globals.ChannelState[channel].offset)<fabs(globals.Flash.distort_max_os[channel]))	) {

			ampl_for_distort_calc = fabs(globals.ChannelState[channel].amplitude);

			/* clamping distortion above a certain amplitude */
			if (ampl_for_distort_calc > globals.Flash.distort_max_ampl[channel]) {
				ampl_for_distort_calc = globals.Flash.distort_max_ampl[channel];
			}

			/* clamping distortion below "zero equiv" amplitude */
			if (ampl_for_distort_calc < globals.Flash.ampl_zero_equiv[channel]) {
				ampl_for_distort_calc = globals.Flash.ampl_zero_equiv[channel];
			}

			/* prohit values of Y that would cause divide-by-zero error */
			if ( (-globals.Flash.distort_Y[channel]) > globals.Flash.ampl_zero_equiv[channel]) {
				return PW_Distort_Error;
			}

			pw_distort_val=globals.Flash.distort_Z[channel] +
			               globals.Flash.distort_X[channel] / (ampl_for_distort_calc+globals.Flash.distort_Y[channel]);

			/* calculate new actual PW */
			use_ampl=use_ampl+pw_distort_val;
		}

		/* tweak depending on polarity */
		if (globals.ChannelState[channel].amplitude<0.0) {
			pw_polarity = 1;
			use_ampl=use_ampl+globals.Flash.pulse_width_pol_tweak[channel][pw_polarity];
		} else {
			pw_polarity = 0;
			use_ampl=use_ampl+globals.Flash.pulse_width_pol_tweak[channel][pw_polarity];
		}

		/* tweak depending on amplitude range (steps, not gradual like distort params */

		if (fabs(globals.ChannelState[channel].amplitude) <= globals.Flash.pw_shift_below_this_ampl[channel]) {
			use_ampl=use_ampl+globals.Flash.pw_shift_below_ampl_by[channel];
		}

		reciprocal_relationship=YES;
		timing_extrapolation_allowed=YES;
		break;
	case pwl_delay_values:
		max_polarity=timing_polarities;
		max_ranges=timing_ranges;
		pwl_vc=&globals.Flash.delay_pwl_Vc_norm4095[0][0][0][0];
		pwl_amp=&globals.Flash.delay_pwl_time[0][0][0][0];
		*UseNegData=0;
		use_ampl=new_ampl;
		reciprocal_relationship=YES;
		timing_extrapolation_allowed=YES;
		break;
	case pwl_period_values:
		max_polarity=timing_polarities;
		max_ranges=timing_ranges;
		pwl_vc=&globals.Flash.period_pwl_Vc_norm4095[0][0][0][0];
		pwl_amp=&globals.Flash.period_pwl_time[0][0][0][0];
		*UseNegData=0;
		use_ampl=new_ampl;
		timing_extrapolation_allowed=YES;
		reciprocal_relationship=YES;
		break;
	case pwl_burst_values:
		max_polarity=timing_polarities;
		max_ranges=timing_ranges;
		pwl_vc=&globals.Flash.burst_pwl_Vc_norm4095[0][0][0][0];
		pwl_amp=&globals.Flash.burst_pwl_time[0][0][0][0];
		*UseNegData=0;
		use_ampl=new_ampl;
		reciprocal_relationship=YES;
		timing_extrapolation_allowed=YES;
		break;
	case pwl_rise_time_values:
		max_polarity=ampl_polarities;
		max_ranges=ampl_ranges;
		pwl_vc=&globals.Flash.rise_time_pwl_Vc_norm4095[0][0][0][0];
		pwl_amp=&globals.Flash.rise_time_pwl_time[0][0][0][0];
		if (globals.ChannelState[channel].amplitude<0.0) {
			*actual_pol=*UseNegData=1;
		} else {
			*actual_pol=*UseNegData=0;
		}
		use_ampl=new_ampl;
		reciprocal_relationship=NO;
		timing_extrapolation_allowed=NO;
		break;
	case pwl_slew_values:
		max_polarity=timing_polarities;
		max_ranges=timing_ranges;
		pwl_vc=&globals.Flash.slew_pwl_Vc_norm4095[0][0][0][0];
		pwl_amp=&globals.Flash.slew_pwl_time[0][0][0][0];
		*UseNegData=0;
		use_ampl=new_ampl;
		reciprocal_relationship=NO;
		timing_extrapolation_allowed=NO;
		top_range_only=YES; /* higher drive voltage = lower ripple in slew */
		break;
	case pwl_vcc1_values:
		max_polarity=1;
		max_ranges=1;
		pwl_vc=&globals.Flash.vcc1_pwl_Vc_norm4095[0][0][0][0];
		pwl_amp=&globals.Flash.vcc1_pwl_amp[0][0][0][0];
		*UseNegData=0;
		use_ampl=new_ampl;
		true_channel=0;
		break;
	case pwl_vcc2_values:
		max_polarity=1;
		max_ranges=1;
		pwl_vc=&globals.Flash.vcc2_pwl_Vc_norm4095[0][0][0][0];
		pwl_amp=&globals.Flash.vcc2_pwl_amp[0][0][0][0];
		*UseNegData=0;
		use_ampl=new_ampl;
		true_channel=0;
		break;
	case pwl_load_type_values:
		max_polarity=load_type_polarities;
		max_ranges=load_type_ranges;
		pwl_vc=&globals.Flash.load_type_pwl_Vc_norm4095[0][0][0][0];
		pwl_amp=&globals.Flash.load_type_pwl_time[0][0][0][0];
		*UseNegData=0;
		use_ampl=new_ampl;
		reciprocal_relationship=YES;
		timing_extrapolation_allowed=YES;
		break;
	}



	/* if top_range_only=false, the first match (i.e. in the lowest range) is used. */
	/* if top_range_only=true, the last match (i.e. in the highest range) is used. */

	*point_found=*relay_range=*entry=0;
	*word_out=-1;

	float use_atten = 1.0;

	for (*atten_range = max_attens; (*atten_range >= -1) && !(*point_found);) {

		(*atten_range)--;

		if (*atten_range == -1) {
			// no valid attenuators
			use_atten = 1.0;
		} else {

			if (parameter != pwl_ampl_values) {
				continue;
			} else if (globals.Flash.attenuators[channel][*atten_range] == 0.0) {
				continue;
			} else if (globals.Flags.attenuators_enabled == 0) {
				continue;
			} else {
				use_atten = globals.Flash.attenuators[channel][*atten_range];
			}

			// Limit max voltage in attenuator mode to 93% of full maximum,
			// to avoid the region in AVPs where PW shifts at high amplitudes
			float max1 = fabs(globals.Flash.min_ampl[channel]);
			float max2 = fabs(globals.Flash.max_ampl[channel]);
			if (max2 > max1) {
				max1 = max2;
			}
			if ((use_ampl * use_atten) >
			                (globals.Flash.atten_percent_max_ampl[channel] * max1)) {
				continue;
			}
		}

		for (range_i=starting_range; (range_i<max_ranges) && (!*point_found || top_range_only); ++range_i) {	/* use non-all-zero ranges */
			/* apply pw ampl/pol tweaks */
			if (parameter == pwl_pw_values) {
				// tweak pw shifts
				tweaked_use_ampl = use_ampl + globals.Flash.pw_range_pol_tweaks[channel][range_i][pw_polarity];
			} else if (parameter == pwl_ampl_values) {
				// compensate for attenuators
				tweaked_use_ampl = use_ampl * use_atten;
			} else {
				// no tweaks
				tweaked_use_ampl = use_ampl;
			}

			for (entry_i=0; (entry_i<max_points-1) && (!*point_found || top_range_only); ++entry_i) {
				index=true_channel*max_ranges*max_polarity*max_points
				      +(range_i)*max_polarity*max_points
				      +(*UseNegData)*max_points
				      +(entry_i);

				if ( (fabs(pwl_amp[index]-pwl_amp[index+1])>smallest_allowed_number)
				                &&
				                (	((tweaked_use_ampl>=pwl_amp[index]) &&	(tweaked_use_ampl<=pwl_amp[index+1]))
				                        ||
				                        (decreasing_values_allowed && (tweaked_use_ampl<=pwl_amp[index]) &&	(tweaked_use_ampl>=pwl_amp[index+1]))  /* for OS only */
				                )
				   ) {
					*point_found=1;

					if ((parameter==pwl_ampl_values) && globals.Flash.ampl_os_ranges_related[channel]) {
						for (i=0; i<points_in_range; i++) {
							if (max_os_in_range<globals.Flash.os_pwl_amp[channel][range_i][0][i]) {
								max_os_in_range=globals.Flash.os_pwl_amp[channel][range_i][0][i];
							}
							if (min_os_in_range>globals.Flash.os_pwl_amp[channel][range_i][0][i]) {
								min_os_in_range=globals.Flash.os_pwl_amp[channel][range_i][0][i];
							}
						}
						if ((globals.ChannelState[channel].offset<min_os_in_range) || (globals.ChannelState[channel].offset>max_os_in_range)) {
							*point_found=0;	/* try higher range if can't satisfy os and ampl both in this range */
						}
					}

					if ((parameter==pwl_os_values) && globals.Flash.ampl_os_ranges_related[channel]) {
						for (i=0; i<points_in_range; i++) {
							if (max_ampl_in_range<globals.Flash.ampl_pwl_amp[channel][range_i][*UseNegData][i]) {
								max_ampl_in_range=globals.Flash.ampl_pwl_amp[channel][range_i][*UseNegData][i];
							}
							if (min_ampl_in_range>globals.Flash.ampl_pwl_amp[channel][range_i][*UseNegData][i]) {
								min_ampl_in_range=globals.Flash.ampl_pwl_amp[channel][range_i][*UseNegData][i];
							}
						}
						if ((fabs(globals.ChannelState[channel].amplitude)<min_ampl_in_range) || (fabs(globals.ChannelState[channel].amplitude)>max_ampl_in_range)) {
							*point_found=0;	/* try higher range if can't satisfy os and ampl both in this range */
						}
					}

					fraction = (tweaked_use_ampl-pwl_amp[index]) / (pwl_amp[index] - pwl_amp[index+1]);
					*relay_range=range_i;
					*entry=entry_i;

					/* check for linear voltage-controlled PW */
					if (	(parameter==pwl_pw_values)
					                && globals.Flash.volt_ctrl_pw[channel]
					                && (pwl_vc[index] < pwl_vc[index+1])) {
						reciprocal_relationship=NO;
					}

					if (reciprocal_relationship) {
						inverse_word_out = fraction * (1.0/((float) pwl_vc[index]) - 1.0/((float) pwl_vc[index+1]));
						inverse_word_out+=1/((float) pwl_vc[index]);
						*word_out=(int) (1.0/inverse_word_out);
					} else {
						*word_out = (int) ( fraction * (float) (pwl_vc[index] - pwl_vc[index+1]) );
						*word_out+=pwl_vc[index];
					}
				}
			}
		}
	}

	/* The amplitude/os settings must lie within the calibration points. */
	/* However, the timing data is allowed to lie outside the upper end, */
	/* because the reciprocal relationship allows an asymptotic extension to infinity, */
	/* at the expense of lower resolution */

	if (*word_out<0 && timing_extrapolation_allowed) {
		*point_found=0;
		use_range=-1;

		tweaked_use_ampl = use_ampl;

		/* identify highest range that starts with a cal point less than the setting */
		for (range_i=0; range_i<max_ranges; ++range_i) {	/* use non-all-zero ranges */
			if (parameter == pwl_pw_values) {
				tweaked_use_ampl = use_ampl + globals.Flash.pw_range_pol_tweaks[channel][range_i][pw_polarity];
			}

			index=true_channel*max_ranges*max_polarity*max_points
			      +(range_i)*max_polarity*max_points
			      +(*UseNegData)*max_points;
			if (pwl_amp[index]>smallest_allowed_number && pwl_amp[index]<tweaked_use_ampl) {
				use_range=range_i;
			}
		}

		if (use_range<0) {
			return HardwareError;
		}

		for (entry_i=max_points-2; (entry_i>=0) && (!*point_found); --entry_i) {
			index=true_channel*max_ranges*max_polarity*max_points
			      +(use_range)*max_polarity*max_points
			      +(*UseNegData)*max_points
			      +(entry_i);

			/* find the last two non-zero entries, and extrapolate from them */
			if (		(fabs(pwl_amp[index])>smallest_allowed_number)
			                && (fabs(pwl_amp[index+1])>smallest_allowed_number)
			                && (fabs(pwl_amp[index]-pwl_amp[index+1])>smallest_allowed_number)	) {
				*point_found=1;

				fraction = (tweaked_use_ampl-pwl_amp[index]) / (pwl_amp[index] - pwl_amp[index+1]);
				*relay_range=use_range;
				*entry=entry_i;

				inverse_word_out = fraction * (1.0/((float) pwl_vc[index]) - 1.0/((float) pwl_vc[index+1]));
				inverse_word_out+=1/((float) pwl_vc[index]);
				*word_out=(int) (1.0/inverse_word_out);
			}
		}
	}

	if (*word_out<0) {
		*word_out=0;
		return HardwareError;
	}

	/* check for 12 or 13 bit overflow */
	if (*word_out>dac_max) {
		*word_out=dac_max;	/* just to prevent wandering bits */
		return HardwareError;
	}

	return OK;
}


int Set_VI_Add_Cal(int parameter,int channel,float cal_point)
{
	int point_found,range,polarity,entry,word_out,total,atten_range;
	int i;
	float max_in_range;
	float least_integrated_error,integrated_error;
	float interp_ampl,fraction;
	int delete_point;
	float old_val;
	float abs_cal_point;
	int max_points,max_polarity,max_ranges;

	float temp_y_float[points_in_range+1];
	float temp_x_float[points_in_range+1];
	int temp_x_short[points_in_range+1];

	int index,actual_pol;
	float *pointer_y_float;
	short *pointer_x_short;

	int eprom_loc,size_of_y_float,size_of_x_short;

	int true_channel;

	true_channel = channel;

	range=polarity=0;

	max_points=points_in_range;

	abs_cal_point=cal_point;

	switch (parameter) {
	case (pwl_ampl_values):
		max_polarity=ampl_polarities;
		max_ranges=ampl_ranges;

		pointer_x_short=&globals.Flash.ampl_pwl_Vc_norm4095[0][0][0][0];
		pointer_y_float=&globals.Flash.ampl_pwl_amp[0][0][0][0];
		size_of_x_short=sizeof(globals.Flash.ampl_pwl_Vc_norm4095);
		size_of_y_float=sizeof(globals.Flash.ampl_pwl_amp);

		Set_VI_Control(pwl_ampl_values,channel,globals.ChannelState[channel].amplitude,&point_found,
		               &range,&atten_range,&polarity,&entry,&word_out,&actual_pol);
		old_val=globals.ChannelState[channel].amplitude;
		abs_cal_point=fabs(cal_point);	/* ampl data is positive always */

		/* special amplitude polarity check */

		if ( (cal_point>0.0 && old_val<0.0) || (cal_point<0.0 && old_val>0.0)) {
			return CalibrationPolarityError;
		}

		break;
	case (pwl_os_values):
		max_polarity=os_polarities;
		max_ranges=os_ranges;
		pointer_x_short=&globals.Flash.os_pwl_Vc_norm4095[0][0][0][0];
		pointer_y_float=&globals.Flash.os_pwl_amp[0][0][0][0];
		size_of_x_short=sizeof(globals.Flash.os_pwl_Vc_norm4095);
		size_of_y_float=sizeof(globals.Flash.os_pwl_amp);

		old_val=globals.ChannelState[channel].offset;
		Set_VI_Control(pwl_os_values,channel,old_val,&point_found,
		               &range,&atten_range,&polarity,&entry,&word_out,&actual_pol);
		break;
	case pwl_pw_values:
		max_polarity=timing_polarities;
		max_ranges=timing_ranges;
		pointer_x_short=&globals.Flash.pw_pwl_Vc_norm4095[0][0][0][0];
		pointer_y_float=&globals.Flash.pw_pwl_time[0][0][0][0];
		size_of_x_short=sizeof(globals.Flash.pw_pwl_Vc_norm4095);
		size_of_y_float=sizeof(globals.Flash.pw_pwl_time);
		old_val=globals.ChannelState[channel].pw;
		Set_VI_Control(pwl_pw_values,channel,old_val,&point_found,
		               &range,&atten_range,&polarity,&entry,&word_out,&actual_pol);
		break;
	case pwl_delay_values:
		max_polarity=timing_polarities;
		max_ranges=timing_ranges;
		pointer_x_short=&globals.Flash.delay_pwl_Vc_norm4095[0][0][0][0];
		pointer_y_float=&globals.Flash.delay_pwl_time[0][0][0][0];
		size_of_x_short=sizeof(globals.Flash.delay_pwl_Vc_norm4095);
		size_of_y_float=sizeof(globals.Flash.delay_pwl_time);
		old_val=globals.ChannelState[channel].delay;
		Set_VI_Control(pwl_delay_values,channel,old_val,&point_found,
		               &range,&atten_range,&polarity,&entry,&word_out,&actual_pol);
		break;
	case pwl_period_values:
		max_polarity=timing_polarities;
		max_ranges=timing_ranges;
		pointer_x_short=&globals.Flash.period_pwl_Vc_norm4095[0][0][0][0];
		pointer_y_float=&globals.Flash.period_pwl_time[0][0][0][0];
		size_of_x_short=sizeof(globals.Flash.period_pwl_Vc_norm4095);
		size_of_y_float=sizeof(globals.Flash.period_pwl_time);
		old_val=1.0/globals.ChannelState[channel].frequency;
		Set_VI_Control(pwl_period_values,channel,old_val,&point_found,
		               &range,&atten_range,&polarity,&entry,&word_out,&actual_pol);
		break;
	case pwl_burst_values:
		max_polarity=timing_polarities;
		max_ranges=timing_ranges;
		pointer_x_short=&globals.Flash.burst_pwl_Vc_norm4095[0][0][0][0];
		pointer_y_float=&globals.Flash.burst_pwl_time[0][0][0][0];
		size_of_x_short=sizeof(globals.Flash.burst_pwl_Vc_norm4095);
		size_of_y_float=sizeof(globals.Flash.burst_pwl_time);
		old_val=globals.ChannelState[channel].burst_time;
		Set_VI_Control(pwl_burst_values,channel,old_val,&point_found,
		               &range,&atten_range,&polarity,&entry,&word_out,&actual_pol);
		break;
	case pwl_rise_time_values:
		max_polarity=ampl_polarities;
		max_ranges=ampl_ranges;
		pointer_x_short=&globals.Flash.rise_time_pwl_Vc_norm4095[0][0][0][0];
		pointer_y_float=&globals.Flash.rise_time_pwl_time[0][0][0][0];
		size_of_x_short=sizeof(globals.Flash.rise_time_pwl_Vc_norm4095);
		size_of_y_float=sizeof(globals.Flash.rise_time_pwl_time);
		old_val=globals.ChannelState[channel].rise_time;
		Set_VI_Control(pwl_rise_time_values,channel,old_val,&point_found,
		               &range,&atten_range,&polarity,&entry,&word_out,&actual_pol);
		break;
	case pwl_slew_values:
		max_polarity=timing_polarities;
		max_ranges=timing_ranges;
		pointer_x_short=&globals.Flash.slew_pwl_Vc_norm4095[0][0][0][0];
		pointer_y_float=&globals.Flash.slew_pwl_time[0][0][0][0];
		size_of_x_short=sizeof(globals.Flash.slew_pwl_Vc_norm4095);
		size_of_y_float=sizeof(globals.Flash.slew_pwl_time);
		old_val=globals.ChannelState[channel].slew;
		Set_VI_Control(pwl_slew_values,channel,old_val,&point_found,
		               &range,&atten_range,&polarity,&entry,&word_out,&actual_pol);
		break;
	case pwl_vcc1_values:
		max_polarity=1;
		max_ranges=1;
		true_channel=0;
		pointer_x_short=&globals.Flash.vcc1_pwl_Vc_norm4095[0][0][0][0];
		pointer_y_float=&globals.Flash.vcc1_pwl_amp[0][0][0][0];
		size_of_x_short=sizeof(globals.Flash.vcc1_pwl_Vc_norm4095);
		size_of_y_float=sizeof(globals.Flash.vcc1_pwl_amp);
		old_val=globals.ChannelState[true_channel].vcc1;
		Set_VI_Control(pwl_vcc1_values,channel,old_val,&point_found,
		               &range,&atten_range,&polarity,&entry,&word_out,&actual_pol);
		break;
	case pwl_vcc2_values:
		max_polarity=1;
		max_ranges=1;
		true_channel=0;
		pointer_x_short=&globals.Flash.vcc2_pwl_Vc_norm4095[0][0][0][0];
		pointer_y_float=&globals.Flash.vcc2_pwl_amp[0][0][0][0];
		size_of_x_short=sizeof(globals.Flash.vcc2_pwl_Vc_norm4095);
		size_of_y_float=sizeof(globals.Flash.vcc2_pwl_amp);
		old_val=globals.ChannelState[true_channel].vcc2;
		Set_VI_Control(pwl_vcc2_values,channel,old_val,&point_found,
		               &range,&atten_range,&polarity,&entry,&word_out,&actual_pol);
		break;
	case pwl_load_type_values:
		max_polarity=load_type_polarities;
		max_ranges=load_type_ranges;
		pointer_x_short=&globals.Flash.load_type_pwl_Vc_norm4095[0][0][0][0];
		pointer_y_float=&globals.Flash.load_type_pwl_time[0][0][0][0];
		size_of_x_short=sizeof(globals.Flash.load_type_pwl_Vc_norm4095);
		size_of_y_float=sizeof(globals.Flash.load_type_pwl_time);
		old_val=globals.ChannelState[channel].load_type;
		Set_VI_Control(pwl_load_type_values,channel,old_val,&point_found,
		               &range,&atten_range,&polarity,&entry,&word_out,&actual_pol);
		break;

	}

	temp_y_float[max_points]=0.0;		/* Adding a cal point may temporarily result in 11 data points. */
	temp_x_float[max_points]=0.0;		/* One is eventually discarded. */
	temp_x_short[max_points]=0;


	index=true_channel*max_ranges*max_polarity*max_points
	      +range*max_polarity*max_points
	      +polarity*max_points;


	/* make a copy of the calibration data in this range */
	for (i=0; i<max_points; i++) {
		temp_y_float[i]=pointer_y_float[index+i];		/* work on this data */
		temp_x_short[i]=pointer_x_short[index+i];
	}


	/* check range for maximum value, and number of points */
	max_in_range=0.0;
	total=max_points;
	for (i=max_points-1; i>=0; --i) {
		if ( 	fabs(temp_y_float[i])<smallest_allowed_number && (total==i+1)) {
			--total;
		}
		if (fabs(temp_y_float[i])>max_in_range) {
			max_in_range=fabs(temp_y_float[i]);
		}
	}


	/*	check for excessive deviation between old and new amplitudes at this range/word combination */
	if (fabs(cal_point-old_val) > max_in_range/3.0) {
		return CalibrationPercentError;
	}
	/* check for overly-close data points */
	if ( word_out==temp_x_short[entry] ) {
		return CalibrationClosenessError;
	}
	if ( word_out==temp_x_short[entry+1]) {
		return CalibrationClosenessError;
	}



	/* move old data over */
	for (i=max_points-1; i>entry; i--) {
		temp_y_float[i+1]=temp_y_float[i];
		temp_x_short[i+1]=temp_x_short[i];
	}


	/*	add the new data */
	temp_y_float[entry+1]=abs_cal_point;
	temp_x_short[entry+1]=word_out;
	for (i=0; i<=max_points; ++i) {
		temp_x_float[i]=(float) temp_x_short[i];
	}


	/* delete a point, if required */
	if (total==max_points) {
		least_integrated_error=1e18;

		for (i=1; i<max_points; i++) {	/* end points are not considered for deletion */
			/* calculate interpolated amplitude, if this cal point was deleted */
			fraction = 	(temp_x_float[i]-temp_x_float[i-1]) / (temp_x_float[i+1]-temp_x_float[i-1]);
			interp_ampl = temp_y_float[i-1] + (fraction * (temp_y_float[i+1]-temp_y_float[i-1]));

			/* calculate percent error at interpolated point */
			integrated_error = fabs( (interp_ampl-temp_y_float[i])/temp_y_float[i]);

			/* calculate intergrated error */
			integrated_error *= fabs( temp_y_float[i+1] - temp_y_float[i-1] );

			if (integrated_error<least_integrated_error) {
				delete_point=i;
				least_integrated_error=integrated_error;
			}
		}

		/* delete the point that is most linear with the two around it */
		for (i=delete_point; i<max_points; i++) {
			temp_y_float[i]=temp_y_float[i+1];
			temp_x_float[i]=temp_x_float[i+1];
			temp_x_short[i]=temp_x_short[i+1];
		}
	}


	/* store new calibration data for this range */
	for (i=0; i<max_points; i++) {
		*(float *)(&pointer_y_float[index+i])=temp_y_float[i];
		*(short *)(&pointer_x_short[index+i])=temp_x_short[i];
	}

	eprom_loc = (char *) pointer_x_short - (char *) &(globals.Flash.flash_start);
	writeUserBlock(&globals.Flash, eprom_loc, size_of_x_short);

	eprom_loc = (char *) pointer_y_float - (char *) &(globals.Flash.flash_start);
	writeUserBlock(&globals.Flash, eprom_loc, size_of_y_float);

	globals.Flags.extended_ampl_min_max=NO;

	/* update output */
	switch (parameter) {
	case (pwl_ampl_values):
	case (pwl_vcc1_values):
	case (pwl_vcc2_values):
		Set_Amplitude(0,0,0,0,0,0,channel,cal_point,0);
		break;

	case (pwl_os_values):
		Set_Offset(0,0,0,0,channel,cal_point);
		break;

	case pwl_pw_values:
		Set_Pw(0,0,0,channel,cal_point,0);
		break;

	case pwl_delay_values:
		Set_Delay(0,0,0,channel,cal_point);
		break;

	case pwl_period_values:
		Set_frequency(0,0,0,channel,cal_point);
		break;

	case pwl_burst_values:
		Set_Burst_Time(0,0,0,channel,cal_point);
		break;

	case pwl_rise_time_values:
		Set_rise_time(0,0,0,channel,cal_point);
		break;

	case pwl_slew_values:
		Set_slew(0,0,0,channel,cal_point);
		break;

	case pwl_load_type_values:
		Set_avrq_res(0,0,0,channel,cal_point);
		break;
	}

	return OK;
}


int Get_VI_Num_Pnts(int parameter,int channel)
{
	int dummy1,dummy2,dummy3,num_in_range,num_of_ranges;

	return Get_VI_Rng_Info(parameter,channel,0,&dummy1,&dummy2,&dummy3,&num_in_range,&num_of_ranges);
}



int Get_VI_Rng_Info(int parameter, int channel, int calibration_point_number, int *range,
                    int *polarity, int *entry, int *num_in_range, int *num_of_ranges)
{
	int x_polarity,x_range,x_entry,total;
	int num_in_current_range;
	int max_polarity,max_points,max_ranges;
	float *pwl;
	int index;
	int true_channel;

	true_channel=channel;

	*polarity=0;
	*range=0;
	*entry=0;
	*num_in_range=0;

	/* determine array structure */
	max_points=points_in_range;

	switch (parameter) {
	case pwl_ampl_values:
		max_polarity=ampl_polarities;
		max_ranges=ampl_ranges;
		pwl=&globals.Flash.ampl_pwl_amp[0][0][0][0];
		break;
	case pwl_os_values:
		max_polarity=os_polarities;
		max_ranges=os_ranges;
		pwl=&globals.Flash.os_pwl_amp[0][0][0][0];
		break;
	case pwl_pw_values:
		max_polarity=timing_polarities;
		max_ranges=timing_ranges;
		pwl=&globals.Flash.pw_pwl_time[0][0][0][0];
		break;
	case pwl_delay_values:
		max_polarity=timing_polarities;
		max_ranges=timing_ranges;
		pwl=&globals.Flash.delay_pwl_time[0][0][0][0];
		break;
	case pwl_period_values:
		max_polarity=timing_polarities;
		max_ranges=timing_ranges;
		pwl=&globals.Flash.period_pwl_time[0][0][0][0];
		break;
	case pwl_burst_values:
		max_polarity=timing_polarities;
		max_ranges=timing_ranges;
		pwl=&globals.Flash.burst_pwl_time[0][0][0][0];
		break;
	case pwl_rise_time_values:
		max_polarity=ampl_polarities;
		max_ranges=ampl_ranges;
		pwl=&globals.Flash.rise_time_pwl_time[0][0][0][0];
		break;
	case pwl_slew_values:
		max_polarity=timing_polarities;
		max_ranges=timing_ranges;
		pwl=&globals.Flash.slew_pwl_time[0][0][0][0];
		break;
	case pwl_vcc1_values:
		max_polarity=1;
		max_ranges=1;
		pwl=&globals.Flash.vcc1_pwl_amp[0][0][0][0];
		true_channel=0;
		break;
	case pwl_vcc2_values:
		max_polarity=1;
		max_ranges=1;
		pwl=&globals.Flash.vcc2_pwl_amp[0][0][0][0];
		true_channel=0;
		break;
	case pwl_load_type_values:
		max_polarity=load_type_polarities;
		max_ranges=load_type_ranges;
		pwl=&globals.Flash.load_type_pwl_time[0][0][0][0];
		break;
	}



	total=0;
	*num_of_ranges=0;

	for (x_polarity=0; x_polarity<max_polarity; x_polarity++)
		for (x_range=0; x_range<max_ranges; x_range++) {

			/* determine number of data points in range */
			/* do this by counting number of zeroes at top end of array */
			num_in_current_range=max_points;
			for (x_entry=max_points-1; x_entry>=0; --x_entry) {
				/* pwl is a pointer to either the amplitude or offset control array. */
				/* Multidimensional pointer arrays are not possible, since the array size */
				/* is unknown at compilation time. Thus, the index offset has to be calculated manually. */
				index=true_channel*max_ranges*max_polarity*max_points
				      +x_range*max_polarity*max_points
				      +x_polarity*max_points
				      +x_entry;
				if ( 	fabs(pwl[index])<smallest_allowed_number && (num_in_current_range==x_entry+1)) {
					--num_in_current_range;
				}
			}

			if (num_in_current_range) {
				*num_of_ranges=x_range;

				for (x_entry=0; x_entry<num_in_current_range; x_entry++) {
					++total;
					if (total==calibration_point_number) {
						calibration_point_number=0;  /* to prevent further matches */
						*range=x_range;
						*polarity=x_polarity;
						*entry=x_entry;
						*num_in_range=num_in_current_range;
					}
				}
			}
		}

	return total;
}


int Check_MinMax_Cal(int channel,int parameter)
{
	int status;

	status=OK;

	/* see if new point prevents zero, min, max, or current value from being obtained */
	switch (parameter) {
	case (pwl_ampl_values):
		status=Set_Amplitude(1,0,0,0,0,0,channel,globals.Flash.min_ampl[channel],0)
		       || Set_Amplitude(1,0,0,0,0,0,channel,globals.Flash.max_ampl[channel],0)
		       || Set_Amplitude(1,0,0,0,0,0,channel,0.0,0);
		break;

	case (pwl_os_values):
		status=Set_Offset(1,0,0,0,channel,globals.Flash.min_offset[channel])
		       || Set_Offset(1,0,0,0,channel,globals.Flash.max_offset[channel])
		       || Set_Offset(1,0,0,0,channel,0.0);
		break;

	case pwl_pw_values:
		status=Set_Pw(1,0,0,channel,globals.Flash.min_pw[channel],0)
		       || Set_Pw(1,0,0,channel,globals.Flash.max_pw[channel],0);
		break;

	case pwl_delay_values:
		status=Set_Delay(1,0,0,channel,globals.Flash.min_delay[channel])
		       || Set_Delay(1,0,0,channel,globals.Flash.max_delay[channel]);
		break;

	case pwl_period_values:
		status=Set_frequency(1,0,0,channel,globals.Flash.min_freq[channel])
		       || Set_frequency(1,0,0,channel,globals.Flash.max_freq[channel]);
		break;

	case pwl_burst_values:
		status=Set_Burst_Time(1,0,0,channel,globals.Flash.min_burst_gap[channel])
		       || Set_Burst_Time(1,0,0,channel,globals.Flash.max_burst_gap[channel]);
		break;

	case pwl_rise_time_values:
		status=Set_rise_time(1,0,0,channel,globals.Flash.min_rise_time[channel])
		       || Set_rise_time(1,0,0,channel,globals.Flash.max_rise_time[channel]);
		break;

	case pwl_slew_values:
		status=Set_slew(1,0,0,channel,globals.Flash.min_slew[channel])
		       || Set_slew(1,0,0,channel,globals.Flash.max_slew[channel]);
		break;

	case (pwl_vcc1_values):
		status=Set_Amplitude(1,0,0,0,0,0,channel,0.0,0)
		       || Set_Amplitude(1,0,0,0,0,0,channel,globals.Flash.vcc1_max[0],0);
		break;

	case (pwl_vcc2_values):
		status=Set_Amplitude(1,0,0,0,0,0,channel,0.0,0)
		       || Set_Amplitude(1,0,0,0,0,0,channel,globals.Flash.vcc2_max[0],0);
		break;

	case pwl_load_type_values:
		status=Set_avrq_res(1,0,0,channel,globals.Flash.low_load_type[channel])
		       || Set_avrq_res(1,0,0,channel,globals.Flash.high_load_type[channel]);
		break;
	}

	if (status) {
		/* change the error code to a calibration-related one */
		status=CalibrationMinMaxError;
	}

	return status;
}


int control_pcb107(int address, int dac, int word, int range)
{
	/* reset all lines */
	I2C_Write(PCF8574+address,0);

	set_dac(dac,word);

	/* send and load range data */
	I2C_Write(PCF8574+address,(range << 3));
	I2C_Write(PCF8574+address,(range << 3) | 0x80);

	/* clear the lines */
	I2C_Write(PCF8574+address,0);

	return OK;
}


int Set_Burst_Count(int channel,int count,float new_burst_time)
{
	int check_valid;

	if (globals.Flash.max_burst_count[channel]<=1) {
		return Unrecognized;
	}

	/* abandon if high channel selected by user but not enabled by firmware */
	if (channel && !globals.Flash.ChanKey_Burst_Count) {
		return InvalidChannel;
	}

	/* check duty cycle */
	int i;
	for (i=0; i<max_channels; ++i) {
		TestState[i]=globals.ChannelState[i];
	}
	TestState[channel].burst_count=count;
	if ((check_valid=Error_check(TestState))) {
		return check_valid;
	}

	globals.ChannelState[channel].burst_count = count;
	Set_Update_Chans();

	if (!globals.Flash.burst_func[channel]) {
		/* Call update routine for pulse generator burst mode. */
		/* Update pulse separation at the same time. This routine does the actual changes. */
		Set_Burst_Time(0,0,0,channel,new_burst_time);
	} else {
		int hextext_out;

		/* reset all lines */
		I2C_Write(PCF8574+Second_PW_Port,0);

		/* send first hextet */
		hextext_out=globals.ChannelState[channel].burst_count & 0x003f;

		I2C_Write(PCF8574+Second_PW_Port, hextext_out);
		/* load hextet */
		I2C_Write(PCF8574+Second_PW_Port, hextext_out | 0x40 );

		/* send second hextet */
		if (globals.ChannelState[channel].burst_count > 0) {
			/* set "not_reset" bit high to enable burst mode for N > 0 */
			hextext_out = ((globals.ChannelState[channel].burst_count >> 6) & 0x07) | 0x08;
		} else {
			hextext_out = 0;
		}

		I2C_Write(PCF8574+Second_PW_Port, hextext_out);
		/* load hextet */
		I2C_Write(PCF8574+Second_PW_Port, hextext_out | 0x80 );

		/* clear the lines */
		I2C_Write(PCF8574+Second_PW_Port,0);

	}

	return OK;
}


int Set_Burst_Time(int check_possible_only,int word_override,int range_override,int channel,float new_burst_time)
{
	int count_word_out;
	int point_found,relay_range,UseNegData,entry,actual_pol,word_out,dummy;

	if (globals.Flash.max_burst_count[channel]<=1) {
		return Unrecognized;
	}

	/* abandon if high channel selected by user but not enabled by firmware */
	if (channel && !globals.Flash.ChanKey_Burst_Time) {
		return InvalidChannel;
	}

	if (!check_possible_only) {
		/* check duty cycle */
		int i, check_valid;
		for (i=0; i<max_channels; ++i) {
			TestState[i]=globals.ChannelState[i];
		}
		TestState[channel].burst_time=new_burst_time;
		if ((check_valid=Error_check(TestState))) {
			return check_valid;
		}
	}

	/* tweak count based on actual circuit */
	count_word_out = globals.ChannelState[channel].burst_count - 1;

	/* find appropriate range/fine settings from piece-wise linear data in flash memory */
	if (word_override) {
		word_out=word_override;
		relay_range=range_override;
	} else {
		int status;
		if ((status=Set_VI_Control(pwl_burst_values,channel,new_burst_time,&point_found,
		                           &relay_range,&dummy,&UseNegData,&entry,&word_out,&actual_pol))) {
			return status;
		}
	}

	if (check_possible_only) {
		if (point_found) {
			return OK;
		} else {
			return CalibrationMinMaxError;
		}
	}

	start_gate_override ();

	/* update DAC */
	set_dac(6,word_out);


	/* update range and burst count */

	/* reset all lines */
	I2C_Write(PCF8574+Second_PW_Port,0);

	/* send first hextet */
	int hextext_out=count_word_out & 0x003f;

	I2C_Write(PCF8574+Second_PW_Port, hextext_out);
	/* load hextet */
	I2C_Write(PCF8574+Second_PW_Port, hextext_out | 0x40 );

	/* send second hextet */
	hextext_out=	((count_word_out >> 6) & 0x07) | ((relay_range	 << 3) & 0x0038);

	I2C_Write(PCF8574+Second_PW_Port, hextext_out);
	/* load hextet */
	I2C_Write(PCF8574+Second_PW_Port, hextext_out | 0x80 );

	/* clear the lines */
	I2C_Write(PCF8574+Second_PW_Port,0);

	if (globals.Registers.last_relay_driver_settings[2] != relay_range) {
		g_usleep (1e3 * globals.Timers.normal_relay_bounce_time_in_milliseconds);
	}
	stop_gate_override ();

	globals.Registers.last_relay_driver_settings[2] = relay_range;

	globals.ChannelState[channel].burst_time = new_burst_time;

	Set_Update_Chans();

	return OK;
}


int Set_rise_time(int check_possible_only,int word_override,int range_override,int channel,float new_rise_time)
{
	int point_found,relay_range,UseNegData,entry,actual_pol,word_out,dummy;
	char range_control;

	if (globals.Flash.fixed_rise_time[channel]) {
		return Unrecognized;
	}

	/* abandon if high channel selected by user but not enabled by firmware */
	if (channel && !globals.Flash.ChanKey_rise_time) {
		return InvalidChannel;
	}

	if (!check_possible_only) {
		/* check duty cycle */
		int i, check_valid;
		for (i=0; i<max_channels; ++i) {
			TestState[i]=globals.ChannelState[i];
		}
		TestState[channel].rise_time=new_rise_time;
		if ((check_valid=Error_check(TestState))) {
			return check_valid;
		}
	}

	/* find appropriate range/fine settings from piece-wise linear data in flash memory */
	if (word_override) {
		word_out=word_override;
		relay_range=range_override;
	} else {
		int status;
		if ((status=Set_VI_Control(pwl_rise_time_values,channel,new_rise_time,&point_found,
		                           &relay_range,&dummy,&UseNegData,&entry,&word_out,&actual_pol))) {
			return status;
		}
	}

	if (check_possible_only) {
		if (point_found) {
			return OK;
		} else {
			return CalibrationMinMaxError;
		}
	}

	set_dac(globals.Flash.rise_time_dac[channel],word_out);

	if (!globals.Flash.pcb_203a_rise_time[channel]) {
		/* original AVRQ PCB */
		switch (relay_range) {
		case 0:
			range_control = (char) 0x01;
			break;
		case 1:
			range_control = (char) 0x08;
			break;
		case 2:
			range_control = (char) 0x10;
			break;
		case 3:
			range_control = (char) 0x20;
			break;
		case 4:
			range_control = (char) 0x40;
			break;
		case 5:
			range_control = (char) 0x80;
			break;
		case 6:
		case 7:
			return HardwareError;
			break;
		}
	} else {
		/* PCB 203A */
		switch (relay_range) {
		case 0:
			range_control = (char) 0x0f;
			break;
		case 1:
			range_control = (char) 0x02;
			break;
		case 2:
			range_control = (char) 0x04;
			break;
		case 3:
			range_control = (char) 0x08;
			break;
		case 4:
			range_control = (char) 0x10;
			break;
		case 5:
		case 6:
		case 7:
			return HardwareError;
			break;
		}
	}



	if (globals.Registers.last_rise_time_relay_setting != range_control) {
		/* update range hardware */
		if (globals.ChannelState[channel].trigger_source!=source_hold) {
			bus_setpin(O_GATE, 1);
		}

		if (!globals.Flash.pcb_203a_rise_time[channel]) {
			I2C_Write(PCF8574A+rise_time_port,range_control);
		} else {
			globals.Registers.avrq_reg = (range_control & 0x1f) |
			                             (globals.Registers.avrq_reg & 0xe0);
			I2C_Write(PCF8574A+rise_time_port,globals.Registers.avrq_reg);
		}
		g_usleep (5e5);

		if (globals.ChannelState[channel].trigger_source!=source_hold) {
			bus_setpin(O_GATE, 0);
		}
	}

	globals.ChannelState[channel].rise_time = new_rise_time;
	globals.Registers.last_rise_time_relay_setting = range_control;

	Set_Update_Chans();

	return OK;
}


int Set_current_limit(int check_possible_only,int channel,float new_adj_current_limit)
{
	float limit;
	int word_out;

	if (!globals.Flash.hard_current_limit_enabled[channel]) {
		return Unrecognized;
	}

	/* abandon if high channel selected by user but not enabled by firmware */
	if (channel && !globals.Flash.ChanKey_current_limit) {
		return InvalidChannel;
	}

	if (!check_possible_only) {
		int i, check_valid;
		for (i=0; i<max_channels; ++i) {
			TestState[i]=globals.ChannelState[i];
		}
		TestState[channel].soft_current_limit=new_adj_current_limit;
		if ((check_valid=Error_check(TestState))) {
			return check_valid;
		}
	}

	if (globals.ChannelState[channel].func_mode==dc_mode_on) {
		limit=globals.Flash.current_limit_dc_mode[channel];
	} else {
		limit=globals.Flash.current_limit_pulse_mode[channel];
	}

	if (globals.Flash.soft_current_limit_enabled[channel] && (new_adj_current_limit<limit)) {
		limit=new_adj_current_limit;
	}

	word_out = (int) ( ((float) dac_max) * (limit/globals.Flash.current_limit_full_scale[channel]) );
	set_dac(globals.Flash.current_limit_dac[channel],word_out);

	globals.ChannelState[channel].soft_current_limit=limit;

	return OK;
}


int Set_avrq_res(int check_possible_only,int word_override,int range_override,int channel,float new_res)
{
	int point_found,relay_range,UseNegData,entry,actual_pol,word_out,dummy;

	if (!globals.Flash.switchable_load[channel]) {
		return Unrecognized;
	}

	/* abandon if high channel selected by user but not enabled by firmware */
	if (channel && !globals.Flash.ChanKey_load_type) {
		return InvalidChannel;
	}

	if (!check_possible_only) {
		int i, check_valid;

		for (i=0; i<max_channels; ++i) {
			TestState[i]=globals.ChannelState[i];
		}
		TestState[channel].load_type=new_res;
		if ((check_valid=Error_check(TestState))) {
			return check_valid;
		}
	}

	/* find appropriate range/fine settings from piece-wise linear data in flash memory */
	if (word_override) {
		word_out=word_override;
		relay_range=range_override;
	} else {
		/* ugly hack - fix me?
		   CH2 = opto VCC
		   Calibrate res at 3.0V
		   Compensate for >3.0V */
		float compensated_new_res;

		if (globals.ChannelState[0].vcc2 > 0.0) {
			compensated_new_res = new_res * 3.0/globals.ChannelState[0].vcc2;
		} else {
			return amplitude_lower_limit;
		}

		int status;
		if ((status=Set_VI_Control(pwl_load_type_values,channel,compensated_new_res,&point_found,
		                           &relay_range,&dummy,&UseNegData,&entry,&word_out,&actual_pol))) {
			return status;
		}
	}

	if (check_possible_only) {
		if (point_found) {
			return OK;
		} else {
			return CalibrationMinMaxError;
		}
	}

	set_dac(globals.Flash.load_type_dac[channel],word_out);

	/* no ranges are used */

	/* update variables if OK */
	if (new_res!=globals.ChannelState[channel].load_type) {
		globals.Changes.update_load=YES;
	}

	globals.ChannelState[channel].load_type = new_res;

	Set_Update_Chans();

	return OK;
}


int Set_slew(int check_possible_only,int word_override,int range_override,int channel,float new_slew)
{
	int check_valid;
	int status,point_found,relay_range,UseNegData,entry,actual_pol,word_out,dummy;
	char range_control;

	if (!globals.Flash.curr_slew[channel]) {
		return Unrecognized;
	}

	/* abandon if high channel selected by user but not enabled by firmware */
	if (channel && !globals.Flash.ChanKey_slew) {
		return InvalidChannel;
	}

	if (!check_possible_only) {
		/* check duty cycle */
		int i;
		for (i=0; i<max_channels; ++i) {
			TestState[i]=globals.ChannelState[i];
		}
		TestState[channel].slew=new_slew;
		if ((check_valid=Error_check(TestState))) {
			return check_valid;
		}
	}

	/* find appropriate range/fine settings from piece-wise linear data in flash memory */
	if (word_override) {
		word_out=word_override;
		relay_range=range_override;
	} else {
		if ((status=Set_VI_Control(pwl_slew_values,channel,new_slew,&point_found,
		                           &relay_range,&dummy,&UseNegData,&entry,&word_out,&actual_pol))) {
			return status;
		}
	}

	if (check_possible_only) {
		if (point_found) {
			return OK;
		} else {
			return CalibrationMinMaxError;
		}
	}

	set_dac(globals.Flash.slew_dac[channel],word_out);

	switch (relay_range) {
	case 0:
		range_control = 0x0f;
		break;
	case 1:
		range_control = 0x07;
		break;
	case 2:
		range_control = 0x03;
		break;
	case 3:
		range_control = 0x01;
		break;
	case 4:
	default:
		range_control = 0x00;
		break;
	}

	I2C_Write(PCF8574,range_control);

	globals.ChannelState[channel].slew = new_slew;

	Set_Update_Chans();

	return OK;
}


int Set_avrq_ampl(int check_possible_only,int word_override,int range_override,int channel,float new_avrq_ampl)
{
	int check_valid;
	int status,point_found,relay_range,UseNegData,entry,actual_pol,word_out,dummy;
	int true_channel;
	int use_pwl;

	if (!globals.Flash.enable_avrq_extra_ampls) {
		return Unrecognized;
	}

	/* check channels */
	if ((channel < 1) || (channel>4)) {
		return InvalidChannel;
	}

	if (channel == 1) {
		true_channel = 1;
	} else {
		true_channel = 0;
	}

	if (!check_possible_only) {
		/* check duty cycle */
		int i;
		for (i=0; i<max_channels; ++i) {
			TestState[i]=globals.ChannelState[i];
		}
		switch (channel) {
		case 1:
			TestState[true_channel].amplitude=new_avrq_ampl;
			break;
		case 2:
			TestState[true_channel].vcc1=new_avrq_ampl;
			if (TestState[true_channel].vlogic >= smallest_allowed_number) {
				TestState[true_channel].vlogic = new_avrq_ampl;
			}
			break;
		case 3:
			TestState[true_channel].vcc2=new_avrq_ampl;
			break;
		case 4:
			TestState[true_channel].vlogic=new_avrq_ampl;
			break;
		}
		if ((check_valid=Error_check(TestState))) {
			return check_valid;
		}
	}

	/* find appropriate range/fine settings from piece-wise linear data in flash memory */
	if (word_override) {
		word_out=word_override;
		relay_range=range_override;
	} else {
		switch (channel) {
		case 1:
			use_pwl = pwl_ampl_values;
			break;
		case 2:
			use_pwl = pwl_vcc1_values;
			break;
		case 3:
			use_pwl = pwl_vcc2_values;
			break;
		case 4:
			use_pwl = pwl_vlogic_values;
			if (new_avrq_ampl > 0.0) {
				new_avrq_ampl = globals.ChannelState[true_channel].vcc1;
			}
			break;
		}

		if (use_pwl != pwl_vlogic_values)
			if ((status=Set_VI_Control(use_pwl,channel,new_avrq_ampl,&point_found,&relay_range,&dummy,&UseNegData,&entry,&word_out,&actual_pol))) {
				return status;
			}

	}

	if (check_possible_only) {
		if (point_found) {
			return OK;
		} else {
			return CalibrationMinMaxError;
		}
	}


	/* update variables if OK */
	switch (channel) {
	case 1:
		Set_avrq_ampl(0,0,0,4,0.0);

		set_dac(1,word_out);

		globals.Registers.avrq_reg = globals.Registers.avrq_reg & 0x3f;
		I2C_Write(PCF8574A+rise_time_port,globals.Registers.avrq_reg);

		if (new_avrq_ampl!=globals.ChannelState[true_channel].amplitude) {
			globals.Changes.update_amp=YES;
		}
		globals.ChannelState[true_channel].amplitude=new_avrq_ampl;
		break;
	case 2:
		set_dac(1,word_out);

		globals.Registers.avrq_reg = 0x40 | (globals.Registers.avrq_reg & 0xbf);
		I2C_Write(PCF8574A+rise_time_port,globals.Registers.avrq_reg);

		if (new_avrq_ampl!=globals.ChannelState[true_channel].vcc1) {
			globals.Changes.update_amp=YES;
		}
		globals.ChannelState[true_channel].vcc1=new_avrq_ampl;
		break;
	case 3:
		set_dac(0,word_out);
		if (new_avrq_ampl!=globals.ChannelState[true_channel].vcc2) {
			globals.Changes.update_amp=YES;
		}
		globals.ChannelState[true_channel].vcc2=new_avrq_ampl;

		/* update ampl-dependent load resistance */
		Set_avrq_res(0,0,0,0,globals.ChannelState[0].load_type);

		break;
	case 4:
		if (new_avrq_ampl >= smallest_allowed_number) {
			relay_range = 0x80;
		} else {
			relay_range = 0x0;
		}
		globals.Registers.avrq_reg = relay_range | (globals.Registers.avrq_reg & 0x7f);
		I2C_Write(PCF8574A+rise_time_port,globals.Registers.avrq_reg);

		if (new_avrq_ampl!=globals.ChannelState[true_channel].vlogic) {
			globals.Changes.update_amp=YES;
		}
		globals.ChannelState[true_channel].vlogic=new_avrq_ampl;
		break;
	}


	/* reduce vlogic based on vcc1, if required */
	if (channel == 2) {
		if (globals.ChannelState[true_channel].vlogic > smallest_allowed_number) {
			Set_avrq_ampl(0,0,0,4,globals.ChannelState[true_channel].vcc1);
		} else {
			Set_avrq_ampl(0,0,0,4,0.0);
		}
	}

	/* reduce vcc1 if ibias is active, because they share a DAC */
	if ((channel == 1) && (globals.ChannelState[true_channel].amplitude > smallest_allowed_number)) {
		globals.ChannelState[0].vcc1=0.0;
	}

	/* reduce vcc1 if ibias is active, because they share a DAC */
	if ((channel == 2) && (globals.ChannelState[true_channel].vcc1 > smallest_allowed_number)) {
		globals.ChannelState[1].amplitude=0.0;
	}

	Set_Update_Chans();

	return OK;
}


int go_cal(CalStruct *caldata)
{
	int i, status, points;
	float meas, nom_val, change;
	float min_val, max_val, chk_val;

	gchar *lcd_msg = NULL;
	gchar *prefix = NULL;

	points = Get_VI_Num_Pnts(caldata->cal_type,caldata->channel);

	caldata->count = 0;
	caldata->error = 0;
	caldata->max_change = 0.0;
	caldata->avg_change = 0.0;

	min_val = 100e-6;
	if (caldata->cal_type == pwl_period_values) {
		max_val = 4.0 / globals.Flash.min_freq[caldata->channel];
		chk_val = 0.25 / globals.Flash.max_freq[caldata->channel];
		if (chk_val > min_val) {
			min_val = chk_val;
		}
	} else if (caldata->cal_type == pwl_pw_values) {
		max_val = 4.0 * globals.Flash.max_pw[caldata->channel];
		chk_val = 0.25 * globals.Flash.min_pw[caldata->channel];
		if (chk_val > min_val) {
			min_val = chk_val;
		}
	} else if (caldata->cal_type == pwl_delay_values) {
		max_val = 4.0 * globals.Flash.max_delay[caldata->channel];
	}

	for (i=1; i<=points; i++) {
		Set_Cal_Nom(caldata->channel,i,caldata->cal_type,&nom_val);
		if ( (nom_val >= min_val) && (nom_val <= max_val) ) {
			Set_Cal_Nom(caldata->channel,i,caldata->cal_type,NULL);

			LCD_clear();
			LCD_write(0,0,"Self-calibration in progress.");

			if (caldata->cal_type == pwl_period_values) {
				prefix = g_strdup_printf ("CH%d PER %d:",caldata->channel+1,i);
			} else if (caldata->cal_type == pwl_pw_values) {
				prefix = g_strdup_printf ("CH%d PW %d:",caldata->channel+1,i);
			} else if (caldata->cal_type == pwl_delay_values) {
				prefix = g_strdup_printf ("CH%d DLY %d:",caldata->channel+1,i);
			}

			lcd_msg = g_strdup_printf ("%s %.3e",prefix,nom_val);
			LCD_write(2,0,lcd_msg);
			g_free (lcd_msg);

			if (caldata->cal_type == pwl_period_values) {
				I2C_Self_Cal (caldata->channel, MEAS_PRF, &meas, 1.0 / globals.ChannelState[caldata->channel].frequency);
			} else if (caldata->cal_type == pwl_pw_values) {
				I2C_Self_Cal (caldata->channel, MEAS_PW, &meas, globals.ChannelState[caldata->channel].pw);
			} else {
				I2C_Self_Cal (caldata->channel, MEAS_PW, &meas, globals.ChannelState[caldata->channel].delay);
			}

			change = 100.0 * (meas-nom_val)/nom_val;
			status = Set_VI_Cal_Pnt(caldata->cal_type,caldata->channel,i,meas);
			if (status) {
				++caldata->error;
			} else {
				++caldata->count;
				if (fabs(change)>caldata->max_change) {
					caldata->max_change = change;
				}
				caldata->avg_change = ((caldata->avg_change * (((float) caldata->count) - 1.0)) + change) / caldata->count;
			}

			LCD_clear();
			LCD_write(0,0,"Self-calibration in progress.");

			lcd_msg = g_strdup_printf ("%s %.3e -> %.3e",prefix,nom_val,meas);
			LCD_write(2,0,lcd_msg);
			g_free (lcd_msg);

			lcd_msg = g_strdup_printf ("changed %6.2f%%, err: %d",change,status);
			LCD_write(3,0,lcd_msg);
			g_free (lcd_msg);

			g_free (prefix);

			g_usleep (2e6);
		}
	}

	return OK;
}


void cal_string(char *parameter, CalStruct *caldata)
{
	gchar *string = g_strdup_printf ("CH%d %s cal: %d errors. Adj: %6.2f%% max, %6.2f%% avg.\r\n", caldata->channel+1,parameter, caldata->error, caldata->max_change, caldata->avg_change);
	g_string_append (caldata->response, string);
	g_free (string);
}


int self_cal()
{
	CalStruct caldata;

	int status = do_full_self_cal(&caldata);

	g_string_free (caldata.response, TRUE);

	return status;
}


int do_full_self_cal(CalStruct *caldata)
{
	gchar *string;
	long start_timer, diff_timer;
	int eprom_loc;

	caldata->response = g_string_new ("");

	Menu_Clear_Buttons();
	LCD_clear();              /*0123456789012345678901234567890123456789*/
	LCD_write(0,0,"Self-calibration in progress.");
	/*0123456789012345678901234567890123456789*/

	string = g_strdup_printf ("Typical run time: %d min, %d sec.", globals.Flash.self_cal_typical_time_min, globals.Flash.self_cal_typical_time_sec);
	LCD_write(1,0,string);
	g_free (string);

	while ((sec_timer() - globals.Timers.startup_timer_value) < (long)globals.Flash.self_cal_pause) {
		/*0123456789012345678901234567890123456789*/
		string = g_strdup_printf ("Min warm-up is %ds, on for %lds.", globals.Flash.self_cal_pause, sec_timer() - globals.Timers.startup_timer_value);
		LCD_write(3,0,string);
		g_free (string);
		g_usleep (2e5);
	}

	start_timer = sec_timer();
	caldata->total_errors = 0;
	caldata->total_max_change = 0.0;

	caldata->cal_type = pwl_pw_values;
	for (caldata->channel=0; caldata->channel<(globals.Flash.ChanKey_pw?globals.Flash.channels:1); ++caldata->channel) {
		go_cal(caldata);
		cal_string("PW", caldata);
		caldata->total_errors += caldata->error;
		if (fabs(caldata->total_max_change) < fabs(caldata->max_change)) {
			caldata->total_max_change = caldata->max_change;
		}
	}

	caldata->cal_type = pwl_delay_values;
	for (caldata->channel=0; caldata->channel<(globals.Flash.ChanKey_delay?globals.Flash.channels:1); ++caldata->channel) {
		go_cal(caldata);
		cal_string("Delay", caldata);
		caldata->total_errors += caldata->error;
		if (fabs(caldata->total_max_change) < fabs(caldata->max_change)) {
			caldata->total_max_change = caldata->max_change;
		}
	}

	caldata->cal_type = pwl_period_values;
	for (caldata->channel=0; caldata->channel<(globals.Flash.ChanKey_frequency?globals.Flash.channels:1); ++caldata->channel) {
		go_cal(caldata);
		cal_string("Period", caldata);
		caldata->total_errors += caldata->error;
		if (fabs(caldata->total_max_change) < fabs(caldata->max_change)) {
			caldata->total_max_change = caldata->max_change;
		}
	}

	Main_Rst();

	LCD_clear();              /*0123456789012345678901234567890123456789*/
	LCD_write(0,0,"Self-calibration complete.");

	string = g_strdup_printf ("%d errors. Max change: %6.2f%%", caldata->total_errors, caldata->total_max_change);
	LCD_write(1,0,string);
	g_free (string);

	LCD_write(2,0,"More details are provided by \"cal?\"");
	g_usleep (3e6);

	Show_Main_Menu();

	diff_timer = sec_timer() - start_timer;

	globals.Flash.self_cal_typical_time_min = (int) (diff_timer / 60);
	globals.Flash.self_cal_typical_time_sec = (int) (diff_timer % 60);

	string = g_strdup_printf ("Completed in %d minutes and %d seconds\r\n", globals.Flash.self_cal_typical_time_min, globals.Flash.self_cal_typical_time_sec);
	g_string_append (caldata->response, string);
	g_free (string);

	eprom_loc = (char *) &(globals.Flash.self_cal_typical_time_min) - (char *) &(globals.Flash.flash_start);
	writeUserBlock(&globals.Flash, eprom_loc, sizeof(globals.Flash.self_cal_typical_time_min));
	eprom_loc = (char *) &(globals.Flash.self_cal_typical_time_sec) - (char *) &(globals.Flash.flash_start);
	writeUserBlock(&globals.Flash, eprom_loc, sizeof(globals.Flash.self_cal_typical_time_sec));

	if (caldata->total_errors) {
		return SelfCalError;
	} else {
		return OK;
	}
}


int I2C_Self_Cal(int channel, int meas_mode, float *meas, float target_time)
{
#define MAX_TRIES 35
#define AVG_TRIES 4

	int word, buffer, ch_mode;
	long count;
	float measuring_time;
	float min_time;
	float avg, max_error, this, this_error;
	int iter, stable,i;
	float prev[AVG_TRIES];

	for (i=0; i<AVG_TRIES; i++) {
		prev[i]=0.0;
	}

	*meas = 0.0;
	stable = 0;

	if (channel) {
		ch_mode = MEAS_CH2;
	} else {
		ch_mode = MEAS_CH1;
	}

	measuring_time = 1.0;

	min_time = globals.ChannelState[channel].pw * 3.0;
	if (min_time > measuring_time) {
		measuring_time = min_time;
	}

	min_time = (1.0/globals.ChannelState[channel].frequency);
	if (min_time > measuring_time) {
		measuring_time = min_time;
	}

	if (meas_mode == MEAS_PRF) {
		min_time = (1.0/globals.ChannelState[channel].frequency) * 3.0;
		if (min_time > measuring_time) {
			measuring_time = min_time;
		}
	}

	min_time = fabs(globals.ChannelState[channel].delay) * 3.0;
	if (min_time > measuring_time) {
		measuring_time = min_time;
	}

	for (iter = 1; (iter <= MAX_TRIES) && !stable; iter++) {

		if (iter <= LCD_cols) {
			LCD_write(3,(iter-1) % 35,". ");
		}

		if (meas_mode == MEAS_PW) {
			start_gate_override ();
			// delay to let oscillator reset, within 20% of a period?
			g_usleep((gulong) (2.0e5 / globals.ChannelState[channel].frequency));
		}

		// reset counters
		word = NOT_ENABLE_COUNT | NOT_LOAD_BUFFERS_CLK | CLR_COUNTERS | meas_mode | ch_mode | COUNTER_BYTE_0;
		I2C_Write(PCF8574+To_Self_Cal_Port,word);
		word = NOT_ENABLE_COUNT | NOT_LOAD_BUFFERS_CLK | NOT_CLR_COUNTERS | meas_mode | ch_mode | COUNTER_BYTE_0;
		I2C_Write(PCF8574+To_Self_Cal_Port,word);

		// arm flip-flops
		word = ENABLE_COUNT | NOT_LOAD_BUFFERS_CLK | NOT_CLR_COUNTERS | meas_mode | ch_mode | COUNTER_BYTE_0;
		I2C_Write(PCF8574+To_Self_Cal_Port,word);
		word = NOT_ENABLE_COUNT | NOT_LOAD_BUFFERS_CLK | NOT_CLR_COUNTERS | meas_mode | ch_mode | COUNTER_BYTE_0;
		I2C_Write(PCF8574+To_Self_Cal_Port,word);

		if (meas_mode == MEAS_PW) {
			stop_gate_override ();
		}

		// time to measure
		g_usleep((gulong) (measuring_time * 1.0e6));

		// latch into buffers
		word = NOT_ENABLE_COUNT | LOAD_BUFFERS_CLK | NOT_CLR_COUNTERS | meas_mode | ch_mode | COUNTER_BYTE_0;
		I2C_Write(PCF8574+To_Self_Cal_Port,word);
		word = NOT_ENABLE_COUNT | NOT_LOAD_BUFFERS_CLK | NOT_CLR_COUNTERS | meas_mode | ch_mode | COUNTER_BYTE_0;
		I2C_Write(PCF8574+To_Self_Cal_Port,word);

		// read buffers
		buffer = I2C_Read (PCF8574+From_Self_Cal_Port);
		count = (long) buffer;

		word = NOT_ENABLE_COUNT | NOT_LOAD_BUFFERS_CLK | NOT_CLR_COUNTERS | meas_mode | ch_mode | COUNTER_BYTE_1;
		I2C_Write(PCF8574+To_Self_Cal_Port,word);
		buffer = I2C_Read (PCF8574+From_Self_Cal_Port);
		count = count | ((long) buffer << 8);

		word = NOT_ENABLE_COUNT | NOT_LOAD_BUFFERS_CLK | NOT_CLR_COUNTERS | meas_mode | ch_mode | COUNTER_BYTE_2;
		I2C_Write(PCF8574+To_Self_Cal_Port,word);
		buffer = I2C_Read (PCF8574+From_Self_Cal_Port);
		count = count | ((long) buffer << 16);

		word = NOT_ENABLE_COUNT | NOT_LOAD_BUFFERS_CLK | NOT_CLR_COUNTERS | meas_mode | ch_mode | COUNTER_BYTE_3;
		I2C_Write(PCF8574+To_Self_Cal_Port,word);
		buffer = I2C_Read (PCF8574+From_Self_Cal_Port);
		count = count | ((long) buffer << 24);

		if (count < 100) {
			//return SelfCalError;
			if (iter <= LCD_cols) {
				LCD_write(3,iter-1,"x");
			}
			continue;
		}

		this = count * 100e-9;
		max_error = 0.001;
		avg = 0.0;

		for (i=0; i<(AVG_TRIES-1); i++) {
			prev[i]=prev[i+1];
		}
		prev[AVG_TRIES-1] = this;
		for (i = 0; i<AVG_TRIES; i++) {
			avg += (prev[i]/AVG_TRIES);
		}

		this_error = fabs ((this - avg) / avg);
		if (this_error < max_error) {
			stable = 1;
		}
	}

	if (!stable) {
		return SelfCalError;
	}

	*meas = this;
	return OK;
}


void Set_Rcl(int setting_num)
{
	int temp;
	int i;

	globals.Flags.do_check_settings=NO;	/* don't check for conflicting settings */
	globals.Flags.extended_ampl_min_max=NO;

	// go backwards, so channel 0 overrides everything
	for (i=globals.Flash.channels-1; i>=0; --i) {

		globals.ChannelState[i].route_primary=globals.Flash.rcl_route_primary[i][setting_num];
		globals.ChannelState[i].route_secondary=globals.Flash.rcl_route_secondary[i][setting_num];

		globals.ChannelState[i].frequency=globals.Flash.rcl_frequency[i][setting_num];
		globals.ChannelState[i].delay=globals.Flash.rcl_delay[i][setting_num];
		globals.ChannelState[i].pw=globals.Flash.rcl_pw[i][setting_num];
		globals.ChannelState[i].amplitude=globals.Flash.rcl_amplitude[i][setting_num];
		globals.ChannelState[i].offset=globals.Flash.rcl_offset[i][setting_num];
		globals.ChannelState[i].burst_count=globals.Flash.rcl_burst_count[i][setting_num];
		globals.ChannelState[i].burst_time=globals.Flash.rcl_burst_time[i][setting_num];
		globals.ChannelState[i].rise_time=globals.Flash.rcl_rise_time[i][setting_num];
		globals.ChannelState[i].slew=globals.Flash.rcl_slew[i][setting_num];

		globals.ChannelState[i].vcc1=globals.Flash.rcl_vcc1[i][setting_num];
		globals.ChannelState[i].vcc2=globals.Flash.rcl_vcc2[i][setting_num];
		globals.ChannelState[i].vlogic=globals.Flash.rcl_vlogic[i][setting_num];

		globals.ChannelState[i].soft_current_limit=globals.Flash.rcl_soft_current_limit[i][setting_num];
		globals.ChannelState[i].load_type=globals.Flash.rcl_load[i][setting_num];

		temp=globals.Flash.rcl_misc[i][setting_num];

		if (temp&1) {
			globals.ChannelState[i].zout=globals.Flash.zout_max[i];
		} else {
			globals.ChannelState[i].zout=globals.Flash.zout_min[i];
		}
		globals.ChannelState[i].hold_setting = (temp >> 1) & 1;
		globals.ChannelState[i].double_pulse = (temp >> 2) & 1;
		globals.ChannelState[i].pw_ctrl_mode = (temp >> 3) & 3;
		globals.ChannelState[i].polarity = (temp >> 5) & 1;
		globals.ChannelState[i].output_state = (temp >> 6) & 1;
		globals.ChannelState[i].gate_type = (temp >> 7) & 1;
		globals.ChannelState[i].trigger_source = (temp >> 8) & 7;
		globals.ChannelState[i].amp_mode = (temp >> 11) & 1;
		globals.ChannelState[i].gate_level = (temp >> 12) & 1;
		globals.ChannelState[i].logic_level = (temp >> 14) & 1;
		globals.ChannelState[i].os_mode = (temp >> 15) & 1;

		temp=globals.Flash.rcl_misc2[i][setting_num];
		/* the high bit of amp_mode was added later; hence the odd reconstruction */
		globals.ChannelState[i].amp_mode |= (temp << 1) & 2;
		globals.ChannelState[i].func_mode = (temp >> 2) & 0x1f;	/* 5 bits! */

		Set_Route(i,ROUTE_PRIMARY,globals.ChannelState[i].route_primary);
		Set_Route(i,ROUTE_SECONDARY,globals.ChannelState[i].route_secondary);

		Set_frequency(0,0,0,i,globals.ChannelState[i].frequency);
		Set_Delay(0,0,0,i,globals.ChannelState[i].delay);

		Set_Pw(0,0,0,i,globals.ChannelState[i].pw,0);

		Set_Double(i,globals.ChannelState[i].double_pulse);
		Set_Pwmode(i,globals.ChannelState[i].pw_ctrl_mode);

		Set_Func(i,globals.ChannelState[i].func_mode);
		Set_Pol(i,globals.ChannelState[i].polarity);
		Set_Gate_Sync(i,globals.ChannelState[i].gate_type);
		Set_Gate_Level(i,globals.ChannelState[i].gate_level);
		Set_EA(i,globals.ChannelState[i].amp_mode);
		Set_EO(i,globals.ChannelState[i].os_mode);

		if (globals.Flash.switchable_zout[i]) {
			Set_zout(i,globals.ChannelState[i].zout,1);
		}

		Set_Load(i,globals.ChannelState[i].load_type);
		Set_Logic_Level(i,globals.ChannelState[i].logic_level);
		Set_rise_time(0,0,0,i,globals.ChannelState[i].rise_time);
		Set_slew(0,0,0,i,globals.ChannelState[i].slew);
		Set_current_limit(0,i,globals.ChannelState[i].soft_current_limit);

		if (globals.Flash.max_burst_count[i]>1) {
			Set_Burst_Count(i,globals.ChannelState[i].burst_count,globals.ChannelState[i].burst_time);
		}

		Set_Trig_Source(i,globals.ChannelState[i].trigger_source);

		Set_Amplitude(0,0,0,0,0,0,i,globals.ChannelState[i].amplitude,0);

		if	 (globals.Flash.voltage_offset_enabled[i] || globals.Flash.current_offset_enabled[i]) {
			Set_Offset(0,0,0,0,i,globals.ChannelState[i].offset);
		}

		Set_avrq_ampl(0,0,0,2,globals.ChannelState[i].vcc1);
		Set_avrq_ampl(0,0,0,3,globals.ChannelState[i].vcc2);
		Set_avrq_ampl(0,0,0,4,globals.ChannelState[i].vlogic);

		Set_Output_State(i,globals.ChannelState[i].output_state);

	}

	Menu_Clear_Buttons();

	globals.Flags.do_check_settings=YES;	/* check for conflicting settings */

	Error_check(globals.ChannelState);	/* establishes min/max values for queries, but reports no errors */

	return;
}


void Set_Sav(int setting_num)
{
	int temp;
	int i;

	for (i=0; i<globals.Flash.channels; i++) {

		/* float and large integers */
		globals.Flash.rcl_route_primary[i][setting_num]=globals.ChannelState[i].route_primary;
		globals.Flash.rcl_route_secondary[i][setting_num]=globals.ChannelState[i].route_secondary;

		globals.Flash.rcl_frequency[i][setting_num]=globals.ChannelState[i].frequency;
		globals.Flash.rcl_delay[i][setting_num]=globals.ChannelState[i].delay;
		globals.Flash.rcl_pw[i][setting_num]=globals.ChannelState[i].pw;
		globals.Flash.rcl_amplitude[i][setting_num]=globals.ChannelState[i].amplitude;
		globals.Flash.rcl_offset[i][setting_num]=globals.ChannelState[i].offset;
		globals.Flash.rcl_burst_count[i][setting_num]=globals.ChannelState[i].burst_count;
		globals.Flash.rcl_burst_time[i][setting_num]=globals.ChannelState[i].burst_time;
		globals.Flash.rcl_rise_time[i][setting_num]=globals.ChannelState[i].rise_time;
		globals.Flash.rcl_slew[i][setting_num]=globals.ChannelState[i].slew;
		globals.Flash.rcl_soft_current_limit[i][setting_num]=globals.ChannelState[i].soft_current_limit;
		globals.Flash.rcl_load[i][setting_num]=globals.ChannelState[i].load_type;
		globals.Flash.rcl_vcc1[i][setting_num]=globals.ChannelState[i].vcc1;
		globals.Flash.rcl_vcc2[i][setting_num]=globals.ChannelState[i].vcc2;
		globals.Flash.rcl_vlogic[i][setting_num]=globals.ChannelState[i].vlogic;

		/* integers	 		setting_nums
		globals.ChannelState[i].zout					50 (1) or min (0)
		globals.ChannelState[i].hold_setting		hold_width 0, hold_duty 1
		globals.ChannelState[i].double_pulse		double_on 1, double_off 0
		globals.ChannelState[i].pw_ctrl_mode				pw_normal 1, pw_in_out 0
		globals.ChannelState[i].func_mode			pulse_mode_on 0, dc_mode_on 1
		globals.ChannelState[i].polarity				pol_norm 0, pol_complement 1
		globals.ChannelState[i].output_state		output_off 0,output_on 1
		globals.ChannelState[i].gate_type			gate_sync 0, gate_async 1
		globals.ChannelState[i].trigger_source		source_internal 0, source_external 1, source_manual 2, source_hold 3, source_immediate 4
		globals.ChannelState[i].amp_mode			normal 0, ea 1, ext amplify 2
		globals.ChannelState[i].gate_level			gate_low 0, gate_high 1
		*/

		/* compress the assorted integer parameters into a single bitmapped integer */
		if (globals.ChannelState[i].zout==globals.Flash.zout_max[i]) {
			temp=1;
		} else {
			temp=0;
		}
		temp |= globals.ChannelState[i].hold_setting << 1;
		temp |= globals.ChannelState[i].double_pulse << 2;
		temp |= globals.ChannelState[i].pw_ctrl_mode << 3;
		temp |= globals.ChannelState[i].polarity << 5;
		temp |= globals.ChannelState[i].output_state << 6;
		temp |= globals.ChannelState[i].gate_type << 7;
		temp |= globals.ChannelState[i].trigger_source << 8;
		temp |= (globals.ChannelState[i].amp_mode & 1) << 11;
		temp |= globals.ChannelState[i].gate_level << 12;
		temp |= globals.ChannelState[i].logic_level << 14;
		temp |= globals.ChannelState[i].os_mode << 15;

		globals.Flash.rcl_misc[i][setting_num]=temp;

		/* need another integer to hold everything now */
		temp  = 0;
		temp |= ( (globals.ChannelState[i].amp_mode>1) & 1) << 0;
		temp |= globals.ChannelState[i].func_mode << 2;	/* 5 bits! */

		globals.Flash.rcl_misc2[i][setting_num]=temp;

	}

	/* save everything */
	writeUserBlock(&globals.Flash, 0, sizeof(globals.Flash));

	return;
}


void Main_update_shift_registers()
{
	static GStaticMutex mutex = G_STATIC_MUTEX_INIT;
	g_static_mutex_lock (&mutex);

	/* send MSB first, LSB last */
	/* send highest # SR first */

	int i,j,n;	  /* counters */
	char data_out;
	int temp_output_state[max_channels];

	/* suppress triggering during relay switching */
	if (	globals.Registers.last_relay_driver_settings[0]!=globals.Registers.shift_reg_out[2]
	                || globals.Registers.last_relay_driver_settings[1]!=globals.Registers.shift_reg_out[3]) {
		start_gate_override ();
	}


	/* physically turn off output for amplitude range changes */
	/* i.e., short output in 155's */
	if (globals.Flags.force_output_fully_off==YES)
		for (i=0; i<(globals.Flash.ChanKey_frequency?globals.Flash.channels:1); ++i) {
			temp_output_state[i]=globals.ChannelState[i].output_state;
			Set_Output_State(i,output_off);
			g_usleep (1e3 * globals.Timers.normal_relay_bounce_time_in_milliseconds);
		}

	bus_setpin(out_CLOCK_LINE, 1);
	bus_setpin(out_STROBE_LINE, 0);

	for (i=(num_out_SRs-1); i>=0; --i) {
		switch (i) {
		case 0:
		case 1:
			n=8;
			break;
		case 2:
		case 3:
			n=20;			/* 20 bit shift registers - UCN5812s */
			break;
		}

		for (j=n-1; j>=0; --j) {	/* send MSB first, LSB last */
			bus_setpin(out_CLOCK_LINE, 0);
			data_out = (char) (1 & (globals.Registers.shift_reg_out[i]>>j));
			bus_setpin(out_DATA_LINE, data_out);
			bus_setpin(out_CLOCK_LINE, 1);
		}
	}

	bus_setpin(out_STROBE_LINE, 1);		// latch the data
	bus_setpin(out_STROBE_LINE, 0);		// release latch

	for (i=0; i<8; ++i) {
		/* load the upper-nibble latch */
		bus_writebyte ((uint8_t) (Octal_DACportCS_high), (uint8_t) (globals.Registers.parallel_DAC_reg[i]  >> 8));

		/* write lower byte (with address data), and force transfer of latched nibble */
		bus_writebyte ((uint8_t) (Octal_DACportCS_low + i), (uint8_t) (globals.Registers.parallel_DAC_reg[i] & 255));
	}

	/* keep trigger suppressed for a time (normally 5ms) after a relay update */
	if (	globals.Registers.last_relay_driver_settings[0]!=globals.Registers.shift_reg_out[2]
	                || globals.Registers.last_relay_driver_settings[1]!=globals.Registers.shift_reg_out[3]
	                || globals.Flags.force_output_fully_off==YES) {
		g_usleep (1e3 * globals.Timers.Relay_Switching_Delay_in_Milliseconds);
	}


	/* restore output if required */
	if (globals.Flags.force_output_fully_off==YES)
		for (i=0; i<(globals.Flash.ChanKey_frequency?globals.Flash.channels:1); ++i) {
			Set_Output_State(i,temp_output_state[i]);
			g_usleep (1e3 * globals.Timers.normal_relay_bounce_time_in_milliseconds);
			globals.Flags.force_output_fully_off=NO;
		}

	stop_gate_override ();

	globals.Timers.Relay_Switching_Delay_in_Milliseconds=globals.Timers.normal_relay_bounce_time_in_milliseconds;
	/* restore default delay */

	/* save relay data for comparision next time */
	globals.Registers.last_relay_driver_settings[0]=globals.Registers.shift_reg_out[2];
	globals.Registers.last_relay_driver_settings[1]=globals.Registers.shift_reg_out[3];

	// reset update sensors
	globals.Changes.update_os = 0;
	globals.Changes.update_amp = 0;
	globals.Changes.update_zout = 0;
	globals.Changes.update_load = 0;

	g_static_mutex_unlock (&mutex);
}


int IO_Setup_RS232(int baud, char hardhand, gboolean update_flash)
{
	FILE* configfile = fopen("/tmp/instgettyopts", "w");
	if(configfile) {
		fprintf(configfile, "OPTS=-L %s\n", hardhand ? "-h" : "");
		fprintf(configfile, "BAUD=%d\n", baud);
		fclose(configfile);
		system("systemctl --no-block restart inst-getty@ttyO5.service");
	}

	globals.Flash.baud = baud;
	globals.Flash.hardhand = hardhand;

	if (update_flash) {
		int size = sizeof(globals.Flash.baud) + sizeof(globals.Flash.parity) + sizeof(globals.Flash.stopbits) +
		           sizeof(globals.Flash.databits) + sizeof(globals.Flash.hardhand) + sizeof(globals.Flash.echo);

		int eprom_loc = (char *) &(globals.Flash.baud) - (char *) &(globals.Flash.flash_start);
		writeUserBlock(&globals.Flash, eprom_loc, size);
	}
	return OK;
}

// this is a conversation handler for pam, it basically sends the password when pam asks for it

static int conversation(int num_msg, const struct pam_message **msgs, struct pam_response **resp, void *appdata_ptr)
{

	struct pam_response* responses = calloc(num_msg, sizeof(struct pam_response));
	if (!responses) {
		return PAM_CONV_ERR;
	}

	int i; // not compiling in gnu99 mode?
	for (i = 0; i < num_msg; i++) {
		const struct pam_message *msg = msgs[i];
		struct pam_response* response = &(responses[i]);
		switch (msg->msg_style) {
		case PAM_PROMPT_ECHO_OFF:
			response->resp = strdup((char*) appdata_ptr);
			if (!response->resp) {
				return PAM_CONV_ERR;
			}
			break;

		default:
			return PAM_CONV_ERR;
		}
		response->resp_retcode = 0;
	}

	*resp = responses;

	return PAM_SUCCESS;
}

static gboolean checkpassword(const char* username, char* password)
{
	struct pam_conv pam_conversation = { conversation, password };
	pam_handle_t* pamh;

	if (pam_start("passwd", username, &pam_conversation, &pamh) != PAM_SUCCESS) {
		return FALSE;
	}

	if (pam_authenticate(pamh, 0) != PAM_SUCCESS) {
		return FALSE;
	}

	// we only want to check the password and not actually start a session, so get out of here

	pam_end(pamh, 0);

	return TRUE;
}

int change_password(gchar *old_password, gchar *new_password)
{

	gboolean old_valid = TRUE;

	char* user = "admin";

	// Skip password check if the supplied old_password is NULL. This
	// only happens when resetting the password to the default.
	if (old_password != NULL ) {
		old_valid = checkpassword(user,old_password);
	}

	if (old_valid == TRUE) {
		struct lu_context *ctx;
		struct lu_error *error = NULL;
		struct lu_ent *ent;

		ctx = lu_start(user, lu_user, NULL, NULL, lu_prompt_console_quiet, NULL, &error);

		if (ctx == NULL ) {
			return password_change_error;
		}

		ent = lu_ent_new();

		if (lu_user_lookup_name(ctx, user, ent, &error) == FALSE) {
			return password_change_error; // user doesn't exist
		}

		if (lu_user_setpass(ctx, ent, new_password, FALSE, &error) == FALSE) {
			return password_change_error;
		}

		lu_end(ctx);

		return OK;
	} else {
		return password_change_error;
	}
}


static void set_shiftreg_bits(int shiftreg, int start_at_bit, int numbits, int value)
{
	long used_bits = (1<<numbits) - 1;
	long mask = 0xfffff - (used_bits << start_at_bit);

	long shift_value = (value & used_bits) << start_at_bit;
	long masked_reg = globals.Registers.shift_reg_out[shiftreg] & mask;

//        printf ("shiftreg %d, start_at_bit %d, numbits %d, value %x\n",shiftreg,start_at_bit,numbits,value);
//        printf (" in %lx\n", globals.Registers.shift_reg_out[shiftreg]);
	globals.Registers.shift_reg_out[shiftreg] = masked_reg | shift_value;
//        printf ("out %lx\n", globals.Registers.shift_reg_out[shiftreg]);
}