/* START LIBRARY DESCRIPTION *********************************************
ERRCHK.LIB
	Copyright (c) 2006, Avtech Electrosystems Ltd.

DESCRIPTION:
	Error-checking and reporting functions.

SUPPORT LIB'S:
END DESCRIPTION **********************************************************/


/*** BeginHeader Error_check */

#include <string.h>
#include <glib/gprintf.h>
#include <glib.h>
#include <math.h>
#include "globals.h"
#include "lcd.h"
#include "error_utils.h"
#include "menus.h"
#include "gpib.h"
#include "device-functions.h"

/*** EndHeader */

void set_gpib_error_flags (int error_num);

void set_gpib_error_flags (int error_num)
{

	if (error_num == OK) {
		return;
	}

	/* set the error flags in the GPIB ESR register right away */
	switch (error_num) {
	case AsyncModeDisabled:
	case Unrecognized:
	case SyntaxError:
	case UnknownUnits:
	case InvalidChannel:
	case ThisShouldntHappen:
	case obsolete_feature:
	case zero_equiv_ampl_too_large:
		GPIB_Set_Command_Error();
		break;
	case query_error_interrupted:
	case query_error_unterminated:
		GPIB_Set_Query_Error();
		break;
	case Overload_Detected:
	case Overtemp_Detected:
	case Overvolt_Detected:
	case Device_Specific_Aux_Error_Detected:
	case queue_overflow:
	case CalibrationPercentError:
	case CalibrationZeroError:
	case CalibrationMinMaxError:
	case CalibrationClosenessError:
	case CalibrationRangeError:
	case CalibrationPolarityError:
	case Soft_Limit_Exceeded:
	case SelfCalError:
	case NetworkNotFound:
	case Startup_Not_Finished:
	case GPIB_missing:
		GPIB_Set_Device_Dependent_Error();
		break;
	default:
		GPIB_Set_Execution_Error();
		break;
	}

	return;
}

void queue_error(int error_num);

void queue_error(int error_num)
{
	if (error_num == OK) {
		return;
	}

	set_gpib_error_flags (error_num);
	if (globals.Errors.number_of_errors<error_queue_length) {
		++globals.Errors.number_of_errors;
		globals.Errors.error_queue[globals.Errors.number_of_errors]=error_num;
	} else {
		globals.Errors.error_queue[globals.Errors.number_of_errors]=queue_overflow;
	}

	return;
}



/*----------------------------------------------------------------------------------------------------------*/
void queue_error_and_get_text(gchar** response, int error_num)
{
	g_assert (*response == NULL);

	if (error_num == OK) {
		return;
	}

	queue_error (error_num);
	get_error_text (response, error_num);
	return;
}

/*----------------------------------------------------------------------------------------------------------*/
void queue_error_from_parser(gchar** response, int error_num)
{
	g_assert (*response == NULL);

	if (error_num == OK) {
		return;
	}

	queue_error_and_get_text(response, error_num);
	Show_Main_Menu();
	return;
}


void queue_error_for_gpib_only(int error_num)
{
	if (error_num == OK) {
		return;
	}

	queue_error(error_num);
	return;
}


void queue_error_and_display_on_LCD(int error_num)
{
	if (error_num == OK) {
		return;
	}

	gchar* response = NULL;
	queue_error_and_get_text(&response, error_num);
	LCD_display_extended_message (response, TRUE, TRUE);
	g_free (response);
}

extern void send_message(gchar* message);

void queue_and_broadcast_sensor_alarm(int error_num)
{
	if (error_num == OK) {
		return;
	}

	gchar* response = NULL;
	queue_error_and_get_text(&response, error_num);
	LCD_display_extended_message (response, TRUE, TRUE);

	gchar *broadcast_str = g_strdup_printf ("%s\r\n> ", response);
	send_message(broadcast_str);
	g_free (broadcast_str);

	g_free (response);
}


/*----------------------------------------------------------------------------------------------------------*/
void Error_Remove_From_Queue(void)
{
	int i;
	for (i=1; i<globals.Errors.number_of_errors; ++i) {
		globals.Errors.error_queue[i]=globals.Errors.error_queue[i+1];
	}
	--globals.Errors.number_of_errors; 	/* error reported, so remove it from the error queue */
}


void format_error_text (gchar **response, int error_type, char *in);

void format_error_text (gchar **response, int error_type, char *in)
{
	char* prefix = NULL;

	switch (error_type) {
	case 0:
		prefix = g_strdup("No error");
		break;
	case -100:
		prefix=g_strdup("Command error");
		break;
	case -102:
		prefix=g_strdup("Syntax error");
		break;
	case -114:
		prefix=g_strdup("Command error");
		break;
	case -131:
		prefix=g_strdup("Invalid suffix");
		break;
	case -200:
		prefix=g_strdup("Execution error");
		break;
	case -221:
		prefix=g_strdup("Settings conflict");
		break;
	case -222:
		prefix=g_strdup("Data out of range");
		break;
	case -224:
		prefix=g_strdup("Illegal parameter value");
		break;
	case -240:
		prefix=g_strdup("Hardware error");
		break;
	case -300:
		prefix=g_strdup("Device-specific error");
		break;
	case -340:
		prefix=g_strdup("Calibration failed");
		break;
	case -350:
		prefix=g_strdup("Queue overflow");
		break;
	case -400:
		prefix=g_strdup("Query error");
		break;
	default:
		prefix=g_strdup("System error");
		break;
	}


	*response = g_strdup_printf("%d, \"%s; %s\"", error_type, prefix, in);
	g_free(prefix);
}


void get_error_text(gchar **response, int error_num)
{
	switch (error_num) {

	case OK:
		format_error_text(response,0,"");
		break;

	case Unrecognized:
		format_error_text(response,-102,"Unrecognized command.");
		break;

	case AsyncModeDisabled:
		format_error_text(response,-102,"Async gating not available.");
		break;

	case SyntaxError:
		format_error_text(response,-100,"Recognized command, but improper syntax.");
		break;

	case InvalidChannel:
		format_error_text(response,-114,"Invalid channel suffix.");
		break;

	case OutOfRange:
		format_error_text(response,-222,"Too high or too low.");
		break;

	case Negative_Not_Allowed:
		format_error_text(response,-222,"Negative value not allowed.");
		break;

	case IllegalParameter:
	case amplitude_confined_values:
		format_error_text(response,-224,"Not in list of allowed values.");
		break;

	case UnknownUnits:
		format_error_text(response,-131,"Unrecognized units.");
		break;

	case Route_Range_Error:
		format_error_text(response,-224,"Ranges are not supported.");
		break;

	case AB_Mode_Error:
		format_error_text(response,-221,"This PW mode is not valid for the current trigger mode.");
		break;

	case Valid_For_RS232_TELNET_Only:
		format_error_text(response,-221,"Command valid in RS232 or TELNET modes only.");
		break;

	case PW_Exceeds_Period:
		format_error_text(response,-221,"PW can not exceed period.");
		break;

	case Delay_Exceeds_95Period:
		format_error_text(response,-221,"Delay can not exceed 95% of period.");
		break;

	case NeedNonZeroAmpl:
		format_error_text(response,-222,"Amplitude must be non-zero.");
		break;

	case freq_lower_limit:
		format_error_text(response,-222,"PRF too low.");
		break;

	case freq_upper_limit:
		format_error_text(response,-222,"PRF too high.");
		break;

	case pw_lower_limit:
		format_error_text(response,-222,"PW too low.");
		break;

	case pw_upper_limit:
		format_error_text(response,-222,"PW too high.");
		break;

	case max_rise_time_error:
		format_error_text(response,-222,"Rise time too high.");
		break;

	case min_rise_time_error:
		format_error_text(response,-222,"Rise time too low.");
		break;

	case max_slew_error:
		format_error_text(response,-222,"Slew rate too high.");
		break;

	case min_slew_error:
		format_error_text(response,-222,"Slew rate too low.");
		break;

	case max_load_type_error:
		format_error_text(response,-222,"Resistance too high.");
		break;

	case min_load_type_error:
		format_error_text(response,-222,"Duty cycle too high or load resistance too low.");
		break;

	case max_soft_current_limit_error:
		format_error_text(response,-222,"Current limit too high.");
		break;

	case min_soft_current_limit_error:
		format_error_text(response,-222,"Current limit too low.");
		break;

	case duty_cycle_upper_limit:
		format_error_text(response,-222,"Duty cycle too high.");
		break;

	case Average_Amplitude_Too_High:
		format_error_text(response,-222,"Average amplitude too high.");
		break;

	case delay_lower_limit:
		format_error_text(response,-222,"Delay too low.");
		break;

	case delay_upper_limit:
		format_error_text(response,-222,"Delay too high.");
		break;

	case amplitude_lower_limit:
		format_error_text(response,-222,"Amplitude too low.");
		break;

	case amplitude_upper_limit:
		format_error_text(response,-222,"Amplitude too high.");
		break;

	case peak_power_limit:
		format_error_text(response,-222,"Peak power too high.");
		break;

	case average_power_limit:
		format_error_text(response,-222,"Average power too high.");
		break;

	case offset_lower_limit:
		format_error_text(response,-222,"Offset too low.");
		break;

	case offset_upper_limit:
		format_error_text(response,-222,"Offset too high.");
		break;

	case ampl_plus_os_lower_limit:
		format_error_text(response,-222,"AMPL+OS too low.");
		break;

	case ampl_plus_os_upper_limit:
		format_error_text(response,-222,"AMPL+OS too high.");
		break;

	case DutyTriggerError:
		format_error_text(response,-221,"Duty cycle can not be set. Set PW instead.");
		break;

	case PW_Exceeds_Double_Separation:
		format_error_text(response,-221,"PW can not exceed double pulse separation.");
		break;

	case Double_Separation_Too_Large:
		format_error_text(response,-221,"Double pulse separation is too high. It must be < 95% of the period.");
		break;

	case queue_overflow:
		format_error_text(response,-350,"Clear with *cls or syst:err.");
		break;

	case query_error_interrupted:
		format_error_text(response,-400,"Data lost in output buffer.");
		break;

	case query_error_unterminated:
		format_error_text(response,-400,"No data to send.");
		break;

	case Overload_Detected:
		format_error_text(response,-300,"Power supply overload detected. The output has been turned off.");
		break;

	case Overtemp_Detected:
		format_error_text(response,-300,"Overheating problem. The output has been turned off.");
		break;

	case Overvolt_Detected:
		format_error_text(response,-300,"Over-voltage or over-current! The output has been turned off.");
		break;

	case Soft_Limit_Exceeded:
		format_error_text(response,-300,"Monitor current limit exceeded! The output has been turned off.");
		break;

	case Device_Specific_Aux_Error_Detected:
		format_error_text(response,-300,globals.Flash.aux_error_message);
		break;

	case CalibrationPercentError:
		format_error_text(response,-340,"Percent change is too large.");
		break;

	case CalibrationPolarityError:
		format_error_text(response,-340,"Old and new polarities disagree.");
		break;

	case CalibrationZeroError:
		format_error_text(response,-340,"Zero point can not be changed.");
		break;

	case CalibrationMinMaxError:
		format_error_text(response,-340,"Would prevent operation at the minimum or maximum settings.");
		break;

	case CalibrationClosenessError:
		format_error_text(response,-340,"Too few calibration points, or they are too closely spaced.");
		break;

	case CalibrationRangeError:
		format_error_text(response,-340,"Top or bottom of range. Can not be deleted.");
		break;

	case SelfCalError:
		format_error_text(response,-340,"Calibration problem.");
		break;

	case HardwareError:
		format_error_text(response,-240,"Not possible with the current calibration settings.");
		break;

        case GPIB_missing:
                format_error_text(response,-240,"GPIB chip not detected. Defective hardware?");
                break;

	case CalibrationTimingProblem:
		format_error_text(response,-221,"Set timing to allow operation at minimum and maximum amplitudes.");
		break;

	case Coupled_OS_Ampl_Error:
		format_error_text(response,-221,"If ampl > 0, ampl+offset must be > 0, if ampl < 0, ampl+offset must be < 0.");
		break;

	case PRF_limited_by_offset:
		format_error_text(response,-221,"Offset too high for this PRF.");
		break;

	case PW_Distort_Error:
		format_error_text(response,-240,"Not possible with the current PW distortion settings.");
		break;

        case DelayRangeError:
                format_error_text(response,-240,"Gap in possible delay values.");
                break;

        case ExternalModeDelayError:
                format_error_text(response,-240,"TRIG-OUT delay not monotonic, boost fixed delay or sync delay.");
                break;

	case burst_duty_error:
		format_error_text(response,-222,"Duty cycle inside burst is too high.");
		break;

	case min_burst_period_error:
		format_error_text(response,-222,"Time between consecutive rising edges inside burst is too low.");
		break;

	case max_burst_count_error:
		format_error_text(response,-222,"Too many pulses per burst.");
		break;

	case min_burst_gap_error:
		format_error_text(response,-222,"Pulse separation too low.");
		break;

	case max_burst_gap_error:
		format_error_text(response,-222,"Pulse separation too high.");
		break;

	case Burst_Exceeds_Period:
		format_error_text(response,-222,"Burst width can not exceed period.");
		break;

	case password_change_error:
		format_error_text(response,-222,"Incorrect old password, or new password is too long or short. ");
		break;

	case NetworkNotFound:
		format_error_text(response,-240,"Network not found.");
		break;

	case ThisShouldntHappen:
		format_error_text(response,-200,"Invalid execution path. Programming error.");
		break;

	case Startup_Not_Finished:
		format_error_text(response,-300,"Not ready for commands yet. Still booting up.");
		break;

	case obsolete_feature:
                format_error_text(response,-200,"Obsolete feature enabled. Re-program.");
                break;

	case zero_equiv_ampl_too_large:
                format_error_text(response,-200,"Ampl zero equivalent is too high. Reprogram it.");
                break;

	default:
		format_error_text(response,-200,"Specific problem unknown.");
	}

}


int Error_check(ChannelStruct ChannelStateToTest[max_channels])
{
	static GStaticMutex mutex = G_STATIC_MUTEX_INIT;
	g_static_mutex_lock (&mutex);

	float max_duty_high_ampl,max_duty_low_ampl,max_duty_this_ampl;
	float ampl_fixed_max_duty;
	float temp;
	float duty_scale;
	float duty_cycle;
	int report_error = OK;
	gboolean early_quit = FALSE;

	int i;
	int num_of_chan;

	/* todo: check avrq settings */

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


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

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

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


	// check for non-channel-specific obsolete features
	if ( globals.Flash.enable_avrq_extra_ampls ) {
		report_error = obsolete_feature;
	}


	for (i=0; (i<num_of_chan) && !early_quit; ++i) {
		/* Must be changing a setting */
		globals.Timers.last_activity_at[i] = sec_timer ();

		/* ignore errors if it is in programming mode */
		if (globals.Flash.fully_programmed != All_Programmed) {
			early_quit = TRUE;
			break;
		}

		if (globals.Flags.do_check_settings==NO) {
			early_quit = TRUE;
			break;
		}

		/* check for settings that would cause divide-by-zero errors in the error-checking routine */
		if (ChannelStateToTest[i].frequency<smallest_allowed_number) {
			early_quit = TRUE;
			report_error = freq_lower_limit;
			break;
		}

		if (ChannelStateToTest[i].pw<smallest_allowed_number
		                && globals.Flash.min_pw[i]>0.0) {
			early_quit = TRUE;
			report_error = pw_lower_limit;
			break;
		}


		// check for channel-specific obsolete configurations - but allow further checks
		if ( globals.Flash.ampl_min_max_only[i] ) {
			report_error = obsolete_feature;
		}

		if (fabs(globals.Flash.ampl_zero_equiv[i] > 200.0)) {
			report_error = zero_equiv_ampl_too_large;
		}

		/* calculate maximum duty cycle based on amplitude and load, for later use */

		max_duty_high_ampl=globals.Flash.max_duty_high[i];
		max_duty_low_ampl=globals.Flash.max_duty_low[i];

		/* Choose highest of 4 duty cycles that meet max ampl limits */
		if (globals.Flash.duty_ampl[i]>0.0 && (fabs(ChannelStateToTest[i].amplitude)<= globals.Flash.duty_ampl[i])) {
			max_duty_this_ampl=max_duty_low_ampl;
		} else {
			max_duty_this_ampl=max_duty_high_ampl;
		}

		if (globals.Flash.duty_ampl_mid1[i]>0.0 &&
		                (fabs(ChannelStateToTest[i].amplitude)<= globals.Flash.duty_ampl_mid1[i]) &&
		                (globals.Flash.max_duty_mid1[i] > max_duty_this_ampl)) {
			max_duty_this_ampl = globals.Flash.max_duty_mid1[i];
		}

		if (globals.Flash.duty_ampl_mid2[i]>0.0 &&
		                (fabs(ChannelStateToTest[i].amplitude)<= globals.Flash.duty_ampl_mid2[i]) &&
		                (globals.Flash.max_duty_mid2[i] > max_duty_this_ampl)) {
			max_duty_this_ampl = globals.Flash.max_duty_mid2[i];
		}

		ampl_fixed_max_duty = max_duty_this_ampl;
		if (globals.Flash.switchable_load[i]) {
			ampl_fixed_max_duty = max_duty_this_ampl * (ChannelStateToTest[i].load_type / globals.Flash.low_load_type[i]);
			temp = globals.Flash.max_high_rl_duty[i];
			if (ampl_fixed_max_duty > temp) {
				ampl_fixed_max_duty = temp;
			}
		}


		/* take burst mode and double-pulse mode into account when setting the maximum duty cycle */
		duty_scale = 100.0; 		/* convert from percent */

		if (ChannelStateToTest[i].double_pulse==double_on) {
			duty_scale = 200.0;
		}
		if (	(ChannelStateToTest[i].burst_count>1)
		                && (globals.Flash.max_burst_count[i]>1)
		                && !globals.Flash.burst_func[i]) {
			duty_scale *= (float) ChannelStateToTest[i].burst_count;
		}


		if (ChannelStateToTest[i].trigger_source==source_internal) {

			/* --- check minimum frequency --- */
			globals.Constraints.err_min_freq[i]=globals.Flash.min_freq[i];

			if (ChannelStateToTest[i].frequency<(0.999*globals.Constraints.err_min_freq[i])) {
				report_error=freq_lower_limit;
			}

			if ((ChannelStateToTest[i].polarity!=pol_norm) && (globals.Flash.min_pw[i]>0.0)) {
				temp=(1/ChannelStateToTest[i].pw)*(	1.0- ampl_fixed_max_duty/duty_scale );
				if (temp>globals.Constraints.err_min_freq[i]) {
					globals.Constraints.err_min_freq[i]=temp;
					if (ChannelStateToTest[i].frequency<(0.999*globals.Constraints.err_min_freq[i])) {
						report_error=duty_cycle_upper_limit;
					}
				}
			}


			/* ------------------------------- */

			/* --- check maximum frequency --- */
			globals.Constraints.err_max_freq[i]=globals.Flash.max_freq[i];

			if (ChannelStateToTest[i].frequency>(1.001*globals.Constraints.err_max_freq[i])) {
				report_error=freq_upper_limit;
			}

			if (	(globals.Flash.max_freq_for_high_ot[i] > 0.0) &&
				(fabs(ChannelStateToTest[i].offset) > globals.Flash.high_ot[i])) {

				temp = globals.Flash.max_freq_for_high_ot[i];
				if (temp<globals.Constraints.err_max_freq[i]) {
                                        globals.Constraints.err_max_freq[i]=temp;
                                        if (ChannelStateToTest[i].frequency>(1.001*globals.Constraints.err_max_freq[i])) {
                                                report_error=PRF_limited_by_offset;
                                        }
                                }

			}

			if (fabs(ChannelStateToTest[i].delay)>1.0e-15) {
				temp=0.95/fabs(ChannelStateToTest[i].delay);
				if (temp<globals.Constraints.err_max_freq[i]) {
					globals.Constraints.err_max_freq[i]=temp;
					if (ChannelStateToTest[i].frequency>(1.001*globals.Constraints.err_max_freq[i])) {
						report_error=Delay_Exceeds_95Period;
					}
				}
			}

			if (globals.Flash.min_pw[i] > 0.0) {
				temp=1/ChannelStateToTest[i].pw;
				if (temp<globals.Constraints.err_max_freq[i]) {
					globals.Constraints.err_max_freq[i]=temp;
					if (ChannelStateToTest[i].frequency>(1.001*globals.Constraints.err_max_freq[i])) {
						report_error=PW_Exceeds_Period;
					}
				}

				if (		(ChannelStateToTest[i].burst_count>1)
				                && (globals.Flash.max_burst_count[i]>1)
				                && !globals.Flash.burst_func[i]) {
					temp=1/(ChannelStateToTest[i].burst_count * (ChannelStateToTest[i].pw+ChannelStateToTest[i].burst_time));
					if (temp<globals.Constraints.err_max_freq[i]) {
						globals.Constraints.err_max_freq[i]=temp;
						if (ChannelStateToTest[i].frequency>(1.001*globals.Constraints.err_max_freq[i])) {
							report_error=Burst_Exceeds_Period;
						}
					}
				}

				if (ChannelStateToTest[i].polarity==pol_norm) {
					temp=(1/ChannelStateToTest[i].pw)*(ampl_fixed_max_duty/duty_scale);
					if (temp<globals.Constraints.err_max_freq[i]) {
						globals.Constraints.err_max_freq[i]=temp;
						if (ChannelStateToTest[i].frequency>(1.001*globals.Constraints.err_max_freq[i])) {
							report_error=duty_cycle_upper_limit;
						}
					}
				}

				if (ChannelStateToTest[i].double_pulse==double_on) {
					temp=0.95/(ChannelStateToTest[i].pw+fabs(ChannelStateToTest[i].delay));
					if (temp<globals.Constraints.err_max_freq[i]) {
						globals.Constraints.err_max_freq[i]=temp;
						if (ChannelStateToTest[i].frequency>(1.001*globals.Constraints.err_max_freq[i])) {
							report_error=Double_Separation_Too_Large;
						}
					}
				}

				if ((globals.Flash.max_avg_ampl[i]) > 0.0 && (fabs(ChannelStateToTest[i].amplitude)) > 0.0) {
					temp=100.0 * globals.Flash.max_avg_ampl[i] /
					     (fabs(ChannelStateToTest[i].amplitude) * ChannelStateToTest[i].pw * duty_scale);
					if (temp<globals.Constraints.err_max_freq[i]) {
						globals.Constraints.err_max_freq[i]=temp;
						if (ChannelStateToTest[i].frequency>(1.001*globals.Constraints.err_max_freq[i])) {
							report_error=Average_Amplitude_Too_High;
						}
					}
				}

				if (globals.Flash.max_avg_power[i] > 0.0) {
					temp = (globals.Flash.max_avg_power[i] * ChannelStateToTest[i].load_type) / (ChannelStateToTest[i].amplitude * ChannelStateToTest[i].amplitude * ChannelStateToTest[i].pw);
					if (temp < globals.Constraints.err_max_freq[i]) {
						globals.Constraints.err_max_freq[i] = temp;
						if (ChannelStateToTest[i].frequency>(1.001*globals.Constraints.err_max_freq[i])) {
							report_error=average_power_limit;
						}
					}
				}

			}

			/* ------------------------------- */
		}

		/* --- check minimum pulse width --- */
		globals.Constraints.err_min_pw[i]=globals.Flash.min_pw[i];

		if (   (globals.Constraints.err_min_pw[i]>=0.0 && ChannelStateToTest[i].pw<(0.999*globals.Constraints.err_min_pw[i]))
		                || (globals.Constraints.err_min_pw[i]<0.0 && ChannelStateToTest[i].pw<(1.001*globals.Constraints.err_min_pw[i])) ) {
			report_error=pw_lower_limit;
		}

		/* negative-pw units are special, ignore PRF / duty cycle / PW conflicts */
		if (globals.Constraints.err_min_pw[i]>=0.0) {
			if (ChannelStateToTest[i].trigger_source==source_internal) {
				if (ChannelStateToTest[i].polarity!=pol_norm) {
					temp=(1/ChannelStateToTest[i].frequency)*( 1.0 - ampl_fixed_max_duty/duty_scale );
					if (temp>globals.Constraints.err_min_pw[i]) {
						globals.Constraints.err_min_pw[i]=temp;
						if (ChannelStateToTest[i].pw<(0.999*globals.Constraints.err_min_pw[i])) {
							report_error=duty_cycle_upper_limit;
						}
					}
				}
			}
		}

		/* ------------------------------- */


		/* --- check maximum pw ---------- */
		globals.Constraints.err_max_pw[i]=globals.Flash.max_pw[i];

		/* polarity overrides */
		if ((ChannelStateToTest[i].amplitude >= 0.0) && (globals.Flash.max_pw_pol[i][0] > 0.0) && (globals.Constraints.err_max_pw[i] > globals.Flash.max_pw_pol[i][0])) {
			globals.Constraints.err_max_pw[i] = globals.Flash.max_pw_pol[i][0];
		} else if ((ChannelStateToTest[i].amplitude <0.0) && (globals.Flash.max_pw_pol[i][1] > 0.0) && (globals.Constraints.err_max_pw[i] > globals.Flash.max_pw_pol[i][1])) {
			globals.Constraints.err_max_pw[i] = globals.Flash.max_pw_pol[i][1];
		}

		if (ChannelStateToTest[i].pw>(1.001*globals.Constraints.err_max_pw[i])) {
			report_error=pw_upper_limit;
		}

		if (ChannelStateToTest[i].double_pulse==double_on) {
			temp=ChannelStateToTest[i].delay;
			if (temp<globals.Constraints.err_max_pw[i]) {
				globals.Constraints.err_max_pw[i]=temp;
				if (ChannelStateToTest[i].pw>(1.001*globals.Constraints.err_max_pw[i])) {
					report_error=PW_Exceeds_Double_Separation;
				}
			}
		}

		if (ChannelStateToTest[i].trigger_source==source_internal) {
			temp=1/ChannelStateToTest[i].frequency;
			if (temp<globals.Constraints.err_max_pw[i]) {
				globals.Constraints.err_max_pw[i]=temp;
				if (ChannelStateToTest[i].pw>(1.001*globals.Constraints.err_max_pw[i])) {
					report_error=PW_Exceeds_Period;
				}
			}

			if (		(ChannelStateToTest[i].burst_count>1)
			                && (globals.Flash.max_burst_count[i]>1)
			                && !globals.Flash.burst_func[i]) {
				temp=(1/ChannelStateToTest[i].frequency)/ChannelStateToTest[i].burst_count - ChannelStateToTest[i].burst_time;
				if (temp<globals.Constraints.err_max_pw[i]) {
					globals.Constraints.err_max_pw[i]=temp;
					if (ChannelStateToTest[i].pw>(1.001*globals.Constraints.err_max_pw[i])) {
						report_error=Burst_Exceeds_Period;
					}
				}
			}

			if (ChannelStateToTest[i].polarity==pol_norm) {
				temp=(1/ChannelStateToTest[i].frequency)*(ampl_fixed_max_duty/duty_scale);
				if (temp<globals.Constraints.err_max_pw[i]) {
					globals.Constraints.err_max_pw[i]=temp;
					if (ChannelStateToTest[i].pw>(1.001*globals.Constraints.err_max_pw[i])) {
						report_error=duty_cycle_upper_limit;
					}
				}
			}

			if (ChannelStateToTest[i].double_pulse==double_on) {
				temp=(0.95/ChannelStateToTest[i].frequency)-ChannelStateToTest[i].delay;
				if (temp<globals.Constraints.err_max_pw[i]) {
					globals.Constraints.err_max_pw[i]=temp;
					if (ChannelStateToTest[i].pw>(1.001*globals.Constraints.err_max_pw[i])) {
						report_error=Double_Separation_Too_Large;
					}
				}
			}

			if ((globals.Flash.max_avg_ampl[i]) > 0.0 && (fabs(ChannelStateToTest[i].amplitude)) > 0.0) {
				temp=100.0 * globals.Flash.max_avg_ampl[i] /
				     (fabs(ChannelStateToTest[i].amplitude) * ChannelStateToTest[i].frequency * duty_scale);
				if (temp<globals.Constraints.err_max_pw[i]) {
					globals.Constraints.err_max_pw[i]=temp;
					if (ChannelStateToTest[i].pw>(1.001*globals.Constraints.err_max_pw[i])) {
						report_error=Average_Amplitude_Too_High;
					}
				}
			}

			if (globals.Flash.max_avg_power[i] > 0.0) {
				temp = globals.Flash.max_avg_power[i] * ChannelStateToTest[i].load_type / (ChannelStateToTest[i].amplitude * ChannelStateToTest[i].amplitude * ChannelStateToTest[i].frequency);
				if (temp < globals.Constraints.err_max_pw[i]) {
					globals.Constraints.err_max_pw[i] = temp;
					if (ChannelStateToTest[i].pw>(1.001*globals.Constraints.err_max_pw[i])) {
						report_error=average_power_limit;
					}
				}
			}

		}
		/* ------------------------------- */

		/* --- check minimum delay ------- */
		if (globals.Flash.min_delay[i] != 0.0) {
			/* use programmed limit, if non-zero */
			globals.Constraints.err_min_delay[i]=globals.Flash.min_delay[i];
		} else {
			/* otherwise, assume delay has equal +/- range */
			globals.Constraints.err_min_delay[i]=-globals.Flash.max_delay[i];

			/* for dual-delay units, positive delay only for second channel */
			if ((globals.Flash.ChanKey_delay?globals.Flash.channels:1)>1) {
				if (i>0) {
					globals.Constraints.err_min_delay[i]=0.0;
				}
			}
		}

		if ( (globals.Constraints.err_min_delay[i]>=0.0 && ChannelStateToTest[i].delay<(0.999*globals.Constraints.err_min_delay[i]))
		                || (globals.Constraints.err_min_delay[i]<0.0 && ChannelStateToTest[i].delay<(1.001*globals.Constraints.err_min_delay[i])) ) {
			report_error=delay_lower_limit;
		}

		if (ChannelStateToTest[i].trigger_source==source_internal) {
			temp=-0.95/ChannelStateToTest[i].frequency;
			if (temp>globals.Constraints.err_min_delay[i]) {
				globals.Constraints.err_min_delay[i]=temp;
				if ( (globals.Constraints.err_min_delay[i]>=0.0 && ChannelStateToTest[i].delay<(0.999*globals.Constraints.err_min_delay[i]))
				                || (globals.Constraints.err_min_delay[i]<0.0 && ChannelStateToTest[i].delay<(1.001*globals.Constraints.err_min_delay[i])) ) {
					report_error=Delay_Exceeds_95Period;
				}
			}
		}

		if (ChannelStateToTest[i].double_pulse==double_on) {
			temp=ChannelStateToTest[i].pw;
			if (temp>globals.Constraints.err_min_delay[i]) {
				globals.Constraints.err_min_delay[i]=temp;
				if ( (globals.Constraints.err_min_delay[i]>=0.0 && ChannelStateToTest[i].delay<(0.999*globals.Constraints.err_min_delay[i]))
				                || (globals.Constraints.err_min_delay[i]<0.0 && ChannelStateToTest[i].delay<(1.001*globals.Constraints.err_min_delay[i])) ) {
					report_error=PW_Exceeds_Double_Separation;
				}
			}
		}
		/*	------------------------------- */

		/* --- check maximum delay ------- */
		globals.Constraints.err_max_delay[i]=globals.Flash.max_delay[i];

		if ( (globals.Constraints.err_max_delay[i]>=0.0 && ChannelStateToTest[i].delay>(1.001*globals.Constraints.err_max_delay[i]))
		                || (globals.Constraints.err_max_delay[i]<0.0 && ChannelStateToTest[i].delay>(0.999*globals.Constraints.err_max_delay[i])) ) {
			report_error=delay_upper_limit;
		}

		if (ChannelStateToTest[i].trigger_source==source_internal) {
			temp=0.95/ChannelStateToTest[i].frequency;
			if (temp<globals.Constraints.err_max_delay[i]) {
				globals.Constraints.err_max_delay[i]=temp;
				if ( (globals.Constraints.err_max_delay[i]>=0.0 && ChannelStateToTest[i].delay>(1.001*globals.Constraints.err_max_delay[i]))
				                || (globals.Constraints.err_max_delay[i]<0.0 && ChannelStateToTest[i].delay>(0.999*globals.Constraints.err_max_delay[i])) ) {
					report_error=Delay_Exceeds_95Period;
				}
			}

			if (ChannelStateToTest[i].double_pulse==double_on) {
				temp=(0.95/ChannelStateToTest[i].frequency)-ChannelStateToTest[i].pw;
				if (temp<globals.Constraints.err_max_delay[i]) {
					globals.Constraints.err_max_delay[i]=temp;
					if ( (globals.Constraints.err_max_delay[i]>=0.0 && ChannelStateToTest[i].delay>(1.001*globals.Constraints.err_max_delay[i]))
					                || (globals.Constraints.err_max_delay[i]<0.0 && ChannelStateToTest[i].delay>(0.999*globals.Constraints.err_max_delay[i])) ) {
						report_error=Double_Separation_Too_Large;
					}
				}
			}
		}
		/* ------------------------------- */

		/* disable amplitude checks if amplitude calibration is in progress */
		if (!globals.Flags.extended_ampl_min_max) {

			/* --- check fixed amplitudes ---- */

			float min_fixed_ampl, max_fixed_ampl;
			min_fixed_ampl = max_fixed_ampl = 0.0;

			gboolean uses_fixed_ampl;
			uses_fixed_ampl = (number_of_fixed_ampl_points(i)>0);

			if (uses_fixed_ampl) {
				get_min_max_fixed_ampls (i, &min_fixed_ampl, &max_fixed_ampl);

				if (!fixed_ampl_ok (i, ChannelStateToTest[i].amplitude)) {
					report_error=amplitude_confined_values;
				}
			}
			/* ------------------------------- */

			/* --- check minimum amplitude --- */

			if (uses_fixed_ampl && (min_fixed_ampl > globals.Flash.min_ampl[i])) {
				globals.Constraints.err_min_ampl[i]=min_fixed_ampl;
			} else {
				globals.Constraints.err_min_ampl[i]=globals.Flash.min_ampl[i];
			}

			if ( (globals.Constraints.err_min_ampl[i]>=0.0 && ChannelStateToTest[i].amplitude<(0.999*globals.Constraints.err_min_ampl[i]))
			                || (globals.Constraints.err_min_ampl[i]<0.0 && ChannelStateToTest[i].amplitude<(1.001*globals.Constraints.err_min_ampl[i])) ) {
				report_error=amplitude_lower_limit;
			}

			temp=globals.Flash.min_vout[i]-ChannelStateToTest[i].offset;
			if (temp>globals.Constraints.err_min_ampl[i]) {
				globals.Constraints.err_min_ampl[i]=temp;
				if ( (globals.Constraints.err_min_ampl[i]>=0.0 && ChannelStateToTest[i].amplitude<(0.999*globals.Constraints.err_min_ampl[i]))
				                || (globals.Constraints.err_min_ampl[i]<0.0 && ChannelStateToTest[i].amplitude<(1.001*globals.Constraints.err_min_ampl[i])) ) {
					report_error=ampl_plus_os_lower_limit;
				}
			}

			/* ------------------------------- */



			/* --- check maximum amplitude --- */
                        if (uses_fixed_ampl && (max_fixed_ampl < globals.Flash.max_ampl[i])) {
                                globals.Constraints.err_max_ampl[i]=max_fixed_ampl;
			} else {
                                globals.Constraints.err_max_ampl[i]=globals.Flash.max_ampl[i];
			}

			if ( (globals.Constraints.err_max_ampl[i]>=0.0 && ChannelStateToTest[i].amplitude>(1.001*globals.Constraints.err_max_ampl[i]))
			                || (globals.Constraints.err_max_ampl[i]<0.0 && ChannelStateToTest[i].amplitude>(0.999*globals.Constraints.err_max_ampl[i])) ) {
				report_error=amplitude_upper_limit;
			}

			temp = sqrt(globals.Flash.max_peak_power[i] * ChannelStateToTest[i].load_type);
			if ((temp > 0.0) && (temp < globals.Constraints.err_max_ampl[i])) {
				globals.Constraints.err_max_ampl[i] = temp;
				if (-temp > globals.Constraints.err_min_ampl[i]) {
					globals.Constraints.err_min_ampl[i] = -temp;
				}

				if ((1.001*globals.Constraints.err_max_ampl[i]) < fabs(ChannelStateToTest[i].amplitude)) {
					report_error=peak_power_limit;
				}
			}

			if (ChannelStateToTest[i].trigger_source==source_internal) {
				temp = sqrt(globals.Flash.max_avg_power[i] * ChannelStateToTest[i].load_type / (ChannelStateToTest[i].pw  * ChannelStateToTest[i].frequency));
				if ((temp > 0.0) && (temp < globals.Constraints.err_max_ampl[i])) {
					globals.Constraints.err_max_ampl[i] = temp;
					if (-temp > globals.Constraints.err_min_ampl[i]) {
						globals.Constraints.err_min_ampl[i] = -temp;
					}

					if ((1.001*globals.Constraints.err_max_ampl[i]) < fabs(ChannelStateToTest[i].amplitude)) {
						report_error=average_power_limit;
					}
				}
			}

			temp=globals.Flash.max_vout[i]-ChannelStateToTest[i].offset;
			if (temp<globals.Constraints.err_max_ampl[i]) {
				globals.Constraints.err_max_ampl[i]=temp;
				if ( (globals.Constraints.err_max_ampl[i]>=0.0 && ChannelStateToTest[i].amplitude>(1.001*globals.Constraints.err_max_ampl[i]))
				                || (globals.Constraints.err_max_ampl[i]<0.0 && ChannelStateToTest[i].amplitude>(0.999*globals.Constraints.err_max_ampl[i])) ) {
					report_error=ampl_plus_os_upper_limit;
				}
			}

			if (globals.Flash.min_pw[i] > 0.0) {
				/* Check average amplitude */
				if ((globals.Flash.max_avg_ampl[i]) > 0.0 && (fabs(ChannelStateToTest[i].amplitude)) > 0.0) {
					temp=100.0 * globals.Flash.max_avg_ampl[i] / (ChannelStateToTest[i].frequency * ChannelStateToTest[i].pw * duty_scale);
					if (temp<globals.Constraints.err_max_ampl[i]) {
						globals.Constraints.err_max_ampl[i]=temp;
						if ( (globals.Constraints.err_max_ampl[i]>=0.0 && ChannelStateToTest[i].amplitude>(1.001*globals.Constraints.err_max_ampl[i]))
						                || (globals.Constraints.err_max_ampl[i]<0.0 && ChannelStateToTest[i].amplitude>(0.999*globals.Constraints.err_max_ampl[i])) ) {
							report_error=Average_Amplitude_Too_High;
						}
					}
				}

				duty_cycle=ChannelStateToTest[i].pw*ChannelStateToTest[i].frequency*100;					/* calculate duty cycle */
				if (ChannelStateToTest[i].polarity!=pol_norm) {
					duty_cycle=100-duty_cycle;    /* account for inversion */
				}

				if (duty_cycle>(100.0*ampl_fixed_max_duty/duty_scale))
					/* set at crossover voltage */
				{
					temp=globals.Flash.duty_ampl[i];

					if (temp<globals.Constraints.err_max_ampl[i]) {
						globals.Constraints.err_max_ampl[i]=temp;
						if (	(		(globals.Constraints.err_max_ampl[i]>=0.0 && ChannelStateToTest[i].amplitude>(1.001*globals.Constraints.err_max_ampl[i]))
						                        || (globals.Constraints.err_max_ampl[i]<0.0 && ChannelStateToTest[i].amplitude>(0.999*globals.Constraints.err_max_ampl[i]))
						     )
						                && (duty_cycle > 1.001*(100.0*max_duty_high_ampl/duty_scale))
						   ) {
							report_error=duty_cycle_upper_limit;
						}
					}
				}

			}

			/* ------------------------------- */


			if ((globals.Flash.voltage_offset_enabled[i]) || (globals.Flash.current_offset_enabled[i])) {
				/* --- check minimum offset ------ */
				globals.Constraints.err_min_offset[i]=globals.Flash.min_offset[i];

				if ( (globals.Constraints.err_min_offset[i]>=0.0 && ChannelStateToTest[i].offset<(0.999*globals.Constraints.err_min_offset[i]))
				                || (globals.Constraints.err_min_offset[i]<0.0 && ChannelStateToTest[i].offset<(1.001*globals.Constraints.err_min_offset[i])) ) {
					report_error=offset_lower_limit;
				}

				temp=globals.Flash.min_vout[i]-ChannelStateToTest[i].amplitude;
				if (temp>globals.Constraints.err_min_offset[i]) {
					globals.Constraints.err_min_offset[i]=temp;
					if ( (globals.Constraints.err_min_offset[i]>=0.0 && ChannelStateToTest[i].offset<(0.999*globals.Constraints.err_min_offset[i]))
					                || (globals.Constraints.err_min_offset[i]<0.0 && ChannelStateToTest[i].offset<(1.001*globals.Constraints.err_min_offset[i])) ) {
						report_error=ampl_plus_os_lower_limit;
					}
				}

				/* 1011 models: high level of PG-P output must remain positive */
				if (globals.Flash.ampl_coupled_to_os[i]) {
					if (ChannelStateToTest[i].amplitude>globals.Flash.ampl_zero_equiv[i]) {
						temp=-ChannelStateToTest[i].amplitude;
						if (temp>globals.Constraints.err_min_offset[i]) {
							globals.Constraints.err_min_offset[i]=temp;
							if (ChannelStateToTest[i].offset<=globals.Constraints.err_min_offset[i]) {
								report_error=Coupled_OS_Ampl_Error;
							}
						}
					}
				}

				if (    (globals.Flash.max_freq_for_high_ot[i] > 0.0) &&
					(ChannelStateToTest[i].frequency > globals.Flash.max_freq_for_high_ot[i])) {

					temp = -globals.Flash.high_ot[i];
						if (temp>globals.Constraints.err_min_offset[i]) {
						globals.Constraints.err_min_offset[i]=temp;
						if (ChannelStateToTest[i].offset<=globals.Constraints.err_min_offset[i]) {
							report_error=PRF_limited_by_offset;
						}
					}
				}

				/* ------------------------------- */


				/* --- check maximum offset ------ */
				globals.Constraints.err_max_offset[i]=globals.Flash.max_offset[i];

				if ( (globals.Constraints.err_max_offset[i]>=0.0 && ChannelStateToTest[i].offset>(1.001*globals.Constraints.err_max_offset[i]))
				                || (globals.Constraints.err_max_offset[i]<0.0 && ChannelStateToTest[i].offset>(0.999*globals.Constraints.err_max_offset[i])) ) {
					report_error=offset_upper_limit;
				}

				temp=globals.Flash.max_vout[i]-ChannelStateToTest[i].amplitude;
				if (temp<globals.Constraints.err_max_offset[i]) {
					globals.Constraints.err_max_offset[i]=temp;
					if ( (globals.Constraints.err_max_offset[i]>=0.0 && ChannelStateToTest[i].offset>(1.001*globals.Constraints.err_max_offset[i]))
					                || (globals.Constraints.err_max_offset[i]<0.0 && ChannelStateToTest[i].offset>(0.999*globals.Constraints.err_max_offset[i])) ) {
						report_error=ampl_plus_os_upper_limit;
					}
				}

				/* 1011 models: low level of PG-N output must remain negative */
				if (globals.Flash.ampl_coupled_to_os[i]) {
					if (ChannelStateToTest[i].amplitude<-globals.Flash.ampl_zero_equiv[i]) {
						temp=-ChannelStateToTest[i].amplitude;
						if (temp<globals.Constraints.err_max_offset[i]) {
							globals.Constraints.err_max_offset[i]=temp;
							if (ChannelStateToTest[i].offset>=globals.Constraints.err_max_offset[i]) {
								report_error=Coupled_OS_Ampl_Error;
							}
						}
					}
				}

                                if (    (globals.Flash.max_freq_for_high_ot[i] > 0.0) &&
                                        (ChannelStateToTest[i].frequency > globals.Flash.max_freq_for_high_ot[i])) {

                                        temp = globals.Flash.high_ot[i];
                                        if (temp<globals.Constraints.err_max_offset[i]) {
                                                globals.Constraints.err_max_offset[i]=temp;
                                                if (ChannelStateToTest[i].offset>=globals.Constraints.err_max_offset[i]) {
                                                        report_error=PRF_limited_by_offset;
                                                }
                                        }
                                }

				/* ------------------------------- */
			}
		}


		/* --- check for nonsensical negatives ------ */
		if (	ChannelStateToTest[i].frequency<0.0
		                || ((ChannelStateToTest[i].pw<0.0) && (globals.Flash.min_pw[i] > 0.0))
		                || ChannelStateToTest[i].zout<0) {
			report_error=Negative_Not_Allowed;
		}
		/* ------------------------------- */



		/* --- check burst count settings --- */

		if (globals.Flash.max_burst_count[i]>1) {

			/* --- check minimum setting --- */
			if ( (!globals.Flash.burst_func[i] && ChannelStateToTest[i].burst_count<1)
			                ||
			                (globals.Flash.burst_func[i] &&  ChannelStateToTest[i].burst_count<0)) {
				report_error=IllegalParameter;
			}

			/* --- check maximum setting --- */

			globals.Constraints.err_max_burst_count[i]=globals.Flash.max_burst_count[i];

			if (ChannelStateToTest[i].burst_count>globals.Constraints.err_max_burst_count[i]) {
				report_error=max_burst_count_error;
			}


			if (!globals.Flash.burst_func[i]) {
				if (globals.Flash.min_pw[i] > 0.0) {
					temp = (ChannelStateToTest[i].frequency * ampl_fixed_max_duty) /
					       (ChannelStateToTest[i].pw * 100.0);

					if (temp < (float) globals.Constraints.err_max_burst_count[i]) {
						globals.Constraints.err_max_burst_count[i] = (int) temp;
						if (		(ChannelStateToTest[i].burst_count>globals.Constraints.err_max_burst_count[i])
						                && (ChannelStateToTest[i].burst_count>1)) {
							report_error=max_burst_count_error;
						}
					}


					temp = (1.0/ChannelStateToTest[i].frequency) /
					       (ChannelStateToTest[i].pw+ChannelStateToTest[i].burst_time);

					if (temp < (float) globals.Constraints.err_max_burst_count[i]) {
						globals.Constraints.err_max_burst_count[i] = (int) temp;
						if (		(ChannelStateToTest[i].burst_count>globals.Constraints.err_max_burst_count[i])
						                &&	(ChannelStateToTest[i].burst_count>1)) {
							report_error=Burst_Exceeds_Period;
						}
					}
				}


				/* --- check minimum burst gap settings --- */

				globals.Constraints.err_min_burst_time[i]=globals.Flash.min_burst_gap[i];
				if (		(ChannelStateToTest[i].burst_count>1)
				                && (ChannelStateToTest[i].burst_time<(0.999*globals.Constraints.err_min_burst_time[i]))) {
					report_error=min_burst_gap_error;
				}


				/* --- check minimum burst period settings --- */

				if (globals.Flash.min_pw[i] > 0.0) {
					temp = globals.Flash.min_burst_per[i] - ChannelStateToTest[i].pw;
					if (temp > globals.Constraints.err_min_burst_time[i]) {
						globals.Constraints.err_min_burst_time[i] = temp;
						if (		(ChannelStateToTest[i].burst_count>1)
						                && (ChannelStateToTest[i].burst_time<(0.999*globals.Constraints.err_min_burst_time[i]))) {
							report_error=min_burst_period_error;
						}
					}

					/* only report error when burst count is > 1, otherwise it gets annoying */
					temp = ChannelStateToTest[i].pw * (100.0/globals.Flash.max_burst_duty[i] - 1.0);
					if (temp > globals.Constraints.err_min_burst_time[i]) {
						globals.Constraints.err_min_burst_time[i] = temp;
						if (		(ChannelStateToTest[i].burst_time<(0.999*globals.Constraints.err_min_burst_time[i]))
						                && (ChannelStateToTest[i].burst_count>1)) {
							report_error=burst_duty_error;
						}
					}
				}

				/* --- check maximum burst gap settings --- */

				globals.Constraints.err_max_burst_time[i]=globals.Flash.max_burst_gap[i];
				if (		(ChannelStateToTest[i].burst_count>1)
				                &&	(ChannelStateToTest[i].burst_time>(1.001*globals.Constraints.err_max_burst_time[i]))) {
					report_error=max_burst_gap_error;
				}

			}
		}

		/* --- check rise time settings --- */
		/* disable amplitude checks if calibration is in progress */
		if (!globals.Flash.fixed_rise_time[i])
			if (!globals.Flags.extended_ampl_min_max) {
				globals.Constraints.err_min_rise_time[i]=globals.Flash.min_rise_time[i];
				if (ChannelStateToTest[i].rise_time<(0.999*globals.Constraints.err_min_rise_time[i])) {
					report_error=min_rise_time_error;
				}

				globals.Constraints.err_max_rise_time[i]=globals.Flash.max_rise_time[i];
				if (ChannelStateToTest[i].rise_time>(1.001*globals.Constraints.err_max_rise_time[i])) {
					report_error=max_rise_time_error;
				}
			}

		/* --- check slew settings --- */
		/* disable amplitude checks if calibration is in progress */
		if (globals.Flash.curr_slew[i])
			if (!globals.Flags.extended_ampl_min_max) {
				globals.Constraints.err_min_slew[i]=globals.Flash.min_slew[i];
				if (ChannelStateToTest[i].slew<(0.999*globals.Constraints.err_min_slew[i])) {
					report_error=min_slew_error;
				}

				globals.Constraints.err_max_slew[i]=globals.Flash.max_slew[i];
				if (ChannelStateToTest[i].slew>(1.001*globals.Constraints.err_max_slew[i])) {
					report_error=max_slew_error;
				}
			}

		/* --- check resistance settings --- */

		if (globals.Flash.switchable_load[i]) {
			globals.Constraints.err_min_load_type[i]=globals.Flash.low_load_type[i];
			if ((ChannelStateToTest[i].trigger_source==source_internal) && (duty_cycle > max_duty_this_ampl)) {
				globals.Constraints.err_min_load_type[i] *= duty_cycle / max_duty_this_ampl;
			}
			if (ChannelStateToTest[i].load_type<(0.999*globals.Constraints.err_min_load_type[i])) {
				report_error=min_load_type_error;
			}
		}

		if (globals.Flash.max_peak_power[i] > 0.0) {
			temp = ChannelStateToTest[i].amplitude * ChannelStateToTest[i].amplitude / globals.Flash.max_peak_power[i];
			if (temp > globals.Constraints.err_min_load_type[i]) {
				globals.Constraints.err_min_load_type[i] = temp;
				if (ChannelStateToTest[i].load_type<(0.999*globals.Constraints.err_min_load_type[i])) {
					report_error=peak_power_limit;
				}
			}
		}

		if (ChannelStateToTest[i].trigger_source==source_internal) {
			if (globals.Flash.max_avg_power[i] > 0.0) {
				temp = (ChannelStateToTest[i].amplitude * ChannelStateToTest[i].amplitude * ChannelStateToTest[i].pw * ChannelStateToTest[i].frequency) / globals.Flash.max_avg_power[i];
				if (temp > globals.Constraints.err_min_load_type[i]) {
					globals.Constraints.err_min_load_type[i] = temp;
					if (ChannelStateToTest[i].load_type<(0.999*globals.Constraints.err_min_load_type[i])) {
						report_error=average_power_limit;
					}
				}
			}
		}

		globals.Constraints.err_max_load_type[i]=1.0*globals.Flash.high_load_type[i];
		if (ChannelStateToTest[i].load_type>(1.001*globals.Constraints.err_max_load_type[i])) {
			report_error=max_load_type_error;
		}

		/* --- check soft_current_limit settings --- */
		if (globals.Flash.soft_current_limit_enabled[i]) {
			globals.Constraints.err_min_soft_current_limit[i]=globals.Flash.min_soft_current_limit[i];
			if (ChannelStateToTest[i].soft_current_limit<(0.999*globals.Constraints.err_min_soft_current_limit[i])) {
				report_error=min_soft_current_limit_error;
			}

			globals.Constraints.err_max_soft_current_limit[i]=globals.Flash.max_soft_current_limit[i];

			temp=globals.Flash.current_limit_pulse_mode[i];;
			if (temp < globals.Constraints.err_max_soft_current_limit[i]) {
				globals.Constraints.err_max_soft_current_limit[i] = temp;
			}

			if (ChannelStateToTest[i].func_mode==dc_mode_on) {
				temp=globals.Flash.current_limit_dc_mode[i];
				if (temp < globals.Constraints.err_max_soft_current_limit[i]) {
					globals.Constraints.err_max_soft_current_limit[i] = temp;
				}
			}

			if (ChannelStateToTest[i].soft_current_limit>(1.001*globals.Constraints.err_max_soft_current_limit[i])) {
				report_error=max_soft_current_limit_error;
			}
		}


		/* --- done all per-channel checking ---- */
	}

	// merge min/max values for shared channel variables on multi-channel instruments
	// for example, ampl on CH2 may force lower max freq on all channels

	if (num_of_chan>1) {
		if (!globals.Flash.ChanKey_frequency) for (i=1; i<num_of_chan; ++i) {
				if (globals.Constraints.err_min_freq[0] < globals.Constraints.err_min_freq[i]) {
					globals.Constraints.err_min_freq[0] = globals.Constraints.err_min_freq[i];
				}
				if (globals.Constraints.err_max_freq[0] > globals.Constraints.err_max_freq[i]) {
					globals.Constraints.err_max_freq[0] = globals.Constraints.err_max_freq[i];
				}
			}

		if (!globals.Flash.ChanKey_delay) for (i=1; i<num_of_chan; ++i) {
				if (globals.Constraints.err_min_delay[0] < globals.Constraints.err_min_delay[i]) {
					globals.Constraints.err_min_delay[0] = globals.Constraints.err_min_delay[i];
				}
				if (globals.Constraints.err_max_delay[0] > globals.Constraints.err_max_delay[i]) {
					globals.Constraints.err_max_delay[0] = globals.Constraints.err_max_delay[i];
				}
			}

		if (!globals.Flash.ChanKey_pw) for (i=1; i<num_of_chan; ++i) {
				if (globals.Constraints.err_min_pw[0] < globals.Constraints.err_min_pw[i]) {
					globals.Constraints.err_min_pw[0] = globals.Constraints.err_min_pw[i];
				}
				if (globals.Constraints.err_max_pw[0] > globals.Constraints.err_max_pw[i]) {
					globals.Constraints.err_max_pw[0] = globals.Constraints.err_max_pw[i];
				}
			}

		if (!globals.Flash.ChanKey_amplitude) for (i=1; i<num_of_chan; ++i) {
				if (globals.Constraints.err_min_ampl[0] < globals.Constraints.err_min_ampl[i]) {
					globals.Constraints.err_min_ampl[0] = globals.Constraints.err_min_ampl[i];
				}
				if (globals.Constraints.err_max_ampl[0] > globals.Constraints.err_max_ampl[i]) {
					globals.Constraints.err_max_ampl[0] = globals.Constraints.err_max_ampl[i];
				}
			}

		if (!globals.Flash.ChanKey_offset) for (i=1; i<num_of_chan; ++i) {
				if (globals.Constraints.err_min_offset[0] < globals.Constraints.err_min_offset[i]) {
					globals.Constraints.err_min_offset[0] = globals.Constraints.err_min_offset[i];
				}
				if (globals.Constraints.err_max_offset[0] > globals.Constraints.err_max_offset[i]) {
					globals.Constraints.err_max_offset[0] = globals.Constraints.err_max_offset[i];
				}
			}

		if (!globals.Flash.ChanKey_load_type) for (i=1; i<num_of_chan; ++i) {
				if (globals.Constraints.err_min_load_type[0] < globals.Constraints.err_min_load_type[i]) {
					globals.Constraints.err_min_load_type[0] = globals.Constraints.err_min_load_type[i];
				}
				if (globals.Constraints.err_max_load_type[0] > globals.Constraints.err_max_load_type[i]) {
					globals.Constraints.err_max_load_type[0] = globals.Constraints.err_max_load_type[i];
				}
			}

		if (!globals.Flash.ChanKey_Burst_Count) for (i=1; i<num_of_chan; ++i) {
				if (globals.Constraints.err_max_burst_count[0] > globals.Constraints.err_max_burst_count[i]) {
					globals.Constraints.err_max_burst_count[0] = globals.Constraints.err_max_burst_count[i];
				}
			}

		if (!globals.Flash.ChanKey_Burst_Time) for (i=1; i<num_of_chan; ++i) {
				if (globals.Constraints.err_min_burst_time[0] < globals.Constraints.err_min_burst_time[i]) {
					globals.Constraints.err_min_burst_time[0] = globals.Constraints.err_min_burst_time[i];
				}
				if (globals.Constraints.err_max_burst_time[0] > globals.Constraints.err_max_burst_time[i]) {
					globals.Constraints.err_max_burst_time[0] = globals.Constraints.err_max_burst_time[i];
				}
			}

		if (!globals.Flash.ChanKey_rise_time) for (i=1; i<num_of_chan; ++i) {
				if (globals.Constraints.err_min_rise_time[0] < globals.Constraints.err_min_rise_time[i]) {
					globals.Constraints.err_min_rise_time[0] = globals.Constraints.err_min_rise_time[i];
				}
				if (globals.Constraints.err_max_rise_time[0] > globals.Constraints.err_max_rise_time[i]) {
					globals.Constraints.err_max_rise_time[0] = globals.Constraints.err_max_rise_time[i];
				}
			}

		if (!globals.Flash.ChanKey_slew) for (i=1; i<num_of_chan; ++i) {
				if (globals.Constraints.err_min_slew[0] < globals.Constraints.err_min_slew[i]) {
					globals.Constraints.err_min_slew[0] = globals.Constraints.err_min_slew[i];
				}
				if (globals.Constraints.err_max_slew[0] > globals.Constraints.err_max_slew[i]) {
					globals.Constraints.err_max_slew[0] = globals.Constraints.err_max_slew[i];
				}
			}

		if (!globals.Flash.ChanKey_current_limit) for (i=1; i<num_of_chan; ++i) {
				if (globals.Constraints.err_min_soft_current_limit[0] < globals.Constraints.err_min_soft_current_limit[i]) {
					globals.Constraints.err_min_soft_current_limit[0] = globals.Constraints.err_min_soft_current_limit[i];
				}
				if (globals.Constraints.err_max_soft_current_limit[0] > globals.Constraints.err_max_soft_current_limit[i]) {
					globals.Constraints.err_max_soft_current_limit[0] = globals.Constraints.err_max_soft_current_limit[i];
				}
			}
	}


	g_static_mutex_unlock (&mutex);

	return report_error;

}