"Fine sweep using {:d} tones spanning {:.1f} MHz with resolution {:.0f} Hz (2^{:d} samples)"
    .format(num_sweep_tones, 1e-6 * f_baseband.ptp(), df_baseband,
            tone_sample_exponent))
logger.info(
    "Coarse sweep using {:d} tones spanning {:.1f} MHz with resolution {:.0f} Hz (2^{:d} samples)"
    .format(num_sweep_tones // coarse_stride, 1e-6 * f_baseband.ptp(),
            coarse_stride * df_baseband, tone_sample_exponent))

# Run
npd = acquire.new_npy_directory(suffix=suffix)
tic = time.time()
try:
    for lo_index, f_lo in enumerate(f_lo_center):
        assert np.all(ri.adc_valon.get_phase_locks())
        tools.set_and_attempt_external_phase_lock(ri=ri,
                                                  f_lo=1e-6 * f_lo,
                                                  f_lo_spacing=1e-6 *
                                                  f_lo_spacing)
        for attenuation_index, attenuation in enumerate(attenuations):
            ri.set_dac_attenuator(attenuation)
            ri.set_tone_baseband_freqs(freqs=1e-6 * np.array([f_baseband[0]]),
                                       nsamp=2**tone_sample_exponent)
            #time.sleep(1)
            #tools.optimize_fft_gain(ri, fraction_of_maximum=0.5)
            ri.set_fft_gain(4)
            coarse_state = hw.state()
            coarse_state['lo_index'] = lo_index
            coarse_state['attenuation_index'] = attenuation_index
            coarse_sweep = acquire.run_sweep(
                ri=ri,
                tone_banks=1e-6 *
                (f_lo + f_baseband[::coarse_stride, np.newaxis]),
ri.adc_valon.set_ref_select(0)
ri.lo_valon.set_ref_select(1)

# Calculate tone bin integers
f_filterbank_MHz = ri.fs / ri.nfft
n_filterbank = int(np.round(baseband_MHz / f_filterbank_MHz))
tone_sample_exponent = int(np.log2(ri.nfft) + tones_per_bin_exponent)
center_integer = 2 ** tones_per_bin_exponent * n_filterbank
tone_integers = center_integer + np.arange(-half_width_in_bins * 2 ** tones_per_bin_exponent,
                                           half_width_in_bins * 2 ** tones_per_bin_exponent + 1)

# Acquire
npd = acquire.new_npy_directory(suffix=suffix)
tic = time.time()
try:
    tools.set_and_attempt_external_phase_lock(ri, f_lo=lo_MHz, f_lo_spacing=lo_round_to_MHz)
    ri.set_lo(lomhz=lo_MHz, chan_spacing=lo_round_to_MHz)
    assert np.all(ri.adc_valon.get_phase_locks())
    assert np.all(ri.lo_valon.get_phase_locks())
    ri.set_dac_attenuator(dac_attenuation)
    ri.set_tone_bins(bins=np.array([center_integer]), nsamp=2 ** tone_sample_exponent)
    ri.fft_bins = np.atleast_2d(np.array([n_filterbank]))
    ri.select_bank(0)
    ri.select_fft_bins(np.array([0]))
    time.sleep(wait)
    tools.optimize_fft_gain(ri, fraction_of_maximum=0.5)
    time.sleep(wait)
    ri.iq_delay, _ = tools.find_best_iq_delay_adc(ri=ri)
    for tone_integer in tone_integers:
        ri.set_tone_bins(bins=np.array([tone_integer]), nsamp=2 ** tone_sample_exponent)
        ri.fft_bins = np.atleast_2d(np.array([n_filterbank]))
# Calculate tone bin integers
f_filterbank_MHz = ri.fs / ri.nfft
n_filterbank = int(np.round(baseband_MHz / f_filterbank_MHz))
tone_sample_exponent = int(np.log2(ri.nfft) + tones_per_bin_exponent)
center_integer = 2**tones_per_bin_exponent * n_filterbank
tone_integers = center_integer + np.arange(
    -half_width_in_bins * 2**tones_per_bin_exponent,
    half_width_in_bins * 2**tones_per_bin_exponent + 1)

# Acquire
npd = acquire.new_npy_directory(suffix=suffix)
tic = time.time()
try:
    tools.set_and_attempt_external_phase_lock(ri,
                                              f_lo=lo_MHz,
                                              f_lo_spacing=lo_round_to_MHz)
    ri.set_dac_attenuator(dac_attenuation)
    ri.set_tone_bins(bins=np.array([center_integer]),
                     nsamp=2**tone_sample_exponent)
    ri.fft_bins = np.atleast_2d(np.array([n_filterbank]))
    ri.select_bank(0)
    ri.select_fft_bins(np.array([0]))
    time.sleep(wait)
    tools.optimize_fft_gain(ri, fraction_of_maximum=fraction_of_maximum)
    for tone_integer in tone_integers:
        ri.set_tone_bins(bins=np.array([tone_integer]),
                         nsamp=2**tone_sample_exponent)
        ri.fft_bins = np.atleast_2d(np.array([n_filterbank]))
        ri.select_bank(0)
        ri.select_fft_bins(np.array([0]))
num_sweep_tones = min(int(df_total / df_baseband), ri.max_num_waveforms(2 ** tone_sample_exponent))
logger.info("Using {:d} tones".format(num_sweep_tones))
f_baseband = f_baseband_minimum + ri.state.adc_sample_rate / 2**tone_sample_exponent * np.arange(num_sweep_tones)
logger.info("Coarse sweep span is {:.1f} MHz".format(1e-6 * f_baseband.ptp()))
coarse_stride = max(df_coarse_sweep // df_baseband, 1)
logger.info("Coarse sweep resolution is {:.0f} Hz".format(coarse_stride * df_baseband))
f_lo_center = df_lo * np.round((f_center - f_baseband.mean()) / df_lo)

# Run
npd = acquire.new_npy_directory(suffix=suffix)
tic = time.time()
try:
    ri.set_tone_baseband_freqs(freqs=1e-6 * f_baseband[:, np.newaxis], nsamp=2 ** tone_sample_exponent)
    for lo_index, f_lo in enumerate(f_lo_center):
        assert np.all(ri.adc_valon.get_phase_locks())
        tools.set_and_attempt_external_phase_lock(ri=ri, f_lo=1e-6 * f_lo, f_lo_spacing=1e-6 * df_lo)
        for attenuation_index, (attenuation, fft_gain) in enumerate(zip(attenuations, fft_gains)):
            ri.set_dac_attenuator(attenuation)
            ri.set_fft_gain(fft_gain)
            state = hw.state()
            state['lo_index'] = lo_index
            coarse_sweep = acquire.run_loaded_sweep(ri, length_seconds=sweep_length_seconds,
                                                    tone_bank_indices=np.arange(0, num_sweep_tones, coarse_stride))[0]
            npd.write(coarse_sweep)
            coarse_f_r = coarse_sweep.resonator.f_0
            coarse_Q = coarse_sweep.resonator.Q
            logger.info("Coarse sweep f_r = {:.3f} MHz +/- {:.0f} Hz".format(1e-6 * coarse_f_r,
                                                                             coarse_sweep.resonator.f_0_error))
            logger.info("Coarse sweep Q = {:.0f} +/- {:.0f}".format(coarse_Q, coarse_sweep.resonator.Q_error))
            df_filterbank = calculate.stream_sample_rate(ri_state)
            f_baseband_bin_center = df_filterbank * np.round(f_baseband.mean() / df_filterbank)