def chkshrinkstatechanged(self): """Callback deactivating the shrink fields when shrink checkbox is unchecked. """ utils.trace() self.azfield.setEnabled(self.chkshrink.isChecked()) self.elfield.setEnabled(self.chkshrink.isChecked())
def clearplot(self): utils.trace('in') if self._plot is not None: if self._plot[0] == 'surface': self._plot[1].remove() figure = self._plot[3] if len(figure.axes) > 1: figure.delaxes(figure.axes[1]) ax = self._earthplot._axes cbax = self._earthplot._clrbar_axes divider = make_axes_locatable(ax) divider.set_horizontal( [Size.AxesX(ax), Size.Fixed(0), Size.Fixed(0)]) elif self._plot[0] == 'contour': for element in self._plot[1].collections: try: element.remove() except ValueError: print(element) if len(self._plot) > 2: try: self._plot[2][0].remove() except TypeError: print("None element cannot be removed") try: self._plot[3].remove() except AttributeError: print("None element have no attribute remove") for element in self._plot[4]: element.remove() self._plot = None utils.trace('out')
def elevation(self, stalon, stalat): """Compute elevation of spacecraft seen from a station on the ground. """ utils.trace('in') # compute phi phi = np.arccos( np.cos(cst.DEG2RAD * stalat) * np.cos(cst.DEG2RAD * (self._viewer.longitude() - stalon))) # compute elevation elev = np.reshape([ 90 if phi == 0 else cst.RAD2DEG * np.arctan( (np.cos(phi) - (cst.EARTH_RAD_EQUATOR_M / (cst.EARTH_RAD_EQUATOR_M + self._viewer.altitude()))) / np.sin(phi)) for phi in phi.flatten() ], phi.shape) # remove station out of view elev = np.where( np.absolute(stalon - self._viewer.longitude()) < 90, elev, -1) utils.trace('out') # Return vector return elev
def settitle(self, title: str): """Set Earth plot title. """ utils.trace('in') self._plot_title = title self._axes.set_title(self._plot_title) utils.trace('out')
def export_pattern(self): """Open QDialog box to select file//directory where to export the file. """ utils.trace('in') # get directory directory = self._earthplot.rootdir # recreate default filename with .pat extension if isinstance(self._pattern, MultiGrd): default_filename = '' else: origin_filename = os.path.basename(self._config['filename']) default_filename = origin_filename[:-3] + 'pat' # Get filename for exporting file filename, _ = \ QFileDialog.getSaveFileName(parent=self._mainwindow, caption='Select file', directory=os.path.join( directory, default_filename), filter='pattern file (*.pat)') # get pattern to export if filename: self._pattern.export_to_file(filename, shrunk=self._pattern._shrink) utils.trace('out')
def interpolate_slope(self, az, el, set: int = 0, spline=None): """return interpolated value of the pattern """ utils.trace('in') if spline is None: if self._x[set][0, 0] == self._x[set][1, 0]: x = self._x[set][0, :] y = self._y[set][:, 0] z = self.slope(set).T else: x = self._x[set][:, 0] y = self._y[set][0, :] z = self.slope(set) if x[0] > x[1]: x = x[::-1] z = z[::-1, :] if y[0] > y[1]: y = y[::-1] z = z[:, ::-1] z[np.where(np.isnan(z))] = -99 z[np.where(np.isneginf(z))] = -99 spline = interp.RectBivariateSpline(x, y, z) # transform azel into native coordinates x, y = self.azel2xy(az, el) # prepare results for return statement a, b = np.reshape(spline.ev(x.flatten(), y.flatten()), np.array(az).shape), spline utils.trace('out') return a, b
def set_to_plot(self, cross=False): """Set the pattern data to be plotted by the plot method. """ utils.trace('in') # select to plot co or cross if not cross: self._to_plot = self.copol() else: self._to_plot = self.cross() if self._to_plot is None: print( 'set_to_plot: No crosspol data available. Stick to copol.') self._to_plot = self.copol() # if shrink option if self._shrink: # shrink_copol uses interpolate_copol function that # uses _to_plot attribute self._to_plot = self.shrink_copol(self._azshrink, self._elshrink) elif self.set(self.configure(), 'expand', False): # expand_copol uses interpolate_copol function that # uses _to_plot attribute self._to_plot = self.expand_copol(self._azshrink, self._elshrink) utils.trace('out')
def set_resolution(self, resolution: str = 'c'): """Set Earth map resolution. """ utils.trace('in') self._resolution = resolution self._earth_map.resolution = self._resolution self.draw_elements() utils.trace('out')
def configure(self, pattern): """This method configure the fields of the dialog with the pattern configuration values. pattern is the antenna pattern object which provide the configuration for this GUI """ utils.trace('in') try: self.title_field.setText(pattern._conf['title']) except KeyError: print('pattern.dialog: No title in pattern._conf dictionary.') self.lon_field.setText(str(pattern.satellite().longitude())) self.lat_field.setText(str(pattern.satellite().latitude())) self.alt_field.setText(str(pattern.satellite().altitude())) self.yaw_field.setText( str(pattern.set(pattern.configure(), 'sat_yaw', 0.0))) if pattern._display_slope: low = np.amin(pattern.configure()['slopes']) high = np.amax(pattern.configure()['slopes']) self.isolevel_field.setText('{},{}'.format(low, high)) else: self.isolevel_field.setText(self.get_isolevel()) self.chk_revert_x.setChecked(pattern._revert_x) self.chk_revert_y.setChecked(pattern._revert_y) self.chk_rotate.setChecked(pattern._rotated) self.chkxpol.setChecked(pattern._use_second_pol) self.chkslope.setChecked(pattern._display_slope) _shrink = pattern._shrink _expand = pattern.set(pattern.configure(), 'expand', False) self.chkshrink.setChecked(_shrink | _expand) if _shrink != _expand: self.shrink_button_state_changed() elif _shrink and _expand: print("Error: you cannot shrink and expand in the same time.") self.chk_offset.setChecked(pattern._offset) self.chksurf.setChecked(pattern.set( pattern.configure(), 'Color surface', False)) if pattern._shrink: self.azfield.setText(str(pattern._azshrink)) self.elfield.setText(str(pattern._elshrink)) if pattern._offset: self.az_offset_field.setText(str(pattern._azimuth_offset)) self.el_offset_field.setText(str(pattern._elevation_offset)) self.offset_button.setChecked(pattern.set(pattern.configure(), 'azeloffset', True)) self.offset_button_state_changed() # disable use second pol option if second pol not available if len(pattern._E_cr): self.chkxpol.setEnabled(True) else: self.chkxpol.setEnabled(False) self.refresh_isolevel() utils.trace('out')
def chk_display_slope_changed(self): """Callback changing the range displayed in case the display slope option is checked. """ utils.trace() if self.chkslope.isChecked(): self.isolevel_field.setText('{},{}'.format( self._patternctlr._slope_range[0], self._patternctlr._slope_range[1])) else: self.isolevel_field.setText(self.get_isolevel())
def projection(self, proj: str = None): """This function allows access to attribute _projection. """ utils.trace('in') if proj: if proj == 'nsper' or proj == 'cyl': self._projection = proj else: raise ValueError("Projection is either 'nsper' or 'cyl'.") utils.trace('out') return self._projection
def save(self, filename=None): """Save the plot with given filename. If file name not provided, use last used name. """ utils.trace('in') # store file name for future call to this function if filename: self.filename = filename # save plot into file # plt.savefig(self.filename, dpi='figure') self.print_figure(self.filename) utils.trace('out')
def set_meridians(self, m: str, refresh: bool = False): """Set the value of the private attribute _meridians. If refresh is True, redraw Earth. Return the value passed to the function. """ utils.trace('in') self._meridians = m if refresh: self.drawearth(proj=self._projection, resolution=self._resolution) self.draw_axis() self.draw() utils.trace('out') return self._meridians
def set_countries(self, c: str, refresh: bool = False): """Set private attribute _countries value. If refresh is True, redraw Earth. Return the value passed to the function. """ utils.trace('in') self._countries = c if refresh: self.drawearth(proj=self._projection, resolution=self._resolution) self.draw_axis() self.draw() utils.trace('out') return self._countries
def cross(self, set=0): """Compute crosspolarisation magnitude (in dBi). Overloading Grd.cross(). """ utils.trace('in') _, nb_rows, nb_cols, _ = np.array(self._E_cr).shape E_cr = np.zeros((nb_rows, nb_cols), dtype=complex) for r, c in itertools.product(range(nb_rows), range(nb_cols)): E_cr[r][c] = np.dot(self._E_cr[set][r][c], self._excitation_law) z = 20.0 * np.log10(np.abs(E_cr)) z[np.where(np.isnan(z))] = -99 z[np.where(np.isneginf(z))] = -99 utils.trace('out') return z
def configure(self, config=None, dialog=True): """This method is used to update PatternControler attributes either via predefined configuration or via dialog window. """ utils.trace('in') if config: self._config.update(config) self._pattern.configure(config=config) if dialog: # if dialog use the GUI to update self._pdialog.configure(self._pattern) self._pdialog.setModal(True) self._pdialog.show() self._pdialog.exec_() utils.trace('out')
def get_file_key(self, filename): utils.trace('in') file_index = 1 if type(filename) is list: f = os.path.basename(filename[0]) else: f = os.path.basename(filename) file_key = f + ' ' + str(file_index) while (file_key in self._patterns) and file_index <= 50: file_index = file_index + 1 file_key = f + ' ' + str(file_index) if file_index == 50: print(('Max repetition of same file reached.' ' Index 50 will be overwritten')) utils.trace('out') return file_key
def read_file(self, filename): """Overloading of read_file function from Grd class to handle a set of grd instead of only one """ utils.trace('in') nb_sets_list = [] grid_list = [] x_list = [] y_list = [] E_co_list = [] E_cr_list = [] # create progres bar for f in filename: (nb_sets, grid, x, y, E_co, E_cr) = Grd.read_file(None, f) nb_sets_list.append(nb_sets) grid_list.append(grid) x_list.append(x) y_list.append(y) E_co_list.append(E_co) E_cr_list.append(E_cr) # for all field common to the RE unit files, use only the # value from the first file in the series nb_sets = nb_sets_list[0] grid = grid_list[0] # if grid of the files are different throw exception for x in x_list[1:]: if not np.array_equal(x_list[0], x): raise UnassortedGrid("x coordinate grids are not identical.") x = x_list[0] for y in y_list[1:]: if not np.array_equal(y_list[0], y): raise UnassortedGrid("x coordinate grids are not identical.") y = y_list[0] nb_re, nb_set, nb_rows, nb_col = np.array(E_co_list).shape E_co = np.zeros((nb_set, nb_rows, nb_col, nb_re), dtype=complex) E_cr = np.zeros((nb_set, nb_rows, nb_col, nb_re), dtype=complex) for s, r, c, e in itertools.product(range(nb_set), range(nb_rows), range(nb_col), range(nb_re)): # print('{} {} {} {}'.format(s, r, c, e)) E_co[s][r][c][e] = E_co_list[e][s][r][c] E_cr[s][r][c][e] = E_cr_list[e][s][r][c] utils.trace('out') return (nb_sets, grid, x, y, E_co, E_cr)
def plot(self): """Plot the antenna pattern into the parent EarthPlot. """ utils.trace('in') if self._plot: self.clearplot() self._plot = self._pattern.plot() try: if self._config['display_slope'] is True: self._plot_type = 'surf' else: self._plot_type = 'contour' self._earthplot.draw() except KeyError: # if KeyError it means that Cancel button has been pressed print('control.plot: issue with display_slope attribute') utils.trace('out')
def draw_elements(self): """This method redraw all elements of the earth plot """ utils.trace('in') # clear display and reset it self._axes.clear() # update the zoom self.updatezoom() # Draw Earth in the background self.drawearth(proj=self._projection, resolution=self._resolution) # draw all patterns at_least_one_slope = False for key in self._patterns: self._patterns[key].plot() if 'display_slope' in self._patterns[key].get_config(): if self._patterns[key].get_config()['display_slope']: at_least_one_slope = True if not at_least_one_slope and len(self._patterns): for i in range(len(self._figure.axes)): if i: self._figure.delaxes(self._figure.axes[i]) # draw all Elevation contour for element in self._elev: self._elev[element].plot() # draw stations for s in self._stations: s.clearplot() s.plot() # draw polygons for p in self._polygons: p.clearplot() p.plot() # draw axis self.draw_axis() # call to surcharged draw function self.draw() utils.trace('out')
def slope(self, set: int = 0): """Return gradient of Co-polarisation pattern """ utils.trace('in') if self._E_grad_co == []: # get gradient of Azimuth coordinate azimuth_grad, _ = np.gradient(self.azimuth()) # get gradient of Elevation coordinate _, elevation_grad = np.gradient(self.elevation()) # get gradient of pattern in Azimuth and Elevation co_grad_az, co_grad_el = np.gradient(self._to_plot) # normalize gradient of pattern in Azimuth direction co_grad_az /= azimuth_grad # normalize gradient of pattern in Elevation direction co_grad_el /= elevation_grad # RSS the 2 directions gradient in one scalar field self._E_grad_co = np.sqrt(co_grad_az**2 + co_grad_el**2) utils.trace('out') return self._E_grad_co
def draw_axis(self): utils.trace('in') if self._projection == 'nsper': self._axes.set_xlabel('Azimuth (deg)') self._axes.set_ylabel('Elevation (deg)') # get viewer coordinate in rendering frame viewer_x, viewer_y = self._earth_map(self._viewer.longitude(), self._viewer.latitude()) # compute and add x-axis ticks azticks = np.arange(self._zoom.min_azimuth, self._zoom.max_azimuth + 0.1, 2) self._axes.set_xticks(self.az2x(azticks) + viewer_x) self._axes.set_xticklabels('{0:0.1f}'.format(f) for f in azticks) # compute and add y-axis ticks elticks = np.arange(self._zoom.min_elevation, self._zoom.max_elevation + 0.1, 2) self._axes.set_yticks(self.el2y(elticks) + viewer_y) self._axes.set_yticklabels('{0:0.1f}'.format(f) for f in elticks) elif self._projection == 'cyl': self._axes.set_xlabel('Longitude (deg)') self._axes.set_ylabel('Latitude (deg)') lonticks = np.arange( int(self._zoom.min_longitude / 10) * 10, self._zoom.max_longitude + 0.1, 20) lonticks_converted, _ = np.array( self._earth_map( lonticks, np.ones(lonticks.shape) * self._zoom.min_latitude)) self._axes.set_xticks(lonticks_converted) self._axes.set_xticklabels('{0:0.1f}'.format(f) for f in lonticks) # compute and add y-axis ticks latticks = np.arange( int(self._zoom.min_latitude / 10) * 10, self._zoom.max_latitude + 0.1, 20) _, latticks_converted = np.array( self._earth_map( np.ones(latticks.shape) * self._zoom.min_longitude, latticks)) self._axes.set_yticks(latticks_converted) self._axes.set_yticklabels('{0:0.1f}'.format(f) for f in latticks) self._axes.tick_params(axis='both', width=0.2) self._axes.set_title(self._plot_title) utils.trace('out')
def plot(self): utils.trace('in') emap = self._parent.get_earthmap() # define grid nx = 200 ny = 200 xvec = np.linspace(emap.xmin, emap.xmax, nx) yvec = np.linspace(emap.ymin, emap.ymax, ny) xgrid, ygrid = np.meshgrid(xvec, yvec) longrid, latgrid = emap(xgrid, ygrid, inverse=True) # define Elevation matrix elevgrid = self.elevation(longrid, latgrid) self._plot = emap.contour(xgrid, ygrid, elevgrid, [self._config['elevation']], colors=self._config['colors'], linestyles=self._config['linestyles'], linewidths=self._config['linewidths']) utils.trace('out') return self._plot
def add_menu_items(self, station_key): """Add Pattern menu elements to exploit current pattern. """ utils.trace('in') # get Pattern menu reference and add sub menu for current pattern stns_menu = self._app.getmenuitem('Misc.>Stations').menu() stn_name = self._station.configure()['name'] stn_menu = stns_menu.addMenu(stn_name) # add Remove action rem_action = QAction('Remove', self._app) rem_action.triggered.connect(self.remove_station) stn_menu.addAction(rem_action) # add Edit action edit_action = QAction('Edit', self._app) stn_menu.addAction(edit_action) edit_action.triggered.connect(self.edit_station) utils.trace('out') # return submenu return stn_menu
def drawelevation(self, level=(10, 20, 30)): utils.trace('in') # define grid iNx = 200 iNy = 200 fXlin = np.linspace(self._earth_map.xmin, self._earth_map.xmax, iNx) fYlin = np.linspace(self._earth_map.ymin, self._earth_map.ymax, iNy) fXMesh, fYMesh = np.meshgrid(fXlin, fYlin) fLonMesh, fLatMesh = self._earth_map(fXMesh, fYMesh, inverse=True) # define Elevation matrix fElev = self.elevation(fLonMesh, fLatMesh) csElev = self._earth_map.contour(fXMesh, fYMesh, fElev, level, colors='black', linestyles='dotted', linewidths=0.5) utils.trace('out') return csElev
def remove_pattern(self): """Callback maker for remove pattern menu items. """ utils.trace('in') menu = self._pattern_sub_menu menu_action = menu.menuAction() menu.parent().removeAction(menu_action) # delete reference to pattern object del self._earthplot._patterns[self._config['key']] # clear the plot and redraw EarthPlot self.clearplot() self._earthplot.draw() # refresh pattern combo box itemlist = [''] itemlist.extend(self._earthplot._patterns.keys()) self._mainwindow.setpatterncombo(itemlist) utils.trace('out')
def add_menu_items(self, file_key): """Add Pattern menu elements to exploit current pattern. """ utils.trace('in') # get Pattern menu reference and add sub menu for current pattern patternmenu = self._pattern_menu.addMenu(file_key) # add Remove action remove_pat_action = QAction('Remove', self._mainwindow) remove_pat_action.triggered.connect(self.remove_pattern) patternmenu.addAction(remove_pat_action) # add Edit action edit_pat_action = QAction('Edit', self._mainwindow) patternmenu.addAction(edit_pat_action) edit_pat_action.triggered.connect(self.edit_pattern) # add Export action export_pat_action = QAction('Export', self._mainwindow) patternmenu.addAction(export_pat_action) export_pat_action.triggered.connect(self.export_pattern) utils.trace('out') # return submenu return patternmenu
def phase(self, iunit, component1, component2): """Convert (C1, C2) to phase (deg) depending on IUNIT value """ utils.trace('in') def convert(component1, component2): """Convert from real/imag electrical field to phase (in degrees) """ return np.angle(component1 + 1j * component2) * cst.RAD2DEG def identity(_, component2): """Directly returns component2 which is the phase. """ return component2 # create the processing dictionary converter = {0: convert, 1: identity} # convert/extract the magnitude phs = converter[iunit](component1, component2) utils.trace('out') return phs
def magnitude(self, iunit, component1, component2): """Convert (C1, C2) to magnitude (dB) depending on IUNIT value """ utils.trace('in') def convert(component1, component2): """Convert from real/imag electrical field values to magnitude. """ return 20 * np.log10(np.absolute(component1 + 1j * component2)) def identity(component1, _): """Return directly the magnitude which is the first component. """ return component1 # create the processing dictionary converter = {0: convert, 1: identity} # convert/extract the magnitude mag = converter[iunit](component1, component2) utils.trace('out') return mag
def loadpattern(self, conf=None): """Load and display a grd file. """ utils.trace('in') try: filename = conf['filename'] except KeyError: print('load_pattern:File name is mandatory.') utils.trace('out') return None file_key = self.get_file_key(filename) conf['key'] = file_key try: pattern = PatternControler(parent=self, config=conf) except PatternNotCreatedError as pnc: print(pnc.__str__()) utils.trace('out') return None if 'sat_lon' not in conf: dialog = True conf['sat_lon'] = self._viewer.longitude() conf['sat_lat'] = self._viewer.latitude() conf['sat_alt'] = self._viewer.altitude() else: dialog = False pattern.configure(dialog=dialog, config=conf) # Add grd in grd dictionary self._patterns[file_key] = pattern # refresh pattern combo box itemlist = [''] itemlist.extend(self._patterns.keys()) self._app.setpatterncombo(itemlist) # return pattern controler instance utils.trace('out') return self._patterns[file_key]