diff --git a/CHANGELOG.md b/CHANGELOG.md index 0d0971ff..df5df8eb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,8 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) ## [Unreleased] +### Changed +- config/waveform_tools - Added sampling rate argument with default value set to 1GS/s to the waveforms. ## [0.16.0] - 2024-01-25 ### Fixed diff --git a/qualang_tools/config/waveform_tools.py b/qualang_tools/config/waveform_tools.py index 688268a2..340d6381 100644 --- a/qualang_tools/config/waveform_tools.py +++ b/qualang_tools/config/waveform_tools.py @@ -3,7 +3,7 @@ def drag_gaussian_pulse_waveforms( - amplitude, length, sigma, alpha, anharmonicity, detuning=0.0, subtracted=True, **kwargs + amplitude, length, sigma, alpha, anharmonicity, detuning=0.0, subtracted=True, sampling_rate=1e9, **kwargs ): """ Creates Gaussian based DRAG waveforms that compensate for the leakage and for the AC stark shift. @@ -22,16 +22,17 @@ def drag_gaussian_pulse_waveforms( :param float detuning: The frequency shift to correct for AC stark shift, in Hz. :param bool subtracted: If true, returns a subtracted Gaussian, such that the first and last points will be at 0 volts. This reduces high-frequency components due to the initial and final points offset. Default is true. - :return: Returns a tuple of two lists. The first list is the I waveform (real part) and the second is the - Q waveform (imaginary part) + :param float sampling_rate: The sampling rate used to describe the waveform, in samples/s. Default is 1G samples/s. + :return: Returns a tuple of two lists. The first list is the 'I' waveform (real part) and the second is the + 'Q' waveform (imaginary part) """ delta = kwargs.get("delta", None) if delta is not None: print("'delta' has been replaced by 'anharmonicity' and will be deprecated in the future. ") if alpha != 0 and delta == 0: raise Exception("Cannot create a DRAG pulse with `anharmonicity=0`") - t = np.arange(length, dtype=int) # An array of size pulse length in ns - center = (length - 1) / 2 + t = np.arange(length, step=1e9 / sampling_rate) # An array of size pulse length in ns + center = (length - 1e9 / sampling_rate) / 2 gauss_wave = amplitude * np.exp(-((t - center) ** 2) / (2 * sigma**2)) # The gaussian function gauss_der_wave = ( amplitude * (-2 * 1e9 * (t - center) / (2 * sigma**2)) * np.exp(-((t - center) ** 2) / (2 * sigma**2)) @@ -49,8 +50,8 @@ def drag_gaussian_pulse_waveforms( else: if alpha != 0 and anharmonicity == 0: raise Exception("Cannot create a DRAG pulse with `anharmonicity=0`") - t = np.arange(length, dtype=int) # An array of size pulse length in ns - center = (length - 1) / 2 + t = np.arange(length, step=1e9 / sampling_rate) # An array of size pulse length in ns + center = (length - 1e9 / sampling_rate) / 2 gauss_wave = amplitude * np.exp(-((t - center) ** 2) / (2 * sigma**2)) # The gaussian function gauss_der_wave = ( amplitude * (-2 * 1e9 * (t - center) / (2 * sigma**2)) * np.exp(-((t - center) ** 2) / (2 * sigma**2)) @@ -68,7 +69,7 @@ def drag_gaussian_pulse_waveforms( return I_wf, Q_wf -def drag_cosine_pulse_waveforms(amplitude, length, alpha, anharmonicity, detuning=0.0, **kwargs): +def drag_cosine_pulse_waveforms(amplitude, length, alpha, anharmonicity, detuning=0.0, sampling_rate=1e9, **kwargs): """ Creates Cosine based DRAG waveforms that compensate for the leakage and for the AC stark shift. @@ -83,16 +84,17 @@ def drag_cosine_pulse_waveforms(amplitude, length, alpha, anharmonicity, detunin :param float alpha: The DRAG coefficient. :param float anharmonicity: f_21 - f_10 - The differences in energy between the 2-1 and the 1-0 energy levels, in Hz. :param float detuning: The frequency shift to correct for AC stark shift, in Hz. - :return: Returns a tuple of two lists. The first list is the I waveform (real part) and the second is the - Q waveform (imaginary part) + :param float sampling_rate: The sampling rate used to describe the waveform, in samples/s. Default is 1G samples/s. + :return: Returns a tuple of two lists. The first list is the 'I' waveform (real part) and the second is the + 'Q' waveform (imaginary part) """ delta = kwargs.get("delta", None) if delta is not None: print("'delta' has been replaced by 'anharmonicity' and will be deprecated in the future.") if alpha != 0 and anharmonicity == 0: raise Exception("Cannot create a DRAG pulse with `anharmonicity=0`") - t = np.arange(length, dtype=int) # An array of size pulse length in ns - end_point = length - 1 + t = np.arange(length, step=1e9 / sampling_rate) # An array of size pulse length in ns + end_point = length - 1e9 / sampling_rate cos_wave = 0.5 * amplitude * (1 - np.cos(t * 2 * np.pi / end_point)) # The cosine function sin_wave = ( 0.5 * amplitude * (2 * np.pi / end_point * 1e9) * np.sin(t * 2 * np.pi / end_point) @@ -108,8 +110,8 @@ def drag_cosine_pulse_waveforms(amplitude, length, alpha, anharmonicity, detunin else: if alpha != 0 and anharmonicity == 0: raise Exception("Cannot create a DRAG pulse with `anharmonicity=0`") - t = np.arange(length, dtype=int) # An array of size pulse length in ns - end_point = length - 1 + t = np.arange(length, step=1e9 / sampling_rate) # An array of size pulse length in ns + end_point = length - 1e9 / sampling_rate cos_wave = 0.5 * amplitude * (1 - np.cos(t * 2 * np.pi / end_point)) # The cosine function sin_wave = ( 0.5 * amplitude * (2 * np.pi / end_point * 1e9) * np.sin(t * 2 * np.pi / end_point) @@ -125,7 +127,7 @@ def drag_cosine_pulse_waveforms(amplitude, length, alpha, anharmonicity, detunin return I_wf, Q_wf -def flattop_gaussian_waveform(amplitude, flat_length, rise_fall_length, return_part="all"): +def flattop_gaussian_waveform(amplitude, flat_length, rise_fall_length, return_part="all", sampling_rate=1e9): """ Returns a flat top Gaussian waveform. This is a square pulse with a rise and fall of a Gaussian with the given sigma. It is possible to only get the rising or falling parts, which allows scanning the flat part length from QUA. @@ -138,14 +140,19 @@ def flattop_gaussian_waveform(amplitude, flat_length, rise_fall_length, return_p :param str return_part: When set to 'all', returns the complete waveform. Default is 'all'. When set to 'rise', returns only the rising part. When set to 'fall', returns only the falling part. This is useful for separating the three parts which allows scanning the duration of the flat part is to scanned from QUA + :param float sampling_rate: The sampling rate used to describe the waveform, in samples/s. Must be an integer multiple of 1e9 samples per seconds. Default is 1G samples/s. :return: Returns the waveform as a list of values with 1ns spacing """ - gauss_wave = amplitude * gaussian(2 * rise_fall_length, rise_fall_length / 5) - rise_part = gauss_wave[:rise_fall_length] + assert sampling_rate % 1e9 == 0, "The sampling rate must be an integer multiple of 1e9 samples per second." + + gauss_wave = amplitude * gaussian( + int(np.round(2 * rise_fall_length * sampling_rate / 1e9)), rise_fall_length / 5 * sampling_rate / 1e9 + ) + rise_part = gauss_wave[: int(rise_fall_length * sampling_rate / 1e9)] rise_part = rise_part.tolist() if return_part == "all": - return rise_part + [amplitude] * flat_length + rise_part[::-1] + return rise_part + [amplitude] * int(flat_length * sampling_rate / 1e9) + rise_part[::-1] elif return_part == "rise": return rise_part elif return_part == "fall": @@ -154,7 +161,7 @@ def flattop_gaussian_waveform(amplitude, flat_length, rise_fall_length, return_p raise Exception("'return_part' must be either 'all', 'rise' or 'fall'") -def flattop_cosine_waveform(amplitude, flat_length, rise_fall_length, return_part="all"): +def flattop_cosine_waveform(amplitude, flat_length, rise_fall_length, return_part="all", sampling_rate=1e9): """ Returns a flat top cosine waveform. This is a square pulse with a rise and fall with cosine shape with the given sigma. It is possible to only get the rising or falling parts, which allows scanning the flat part length from QUA. @@ -167,12 +174,14 @@ def flattop_cosine_waveform(amplitude, flat_length, rise_fall_length, return_par :param str return_part: When set to 'all', returns the complete waveform. Default is 'all'. When set to 'rise', returns only the rising part. When set to 'fall', returns only the falling part. This is useful for separating the three parts which allows scanning the duration of the flat part is to scanned from QUA + :param float sampling_rate: The sampling rate used to describe the waveform, in samples/s. Must be an integer multiple of 1e9 samples per seconds. Default is 1G samples/s. :return: Returns the waveform as a list of values with 1ns spacing """ - rise_part = amplitude * 0.5 * (1 - np.cos(np.linspace(0, np.pi, rise_fall_length))) + assert sampling_rate % 1e9 == 0, "The sampling rate must be an integer multiple of 1e9 samples per second." + rise_part = amplitude * 0.5 * (1 - np.cos(np.linspace(0, np.pi, int(rise_fall_length * sampling_rate / 1e9)))) rise_part = rise_part.tolist() if return_part == "all": - return rise_part + [amplitude] * flat_length + rise_part[::-1] + return rise_part + [amplitude] * int(flat_length * sampling_rate / 1e9) + rise_part[::-1] elif return_part == "rise": return rise_part elif return_part == "fall": @@ -181,7 +190,7 @@ def flattop_cosine_waveform(amplitude, flat_length, rise_fall_length, return_par raise Exception("'return_part' must be either 'all', 'rise' or 'fall'") -def flattop_tanh_waveform(amplitude, flat_length, rise_fall_length, return_part="all"): +def flattop_tanh_waveform(amplitude, flat_length, rise_fall_length, return_part="all", sampling_rate=1e9): """ Returns a flat top tanh waveform. This is a square pulse with a rise and fall with tanh shape with the given sigma. It is possible to only get the rising or falling parts, which allows scanning the flat part length from QUA. @@ -194,12 +203,14 @@ def flattop_tanh_waveform(amplitude, flat_length, rise_fall_length, return_part= :param str return_part: When set to 'all', returns the complete waveform. Default is 'all'. When set to 'rise', returns only the rising part. When set to 'fall', returns only the falling part. This is useful for separating the three parts which allows scanning the duration of the flat part is to scanned from QUA + :param float sampling_rate: The sampling rate used to describe the waveform, in samples/s. Must be an integer multiple of 1e9 samples per seconds. Default is 1G samples/s. :return: Returns the waveform as a list of values with 1ns spacing """ - rise_part = amplitude * 0.5 * (1 + np.tanh(np.linspace(-4, 4, rise_fall_length))) + assert sampling_rate % 1e9 == 0, "The sampling rate must be an integer multiple of 1e9 samples per second." + rise_part = amplitude * 0.5 * (1 + np.tanh(np.linspace(-4, 4, int(rise_fall_length * sampling_rate / 1e9)))) rise_part = rise_part.tolist() if return_part == "all": - return rise_part + [amplitude] * flat_length + rise_part[::-1] + return rise_part + [amplitude] * int(flat_length * sampling_rate / 1e9) + rise_part[::-1] elif return_part == "rise": return rise_part elif return_part == "fall": @@ -208,7 +219,7 @@ def flattop_tanh_waveform(amplitude, flat_length, rise_fall_length, return_part= raise Exception("'return_part' must be either 'all', 'rise' or 'fall'") -def flattop_blackman_waveform(amplitude, flat_length, rise_fall_length, return_part="all"): +def flattop_blackman_waveform(amplitude, flat_length, rise_fall_length, return_part="all", sampling_rate=1e9): """ Returns a flat top Blackman waveform. This is a square pulse with a rise and fall with Blackman shape with the given length. It is possible to only get the rising or falling parts, which allows scanning the flat part length from QUA. @@ -220,13 +231,15 @@ def flattop_blackman_waveform(amplitude, flat_length, rise_fall_length, return_p :param str return_part: When set to 'all', returns the complete waveform. Default is 'all'. When set to 'rise', returns only the rising part. When set to 'fall', returns only the falling part. This is useful for separating the three parts which allows scanning the duration of the flat part is to scanned from QUA + :param float sampling_rate: The sampling rate used to describe the waveform, in samples/s. Must be an integer multiple of 1e9 samples per seconds. Default is 1G samples/s. :return: Returns the waveform as a list """ - backman_wave = amplitude * blackman(2 * rise_fall_length) - rise_part = backman_wave[:rise_fall_length] + assert sampling_rate % 1e9 == 0, "The sampling rate must be an integer multiple of 1e9 samples per second." + backman_wave = amplitude * blackman(2 * int(rise_fall_length * sampling_rate / 1e9)) + rise_part = backman_wave[: int(rise_fall_length * sampling_rate / 1e9)] rise_part = rise_part.tolist() if return_part == "all": - return rise_part + [amplitude] * flat_length + rise_part[::-1] + return rise_part + [amplitude] * int(flat_length * sampling_rate / 1e9) + rise_part[::-1] elif return_part == "rise": return rise_part elif return_part == "fall": @@ -235,7 +248,7 @@ def flattop_blackman_waveform(amplitude, flat_length, rise_fall_length, return_p raise Exception("'return_part' must be either 'all', 'rise' or 'fall'") -def blackman_integral_waveform(pulse_length, v_start, v_end): +def blackman_integral_waveform(pulse_length, v_start, v_end, sampling_rate=1e9): """ Returns a Blackman integral waveform. This is the integral of a Blackman waveform, adiabatically going from 'v_start' to 'v_end' in 'pulse_length' ns. @@ -243,9 +256,11 @@ def blackman_integral_waveform(pulse_length, v_start, v_end): :param int pulse_length: The pulse length in ns. :param float v_start: The starting amplitude in volts. :param float v_end: The ending amplitude in volts. + :param float sampling_rate: The sampling rate used to describe the waveform, in samples/s. Must be an integer multiple of 1e9 samples per seconds. Default is 1G samples/s. :return: Returns the waveform as a list """ - time = np.asarray([x * 1.0 for x in range(int(pulse_length))]) + assert sampling_rate % 1e9 == 0, "The sampling rate must be an integer multiple of 1e9 samples per second." + time = np.linspace(0, pulse_length - 1, int(pulse_length * sampling_rate / 1e9)) black_wave = v_start + ( time / (pulse_length - 1) - (25 / (42 * np.pi)) * np.sin(2 * np.pi * time / (pulse_length - 1)) diff --git a/tests/test_waveform_tools.py b/tests/test_waveform_tools.py index 7f11ea27..5155ccd8 100644 --- a/tests/test_waveform_tools.py +++ b/tests/test_waveform_tools.py @@ -1,6 +1,5 @@ import numpy as np import pytest -from matplotlib import pyplot as plt from scipy.signal.windows import gaussian, blackman from qualang_tools.config import ( @@ -16,8 +15,8 @@ ) -@pytest.mark.parametrize("length", [16, 21, 60]) -def test_drag_no_drag_gaussian_to_scipy(length): +@pytest.mark.parametrize("length, sampling_rate", list(zip([16, 21, 60] * 2, [1e9, 1e9, 1e9, 2e9, 2e9, 2e9]))) +def test_drag_no_drag_gaussian_to_scipy(length, sampling_rate): amp = 0.1 sigma = length // 5 I_wf, Q_wf = drag_gaussian_pulse_waveforms( @@ -28,6 +27,7 @@ def test_drag_no_drag_gaussian_to_scipy(length): anharmonicity=0, detuning=0, subtracted=False, + sampling_rate=sampling_rate ) I_sub_wf, Q_sub_wf = drag_gaussian_pulse_waveforms( amplitude=amp, @@ -37,15 +37,16 @@ def test_drag_no_drag_gaussian_to_scipy(length): anharmonicity=0, detuning=0, subtracted=True, + sampling_rate=sampling_rate ) - gauss = amp * gaussian(length, sigma) + gauss = amp * gaussian(int(length * sampling_rate/1e9), sigma * sampling_rate / 1e9) sub_gauss = gauss - gauss[0] assert (I_wf == gauss).all() assert (I_sub_wf == sub_gauss).all() -@pytest.mark.parametrize("length", [16, 21, 60]) -def test_drag_no_detune_symmetric(length): +@pytest.mark.parametrize("length, sampling_rate", list(zip([16, 21, 60] * 2, [1e9, 1e9, 1e9, 2e9, 2e9, 2e9]))) +def test_drag_no_detune_symmetric(length, sampling_rate): amp = 0.1 sigma = length // 5 I_gauss_wf, Q_gauss_wf = drag_gaussian_pulse_waveforms( @@ -56,28 +57,29 @@ def test_drag_no_detune_symmetric(length): anharmonicity=10e6, detuning=0, subtracted=False, + sampling_rate=sampling_rate ) I_cos_wf, Q_cos_wf = drag_cosine_pulse_waveforms( - amplitude=amp, length=length, alpha=0.1, anharmonicity=10e6, detuning=0 + amplitude=amp, length=length, alpha=0.1, anharmonicity=10e6, detuning=0, sampling_rate=sampling_rate ) - I_gauss_first_half = I_gauss_wf[: length // 2] - Q_gauss_first_half = Q_gauss_wf[: length // 2] - if length % 2 == 0: - I_gauss_second_half = I_gauss_wf[length // 2 :] - Q_gauss_second_half = Q_gauss_wf[length // 2 :] + I_gauss_first_half = I_gauss_wf[: int(length * sampling_rate / 1e9) // 2] + Q_gauss_first_half = Q_gauss_wf[: int(length * sampling_rate / 1e9) // 2] + if int(length * sampling_rate / 1e9) % 2 == 0: + I_gauss_second_half = I_gauss_wf[int(length * sampling_rate / 1e9) // 2 :] + Q_gauss_second_half = Q_gauss_wf[int(length * sampling_rate / 1e9) // 2 :] else: - I_gauss_second_half = I_gauss_wf[length // 2 + 1 :] - Q_gauss_second_half = Q_gauss_wf[length // 2 + 1 :] - - I_cos_first_half = I_cos_wf[: length // 2] - Q_cos_first_half = Q_cos_wf[: length // 2] - if length % 2 == 0: - I_cos_second_half = I_cos_wf[length // 2 :] - Q_cos_second_half = Q_cos_wf[length // 2 :] + I_gauss_second_half = I_gauss_wf[int(length * sampling_rate / 1e9) // 2 + 1 :] + Q_gauss_second_half = Q_gauss_wf[int(length * sampling_rate / 1e9) // 2 + 1 :] + + I_cos_first_half = I_cos_wf[: int(length * sampling_rate / 1e9) // 2] + Q_cos_first_half = Q_cos_wf[: int(length * sampling_rate / 1e9) // 2] + if int(length * sampling_rate / 1e9) % 2 == 0: + I_cos_second_half = I_cos_wf[int(length * sampling_rate / 1e9) // 2 :] + Q_cos_second_half = Q_cos_wf[int(length * sampling_rate / 1e9) // 2 :] else: - I_cos_second_half = I_cos_wf[length // 2 + 1 :] - Q_cos_second_half = Q_cos_wf[length // 2 + 1 :] + I_cos_second_half = I_cos_wf[int(length * sampling_rate / 1e9) // 2 + 1 :] + Q_cos_second_half = Q_cos_wf[int(length * sampling_rate / 1e9) // 2 + 1 :] assert (np.array(I_gauss_first_half) == np.flip(I_gauss_second_half)).all() assert (np.array(Q_gauss_first_half) == -np.flip(Q_gauss_second_half)).all() @@ -145,69 +147,69 @@ def test_drag_zero_delta(): @pytest.mark.parametrize( - "flat_length, rise_fall_length", - list(zip([0, 16, 16, 21, 21, 60, 60], [8, 5, 10, 5, 10, 0, 10])), + "flat_length, rise_fall_length, sampling_rate", + list(zip([0, 16, 16, 21, 21, 60, 60, 0, 16, 16, 21, 21, 60, 60], [8, 5, 10, 5, 10, 0, 10, 8, 5, 10, 5, 10, 0, 10], [1e9, 1e9, 1e9, 1e9, 1e9, 1e9, 1e9, 2e9, 2e9, 2e9, 2e9, 2e9, 2e9, 2e9])), ) -def test_flattop_flat_length(flat_length, rise_fall_length): +def test_flattop_flat_length(flat_length, rise_fall_length, sampling_rate): amp = 0.1 - flattop_gaussian = flattop_gaussian_waveform(amp, flat_length, rise_fall_length) - flattop_cosine = flattop_cosine_waveform(amp, flat_length, rise_fall_length) - flattop_tanh = flattop_tanh_waveform(amp, flat_length, rise_fall_length) - flattop_blackman = flattop_blackman_waveform(amp, flat_length, rise_fall_length) + flattop_gaussian = flattop_gaussian_waveform(amp, flat_length, rise_fall_length, sampling_rate=sampling_rate) + flattop_cosine = flattop_cosine_waveform(amp, flat_length, rise_fall_length, sampling_rate=sampling_rate) + flattop_tanh = flattop_tanh_waveform(amp, flat_length, rise_fall_length, sampling_rate=sampling_rate) + flattop_blackman = flattop_blackman_waveform(amp, flat_length, rise_fall_length, sampling_rate=sampling_rate) flattop_gaussian_rise = flattop_gaussian_waveform( - amp, flat_length, rise_fall_length, return_part="rise" + amp, flat_length, rise_fall_length, return_part="rise", sampling_rate=sampling_rate ) flattop_cosine_rise = flattop_cosine_waveform( - amp, flat_length, rise_fall_length, return_part="rise" + amp, flat_length, rise_fall_length, return_part="rise", sampling_rate=sampling_rate ) flattop_tanh_rise = flattop_tanh_waveform( - amp, flat_length, rise_fall_length, return_part="rise" + amp, flat_length, rise_fall_length, return_part="rise", sampling_rate=sampling_rate ) flattop_blackman_rise = flattop_blackman_waveform( - amp, flat_length, rise_fall_length, return_part="rise" + amp, flat_length, rise_fall_length, return_part="rise", sampling_rate=sampling_rate ) flattop_gaussian_fall = flattop_gaussian_waveform( - amp, flat_length, rise_fall_length, return_part="fall" + amp, flat_length, rise_fall_length, return_part="fall", sampling_rate=sampling_rate ) flattop_cosine_fall = flattop_cosine_waveform( - amp, flat_length, rise_fall_length, return_part="fall" + amp, flat_length, rise_fall_length, return_part="fall", sampling_rate=sampling_rate ) flattop_tanh_fall = flattop_tanh_waveform( - amp, flat_length, rise_fall_length, return_part="fall" + amp, flat_length, rise_fall_length, return_part="fall", sampling_rate=sampling_rate ) flattop_blackman_fall = flattop_blackman_waveform( - amp, flat_length, rise_fall_length, return_part="fall" + amp, flat_length, rise_fall_length, return_part="fall", sampling_rate=sampling_rate ) assert np.allclose( flattop_gaussian, - flattop_gaussian_rise + [amp] * flat_length + flattop_gaussian_fall, + flattop_gaussian_rise + [amp] * int(flat_length * sampling_rate / 1e9) + flattop_gaussian_fall, rtol=1e-10, ) assert np.allclose( flattop_cosine, - flattop_cosine_rise + [amp] * flat_length + flattop_cosine_fall, + flattop_cosine_rise + [amp] * int(flat_length * sampling_rate / 1e9) + flattop_cosine_fall, rtol=1e-10, ) assert np.allclose( flattop_tanh, - flattop_tanh_rise + [amp] * flat_length + flattop_tanh_fall, + flattop_tanh_rise + [amp] * int(flat_length * sampling_rate / 1e9) + flattop_tanh_fall, rtol=1e-10, ) assert np.allclose( flattop_blackman, - flattop_blackman_rise + [amp] * flat_length + flattop_blackman_fall, + flattop_blackman_rise + [amp] * int(flat_length * sampling_rate / 1e9) + flattop_blackman_fall, rtol=1e-10, ) assert np.allclose( flattop_gaussian_rise + flattop_gaussian_fall, - (amp * gaussian(2 * rise_fall_length, rise_fall_length / 5)).tolist(), + (amp * gaussian(int(np.round(2 * rise_fall_length * sampling_rate / 1e9)), rise_fall_length / 5 * sampling_rate / 1e9)).tolist(), rtol=1e-10, ) cosine_rise_part = ( - amp * 0.5 * (1 - np.cos(np.linspace(0, np.pi, rise_fall_length))) + amp * 0.5 * (1 - np.cos(np.linspace(0, np.pi, int(rise_fall_length * sampling_rate / 1e9)))) ).tolist() assert np.allclose( flattop_cosine_rise + flattop_cosine_fall, @@ -215,7 +217,7 @@ def test_flattop_flat_length(flat_length, rise_fall_length): rtol=1e-10, ) tanh_rise_part = ( - amp * 0.5 * (1 + np.tanh(np.linspace(-4, 4, rise_fall_length))) + amp * 0.5 * (1 + np.tanh(np.linspace(-4, 4, int(rise_fall_length * sampling_rate / 1e9)))) ).tolist() assert np.allclose( flattop_tanh_rise + flattop_tanh_fall, @@ -224,23 +226,24 @@ def test_flattop_flat_length(flat_length, rise_fall_length): ) assert np.allclose( flattop_blackman_rise + flattop_blackman_fall, - (amp * blackman(2 * rise_fall_length)).tolist(), + (amp * blackman(2 * int(rise_fall_length * sampling_rate / 1e9))).tolist(), rtol=1e-10, ) @pytest.mark.parametrize( - "pulse_length, v_start, v_end", + "pulse_length, v_start, v_end, sampling_rate", list( zip( - np.linspace(2, 31, 30).astype(int).tolist() + [10], - np.linspace(0, 0.5, 30).tolist() + [-0.2], - np.linspace(0.5, 0, 30).tolist() + [-0.2], + np.linspace(2, 31, 30).astype(int).tolist(), + np.linspace(-0.5, 0.5, 30).tolist(), + np.linspace(0.5, -0.5, 30).tolist(), + [1e9]*15 + [2e9]*15, ) ), ) -def test_blackman_integral_waveform(pulse_length, v_start, v_end): - waveform = blackman_integral_waveform(pulse_length, v_start, v_end) - assert len(waveform) == pulse_length +def test_blackman_integral_waveform(pulse_length, v_start, v_end, sampling_rate): + waveform = blackman_integral_waveform(pulse_length, v_start, v_end, sampling_rate) + assert len(waveform) == int(pulse_length * sampling_rate / 1e9) assert np.isclose(waveform[0], v_start, rtol=1e-10) assert np.isclose(waveform[-1], v_end, rtol=1e-10)