#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 #include #include #include #include #include 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 static float get_bounded_float (float val_in, float default_val) { // limits returned value within +/- 1 order of magnitude if (val_in > (10.0 * default_val)) { return default_val; } if (val_in < (default_val / 10.0)) { return default_val; } return val_in; } static int get_bounded_int (int val_in, int default_val) { // limits returned value within +/- 1 order of magnitude if (val_in > (10 * default_val)) { return default_val; } if (val_in < (default_val / 10)) { return default_val; } return val_in; } static float get_float_with_min (float val_in, float default_val) { // limits returned value within +2 / -0 order of magnitude if (val_in > (100.0 * default_val)) { return default_val; } if (val_in < default_val) { return default_val; } return val_in; } void idn_string(gchar** response) { *response = g_strdup_printf ("AVTECH ELECTROSYSTEMS,%s,SN:%s,v%s", globals.Flash.model_num, globals.Flash.serial_num, globals.HWDetect.firmware); } void Main_Rst (void) { int i; // the "alive" output - used to enable VCC2 in AVRQ, for example set_shiftreg_bits(SR_3, POS_15, ONE_BIT, BIT_HIGH); 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]; if (globals.Flash.min_delay[i] > 0.0) { globals.ChannelState[i].delay=globals.Flash.min_delay[i]; } else if (globals.Flash.max_delay[i] < 0.0) { globals.ChannelState[i].delay=globals.Flash.max_delay[i]; } else { globals.ChannelState[i].delay=0.0; } if (globals.Flash.min_pw[i] > 0.0) { globals.ChannelState[i].pw=globals.Flash.min_pw[i]; } else { globals.ChannelState[i].pw=0.0; } globals.ChannelState[i].amplitude = rst_ampl_value(i); 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].inverted = 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].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_Inverted(i,globals.ChannelState[i].inverted); 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); 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,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); 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(YES); } void set_dac(int dac, int word) { int i; float dac_percent_change; dac_percent_change = 0.0; g_print_debug(": setting dac %d to %d\n\r",dac,word); /* allows dacs to be disabled (using dac=-1, for example) */ if ((dac >= 0) && (dac < max_dacs)) { for (i = 0; i < max_channels; i++) { // signal a trigger sleep if the output is on, and this is an amplitude DAC if ((dac == globals.Flash.ampl_DAC[i]) && (globals.ChannelState[i].output_state==output_on)) { dac_percent_change = fabs (((float)word-(float)globals.Registers.parallel_DAC_reg[dac]) / (float)dac_max); g_print_debug("ampl dac, %d vs %d old, %e fractional change,\n\r",word,globals.Registers.parallel_DAC_reg[dac],dac_percent_change); globals.Timers.trigger_sleep_time = dac_percent_change * globals.Flash.hv_full_chargeup_time; g_print_debug(" so dac %d requires %e settling time\n\r",dac,globals.Timers.trigger_sleep_time); } } globals.Registers.parallel_DAC_reg[dac]=word; } } static int attenuator_count (int channel) { int i; i = 0; while ((i < max_attens) && (globals.Flash.attenuators[channel][i] > 0.0)) { ++i; } return i; } int Set_Amplitude(int check_possible_only,int pol_override,int override_on,int word_override,int range_override,int aux_override, int switch_range_only,int channel,float requested_ampl,int called_from_set_pw) { const int parameter = pwl_ampl_values; int old_range,old_actual_pol; /* selects relay range */ float new_ampl; new_ampl = requested_ampl; 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; } /* if coupled polarities, as in -ESF units, update CH2 based on CH1 */ if ((channel==1) && (globals.Flash.ignore_ampl_polarity[1])) { // flip CH2 polarity if necessary if ((globals.ChannelState[0].amplitude * new_ampl) < 0.0) { new_ampl *= -1.0; } } /* 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 old_range) && (pwl_struct[parameter][channel].actual_pol == old_actual_pol) ) { globals.Timers.Relay_Switching_Delay_in_Milliseconds=(long) (1000L * globals.Flash.extended_relay_delay_in_sec); } if (globals.Flash.distort_enabled[channel]) { set_dac(globals.Flash.distort_dac[channel],pwl_struct[parameter][channel].word_out_aux); } set_dac(globals.Flash.ampl_DAC[channel],pwl_struct[parameter][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 << pwl_struct[parameter][channel].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 << pwl_struct[parameter][channel].range); } } if ( (!pwl_struct[parameter][channel].actual_pol && globals.Flash.pol_relay_high_for_pos[channel]) || (pwl_struct[parameter][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 */ } } 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 << pwl_struct[parameter][channel].range); } else { /* sometimes CH2 can use pins 0-4 */ set_shiftreg_bits(SR_3, POS_7, FIVE_BITS, 1 << pwl_struct[parameter][channel].range); } // only do this on dual-polarity dual-channel units if ( !globals.Flash.ignore_ampl_polarity[channel] && ((globals.Flash.min_ampl[channel] * globals.Flash.max_ampl[channel]) < 0.0)) { 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 + */ } } } // Are attenuators used? int atten_ctl = 0; int adj_atten_range = pwl_struct[parameter][channel].atten_range; if (attenuator_count(channel) == 1) { if (adj_atten_range == 0) { atten_ctl = BIT_HIGH; } else { atten_ctl = BIT_LOW; } // XRLY1 for CH1, XRLY4 for CH2 set_shiftreg_bits(SR_2, XTR_POS + 1 + (channel*3), ONE_BIT, atten_ctl); } else if (attenuator_count(channel) > 1) { // octal relay driver is inverted if ((adj_atten_range < 0) || (adj_atten_range >= max_attens)) { atten_ctl = 0xff; } else { if (channel) { // shift over 4 positions, out of 8, if CH2 adj_atten_range += 4; } if (globals.Flash.sequential_attenuators[channel]) { // For AVRZ-5W-B-LVA, which uses 3 identical 20 dB attenuators. // 0, 1, 2 or 3 in series are used. atten_ctl = ~((1 << (adj_atten_range+1)) - 1); } else { // For more standard configurations, where different attenuators // are combined in a binary style - 000, 001, 010, 011, 100, etc atten_ctl = ~(adj_atten_range+1); } } I2C_Write(PCF8574+Octal_Relay_Driver, atten_ctl); } if ( (globals.Registers.last_relay_driver_settings[3+channel] != pwl_struct[parameter][channel].atten_range)) { // allow cap banks time to discharge if switching attenuators globals.Flags.force_output_fully_off=YES; if (pwl_struct[parameter][channel].atten_range < globals.Registers.last_relay_driver_settings[3+channel]) { // allow time for HV cap banks to fall globals.Timers.Relay_Switching_Delay_in_Milliseconds=(long) (1000L * globals.Flash.extended_relay_delay_in_sec); } globals.Registers.last_relay_driver_settings[3+channel] = pwl_struct[parameter][channel].atten_range; } debug_new_parameter (channel, parameter, requested_ampl); globals.Changes.update_amp = YES; globals.ChannelState[channel].amplitude = new_ampl; Set_Update_Chans(); if (pwl_struct[parameter][channel].range!=old_range) { 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); /* if coupled polarities, as in -ESF units, update CH2 based on CH1 */ if ((channel==0) && (globals.Flash.ignore_ampl_polarity[1])) { // flip CH2 polarity if necessary if ((globals.ChannelState[0].amplitude * globals.ChannelState[1].amplitude) < 0.0) { Set_Amplitude(0,0,0,0,0,0,0,1,globals.ChannelState[1].amplitude,1); } } return OK; } int Set_Pw(int check_possible_only,int word_override,int range_override,int channel,float requested_pw, int called_from_set_ampl) { const int parameter = pwl_pw_values; int cap_range_control; int status; float set_pw = requested_pw; /* 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 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 (!pwl_struct[parameter][channel].range) { // set the voltage to the actual PW-control circuit (normally in a module) set_dac(globals.Flash.pw_dac[channel],pwl_struct[parameter][channel].word_out); // 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); } } Set_Use_Vctrl_PW_Range(channel,TRUE); // AVP-2CHX units use the trigger signal PW to control the +/- relative // delay of the second channel. Therefore, do not use the fix_pw_dac_val // value from above. Instead, the CH2 delay function controls the // pulse width of the trigger signal. Obscure! if (globals.Flash.min_delay[1] >= 0.0) { // 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. set_dac(4,globals.Flash.fix_pw_dac_val[channel]); set_shiftreg_bits(SR_3, POS_0, SEVEN_BITS, cap_range_control); } } else { Set_Use_Vctrl_PW_Range(channel,FALSE); /* 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 (pwl_struct[parameter][channel].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 << (pwl_struct[parameter][channel].range-2); } set_dac(4,pwl_struct[parameter][channel].word_out); set_shiftreg_bits(SR_3, POS_0, SEVEN_BITS, cap_range_control); } } else { set_dac(4,pwl_struct[parameter][channel].word_out); 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],pwl_struct[parameter][channel].word_out,pwl_struct[parameter][channel].range); } else { /* ISI-style units have voltage-controlled PW and two ranges */ set_dac(globals.Flash.pw_dac[channel],pwl_struct[parameter][channel].word_out); /* two ranges controlled by XTRA-RLY2 line */ set_shiftreg_bits(SR_2, XTR_POS + 2, ONE_BIT, pwl_struct[parameter][channel].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[channel]) { return obsolete_feature; } debug_new_parameter (channel, parameter, requested_pw); globals.ChannelState[channel].pw=set_pw; Set_Update_Chans(); if (!called_from_set_ampl) { Set_Amplitude(0,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 requested_offset) { const int parameter = pwl_os_values; int old_range; float new_offset = requested_offset; 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; } if (!check_possible_only) { int i, error_num; for (i=0; i 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; } setting = set_delay; if (!globals.Flash.volt_ctrl_delay[channel]) { /* tweak depending on polarity */ float 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= 0.0) ) { return HardwareError; } /* set the amplitude controls now. */ int status; if (status=Set_VI_Control(parameter,channel,setting)) { return status; } } if (check_possible_only) { if (pwl_struct[parameter][channel].point_found) { return OK; } else { return CalibrationMinMaxError_delay; } } if (pwl_struct[parameter][channel].range==0) { cap_range_control=0; } else { cap_range_control = 1 << (pwl_struct[parameter][channel].range-1); } if (channel==0) { set_dac(globals.Flash.delay_dac[channel],pwl_struct[parameter][channel].word_out); set_shiftreg_bits(SR_2, POS_8, SEVEN_BITS, cap_range_control); } else if ((channel==1) && (globals.Flash.min_delay[channel] >= 0.0)) { control_pcb107(globals.Flash.I2C_port_for_CH2_delay,globals.Flash.delay_dac[channel], pwl_struct[parameter][channel].word_out, pwl_struct[parameter][channel].range); } else if ((channel==1) && (globals.Flash.min_delay[channel] < 0.0)) { // for obscure AVP-2CHX units, where trigger PW controls the delay of CH2 set_dac(4,pwl_struct[parameter][channel].word_out); set_shiftreg_bits(SR_3, POS_0, SEVEN_BITS, cap_range_control); } debug_new_parameter (channel, parameter, requested_delay); 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; i1) { int i; if (!globals.Flash.ChanKey_frequency) for (i=1; i1.3 || change_ratio < 0.7) { return CalibrationPercentError; } for (i=0; i1.3 || change_ratio < 0.7) { return CalibrationPercentError; } globals.Flash.mon_vi_ratio[channel][pwl_struct[parameter][channel].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); } } // channel 2 not implemented currently 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)1.3 || change_ratio < 0.7) { return CalibrationPercentError; } for (i=0; i= 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 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, use_pw; 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 = MIN(globals.Flash.max_ampl[channel],globals.Flash.max_vout[channel]); } else { ampl = MAX(globals.Flash.min_ampl[channel],globals.Flash.min_vout[channel]); } Set_Amplitude(0,0,0,0,0,0,0,channel,ampl,0); // set to PW to 20 ns, if possible use_pw = MAX(globals.Flash.min_pw[channel], 20e-9); use_pw = MIN(globals.Flash.max_pw[channel], use_pw); Set_Pw(0,0,0,channel,MAX(globals.Flash.min_pw[channel],use_pw),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,nominal_wordout_aux,polarity,range,entry,status,num_in_range,num_of_ranges; float nominal_val,temp1,temp2; float other_setting, other_setting_freq, other_setting_pw; 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[channel][range][polarity][entry]; if (polarity) { nominal_val=-nominal_val; } nominal_wordout=globals.Flash.ampl_dacval[channel][range][polarity][entry]; if (globals.Flash.distort_enabled[channel]) { nominal_wordout_aux=globals.Flash.distort_dacval[channel][range][polarity][entry]; } else { nominal_wordout_aux=0; } if ( Set_Amplitude(0,0,0,0,0,0,0,channel,MAX(globals.Flash.min_ampl[channel],globals.Flash.min_vout[channel]),0) || Set_Amplitude(0,0,0,0,0,0,0,channel,MIN(globals.Flash.max_ampl[channel],globals.Flash.max_vout[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[channel][range][polarity][entry]; nominal_wordout=globals.Flash.os_dacval[channel][range][polarity][entry]; break; case pwl_pw_values: nominal_val=globals.Flash.pw_pwl[channel][range][polarity][entry]; nominal_wordout=globals.Flash.pw_dacval[channel][range][polarity][entry]; reset_state=disable_errors=1; break; case pwl_delay_values: nominal_val=globals.Flash.delay_pwl[channel][range][polarity][entry]; nominal_wordout=globals.Flash.delay_dacval[channel][range][polarity][entry]; disable_errors=1; if (!globals.Flash.volt_ctrl_delay[channel]) { reset_state=1; } break; case pwl_period_values: nominal_val=globals.Flash.period_pwl[channel][range][polarity][entry]; nominal_wordout=globals.Flash.period_dacval[channel][range][polarity][entry]; reset_state=disable_errors=1; break; case pwl_burst_values: nominal_val=globals.Flash.burst_pwl[channel][range][polarity][entry]; nominal_wordout=globals.Flash.burst_dacval[channel][range][polarity][entry]; reset_state=disable_errors=1; break; case pwl_rise_time_values: nominal_val=globals.Flash.rise_time_pwl[channel][range][polarity][entry]; nominal_wordout=globals.Flash.rise_time_dacval[channel][range][polarity][entry]; disable_errors=1; break; case pwl_slew_values: nominal_val=globals.Flash.slew_pwl[channel][range][polarity][entry]; nominal_wordout=globals.Flash.slew_dacval[channel][range][polarity][entry]; break; default: return SyntaxError; 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, except for voltage-controlled delay (2CHPP, for example). For ampl/os parameters, and voltage-controlled delay, 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,nominal_wordout_aux,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; } else if (other_setting < 0.4) { other_setting=0.4; } Set_Inverted(channel,NO); 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/fabs(nominal_val); if (other_setting > 500e3) { other_setting=500e3; } else if (other_setting < 0.4) { other_setting=0.4; } /* 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; if (!globals.Flash.volt_ctrl_delay[channel]) { // skipped for voltage-controlled delay (2CHPP) if (!channel) { // no equivalent for CH2 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_freq = 1 / (20.0 * nominal_val); other_setting_pw = nominal_val; if (other_setting_freq < 0.1) { other_setting_freq = 0.1; } if (other_setting_pw > 0.1) { other_setting_pw = 0.1; } status=Set_frequency(0,0,0,channel,other_setting_freq); Main_update_shift_registers(); if (!status) { status=Set_Pw(0,0,0,globals.Flash.ChanKey_pw?channel:0,other_setting_pw,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; } /* 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 nom_distort; int index; int max_points,max_polarity,max_ranges; float *pwl; // most cases short *pwl_distort; // special case int true_channel; true_channel=channel; Get_VI_Rng_Info(parameter,channel,calibration_point_number,&range,&polarity,&entry,&num_in_range,&num_of_ranges); pwl = 0; pwl_distort = 0; max_points=std_range_size; switch (parameter) { case (pwl_ampl_values): max_polarity=ampl_polarities; max_ranges=ampl_ranges; pwl=&globals.Flash.ampl_pwl[0][0][0][0]; break; case (pwl_distort_values): max_polarity=ampl_polarities; max_ranges=ampl_ranges; pwl_distort=&globals.Flash.distort_dacval[0][0][0][0]; break; case (pwl_os_values): max_polarity=os_polarities; max_ranges=os_ranges; pwl=&globals.Flash.os_pwl[0][0][0][0]; break; case pwl_pw_values: max_polarity=timing_polarities; max_ranges=timing_ranges; pwl=&globals.Flash.pw_pwl[0][0][0][0]; break; case pwl_delay_values: max_polarity=timing_polarities; max_ranges=timing_ranges; pwl=&globals.Flash.delay_pwl[0][0][0][0]; break; case pwl_period_values: max_polarity=timing_polarities; max_ranges=timing_ranges; pwl=&globals.Flash.period_pwl[0][0][0][0]; break; case pwl_burst_values: max_polarity=timing_polarities; max_ranges=timing_ranges; pwl=&globals.Flash.burst_pwl[0][0][0][0]; break; case pwl_rise_time_values: max_polarity=ampl_polarities; max_ranges=ampl_ranges; pwl=&globals.Flash.rise_time_pwl[0][0][0][0]; break; case pwl_slew_values: max_polarity=timing_polarities; max_ranges=timing_ranges; pwl=&globals.Flash.slew_pwl[0][0][0][0]; break; } index=true_channel*max_ranges*max_polarity*max_points +range*max_polarity*max_points +polarity*max_points +entry; if (pwl) { nom_ampl=pwl[index]; } if (pwl_distort) { nom_distort = pwl_distort[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: if (cal_point<0.0) { return Negative_Not_Allowed; } // no break here, continue - delay can be negative 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) && !globals.Sys.disable_timing_cal_percent_check) { // this check can be temporarily disabled for convenience return CalibrationPercentError; } break; case pwl_distort_values: if ((cal_point < 0.0) || (cal_point > 10.0)) { return Between_0_and_10_Volts; } break; } status=OK; globals.Flags.extended_ampl_min_max=YES; if (parameter==pwl_ampl_values) { cal_point=fabs(cal_point); } if (pwl) { *(float *)(&pwl[index])=cal_point; } if (pwl_distort) { /* supplied as a voltage between 0 and 10.0 Volts */ *(short *)(&pwl_distort[index])=(cal_point/10.0) * dac_max; } if (pwl) { /* 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[index])=nom_ampl; } } if (pwl_distort) { // update amplitude now parameter = pwl_ampl_values; } Set_Cal_Nom(channel,calibration_point_number,parameter,NULL); Main_update_shift_registers(); Show_Main_Menu(); if (pwl) { eprom_loc = (char *) (&pwl[index]) - (char *) &(globals.Flash.flash_start); writeUserBlock(&globals.Flash, eprom_loc, sizeof(nom_ampl)); } if (pwl_distort) { eprom_loc = (char *) (&pwl_distort[index]) - (char *) &(globals.Flash.flash_start); writeUserBlock(&globals.Flash, eprom_loc, sizeof(nom_distort)); } 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 temp_float1[std_range_size]; short temp_short1[std_range_size]; short temp_short2[std_range_size]; int index; int max_points,max_polarity,max_ranges; short *pointer_short1; short *pointer_short2; float *pointer_float1; int eprom_loc,size_of_float1,size_of_short1,size_of_short2; int true_channel; true_channel=channel; max_points=std_range_size; pointer_short2=0; switch (parameter) { case (pwl_ampl_values): max_polarity=ampl_polarities; max_ranges=ampl_ranges; pointer_short1=&globals.Flash.ampl_dacval[0][0][0][0]; pointer_short2=&globals.Flash.distort_dacval[0][0][0][0]; pointer_float1=&globals.Flash.ampl_pwl[0][0][0][0]; size_of_short1=sizeof(globals.Flash.ampl_dacval); size_of_short2=sizeof(globals.Flash.distort_dacval); size_of_float1=sizeof(globals.Flash.ampl_pwl); break; case (pwl_os_values): max_polarity=os_polarities; max_ranges=os_ranges; pointer_short1=&globals.Flash.os_dacval[0][0][0][0]; pointer_float1=&globals.Flash.os_pwl[0][0][0][0]; size_of_short1=sizeof(globals.Flash.os_dacval); size_of_float1=sizeof(globals.Flash.os_pwl); break; case pwl_pw_values: max_polarity=timing_polarities; max_ranges=timing_ranges; pointer_short1=&globals.Flash.pw_dacval[0][0][0][0]; pointer_float1=&globals.Flash.pw_pwl[0][0][0][0]; size_of_short1=sizeof(globals.Flash.pw_dacval); size_of_float1=sizeof(globals.Flash.pw_pwl); break; case pwl_delay_values: max_polarity=timing_polarities; max_ranges=timing_ranges; pointer_short1=&globals.Flash.delay_dacval[0][0][0][0]; pointer_float1=&globals.Flash.delay_pwl[0][0][0][0]; size_of_short1=sizeof(globals.Flash.delay_dacval); size_of_float1=sizeof(globals.Flash.delay_pwl); break; case pwl_period_values: max_polarity=timing_polarities; max_ranges=timing_ranges; pointer_short1=&globals.Flash.period_dacval[0][0][0][0]; pointer_float1=&globals.Flash.period_pwl[0][0][0][0]; size_of_short1=sizeof(globals.Flash.period_dacval); size_of_float1=sizeof(globals.Flash.period_pwl); break; case pwl_burst_values: max_polarity=timing_polarities; max_ranges=timing_ranges; pointer_short1=&globals.Flash.burst_dacval[0][0][0][0]; pointer_float1=&globals.Flash.burst_pwl[0][0][0][0]; size_of_short1=sizeof(globals.Flash.burst_dacval); size_of_float1=sizeof(globals.Flash.burst_pwl); break; case pwl_rise_time_values: max_polarity=ampl_polarities; max_ranges=ampl_ranges; pointer_short1=&globals.Flash.rise_time_dacval[0][0][0][0]; pointer_float1=&globals.Flash.rise_time_pwl[0][0][0][0]; size_of_short1=sizeof(globals.Flash.rise_time_dacval); size_of_float1=sizeof(globals.Flash.rise_time_pwl); break; case pwl_slew_values: max_polarity=timing_polarities; max_ranges=timing_ranges; pointer_short1=&globals.Flash.slew_dacval[0][0][0][0]; pointer_float1=&globals.Flash.slew_pwl[0][0][0][0]; size_of_short1=sizeof(globals.Flash.slew_dacval); size_of_float1=sizeof(globals.Flash.slew_pwl); break; default: return SyntaxError; 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 0.0) && (try_this < min_so_far)) { min_so_far = try_this; } } } return min_so_far; } int Set_VI_Control(int parameter,int channel,float new_ampl) { float use_ampl,tweaked_use_ampl; 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; short *pwl_dacval; short *pwl_dacval_aux; /* a second coupled set of dac values, for distort output */ int index,use_range; int range_i,entry_i; 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; pwl_struct[parameter][channel].actual_value = 0.0; int new_point_found = 0; pwl_struct[parameter][channel].point_found = 0; int new_atten_range = 0; pwl_struct[parameter][channel].atten_range = 0; int new_relay_range = 0; pwl_struct[parameter][channel].range = 0; int new_entry = 0; pwl_struct[parameter][channel].entry = 0; int new_word_out = 0; pwl_struct[parameter][channel].word_out = 0; int new_word_out_aux = 0; pwl_struct[parameter][channel].word_out_aux = 0; int new_use_neg_data = 0; pwl_struct[parameter][channel].use_neg_data = 0; int new_actual_pol = 0; pwl_struct[parameter][channel].actual_pol = 0; top_range_only=0; starting_range=0; max_points=std_range_size; 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; pwl_dacval_aux = 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_dacval=&globals.Flash.ampl_dacval[0][0][0][0]; pwl_dacval_aux=&globals.Flash.distort_dacval[0][0][0][0]; pwl=&globals.Flash.ampl_pwl[0][0][0][0]; if (new_ampl<0.0) { new_actual_pol=1; if (!globals.Flash.use_pos_ampl_data_only[channel]) { new_use_neg_data=1; } } 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_dacval[channel][3][new_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; } 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_dacval=&globals.Flash.os_dacval[0][0][0][0]; pwl=&globals.Flash.os_pwl[0][0][0][0]; use_ampl=new_ampl; break; case pwl_pw_values: max_polarity=timing_polarities; max_ranges=timing_ranges; pwl_dacval=&globals.Flash.pw_dacval[0][0][0][0]; pwl=&globals.Flash.pw_pwl[0][0][0][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) 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 < get_ampl_zero_equiv(channel)) { ampl_for_distort_calc = get_ampl_zero_equiv(channel); } /* prohit values of Y that would cause divide-by-zero error */ if ( (-globals.Flash.distort_Y[channel]) > get_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; } else { pw_polarity = 0; } /* 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]; } // may be overridden below, if volt_ctrl_pw and words are increasing in value reciprocal_relationship=YES; break; case pwl_delay_values: max_polarity=timing_polarities; max_ranges=timing_ranges; pwl_dacval=&globals.Flash.delay_dacval[0][0][0][0]; pwl=&globals.Flash.delay_pwl[0][0][0][0]; use_ampl=new_ampl; // may be overridden below, if volt_ctrl_delay and words are increasing in value reciprocal_relationship=YES; break; case pwl_period_values: max_polarity=timing_polarities; max_ranges=timing_ranges; pwl_dacval=&globals.Flash.period_dacval[0][0][0][0]; pwl=&globals.Flash.period_pwl[0][0][0][0]; use_ampl=new_ampl; reciprocal_relationship=YES; break; case pwl_burst_values: max_polarity=timing_polarities; max_ranges=timing_ranges; pwl_dacval=&globals.Flash.burst_dacval[0][0][0][0]; pwl=&globals.Flash.burst_pwl[0][0][0][0]; use_ampl=new_ampl; reciprocal_relationship=YES; break; case pwl_rise_time_values: max_polarity=ampl_polarities; max_ranges=ampl_ranges; pwl_dacval=&globals.Flash.rise_time_dacval[0][0][0][0]; pwl=&globals.Flash.rise_time_pwl[0][0][0][0]; if (globals.ChannelState[channel].amplitude<0.0) { new_actual_pol=new_use_neg_data=1; } else { new_actual_pol=new_use_neg_data=0; } use_ampl=new_ampl; reciprocal_relationship=NO; break; case pwl_slew_values: max_polarity=timing_polarities; max_ranges=timing_ranges; pwl_dacval=&globals.Flash.slew_dacval[0][0][0][0]; pwl=&globals.Flash.slew_pwl[0][0][0][0]; use_ampl=new_ampl; reciprocal_relationship=NO; top_range_only=YES; /* higher drive voltage = lower ripple in slew */ 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. */ new_entry=0; new_word_out=-1; float max1 = fabs(globals.Flash.min_ampl[channel]); float max2 = fabs(globals.Flash.max_ampl[channel]); if (max2 > max1) { max1 = max2; } // PW tweaking per PW range as function of amplitude float dist_frac = 1.0; // use same tweaking for all amplitudes by default if (fabs(globals.Flash.distort_fully_below_ampl[channel]) > 0.0) { // if distort_fully_below_ampl is specified (typically for AVR-E, AVIR, etc), // distort zero at max ampl, and fully below specified ampl dist_frac = (max1 - fabs(globals.ChannelState[channel].amplitude)) / (max1 - fabs(globals.Flash.distort_fully_below_ampl[channel])); if (dist_frac > 1.0) { dist_frac = 1.0; } if (dist_frac < 0.0) { dist_frac = 0.0; } } float use_atten = 1.0; for (new_atten_range = max_attens; (new_atten_range >= -1) && !new_point_found; ) { new_atten_range--; if (new_atten_range == -1) { // no valid attenuators use_atten = 1.0; } else { if (parameter != pwl_ampl_values) { continue; } else if (globals.Flash.attenuators[channel][new_atten_range] == 0.0) { continue; } else if (globals.Flags.attenuators_enabled == 0) { continue; } else if (globals.Flash.switchable_zout[channel] && (globals.ChannelState[channel].zout==globals.Flash.zout_min[channel])) { continue; } else { use_atten = globals.Flash.attenuators[channel][new_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 if ((use_ampl * use_atten) > (globals.Flash.atten_percent_max_ampl[channel] * max1)) { continue; } } for (range_i=starting_range; (range_i 0) || (!globals.Flash.volt_ctrl_pw[channel]) ) { // not used in bot range if bot range is voltage controlled tweaked_use_ampl += globals.Flash.pulse_width_pol_tweak[channel][pw_polarity]; } } else if (parameter == pwl_ampl_values) { // compensate for attenuators tweaked_use_ampl *= use_atten; } for (entry_i=0; (entry_ismallest_allowed_number) && ( ((tweaked_use_ampl>=pwlamp1) && (tweaked_use_ampl<=pwlamp2)) || (decreasing_values_allowed && (tweaked_use_ampl<=pwlamp1) && (tweaked_use_ampl>=pwlamp2)) /* for OS only */ ) ) { new_point_found=1; if ((parameter==pwl_ampl_values) && globals.Flash.ampl_os_ranges_related[channel]) { for (i=0; iglobals.Flash.os_pwl[channel][range_i][0][i]) { min_os_in_range=globals.Flash.os_pwl[channel][range_i][0][i]; } } if ((globals.ChannelState[channel].offsetmax_os_in_range)) { new_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; iglobals.Flash.ampl_pwl[channel][range_i][new_use_neg_data][i]) { min_ampl_in_range=globals.Flash.ampl_pwl[channel][range_i][new_use_neg_data][i]; } } if ((fabs(globals.ChannelState[channel].amplitude)max_ampl_in_range)) { new_point_found=0; /* try higher range if can't satisfy os and ampl both in this range */ } } new_relay_range=range_i; new_entry=entry_i; /* check for linear voltage-controlled PW */ if ( (parameter==pwl_pw_values) && globals.Flash.volt_ctrl_pw[channel] && (pwl_dacval[index] < pwl_dacval[index+1])) { reciprocal_relationship=NO; } /* check for linear voltage-controlled delay */ if ( (parameter==pwl_delay_values) && globals.Flash.volt_ctrl_delay[channel] && (pwl_dacval[index] < pwl_dacval[index+1])) { reciprocal_relationship=NO; } // The above checks only apply if the control words are increasing with index. // Normally just the first range is voltage-controlled. if (reciprocal_relationship) { new_word_out = inverse_interpolation (pwl_dacval[index], pwl_dacval[index+1], pwlamp1, pwlamp2, tweaked_use_ampl); } else { new_word_out = linear_interpolation (pwl_dacval[index], pwl_dacval[index+1], pwlamp1, pwlamp2, tweaked_use_ampl); if (pwl_dacval_aux) { new_word_out_aux = linear_interpolation (pwl_dacval_aux[index], pwl_dacval_aux[index+1], pwlamp1, pwlamp2, tweaked_use_ampl); } } } } } } /* 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 (new_word_out<0 && reciprocal_relationship) { new_point_found=0; use_range=-1; float range_start; tweaked_use_ampl = use_ampl; /* identify highest range that starts with a cal point less than the setting */ for (range_i=0; range_i 0) || (!globals.Flash.volt_ctrl_pw[channel]) ) { // not used in bot range if bot range is voltage controlled tweaked_use_ampl += globals.Flash.pulse_width_pol_tweak[channel][pw_polarity]; } } index=true_channel*max_ranges*max_polarity*max_points +(range_i)*max_polarity*max_points +(new_use_neg_data)*max_points; range_start = pwl[index]; if ((fabs(range_start) > smallest_allowed_number) && (range_start < tweaked_use_ampl)) { use_range=range_i; } } if (use_range<0) { return NoHardwareRangeFoundError; } for (entry_i=max_points-2; (entry_i>=0) && (!new_point_found); --entry_i) { index=true_channel*max_ranges*max_polarity*max_points +(use_range)*max_polarity*max_points +(new_use_neg_data)*max_points +(entry_i); /* find the last two non-zero entries, and extrapolate from them */ if ( (fabs(pwl[index])>smallest_allowed_number) && (fabs(pwl[index+1])>smallest_allowed_number) && (fabs(pwl[index]-pwl[index+1])>smallest_allowed_number) ) { new_point_found=1; new_relay_range=use_range; new_entry=entry_i; new_word_out = inverse_interpolation (pwl_dacval[index], pwl_dacval[index+1], pwl[index], pwl[index+1], tweaked_use_ampl); } } } if (new_word_out<0) { new_word_out=0; return HardwareWordError; } /* check for 12 or 13 bit overflow */ if (new_word_out>dac_max) { new_word_out=dac_max; /* just to prevent wandering bits */ return HardwareWordError; } if (new_point_found) { pwl_struct[parameter][channel].point_found = new_point_found; pwl_struct[parameter][channel].actual_value = tweaked_use_ampl; pwl_struct[parameter][channel].atten_range = new_atten_range; pwl_struct[parameter][channel].range = new_relay_range; pwl_struct[parameter][channel].entry = new_entry; pwl_struct[parameter][channel].word_out = new_word_out; pwl_struct[parameter][channel].word_out_aux = new_word_out_aux; pwl_struct[parameter][channel].use_neg_data = new_use_neg_data; pwl_struct[parameter][channel].actual_pol = new_actual_pol; } return OK; } int Set_VI_Add_Cal(int parameter,int channel,float cal_point) { int range,polarity,entry,word_out,total; 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_float1[std_range_size+1]; float temp_x_float1[std_range_size+1]; /* float copy of short, for calculations */ int temp_x_short1[std_range_size+1]; int temp_x_short2[std_range_size+1]; int index; float *pointer_y_float1; short *pointer_x_short1; short *pointer_x_short2; int eprom_loc,size_of_y_float1,size_of_x_short1,size_of_x_short2; int true_channel; true_channel = channel; range=polarity=0; max_points=std_range_size; pointer_x_short2=0; abs_cal_point=cal_point; // current values range = pwl_struct[parameter][channel].range; polarity = pwl_struct[parameter][channel].use_neg_data; entry = pwl_struct[parameter][channel].entry; word_out = pwl_struct[parameter][channel].word_out; switch (parameter) { case (pwl_ampl_values): max_polarity=ampl_polarities; max_ranges=ampl_ranges; pointer_x_short1=&globals.Flash.ampl_dacval[0][0][0][0]; pointer_x_short2=&globals.Flash.distort_dacval[0][0][0][0]; pointer_y_float1=&globals.Flash.ampl_pwl[0][0][0][0]; size_of_x_short1=sizeof(globals.Flash.ampl_dacval); size_of_x_short2=sizeof(globals.Flash.distort_dacval); size_of_y_float1=sizeof(globals.Flash.ampl_pwl); 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_short1=&globals.Flash.os_dacval[0][0][0][0]; pointer_y_float1=&globals.Flash.os_pwl[0][0][0][0]; size_of_x_short1=sizeof(globals.Flash.os_dacval); size_of_y_float1=sizeof(globals.Flash.os_pwl); old_val=globals.ChannelState[channel].offset; break; case pwl_pw_values: max_polarity=timing_polarities; max_ranges=timing_ranges; pointer_x_short1=&globals.Flash.pw_dacval[0][0][0][0]; pointer_y_float1=&globals.Flash.pw_pwl[0][0][0][0]; size_of_x_short1=sizeof(globals.Flash.pw_dacval); size_of_y_float1=sizeof(globals.Flash.pw_pwl); old_val=globals.ChannelState[channel].pw; break; case pwl_delay_values: max_polarity=timing_polarities; max_ranges=timing_ranges; pointer_x_short1=&globals.Flash.delay_dacval[0][0][0][0]; pointer_y_float1=&globals.Flash.delay_pwl[0][0][0][0]; size_of_x_short1=sizeof(globals.Flash.delay_dacval); size_of_y_float1=sizeof(globals.Flash.delay_pwl); old_val=globals.ChannelState[channel].delay; break; case pwl_period_values: max_polarity=timing_polarities; max_ranges=timing_ranges; pointer_x_short1=&globals.Flash.period_dacval[0][0][0][0]; pointer_y_float1=&globals.Flash.period_pwl[0][0][0][0]; size_of_x_short1=sizeof(globals.Flash.period_dacval); size_of_y_float1=sizeof(globals.Flash.period_pwl); old_val=1.0/globals.ChannelState[channel].frequency; break; case pwl_burst_values: max_polarity=timing_polarities; max_ranges=timing_ranges; pointer_x_short1=&globals.Flash.burst_dacval[0][0][0][0]; pointer_y_float1=&globals.Flash.burst_pwl[0][0][0][0]; size_of_x_short1=sizeof(globals.Flash.burst_dacval); size_of_y_float1=sizeof(globals.Flash.burst_pwl); old_val=globals.ChannelState[channel].burst_time; break; case pwl_rise_time_values: max_polarity=ampl_polarities; max_ranges=ampl_ranges; pointer_x_short1=&globals.Flash.rise_time_dacval[0][0][0][0]; pointer_y_float1=&globals.Flash.rise_time_pwl[0][0][0][0]; size_of_x_short1=sizeof(globals.Flash.rise_time_dacval); size_of_y_float1=sizeof(globals.Flash.rise_time_pwl); old_val=globals.ChannelState[channel].rise_time; break; case pwl_slew_values: max_polarity=timing_polarities; max_ranges=timing_ranges; pointer_x_short1=&globals.Flash.slew_dacval[0][0][0][0]; pointer_y_float1=&globals.Flash.slew_pwl[0][0][0][0]; size_of_x_short1=sizeof(globals.Flash.slew_dacval); size_of_y_float1=sizeof(globals.Flash.slew_pwl); old_val=globals.ChannelState[channel].slew; break; default: return SyntaxError; break; } temp_y_float1[max_points]=0.0; /* Adding a cal point may temporarily result in 11 data points. */ temp_x_float1[max_points]=0.0; /* One is eventually discarded. */ temp_x_short1[max_points]=0; temp_x_short2[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=0; --i) { if ( fabs(temp_y_float1[i])max_in_range) { max_in_range=fabs(temp_y_float1[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) && !globals.Sys.disable_timing_cal_percent_check) { return CalibrationPercentError; } /* check for overly-close data points */ if ( word_out==temp_x_short1[entry] ) { return CalibrationClosenessError; } if ( word_out==temp_x_short1[entry+1]) { return CalibrationClosenessError; } /* move old data over */ for (i=max_points-1; i>entry; i--) { temp_y_float1[i+1]=temp_y_float1[i]; temp_x_short1[i+1]=temp_x_short1[i]; if (pointer_x_short2) { temp_x_short2[i+1]=temp_x_short2[i]; } } /* add the new data */ temp_y_float1[entry+1]=abs_cal_point; temp_x_short1[entry+1]=word_out; for (i=0; i<=max_points; ++i) { temp_x_float1[i]=(float) temp_x_short1[i]; } if (pointer_x_short2) { // use a simple average of the nearest points for the pw distort dac temp_x_short2[entry+1]= (temp_x_short2[entry] + temp_x_short2[entry+2]) / 2; } /* delete a point, if required */ if (total==max_points) { least_integrated_error=1e18; for (i=1; i=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; /* fix non-aligned float access */ float temp; memcpy(&temp, (float *)(pwl + index), sizeof(float)); if ( fabs(temp) 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 requested_burst_time) { const int parameter = pwl_burst_values; int count_word_out; float new_burst_time = requested_burst_time; 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> 6) & 0x07) | ((pwl_struct[parameter][channel].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] != pwl_struct[parameter][channel].range) { g_usleep (1e3 * globals.Timers.normal_relay_bounce_time_in_milliseconds); } stop_gate_override (); globals.Registers.last_relay_driver_settings[2] = pwl_struct[parameter][channel].range; debug_new_parameter (channel, parameter, requested_burst_time); 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 requested_rise_time) { const int parameter = pwl_rise_time_values; char range_control; float new_rise_time = requested_rise_time; 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; ical_type,caldata->channel); caldata->count = 0; caldata->error = 0; caldata->max_change = 0.0; caldata->avg_change = 0.0; min_val = get_bounded_int (globals.Flash.self_cal_min_count, SELF_CAL_MIN_COUNT) / get_bounded_float (globals.Flash.self_cal_ref_freq, SELF_CAL_REF_FREQ); 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(YES); string = g_strdup_printf ("Self-calibration in progress - typical run time: %d min, %d sec. To skip, power off 60 sec, then power back on.", globals.Flash.self_cal_typical_time_min, globals.Flash.self_cal_typical_time_sec); LCD_display_extended_message (string, FALSE, FALSE); 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 measuring_time) { measuring_time = min_time; } min_time = 2.0 / globals.ChannelState[channel].frequency; if (min_time > measuring_time) { measuring_time = min_time; } if (meas_mode == MEAS_PRF) { min_time = 3.0 / globals.ChannelState[channel].frequency; if (min_time > measuring_time) { measuring_time = min_time; } } min_time = 3.0 * fabs(globals.ChannelState[channel].delay); 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 - 50% of a period g_usleep((gulong) (5.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 / get_bounded_float (globals.Flash.self_cal_ref_freq, SELF_CAL_REF_FREQ); 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=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].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].inverted = (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_Inverted(i,globals.ChannelState[i].inverted); 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); 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,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_Output_State(i,globals.ChannelState[i].output_state); } Menu_Clear_Buttons(YES); 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; i1) & 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); g_print_debug("Main_update_shift_registers(), requested sleep %e\n\r", globals.Timers.trigger_sleep_time); /* 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 but don't bother if all outputs are off */ int outputs_on; outputs_on = 0; for (i = 0; i < max_channels; i++) { if (globals.ChannelState[i].output_state==output_on) { ++outputs_on; } } if (outputs_on == 0) { globals.Flags.force_output_fully_off=NO; } 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); // g_print_debug ("\n\r-----------\n\r"); for (i=(num_out_SRs-1); i>=0; --i) { // g_print_debug ("SR %d = %lx hex\n\r",i,globals.Registers.shift_reg_out[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)); /* 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)); } for (i=std_dacs; i> 8); // load upper byte data I2C_Write(PCF8574+Extra_DACs_Addr_Port, addr + 16 + 8); // latch into HC574 I2C_Write(PCF8574+Extra_DACs_Data_Port, globals.Registers.parallel_DAC_reg[i] & 255); // load lower byte data I2C_Write(PCF8574+Extra_DACs_Addr_Port, addr); // latch into AD7839 I2C_Write(PCF8574+Extra_DACs_Addr_Port, addr + 8); // latch into AD7839 } gulong sleep_us = 0; /* keep trigger suppressed for a time (normally 0.5s) 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) { sleep_us = 1e3 * globals.Timers.Relay_Switching_Delay_in_Milliseconds; } /* amplitude settling time after a change */ if (globals.Timers.trigger_sleep_time > 0.0) { sleep_us = MAX(sleep_us, 1e6 * globals.Timers.trigger_sleep_time); } /* limit to 5 seconds */ sleep_us = MIN(sleep_us, 5e6); /* relay change and/or ampl settling time */ g_print_debug("update sleep: %ld\n", sleep_us); g_usleep (sleep_us); /* 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.Timers.trigger_sleep_time = 0.0; globals.Changes.update_zout = 0; globals.Changes.update_load = 0; g_static_mutex_unlock (&mutex); } int IO_Setup_RS232(int baud, char hardhand) { printf ("start writeable: rs232 change\n"); remount_root_as_writeable (TRUE); FILE* configfile = fopen("/tmp/instgettyopts", "w"); if(configfile) { fprintf(configfile, "OPTS=-L %s\n", hardhand ? "-h" : ""); fprintf(configfile, "BAUD=%d\n", baud); fclose(configfile); if (globals.HWDetect.olimex) { system("systemctl --no-block restart inst-getty@ttyS5.service"); } else if (globals.HWDetect.beaglebone) { system("systemctl --no-block restart inst-getty@ttyO5.service"); } } printf ("end writeable: rs232 change\n"); remount_root_as_writeable (FALSE); globals.Flash.baud = baud; globals.Flash.hardhand = hardhand; 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; int result; result = OK; printf ("start writeable: password change\n"); remount_root_as_writeable (TRUE); ctx = lu_start(user, lu_user, NULL, NULL, lu_prompt_console_quiet, NULL, &error); if (ctx == NULL ) { result = password_change_error; } ent = lu_ent_new(); if (!result && (lu_user_lookup_name(ctx, user, ent, &error) == FALSE)) { result = password_change_error; // user doesn't exist } if (!result && (lu_user_setpass(ctx, ent, new_password, FALSE, &error) == FALSE)) { result = password_change_error; } lu_end(ctx); printf ("end writeable: password change\n"); remount_root_as_writeable (FALSE); return result; } else { return password_change_error; } } static void set_shiftreg_bits(int shiftreg, int start_at_bit, int numbits, int value) { long used_bits = (1<= start_at_bit) && (pos < (start_at_bit + numbits))) { if ((1 << pos) & shift_value) { g_print_debug(": XTR%d set HIGH\n", i); } else { g_print_debug(": XTR%d set LOW\n", i); } } } } globals.Registers.shift_reg_out[shiftreg] = masked_reg | shift_value; } int number_of_fixed_ampl_points(int channel) { int i, count; count = 0; for (i=0; i 0.0) { count = i+1; } } return count; } gboolean non_zero_first_ampl_point (int channel) { return (globals.Flash.ampl_pwl[channel][0][0][0] != 0.0); } float rst_ampl_value (int channel) { // smallest positive value, or zero int max; max = number_of_fixed_ampl_points(channel); if (non_zero_first_ampl_point(channel)) { // For AVRQ -AHV, -XHV options, where valids ampls may be // 1.0 to 1.5 kV, positive or negative return get_ampl_zero_equiv(channel); } else if (max == 0) { // not a unit that uses a list of fixed amplitudes // normal scenario if ((globals.Flash.min_ampl[channel] > 0.0) && (globals.Flash.max_ampl[channel] > 0.0)) { // both min and max ampls are positive, range does not include zero. AVR-D4-B. return globals.Flash.min_ampl[channel]; } else if ((globals.Flash.min_ampl[channel] < 0.0) && (globals.Flash.max_ampl[channel] < 0.0)) { // both min and max ampls are negative, range does not include zero. AVR-CD2-B CH2. return globals.Flash.max_ampl[channel]; } else { // normal unit return 0.0; } } else { // a list of fixed amplitudes is used int i, pos_count, neg_count; pos_count = 0; neg_count = 0; float smallest_pos, smallest_neg; smallest_pos = 1e9; smallest_neg = -1e9; // any zeroes? for (i=0; i= 0.0 ) { ++pos_count; if (globals.Flash.fixed_ampl_points[channel][i] < smallest_pos) { smallest_pos = globals.Flash.fixed_ampl_points[channel][i]; } } else if (globals.Flash.fixed_ampl_points[channel][i] < 0.0 ) { ++neg_count; if (globals.Flash.fixed_ampl_points[channel][i] > smallest_neg) { smallest_neg = globals.Flash.fixed_ampl_points[channel][i]; } } } if (pos_count > 0) { return smallest_pos; } else if (neg_count > 0) { return smallest_neg; } else { return 0.0; } } } gboolean fixed_ampl_ok (int channel, float use_ampl) { int i,max; max = number_of_fixed_ampl_points(channel); for (i=0; i *max_ampl) *max_ampl = use_ampl; if (use_ampl < *min_ampl) *min_ampl = use_ampl; } } float get_ampl_zero_equiv(int channel) { float result; result = globals.Flash.ampl_zero_equiv[channel]; if (globals.Flash.switchable_zout[channel] && attenuator_count(channel) && (globals.ChannelState[channel].zout==globals.Flash.zout_max[channel]) ) { // lower min equiv ampl if attenuators are in use in -LV units */ result /= 10.0; } return result; }