def analyse_with_bbq_corrections(opt):
    """ Create amplitude detuning analysis with BBQ correction from timber data.

     """
    LOG.info("Starting Amplitude Detuning Analysis")
    _save_options(opt)

    with DebugMode(active=opt.debug, log_file=opt.logfile):
        opt, filter_opt = _check_analyse_opt(opt)

        # get data
        kick_df = read_two_kick_files_from_folder(opt.kick)
        bbq_df = None

        if opt.bbq_in is not None:
            bbq_df = _get_bbq_data(opt.beam, opt.bbq_in, kick_df)
            x_interval = get_approx_bbq_interval(bbq_df, kick_df.index,
                                                 opt.window_length)

            # add moving average to kick
            kick_df, bbq_df = kick_file_modifiers.add_moving_average(
                kick_df, bbq_df, filter_opt)

            # add corrected values to kick
            kick_df = kick_file_modifiers.add_corrected_natural_tunes(kick_df)
            kick_df = kick_file_modifiers.add_total_natq_std(kick_df)

        kick_plane = opt.plane

        # amplitude detuning odr
        for tune_plane in PLANES:
            for corrected in [False, True]:
                if corrected and opt.bbq_in is None:
                    continue

                # get the proper data
                data_df = kick_file_modifiers.get_ampdet_data(
                    kick_df, kick_plane, tune_plane, corrected=corrected)

                # make the odr
                odr_fit = fitting_tools.do_odr(x=data_df['action'],
                                               y=data_df['tune'],
                                               xerr=data_df['action_err'],
                                               yerr=data_df['tune_err'],
                                               order=opt.detuning_order)
                kick_df = kick_file_modifiers.add_odr(kick_df,
                                                      odr_fit,
                                                      kick_plane,
                                                      tune_plane,
                                                      corrected=corrected)

    # output kick and bbq data
    if opt.output:
        opt.output.mkdir(parents=True, exist_ok=True)
        write_timed_dataframe(opt.output / get_kick_out_name(), kick_df)
        if bbq_df is not None:
            write_timed_dataframe(opt.output / get_bbq_out_name(),
                                  bbq_df.loc[x_interval[0]:x_interval[1]])

    return kick_df, bbq_df
def main(opt):
    LOG.info("Plotting Amplitude Detuning Results.")
    _save_options(opt)
    _check_opt(opt)

    kick_plane = opt.plane
    figs = {}

    _set_plotstyle(opt.manual_style)
    limits = opt.get_subdict(['x_lim', 'y_lim'])

    for tune_plane in PLANES:
        for corrected in [False, True]:
            corr_label = "_corrected" if corrected else ""
            acd_corr = 1
            if opt.correct_acd and (kick_plane == tune_plane):
                acd_corr = 0.5

            fig = plt.figure()
            ax = fig.add_subplot(111)

            for idx, (kick, label) in enumerate(zip(opt.kicks, opt.labels)):
                kick_df = kick_mod.read_timed_dataframe(kick) if isinstance(kick, str) else kick
                try:
                    data_df = kick_mod.get_ampdet_data(kick_df, kick_plane, tune_plane, corrected=corrected)
                except KeyError:
                    continue  # should only happen when there is no 'corrected' columns

                odr_fit = kick_mod.get_odr_data(kick_df, kick_plane, tune_plane,
                                                order=opt.detuning_order, corrected=corrected)
                data_df, odr_fit = _correct_and_scale(data_df, odr_fit,
                                                   opt.action_unit, opt.action_plot_unit,
                                                   10**opt.tune_scale, acd_corr)

                _plot_detuning(ax, data=data_df, label=label, limits=limits,
                               odr_fit=odr_fit,
                               color=pcolors.get_mpl_color(idx),
                               action_unit=opt.action_plot_unit, tune_scale=10**opt.tune_scale)

            ax_labels = const.get_paired_lables(tune_plane, kick_plane, opt.tune_scale)
            id_str = f"dQ{tune_plane.upper():s}d2J{kick_plane.upper():s}{corr_label:s}"
            pannot.set_name(id_str, fig)
            _format_axes(ax, labels=ax_labels, limits=limits)

            if opt.show:
                plt.show()

            if opt.output:
                output = Path(opt.output)
                fig.savefig(f'{output.with_suffix("")}_{id_str}{output.suffix}')

            figs[id_str] = fig

    return figs
def analyse_with_bbq_corrections(
        opt: DotDict) -> Tuple[TfsDataFrame, TfsDataFrame]:
    """
    Create amplitude detuning analysis with BBQ correction from timber data.

    Returns:
        The amplitude detuning analysis results as a TfsDataFrame and the BBQ data as a TfsDataFrame.
    """
    LOG.info("Starting Amplitude Detuning Analysis")
    _save_options(opt)

    opt, filter_opt = _check_analyse_opt(opt)

    LOG.debug("Getting data from kick files")
    kick_df = read_two_kick_files_from_folder(opt.kick)
    bbq_df = None

    if opt.bbq_in is not None:
        bbq_df = _get_bbq_data(opt.beam, opt.bbq_in, kick_df)
        x_interval = get_approx_bbq_interval(bbq_df, kick_df.index,
                                             opt.window_length)

        LOG.debug("Adding moving average data to kick data")
        kick_df, bbq_df = kick_file_modifiers.add_moving_average(
            kick_df, bbq_df, filter_opt)

        LOG.debug("Adding corrected natural tunes and stdev to kick data")
        kick_df = kick_file_modifiers.add_corrected_natural_tunes(kick_df)
        kick_df = kick_file_modifiers.add_total_natq_std(kick_df)

    kick_plane = opt.plane

    LOG.info("Performing amplitude detuning odr")
    for tune_plane in PLANES:
        for corrected in [False, True]:
            if corrected and opt.bbq_in is None:
                continue

            LOG.debug("Getting ampdet data")
            data_df = kick_file_modifiers.get_ampdet_data(kick_df,
                                                          kick_plane,
                                                          tune_plane,
                                                          corrected=corrected)

            LOG.debug("Fitting ODR to kick data")
            odr_fit = fitting_tools.do_odr(
                x=data_df["action"],
                y=data_df["tune"],
                xerr=data_df["action_err"],
                yerr=data_df["tune_err"],
                order=opt.detuning_order,
            )
            kick_df = kick_file_modifiers.add_odr(kick_df,
                                                  odr_fit,
                                                  kick_plane,
                                                  tune_plane,
                                                  corrected=corrected)

    # output kick and bbq data
    if opt.output:
        LOG.info(
            f"Writing kick and BBQ data to files in directory '{opt.output.absolute()}'"
        )
        opt.output.mkdir(parents=True, exist_ok=True)
        write_timed_dataframe(opt.output / get_kick_out_name(), kick_df)
        if bbq_df is not None:
            write_timed_dataframe(opt.output / get_bbq_out_name(),
                                  bbq_df.loc[x_interval[0]:x_interval[1]])

    return kick_df, bbq_df