diff options
author | Michael J. Chudobiak <mjc@avtechpulse.com> | 2012-08-24 10:15:36 -0400 |
---|---|---|
committer | Michael J. Chudobiak <mjc@avtechpulse.com> | 2012-08-24 10:15:36 -0400 |
commit | c116bc537041827e438cec9d49c5249c33f0377e (patch) | |
tree | b60d09aa46abcdfe3fa63a293d1fb4398c9f54a9 /device-functions.c | |
parent | 3764cf19983e58a471ac0359509ca81b60526ce3 (diff) |
fix attempt at full set of device functions
Diffstat (limited to 'device-functions.c')
-rw-r--r-- | device-functions.c | 4346 |
1 files changed, 4271 insertions, 75 deletions
diff --git a/device-functions.c b/device-functions.c index 5f3ebc4..002bfe8 100644 --- a/device-functions.c +++ b/device-functions.c @@ -1,14 +1,15 @@ #include "device-functions.h" +#include "dummy_functions.h" #include "globals.h" #include "version.h" #include "error_utils.h" +#include "i2c.h" +#include "bus.h" +#include "lcd.h" +#include <math.h> #include <glib.h> -/*** BeginHeader idn_string */ -void idn_string(gchar** response); -/*** EndHeader */ -/*----------------------------------------------------------------------------------------------------------*/ void idn_string(gchar** response) { *response = g_strdup_printf ("AVTECH ELECTROSYSTEMS,%s,%s,%s", @@ -18,41 +19,11 @@ void idn_string(gchar** response) } -/*----------------------------------------------------------------------------------------------------------*/ -int Set_frequency(int check_possible_only,int word_override,int range_override,int channel,float set_freq) -{ - // keep, but ignore, the first 3 parameters for now - - // all this does right now is check the frequency range, - // and store the set value. - - /* abandon if high channel selected by user but not enabled by firmware */ - if (channel && !globals.Flash.ChanKey_frequency) { - return InvalidChannel; - } - - if (!check_possible_only) { - int i, check_valid; - for (i=0; i<max_channels; ++i) { - TestState[i]=globals.ChannelState[i]; - } - TestState[channel].frequency=set_freq; - if ((check_valid=Error_check(TestState))) { - return check_valid; - } - } - - globals.ChannelState[channel].frequency=set_freq; - - return OK; -} - - void Main_Rst (void) { int i; - globals.extended_ampl_min_max=NO; + globals.Flags.extended_ampl_min_max=NO; // go backwards, so channel 0 overrides everything for (i=globals.Flash.channels-1; i>=0; --i) { @@ -99,55 +70,4280 @@ void Main_Rst (void) globals.ChannelState[i].vlogic = 0.0; globals.ChannelState[i].slew = globals.Flash.min_slew[i]; - globals.do_check_settings=NO; /* don't check for conflicting settings */ - /* FIXME - implement Set_* functions! - 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].ab_mode); - Set_Func(i,globals.ChannelState[i].func_mode); - Set_Pol(i,globals.ChannelState[i].polarity); - Set_Gate_Sync(i,globals.ChannelState[i].gate_type); - Set_Gate_Level(i,globals.ChannelState[i].gate_level); - Set_EA(i,globals.ChannelState[i].amp_mode); - Set_EO(i,globals.ChannelState[i].os_mode); - if (globals.Flash.switchable_zout[i]) { - Set_zout(i,globals.ChannelState[i].zout,1); + 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].ab_mode); + Set_Func(i,globals.ChannelState[i].func_mode); + Set_Pol(i,globals.ChannelState[i].polarity); + Set_Gate_Sync(i,globals.ChannelState[i].gate_type); + Set_Gate_Level(i,globals.ChannelState[i].gate_level); + Set_EA(i,globals.ChannelState[i].amp_mode); + Set_EO(i,globals.ChannelState[i].os_mode); + if (globals.Flash.switchable_zout[i]) { + Set_zout(i,globals.ChannelState[i].zout,1); + } + + Set_Load(i,globals.ChannelState[i].load_type); + Set_Logic_Level(i,globals.ChannelState[i].logic_level); + Set_rise_time(0,0,0,i,globals.ChannelState[i].rise_time); + Set_slew(0,0,0,i,globals.ChannelState[i].slew); + Set_current_limit(0,i,globals.ChannelState[i].soft_current_limit); + + if (globals.Flash.max_burst_count[i]>1) { + Set_Burst_Count(i,globals.ChannelState[i].burst_count,globals.ChannelState[i].burst_time); + } + + Set_Trig_Source(i,globals.ChannelState[i].trigger_source); + + Set_Amplitude(0,0,0,0,0,0,i,globals.ChannelState[i].amplitude,0); + Set_Offset(0,0,0,0,i,globals.ChannelState[i].offset); + Set_Output_State(i,globals.ChannelState[i].output_state); + + Set_current_limit(0,i,globals.ChannelState[i].soft_current_limit); + + Set_avrq_ampl(0,0,0,2,globals.ChannelState[i].vcc1); + Set_avrq_ampl(0,0,0,3,globals.ChannelState[i].vcc2); + Set_avrq_ampl(0,0,0,4,globals.ChannelState[i].vlogic); + globals.Flags.do_check_settings=YES; /* check for conflicting settings */ + } + + // establishes min/max values for queries, but reports no errors + Error_check(globals.ChannelState); + globals.Changes.update_whole_main_menu=YES; + + 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; + + // FIXME - check which misc globals should be reset here! + + Menu_Clear_Buttons(); +} + + +int set_dac(int dac, int word) +{ + /* allows dacs to be disabled (using dac=-1, for example) */ + if ((dac >= 0) && (dac < 8)) { + globals.Registers.parallel_DAC_reg[dac]=word; + } +} + + +int Set_Amplitude(int check_possible_only,int pol_override,int override_on,int word_override,int range_override, + int switch_range_only,int channel,float new_ampl,int called_from_set_pw) +{ + int word_out; /* what is sent to the DAC */ + int relay_range,old_range,old_actual_pol; /* selects relay range */ + int i; + int error_num; + int UseNegData; /* if polarity is negative and separate piece-wise linear data is available for neg */ + int point_found; + long mask; + int entry; + int status; + int actual_pol; + + if (globals.Flash.enable_avrq_extra_ampls && channel) { + return Set_avrq_ampl(check_possible_only,word_override,range_override,channel,new_ampl); + } + if (globals.Flash.enable_avrq_extra_ampls && !channel) { + if ((new_ampl>=0.0) && (new_ampl<globals.Flash.ampl_zero_equiv[channel])) { + new_ampl=globals.Flash.ampl_zero_equiv[channel]; + } + if ((new_ampl<0.0) && (new_ampl>-globals.Flash.ampl_zero_equiv[channel])) { + new_ampl=-globals.Flash.ampl_zero_equiv[channel]; + } + } + + if (!globals.Flash.voltage_enabled[channel] && !globals.Flash.current_enabled[channel]) { + return Unrecognized; + } + + /* abandon if high channel selected by user but not enabled by firmware */ + if (channel && !globals.Flash.ChanKey_amplitude) { + return InvalidChannel; + } + + /* keep "-0" in negative area, to avoid using zeroed positive data */ + if ( new_ampl<=smallest_allowed_number + && new_ampl>=-smallest_allowed_number + && !(globals.Flash.max_ampl[channel]>0) ) { + new_ampl=-smallest_allowed_number; + } + + /* Check for min abs value, step size */ + if (globals.Flash.ampl_min_abs_value[channel] > 0.0) { + if ((new_ampl < 0.0) && + (new_ampl > -globals.Flash.ampl_min_abs_value[channel])) { + new_ampl = -globals.Flash.ampl_min_abs_value[channel]; + } + if ((new_ampl >= 0.0) && + (new_ampl < globals.Flash.ampl_min_abs_value[channel])) { + new_ampl = globals.Flash.ampl_min_abs_value[channel]; + } + } + + /* For units with large amplitude step sizes (AVRQ-2-B-PN-EVE) */ + if (globals.Flash.ampl_step_size[channel] > 0.0) { + new_ampl = ((int) (new_ampl / globals.Flash.ampl_step_size[channel])) * globals.Flash.ampl_step_size[channel]; + } + + if (!check_possible_only) { + for (i=0; i<max_channels; ++i) { + TestState[i]=globals.ChannelState[i]; + } + TestState[channel].amplitude=new_ampl; + if ((error_num=Error_check(TestState))) { + return error_num; + } + + /* determine the previous relay range */ + Set_VI_Control(pwl_ampl_values,channel,globals.ChannelState[channel].amplitude,&point_found, + &old_range,&UseNegData,&entry,&word_out,&old_actual_pol); + } + + word_out = -1; + relay_range = 0; + + + /* find appropriate range/fine settings from piece-wise linear data in flash memory */ + + if (override_on) { + word_out=word_override; + relay_range=range_override; + actual_pol=pol_override; + } else { + /* set the amplitude controls now. */ + if ((status=Set_VI_Control(pwl_ampl_values,channel,new_ampl,&point_found, + &relay_range,&UseNegData,&entry,&word_out,&actual_pol))) { + return status; + } + } + + if (check_possible_only) { + if (point_found) { + return OK; + } else { + return CalibrationMinMaxError; + } + } + + /* when switching to EA mode, need to switch range but keep DAC voltage zero */ + if (switch_range_only) { + word_out=0; + } + + + /* if switching to a higher range with the same polarity, an extended delay may be needed to allow */ + /* capacitor banks to discharge (1011, 1015 series especially) */ + if ((relay_range>old_range) && (actual_pol==old_actual_pol) ) { + globals.Timers.Relay_Switching_Delay_in_Milliseconds=(long) (1000L * globals.Flash.extended_relay_delay_in_sec); + } + + + set_dac(globals.Flash.ampl_DAC[channel],word_out); + + + if (!channel) { + if (!globals.Flash.ampl_ranges_for_ch2_only) { + if ((globals.Flash.ChanKey_amplitude?globals.Flash.channels:1)<=1) { + /* for regular, single-channel units use AMPL RANGE pins 0-4 */ + globals.Registers.shift_reg_out[3] = ((long)(globals.Registers.shift_reg_out[3] & 0xff07f)) | ((long)(1<<(relay_range+7))); + } else { + /* for CH1 of dual-channel units, use AMPL RANGE pin 0-2 for CH1, and 3-4 for CH2 */ + globals.Registers.shift_reg_out[3] = ((long)(globals.Registers.shift_reg_out[3] & 0xffc7f)) | ((long)(1<<(relay_range+7))); + } + } + + if ( (!actual_pol && globals.Flash.pol_relay_high_for_pos[channel]) + || (actual_pol && !globals.Flash.pol_relay_high_for_pos[channel]) ) { + globals.Registers.shift_reg_out[3] |= (long)(0x02000); /* set O.POL line high to switch pol relay to +, normally */ + } else { + globals.Registers.shift_reg_out[3] &= (long)(0xfffff-0x02000); /* 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 */ + globals.Registers.shift_reg_out[3] = ((long)(globals.Registers.shift_reg_out[3] & 0xff3ff)) | ((long)(1<<(relay_range+10))); + } else { + /* sometimes CH2 can use pins 0-4 */ + globals.Registers.shift_reg_out[3] = ((long)(globals.Registers.shift_reg_out[3] & 0xff07f)) | ((long)(1<<(relay_range+7))); + } + + mask = ((long) 1) << (globals.Flash.polarity_xtra_rly[channel]+14); + if (new_ampl<0.0) { + globals.Registers.shift_reg_out[2] &= (long)(0xfffff-mask); /* set O.POL line low to switch pol relay to - */ + } else { + globals.Registers.shift_reg_out[2] |= mask; /* set O.POL line high to switch pol relay to + */ + } + } + + globals.Changes.update_amp=YES; + + globals.ChannelState[channel].amplitude=new_ampl; + Set_Update_Chans(); + + if ((relay_range!=old_range) && (globals.ChannelState[channel].output_state==output_on)) { + globals.Flags.force_output_fully_off=YES; + } + + /* update offset */ + if (globals.Flash.ampl_os_ranges_related[channel] && !globals.Changes.update_os) { + Set_Offset(0,0,0,0,channel,globals.ChannelState[channel].offset); + } + + /* ampl / pw tweaks */ + if (!called_from_set_pw) { + Set_Pw(0,0,0,channel,globals.ChannelState[channel].pw,1); + } + + /* ampl / delay tweaks */ + Set_Delay(0,0,0,channel,globals.ChannelState[channel].delay); + Set_rise_time(0,0,0,channel,globals.ChannelState[channel].rise_time); + + return OK; +} + + +int Set_Pw(int check_possible_only,int word_override,int range_override,int channel,float set_pw,int called_from_set_ampl) +{ + int word_out; /* what is sent to the DAC */ + int cap_range_control; + int point_found,relay_range,UseNegData,entry; + int i; + int check_valid; + int status,actual_pol; + + /* abandon if high channel selected by user but not enabled by firmware */ + if (channel && !globals.Flash.ChanKey_pw) { + return InvalidChannel; + } + + /* for fixed PW units, set actual trigger PW to a reasonable minimum */ + if (globals.Flash.fixed_pw[channel] + && !check_possible_only + && !word_override + && !range_override + && (globals.Flash.fully_programmed==All_Programmed)) { + set_pw=globals.Flash.min_pw[channel]; + } + + if (!check_possible_only) { + for (i=0; i<max_channels; ++i) { + TestState[i]=globals.ChannelState[i]; + } + TestState[channel].pw=set_pw; + if ((check_valid=Error_check(TestState))) { + return check_valid; + } + } + + + /* find appropriate range/fine settings from piece-wise linear data in flash memory */ + if (word_override) { + word_out=word_override; + relay_range=range_override; + } else { + /* set the pw controls now. */ + if ((status=Set_VI_Control(pwl_pw_values,channel,set_pw,&point_found, + &relay_range,&UseNegData,&entry,&word_out,&actual_pol))) { + return status; + } + } + + + if (check_possible_only) { + if (point_found) { + return OK; + } else { + return CalibrationMinMaxError; + } + } + + if (relay_range==0) { + cap_range_control=0; + } else { + cap_range_control = 1 << (relay_range-1); + } + + /* if this is for CH1, use the standard output chain */ + if (channel==0) { + if (globals.Flash.special_pw_range_minimum[channel] > 0.0) { + if (set_pw > globals.Flash.special_pw_range_minimum[channel]) { + globals.Registers.shift_reg_out[2] = ((long) (globals.Registers.shift_reg_out[2] & 0xeffff)) | (long) 1<<16; + } else { + globals.Registers.shift_reg_out[2] = ((long) (globals.Registers.shift_reg_out[2] & 0xeffff)); + } + } + + /* AVPP-style: lower PW range is voltage-controlled */ + if (globals.Flash.volt_ctrl_pw[channel]) { + /* use DAC8420 to control PW in lowest PW range */ + if (!relay_range) { + set_dac(globals.Flash.pw_dac[channel],word_out); + word_out=globals.Flash.fix_pw_dac_val[channel]; + /* Set to fixed value. */ + /* Isn't actually used to control PW in this mode, but may trigger following stages */ + + /* Zout hardware/software is used in AVPP to control PW ranges, not Zout */ + Set_zout(channel,globals.Flash.zout_min[channel],0); + } else { + Set_zout(channel,globals.Flash.zout_max[channel],0); + + /* In AVPP units, the bottom range is voltage-controlled, and the upper range(s) are + PWin=PWout. Therefore, shift the relay used by the upper ranges by one, to avoid + changing capacitors. */ + + if (relay_range==1) { + cap_range_control=0; + } else { + cap_range_control = 1 << (relay_range-2); + } + } + } + + /* set fine controls */ + set_dac(4,word_out); + + /* set ranges */ + globals.Registers.shift_reg_out[3] = ((long)(globals.Registers.shift_reg_out[3] & 0xfff80)) | ((long)(cap_range_control)); + } + + if (channel==1) { + /* if this is for CH2, use the standard 1021D dual-PW board, which has a PCF8574 for I/O, */ + /* unless it is a voltage-controlled scheme like the ISI units. */ + if (!globals.Flash.volt_ctrl_pw[channel]) { + control_pcb107(Second_PW_Port,globals.Flash.pw_dac[channel],word_out,relay_range); + } else { + /* ISI-style units have voltage-controlled PW and two ranges */ + set_dac(globals.Flash.pw_dac[channel],word_out); + + /* two ranges controlled by XTRA-RLY2 line */ + globals.Registers.shift_reg_out[2] = ((long) (globals.Registers.shift_reg_out[2] & 0xeffff)) | (long) (((long) (relay_range&1))<<16); + } + } + + /* need a center-frequency control voltage for monocycle generators, to space the + and - parts of the monocycle. */ + /* use CH2 calibration to do this */ + if (globals.Flash.is_monocycle[0] && channel==0) { + if ((status=Set_VI_Control(pwl_pw_values,1,set_pw,&point_found, + &relay_range,&UseNegData,&entry,&word_out,&actual_pol))) { + return status; + } + set_dac(globals.Flash.monocycle_dac[0],word_out); + } + + if (set_pw!=globals.ChannelState[channel].pw) { + globals.Changes.update_pw=YES; + } + + globals.ChannelState[channel].pw=set_pw; + Set_Update_Chans(); + + if (!called_from_set_ampl) { + Set_Amplitude(0,0,0,0,0,0,channel,globals.ChannelState[channel].amplitude,1); + } + + return OK; +} + + +int Set_Offset(int check_possible_only,int override_on,int word_override,int range_override,int channel,float new_offset) +{ + int word_out; /* what is sent to the DAC */ + int i; + int error_num; + int point_found; + int entry,dummy1,dummy2,dummy3; + int status,actual_pol; + int old_range,relay_range; + + if (!globals.Flash.voltage_offset_enabled[channel] && !globals.Flash.current_offset_enabled[channel]) { + return Unrecognized; + } + + /* abandon if high channel selected by user but not enabled by firmware */ + if (channel && !globals.Flash.ChanKey_offset) { + return InvalidChannel; + } + + word_out = -1; + point_found = 0; + + if (!check_possible_only) { + for (i=0; i<max_channels; ++i) { + TestState[i]=globals.ChannelState[i]; + } + TestState[channel].offset=new_offset; + if ((error_num=Error_check(TestState))) { + return error_num; + } + + /* determine the previous relay range */ + Set_VI_Control(pwl_os_values,channel,globals.ChannelState[channel].offset,&point_found, + &old_range,&dummy1,&dummy2,&word_out,&dummy3); + } + + if (override_on) { + relay_range=range_override; + word_out=word_override; + } else { + if ((status=Set_VI_Control(pwl_os_values,channel,new_offset,&point_found, + &relay_range,&dummy2,&entry,&word_out,&actual_pol))) { + return status; + } + } + + if (check_possible_only) { + if (point_found) { + return OK; + } else { + return CalibrationMinMaxError; + } + } + + set_dac(globals.Flash.os_DAC[channel],word_out); + + globals.Changes.update_os=YES; + + globals.ChannelState[channel].offset=new_offset; + Set_Update_Chans(); + + if ((relay_range!=old_range) && (globals.ChannelState[channel].output_state==output_on)) { + globals.Flags.force_output_fully_off=YES; + } + + if ((globals.Flash.ampl_coupled_to_os[channel] || globals.Flash.ampl_os_ranges_related[channel]) && !globals.Changes.update_amp) { + Set_Amplitude(0,0,0,0,0,0,channel,globals.ChannelState[channel].amplitude,0); + } + + /* change amplitude range if required for calibration */ + if (override_on) { + Set_Amplitude(0,0,1,0,relay_range,0,channel,0.0,0); + } + + /* increase PW if required to compensate for series output diode turn-on time */ + if (globals.Flash.distort_Y[channel] != 0.0) { + Set_Pw(0,0,0,channel,globals.ChannelState[channel].pw,0); + } + + return OK; +} + + +int Set_frequency(int check_possible_only,int word_override,int range_override,int channel,float set_freq) +{ + int cap_range_control; /* what's actually sent to SR */ + int i; + int check_valid; + float new_pw; + int status; + int point_found,relay_range,UseNegData,entry,actual_pol,old_range; + int word_out; /* what is sent to the DAC */ + + /* abandon if high channel selected by user but not enabled by firmware */ + if (channel && !globals.Flash.ChanKey_frequency) { + return InvalidChannel; + } + + word_out = -1; + + /* check new duty cycle, if pw fixed */ + if (globals.ChannelState[channel].hold_setting==hold_width) { + new_pw=globals.ChannelState[channel].pw; + } else { + new_pw=globals.ChannelState[channel].pw * globals.ChannelState[channel].frequency/set_freq; + } + + if (!check_possible_only) { + for (i=0; i<max_channels; ++i) { + TestState[i]=globals.ChannelState[i]; + } + TestState[channel].pw=new_pw; + TestState[channel].frequency=set_freq; + if ((check_valid=Error_check(TestState))) { + return check_valid; + } + + /* added Feb 23/05 */ + /* determine the previous relay range */ + Set_VI_Control(pwl_period_values,channel,1.0/globals.ChannelState[channel].frequency,&point_found, + &old_range,&UseNegData,&entry,&word_out,&actual_pol); + } + + /* find appropriate range/fine settings from piece-wise linear data in flash memory */ + if (word_override) { + word_out=word_override; + relay_range=range_override; + } else { + /* set the amplitude controls now. */ + if ((status=Set_VI_Control(pwl_period_values,channel,1.0/set_freq,&point_found, + &relay_range,&UseNegData,&entry,&word_out,&actual_pol))) { + return status; + } + } + + if (check_possible_only) { + if (point_found) { + return OK; + } else { + return CalibrationMinMaxError; + } + } + + if (globals.Flash.is_func_gen[channel]) { + set_dac(globals.Flash.freq_dac[channel],word_out); + + /* Function generator PRF range controlled by XTRA-RLY 1-3 lines. */ + /* Range=0 disables oscillator, for amplifier mode. */ + + if (globals.ChannelState[channel].func_mode==amp_mode_on) { + relay_range=0; + } + + globals.Registers.shift_reg_out[2] = ((long) (globals.Registers.shift_reg_out[2] & 0xc7fff)) | (long) (((long) (relay_range & 0x07))<<15); + + if ((relay_range!=old_range) && (globals.ChannelState[channel].output_state==output_on)) { + globals.Flags.force_output_fully_off=YES; + } + } + + else { + if (relay_range==0) { + cap_range_control=0; + } else { + cap_range_control = 1 << (relay_range-1); + } + + set_dac(globals.Flash.freq_dac[channel],word_out); + globals.Registers.shift_reg_out[2] = ((long)(globals.Registers.shift_reg_out[2] & 0xfff00)) | ((long)cap_range_control); /* bottom 8 bits of 20 bits */ + } + + if (set_freq!=globals.ChannelState[channel].frequency) { + globals.Changes.update_freq=YES; + } + + globals.ChannelState[channel].frequency=set_freq; + + Set_Pw(0,0,0,channel,new_pw,0); + Set_Update_Chans(); + + return OK; +} + + +int Set_Delay(int check_possible_only,int word_override,int range_override,int channel,float set_delay) +{ + int word_out; /* what is sent to the DAC */ + int cap_range_control; /* what is actually sent to shift register */ + float setting; + int i; + int check_valid; + int status; + int point_found,relay_range,UseNegData,entry,actual_pol; + + /* abandon if high channel selected by user but not enabled by firmware */ + if (channel && !globals.Flash.ChanKey_delay) { + return InvalidChannel; + } + + if (!check_possible_only) { + for (i=0; i<max_channels; ++i) { + TestState[i]=globals.ChannelState[i]; + } + TestState[channel].delay=set_delay; + if ((check_valid=Error_check(TestState))) { + return check_valid; + } + } + + setting=set_delay-globals.Flash.propagation_delay[channel]; + + /* tweak depending on polarity */ + if (globals.ChannelState[channel].amplitude<0.0) { + setting -= globals.Flash.delay_pol_tweak[channel][1]; + } else { + setting -= globals.Flash.delay_pol_tweak[channel][0]; + } + + if (setting >= 0.0) { + Set_AdvDel(channel,to_Advance); + } else { + Set_AdvDel(channel,to_Delay); + setting *= -1; + } + + setting=setting+globals.Flash.delay_shrink[channel]; + + /* protect against impossible settings during first turn-on */ + if (setting<zero_equiv_timing) { + return HardwareError; + } + + /* find appropriate range/fine settings from piece-wise linear data in flash memory */ + if (word_override) { + word_out=word_override; + relay_range=range_override; + } else { + /* set the amplitude controls now. */ + if ((status=Set_VI_Control(pwl_delay_values,channel,setting,&point_found, + &relay_range,&UseNegData,&entry,&word_out,&actual_pol))) { + return status; + } + } + + if (check_possible_only) { + if (point_found) { + return OK; + } else { + return CalibrationMinMaxError; + } + } + + + if (relay_range==0) { + cap_range_control=0; + } else { + cap_range_control = 1 << (relay_range-1); + } + + if (channel==0) { + set_dac(globals.Flash.delay_dac[channel],word_out); + globals.Registers.shift_reg_out[2] = ((long)(globals.Registers.shift_reg_out[2] & 0xf80ff)) | ((long)cap_range_control<<8); + } + + if (channel==1) { + control_pcb107(Second_Dly_Port,globals.Flash.delay_dac[channel],word_out,relay_range); + } + + if (set_delay!=globals.ChannelState[channel].delay) { + globals.Changes.update_delay=YES; + } + + globals.ChannelState[channel].delay=set_delay; + Set_Update_Chans(); + + return OK; +} + + +int Set_Double(int channel,int new_setting) +{ + int error_num; + int i; + + /* abandon if high channel selected by user but not enabled by firmware */ + if (channel && !globals.Flash.ChanKey_double_pulse) { + return InvalidChannel; + } + + for (i=0; i<max_channels; ++i) { + TestState[i]=globals.ChannelState[i]; + } + TestState[channel].double_pulse=new_setting; + if ((error_num=Error_check(TestState))) { + return error_num; + } + + if (new_setting == double_off) { + globals.Registers.shift_reg_out[0] &= 0xdf; /* turn double-pulse off */ + } else { + globals.Registers.shift_reg_out[0] |= 0x20; /* turn double-pulse on */ + } + + if (globals.ChannelState[channel].double_pulse!=new_setting) { + globals.Changes.update_delay=YES; + } + + globals.ChannelState[channel].double_pulse=new_setting; + Set_Update_Chans(); + + return OK; +} + + +void Ctrl_PRF_Limiter(int enable) +{ + if (enable && globals.Flash.prf_limiter) { + globals.Registers.shift_reg_out[0] |= 0x80; /* turn prf limiter on */ + } else { + globals.Registers.shift_reg_out[0] &= 0x7f; /* turn prf limiter off */ + } + + Main_update_shift_registers(); + + return; +} + + +int Set_Mux(int channel) +{ + int mux_out; + + if (globals.ChannelState[channel].func_mode==dc_mode_on && globals.ChannelState[channel].polarity==pol_norm) { + mux_out=4; + } else if (globals.ChannelState[channel].func_mode==dc_mode_on && globals.ChannelState[channel].polarity!=pol_norm) { + mux_out=5; + } else if (globals.ChannelState[channel].ab_mode==pw_normal && globals.ChannelState[channel].polarity==pol_norm) { + mux_out=2; + } else if (globals.ChannelState[channel].ab_mode==pw_normal && globals.ChannelState[channel].polarity!=pol_norm) { + mux_out=3; + } else if (globals.ChannelState[channel].polarity==pol_norm) { + mux_out=0; + } else if (globals.ChannelState[channel].polarity!=pol_norm) { + mux_out=1; + } + + if (globals.ChannelState[channel].test_delay_mode==YES) { + mux_out=6; + } + + globals.Registers.shift_reg_out[1] = (globals.Registers.shift_reg_out[1] & 0xf8) + mux_out; +} + + +int Set_Func(int channel,int mode) +{ + int i; + int check_valid; + + /* abandon if high channel selected by user but not enabled by firmware */ + if (channel && !globals.Flash.ChanKey_func_mode) { + return InvalidChannel; + } + + if ( (mode==dc_mode_on) && !globals.Flash.dc_mode_allowed[channel]) { + return SyntaxError; + } + + /* some modes are for function generators only */ + if ( ((mode==sin_mode_on) || (mode==squ_mode_on) || (mode==tri_mode_on) || (mode==amp_mode_on)) + && !globals.Flash.is_func_gen[channel]) { + return SyntaxError; + } + + for (i=0; i<max_channels; ++i) { + TestState[i]=globals.ChannelState[i]; + } + TestState[channel].func_mode=mode; + if ((check_valid=Error_check(TestState))) { + return check_valid; + } + + if (globals.Flash.is_func_gen[channel]) { + /* set sin, tri, squ lines */ + globals.Registers.shift_reg_out[3] = ((long) (globals.Registers.shift_reg_out[3] & 0x1ffff)) + | (long) (((long) (mode & 0x07))<<17); + + /* set amplify mode line */ + + globals.Registers.shift_reg_out[2] = ((long) (globals.Registers.shift_reg_out[2] & 0xbffff)) + | (long) (((long) (mode & 0x08)) << (globals.Flash.ext_amplify_xtra_rly[channel]+11)); + + globals.Changes.update_func=YES; + } + + + /* reset pulse width and delay to safe values */ + if (globals.ChannelState[channel].func_mode!=mode) { + globals.ChannelState[channel].func_mode=mode; + + Set_Pw(0,0,0,channel,globals.Flash.min_pw[channel],0); + Set_Delay(0,0,0,channel,globals.Flash.min_delay[channel]); + + /* update frequency, to reset func gen control lines */ + Set_frequency(0,0,0,channel,globals.ChannelState[channel].frequency); + + Set_current_limit(0,channel,globals.ChannelState[channel].soft_current_limit); + } + + Set_Mux(channel); + Set_Update_Chans(); + + return OK; +} + + +int Set_Pol(int channel,int mode) +{ + int error_num, i; + + /* abandon if high channel selected by user but not enabled by firmware */ + if (channel && !globals.Flash.ChanKey_polarity) { + return InvalidChannel; + } + + /* duty cycle check */ + for (i=0; i<max_channels; ++i) { + TestState[i]=globals.ChannelState[i]; + } + TestState[channel].polarity=mode; + if ((error_num=Error_check(TestState))) { + return error_num; + } + + if (globals.ChannelState[channel].polarity!=mode) { + globals.Changes.update_inv=YES; + } + + globals.ChannelState[channel].polarity=mode; + + Set_Mux(channel); + Set_Update_Chans(); + + return OK; +} + + +int Set_Hold(int channel,int mode) +{ + /* abandon if high channel selected by user but not enabled by firmware */ + if (channel && !globals.Flash.ChanKey_hold_setting) { + return InvalidChannel; + } + + if (globals.ChannelState[channel].hold_setting!=mode) { + globals.Changes.update_pw=YES; + } + + globals.ChannelState[channel].hold_setting=mode; + Set_Update_Chans(); + + return OK; +} + + +int Set_Output_State(int channel,int mode) +{ + int i, error_num; + + /* abandon if high channel selected by user but not enabled by firmware */ + if (channel && !globals.Flash.ChanKey_output_state) { + return InvalidChannel; + } + + for (i=0; i<max_channels; ++i) { + TestState[i]=globals.ChannelState[i]; + } + TestState[channel].output_state=mode; + if ((error_num=Error_check(TestState))) { + return error_num; + } + + /* suppress triggering during relay bounce */ + /* set gate bit high, if not already high */ + + for (i=0; i<(globals.Flash.ChanKey_frequency?globals.Flash.channels:1); ++i) + if (globals.ChannelState[i].trigger_source!=source_hold) { + bus_setpin(O_GATE, 1); + } + + if (mode==output_on || !globals.Flash.on_off_used) { + /* do not permit enabling of output during overload condition */ + if ( (I2C_Read(PCF8574A+Button_Press_Port) & 0x40) == 0) { + return Overload_Detected; + } + + bus_setpin(OUTPUT_RELAY, 1); /* turn output on */ + bus_setpin(PW_ENABLE, 1); /* enable PW circuit */ + globals.Timers.last_activity_at[channel] = sec_timer(); + } else { + bus_setpin(PW_ENABLE, 0); /* disable PW circuit */ + bus_setpin(OUTPUT_RELAY, 0); /* turn output off */ + globals.Timers.last_activity_at[channel] = 0L; + } + + if (globals.ChannelState[channel].output_state!=mode) { + globals.Changes.update_output=YES; + } + + globals.ChannelState[channel].output_state=mode; + Set_Update_Chans(); + + + /* keep trigger suppressed during output relay update */ + /* trigger gate will be released at end of following Main_update_shift_registers(); */ + + return OK; +} + + +int Set_Trig_Source(int channel,int mode) +{ + int gate_mode; + int i; + int error_num; + int use_ab_mode; + int reset_freq; + float use_freq; + + /* abandon if high channel selected by user but not enabled by firmware */ + if (channel && !globals.Flash.ChanKey_trigger_source) { + return InvalidChannel; + } + + use_ab_mode = globals.ChannelState[channel].ab_mode; + use_freq = globals.ChannelState[channel].frequency; + reset_freq = 0; + + if (globals.ChannelState[channel].trigger_source!=mode) { + for (i=0; i<max_channels; ++i) { + TestState[i]=globals.ChannelState[i]; + } + TestState[channel].trigger_source = mode; + + if ((globals.ChannelState[channel].trigger_source != source_hold) && (mode != source_hold)) { + reset_freq = 1; + use_freq = globals.Flash.min_freq[channel]; + use_ab_mode = pw_normal; + } + + TestState[channel].ab_mode = use_ab_mode; + TestState[channel].frequency = use_freq; + + if ((error_num=Error_check(TestState))) { + return error_num; + } + + if (reset_freq) { + Set_frequency(0,0,0,channel,use_freq); + Set_Pwmode(channel,use_ab_mode); + } + + globals.Changes.update_freq=YES; + } + + globals.ChannelState[channel].trigger_source=mode; + Set_Update_Chans(); + + + /* Function generator operate in the "external" mode only. The internal oscillator is never used. */ + if (globals.Flash.is_func_gen[channel]) { + mode=source_external; + } + + + if (mode==source_internal) { + gate_mode=0; + /* frequency is automatically lowered if pw or delay have been changed to conflicting */ + /* settings while in a non-internal mode */ + globals.Registers.shift_reg_out[0] = (globals.Registers.shift_reg_out[0] & 0xe3) | 0; + } else if (mode==source_external) { + gate_mode=0; + globals.ChannelState[channel].hold_setting=hold_width; + globals.Registers.shift_reg_out[0] = (globals.Registers.shift_reg_out[0] & 0xe3) | 3<<2; + } else if (mode==source_manual) { + gate_mode=0; + globals.ChannelState[channel].hold_setting=hold_width; + globals.Registers.shift_reg_out[0] = (globals.Registers.shift_reg_out[0] & 0xe3) | 1<<2; + } else if (mode==source_hold) { + gate_mode=1; + } else if (mode==source_immediate) { + if (gate_mode==1) { + globals.Registers.shift_reg_out[0] = (globals.Registers.shift_reg_out[0] & 0xe3) | 4<<2; + Main_update_shift_registers(); + gate_mode=0; /* allow triggering */ + bus_setpin(O_GATE, gate_mode); + globals.Registers.shift_reg_out[0] = (globals.Registers.shift_reg_out[0] & 0xe3) | 7<<2; + } else { + globals.Registers.shift_reg_out[0] = (globals.Registers.shift_reg_out[0] & 0xe3) | 4<<2; + Main_update_shift_registers(); + globals.Registers.shift_reg_out[0] = (globals.Registers.shift_reg_out[0] & 0xe3) | 7<<2; + } + } + + Main_update_shift_registers(); + bus_setpin(O_GATE, gate_mode); /* change gate only after new source set */ + + return OK; +} + + +int Set_Gate_Sync(int channel,int mode) +{ + /* abandon if high channel selected by user but not enabled by firmware */ + if (channel && !globals.Flash.ChanKey_gate_type) { + return InvalidChannel; + } + + if (mode==gate_sync) { + globals.Registers.shift_reg_out[0] = (globals.Registers.shift_reg_out[0] & 0xfe) | 0; + } else { + globals.Registers.shift_reg_out[0] = (globals.Registers.shift_reg_out[0] & 0xfe) | 1; + } + + if (globals.ChannelState[channel].gate_type!=mode) { + globals.Changes.update_gate=YES; + } + + globals.ChannelState[channel].gate_type=mode; + Set_Update_Chans(); + + return OK; +} + + +int Set_Gate_Level(int channel,int mode) +{ + /* abandon if high channel selected by user but not enabled by firmware */ + if (channel && !globals.Flash.ChanKey_gate_level) { + return InvalidChannel; + } + + if (mode==0) { + globals.Registers.shift_reg_out[0] = (globals.Registers.shift_reg_out[0] & 0xfd) | 0x02; + } else { + globals.Registers.shift_reg_out[0] = (globals.Registers.shift_reg_out[0] & 0xfd) | 0; + } + + if (globals.ChannelState[channel].gate_level!=mode) { + globals.Changes.update_gate=YES; + } + + globals.ChannelState[channel].gate_level=mode; + Set_Update_Chans(); + + return OK; +} + + +int start_gate_override () +{ + /* just set gate bit high, if not already high */ + int i; + for (i=0; i<(globals.Flash.ChanKey_frequency?globals.Flash.channels:1); ++i) + if (globals.ChannelState[i].trigger_source!=source_hold) { + bus_setpin(O_GATE, 1); + } +} + + +int stop_gate_override () +{ + /* release gate (if not in hold mode) */ + int i; + for (i=0; i<(globals.Flash.ChanKey_frequency?globals.Flash.channels:1); ++i) + if (globals.ChannelState[i].trigger_source!=source_hold) { + bus_setpin(O_GATE, 0); + } +} + + +int Set_zout(int channel,int setting,int really_for_zout) +{ + int update_zout_slowly; + + /* abandon if high channel selected by user but not enabled by firmware */ + if (channel && !globals.Flash.ChanKey_zout) { + return InvalidChannel; + } + + update_zout_slowly=NO; + + /* The Zout hardware and software is controlled by PW commands in the AVPP. */ + /* This mode is signalled by really_for_zout=0 */ + + /* first channel uses dedicated Zout line */ + if (channel==0) { + if (setting!=globals.Flash.zout_max[channel]) { + globals.Registers.shift_reg_out[3] |= (long)0x01000; /* power zout relay */ + } else { + globals.Registers.shift_reg_out[3] &= (long)0xfefff; /* de-power zout relay */ + } + } + + /* second channel uses dedicated xtra-rly2 line */ + if (channel==1) { + if (setting!=globals.Flash.zout_max[channel]) { + globals.Registers.shift_reg_out[2] |= (long)0x10000; /* power zout relay */ + } else { + globals.Registers.shift_reg_out[2] &= (long)0xeffff; /* de-power zout relay */ + } + } + + if (setting!=globals.ChannelState[channel].zout) { + globals.Changes.update_zout=YES; + + /* allow extra time for cap banks to discharge if going from + 50 Ohms to 2 Ohm Zout */ + if (setting<globals.ChannelState[channel].zout) { + update_zout_slowly=YES; + } + } + + globals.ChannelState[channel].zout=setting; + Set_Update_Chans(); + + /* Skip this stuff if this is actually for AVPP PW control */ + if (really_for_zout) { + if (globals.Changes.update_zout) { + /* change of Zout may require change in amplitude controls */ + Set_Amplitude(0,0,0,0,0,0,channel,globals.ChannelState[channel].amplitude,0); + } + + if (update_zout_slowly) { + globals.Timers.Relay_Switching_Delay_in_Milliseconds=(long) (1000L * globals.Flash.extended_relay_delay_in_sec); + } + } + + return OK; +} + + +int Set_Load(int channel, float value) +{ + int error_num; + + /* abandon if high channel selected by user but not enabled by firmware */ + if (channel && !globals.Flash.ChanKey_load_type) { + return InvalidChannel; + } + + if (globals.Flash.enable_avrq_extra_ampls) { + return Set_avrq_res(0,0,0,channel,value); + } + + /* duty cycle check */ + int i; + for (i=0; i<max_channels; ++i) { + TestState[i]=globals.ChannelState[i]; + } + TestState[channel].load_type=value; + if ((error_num=Error_check(TestState))) { + return error_num; + } + + if (globals.ChannelState[channel].load_type!=value) { + globals.Changes.update_load=YES; + } + + globals.ChannelState[channel].load_type=value; + Set_Update_Chans(); + + if (globals.Changes.update_load) { + /* change of load may require change in amplitude controls */ + Set_Amplitude(0,0,0,0,0,0,channel,globals.ChannelState[channel].amplitude,0); + } + + return OK; +} + + +int Set_EA(int channel,int mode) +{ + long amplify_mask, ea_mask; + char set_DAC_to_zero; + + /* abandon if high channel selected by user but not enabled by firmware */ + if (channel && !globals.Flash.ChanKey_amplitude) { + return InvalidChannel; + } + + if (!(globals.Flash.ea_enabled[channel]) && (mode==amp_mode_ea) ) { + return SyntaxError; + } + if (!(globals.Flash.ext_amplify_enabled[channel]) && (mode==amp_mode_amplify) ) { + return SyntaxError; + } + + set_DAC_to_zero=YES; + if (mode==amp_mode_amplify) { + set_DAC_to_zero=NO; + } + + /* set to max amplitude range when switching from normal to EA or Ext Amplify */ + if ( (globals.ChannelState[channel].amp_mode==amp_mode_normal) && (mode!=amp_mode_normal) ) { + if (globals.ChannelState[channel].amplitude<0.0) + Set_Amplitude(0,0,0,0,0,set_DAC_to_zero,channel, + globals.ChannelState[channel].amplitude=globals.Flash.min_ampl[channel],0); /* most negative */ + else + Set_Amplitude(0,0,0,0,0,set_DAC_to_zero,channel, + globals.ChannelState[channel].amplitude=globals.Flash.max_ampl[channel],0); /* most positive */ + } + + + /* protect when switching from EA or Ext Amplify back to normal */ + if ( (globals.ChannelState[channel].amp_mode!=amp_mode_normal) && (mode==amp_mode_normal) ) { + if (globals.ChannelState[channel].amplitude<0.0) { + Set_Amplitude(0,0,0,0,0,0,channel,globals.ChannelState[channel].amplitude=-smallest_allowed_number,0); + } else { + Set_Amplitude(0,0,0,0,0,0,channel,globals.ChannelState[channel].amplitude=smallest_allowed_number,0); + } + } + + + if (globals.Flash.ea_enabled[channel]) { + int ea_reg; + + if (!channel) { + ea_mask = 0x10000; + ea_reg = 3; + } else { + ea_mask = ((long) 1) << (globals.Flash.ea_xtra_rly[channel]+14); + ea_reg = 2; + } + + if (mode==amp_mode_ea) { + globals.Registers.shift_reg_out[ea_reg] |= ea_mask; + } else { + globals.Registers.shift_reg_out[ea_reg] &= ((long)(0xfffff - ea_mask)); + } + } + + + if (globals.Flash.ext_amplify_enabled[channel]) { + amplify_mask = ((long) 1) << (globals.Flash.ext_amplify_xtra_rly[channel]+14); + + if (mode==amp_mode_amplify) { + globals.Registers.shift_reg_out[2] |= amplify_mask; + } else { + globals.Registers.shift_reg_out[2] &= ((long)(0xfffff - amplify_mask)); + } + } + + + if (globals.ChannelState[channel].amp_mode!=mode) { + globals.Changes.update_amp=YES; + } + + globals.ChannelState[channel].amp_mode=mode; + Set_Update_Chans(); + + return OK; +} + + +int Set_AdvDel(int channel,int setting) +{ + if (setting==to_Advance) { + globals.Registers.shift_reg_out[0] |= 64L; /* turn advance on */ + } else { + globals.Registers.shift_reg_out[0] &= 255L-64L; /* turn delay on */ + } + + Set_Update_Chans(); + + return OK; +} + + +int Set_Pwmode(int channel,int mode) +{ + /* abandon if high channel selected by user but not enabled by firmware */ + if (channel && !globals.Flash.ChanKey_pw) { + return InvalidChannel; + } + + if (!(globals.Flash.ab_mode_allowed[channel]) && (mode==pw_in_out)) { + return SyntaxError; + } + + if ((mode==pw_in_out) && (globals.ChannelState[channel].trigger_source!=source_external)) { + return AB_Mode_Error; + } + + if (globals.ChannelState[channel].ab_mode!=mode) { + globals.Changes.update_pw=YES; + } + + globals.ChannelState[channel].ab_mode=mode; + + Set_Mux(0); + Set_Update_Chans(); + + return OK; +} + + +int Set_Update_Chans(void) +{ + int num_of_chan; + + num_of_chan=globals.Flash.channels; + if (num_of_chan==0) { + num_of_chan=1; + } + + if (num_of_chan>1) { + int i; + if (!globals.Flash.ChanKey_frequency) for (i=1; i<num_of_chan; ++i) { + globals.ChannelState[i].frequency=globals.ChannelState[0].frequency; + } + if (!globals.Flash.ChanKey_delay) for (i=1; i<num_of_chan; ++i) { + globals.ChannelState[i].delay=globals.ChannelState[0].delay; + } + if (!globals.Flash.ChanKey_pw) for (i=1; i<num_of_chan; ++i) { + globals.ChannelState[i].pw=globals.ChannelState[0].pw; + } + if (!globals.Flash.ChanKey_amplitude) for (i=1; i<num_of_chan; ++i) { + globals.ChannelState[i].amplitude=globals.ChannelState[0].amplitude; + } + if (!globals.Flash.ChanKey_offset) for (i=1; i<num_of_chan; ++i) { + globals.ChannelState[i].offset=globals.ChannelState[0].offset; + } + + if (!globals.Flash.ChanKey_Curr_Mon_value) for (i=1; i<num_of_chan; ++i) { + globals.ChannelState[i].Curr_Mon_value=globals.ChannelState[0].Curr_Mon_value; + } + if (!globals.Flash.ChanKey_Curr_Mon_offset) for (i=1; i<num_of_chan; ++i) { + globals.ChannelState[i].Curr_Mon_offset=globals.ChannelState[0].Curr_Mon_offset; + } + + if (!globals.Flash.ChanKey_zout) for (i=1; i<num_of_chan; ++i) { + globals.ChannelState[i].zout=globals.ChannelState[0].zout; + } + if (!globals.Flash.ChanKey_hold_setting) for (i=1; i<num_of_chan; ++i) { + globals.ChannelState[i].hold_setting=globals.ChannelState[0].hold_setting; + } + if (!globals.Flash.ChanKey_double_pulse) for (i=1; i<num_of_chan; ++i) { + globals.ChannelState[i].double_pulse=globals.ChannelState[0].double_pulse; + } + if (!globals.Flash.ChanKey_ab_mode) for (i=1; i<num_of_chan; ++i) { + globals.ChannelState[i].ab_mode=globals.ChannelState[0].ab_mode; + } + if (!globals.Flash.ChanKey_func_mode) for (i=1; i<num_of_chan; ++i) { + globals.ChannelState[i].func_mode=globals.ChannelState[0].func_mode; + } + if (!globals.Flash.ChanKey_polarity) for (i=1; i<num_of_chan; ++i) { + globals.ChannelState[i].polarity=globals.ChannelState[0].polarity; + } + if (!globals.Flash.ChanKey_output_state) for (i=1; i<num_of_chan; ++i) { + globals.ChannelState[i].output_state=globals.ChannelState[0].output_state; + } + if (!globals.Flash.ChanKey_gate_type) for (i=1; i<num_of_chan; ++i) { + globals.ChannelState[i].gate_type=globals.ChannelState[0].gate_type; + } + if (!globals.Flash.ChanKey_trigger_source) for (i=1; i<num_of_chan; ++i) { + globals.ChannelState[i].trigger_source=globals.ChannelState[0].trigger_source; + } + if (!globals.Flash.ChanKey_amp_mode) for (i=1; i<num_of_chan; ++i) { + globals.ChannelState[i].amp_mode=globals.ChannelState[0].amp_mode; + } + if (!globals.Flash.ChanKey_gate_level) for (i=1; i<num_of_chan; ++i) { + globals.ChannelState[i].gate_level=globals.ChannelState[0].gate_level; + } + if (!globals.Flash.ChanKey_load_type) for (i=1; i<num_of_chan; ++i) { + globals.ChannelState[i].load_type=globals.ChannelState[0].load_type; + } + if (!globals.Flash.ChanKey_test_delay_mode) for (i=1; i<num_of_chan; ++i) { + globals.ChannelState[i].test_delay_mode=globals.ChannelState[0].test_delay_mode; + } + if (!globals.Flash.ChanKey_os_mode) for (i=1; i<num_of_chan; ++i) { + globals.ChannelState[i].os_mode=globals.ChannelState[0].os_mode; + } + if (!globals.Flash.ChanKey_Burst_Count) for (i=1; i<num_of_chan; ++i) { + globals.ChannelState[i].burst_count=globals.ChannelState[0].burst_count; + } + if (!globals.Flash.ChanKey_Burst_Time) for (i=1; i<num_of_chan; ++i) { + globals.ChannelState[i].burst_time=globals.ChannelState[0].burst_time; + } + if (!globals.Flash.ChanKey_rise_time) for (i=1; i<num_of_chan; ++i) { + globals.ChannelState[i].rise_time=globals.ChannelState[0].rise_time; + } + if (!globals.Flash.ChanKey_slew) for (i=1; i<num_of_chan; ++i) { + globals.ChannelState[i].slew=globals.ChannelState[0].slew; + } + + for (i=1; i<num_of_chan; ++i) { + globals.ChannelState[i].vcc1=globals.ChannelState[0].vcc1; + globals.ChannelState[i].vcc2=globals.ChannelState[0].vcc2; + globals.ChannelState[i].vlogic=globals.ChannelState[0].vlogic; + } + + } + + return OK; +} + + +int Set_Amp_Calib(int channel,float meas_ampl) +{ + float change_ratio; + int i,status; + int point_found,relay_range,UseNegData,entry,word_out,actual_pol; + int eprom_loc; + + /* use for all channels */ + + /* get current range and polarity settings */ + Set_VI_Control(pwl_ampl_values,channel,globals.ChannelState[channel].amplitude,&point_found, + &relay_range,&UseNegData,&entry,&word_out,&actual_pol); + + if (fabs(globals.ChannelState[channel].amplitude)<globals.Flash.ampl_zero_equiv[channel]) { + return NeedNonZeroAmpl; + } + + change_ratio = meas_ampl/globals.ChannelState[channel].amplitude; + + if (change_ratio>1.3 || change_ratio < 0.7) { + return CalibrationPercentError; + } + + for (i=0; i<10; ++i) + globals.Flash.ampl_pwl_amp[channel][relay_range][UseNegData][i]= + globals.Flash.ampl_pwl_amp[channel][relay_range][UseNegData][i]*change_ratio; + + + /* see if new data prevents min/max from being obtained */ + status=Check_MinMax_Cal(channel,pwl_ampl_values); + + if (status) { + /* revert to original calibration */ + for (i=0; i<10; ++i) + globals.Flash.ampl_pwl_amp[channel][relay_range][UseNegData][i]= + globals.Flash.ampl_pwl_amp[channel][relay_range][UseNegData][i]/change_ratio; + } + + eprom_loc = (char *) &(globals.Flash.ampl_pwl_amp) - (char *) &(globals.Flash.flash_start); + writeUserBlock(eprom_loc,&globals.Flash.flash_start + eprom_loc,sizeof(globals.Flash.ampl_pwl_amp)); + + return status; +} + + +int Set_Mon_Calib(int channel,float meas_ampl) +{ + float change_ratio; + int point_found,relay_range,UseNegData,entry,word_out,actual_pol,eprom_loc; + + /* use for all channels */ + + /* get current range and polarity settings */ + Set_VI_Control(pwl_ampl_values,channel,globals.ChannelState[channel].amplitude,&point_found, + &relay_range,&UseNegData,&entry,&word_out,&actual_pol); + + if (fabs(globals.ChannelState[channel].Curr_Mon_value)< (5.0 * globals.Flash.monitor_step[channel])) { + return NeedNonZeroAmpl; + } + + if (globals.Flash.sep_posneg_mon_ratio[channel] && globals.ChannelState[channel].amplitude<0.0) { + UseNegData=1; + } else { + UseNegData=0; + } + + change_ratio = meas_ampl/globals.ChannelState[channel].Curr_Mon_value; + + if (change_ratio>1.3 || change_ratio < 0.7) { + return CalibrationPercentError; + } + + globals.Flash.mon_vi_ratio[channel][relay_range][UseNegData]=globals.Flash.mon_vi_ratio[channel][relay_range][UseNegData]/change_ratio; + + eprom_loc = (char *) &(globals.Flash.mon_vi_ratio) - (char *) &(globals.Flash.flash_start); + writeUserBlock(eprom_loc,&globals.Flash.flash_start + eprom_loc,sizeof(globals.Flash.mon_vi_ratio)); + + return OK; +} + + +int Set_Logic_Level(int channel,int mode) +{ + /* first channel uses dedicated Zout line */ + if (channel==0) { + if (mode==logic_ecl) { + globals.Registers.shift_reg_out[3] |= (long)0x04000; /* power zout relay */ + } else { + globals.Registers.shift_reg_out[3] &= (long)0xfbfff; /* de-power zout relay */ + } + } + + /* second channel uses xtra-rly3 */ + if (channel==1) { + if (mode==logic_ecl) { + globals.Registers.shift_reg_out[2] |= (long)0x20000; /* power zout relay */ + } else { + globals.Registers.shift_reg_out[2] &= (long)0xdffff; /* de-power zout relay */ + } + } + + + if (globals.ChannelState[channel].logic_level!=mode) { + globals.Changes.update_logic_level=YES; + } + + globals.ChannelState[channel].logic_level=mode; + Set_Update_Chans(); + + return OK; +} + + +int Set_OS_Calib(int channel,float meas_ampl) +{ + float change_ratio; + int i,status,eprom_loc; + + if (fabs(globals.ChannelState[channel].offset)<globals.Flash.ampl_zero_equiv[channel]) { + return NeedNonZeroAmpl; + } + + change_ratio = meas_ampl/globals.ChannelState[channel].offset; + + if (change_ratio>1.3 || change_ratio < 0.7) { + return CalibrationPercentError; + } + + for (i=0; i<10; ++i) { + globals.Flash.os_pwl_amp[channel][0][0][i]=globals.Flash.os_pwl_amp[channel][0][0][i]*change_ratio; + } + + + /* see if new data prevents min/max from being obtained */ + status=Check_MinMax_Cal(channel,pwl_os_values); + + if (status) { + /* revert to original calibration */ + for (i=0; i<10; ++i) { + globals.Flash.os_pwl_amp[channel][0][0][i]=globals.Flash.os_pwl_amp[channel][0][0][i]/change_ratio; + } + } + + eprom_loc = (char *) &(globals.Flash.os_pwl_amp) - (char *) &(globals.Flash.flash_start); + writeUserBlock(eprom_loc,&globals.Flash.flash_start + eprom_loc,sizeof(globals.Flash.os_pwl_amp)); + + return status; +} + + +float Get_PW_shift(int channel) +{ + int UseNegData; + + UseNegData = 0; + if (globals.ChannelState[channel].amplitude < 0.0) { + UseNegData = 1; + } + + return globals.Flash.pulse_width_pol_tweak[channel][UseNegData]; +} + + +int Set_PW_shift(int channel,float new_shift) +{ + float orig_shift; + int UseNegData, status, eprom_loc; + + UseNegData = 0; + if (globals.ChannelState[channel].amplitude < 0.0) { + UseNegData = 1; + } + + orig_shift = globals.Flash.pulse_width_pol_tweak[channel][UseNegData]; + globals.Flash.pulse_width_pol_tweak[channel][UseNegData] = new_shift; + + status=Check_MinMax_Cal(channel,pwl_pw_values); + + if (status) { + globals.Flash.pulse_width_pol_tweak[channel][UseNegData] = orig_shift; + return status; + } + + eprom_loc = (char *) &(globals.Flash.pulse_width_pol_tweak[channel][UseNegData]) - (char *) &(globals.Flash.flash_start); + writeUserBlock(eprom_loc,&globals.Flash.flash_start + eprom_loc,sizeof(globals.Flash.pulse_width_pol_tweak[channel][UseNegData])); + + return Set_Pw(0,0,0,channel,globals.ChannelState[channel].pw,0); +} + + +int Set_Route(int channel, int module, int mode) +{ + int error_num; + int word_out; + + if (channel && !globals.Flash.ChanKey_route) { + return InvalidChannel; + } + + if (!globals.Flash.routing_required[channel]) { + return Unrecognized; + } + + if (module >= globals.Flash.routing_required[channel]) { + return IllegalParameter; + } + + if ((mode < 1) || (mode > globals.Flash.routing_max_pins[channel])) { + return OutOfRange; + } + + int i; + for (i=0; i<max_channels; ++i) { + TestState[i]=globals.ChannelState[i]; + } + + if (globals.Flash.routing_max_pins[channel] > 2) { + if (module == ROUTE_PRIMARY) { + TestState[channel].route_primary=mode; + if ((error_num=Error_check(TestState))) { + return error_num; + } + + if (mode!=globals.ChannelState[channel].route_primary) { + globals.Changes.update_routes=YES; + 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.Changes.update_routes=YES; + 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) { + globals.Registers.shift_reg_out[2] |= (long)0x08000; /* power zout relay */ + } else { + globals.Registers.shift_reg_out[2] &= (long)0xf7fff; /* de-power zout relay */ + } + 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) { + globals.Registers.shift_reg_out[2] |= (long)0x40000; + } else { + globals.Registers.shift_reg_out[2] &= ((long)(0xfffff - 0x40000)); + } + + if (globals.ChannelState[channel].os_mode!=mode) { + globals.Changes.update_os=YES; + } + + globals.ChannelState[channel].os_mode=mode; + Set_Update_Chans(); + Main_update_shift_registers(); + + return OK; +} + + +int Set_Dly_Shr_Nom(int channel,int calibration_point_number) +{ + float temp1, temp2, ampl; + + Main_Rst(); + Main_update_shift_registers(); + + Set_frequency(0,0,0,globals.Flash.ChanKey_frequency?channel:0,5.0e0); + + if (globals.Flash.max_ampl[channel] > 0.0) { + ampl = globals.Flash.max_ampl[channel]; + } else { + ampl = globals.Flash.min_ampl[channel]; + } + Set_Amplitude(0,0,0,0,0,0,channel,ampl,0); + + + /* Set delay constants to zero, temporarily, for nulling purposes. */ + /* Restore the constants after the delay is set, so that the unit */ + /* will not suffer from an interrupted calibration. */ + + temp1=globals.Flash.propagation_delay[channel]; + temp2=globals.Flash.delay_shrink[channel]; + globals.Flash.propagation_delay[channel]=0.0; + globals.Flash.delay_shrink[channel]=0.0; + + if (calibration_point_number==1) { + Set_Delay(0,0,0,channel,75e-9); + } else { + Set_Delay(0,0,0,channel,-75e-9); + } + + /* user must set output on, when ready */ + + globals.Flash.propagation_delay[channel]=temp1; + globals.Flash.delay_shrink[channel]=temp2; + + Main_update_shift_registers(); + + Menu_Update_Display(); + + 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(eprom_loc,&globals.Flash.flash_start + eprom_loc,sizeof(globals.Flash.propagation_delay)); + + eprom_loc = (char *) &(globals.Flash.delay_shrink) - (char *) &(globals.Flash.flash_start); + writeUserBlock(eprom_loc,&globals.Flash.flash_start + eprom_loc,sizeof(globals.Flash.delay_shrink)); + + return OK; +} + + + +int Set_Cal_Nom(int channel,int calibration_point_number,int parameter, float *nom_val) +{ + int nominal_wordout,polarity,range,entry,status,num_in_range,num_of_ranges; + float nominal_val,other_setting,temp1,temp2; + int reset_state; + int disable_errors; + int true_channel; + + true_channel=channel; + + Get_VI_Rng_Info(parameter,channel,calibration_point_number,&range,&polarity,&entry,&num_in_range,&num_of_ranges); + status=reset_state=disable_errors=0; + + switch (parameter) { + case (pwl_ampl_values): + nominal_val=globals.Flash.ampl_pwl_amp[channel][range][polarity][entry]; + if (polarity) { + nominal_val=-nominal_val; + } + nominal_wordout=globals.Flash.ampl_pwl_Vc_norm4095[channel][range][polarity][entry]; + if (Set_Amplitude(0,0,0,0,0,0,channel,globals.Flash.min_ampl[channel],0) || Set_Amplitude(0,0,0,0,0,0,channel,globals.Flash.max_ampl[channel],0)) { + /* confirm that timing settings can support min/max ampl settings */ + return CalibrationTimingProblem; + } + /* auto-set output impedance, as required */ + if (globals.Flash.switchable_zout[channel]) { + /* if the cal point is in the top used range, then set Zout to min */ + if (range==num_of_ranges) { + Set_zout(channel,globals.Flash.zout_min[channel],1); + } else { + Set_zout(channel,globals.Flash.zout_max[channel],1); + } + } + break; + case (pwl_os_values): + nominal_val=globals.Flash.os_pwl_amp[channel][range][polarity][entry]; + nominal_wordout=globals.Flash.os_pwl_Vc_norm4095[channel][range][polarity][entry]; + break; + case pwl_pw_values: + nominal_val=globals.Flash.pw_pwl_time[channel][range][polarity][entry]; + nominal_wordout=globals.Flash.pw_pwl_Vc_norm4095[channel][range][polarity][entry]; + reset_state=disable_errors=1; + break; + case pwl_delay_values: + nominal_val=globals.Flash.delay_pwl_time[channel][range][polarity][entry]; + nominal_wordout=globals.Flash.delay_pwl_Vc_norm4095[channel][range][polarity][entry]; + reset_state=disable_errors=1; + break; + case pwl_period_values: + nominal_val=globals.Flash.period_pwl_time[channel][range][polarity][entry]; + nominal_wordout=globals.Flash.period_pwl_Vc_norm4095[channel][range][polarity][entry]; + reset_state=disable_errors=1; + break; + case pwl_burst_values: + nominal_val=globals.Flash.burst_pwl_time[channel][range][polarity][entry]; + nominal_wordout=globals.Flash.burst_pwl_Vc_norm4095[channel][range][polarity][entry]; + reset_state=disable_errors=1; + break; + case pwl_rise_time_values: + nominal_val=globals.Flash.rise_time_pwl_time[channel][range][polarity][entry]; + nominal_wordout=globals.Flash.rise_time_pwl_Vc_norm4095[channel][range][polarity][entry]; + reset_state=disable_errors=1; + break; + case pwl_slew_values: + nominal_val=globals.Flash.slew_pwl_time[channel][range][polarity][entry]; + nominal_wordout=globals.Flash.slew_pwl_Vc_norm4095[channel][range][polarity][entry]; + break; + case pwl_vcc1_values: + nominal_val=globals.Flash.vcc1_pwl_amp[0][0][0][entry]; + nominal_wordout=globals.Flash.vcc1_pwl_Vc_norm4095[0][0][0][entry]; + true_channel=0; + break; + case pwl_vcc2_values: + nominal_val=globals.Flash.vcc2_pwl_amp[0][0][0][entry]; + nominal_wordout=globals.Flash.vcc2_pwl_Vc_norm4095[0][0][0][entry]; + true_channel=0; + break; + case pwl_load_type_values: + nominal_val=globals.Flash.load_type_pwl_time[channel][range][polarity][entry]; + nominal_wordout=globals.Flash.load_type_pwl_Vc_norm4095[channel][range][polarity][entry]; + reset_state=0; + disable_errors=1; + break; + } + + /* just get nominal value, don't activate */ + if (nom_val) { + *nom_val = nominal_val; + return OK; + } + + /* For timing parameters, turn off output and error-checking, and change prf/pw */ + /* For ampl/os parameters, use existing timing and output state, and use error-checking */ + + if (reset_state) { + Main_Rst(); + Set_Output_State(true_channel,output_off); + Main_update_shift_registers(); + bus_setpin(PW_ENABLE, 1); /* enable PW circuit (but output relay still off) */ + } + + if (disable_errors) { + globals.Flash.fully_programmed=Being_Programmed; /* disable error-checking */ + Ctrl_PRF_Limiter(0); /* re-enables after *rst */ + } + + globals.Flags.extended_ampl_min_max=YES; + globals.ChannelState[true_channel].test_delay_mode=NO; + + switch (parameter) { + case (pwl_ampl_values): + status=Set_Amplitude(0,polarity,1,nominal_wordout,range,0,channel,nominal_val,0); + break; + case (pwl_os_values): + status=Set_Offset(0,1,nominal_wordout,range,channel,nominal_val); + break; + case pwl_pw_values: + other_setting=0.08/nominal_val; + if (other_setting > 500e3) { + other_setting=500e3; + } + + Set_Pol(channel,pol_norm); + + status=Set_frequency(0,0,0,globals.Flash.ChanKey_frequency?channel:0,other_setting); + + if (!status) { + status=Set_Pw(0,nominal_wordout,range,channel,nominal_val,0); + } + break; + case pwl_delay_values: + other_setting=0.08/nominal_val; + if (other_setting > 500e3) { + other_setting=500e3; + } + + /* set delay constants to zero, temporarily */ + temp1=globals.Flash.propagation_delay[channel]; + temp2=globals.Flash.delay_shrink[channel]; + globals.Flash.propagation_delay[channel]=0.0; + globals.Flash.delay_shrink[channel]=0.0; + + globals.ChannelState[channel].test_delay_mode=YES; + + status=Set_frequency(0,0,0,globals.Flash.ChanKey_frequency?channel:0,other_setting); + if (!status) { + status=Set_Delay(0,nominal_wordout,range,channel,nominal_val); + } + + globals.Flash.propagation_delay[channel]=temp1; + globals.Flash.delay_shrink[channel]=temp2; + + break; + case pwl_period_values: + other_setting=0.4*nominal_val; + if (other_setting>1.0) { + other_setting=1.0; /* reduce pulse width if required */ + } + + status=Set_frequency(0,nominal_wordout,range,channel,1.0/nominal_val); + + if (!status) { + status=Set_Pw(0,0,0,globals.Flash.ChanKey_pw?channel:0,other_setting,0); + } + break; + + case pwl_burst_values: + other_setting=20.0*nominal_val; + + if (other_setting>10.0) { + other_setting=10.0; /* reduce pulse width if required */ + } + status=Set_frequency(0,0,0,channel,1.0/other_setting); + Main_update_shift_registers(); + + if (!status) { + status=Set_Pw(0,0,0,globals.Flash.ChanKey_pw?channel:0,nominal_val,0); + } + if (!status) { + status=Set_Burst_Count(channel,4,nominal_val); + } + if (!status) { + status=Set_Burst_Time(0,nominal_wordout,range,channel,nominal_val); + } + + break; + + case pwl_rise_time_values: + other_setting=10; /* reduce test frequency if required */ + status=Set_frequency(0,0,0,globals.Flash.ChanKey_frequency?channel:0,other_setting); + if (!status) { + status=Set_rise_time(0,nominal_wordout,range,channel,nominal_val); + } + break; + + case pwl_slew_values: + other_setting=10; /* reduce test frequency if required */ + status=Set_frequency(0,0,0,globals.Flash.ChanKey_frequency?channel:0,other_setting); + if (!status) { + status=Set_slew(0,nominal_wordout,range,channel,nominal_val); + } + break; + + case pwl_load_type_values: + Set_avrq_res(0,nominal_wordout,range,channel,nominal_val); + break; + + case pwl_vcc1_values: + status=Set_Amplitude(0,polarity,1,nominal_wordout,range,0,channel,nominal_val,0); + break; + + case pwl_vcc2_values: + status=Set_Amplitude(0,polarity,1,nominal_wordout,range,0,channel,nominal_val,0); + break; + + } + + /* enables or disables delay diagnostic mode */ + Set_Mux(true_channel); + + Main_update_shift_registers(); + Menu_Update_Display(); + + /* re-enable error-checking */ + globals.Flash.fully_programmed=All_Programmed; + + /* rise time requires more values to be set before it can be measured */ + if (parameter != pwl_rise_time_values) { + globals.Flags.extended_ampl_min_max=NO; + } + + return status; +} + + +int Set_VI_Cal_Pnt(int parameter,int channel,int calibration_point_number,float cal_point) +{ + int polarity,range,entry,status,num_in_range,num_of_ranges,eprom_loc; + float nom_ampl; + int index; + int max_points,max_polarity,max_ranges; + float *pwl_amp; + int true_channel; + + true_channel=channel; + + Get_VI_Rng_Info(parameter,channel,calibration_point_number,&range,&polarity,&entry,&num_in_range,&num_of_ranges); + + max_points=points_in_range; + switch (parameter) { + case (pwl_ampl_values): + max_polarity=ampl_polarities; + max_ranges=ampl_ranges; + pwl_amp=&globals.Flash.ampl_pwl_amp[0][0][0][0]; + break; + case (pwl_os_values): + max_polarity=os_polarities; + max_ranges=os_ranges; + pwl_amp=&globals.Flash.os_pwl_amp[0][0][0][0]; + break; + case pwl_pw_values: + max_polarity=timing_polarities; + max_ranges=timing_ranges; + pwl_amp=&globals.Flash.pw_pwl_time[0][0][0][0]; + break; + case pwl_delay_values: + max_polarity=timing_polarities; + max_ranges=timing_ranges; + pwl_amp=&globals.Flash.delay_pwl_time[0][0][0][0]; + break; + case pwl_period_values: + max_polarity=timing_polarities; + max_ranges=timing_ranges; + pwl_amp=&globals.Flash.period_pwl_time[0][0][0][0]; + break; + case pwl_burst_values: + max_polarity=timing_polarities; + max_ranges=timing_ranges; + pwl_amp=&globals.Flash.burst_pwl_time[0][0][0][0]; + break; + case pwl_rise_time_values: + max_polarity=ampl_polarities; + max_ranges=ampl_ranges; + pwl_amp=&globals.Flash.rise_time_pwl_time[0][0][0][0]; + break; + case pwl_slew_values: + max_polarity=timing_polarities; + max_ranges=timing_ranges; + pwl_amp=&globals.Flash.slew_pwl_time[0][0][0][0]; + break; + case pwl_vcc1_values: + max_polarity=1; + max_ranges=1; + true_channel=0; + pwl_amp=&globals.Flash.vcc1_pwl_amp[0][0][0][0]; + break; + case pwl_vcc2_values: + max_polarity=1; + max_ranges=1; + true_channel=0; + pwl_amp=&globals.Flash.vcc2_pwl_amp[0][0][0][0]; + break; + case pwl_load_type_values: + max_polarity=load_type_polarities; + max_ranges=load_type_ranges; + pwl_amp=&globals.Flash.load_type_pwl_time[0][0][0][0]; + break; + } + index=true_channel*max_ranges*max_polarity*max_points + +range*max_polarity*max_points + +polarity*max_points + +entry; + + nom_ampl=pwl_amp[index]; + + switch (parameter) { + case (pwl_ampl_values): + if (globals.ChannelState[channel].amplitude<0.0) { + nom_ampl=-nom_ampl; + } + if ( (cal_point>0.0 && nom_ampl<0.0) || (cal_point<0.0 && nom_ampl>0.0)) { + return CalibrationPolarityError; + } + if (fabs(nom_ampl) < smallest_allowed_number) { + return CalibrationZeroError; + } + if (fabs( (cal_point-nom_ampl)/nom_ampl) >0.3) { + return CalibrationPercentError; + } + break; + + case (pwl_os_values): + /* check for divide-by-zero error */ + if (fabs(nom_ampl) > smallest_allowed_number) { + /* exclude relative changes of 30% or more */ + if (fabs( (cal_point-nom_ampl)/nom_ampl) > 0.3) { + return CalibrationPercentError; + } + } + /* exclude changes greater than 20% of entire range */ + if (fabs(cal_point-nom_ampl) > (0.2*fabs(globals.Flash.max_offset[channel]-globals.Flash.min_offset[channel]))) { + return CalibrationPercentError; + } + break; + + case pwl_pw_values: + case pwl_period_values: + case pwl_burst_values: + case pwl_rise_time_values: + case pwl_slew_values: + case pwl_load_type_values: + /* exclude relative changes of 45% or more unless < 15 ns */ + if ( (fabs(cal_point-nom_ampl)/nom_ampl > 0.45) && (fabs(cal_point-nom_ampl)>15e-9)) { + return CalibrationPercentError; + } + if (cal_point<0.0) { + return Negative_Not_Allowed; + } + break; + + case pwl_delay_values: + /* exclude relative changes of 45% or more unless < 15 ns */ + if ( (fabs(cal_point-nom_ampl)/nom_ampl > 0.45) && (fabs(cal_point-nom_ampl)>15e-9)) { + return CalibrationPercentError; + } + break; + + case pwl_vcc1_values: + case pwl_vcc2_values: + if (fabs(nom_ampl) > smallest_allowed_number) { + /* exclude relative changes of 30% or more */ + if (fabs( (cal_point-nom_ampl)/nom_ampl) > 0.3) { + return CalibrationPercentError; + } + } + break; + } + + + + status=OK; + globals.Flags.extended_ampl_min_max=YES; + + if (parameter==pwl_ampl_values) { + cal_point=fabs(cal_point); + } + + *(float *)(&pwl_amp[index])=cal_point; + + /* see if new point prevents min/max from being obtained */ + status=Check_MinMax_Cal(channel,parameter); + + if (status) { + /* revert to original value if required */ + *(float *)(&pwl_amp[index])=nom_ampl; + } + + Set_Cal_Nom(channel,calibration_point_number,parameter,NULL); + + Main_update_shift_registers(); + Menu_Update_Display(); + + eprom_loc = (char *) (&pwl_amp[index]) - (char *) &(globals.Flash.flash_start); + writeUserBlock(eprom_loc,&globals.Flash.flash_start + eprom_loc,sizeof(nom_ampl)); + + globals.Flags.extended_ampl_min_max=NO; + return status; +} + + +int Set_VI_Del_Cal(int parameter,int channel,int calibration_point_number) +{ + int polarity,range,entry,status,i,num_in_range,num_of_ranges; + float temp1[10]; + int temp2[10]; + int index; + int max_points,max_polarity,max_ranges; + int *pointer_int1; + float *pointer_float1; + int eprom_loc,size_of_float1,size_of_int1; + int true_channel; + + true_channel=channel; + + max_points=points_in_range; + + switch (parameter) { + case (pwl_ampl_values): + max_polarity=ampl_polarities; + max_ranges=ampl_ranges; + pointer_int1=&globals.Flash.ampl_pwl_Vc_norm4095[0][0][0][0]; + pointer_float1=&globals.Flash.ampl_pwl_amp[0][0][0][0]; + size_of_int1=sizeof(globals.Flash.ampl_pwl_Vc_norm4095); + size_of_float1=sizeof(globals.Flash.ampl_pwl_amp); + break; + case (pwl_os_values): + max_polarity=os_polarities; + max_ranges=os_ranges; + pointer_int1=&globals.Flash.os_pwl_Vc_norm4095[0][0][0][0]; + pointer_float1=&globals.Flash.os_pwl_amp[0][0][0][0]; + size_of_int1=sizeof(globals.Flash.os_pwl_Vc_norm4095); + size_of_float1=sizeof(globals.Flash.os_pwl_amp); + break; + case pwl_pw_values: + max_polarity=timing_polarities; + max_ranges=timing_ranges; + pointer_int1=&globals.Flash.pw_pwl_Vc_norm4095[0][0][0][0]; + pointer_float1=&globals.Flash.pw_pwl_time[0][0][0][0]; + size_of_int1=sizeof(globals.Flash.pw_pwl_Vc_norm4095); + size_of_float1=sizeof(globals.Flash.pw_pwl_time); + break; + case pwl_delay_values: + max_polarity=timing_polarities; + max_ranges=timing_ranges; + pointer_int1=&globals.Flash.delay_pwl_Vc_norm4095[0][0][0][0]; + pointer_float1=&globals.Flash.delay_pwl_time[0][0][0][0]; + size_of_int1=sizeof(globals.Flash.delay_pwl_Vc_norm4095); + size_of_float1=sizeof(globals.Flash.delay_pwl_time); + break; + case pwl_period_values: + max_polarity=timing_polarities; + max_ranges=timing_ranges; + pointer_int1=&globals.Flash.period_pwl_Vc_norm4095[0][0][0][0]; + pointer_float1=&globals.Flash.period_pwl_time[0][0][0][0]; + size_of_int1=sizeof(globals.Flash.period_pwl_Vc_norm4095); + size_of_float1=sizeof(globals.Flash.period_pwl_time); + break; + case pwl_burst_values: + max_polarity=timing_polarities; + max_ranges=timing_ranges; + pointer_int1=&globals.Flash.burst_pwl_Vc_norm4095[0][0][0][0]; + pointer_float1=&globals.Flash.burst_pwl_time[0][0][0][0]; + size_of_int1=sizeof(globals.Flash.burst_pwl_Vc_norm4095); + size_of_float1=sizeof(globals.Flash.burst_pwl_time); + break; + case pwl_rise_time_values: + max_polarity=ampl_polarities; + max_ranges=ampl_ranges; + pointer_int1=&globals.Flash.rise_time_pwl_Vc_norm4095[0][0][0][0]; + pointer_float1=&globals.Flash.rise_time_pwl_time[0][0][0][0]; + size_of_int1=sizeof(globals.Flash.rise_time_pwl_Vc_norm4095); + size_of_float1=sizeof(globals.Flash.rise_time_pwl_time); + break; + case pwl_slew_values: + max_polarity=timing_polarities; + max_ranges=timing_ranges; + pointer_int1=&globals.Flash.slew_pwl_Vc_norm4095[0][0][0][0]; + pointer_float1=&globals.Flash.slew_pwl_time[0][0][0][0]; + size_of_int1=sizeof(globals.Flash.slew_pwl_Vc_norm4095); + size_of_float1=sizeof(globals.Flash.slew_pwl_time); + break; + case pwl_vcc1_values: + max_polarity=1; + max_ranges=1; + pointer_int1=&globals.Flash.vcc1_pwl_Vc_norm4095[0][0][0][0]; + pointer_float1=&globals.Flash.vcc1_pwl_amp[0][0][0][0]; + size_of_int1=sizeof(globals.Flash.vcc1_pwl_Vc_norm4095); + size_of_float1=sizeof(globals.Flash.vcc1_pwl_amp); + true_channel=0; + break; + case pwl_vcc2_values: + max_polarity=1; + max_ranges=1; + pointer_int1=&globals.Flash.vcc2_pwl_Vc_norm4095[0][0][0][0]; + pointer_float1=&globals.Flash.vcc2_pwl_amp[0][0][0][0]; + size_of_int1=sizeof(globals.Flash.vcc2_pwl_Vc_norm4095); + size_of_float1=sizeof(globals.Flash.vcc2_pwl_amp); + true_channel=0; + break; + case pwl_load_type_values: + max_polarity=load_type_polarities; + max_ranges=load_type_ranges; + pointer_int1=&globals.Flash.load_type_pwl_Vc_norm4095[0][0][0][0]; + pointer_float1=&globals.Flash.load_type_pwl_time[0][0][0][0]; + size_of_int1=sizeof(globals.Flash.load_type_pwl_Vc_norm4095); + size_of_float1=sizeof(globals.Flash.load_type_pwl_time); + break; + } + + Get_VI_Rng_Info(parameter,channel,calibration_point_number,&range,&polarity,&entry,&num_in_range,&num_of_ranges); + + index=true_channel*max_ranges*max_polarity*max_points + +range*max_polarity*max_points + +polarity*max_points; + + + /* can not delete top or bottom of a range */ + if ((entry==0) || (entry==(num_in_range-1))) { + return CalibrationRangeError; + } + + /* backup calibration values */ + for (i=0; i<max_points; i++) { + temp1[i]=pointer_float1[index+i]; + temp2[i]=pointer_int1[index+i]; + } + + /* move the data, overwriting the deleted point */ + for (i=entry; i<max_points-1; i++) { + *(float *)(&pointer_float1[index+i])=pointer_float1[index+i+1]; + *(int *)(&pointer_int1[index+i])=pointer_int1[index+i+1]; + } + + *(float *)(&pointer_float1[index+points_in_range-1])=0.0; + *(int *)(&pointer_int1[index+points_in_range-1])=0; + + /* see if new point prevents min/max from being obtained */ + status=Check_MinMax_Cal(channel,parameter); + + if (status) { + /* revert to original calibration */ + for (i=0; i<max_points; i++) { + *(float *)(&pointer_float1[index+i])=temp1[i]; + *(int *)(&pointer_int1[index+i])=temp2[i]; + } + } + + Set_Amplitude(0,0,0,0,0,0,channel,0.0,0); + Main_update_shift_registers(); + + Set_Offset(0,0,0,0,channel,0.0); + Main_update_shift_registers(); + + Menu_Update_Display(); + + eprom_loc = (char *) pointer_int1 - (char *) &(globals.Flash.flash_start); + writeUserBlock(eprom_loc,&globals.Flash.flash_start + eprom_loc,size_of_int1); + + eprom_loc = (char *) pointer_float1 - (char *) &(globals.Flash.flash_start); + writeUserBlock(eprom_loc,&globals.Flash.flash_start + eprom_loc,size_of_float1); + + return status; +} + + +int Set_VI_Control(int parameter,int channel,float new_ampl,int *point_found,int *relay_range, + int *UseNegData,int *entry,int *word_out,int *actual_pol) +{ + float use_ampl,tweaked_use_ampl,fraction; + int i; + float min_os_in_range,max_os_in_range; + float min_ampl_in_range,max_ampl_in_range; + + int max_points,max_polarity,max_ranges; + float *pwl_amp; + int *pwl_vc; + int index,use_range; + int range_i,entry_i; + float inverse_word_out; + int timing_extrapolation_allowed; + int pw_polarity; + + float pw_distort_val; + float ampl_for_distort_calc; + + + int top_range_only; + int starting_range; + + int decreasing_values_allowed; + int reciprocal_relationship; + + int true_channel; + + true_channel=channel; + + top_range_only=0; + starting_range=0; + max_points=points_in_range; + timing_extrapolation_allowed=NO; + reciprocal_relationship=NO; + decreasing_values_allowed=NO; + min_os_in_range=max_os_in_range=0.0; + min_ampl_in_range=max_ampl_in_range=0.0; + + + switch (parameter) { + case pwl_ampl_values: + + if (globals.Flash.ampl_coupled_to_os[channel]) { + new_ampl+=globals.ChannelState[channel].offset; /* for 1011-style offset circuit */ + } + + max_polarity=ampl_polarities; + max_ranges=ampl_ranges; + pwl_vc=&globals.Flash.ampl_pwl_Vc_norm4095[0][0][0][0]; + pwl_amp=&globals.Flash.ampl_pwl_amp[0][0][0][0]; + if (new_ampl<0.0) { + *actual_pol=1; + *UseNegData=1; + } else { + *actual_pol=0; + *UseNegData=0; + } + if (globals.Flash.use_pos_ampl_data_only[channel] && new_ampl<0.0) { + *UseNegData=0; + } + use_ampl=fabs(new_ampl); + + /* use top amplitude range in AV-1011 if Zout=2 */ + top_range_only = globals.Flash.switchable_zout[channel] + && (globals.ChannelState[channel].zout==globals.Flash.zout_min[channel]); + + /* if high_impedance_mode the relay range is automatically set to 3 (for 1011, 1015 only), + if such a range exists */ + + if ( globals.Flash.switchable_load[channel] && + (globals.ChannelState[channel].load_type>globals.Flash.low_load_type[channel]) && + globals.Flash.ampl_pwl_Vc_norm4095[channel][3][*actual_pol][1] > 0) { + starting_range=3; + top_range_only=0; + } + + /* if 1011-OT offset is non-zero, the relay range is automatically set to 3 or 4 (for 1011, 1015 only) */ + + if (globals.Flash.ampl_coupled_to_os[channel] + && globals.Flash.switchable_zout[channel] + && (fabs(globals.ChannelState[channel].offset)>(globals.Flash.max_offset[channel]/max_v_dymanic_range))) { + starting_range=3; + } + + /* for AVMP-4-B: lower ampl range used for first PW range only */ + if (globals.Flash.use_high_ampl_ranges_for_high_pw_ranges[channel] && + ((globals.Registers.shift_reg_out[3] & 0x7f) > 0)) { + top_range_only=1; + } + + break; + case pwl_os_values: + decreasing_values_allowed=YES; /* allows Vc=0 to corresponds to most + OS, and Vc=10V to give most - OS */ + max_polarity=os_polarities; + max_ranges=os_ranges; + pwl_vc=&globals.Flash.os_pwl_Vc_norm4095[0][0][0][0]; + pwl_amp=&globals.Flash.os_pwl_amp[0][0][0][0]; + *UseNegData=0; + use_ampl=new_ampl; + break; + case pwl_pw_values: + max_polarity=timing_polarities; + max_ranges=timing_ranges; + pwl_vc=&globals.Flash.pw_pwl_Vc_norm4095[0][0][0][0]; + pwl_amp=&globals.Flash.pw_pwl_time[0][0][0][0]; + *UseNegData=0; + use_ampl=new_ampl; + + /* increase PW if required to compensate for series output diode turn-on time */ + if ( (globals.Flash.fully_programmed != Being_Programmed) + && (globals.Flash.distort_Y[channel] != 0.0) + && (fabs(globals.ChannelState[channel].offset)<fabs(globals.Flash.distort_max_os[channel])) ) { + + ampl_for_distort_calc = fabs(globals.ChannelState[channel].amplitude); + + /* clamping distortion above a certain amplitude */ + if (ampl_for_distort_calc > globals.Flash.distort_max_ampl[channel]) { + ampl_for_distort_calc = globals.Flash.distort_max_ampl[channel]; + } + + /* clamping distortion below "zero equiv" amplitude */ + if (ampl_for_distort_calc < globals.Flash.ampl_zero_equiv[channel]) { + ampl_for_distort_calc = globals.Flash.ampl_zero_equiv[channel]; + } + + /* prohit values of Y that would cause divide-by-zero error */ + if ( (-globals.Flash.distort_Y[channel]) > globals.Flash.ampl_zero_equiv[channel]) { + return PW_Distort_Error; + } + + pw_distort_val=globals.Flash.distort_Z[channel] + + globals.Flash.distort_X[channel] / (ampl_for_distort_calc+globals.Flash.distort_Y[channel]); + + /* calculate new actual PW */ + use_ampl=use_ampl+pw_distort_val; + } + + /* tweak depending on polarity */ + if (globals.ChannelState[channel].amplitude<0.0) { + pw_polarity = 1; + use_ampl=use_ampl+globals.Flash.pulse_width_pol_tweak[channel][pw_polarity]; + } else { + pw_polarity = 0; + use_ampl=use_ampl+globals.Flash.pulse_width_pol_tweak[channel][pw_polarity]; + } + + /* tweak depending on amplitude range (steps, not gradual like distort params */ + + if (fabs(globals.ChannelState[channel].amplitude) <= globals.Flash.pw_shift_below_this_ampl[channel]) { + use_ampl=use_ampl+globals.Flash.pw_shift_below_ampl_by[channel]; + } + + reciprocal_relationship=YES; + timing_extrapolation_allowed=YES; + break; + case pwl_delay_values: + max_polarity=timing_polarities; + max_ranges=timing_ranges; + pwl_vc=&globals.Flash.delay_pwl_Vc_norm4095[0][0][0][0]; + pwl_amp=&globals.Flash.delay_pwl_time[0][0][0][0]; + *UseNegData=0; + use_ampl=new_ampl; + reciprocal_relationship=YES; + timing_extrapolation_allowed=YES; + break; + case pwl_period_values: + max_polarity=timing_polarities; + max_ranges=timing_ranges; + pwl_vc=&globals.Flash.period_pwl_Vc_norm4095[0][0][0][0]; + pwl_amp=&globals.Flash.period_pwl_time[0][0][0][0]; + *UseNegData=0; + use_ampl=new_ampl; + timing_extrapolation_allowed=YES; + reciprocal_relationship=YES; + break; + case pwl_burst_values: + max_polarity=timing_polarities; + max_ranges=timing_ranges; + pwl_vc=&globals.Flash.burst_pwl_Vc_norm4095[0][0][0][0]; + pwl_amp=&globals.Flash.burst_pwl_time[0][0][0][0]; + *UseNegData=0; + use_ampl=new_ampl; + reciprocal_relationship=YES; + timing_extrapolation_allowed=YES; + break; + case pwl_rise_time_values: + max_polarity=ampl_polarities; + max_ranges=ampl_ranges; + pwl_vc=&globals.Flash.rise_time_pwl_Vc_norm4095[0][0][0][0]; + pwl_amp=&globals.Flash.rise_time_pwl_time[0][0][0][0]; + if (globals.ChannelState[channel].amplitude<0.0) { + *actual_pol=*UseNegData=1; + } else { + *actual_pol=*UseNegData=0; + } + use_ampl=new_ampl; + reciprocal_relationship=NO; + timing_extrapolation_allowed=NO; + break; + case pwl_slew_values: + max_polarity=timing_polarities; + max_ranges=timing_ranges; + pwl_vc=&globals.Flash.slew_pwl_Vc_norm4095[0][0][0][0]; + pwl_amp=&globals.Flash.slew_pwl_time[0][0][0][0]; + *UseNegData=0; + use_ampl=new_ampl; + reciprocal_relationship=NO; + timing_extrapolation_allowed=NO; + top_range_only=YES; /* higher drive voltage = lower ripple in slew */ + break; + case pwl_vcc1_values: + max_polarity=1; + max_ranges=1; + pwl_vc=&globals.Flash.vcc1_pwl_Vc_norm4095[0][0][0][0]; + pwl_amp=&globals.Flash.vcc1_pwl_amp[0][0][0][0]; + *UseNegData=0; + use_ampl=new_ampl; + true_channel=0; + break; + case pwl_vcc2_values: + max_polarity=1; + max_ranges=1; + pwl_vc=&globals.Flash.vcc2_pwl_Vc_norm4095[0][0][0][0]; + pwl_amp=&globals.Flash.vcc2_pwl_amp[0][0][0][0]; + *UseNegData=0; + use_ampl=new_ampl; + true_channel=0; + break; + case pwl_load_type_values: + max_polarity=load_type_polarities; + max_ranges=load_type_ranges; + pwl_vc=&globals.Flash.load_type_pwl_Vc_norm4095[0][0][0][0]; + pwl_amp=&globals.Flash.load_type_pwl_time[0][0][0][0]; + *UseNegData=0; + use_ampl=new_ampl; + reciprocal_relationship=YES; + timing_extrapolation_allowed=YES; + break; + } + + + /* if top_range_only=false, the first match (i.e. in the lowest range) is used. */ + /* if top_range_only=true, the last match (i.e. in the highest range) is used. */ + + *point_found=*relay_range=*entry=0; + *word_out=-1; + + tweaked_use_ampl = use_ampl; + + for (range_i=starting_range; (range_i<max_ranges) && (!*point_found || top_range_only); ++range_i) { /* use non-all-zero ranges */ +//printf ("range_i %d\r\n",range_i); + /* apply pw ampl/pol tweaks */ + if (parameter == pwl_pw_values) { + tweaked_use_ampl = use_ampl + globals.Flash.pw_range_pol_tweaks[channel][range_i][pw_polarity]; + } + + for (entry_i=0; (entry_i<max_points-1) && (!*point_found || top_range_only); ++entry_i) { +//printf ("entry_i %d\r\n",entry_i); + + index=true_channel*max_ranges*max_polarity*max_points + +(range_i)*max_polarity*max_points + +(*UseNegData)*max_points + +(entry_i); + + if ( (fabs(pwl_amp[index]-pwl_amp[index+1])>smallest_allowed_number) + && + ( ((tweaked_use_ampl>=pwl_amp[index]) && (tweaked_use_ampl<=pwl_amp[index+1])) + || + (decreasing_values_allowed && (tweaked_use_ampl<=pwl_amp[index]) && (tweaked_use_ampl>=pwl_amp[index+1])) /* for OS only */ + ) + ) { + *point_found=1; + + if ((parameter==pwl_ampl_values) && globals.Flash.ampl_os_ranges_related[channel]) { + for (i=0; i<points_in_range; i++) { + if (max_os_in_range<globals.Flash.os_pwl_amp[channel][range_i][0][i]) { + max_os_in_range=globals.Flash.os_pwl_amp[channel][range_i][0][i]; + } + if (min_os_in_range>globals.Flash.os_pwl_amp[channel][range_i][0][i]) { + min_os_in_range=globals.Flash.os_pwl_amp[channel][range_i][0][i]; + } + } + if ((globals.ChannelState[channel].offset<min_os_in_range) || (globals.ChannelState[channel].offset>max_os_in_range)) { + *point_found=0; /* try higher range if can't satisfy os and ampl both in this range */ + } } - 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 ((parameter==pwl_os_values) && globals.Flash.ampl_os_ranges_related[channel]) { + for (i=0; i<points_in_range; i++) { + if (max_ampl_in_range<globals.Flash.ampl_pwl_amp[channel][range_i][*UseNegData][i]) { + max_ampl_in_range=globals.Flash.ampl_pwl_amp[channel][range_i][*UseNegData][i]; + } + if (min_ampl_in_range>globals.Flash.ampl_pwl_amp[channel][range_i][*UseNegData][i]) { + min_ampl_in_range=globals.Flash.ampl_pwl_amp[channel][range_i][*UseNegData][i]; + } + } + if ((fabs(globals.ChannelState[channel].amplitude)<min_ampl_in_range) || (fabs(globals.ChannelState[channel].amplitude)>max_ampl_in_range)) { + *point_found=0; /* try higher range if can't satisfy os and ampl both in this range */ + } + } + + fraction = (tweaked_use_ampl-pwl_amp[index]) / (pwl_amp[index] - pwl_amp[index+1]); + *relay_range=range_i; + *entry=entry_i; + + /* check for linear voltage-controlled PW */ + if ( (parameter==pwl_pw_values) + && globals.Flash.volt_ctrl_pw[channel] + && (pwl_vc[index] < pwl_vc[index+1])) { + reciprocal_relationship=NO; + } - if (globals.Flash.max_burst_count[i]>1) { - Set_Burst_Count(i,globals.ChannelState[i].burst_count,globals.ChannelState[i].burst_time); + if (reciprocal_relationship) { + inverse_word_out = fraction * (1.0/((float) pwl_vc[index]) - 1.0/((float) pwl_vc[index+1])); + inverse_word_out+=1/((float) pwl_vc[index]); + *word_out=(int) (1.0/inverse_word_out); + } else { + *word_out = (int) ( fraction * (float) (pwl_vc[index] - pwl_vc[index+1]) ); + *word_out+=pwl_vc[index]; } + } + } + } + + /* The amplitude/os settings must lie within the calibration points. */ + /* However, the timing data is allowed to lie outside the upper end, */ + /* because the reciprocal relationship allows an asymptotic extension to infinity, */ + /* at the expense of lower resolution */ + + if (*word_out<0 && timing_extrapolation_allowed) { + *point_found=0; + use_range=-1; + + tweaked_use_ampl = use_ampl; - Set_Trig_Source(i,globals.ChannelState[i].trigger_source); + /* identify highest range that starts with a cal point less than the setting */ + for (range_i=0; range_i<max_ranges; ++range_i) { /* use non-all-zero ranges */ + if (parameter == pwl_pw_values) { + tweaked_use_ampl = use_ampl + globals.Flash.pw_range_pol_tweaks[channel][range_i][pw_polarity]; + } - Set_Amplitude(0,0,0,0,0,0,i,globals.ChannelState[i].amplitude,0); - Set_Offset(0,0,0,0,i,globals.ChannelState[i].offset); - Set_Output_State(i,globals.ChannelState[i].output_state); + index=true_channel*max_ranges*max_polarity*max_points + +(range_i)*max_polarity*max_points + +(*UseNegData)*max_points; + if (pwl_amp[index]>smallest_allowed_number && pwl_amp[index]<tweaked_use_ampl) { + use_range=range_i; + } + } + + if (use_range<0) { + return HardwareError; + } - Set_current_limit(0,i,globals.ChannelState[i].soft_current_limit); + for (entry_i=max_points-2; (entry_i>=0) && (!*point_found); --entry_i) { + index=true_channel*max_ranges*max_polarity*max_points + +(use_range)*max_polarity*max_points + +(*UseNegData)*max_points + +(entry_i); - Set_avrq_ampl(0,0,0,2,globals.ChannelState[i].vcc1); - Set_avrq_ampl(0,0,0,3,globals.ChannelState[i].vcc2); - Set_avrq_ampl(0,0,0,4,globals.ChannelState[i].vlogic); - */ - globals.do_check_settings=YES; /* check for conflicting settings */ + /* find the last two non-zero entries, and extrapolate from them */ + if ( (fabs(pwl_amp[index])>smallest_allowed_number) + && (fabs(pwl_amp[index+1])>smallest_allowed_number) + && (fabs(pwl_amp[index]-pwl_amp[index+1])>smallest_allowed_number) ) { + *point_found=1; + + fraction = (tweaked_use_ampl-pwl_amp[index]) / (pwl_amp[index] - pwl_amp[index+1]); + *relay_range=use_range; + *entry=entry_i; + + inverse_word_out = fraction * (1.0/((float) pwl_vc[index]) - 1.0/((float) pwl_vc[index+1])); + inverse_word_out+=1/((float) pwl_vc[index]); + *word_out=(int) (1.0/inverse_word_out); + } + } } + if (*word_out<0) { + *word_out=0; + return HardwareError; + } + + /* check for 12 or 13 bit overflow */ + if (*word_out>dac_max) { + *word_out=dac_max; /* just to prevent wandering bits */ + return HardwareError; + } -// Error_check(globals.ChannelState); /* establishes min/max values for queries, but reports no errors */ -// update_whole_main_menu=YES; -// Ctrl_PRF_Limiter(1); -// Menu_Clear_Buttons(); + return OK; } + + +int Set_VI_Add_Cal(int parameter,int channel,float cal_point) +{ + int point_found,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_float[points_in_range+1]; + float temp_x_float[points_in_range+1]; + int temp_x_int[points_in_range+1]; + + int index,actual_pol; + float *pointer_y_float; + int *pointer_x_int; + + int eprom_loc,size_of_y_float,size_of_x_int; + + int true_channel; + + true_channel = channel; + + range=polarity=0; + + max_points=points_in_range; + + abs_cal_point=cal_point; + + switch (parameter) { + case (pwl_ampl_values): + max_polarity=ampl_polarities; + max_ranges=ampl_ranges; + + pointer_x_int=&globals.Flash.ampl_pwl_Vc_norm4095[0][0][0][0]; + pointer_y_float=&globals.Flash.ampl_pwl_amp[0][0][0][0]; + size_of_x_int=sizeof(globals.Flash.ampl_pwl_Vc_norm4095); + size_of_y_float=sizeof(globals.Flash.ampl_pwl_amp); + + Set_VI_Control(pwl_ampl_values,channel,globals.ChannelState[channel].amplitude,&point_found, + &range,&polarity,&entry,&word_out,&actual_pol); + old_val=globals.ChannelState[channel].amplitude; + abs_cal_point=fabs(cal_point); /* ampl data is positive always */ + + /* special amplitude polarity check */ + + if ( (cal_point>0.0 && old_val<0.0) || (cal_point<0.0 && old_val>0.0)) { + return CalibrationPolarityError; + } + + break; + case (pwl_os_values): + max_polarity=os_polarities; + max_ranges=os_ranges; + pointer_x_int=&globals.Flash.os_pwl_Vc_norm4095[0][0][0][0]; + pointer_y_float=&globals.Flash.os_pwl_amp[0][0][0][0]; + size_of_x_int=sizeof(globals.Flash.os_pwl_Vc_norm4095); + size_of_y_float=sizeof(globals.Flash.os_pwl_amp); + + old_val=globals.ChannelState[channel].offset; + Set_VI_Control(pwl_os_values,channel,old_val,&point_found, + &range,&polarity,&entry,&word_out,&actual_pol); + break; + case pwl_pw_values: + max_polarity=timing_polarities; + max_ranges=timing_ranges; + pointer_x_int=&globals.Flash.pw_pwl_Vc_norm4095[0][0][0][0]; + pointer_y_float=&globals.Flash.pw_pwl_time[0][0][0][0]; + size_of_x_int=sizeof(globals.Flash.pw_pwl_Vc_norm4095); + size_of_y_float=sizeof(globals.Flash.pw_pwl_time); + old_val=globals.ChannelState[channel].pw; + Set_VI_Control(pwl_pw_values,channel,old_val,&point_found, + &range,&polarity,&entry,&word_out,&actual_pol); + break; + case pwl_delay_values: + max_polarity=timing_polarities; + max_ranges=timing_ranges; + pointer_x_int=&globals.Flash.delay_pwl_Vc_norm4095[0][0][0][0]; + pointer_y_float=&globals.Flash.delay_pwl_time[0][0][0][0]; + size_of_x_int=sizeof(globals.Flash.delay_pwl_Vc_norm4095); + size_of_y_float=sizeof(globals.Flash.delay_pwl_time); + old_val=globals.ChannelState[channel].delay; + Set_VI_Control(pwl_delay_values,channel,old_val,&point_found, + &range,&polarity,&entry,&word_out,&actual_pol); + break; + case pwl_period_values: + max_polarity=timing_polarities; + max_ranges=timing_ranges; + pointer_x_int=&globals.Flash.period_pwl_Vc_norm4095[0][0][0][0]; + pointer_y_float=&globals.Flash.period_pwl_time[0][0][0][0]; + size_of_x_int=sizeof(globals.Flash.period_pwl_Vc_norm4095); + size_of_y_float=sizeof(globals.Flash.period_pwl_time); + old_val=1.0/globals.ChannelState[channel].frequency; + Set_VI_Control(pwl_period_values,channel,old_val,&point_found, + &range,&polarity,&entry,&word_out,&actual_pol); + break; + case pwl_burst_values: + max_polarity=timing_polarities; + max_ranges=timing_ranges; + pointer_x_int=&globals.Flash.burst_pwl_Vc_norm4095[0][0][0][0]; + pointer_y_float=&globals.Flash.burst_pwl_time[0][0][0][0]; + size_of_x_int=sizeof(globals.Flash.burst_pwl_Vc_norm4095); + size_of_y_float=sizeof(globals.Flash.burst_pwl_time); + old_val=globals.ChannelState[channel].burst_time; + Set_VI_Control(pwl_burst_values,channel,old_val,&point_found, + &range,&polarity,&entry,&word_out,&actual_pol); + break; + case pwl_rise_time_values: + max_polarity=ampl_polarities; + max_ranges=ampl_ranges; + pointer_x_int=&globals.Flash.rise_time_pwl_Vc_norm4095[0][0][0][0]; + pointer_y_float=&globals.Flash.rise_time_pwl_time[0][0][0][0]; + size_of_x_int=sizeof(globals.Flash.rise_time_pwl_Vc_norm4095); + size_of_y_float=sizeof(globals.Flash.rise_time_pwl_time); + old_val=globals.ChannelState[channel].rise_time; + Set_VI_Control(pwl_rise_time_values,channel,old_val,&point_found, + &range,&polarity,&entry,&word_out,&actual_pol); + break; + case pwl_slew_values: + max_polarity=timing_polarities; + max_ranges=timing_ranges; + pointer_x_int=&globals.Flash.slew_pwl_Vc_norm4095[0][0][0][0]; + pointer_y_float=&globals.Flash.slew_pwl_time[0][0][0][0]; + size_of_x_int=sizeof(globals.Flash.slew_pwl_Vc_norm4095); + size_of_y_float=sizeof(globals.Flash.slew_pwl_time); + old_val=globals.ChannelState[channel].slew; + Set_VI_Control(pwl_slew_values,channel,old_val,&point_found, + &range,&polarity,&entry,&word_out,&actual_pol); + break; + case pwl_vcc1_values: + max_polarity=1; + max_ranges=1; + true_channel=0; + pointer_x_int=&globals.Flash.vcc1_pwl_Vc_norm4095[0][0][0][0]; + pointer_y_float=&globals.Flash.vcc1_pwl_amp[0][0][0][0]; + size_of_x_int=sizeof(globals.Flash.vcc1_pwl_Vc_norm4095); + size_of_y_float=sizeof(globals.Flash.vcc1_pwl_amp); + old_val=globals.ChannelState[true_channel].vcc1; + Set_VI_Control(pwl_vcc1_values,channel,old_val,&point_found, + &range,&polarity,&entry,&word_out,&actual_pol); + break; + case pwl_vcc2_values: + max_polarity=1; + max_ranges=1; + true_channel=0; + pointer_x_int=&globals.Flash.vcc2_pwl_Vc_norm4095[0][0][0][0]; + pointer_y_float=&globals.Flash.vcc2_pwl_amp[0][0][0][0]; + size_of_x_int=sizeof(globals.Flash.vcc2_pwl_Vc_norm4095); + size_of_y_float=sizeof(globals.Flash.vcc2_pwl_amp); + old_val=globals.ChannelState[true_channel].vcc2; + Set_VI_Control(pwl_vcc2_values,channel,old_val,&point_found, + &range,&polarity,&entry,&word_out,&actual_pol); + break; + case pwl_load_type_values: + max_polarity=load_type_polarities; + max_ranges=load_type_ranges; + pointer_x_int=&globals.Flash.load_type_pwl_Vc_norm4095[0][0][0][0]; + pointer_y_float=&globals.Flash.load_type_pwl_time[0][0][0][0]; + size_of_x_int=sizeof(globals.Flash.load_type_pwl_Vc_norm4095); + size_of_y_float=sizeof(globals.Flash.load_type_pwl_time); + old_val=globals.ChannelState[channel].load_type; + Set_VI_Control(pwl_load_type_values,channel,old_val,&point_found, + &range,&polarity,&entry,&word_out,&actual_pol); + break; + + } + + temp_y_float[max_points]=0.0; /* Adding a cal point may temporarily result in 11 data points. */ + temp_x_float[max_points]=0.0; /* One is eventually discarded. */ + temp_x_int[max_points]=0; + + + index=true_channel*max_ranges*max_polarity*max_points + +range*max_polarity*max_points + +polarity*max_points; + + + /* make a copy of the calibration data in this range */ + for (i=0; i<max_points; i++) { + temp_y_float[i]=pointer_y_float[index+i]; /* work on this data */ + temp_x_int[i]=pointer_x_int[index+i]; + } + + + /* check range for maximum value, and number of points */ + max_in_range=0.0; + total=max_points; + for (i=max_points-1; i>=0; --i) { + if ( fabs(temp_y_float[i])<smallest_allowed_number && (total==i+1)) { + --total; + } + if (fabs(temp_y_float[i])>max_in_range) { + max_in_range=fabs(temp_y_float[i]); + } + } + + + /* check for excessive deviation between old and new amplitudes at this range/word combination */ + if (fabs(cal_point-old_val) > max_in_range/3.0) { + return CalibrationPercentError; + } + /* check for overly-close data points */ + if ( word_out==temp_x_int[entry] ) { + return CalibrationClosenessError; + } + if ( word_out==temp_x_int[entry+1]) { + return CalibrationClosenessError; + } + + + + /* move old data over */ + for (i=max_points-1; i>entry; i--) { + temp_y_float[i+1]=temp_y_float[i]; + temp_x_int[i+1]=temp_x_int[i]; + } + + + /* add the new data */ + temp_y_float[entry+1]=abs_cal_point; + temp_x_int[entry+1]=word_out; + for (i=0; i<=max_points; ++i) { + temp_x_float[i]=(float) temp_x_int[i]; + } + + + /* delete a point, if required */ + if (total==max_points) { + least_integrated_error=1e18; + + for (i=1; i<max_points; i++) { /* end points are not considered for deletion */ + /* calculate interpolated amplitude, if this cal point was deleted */ + fraction = (temp_x_float[i]-temp_x_float[i-1]) / (temp_x_float[i+1]-temp_x_float[i-1]); + interp_ampl = temp_y_float[i-1] + (fraction * (temp_y_float[i+1]-temp_y_float[i-1])); + + /* calculate percent error at interpolated point */ + integrated_error = fabs( (interp_ampl-temp_y_float[i])/temp_y_float[i]); + + /* calculate intergrated error */ + integrated_error *= fabs( temp_y_float[i+1] - temp_y_float[i-1] ); + + if (integrated_error<least_integrated_error) { + delete_point=i; + least_integrated_error=integrated_error; + } + } + + /* delete the point that is most linear with the two around it */ + for (i=delete_point; i<max_points; i++) { + temp_y_float[i]=temp_y_float[i+1]; + temp_x_float[i]=temp_x_float[i+1]; + temp_x_int[i]=temp_x_int[i+1]; + } + } + + + /* store new calibration data for this range */ + for (i=0; i<max_points; i++) { + *(float *)(&pointer_y_float[index+i])=temp_y_float[i]; + *(int *)(&pointer_x_int[index+i])=temp_x_int[i]; + } + + eprom_loc = (char *) pointer_x_int - (char *) &(globals.Flash.flash_start); + writeUserBlock(eprom_loc,&globals.Flash.flash_start + eprom_loc,size_of_x_int); + + eprom_loc = (char *) pointer_y_float - (char *) &(globals.Flash.flash_start); + writeUserBlock(eprom_loc,&globals.Flash.flash_start + eprom_loc,size_of_y_float); + + globals.Flags.extended_ampl_min_max=NO; + + /* update output */ + switch (parameter) { + case (pwl_ampl_values): + case (pwl_vcc1_values): + case (pwl_vcc2_values): + Set_Amplitude(0,0,0,0,0,0,channel,cal_point,0); + break; + + case (pwl_os_values): + Set_Offset(0,0,0,0,channel,cal_point); + break; + + case pwl_pw_values: + Set_Pw(0,0,0,channel,cal_point,0); + break; + + case pwl_delay_values: + Set_Delay(0,0,0,channel,cal_point); + break; + + case pwl_period_values: + Set_frequency(0,0,0,channel,cal_point); + break; + + case pwl_burst_values: + Set_Burst_Time(0,0,0,channel,cal_point); + break; + + case pwl_rise_time_values: + Set_rise_time(0,0,0,channel,cal_point); + break; + + case pwl_slew_values: + Set_slew(0,0,0,channel,cal_point); + break; + + case pwl_load_type_values: + Set_avrq_res(0,0,0,channel,cal_point); + break; + } + + return OK; +} + + +int Get_VI_Num_Pnts(int parameter,int channel) +{ + int dummy1,dummy2,dummy3,num_in_range,num_of_ranges; + + return Get_VI_Rng_Info(parameter,channel,0,&dummy1,&dummy2,&dummy3,&num_in_range,&num_of_ranges); +} + + + +int Get_VI_Rng_Info(int parameter, int channel, int calibration_point_number, int *range, + int *polarity, int *entry, int *num_in_range, int *num_of_ranges) +{ + int x_polarity,x_range,x_entry,total; + int num_in_current_range; + int max_polarity,max_points,max_ranges; + float *pwl; + int index; + int true_channel; + + true_channel=channel; + + *polarity=0; + *range=0; + *entry=0; + *num_in_range=0; + + /* determine array structure */ + max_points=points_in_range; + + switch (parameter) { + case pwl_ampl_values: + max_polarity=ampl_polarities; + max_ranges=ampl_ranges; + pwl=&globals.Flash.ampl_pwl_amp[0][0][0][0]; + break; + case pwl_os_values: + max_polarity=os_polarities; + max_ranges=os_ranges; + pwl=&globals.Flash.os_pwl_amp[0][0][0][0]; + break; + case pwl_pw_values: + max_polarity=timing_polarities; + max_ranges=timing_ranges; + pwl=&globals.Flash.pw_pwl_time[0][0][0][0]; + break; + case pwl_delay_values: + max_polarity=timing_polarities; + max_ranges=timing_ranges; + pwl=&globals.Flash.delay_pwl_time[0][0][0][0]; + break; + case pwl_period_values: + max_polarity=timing_polarities; + max_ranges=timing_ranges; + pwl=&globals.Flash.period_pwl_time[0][0][0][0]; + break; + case pwl_burst_values: + max_polarity=timing_polarities; + max_ranges=timing_ranges; + pwl=&globals.Flash.burst_pwl_time[0][0][0][0]; + break; + case pwl_rise_time_values: + max_polarity=ampl_polarities; + max_ranges=ampl_ranges; + pwl=&globals.Flash.rise_time_pwl_time[0][0][0][0]; + break; + case pwl_slew_values: + max_polarity=timing_polarities; + max_ranges=timing_ranges; + pwl=&globals.Flash.slew_pwl_time[0][0][0][0]; + break; + case pwl_vcc1_values: + max_polarity=1; + max_ranges=1; + pwl=&globals.Flash.vcc1_pwl_amp[0][0][0][0]; + true_channel=0; + break; + case pwl_vcc2_values: + max_polarity=1; + max_ranges=1; + pwl=&globals.Flash.vcc2_pwl_amp[0][0][0][0]; + true_channel=0; + break; + case pwl_load_type_values: + max_polarity=load_type_polarities; + max_ranges=load_type_ranges; + pwl=&globals.Flash.load_type_pwl_time[0][0][0][0]; + break; + } + + + + total=0; + *num_of_ranges=0; + + for (x_polarity=0; x_polarity<max_polarity; x_polarity++) + for (x_range=0; x_range<max_ranges; x_range++) { + + /* determine number of data points in range */ + /* do this by counting number of zeroes at top end of array */ + num_in_current_range=max_points; + for (x_entry=max_points-1; x_entry>=0; --x_entry) { + /* pwl is a pointer to either the amplitude or offset control array. */ + /* Multidimensional pointer arrays are not possible, since the array size */ + /* is unknown at compilation time. Thus, the index offset has to be calculated manually. */ + index=true_channel*max_ranges*max_polarity*max_points + +x_range*max_polarity*max_points + +x_polarity*max_points + +x_entry; + if ( fabs(pwl[index])<smallest_allowed_number && (num_in_current_range==x_entry+1)) { + --num_in_current_range; + } + } + + if (num_in_current_range) { + *num_of_ranges=x_range; + + for (x_entry=0; x_entry<num_in_current_range; x_entry++) { + ++total; + if (total==calibration_point_number) { + calibration_point_number=0; /* to prevent further matches */ + *range=x_range; + *polarity=x_polarity; + *entry=x_entry; + *num_in_range=num_in_current_range; + } + } + } + } + + return total; +} + + +int Check_MinMax_Cal(int channel,int parameter) +{ + int status; + + status=OK; + + /* see if new point prevents zero, min, max, or current value from being obtained */ + switch (parameter) { + case (pwl_ampl_values): + status=Set_Amplitude(1,0,0,0,0,0,channel,globals.Flash.min_ampl[channel],0) + || Set_Amplitude(1,0,0,0,0,0,channel,globals.Flash.max_ampl[channel],0) + || Set_Amplitude(1,0,0,0,0,0,channel,0.0,0); + break; + + case (pwl_os_values): + status=Set_Offset(1,0,0,0,channel,globals.Flash.min_offset[channel]) + || Set_Offset(1,0,0,0,channel,globals.Flash.max_offset[channel]) + || Set_Offset(1,0,0,0,channel,0.0); + break; + + case pwl_pw_values: + status=Set_Pw(1,0,0,channel,globals.Flash.min_pw[channel],0) + || Set_Pw(1,0,0,channel,globals.Flash.max_pw[channel],0); + break; + + case pwl_delay_values: + status=Set_Delay(1,0,0,channel,globals.Flash.min_delay[channel]) + || Set_Delay(1,0,0,channel,globals.Flash.max_delay[channel]); + break; + + case pwl_period_values: + status=Set_frequency(1,0,0,channel,globals.Flash.min_freq[channel]) + || Set_frequency(1,0,0,channel,globals.Flash.max_freq[channel]); + break; + + case pwl_burst_values: + status=Set_Burst_Time(1,0,0,channel,globals.Flash.min_burst_gap[channel]) + || Set_Burst_Time(1,0,0,channel,globals.Flash.max_burst_gap[channel]); + break; + + case pwl_rise_time_values: + status=Set_rise_time(1,0,0,channel,globals.Flash.min_rise_time[channel]) + || Set_rise_time(1,0,0,channel,globals.Flash.max_rise_time[channel]); + break; + + case pwl_slew_values: + status=Set_slew(1,0,0,channel,globals.Flash.min_slew[channel]) + || Set_slew(1,0,0,channel,globals.Flash.max_slew[channel]); + break; + + case (pwl_vcc1_values): + status=Set_Amplitude(1,0,0,0,0,0,channel,0.0,0) + || Set_Amplitude(1,0,0,0,0,0,channel,globals.Flash.vcc1_max[0],0); + break; + + case (pwl_vcc2_values): + status=Set_Amplitude(1,0,0,0,0,0,channel,0.0,0) + || Set_Amplitude(1,0,0,0,0,0,channel,globals.Flash.vcc2_max[0],0); + break; + + case pwl_load_type_values: + status=Set_avrq_res(1,0,0,channel,globals.Flash.low_load_type[channel]) + || Set_avrq_res(1,0,0,channel,globals.Flash.high_load_type[channel]); + break; + } + + if (status) { + /* change the error code to a calibration-related one */ + status=CalibrationMinMaxError; + } + + return status; +} + + +int control_pcb107(int address, int dac, int word, int range) +{ + /* reset all lines */ + I2C_Write(PCF8574+address,0); + + set_dac(dac,word); + + /* send and load range data */ + I2C_Write(PCF8574+address,(range << 3)); + I2C_Write(PCF8574+address,(range << 3) | 0x80); + + /* clear the lines */ + I2C_Write(PCF8574+address,0); + + return OK; +} + + +int Set_Burst_Count(int channel,int count,float new_burst_time) +{ + int check_valid; + int hextext_out; + + if (globals.Flash.max_burst_count[channel]<=1) { + return Unrecognized; + } + + /* abandon if high channel selected by user but not enabled by firmware */ + if (channel && !globals.Flash.ChanKey_Burst_Count) { + return InvalidChannel; + } + + /* check duty cycle */ + int i; + for (i=0; i<max_channels; ++i) { + TestState[i]=globals.ChannelState[i]; + } + TestState[channel].burst_count=count; + if ((check_valid=Error_check(TestState))) { + return check_valid; + } + + /* update variables if OK */ + if (count!=globals.ChannelState[channel].burst_count) { + globals.Changes.update_burst_count=YES; + } + + globals.ChannelState[channel].burst_count = count; + Set_Update_Chans(); + + if (!globals.Flash.burst_func[channel]) { + /* Call update routine for pulse generator burst mode. */ + /* Update pulse separation at the same time. This routine does the actual changes. */ + Set_Burst_Time(0,0,0,channel,new_burst_time); + } else { + /* reset all lines */ + I2C_Write(PCF8574+Second_PW_Port,0); + + /* send first hextet */ + hextext_out=globals.ChannelState[channel].burst_count & 0x003f; + + I2C_Write(PCF8574+Second_PW_Port, hextext_out); + /* load hextet */ + I2C_Write(PCF8574+Second_PW_Port, hextext_out | 0x40 ); + + /* send second hextet */ + if (globals.ChannelState[channel].burst_count > 0) { + /* set "not_reset" bit high to enable burst mode for N > 0 */ + hextext_out = ((globals.ChannelState[channel].burst_count >> 6) & 0x07) | 0x08; + } else { + hextext_out = 0; + } + + I2C_Write(PCF8574+Second_PW_Port, hextext_out); + /* load hextet */ + I2C_Write(PCF8574+Second_PW_Port, hextext_out | 0x80 ); + + /* clear the lines */ + I2C_Write(PCF8574+Second_PW_Port,0); + + } + + return OK; +} + + +int Set_Burst_Time(int check_possible_only,int word_override,int range_override,int channel,float new_burst_time) +{ + int check_valid; + int count_word_out; + int hextext_out; + int status,point_found,relay_range,UseNegData,entry,actual_pol,word_out; + + 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; + for (i=0; i<max_channels; ++i) { + TestState[i]=globals.ChannelState[i]; + } + TestState[channel].burst_time=new_burst_time; + if ((check_valid=Error_check(TestState))) { + return check_valid; + } + } + + /* tweak count based on actual circuit */ + count_word_out = globals.ChannelState[channel].burst_count - 1; + + /* find appropriate range/fine settings from piece-wise linear data in flash memory */ + if (word_override) { + word_out=word_override; + relay_range=range_override; + } else { + if ((status=Set_VI_Control(pwl_burst_values,channel,new_burst_time,&point_found, + &relay_range,&UseNegData,&entry,&word_out,&actual_pol))) { + return status; + } + } + + if (check_possible_only) { + if (point_found) { + return OK; + } else { + return CalibrationMinMaxError; + } + } + + start_gate_override (); + + /* update DAC */ + set_dac(6,word_out); + + + /* update range and burst count */ + + /* reset all lines */ + I2C_Write(PCF8574+Second_PW_Port,0); + + /* send first hextet */ + hextext_out=count_word_out & 0x003f; + + I2C_Write(PCF8574+Second_PW_Port, hextext_out); + /* load hextet */ + I2C_Write(PCF8574+Second_PW_Port, hextext_out | 0x40 ); + + /* send second hextet */ + hextext_out= ((count_word_out >> 6) & 0x07) | ((relay_range << 3) & 0x0038); + + I2C_Write(PCF8574+Second_PW_Port, hextext_out); + /* load hextet */ + I2C_Write(PCF8574+Second_PW_Port, hextext_out | 0x80 ); + + /* clear the lines */ + I2C_Write(PCF8574+Second_PW_Port,0); + + if (globals.Registers.last_relay_driver_settings[2] != relay_range) { + g_usleep (1e3 * globals.Timers.normal_relay_bounce_time_in_milliseconds); + } + stop_gate_override (); + + globals.Registers.last_relay_driver_settings[2] = relay_range; + + /* update variables if OK */ + if (new_burst_time!=globals.ChannelState[channel].burst_time) { + globals.Changes.update_burst_time=YES; + } + + globals.ChannelState[channel].burst_time = new_burst_time; + + Set_Update_Chans(); + + return OK; +} + + +int Set_rise_time(int check_possible_only,int word_override,int range_override,int channel,float new_rise_time) +{ + int check_valid; + int status,point_found,relay_range,UseNegData,entry,actual_pol,word_out; + char range_control; + + if (globals.Flash.fixed_rise_time[channel]) { + return Unrecognized; + } + + /* abandon if high channel selected by user but not enabled by firmware */ + if (channel && !globals.Flash.ChanKey_rise_time) { + return InvalidChannel; + } + + if (!check_possible_only) { + /* check duty cycle */ + int i; + for (i=0; i<max_channels; ++i) { + TestState[i]=globals.ChannelState[i]; + } + TestState[channel].rise_time=new_rise_time; + if ((check_valid=Error_check(TestState))) { + return check_valid; + } + } + + /* find appropriate range/fine settings from piece-wise linear data in flash memory */ + if (word_override) { + word_out=word_override; + relay_range=range_override; + } else { + if ((status=Set_VI_Control(pwl_rise_time_values,channel,new_rise_time,&point_found, + &relay_range,&UseNegData,&entry,&word_out,&actual_pol))) { + return status; + } + } + + if (check_possible_only) { + if (point_found) { + return OK; + } else { + return CalibrationMinMaxError; + } + } + + set_dac(globals.Flash.rise_time_dac[channel],word_out); + + if (!globals.Flash.pcb_203a_rise_time[channel]) { + /* original AVRQ PCB */ + switch (relay_range) { + case 0: + range_control = (char) 0x01; + break; + case 1: + range_control = (char) 0x08; + break; + case 2: + range_control = (char) 0x10; + break; + case 3: + range_control = (char) 0x20; + break; + case 4: + range_control = (char) 0x40; + break; + case 5: + range_control = (char) 0x80; + break; + case 6: + case 7: + return HardwareError; + break; + } + } else { + /* PCB 203A */ + switch (relay_range) { + case 0: + range_control = (char) 0x0f; + break; + case 1: + range_control = (char) 0x02; + break; + case 2: + range_control = (char) 0x04; + break; + case 3: + range_control = (char) 0x08; + break; + case 4: + range_control = (char) 0x10; + break; + case 5: + case 6: + case 7: + return HardwareError; + break; + } + } + + + + if (globals.Registers.last_rise_time_relay_setting != range_control) { + /* update range hardware */ + if (globals.ChannelState[channel].trigger_source!=source_hold) { + bus_setpin(O_GATE, 1); + } + + if (!globals.Flash.pcb_203a_rise_time[channel]) { + I2C_Write(PCF8574A+rise_time_port,range_control); + } else { + globals.Registers.avrq_reg = (range_control & 0x1f) | + (globals.Registers.avrq_reg & 0xe0); + I2C_Write(PCF8574A+rise_time_port,globals.Registers.avrq_reg); + } + g_usleep (5e5); + + if (globals.ChannelState[channel].trigger_source!=source_hold) { + bus_setpin(O_GATE, 0); + } + } + + /* update variables if OK */ + if (new_rise_time!=globals.ChannelState[channel].rise_time) { + globals.Changes.update_rise_time=YES; + } + + globals.ChannelState[channel].rise_time = new_rise_time; + globals.Registers.last_rise_time_relay_setting = range_control; + + Set_Update_Chans(); + + return OK; +} + + +int Set_current_limit(int check_possible_only,int channel,float new_adj_current_limit) +{ + int check_valid; + float limit; + int word_out; + + if (!globals.Flash.hard_current_limit_enabled[channel]) { + return Unrecognized; + } + + /* abandon if high channel selected by user but not enabled by firmware */ + if (channel && !globals.Flash.ChanKey_current_limit) { + return InvalidChannel; + } + + if (!check_possible_only) { + int i; + for (i=0; i<max_channels; ++i) { + TestState[i]=globals.ChannelState[i]; + } + TestState[channel].soft_current_limit=new_adj_current_limit; + if ((check_valid=Error_check(TestState))) { + return check_valid; + } + } + + if (globals.ChannelState[channel].func_mode==dc_mode_on) { + limit=globals.Flash.current_limit_dc_mode[channel]; + } else { + limit=globals.Flash.current_limit_pulse_mode[channel]; + } + + if (globals.Flash.soft_current_limit_enabled[channel] && (new_adj_current_limit<limit)) { + limit=new_adj_current_limit; + } + + word_out = (int) ( ((float) dac_max) * (limit/globals.Flash.current_limit_full_scale[channel]) ); + set_dac(globals.Flash.current_limit_dac[channel],word_out); + + /* update variables if OK */ + if (limit!=globals.ChannelState[channel].soft_current_limit) { + globals.Changes.update_soft_current_limit=YES; + } + + globals.ChannelState[channel].soft_current_limit=limit; + + return OK; +} + + +int Set_avrq_res(int check_possible_only,int word_override,int range_override,int channel,float new_res) +{ + int check_valid; + int status,point_found,relay_range,UseNegData,entry,actual_pol,word_out; + + float compensated_new_res; + + if (!globals.Flash.switchable_load[channel]) { + return Unrecognized; + } + + /* abandon if high channel selected by user but not enabled by firmware */ + if (channel && !globals.Flash.ChanKey_load_type) { + return InvalidChannel; + } + + if (!check_possible_only) { + int i; + for (i=0; i<max_channels; ++i) { + TestState[i]=globals.ChannelState[i]; + } + TestState[channel].load_type=new_res; + if ((check_valid=Error_check(TestState))) { + return check_valid; + } + } + + /* find appropriate range/fine settings from piece-wise linear data in flash memory */ + if (word_override) { + word_out=word_override; + relay_range=range_override; + } else { + /* ugly hack - fix me? + CH2 = opto VCC + Calibrate res at 3.0V + Compensate for >3.0V */ + if (globals.ChannelState[0].vcc2 > 0.0) { + compensated_new_res = new_res * 3.0/globals.ChannelState[0].vcc2; + } else { + return amplitude_lower_limit; + } + + if ((status=Set_VI_Control(pwl_load_type_values,channel,compensated_new_res,&point_found, + &relay_range,&UseNegData,&entry,&word_out,&actual_pol))) { + return status; + } + } + + if (check_possible_only) { + if (point_found) { + return OK; + } else { + return CalibrationMinMaxError; + } + } + + set_dac(globals.Flash.load_type_dac[channel],word_out); + + /* no ranges are used */ + + /* update variables if OK */ + if (new_res!=globals.ChannelState[channel].load_type) { + globals.Changes.update_load=YES; + } + + globals.ChannelState[channel].load_type = new_res; + + Set_Update_Chans(); + + return OK; +} + + +int Set_slew(int check_possible_only,int word_override,int range_override,int channel,float new_slew) +{ + int check_valid; + int status,point_found,relay_range,UseNegData,entry,actual_pol,word_out; + char range_control; + + if (!globals.Flash.curr_slew[channel]) { + return Unrecognized; + } + + /* abandon if high channel selected by user but not enabled by firmware */ + if (channel && !globals.Flash.ChanKey_slew) { + return InvalidChannel; + } + + if (!check_possible_only) { + /* check duty cycle */ + int i; + for (i=0; i<max_channels; ++i) { + TestState[i]=globals.ChannelState[i]; + } + TestState[channel].slew=new_slew; + if ((check_valid=Error_check(TestState))) { + return check_valid; + } + } + + /* find appropriate range/fine settings from piece-wise linear data in flash memory */ + if (word_override) { + word_out=word_override; + relay_range=range_override; + } else { + if ((status=Set_VI_Control(pwl_slew_values,channel,new_slew,&point_found, + &relay_range,&UseNegData,&entry,&word_out,&actual_pol))) { + return status; + } + } + + if (check_possible_only) { + if (point_found) { + return OK; + } else { + return CalibrationMinMaxError; + } + } + + set_dac(globals.Flash.slew_dac[channel],word_out); + + switch (relay_range) { + case 0: + range_control = 0x0f; + break; + case 1: + range_control = 0x07; + break; + case 2: + range_control = 0x03; + break; + case 3: + range_control = 0x01; + break; + case 4: + default: + range_control = 0x00; + break; + } + + I2C_Write(PCF8574,range_control); + + /* update variables if OK */ + if (new_slew!=globals.ChannelState[channel].slew) { + globals.Changes.update_slew=YES; + } + + globals.ChannelState[channel].slew = new_slew; + + Set_Update_Chans(); + + return OK; +} + + +int Set_avrq_ampl(int check_possible_only,int word_override,int range_override,int channel,float new_avrq_ampl) +{ + int check_valid; + int status,point_found,relay_range,UseNegData,entry,actual_pol,word_out; + int true_channel; + int use_pwl; + + if (!globals.Flash.enable_avrq_extra_ampls) { + return Unrecognized; + } + + /* check channels */ + if ((channel < 1) || (channel>4)) { + return InvalidChannel; + } + + if (channel == 1) { + true_channel = 1; + } else { + true_channel = 0; + } + + if (!check_possible_only) { + /* check duty cycle */ + int i; + for (i=0; i<max_channels; ++i) { + TestState[i]=globals.ChannelState[i]; + } + switch (channel) { + case 1: + TestState[true_channel].amplitude=new_avrq_ampl; + break; + case 2: + TestState[true_channel].vcc1=new_avrq_ampl; + if (TestState[true_channel].vlogic >= smallest_allowed_number) { + TestState[true_channel].vlogic = new_avrq_ampl; + } + break; + case 3: + TestState[true_channel].vcc2=new_avrq_ampl; + break; + case 4: + TestState[true_channel].vlogic=new_avrq_ampl; + break; + } + if ((check_valid=Error_check(TestState))) { + return check_valid; + } + } + + /* find appropriate range/fine settings from piece-wise linear data in flash memory */ + if (word_override) { + word_out=word_override; + relay_range=range_override; + } else { + switch (channel) { + case 1: + use_pwl = pwl_ampl_values; + break; + case 2: + use_pwl = pwl_vcc1_values; + break; + case 3: + use_pwl = pwl_vcc2_values; + break; + case 4: + use_pwl = pwl_vlogic_values; + if (new_avrq_ampl > 0.0) { + new_avrq_ampl = globals.ChannelState[true_channel].vcc1; + } + break; + } + + if (use_pwl != pwl_vlogic_values) + if ((status=Set_VI_Control(use_pwl,channel,new_avrq_ampl,&point_found,&relay_range,&UseNegData,&entry,&word_out,&actual_pol))) { + return status; + } + + } + + if (check_possible_only) { + if (point_found) { + return OK; + } else { + return CalibrationMinMaxError; + } + } + + + /* update variables if OK */ + switch (channel) { + case 1: + Set_avrq_ampl(0,0,0,4,0.0); + + set_dac(1,word_out); + + globals.Registers.avrq_reg = globals.Registers.avrq_reg & 0x3f; + I2C_Write(PCF8574A+rise_time_port,globals.Registers.avrq_reg); + + if (new_avrq_ampl!=globals.ChannelState[true_channel].amplitude) { + globals.Changes.update_amp=YES; + } + globals.ChannelState[true_channel].amplitude=new_avrq_ampl; + break; + case 2: + set_dac(1,word_out); + + globals.Registers.avrq_reg = 0x40 | (globals.Registers.avrq_reg & 0xbf); + I2C_Write(PCF8574A+rise_time_port,globals.Registers.avrq_reg); + + if (new_avrq_ampl!=globals.ChannelState[true_channel].vcc1) { + globals.Changes.update_amp=YES; + } + globals.ChannelState[true_channel].vcc1=new_avrq_ampl; + break; + case 3: + set_dac(0,word_out); + if (new_avrq_ampl!=globals.ChannelState[true_channel].vcc2) { + globals.Changes.update_amp=YES; + } + globals.ChannelState[true_channel].vcc2=new_avrq_ampl; + + /* update ampl-dependent load resistance */ + Set_avrq_res(0,0,0,0,globals.ChannelState[0].load_type); + + break; + case 4: + if (new_avrq_ampl >= smallest_allowed_number) { + relay_range = 0x80; + } else { + relay_range = 0x0; + } + globals.Registers.avrq_reg = relay_range | (globals.Registers.avrq_reg & 0x7f); + I2C_Write(PCF8574A+rise_time_port,globals.Registers.avrq_reg); + + if (new_avrq_ampl!=globals.ChannelState[true_channel].vlogic) { + globals.Changes.update_amp=YES; + } + globals.ChannelState[true_channel].vlogic=new_avrq_ampl; + break; + } + + + /* reduce vlogic based on vcc1, if required */ + if (channel == 2) { + if (globals.ChannelState[true_channel].vlogic > smallest_allowed_number) { + Set_avrq_ampl(0,0,0,4,globals.ChannelState[true_channel].vcc1); + } else { + Set_avrq_ampl(0,0,0,4,0.0); + } + } + + /* reduce vcc1 if ibias is active, because they share a DAC */ + if ((channel == 1) && (globals.ChannelState[true_channel].amplitude > smallest_allowed_number)) { + globals.ChannelState[0].vcc1=0.0; + } + + /* reduce vcc1 if ibias is active, because they share a DAC */ + if ((channel == 2) && (globals.ChannelState[true_channel].vcc1 > smallest_allowed_number)) { + globals.ChannelState[1].amplitude=0.0; + } + + Set_Update_Chans(); + + return OK; +} + + +int go_cal(CalStruct *caldata) +{ + int i, status, points; + float meas, nom_val, change; + float min_val, max_val, chk_val; + char lcd_msg[LCD_cols+1]; + char prefix[LCD_cols+1]; + + points = Get_VI_Num_Pnts(caldata->cal_type,caldata->channel); + + caldata->count = 0; + caldata->error = 0; + caldata->max_change = 0.0; + caldata->avg_change = 0.0; + + min_val = 100e-6; + if (caldata->cal_type == pwl_period_values) { + max_val = 4.0 / globals.Flash.min_freq[caldata->channel]; + chk_val = 0.25 / globals.Flash.max_freq[caldata->channel]; + if (chk_val > min_val) { + min_val = chk_val; + } + } else if (caldata->cal_type == pwl_pw_values) { + max_val = 4.0 * globals.Flash.max_pw[caldata->channel]; + chk_val = 0.25 * globals.Flash.min_pw[caldata->channel]; + if (chk_val > min_val) { + min_val = chk_val; + } + } else if (caldata->cal_type == pwl_delay_values) { + max_val = 4.0 * globals.Flash.max_delay[caldata->channel]; + } + + for (i=1; i<=points; i++) { + Set_Cal_Nom(caldata->channel,i,caldata->cal_type,&nom_val); + if ( (nom_val >= min_val) && (nom_val <= max_val) ) { + Set_Cal_Nom(caldata->channel,i,caldata->cal_type,NULL); + + LCD_clear(); + LCD_write(0,0,"Self-calibration in progress."); + + if (caldata->cal_type == pwl_period_values) { + sprintf (prefix, "CH%d PER %d:",caldata->channel+1,i); + } else if (caldata->cal_type == pwl_pw_values) { + sprintf (prefix, "CH%d PW %d:",caldata->channel+1,i); + } else if (caldata->cal_type == pwl_delay_values) { + sprintf (prefix, "CH%d DLY %d:",caldata->channel+1,i); + } + sprintf (lcd_msg, "%s %.3e",prefix,nom_val); + LCD_write(2,0,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."); + sprintf (lcd_msg, "%s %.3e -> %.3e",prefix,nom_val,meas); + LCD_write(2,0,lcd_msg); + sprintf (lcd_msg, "changed %6.2f%%, err: %d",change,status); + LCD_write(3,0,lcd_msg); + g_usleep (2e6); + } + } + + return OK; +} + + +int cal_string(char *parameter, CalStruct *caldata) +{ + char string[max_output_length]; + + sprintf(string,"CH%d %s cal: %d errors. Adj: %6.2f%% max, %6.2f%% avg.\n\r", caldata->channel+1,parameter, caldata->error, caldata->max_change, caldata->avg_change); + strcat (caldata->response, string); +} + + +int self_cal() +{ + CalStruct caldata; + return do_full_self_cal(&caldata); +} + + +int do_full_self_cal(CalStruct *caldata) +{ + char string[LCD_cols+1]; + long start_timer, diff_timer; + int eprom_loc; + + Menu_Clear_Buttons(); + LCD_clear(); /*0123456789012345678901234567890123456789*/ + LCD_write(0,0,"Self-calibration in progress."); + /*0123456789012345678901234567890123456789*/ + sprintf(string,"Typical run time: %d min, %d sec.", globals.Flash.self_cal_typical_time_min, globals.Flash.self_cal_typical_time_sec); + LCD_write(1,0,string); + + while ((sec_timer() - globals.Timers.startup_timer_value) < (long)globals.Flash.self_cal_pause) { + /*0123456789012345678901234567890123456789*/ + sprintf (string, "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_usleep (2e5); + } + + start_timer = sec_timer(); + caldata->response[0] = 0; + 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."); + sprintf (string, "%d errors. Max change: %6.2f%%", caldata->total_errors, caldata->total_max_change); + LCD_write(1,0,string); + LCD_write(2,0,"More details are provided by \"cal?\""); + g_usleep (3e6); + + Menu_Update_Display(); + + 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); + + sprintf(string,"Completed in %d minutes and %d seconds\n\r", globals.Flash.self_cal_typical_time_min, globals.Flash.self_cal_typical_time_sec); + strcat (caldata->response, string); + + eprom_loc = (char *) &(globals.Flash.self_cal_typical_time_min) - (char *) &(globals.Flash.flash_start); + writeUserBlock(eprom_loc,&globals.Flash.flash_start + 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(eprom_loc,&globals.Flash.flash_start + eprom_loc,sizeof(globals.Flash.self_cal_typical_time_sec)); + + if (caldata->total_errors) { + return SelfCalError; + } +} + +int I2C_Self_Cal(int channel, int meas_mode, float *meas, float target_time) +{ +#define MAX_TRIES 35 +#define AVG_TRIES 4 + + int word, buffer, ch_mode; + long count; + float measuring_time; + float min_time; + float avg, max_error, this, this_error; + int iter, stable,i; + float prev[AVG_TRIES]; + + for (i=0; i<AVG_TRIES; i++) { + prev[i]=0.0; + } + + *meas = 0.0; + stable = 0; + + if (channel) { + ch_mode = MEAS_CH2; + } else { + ch_mode = MEAS_CH1; + } + + measuring_time = 1.0; + + min_time = globals.ChannelState[channel].pw * 3.0; + if (min_time > measuring_time) { + measuring_time = min_time; + } + + min_time = (1.0/globals.ChannelState[channel].frequency); + if (min_time > measuring_time) { + measuring_time = min_time; + } + + if (meas_mode == MEAS_PRF) { + min_time = (1.0/globals.ChannelState[channel].frequency) * 3.0; + if (min_time > measuring_time) { + measuring_time = min_time; + } + } + + min_time = fabs(globals.ChannelState[channel].delay) * 3.0; + if (min_time > measuring_time) { + measuring_time = min_time; + } + + for (iter = 1; (iter <= MAX_TRIES) && !stable; iter++) { + + if (iter <= LCD_cols) { + LCD_write(3,(iter-1) % 35,". "); + } + + if (meas_mode == MEAS_PW) { + start_gate_override (); + // delay to let oscillator reset, within 20% of a period? + g_usleep((gulong) (2.0e5 / globals.ChannelState[channel].frequency)); + } + + // reset counters + word = NOT_ENABLE_COUNT | NOT_LOAD_BUFFERS_CLK | CLR_COUNTERS | meas_mode | ch_mode | COUNTER_BYTE_0; + I2C_Write(PCF8574+To_Self_Cal_Port,word); + word = NOT_ENABLE_COUNT | NOT_LOAD_BUFFERS_CLK | NOT_CLR_COUNTERS | meas_mode | ch_mode | COUNTER_BYTE_0; + I2C_Write(PCF8574+To_Self_Cal_Port,word); + + // arm flip-flops + word = ENABLE_COUNT | NOT_LOAD_BUFFERS_CLK | NOT_CLR_COUNTERS | meas_mode | ch_mode | COUNTER_BYTE_0; + I2C_Write(PCF8574+To_Self_Cal_Port,word); + word = NOT_ENABLE_COUNT | NOT_LOAD_BUFFERS_CLK | NOT_CLR_COUNTERS | meas_mode | ch_mode | COUNTER_BYTE_0; + I2C_Write(PCF8574+To_Self_Cal_Port,word); + + if (meas_mode == MEAS_PW) { + stop_gate_override (); + } + + // time to measure + g_usleep((gulong) (measuring_time * 1.0e6)); + + // latch into buffers + word = NOT_ENABLE_COUNT | LOAD_BUFFERS_CLK | NOT_CLR_COUNTERS | meas_mode | ch_mode | COUNTER_BYTE_0; + I2C_Write(PCF8574+To_Self_Cal_Port,word); + word = NOT_ENABLE_COUNT | NOT_LOAD_BUFFERS_CLK | NOT_CLR_COUNTERS | meas_mode | ch_mode | COUNTER_BYTE_0; + I2C_Write(PCF8574+To_Self_Cal_Port,word); + + // read buffers + buffer = I2C_Read (PCF8574+From_Self_Cal_Port); + count = (long) buffer; + + word = NOT_ENABLE_COUNT | NOT_LOAD_BUFFERS_CLK | NOT_CLR_COUNTERS | meas_mode | ch_mode | COUNTER_BYTE_1; + I2C_Write(PCF8574+To_Self_Cal_Port,word); + buffer = I2C_Read (PCF8574+From_Self_Cal_Port); + count = count | ((long) buffer << 8); + + word = NOT_ENABLE_COUNT | NOT_LOAD_BUFFERS_CLK | NOT_CLR_COUNTERS | meas_mode | ch_mode | COUNTER_BYTE_2; + I2C_Write(PCF8574+To_Self_Cal_Port,word); + buffer = I2C_Read (PCF8574+From_Self_Cal_Port); + count = count | ((long) buffer << 16); + + word = NOT_ENABLE_COUNT | NOT_LOAD_BUFFERS_CLK | NOT_CLR_COUNTERS | meas_mode | ch_mode | COUNTER_BYTE_3; + I2C_Write(PCF8574+To_Self_Cal_Port,word); + buffer = I2C_Read (PCF8574+From_Self_Cal_Port); + count = count | ((long) buffer << 24); + + if (count < 100) { + //return SelfCalError; + if (iter <= LCD_cols) { + LCD_write(3,iter-1,"x"); + } + continue; + } + + this = count * 100e-9; + max_error = 0.001; + avg = 0.0; + + for (i=0; i<(AVG_TRIES-1); i++) { + prev[i]=prev[i+1]; + } + prev[AVG_TRIES-1] = this; + for (i = 0; i<AVG_TRIES; i++) { + avg += (prev[i]/AVG_TRIES); + } + + this_error = fabs ((this - avg) / avg); + if (this_error < max_error) { + stable = 1; + } + } + + if (!stable) { + return SelfCalError; + } + + *meas = this; + return OK; +} + |