Source code for src.Libs.Simulation.BeamWeightDataClass

#!/usr/bin/env python3
"""
This file is part of the PAFFrontendSim.

The PAFFrontendSim is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License v3 as published by the Free Software Foundation.

The PAFFrontendSim is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
See the GNU General Public License for more details.

You should have received a copy of the GNU General Public License along with the PAFFrontendSim. If not, see https://www.gnu.org/licenses/.
"""
#############################################################################
# import libs
#############################################################################
import numpy as np
from matplotlib import pyplot as plt 
from copy       import copy, deepcopy

import LogClass
#############################################################################
# constants
#############################################################################
_CLASSNAME = "BeamWeightData"


# Keywords for the beam-weight type (determination type)
_Types     = {
            "ND"            :   'Not defined'                               ,
            "CFM"           :   'Conjungate field match'                    ,
            "MD"            :   'Maximum directivity'                       ,
            "SNR"           :   'Maximum S/N ratio (from noise Resistance)' ,
            "MSNR"          :   'Maximum S/N ratio (from lin. equations)'   
             }
    
#############################################################################
# classes
#############################################################################
[docs] class EfficiencyData : """ Class with all efficiencies belonging to the beam-weights """ def __init__( self ) : self.Eta_a = 1. # Aperture efficiency self.Eta_spill = 1. # Spill-over efficiency self.Eta_rad = 1. # Radiation efficiency self.Eta_total = 1. # total efficiency
[docs] def setEff ( self, ae = -1., sp = -1., rd = -1., to = -1.) : if ae > 0. : self.Eta_a = ae if sp > 0. : self.Eta_spill = sp if rd > 0. : self.Eta_rad = rd if to > 0. : self.Eta_total = to
[docs] class NoiseData : """ Class with all noise and signal temperatures belonging to the beam-weights """ def __init__( self ) : self.T_sig = 0. # Signal temperature [K] self.T_sky = 0. # Sky noise contribution [K] self.T_spill = 0. # Spill-over noise contribution [K] self.T_loss = 0. # Loss noise contribution [K] self.T_LNA = 0. # LNA noise temperature [K] self.T_rec = 0. # Receiver noise temperature [K] self.T_sys = 0. # system noise temperature [K] self.T_sys_oe = 0. # T_sys over eta [K]
[docs] def setTemp ( self, tsig = 0., tsky = 0., tspill = 0., tloss = 0., tlna = 0., trec = 0., tsys = 0., tsysoe = 0. ) : if tsig > 0. : self.T_sig = tsig if tsky > 0. : self.T_sky = tsky if tspill > 0. : self.T_spill = tspill if tloss > 0. : self.T_loss = tloss if tlna > 0. : self.T_lna = tlna if trec > 0. : self.T_rec = trec if tsys > 0. : self.T_sys = tsys if tsysoe > 0. : self.T_sys_oe = tsysoe
[docs] class BeamWeights : """ Class to handle beam-weight data """ ##################################### # Keywords for the file __KEYS = { "Comment" : '#' , "FileType" : '# Beam data:' , "Wavelength" : 'Wavelength:' , "Theta" : 'Theta:' , "Phi" : 'Phi:' , "Type" : 'Type:' , "Eta" : 'Efficiencies:' , "Noise" : 'Noise' , "Data" : 'Element:' } ##################################### # internal data and init method # init method def __init__( self, log = None, wl = 214., t= 0., p = 0. ): """ Initialize class. """ if log == None: self.log = LogClass.LogClass(_CLASSNAME) else: self.log = deepcopy(log) # logging class used for all in and output messages self.log.addLabel(self) self.Type = _Types['ND'] # Beam-weight type (determination algorithm) self.Wavelength = wl # Wavelength at which the simulation was run [mm] self.Theta = t # Theta angle of the pointing vector [deg] self.Phi = p # Phi angle of the pointing vector [deg] self.Efficiency = EfficiencyData() # actual efficiency values self.Noise = NoiseData() # actual noise performance of the beam-weights self.Data = [] # complex weights
[docs] def printOut( self, line, message_type, noSpace = True, lineFeed = True ) : """ Wrapper for the printOut method of the actual log class """ self.log.printOut(line, message_type, noSpace, lineFeed)
##################################### # File-IO methods # write file
[docs] def writeFile( self, filename): """ Method to write the beam-weight data to a file. Parameters: filename (string): Name of the file to be written. """ self.printOut("Writing File %s" % filename, self.log.FLOW_TYPE) fh = open(filename, 'w') # open file for writing # write header block fh.write(self.__KEYS['Comment'] + " PAF-Simulator Beam-weight file\n" ) fh.write(self.__KEYS['Comment'] + " \n" ) fh.write(self.__KEYS['Comment'] + " File-Header\n" ) fh.write(self.__KEYS['FileType'] + "\n" ) fh.write(self.__KEYS['Comment'] + "# Above: type Identifier (only lines after Type-ID are parsed during reading\n" ) fh.write(self.__KEYS['Comment'] + " \n" ) fh.write(self.__KEYS['Type'] + " %s (algorithm used for calculation)\n" % self.Type) fh.write(self.__KEYS['Comment'] + " Exitation data (Wavelength, I0 and input impedance)\n") fh.write(self.__KEYS['Wavelength'] + " %.3f mm\n" % self.Wavelength ) fh.write(self.__KEYS['Theta'] + " %.5f [deg]\n" % self.Theta ) fh.write(self.__KEYS['Phi'] + " %.5f [deg]\n" % self.Phi ) fh.write(self.__KEYS['Comment'] + " \n" ) fh.write(self.__KEYS['Comment'] + " Performance data\n") fh.write(self.__KEYS['Eta'] + " %.3f %.3f %.3f " % (self.Efficiency.Eta_a, self.Efficiency.Eta_spill, self.Efficiency.Eta_rad,) ) fh.write( "(Apperture efficiency, spill-over efficiency, radiation efficiency)\n") fh.write(self.__KEYS['Noise'] + " %.3f %.3f %.3f %.3f %.3f %.3f %.3f " % (self.Noise.T_sig, self.Noise.T_sky, self.Noise.T_spill, self.Noise.T_loss, self.Noise.T_rec, self.Noise.T_sys, self.Noise.T_sys_oe)) fh.write( "(Tsig, Tsky, Tspill, Tloss, Trec, Tsys, Tsys/eta_a)\n") fh.write(self.__KEYS['Comment'] + " Data-Block (Element-number amplitude phase):\n" ) # write data block for element in range (0, len(self.Data)) : fh.write(self.__KEYS['Data'] + " %d %.6f %.6f\n" % ( element, np.absolute(self.Data[element]), np.angle (self.Data[element]) ) ) # write end block fh.write(self.__KEYS['Comment'] + " \n" ) fh.write(self.__KEYS['Comment'] + " end of file\n" ) fh.close()
[docs] def readFile( self, filename) : """ Method to read an impedance matrix data file. Parameters: filename (string): Name of the input file Returns: idOK (boolean): True on success, False on error. """ Data = [] # preliminary data array self.Data = [] # delete existing data self.printOut("Reading File %s" % filename, self.log.FLOW_TYPE) idOK = False with open(filename, 'r') as f : # open file for reading for line in f : # and loop all lines in the file line = line.strip() # remove CR / NL / EOF from line end columns = line.split() # split line if ( len(columns) == 0) : continue if line == self.__KEYS['FileType'] : # check for correct type-ID idOK = True if not idOK : continue # continue with next line if type-ID was not set so far # Parse header line if columns[0] == self.__KEYS['Type'] : self.Type = columns[1] continue if columns[0] == self.__KEYS['Wavelength'] : self.Wavelength = float(columns[1]) continue if columns[0] == self.__KEYS['Theta'] : self.Theta = float(columns[1]) continue if columns[0] == self.__KEYS['Phi'] : self.Phi = float(columns[1]) continue if columns[0] == self.__KEYS['Eta'] : self.Efficiency.setEff(ae = float(columns[1]), sp = float(columns[2]), rd = float(columns[3])) continue if columns[0] == self.__KEYS['Noise'] : self.Noise.setTemp(tsig = float(columns[1]), tsky = float(columns[2]), tspill = float(columns[3]), tloss = float(columns[4]), trec = float(columns[5]), tsys = float(columns[6]), tsysoe = float(columns[7]) ) continue # read beam-weight data if columns[0] == self.__KEYS['Data'] : elem = int(float(columns[1])) amp = float(columns[2]) ph = float(columns[3]) Data.append([elem, amp*np.exp(1j*ph)]) f.close() # sort data array if idOK : self.Data = np.zeros(len(Data), dtype = complex) for el in Data : self.Data[el[0]] = el[1] else : self.printOut(" Error while reading", self.log.ERROR_TYPE, False) return idOK
##################################### # plot data
[docs] def plot( self, figname = "", show = False, freq = -1 ) : """ Plotting the Beam-weights. Parameters: figname (string): filename of the created plot show (boolean): flag if plot is shown (True) or safed to disk """ self.printOut("Plotting Beam-Weights", self.log.FLOW_TYPE) if freq < 0 : f = "" else : f = "%.2fGHz : " % freq fig, ax1 = plt.subplots() plt.title("%s Beam-Weight data : " % (f + self.Type)) x = np.asarray(range(0, len(self.Data)+2)) amp = np.zeros(len(self.Data)+2) amp[1:-1] = np.absolute(self.Data) amp = amp / np.max(amp) phase = np.zeros(len(self.Data)+2) phase[1:-1] = np.angle(self.Data, deg=True) ax1.step(x, amp, where='mid', color='blue') plt.fill_between(x, amp, step="mid", alpha=0.6, color ='blue') ax1.set_ylabel("relative Amplitude", color='blue') ax1.set_xlabel("Element-Number") for label in ax1.get_yticklabels(): label.set_color('blue') ax2 = ax1.twinx() ax2.set_ylim(-180., 180.) ax2.step(x, phase, where='mid', color='red', linestyle='--') ax2.set_ylabel("Phase [deg]", color='red') for label in ax2.get_yticklabels(): label.set_color('red') if figname != "" : fig.savefig(figname) if show : plt.show() else : plt.show() plt.close()
##################################### # modify data
[docs] def normalize(self, norm = 1. + 0*1j) : self.printOut("Normalizing weights to %.3f %+.3fi" % (norm.real, norm.imag), self.log.FLOW_TYPE) self.Data = np.asarray(self.Data) n = np.sqrt(np.dot(self.Data, self.Data.conj())) if n != 0. : self.Data = self.Data *norm / n return n