Source code for setigen.voltage.level_utils

import numpy as np


[docs]def get_unit_drift_rate(raw_voltage_backend, fftlength, int_factor=1): """ Calculate drift rate corresponding to a 1x1 pixel shift in the final data product. This is equivalent to dividing the fine channelized frequency resolution with the time resolution. Parameters ---------- raw_voltage_backend : RawVoltageBackend Backend object to infer observation parameters fftlength : int FFT length to be used in fine channelization int_factor : int, optional Integration factor to be used in fine channelization Returns ------- unit_drift_rate : float Drift rate in Hz / s """ df = raw_voltage_backend.chan_bw / fftlength dt = raw_voltage_backend.tbin * fftlength * int_factor return df / dt
[docs]def get_level(snr, raw_voltage_backend, fftlength, obs_length=None, num_blocks=None, length_mode='obs_length',): """ Calculate required signal level as a function of desired SNR, assuming initial noise variance of 1. This is calculated for a single polarization. This further assumes the signal is non-drifting and centered on a finely channelized bin. Parameters ---------- snr : float Signal-to-noise ratio (SNR) raw_voltage_backend : RawVoltageBackend Backend object to infer observation parameters fftlength : int, optional FFT length to be used in fine channelization obs_length : float, optional Length of observation in seconds, if in `obs_length` mode num_blocks : int, optional Number of data blocks to record, if in `num_blocks` mode length_mode : str, optional Mode for specifying length of observation, either `obs_length` in seconds or `num_blocks` in data blocks Returns ------- level : float Level, or amplitude, for a real voltage cosine signal """ if length_mode == 'obs_length': if obs_length is None: raise ValueError("Value not given for 'obs_length'.") num_blocks = raw_voltage_backend.get_num_blocks(obs_length) elif length_mode == 'num_blocks': if num_blocks is None: raise ValueError("Value not given for 'num_blocks'.") pass else: raise ValueError("Invalid option given for 'length_mode'.") # Get amplitude required for cosine signal to get required SNR int_factor = 1 # level has no dependence on integration factor dt = raw_voltage_backend.tbin * fftlength * int_factor tchans = int(raw_voltage_backend.time_per_block * num_blocks / dt) chi_df = 2 * raw_voltage_backend.num_pols * int_factor # main_mean = (raw_voltage_backend.requantizer.target_sigma)**2 * chi_df * raw_voltage_backend.filterbank.max_mean_ratio I_per_SNR = np.sqrt(2 / chi_df) / tchans**0.5 signal_level = 1 / (raw_voltage_backend.num_branches * fftlength / 4)**0.5 * (snr * I_per_SNR)**0.5 return signal_level
[docs]def get_leakage_factor(f_start, raw_voltage_backend, fftlength): """ Get factor to scale up signal amplitude from spectral leakage based on the position of a signal in a fine channel. This calculates an inverse normalized sinc value based on the position of the signal with respect to finely channelized bins. Since intensity goes as voltage squared, this gives a scaling proportional to 1/sinc^2 in finely channelized data products; this is the standard fine channel spectral response. Parameters ---------- f_start : float Signal frequency, in Hz raw_voltage_backend : RawVoltageBackend Backend object to infer observation parameters fftlength : int, optional FFT length to be used in fine channelization Returns ------- leakage_factor : float Factor to multiply to signal level / amplitude """ spectral_bin_frac = np.modf((f_start - raw_voltage_backend.fch1) / (raw_voltage_backend.chan_bw / fftlength))[0] spectral_bin_frac = np.min([spectral_bin_frac, 1 - spectral_bin_frac]) return 1 / np.sinc(spectral_bin_frac)
[docs]def get_total_obs_num_samples(obs_length=None, num_blocks=None, length_mode='obs_length', num_antennas=1, sample_rate=3e9, block_size=134217728, num_bits=8, num_pols=2, num_branches=1024, num_chans=64): """ Calculate number of required real voltage time samples for as given `obs_length` or `num_blocks`, without directly using a `RawVoltageBackend` object. Parameters ---------- obs_length : float, optional Length of observation in seconds, if in `obs_length` mode num_blocks : int, optional Number of data blocks to record, if in `num_blocks` mode length_mode : str, optional Mode for specifying length of observation, either `obs_length` in seconds or `num_blocks` in data blocks num_antennas : int Number of antennas sample_rate : float Sample rate in Hz block_size : int Block size used in recording GUPPI RAW files num_bits : int Number of bits in requantized data (for saving into file). Can be 8 or 4. num_pols : int Number of polarizations recorded num_branches : int Number of branches in polyphase filterbank num_chans : int Number of coarse channels written to file Returns ------- num_samples : int Number of samples """ tbin = num_branches / sample_rate chan_bw = 1 / tbin bytes_per_sample = 2 * num_pols * num_bits / 8 if length_mode == 'obs_length': if obs_length is None: raise ValueError("Value not given for 'obs_length'.") num_blocks = int(obs_length * chan_bw * num_antennas * num_chans * bytes_per_sample / block_size) elif length_mode == 'num_blocks': if num_blocks is None: raise ValueError("Value not given for 'num_blocks'.") pass else: raise ValueError("Invalid option given for 'length_mode'.") return num_blocks * int(block_size / (num_antennas * num_chans * bytes_per_sample)) * num_branches