[docs]classSelectiveLeakage(Serializable,Isolation):"""Model of frequency-selective transmit-receive leakage."""yaml_tag="SelectiveLeakage"__leakage_response:np.ndarray# Impulse response of the leakage modeldef__init__(self,leakage_response:np.ndarray,*args,**kwargs)->None:""" Args: leakage_response (np.ndarray): Three-dimensional leakge impulse response matrix :math:`\\mathbf{H}` of dimensions :math:`M \\times N \\times L`, where :math:`M` is the number of receive streams and :math:`N` is the number of transmit streams and :math:`L` is the number of samples in the impulse response. Raises: ValueError: If the leakage response matrix has invalid dimensions. """ifleakage_response.ndim!=3:raiseValueError(f"Leakage response matrix must be a three-dimensional array (has {leakage_response.ndim} dimensions)")# Initialize base classesSerializable.__init__(self)Isolation.__init__(self,*args,**kwargs)# Initialize class attributesself.__leakage_response=leakage_response
[docs]@classmethoddefNormal(cls:Type[SelectiveLeakage],device:SimulatedDevice,num_samples:int=100,mean:float=1.0,variance:float=1.0,)->SelectiveLeakage:"""Initialize a frequency-selective leakage model with a normally distributed frequency response. Args: mean (float, optional): Mean of the frequency response in real and imaginary parts. One by default. variance (float, optional): Variance of the frequency response in real and imaginary parts. One by default. Returns: An initialized selective frequency model. """frequency_response=np.random.normal(np.sqrt(0.5)*mean,variance,(device.antennas.num_receive_antennas,device.antennas.num_transmit_antennas,num_samples,),)+1j*np.random.normal(np.sqrt(0.5)*mean,variance,(device.antennas.num_receive_antennas,device.antennas.num_transmit_antennas,num_samples,),)leakage_response=ifft(frequency_response,axis=2,norm="backward")returncls(leakage_response=leakage_response)
@propertydefleakage_response(self)->np.ndarray:"""Leakage impulse response matrix. Numpy matrix of dimensions :math:`M \\times N \\times L`, where :math:`M` is the number of receive streams and :math:`N` is the number of transmit streams and :math:`L` is the number of samples in the impulse response. """returnself.__leakage_responsedef_leak(self,signal:Signal)->Signal:num_leaked_samples=self.leakage_response.shape[2]+signal.num_samples-1leaking_samples=np.zeros((self.leakage_response.shape[0],num_leaked_samples),dtype=np.complex_)form,ninnp.ndindex(self.leakage_response.shape[0],signal.num_streams):# The leaked signal is the convolution of the transmitted signal with the leakage responseleaking_samples[m,:]+=convolve(self.leakage_response[m,n,:],signal[n,:].flatten(),"full")[:num_leaked_samples]returnsignal.from_ndarray(leaking_samples)