def psd(self, freq):
        """Tune to specified center frequency and compute Power Spectral Density"""
        if not self.device.is_streaming:
            raise RuntimeError('Streaming is not initialized, you must run setup() first!')

        # Tune to new frequency in main thread
        logger.debug('  Frequency hop: {:.2f} Hz'.format(freq))
        t_freq = time.time()
        if self.device.freq != freq:
            # Deactivate streaming before tuning
            if self._reset_stream:
                self.device.device.deactivateStream(self.device.stream)

            # Actually tune to new center frequency
            self.device.freq = freq

            # Reactivate straming after tuning
            if self._reset_stream:
                self.device.device.activateStream(self.device.stream)

            # Delay reading samples after tuning
            if self._tune_delay:
                t_delay = time.time()
                while True:
                    self.device.read_stream()
                    t_delay_end = time.time()
                    if t_delay_end - t_delay >= self._tune_delay:
                        break
                logger.debug('    Tune delay: {:.3f} s'.format(t_delay_end - t_delay))
        else:
            logger.debug('    Same frequency as before, tuning skipped')
        psd_state = self._psd.set_center_freq(freq)
        t_freq_end = time.time()
        logger.debug('    Tune time: {:.3f} s'.format(t_freq_end - t_freq))

        for repeat in range(self._buffer_repeats):
            logger.debug('    Repeat: {}'.format(repeat + 1))
            # Read samples from SDR in main thread
            t_acq = time.time()
            acq_time_start = datetime.datetime.utcnow()
            self.device.read_stream_into_buffer(self._buffer)
            acq_time_stop = datetime.datetime.utcnow()
            t_acq_end = time.time()
            logger.debug('      Acquisition time: {:.3f} s'.format(t_acq_end - t_acq))

            # Start FFT computation in another thread
            self._psd.update_async(psd_state, numpy.copy(self._buffer))

            t_final = time.time()

            if _shutdown:
                break

        psd_future = self._psd.result_async(psd_state)
        logger.debug('    Total hop time: {:.3f} s'.format(t_final - t_freq))

        return (psd_future, acq_time_start, acq_time_stop)
Tune to specified center frequency and compute Power Spectral Density