Example #1
0
def add_temperature(measurement, cryostat, recursive=True, overwrite=False):
    start_epoch = measurement.start_epoch()
    if cryostat.lower() == 'hpd':
        from kid_readout.equipment import hpd_temps as temps
        thermometry = None
    elif cryostat.lower() == 'starcryo':
        from kid_readout.equipment import starcryo_temps as temps
        thermometry = experiments.get_experiment_info_at(start_epoch, cryostat)['thermometry_config']
    else:
        raise ValueError("Invalid cryostat: {}".format(cryostat))
    _add_temperature(measurement, temps, thermometry, recursive=recursive, overwrite=overwrite)
Example #2
0
def add_temperature(measurement, cryostat, recursive=True, overwrite=False):
    start_epoch = measurement.start_epoch()
    if cryostat.lower() == 'hpd':
        from kid_readout.equipment import hpd_temps as temps
        thermometry = None
    elif cryostat.lower() == 'starcryo':
        from kid_readout.equipment import starcryo_temps as temps
        thermometry = experiments.get_experiment_info_at(
            start_epoch, cryostat)['thermometry_config']
    else:
        raise ValueError("Invalid cryostat: {}".format(cryostat))
    _add_temperature(measurement,
                     temps,
                     thermometry,
                     recursive=recursive,
                     overwrite=overwrite)
Example #3
0
"""
This is a template local settings file for the computer `readout` running the STAR Cryo cryostat in 1132.
"""
import time as _time

from kid_readout.analysis.resources import experiments as _experiments

CRYOSTAT = 'STARCryo'
COOLDOWN = _experiments.get_experiment_info_at(_time.time(), cryostat=CRYOSTAT)

LOG_DIR = '/data/readout/log'
TEMPERATURE_LOG_DIR = '/data/readout/SRS'

SRS_TEMPERATURE_SERIAL_PORT = '/dev/serial/by-id/usb-FTDI_USB_to_Serial_Cable_FTGQM0GY-if00-port0'
SRS_TEMPERATURE_BAUD_RATE = 115200

LOCKIN_SERIAL_PORT = '/dev/serial/by-id/usb-Keyspan__a_division_of_InnoSys_Inc._Keyspan_USA-19H-if00-port0'
Example #4
0
    def __init__(self,sweep_filename,sweep_group_index=0,timestream_filename=None,timestream_group_index=0,
                 resonator_index=0,low_pass_cutoff_Hz=4.0,
                 dac_chain_gain = -49, delay_estimate=None,
                 deglitch_threshold=5, cryostat=None, mask_sweep_indicies=None, use_sweep_group_timestream=False,
                 conjugate_data=False):
        """
        sweep_filename : str
            NetCDF4 file with at least the sweep data. By default, this is also used for the timestream data.
            
        sweep_group_index : int (default 0)
            Index of the sweep group to process
            
        timestream_filename : str or None (optional)
            If None, use sweep_filename for the timestream data
            otherwise, this is the NetCDF4 file with the timestream data
            
        timestream_group_index : int (default 0)
            Index of the timestream group to process
        
        resonator_index : int (default 0)
            index of the resonator to process data for
            
        low_pass_cutoff_Hz : float
            Cutoff frequency used for low pass filtering the timeseries for display
            
        dac_chain_gain : float
            Estimate of the gain from output of DAC to device. 
            Default value of -49 represents -2 dB loss intrinsic to analog signal conditioning
            board, 7 dB misc cable loss and 40 dB total cold
            attenuation.
        
        delay_estimate : float
            Estimate of basic cable delay to help fitting proceed more smoothly. Default value
            is appropriate for the wideband noise firmware
            
        deglitch_threshold : float
            Threshold for glitch detection in units of median absolute deviation.
            
        cryostat : str
            (Optional) Override the cryostat used to take this data. By default, the cryostat
            is guessed based on the machine you are processing data on. The guess is made by
            the get_experiment_info_at function
            
            
        """

        if type(sweep_filename) is str:
            self.sweep_filename = sweep_filename
            if timestream_filename:
                self.timestream_filename = timestream_filename
            else:
                self.timestream_filename = self.sweep_filename
            self._sweep_file = None
            self._timestream_file = None
            self._close_file = True
        else:
            # TODO: Fix this to allow different timestream file
            self._sweep_file = sweep_filename
            self._timestream_file = sweep_filename
            self._close_file = False
            self.sweep_filename = self._sweep_file.filename
            self.timestream_filename = self._timestream_file.filename
        self.sweep_group_index = sweep_group_index
        self.timestream_group_index = timestream_group_index
        self._use_sweep_group_timestream = use_sweep_group_timestream
        
        self.sweep_epoch = self.sweep.start_epoch
        pkg1,pkg2,load1,load2 = get_temperatures_at(self.sweep.start_epoch)
        self.sweep_primary_package_temperature = pkg1
        self.sweep_secondary_package_temperature = pkg2
        self.sweep_primary_load_temperature = load1
        self.sweep_secondary_load_temperature = load2
        self.start_temp = self.sweep_primary_package_temperature
        self.resonator_index = resonator_index
        
        info = experiments.get_experiment_info_at(self.sweep_epoch, cryostat=cryostat)
        self.experiment_description = info['description']
        self.experiment_info = info
        self.chip_name = info['chip_id']
        self.is_dark = info['is_dark']
        self.optical_state = info['optical_state']
        self.dac_chain_gain = dac_chain_gain
        self.mmw_atten_turns = self._sweep_file.mmw_atten_turns

        try:
            self.atten, self.total_dac_atten = self._sweep_file.get_effective_dac_atten_at(self.sweep_epoch)
            self.power_dbm = dac_chain_gain - self.total_dac_atten
        except:
            print "failed to find attenuator settings"
            self.atten = np.nan
            self.total_dac_atten = np.nan
            self.power_dbm = np.nan

        # This uses the error calculation in readoutnc.SweepGroup
        self.sweep_freqs_MHz, self.sweep_s21, self.sweep_errors = self.sweep.select_by_index(resonator_index)
        self.conjugate_data = conjugate_data
        if conjugate_data:
            self.sweep_s21 = np.conj(self.sweep_s21)
        
        # find the time series that was measured closest to the sweep frequencies
        # this is a bit sloppy...
        if self._use_sweep_group_timestream:
            if delay_estimate is None:
                self.delay_estimate_microseconds = self._sweep_file.get_delay_estimate()*1e6
            else:
                self.delay_estimate_microseconds = delay_estimate


            if mask_sweep_indicies is None:
                rr = fit_best_resonator(self.sweep_freqs_MHz,self.sweep_s21,errors=self.sweep_errors,
                                        delay_estimate=self.delay_estimate_microseconds)
            else:
                mask = np.ones(self.sweep_s21.shape,dtype=np.bool)
                mask[mask_sweep_indicies] = False
                rr = fit_best_resonator(self.sweep_freqs_MHz[mask],self.sweep_s21[mask],errors=self.sweep_errors[mask],
                                        delay_estimate=self.delay_estimate_microseconds)
            timestream_index = np.argmin(abs(self.timestream.measurement_freq-rr.f_0))
        else:
            timestream_index = np.argmin(abs(self.timestream.measurement_freq-self.sweep_freqs_MHz.mean()))
        self.timestream_index = timestream_index
        
        original_timeseries = self.timestream.get_data_index(timestream_index)
        if conjugate_data:
            original_timeseries = np.conj(original_timeseries)
        self.adc_sampling_freq_MHz = self.timestream.adc_sampling_freq[timestream_index]
        self.noise_measurement_freq_MHz = self.timestream.measurement_freq[timestream_index]
        self.nfft = self.timestream.nfft[timestream_index]
        self.timeseries_sample_rate = self.timestream.sample_rate[timestream_index]
        self.timestream_modulation_duty_cycle = self.timestream.modulation_duty_cycle[timestream_index]
        self.timestream_modulation_freq = self.timestream.modulation_freq[timestream_index]
        self.timestream_modulation_phase = self.timestream.modulation_phase[timestream_index]
        self.timestream_modulation_period_samples = self.timestream.modulation_period_samples[timestream_index]
        if not self._use_sweep_group_timestream:
            self.timestream_mmw_source_freq = self.timestream.mmw_source_freq[timestream_index]
            old_style_source_modulation_freq = self.timestream.mmw_source_modulation_freq[timestream_index]
        else:
            self.timestream_mmw_source_freq = np.nan
            old_style_source_modulation_freq = np.nan

        if (np.isfinite(old_style_source_modulation_freq) and
            (old_style_source_modulation_freq != self.timestream_modulation_freq) and
            (old_style_source_modulation_freq != 0)):
            print ("found old style modulation frequency", old_style_source_modulation_freq,
                   "which doesn't match the new style",
                   self.timestream_modulation_freq,"using the old style value")
            self.timestream_modulation_freq = old_style_source_modulation_freq
            self.timestream_modulation_period_samples = int(self.timeseries_sample_rate/old_style_source_modulation_freq)
            self.timestream_modulation_duty_cycle = 0.5

        self.timestream_epoch = self.timestream.epoch[timestream_index]
        self.timestream_duration = original_timeseries.shape[0]/self.timeseries_sample_rate
        # The following hack helps fix a long standing timing bug which was recently fixed/improved
        if self.timestream_epoch < 1399089567:
            self.timestream_epoch -= self.timestream_duration
        # end hack
        self.timestream_temperatures_sample_times = np.arange(self.timestream_duration)
        pkg1,pkg2,load1,load2 = get_temperatures_at(self.timestream_epoch + self.timestream_temperatures_sample_times)
        self.timestream_primary_package_temperature = pkg1
        self.timestream_secondary_package_temperature = pkg2
        self.timestream_primary_load_temperature = load1
        self.timestream_secondary_load_temperature = load2
        self.end_temp = self.timestream_primary_package_temperature[-1]

        
        # We can use the timestream measurement as an additional sweep point.
        # We average only the first 2048 points of the timeseries to avoid any drift.
        if False:
            self.sweep_freqs_MHz = np.hstack((self.sweep_freqs_MHz,[self.noise_measurement_freq_MHz]))
            self.sweep_s21 = np.hstack((self.sweep_s21,[original_timeseries[:2048].mean()]))
            self.sweep_errors = np.hstack((self.sweep_errors,
                                               [original_timeseries[:2048].real.std()/np.sqrt(2048)
                                                +1j*original_timeseries[:2048].imag.std()/np.sqrt(2048)]))
        
        # Now put all the sweep data in increasing frequency order so it plots nicely
        order = self.sweep_freqs_MHz.argsort()
        self.sweep_freqs_MHz = self.sweep_freqs_MHz[order]
        self.sweep_s21 = self.sweep_s21[order]
        self.sweep_errors = self.sweep_errors[order]

        if delay_estimate is None:
            self.delay_estimate_microseconds = self._sweep_file.get_delay_estimate()*1e6
        else:
            self.delay_estimate_microseconds = delay_estimate


        if mask_sweep_indicies is None:
            rr = fit_best_resonator(self.sweep_freqs_MHz,self.sweep_s21,errors=self.sweep_errors,
                                    delay_estimate=self.delay_estimate_microseconds)
        else:
            mask = np.ones(self.sweep_s21.shape,dtype=np.bool)
            mask[mask_sweep_indicies] = False
            rr = fit_best_resonator(self.sweep_freqs_MHz[mask],self.sweep_s21[mask],errors=self.sweep_errors[mask],
                                    delay_estimate=self.delay_estimate_microseconds)
        self._resonator_model = rr
        self.Q_i = rr.Q_i
        self.Q_i_err = qi_error(rr.result.params['Q'].value, rr.result.params['Q'].stderr,
                                rr.result.params['Q_e_real'].value, rr.result.params['Q_e_real'].stderr,
                                rr.result.params['Q_e_imag'].value, rr.result.params['Q_e_imag'].stderr)
        self.fit_params = rr.result.params
        
        decimation_factor = self.timeseries_sample_rate/low_pass_cutoff_Hz
        normalized_timeseries = rr.normalize(self.noise_measurement_freq_MHz,original_timeseries)
        self.low_pass_normalized_timeseries = low_pass_fir(normalized_timeseries, num_taps=1024, cutoff=low_pass_cutoff_Hz, 
                                                          nyquist_freq=self.timeseries_sample_rate, decimate_by=decimation_factor)
        self.normalized_timeseries_mean = normalized_timeseries.mean()

        projected_timeseries = rr.project_s21_to_delta_freq(self.noise_measurement_freq_MHz,normalized_timeseries,
                                                            s21_already_normalized=True)
        
        # calculate the number of samples for the deglitching window.
        # the following will be the next power of two above 1 second worth of samples
        window = int(2**np.ceil(np.log2(self.timeseries_sample_rate)))
        # reduce the deglitching window if we don't have enough samples
        if window > projected_timeseries.shape[0]:
            window = projected_timeseries.shape[0]//2
        self.deglitch_window = window
        self.deglitch_threshold = deglitch_threshold
        if deglitch_threshold:
            deglitched_timeseries = deglitch_window(projected_timeseries,window,thresh=deglitch_threshold)
        else:
            deglitched_timeseries = projected_timeseries
        
        # TODO: should nyquist_freq be half the sample rate?
        self.low_pass_projected_timeseries = low_pass_fir(deglitched_timeseries, num_taps=1024, cutoff=low_pass_cutoff_Hz,
                                                nyquist_freq=self.timeseries_sample_rate, decimate_by=decimation_factor)
        self.low_pass_timestep = decimation_factor/self.timeseries_sample_rate
        
        self.normalized_model_s21_at_meas_freq = rr.normalized_model(self.noise_measurement_freq_MHz)
        self.normalized_model_s21_at_resonance = rr.normalized_model(rr.f_0)
        self.normalized_ds21_df_at_meas_freq = rr.approx_normalized_gradient(self.noise_measurement_freq_MHz)
        
        self.sweep_normalized_s21 = rr.normalize(self.sweep_freqs_MHz,self.sweep_s21)

        self.sweep_model_freqs_MHz = np.linspace(self.sweep_freqs_MHz.min(),self.sweep_freqs_MHz.max(),1000)
        self.sweep_model_normalized_s21 = rr.normalized_model(self.sweep_model_freqs_MHz) 
        self.sweep_model_normalized_s21_centered = self.sweep_model_normalized_s21 - self.normalized_timeseries_mean
        
        fractional_fluctuation_timeseries = deglitched_timeseries / (self.noise_measurement_freq_MHz*1e6)
        self._fractional_fluctuation_timeseries = fractional_fluctuation_timeseries
        fr,S,evals,evects,angles,piq = iqnoise.pca_noise(fractional_fluctuation_timeseries, 
                                                         NFFT=None, Fs=self.timeseries_sample_rate)
        
        self.pca_freq = fr
        self.pca_S = S
        self.pca_eigvals = evals
        self.pca_eigvects = evects
        self.pca_angles = angles
        self.pca_piq = piq
        
        self.freqs_coarse,self.prr_coarse,self.pii_coarse = self.get_projected_fractional_fluctuation_spectra(NFFT=2**12)
        
        self._normalized_timeseries = normalized_timeseries[:2048].copy()
        self._close_files()
Example #5
0
    def __init__(self,
                 sweep_filename,
                 sweep_group_index=0,
                 timestream_filename=None,
                 timestream_group_index=0,
                 resonator_index=0,
                 low_pass_cutoff_Hz=4.0,
                 dac_chain_gain=-49,
                 delay_estimate=None,
                 deglitch_threshold=5,
                 cryostat=None,
                 mask_sweep_indicies=None,
                 use_sweep_group_timestream=False,
                 conjugate_data=False):
        """
        sweep_filename : str
            NetCDF4 file with at least the sweep data. By default, this is also used for the timestream data.
            
        sweep_group_index : int (default 0)
            Index of the sweep group to process
            
        timestream_filename : str or None (optional)
            If None, use sweep_filename for the timestream data
            otherwise, this is the NetCDF4 file with the timestream data
            
        timestream_group_index : int (default 0)
            Index of the timestream group to process
        
        resonator_index : int (default 0)
            index of the resonator to process data for
            
        low_pass_cutoff_Hz : float
            Cutoff frequency used for low pass filtering the timeseries for display
            
        dac_chain_gain : float
            Estimate of the gain from output of DAC to device. 
            Default value of -49 represents -2 dB loss intrinsic to analog signal conditioning
            board, 7 dB misc cable loss and 40 dB total cold
            attenuation.
        
        delay_estimate : float
            Estimate of basic cable delay to help fitting proceed more smoothly. Default value
            is appropriate for the wideband noise firmware
            
        deglitch_threshold : float
            Threshold for glitch detection in units of median absolute deviation.
            
        cryostat : str
            (Optional) Override the cryostat used to take this data. By default, the cryostat
            is guessed based on the machine you are processing data on. The guess is made by
            the get_experiment_info_at function
            
            
        """

        if type(sweep_filename) is str:
            self.sweep_filename = sweep_filename
            if timestream_filename:
                self.timestream_filename = timestream_filename
            else:
                self.timestream_filename = self.sweep_filename
            self._sweep_file = None
            self._timestream_file = None
            self._close_file = True
        else:
            # TODO: Fix this to allow different timestream file
            self._sweep_file = sweep_filename
            self._timestream_file = sweep_filename
            self._close_file = False
            self.sweep_filename = self._sweep_file.filename
            self.timestream_filename = self._timestream_file.filename
        self.sweep_group_index = sweep_group_index
        self.timestream_group_index = timestream_group_index
        self._use_sweep_group_timestream = use_sweep_group_timestream

        self.sweep_epoch = self.sweep.start_epoch
        pkg1, pkg2, load1, load2 = get_temperatures_at(self.sweep.start_epoch)
        self.sweep_primary_package_temperature = pkg1
        self.sweep_secondary_package_temperature = pkg2
        self.sweep_primary_load_temperature = load1
        self.sweep_secondary_load_temperature = load2
        self.start_temp = self.sweep_primary_package_temperature
        self.resonator_index = resonator_index

        info = experiments.get_experiment_info_at(self.sweep_epoch,
                                                  cryostat=cryostat)
        self.experiment_description = info['description']
        self.experiment_info = info
        self.chip_name = info['chip_id']
        self.is_dark = info['is_dark']
        self.optical_state = info['optical_state']
        self.dac_chain_gain = dac_chain_gain
        self.mmw_atten_turns = self._sweep_file.mmw_atten_turns

        try:
            self.atten, self.total_dac_atten = self._sweep_file.get_effective_dac_atten_at(
                self.sweep_epoch)
            self.power_dbm = dac_chain_gain - self.total_dac_atten
        except:
            print "failed to find attenuator settings"
            self.atten = np.nan
            self.total_dac_atten = np.nan
            self.power_dbm = np.nan

        # This uses the error calculation in readoutnc.SweepGroup
        self.sweep_freqs_MHz, self.sweep_s21, self.sweep_errors = self.sweep.select_by_index(
            resonator_index)
        self.conjugate_data = conjugate_data
        if conjugate_data:
            self.sweep_s21 = np.conj(self.sweep_s21)

        # find the time series that was measured closest to the sweep frequencies
        # this is a bit sloppy...
        if self._use_sweep_group_timestream:
            if delay_estimate is None:
                self.delay_estimate_microseconds = self._sweep_file.get_delay_estimate(
                ) * 1e6
            else:
                self.delay_estimate_microseconds = delay_estimate

            if mask_sweep_indicies is None:
                rr = fit_best_resonator(
                    self.sweep_freqs_MHz,
                    self.sweep_s21,
                    errors=self.sweep_errors,
                    delay_estimate=self.delay_estimate_microseconds)
            else:
                mask = np.ones(self.sweep_s21.shape, dtype=np.bool)
                mask[mask_sweep_indicies] = False
                rr = fit_best_resonator(
                    self.sweep_freqs_MHz[mask],
                    self.sweep_s21[mask],
                    errors=self.sweep_errors[mask],
                    delay_estimate=self.delay_estimate_microseconds)
            timestream_index = np.argmin(
                abs(self.timestream.measurement_freq - rr.f_0))
        else:
            timestream_index = np.argmin(
                abs(self.timestream.measurement_freq -
                    self.sweep_freqs_MHz.mean()))
        self.timestream_index = timestream_index

        original_timeseries = self.timestream.get_data_index(timestream_index)
        if conjugate_data:
            original_timeseries = np.conj(original_timeseries)
        self.adc_sampling_freq_MHz = self.timestream.adc_sampling_freq[
            timestream_index]
        self.noise_measurement_freq_MHz = self.timestream.measurement_freq[
            timestream_index]
        self.nfft = self.timestream.nfft[timestream_index]
        self.timeseries_sample_rate = self.timestream.sample_rate[
            timestream_index]
        self.timestream_modulation_duty_cycle = self.timestream.modulation_duty_cycle[
            timestream_index]
        self.timestream_modulation_freq = self.timestream.modulation_freq[
            timestream_index]
        self.timestream_modulation_phase = self.timestream.modulation_phase[
            timestream_index]
        self.timestream_modulation_period_samples = self.timestream.modulation_period_samples[
            timestream_index]
        if not self._use_sweep_group_timestream:
            self.timestream_mmw_source_freq = self.timestream.mmw_source_freq[
                timestream_index]
            old_style_source_modulation_freq = self.timestream.mmw_source_modulation_freq[
                timestream_index]
        else:
            self.timestream_mmw_source_freq = np.nan
            old_style_source_modulation_freq = np.nan

        if (np.isfinite(old_style_source_modulation_freq)
                and (old_style_source_modulation_freq !=
                     self.timestream_modulation_freq)
                and (old_style_source_modulation_freq != 0)):
            print("found old style modulation frequency",
                  old_style_source_modulation_freq,
                  "which doesn't match the new style",
                  self.timestream_modulation_freq, "using the old style value")
            self.timestream_modulation_freq = old_style_source_modulation_freq
            self.timestream_modulation_period_samples = int(
                self.timeseries_sample_rate / old_style_source_modulation_freq)
            self.timestream_modulation_duty_cycle = 0.5

        self.timestream_epoch = self.timestream.epoch[timestream_index]
        self.timestream_duration = original_timeseries.shape[
            0] / self.timeseries_sample_rate
        # The following hack helps fix a long standing timing bug which was recently fixed/improved
        if self.timestream_epoch < 1399089567:
            self.timestream_epoch -= self.timestream_duration
        # end hack
        self.timestream_temperatures_sample_times = np.arange(
            self.timestream_duration)
        pkg1, pkg2, load1, load2 = get_temperatures_at(
            self.timestream_epoch + self.timestream_temperatures_sample_times)
        self.timestream_primary_package_temperature = pkg1
        self.timestream_secondary_package_temperature = pkg2
        self.timestream_primary_load_temperature = load1
        self.timestream_secondary_load_temperature = load2
        self.end_temp = self.timestream_primary_package_temperature[-1]

        # We can use the timestream measurement as an additional sweep point.
        # We average only the first 2048 points of the timeseries to avoid any drift.
        if False:
            self.sweep_freqs_MHz = np.hstack(
                (self.sweep_freqs_MHz, [self.noise_measurement_freq_MHz]))
            self.sweep_s21 = np.hstack(
                (self.sweep_s21, [original_timeseries[:2048].mean()]))
            self.sweep_errors = np.hstack((self.sweep_errors, [
                original_timeseries[:2048].real.std() / np.sqrt(2048) +
                1j * original_timeseries[:2048].imag.std() / np.sqrt(2048)
            ]))

        # Now put all the sweep data in increasing frequency order so it plots nicely
        order = self.sweep_freqs_MHz.argsort()
        self.sweep_freqs_MHz = self.sweep_freqs_MHz[order]
        self.sweep_s21 = self.sweep_s21[order]
        self.sweep_errors = self.sweep_errors[order]

        if delay_estimate is None:
            self.delay_estimate_microseconds = self._sweep_file.get_delay_estimate(
            ) * 1e6
        else:
            self.delay_estimate_microseconds = delay_estimate

        if mask_sweep_indicies is None:
            rr = fit_best_resonator(
                self.sweep_freqs_MHz,
                self.sweep_s21,
                errors=self.sweep_errors,
                delay_estimate=self.delay_estimate_microseconds)
        else:
            mask = np.ones(self.sweep_s21.shape, dtype=np.bool)
            mask[mask_sweep_indicies] = False
            rr = fit_best_resonator(
                self.sweep_freqs_MHz[mask],
                self.sweep_s21[mask],
                errors=self.sweep_errors[mask],
                delay_estimate=self.delay_estimate_microseconds)
        self._resonator_model = rr
        self.Q_i = rr.Q_i
        self.Q_i_err = qi_error(rr.result.params['Q'].value,
                                rr.result.params['Q'].stderr,
                                rr.result.params['Q_e_real'].value,
                                rr.result.params['Q_e_real'].stderr,
                                rr.result.params['Q_e_imag'].value,
                                rr.result.params['Q_e_imag'].stderr)
        self.fit_params = rr.result.params

        decimation_factor = self.timeseries_sample_rate / low_pass_cutoff_Hz
        normalized_timeseries = rr.normalize(self.noise_measurement_freq_MHz,
                                             original_timeseries)
        self.low_pass_normalized_timeseries = low_pass_fir(
            normalized_timeseries,
            num_taps=1024,
            cutoff=low_pass_cutoff_Hz,
            nyquist_freq=self.timeseries_sample_rate,
            decimate_by=decimation_factor)
        self.normalized_timeseries_mean = normalized_timeseries.mean()

        projected_timeseries = rr.project_s21_to_delta_freq(
            self.noise_measurement_freq_MHz,
            normalized_timeseries,
            s21_already_normalized=True)

        # calculate the number of samples for the deglitching window.
        # the following will be the next power of two above 1 second worth of samples
        window = int(2**np.ceil(np.log2(self.timeseries_sample_rate)))
        # reduce the deglitching window if we don't have enough samples
        if window > projected_timeseries.shape[0]:
            window = projected_timeseries.shape[0] // 2
        self.deglitch_window = window
        self.deglitch_threshold = deglitch_threshold
        if deglitch_threshold:
            deglitched_timeseries = deglitch_window(projected_timeseries,
                                                    window,
                                                    thresh=deglitch_threshold)
        else:
            deglitched_timeseries = projected_timeseries

        # TODO: should nyquist_freq be half the sample rate?
        self.low_pass_projected_timeseries = low_pass_fir(
            deglitched_timeseries,
            num_taps=1024,
            cutoff=low_pass_cutoff_Hz,
            nyquist_freq=self.timeseries_sample_rate,
            decimate_by=decimation_factor)
        self.low_pass_timestep = decimation_factor / self.timeseries_sample_rate

        self.normalized_model_s21_at_meas_freq = rr.normalized_model(
            self.noise_measurement_freq_MHz)
        self.normalized_model_s21_at_resonance = rr.normalized_model(rr.f_0)
        self.normalized_ds21_df_at_meas_freq = rr.approx_normalized_gradient(
            self.noise_measurement_freq_MHz)

        self.sweep_normalized_s21 = rr.normalize(self.sweep_freqs_MHz,
                                                 self.sweep_s21)

        self.sweep_model_freqs_MHz = np.linspace(self.sweep_freqs_MHz.min(),
                                                 self.sweep_freqs_MHz.max(),
                                                 1000)
        self.sweep_model_normalized_s21 = rr.normalized_model(
            self.sweep_model_freqs_MHz)
        self.sweep_model_normalized_s21_centered = self.sweep_model_normalized_s21 - self.normalized_timeseries_mean

        fractional_fluctuation_timeseries = deglitched_timeseries / (
            self.noise_measurement_freq_MHz * 1e6)
        self._fractional_fluctuation_timeseries = fractional_fluctuation_timeseries
        fr, S, evals, evects, angles, piq = iqnoise.pca_noise(
            fractional_fluctuation_timeseries,
            NFFT=None,
            Fs=self.timeseries_sample_rate)

        self.pca_freq = fr
        self.pca_S = S
        self.pca_eigvals = evals
        self.pca_eigvects = evects
        self.pca_angles = angles
        self.pca_piq = piq

        self.freqs_coarse, self.prr_coarse, self.pii_coarse = self.get_projected_fractional_fluctuation_spectra(
            NFFT=2**12)

        self._normalized_timeseries = normalized_timeseries[:2048].copy()
        self._close_files()