def report_section_beam_power_dependence(report_object, subdirectory, test_data, ref_data):
    with open(os.path.join(subdirectory, test_data), 'r') as read_data:
        loaded_data = json.load(read_data)
    test_limit = 100
    status_label, status = pass_fail.power_dependence_pass_fail(loaded_data=loaded_data, level=test_limit)
    intro_text = r"""Tests the relationship between power at the BPM inputs and values read from the BPM. 
       
       An RF signal is output, and then different parameters are measured from the BPM. 
       The signal is changed, and the measurements are repeated.  \\~\\
       """
    # Get the device names for the report
    device_names = ['RF source is ' + loaded_data['rf_id'],
                    'Programmable attenuator is ' + loaded_data['prog_atten_id']]
    # Get the parameter values for the report
    parameter_names = ["Frequency: " + str(loaded_data['frequency']) + "MHz", "Power levels requested: " + str(
        helper_functions.round_to_2sf(loaded_data['set_output_power_levels'])) + "dBm", "Power levels used: " + str(
        helper_functions.round_to_2sf(loaded_data['output_power_levels'])) + "dBm",
                       "Settling time: " + str(loaded_data['settling_time']) + "s",
                       'AGC %s' % agc_state_label(loaded_data['bpm_agc']),
                       ''.join(('Switching ', switching_state_label(loaded_data['bpm_switching']))),
                       'DSC %s' % dsc_state_label(loaded_data['bpm_dsc']),
                       'BPM attenuation setting %s dB' % str(loaded_data['bpm_attenuation']),
                       'Test limit %s um ' % str(test_limit)]
    # add the test details to the report
    report_object.setup_test(' - '.join((loaded_data['test_name'], status_label)), intro_text, device_names, parameter_names)
    # make a caption and headings for a table of results
    caption = "Beam Power Dependence Results"
    headings = [["Input Power", " mean X Position", "mean Y Position", "Std X", "Std Y"],
                ["(dBm)", "(um)", "(um)", "(nm)", "(nm)"]]
    x_pos_mean, x_pos_std = helper_functions.stat_dataset(loaded_data['x_pos_raw'])
    y_pos_mean, y_pos_std = helper_functions.stat_dataset(loaded_data['y_pos_raw'])
    x_pos_mean_scaled = list()
    x_pos_mean_scaled_abs = list()
    for xpm in x_pos_mean:
        x_pos_mean_scaled.append(xpm * 1e3)
        x_pos_mean_scaled_abs.append(abs(x_pos_mean_scaled[-1]))
    y_pos_mean_scaled = list()
    y_pos_mean_scaled_abs = list()
    for ypm in y_pos_mean:
        y_pos_mean_scaled.append(ypm * 1e3)
        y_pos_mean_scaled_abs.append(abs(y_pos_mean_scaled[-1]))
    x_pos_std_scaled = list()
    for xps in x_pos_std:
        x_pos_std_scaled.append(xps * 1e6)
    y_pos_std_scaled = list()
    for yps in y_pos_std:
        y_pos_std_scaled.append(yps * 1e6)
    data = [loaded_data['set_output_power_levels'],
            x_pos_mean_scaled, y_pos_mean_scaled,
            x_pos_std_scaled, y_pos_std_scaled]
    # copy the values to the report
    report_object.add_table_to_test('|c|c|c|c|c|', data, headings, caption)
    fig1_bpd, fig_noise_time, fig_noise_freq = helper_functions.plot_beam_power_dependence_data(
        sub_directory=subdirectory,
        loaded_data=loaded_data,
        ref_data=ref_data)
    report_object.add_figure_to_test(image_name=fig1_bpd, caption='Position errors as a function of input power',
                                     fig_width=0.5)
def report_section_noise_test(report_object, subdirectory, test_data):
    with open(os.path.join(subdirectory, test_data[0]), 'r') as read_data:
        loaded_data = json.load(read_data)
    with open(os.path.join(subdirectory, test_data[1]), 'r') as read_data_complex:
        loaded_data_complex = json.load(read_data_complex)
    intro_text = r"""Compares the noise generated.

        In order to get the baseline, the RF signal is turned off, and then different parameters 
        are measured from the BPM.  \\~\\
        The BPM input power is then set to different values and the measurements are repeated.
        The mean values of the captured data are plotted, with the Baseline placed at -100dBm.\\~\\
        """
    # Get the device names for the report
    device_names = ['RF source is ' + loaded_data['rf_id'],
                    'Programmable attenuator is ' + loaded_data['prog_atten_id']]
    # Get the parameter values for the report
    parameter_names = ["power levels: " + str(
        helper_functions.round_to_2sf(loaded_data['output_power_levels']))]
    # add the test details to the report
    report_object.setup_test(loaded_data['test_name'], intro_text, device_names, parameter_names)
    # make a caption and headings for a table of results
    # caption = "Noise Results"
    # headings = [["X Position", "Y Position", ], ["(mm)", "(mm)"]]
    # data = [x_pos_baseline, y_pos_baseline]
    # copy the values to the report
    # report_object.add_table_to_test('|c|c|', data, headings, caption)
    fig_names = helper_functions.plot_noise(subdirectory, loaded_data, loaded_data_complex)

    for fig_n in fig_names:
        head, tail = os.path.split(fig_n)
        report_object.add_figure_to_test(image_name=fig_n, caption=tail)
def report_section_position_raster_scan(report_object, subdirectory, test_data):
    with open(os.path.join(subdirectory, test_data), 'r') as read_data:
        loaded_data = json.load(read_data)
    test_limit = 0.05
    status_label, status, test_results = pass_fail.raster_scan_pass_fail(loaded_data=loaded_data, lim=test_limit)
    centre_test_limit = 0.03
    centre_status_label, centre_status, centre_test_results = pass_fail.centre_offset_pass_fail(loaded_data=loaded_data, lim=centre_test_limit)
    # Readies text that will introduce this test in the report
    intro_text = r"""Moves the beam position in the XY plane and records beam position.
             A fixed RF frequency and power is used while the attenuator values are changed. 
             Finally the predicted values are compared with the measured values of position. \\~\\
            """
    # Readies devices that are used in the test so that they can be added to the report
    device_names = ['RF source is ' + loaded_data['rf_id'], 'Attenuator is ' + loaded_data['prog_atten_id']]
    # # Readies parameters that are used in the test so that they can be added to the report
    parameter_names = ["Power at BPM input: " + str(
        helper_functions.round_to_2sf(loaded_data['output_power_level'])) + "dBm",
                       "Fixed RF Output Frequency: " + str(loaded_data['frequency']) + "MHz",
                       "Number of repeat points: " + str(len(loaded_data['measured_x'])),
                       "Settling time: " + str(loaded_data['settling_time']) + "s",
                       'AGC ' + agc_state_label(loaded_data['bpm_agc']),
                       'Switching ' + switching_state_label(loaded_data['bpm_switching']),
                       'DSC ' + dsc_state_label(loaded_data['bpm_dsc']),
                       'BPM attenuation setting %s dB' % str(loaded_data['bpm_attenuation']),
                       'Scan test limit %s (%s um)' % (test_limit, test_limit * 1000),
                       'Centre test limit %s (%s um)' % (centre_test_limit, centre_test_limit * 1000)]

    report_object.setup_test(' '.join((loaded_data['test_name'], '-scan', status_label, '-centre', centre_status_label)), intro_text, device_names, parameter_names)

    fig_name = helper_functions.plot_raster_scan(subdirectory, loaded_data, test_results, centre_test_results)

    report_object.add_figure_to_test(image_name=fig_name, caption="Beam position equidistant grid raster scan test")
def report_section_adc_int_atten(report_object, subdirectory, test_data):
    with open(os.path.join(subdirectory, test_data), 'r') as read_data:
        loaded_data = json.load(read_data)
    test_limit = 0.1
    status_label, status = pass_fail.internal_attenuator_pass_fail(loaded_data=loaded_data, lim=test_limit)
    intro_text = r"""The RF signal is a sine wave.
    As the external attenuation is reduced, the internal is increased to compensate. 
    The total attenuation remains the same.
    SA data from each channel is captured at each signal level. 
       \\~\\
     """
    # Get the device names for the report
    device_names = ['RF source is ' + loaded_data['rf_id'],
                    'Programmable attenuator is ' + loaded_data['prog_atten_id']]

    # Get the parameter values for the report
    parameter_names = ['Output power level: ' + str(
        helper_functions.round_to_2sf(loaded_data['output_power'])),
                       'AGC %s' % agc_state_label(loaded_data['bpm_agc']),
                       ''.join(('Switching ', switching_state_label(loaded_data['bpm_switching']))),
                       'DSC %s' % dsc_state_label(loaded_data['bpm_dsc']),
                       'BPM attenuation setting %s dB' % str(loaded_data['bpm_attenuation']),
                       'Test limit is %s (%s percent)' % (str(test_limit), str(test_limit*100))
                       ]
    # add the test details to the report
    report_object.setup_test(' - '.join((loaded_data['test_name'], status_label)), intro_text, device_names, parameter_names)
    fig1_name, fig2_name = helper_functions.plot_adc_int_atten_sweep_data(sub_directory=subdirectory,
                                                                          loaded_data=loaded_data)
    report_object.add_figure_to_test(image_name=fig1_name, caption='Varying internal attenuation', fig_width=0.5)
    report_object.add_figure_to_test(image_name=fig2_name, caption='Varying internal attenuation (normalised signals)',
                                     fig_width=0.5)
def report_section_fixed_voltage_amplitude_fill_pattern(report_object, subdirectory, test_data):
    with open(os.path.join(subdirectory, test_data), 'r') as read_data:
        loaded_data = json.load(read_data)
    intro_text = r"""
           Equivalent to keeping the total charge in the train constant.
           This test imitates a fill pattern by modulation the RF signal with a square wave. The up time 
           of the square wave represents when a bunch goes passed, and the downtime the gaps between the 
           bunches. This test will take the pulse length in micro seconds, and then linearly step up the 
           duty cycle of the pulse, from 0.1 to 1. Readings on the BPM are then recorded as the duty cycle
           is changed. While the duty cycle is increased, the peak RF voltage stays fixed, meaning that 
           the average power will change with duty cycle. \\~\\
       """

    device_names = ['RF source is ' + str(loaded_data['rf_hw']), 'Gate is ' + str(loaded_data['gate_hw'])]
    # Get the parameter values for the report
    parameter_names = ["Frequency: " + str(loaded_data['frequency']), "Output Power: " + str(
        helper_functions.round_to_2sf(loaded_data['max_power'])) + "dBm",
                       "Pulse Period: " + str(loaded_data['pulse_period']),
                       "Settling time: " + str(loaded_data['settling_time']) + "s",
                       'AGC ' + str(loaded_data['bpm_agc']), 'Switching ' + str(loaded_data['bpm_switching']),
                       'DSC %s' % dsc_state_label(loaded_data['bpm_dsc']),
                       'BPM attenuation setting %s dB' % str(loaded_data['bpm_attenuation'])]
    # add the test details to the report
    report_object.setup_test(loaded_data['test_name'], intro_text, device_names, parameter_names)

    # make a caption and headings for a table of results
    caption = "Changing gate duty cycle, with fixed RF amplitude "
    headings = [["Duty Cycle", "mean X Position", "mean Y Position", "Std X", "Std Y"],
                ["(0-1)", "(um)", "(um)", "(um)", "(um)"]]
    data = [loaded_data['duty_cycles'],
            loaded_data['x_pos_mean'], loaded_data['y_pos_mean'],
            loaded_data['x_pos_std'], loaded_data['y_pos_std']]

    # copy the values to the report
    report_object.add_table_to_test('|c|c|c|c|c|', data, headings, caption)
    fig_name = helper_functions.plot_fixed_voltage_amplitude_fill_pattern_data(sub_directory=subdirectory,
                                                                               loaded_data=loaded_data)
    report_object.add_figure_to_test(image_name=fig_name, caption=fig_name)
def noise_test(test_system_object,
               frequency,
               samples=1000,
               output_power_levels=range(-20, -50, -5),
               settling_time=1,
               sub_directory=""):
    """Compares the noise generated.

    The RF signal is turned off, and then different parameters are measured from the BPM. 

    Args:
        test_system_object (System Obj): Object capturing the devices used, system losses and hardware ids.
        frequency (float): Output frequency for the tests, set as a float that will use the assumed units of MHz.
        samples (int): Number of sample to capture.
        output_power_levels (list): Output power for the tests, default value is np.arange(-100, 0, 10).
            The input values are floats and dBm is assumed.
        settling_time (float): Time in seconds, that the program will wait in between 
            setting an  output power on the RF, and reading the values of the BPM. 
        sub_directory (str): String that can change where the graphs will be saved to.

    Returns:
        float array: X Positions read from the BPM
        float array: Y Positions read from the BPM
    """
    test_name = test_system_object.test_initialisation(test_name=__name__,
                                                       frequency=frequency,
                                                       output_power_level=output_power_levels[0])

    test_system_object.RF.turn_on_RF()

    # Perform the test
    x_time_baseline, x_pos_baseline = test_system_object.BPM.get_x_sa_data(samples)  # record X pos
    y_time_baseline, y_pos_baseline = test_system_object.BPM.get_y_sa_data(samples)  # record Y pos

    x_baseline_mean = np.mean(x_pos_baseline)
    y_baseline_mean = np.mean(y_pos_baseline)
    input_power = []
    output_power = []
    x_time = []
    x_pos = []
    y_time = []
    y_pos = []
    x_mean = []
    y_mean = []
    graph_legend = []
    bpm_input_power = []
    #  Gradually reducing the power level
    starting_attenuations = test_system_object.ProgAtten.get_global_attenuation()
    if starting_attenuations[0] - starting_attenuations[1] > 0.00001 or \
       starting_attenuations[0] - starting_attenuations[2] > 0.00001 or \
       starting_attenuations[0] - starting_attenuations[3] > 0.00001:
        raise ValueError('The initial attenuation values are not the same value')

    for index in output_power_levels:
        # Set attenuator value to give desired power level.
        test_system_object.ProgAtten.set_global_attenuation(starting_attenuations[0] + (output_power_levels[0] - index))
        time.sleep(settling_time)  # Wait for signal to settle
        bpm_input_power.append(test_system_object.BPM.get_input_power())
        x_time_tmp, x_pos_tmp = test_system_object.BPM.get_x_sa_data(samples)  # record X pos
        y_time_tmp, y_pos_tmp = test_system_object.BPM.get_y_sa_data(samples)  # record Y pos
        x_time.append(x_time_tmp)
        x_pos.append(x_pos_tmp)
        y_time.append(y_time_tmp)
        y_pos.append(y_pos_tmp)
        x_mean.append(np.mean(x_pos_tmp))
        y_mean.append(np.mean(y_pos_tmp))
        output_power.append(index)
        input_power.append(test_system_object.BPM.get_input_power())
        graph_legend.append(str(helper_functions.round_to_2sf(index)))

    # turn off the RF
        test_system_object.RF.turn_off_RF()

    specs = test_system_object.BPM.get_performance_spec()

    # Adding baseline data
    x_time.append(x_time_baseline)
    x_pos.append(x_pos_baseline)
    y_time.append(y_time_baseline)
    y_pos.append(y_pos_baseline)
    output_power.append(-100)  # Assuming -100 dBm is equivalent to off.
    bpm_input_power.append(-100)
    x_mean.append(x_baseline_mean)
    y_mean.append(y_baseline_mean)
    graph_legend.append('Baseline')
    # Change to frequency domain
    x_f_freq = []
    x_f_data = []
    y_f_freq = []
    y_f_data = []
    for inds in range(len(x_time)):
        x_f_freq_tmp, x_f_data_tmp = helper_functions.change_to_freq_domain(x_time[inds], x_pos[inds])
        y_f_freq_tmp, y_f_data_tmp = helper_functions.change_to_freq_domain(y_time[inds], y_pos[inds])
        upper_lim_x = int(floor(len(x_f_freq_tmp) / 2.))
        upper_lim_y = int(floor(len(y_f_freq_tmp) / 2.))
        # Starting at one to knock out DC value which messes up the scaling of the graph.
        x_f_freq.append(x_f_freq_tmp[1:upper_lim_x])
        x_f_data.append(x_f_data_tmp[1:upper_lim_x])
        y_f_freq.append(y_f_freq_tmp[1:upper_lim_y])
        y_f_data.append(y_f_data_tmp[1:upper_lim_y])

    data_out = {'n_bits': test_system_object.BPM.adc_n_bits,
                'n_adc': test_system_object.BPM.num_adcs,
                'bpm_agc': test_system_object.BPM.agc,
                'bpm_switching': test_system_object.BPM.switches,
                'bpm_dsc': test_system_object.BPM.dsc,
                'test_name': test_name,
                'rf_id': test_system_object.rf_id,
                'bpm_id': test_system_object.bpm_id,
                'prog_atten_id': test_system_object.prog_atten_id,
                'output_power': output_power,
                'output_power_levels': output_power_levels,
                'bpm_input_power': bpm_input_power,
                'x_time': x_time,
                'x_pos': x_pos,
                'y_time': y_time,
                'y_pos': y_pos,
                'x_mean': x_mean,
                'y_mean': y_mean,
                'graph_legend': graph_legend}

    data_out_complex = {'x_f_freq': x_f_freq,
                        'x_f_data': x_f_data,
                        'y_f_freq': y_f_freq,
                        'y_f_data': y_f_data,
                        }

    with open(sub_directory + "Noise_test_data.json", 'w') as write_file:
        json.dump(data_out, write_file)

    with open(sub_directory + "Noise_test_data_complex.json", 'w') as write_file_complex:
        json.dump(data_out_complex, write_file_complex, cls=ComplexEncoder)