diff --git a/CEP/Calibration/BBSControl/scripts/parmdbplot.py b/CEP/Calibration/BBSControl/scripts/parmdbplot.py index 5f24758afae9dcf73ea976910b79a18dd144ab28..6fc5c1c3202d0b7d35c8c46c4c3c6995a4c1e7ff 100755 --- a/CEP/Calibration/BBSControl/scripts/parmdbplot.py +++ b/CEP/Calibration/BBSControl/scripts/parmdbplot.py @@ -20,13 +20,7 @@ # # $Id$ -import sys -import lofar.parmdb as parmdb -import copy -import math -import numpy - -import matplotlib +import sys, copy, math, numpy, signal from matplotlib.backends.backend_qt4agg import FigureCanvasQTAgg as FigureCanvas from matplotlib.backends.backend_qt4agg import NavigationToolbar2QTAgg as NavigationToolbar from matplotlib.figure import Figure @@ -35,6 +29,9 @@ from matplotlib.font_manager import FontProperties from PyQt4.QtCore import * from PyQt4.QtGui import * +import lofar.parmdb as parmdb + +signal.signal(signal.SIGINT, signal.SIG_DFL) __styles = ["%s%s" % (x, y) for y in ["-", ":"] for x in ["b", "g", "r", "c", "m", "y", "k"]] @@ -51,7 +48,7 @@ def common_domain(parms): domain = [-1e30, 1e30, -1e30, 1e30] for parm in parms: tmp = parm.domain() - domain = [max(domain[0], tmp[0]), min(domain[1], tmp[1]), max(domain[2], tmp[2]), min(domain, tmp[3])] + domain = [max(domain[0], tmp[0]), min(domain[1], tmp[1]), max(domain[2], tmp[2]), min(domain[3], tmp[3])] if domain[0] >= domain[1] or domain[2] >= domain[3]: return None @@ -178,7 +175,7 @@ def plot(fig, y, x=None, clf=True, sub=None, scatter=False, stack=False, The 'labels' argument can be set to a list of labels and 'show_legend' can be set to True to show a legend inside the plot. - The figure number of the figure used to plot in is returned. + The min/max/median of the plotted points are returned is returned. """ global __styles @@ -199,7 +196,8 @@ def plot(fig, y, x=None, clf=True, sub=None, scatter=False, stack=False, if x is None: x = [range(len(yi)) for yi in y] - offset = 0.0 + offset = 0. + med = 0. for i in range(0,len(y)): if labels is None: if scatter: @@ -216,7 +214,8 @@ def plot(fig, y, x=None, clf=True, sub=None, scatter=False, stack=False, axes.plot(x[i], y[i] + offset, __styles[i % len(__styles)], label=labels[i]) - if stack: + med += numpy.median(y[i] + offset) + if stack and i != len(y)-1: if sep_abs: offset += sep else: @@ -225,73 +224,120 @@ def plot(fig, y, x=None, clf=True, sub=None, scatter=False, stack=False, if not labels is None and show_legend: axes.legend(prop=FontProperties(size="x-small"), markerscale=0.5) + # return min max median values + if stack: + return numpy.min(y[0]), numpy.max(y[-1]+offset), med/len(y) + else: + return numpy.min(y), numpy.max(y), med/len(y) + class Parm: - def __init__(self, db, name, elements=None, isPolar=False): + """ + Each entry in the parmdb is an instance of this class + """ + def __init__(self, db, name, elements=None, isPolar=True): self._db = db self._name = name self._elements = elements self._isPolar = isPolar self._value = None + self._freq = None self._value_domain = None self._value_resolution = None - + self._calType = self._name.split(':')[0] + + if self._calType == 'DirectionalGain' or self._calType == 'RotationAngle': self._antenna = self._name.split(':')[-2] + else: self._antenna = self._name.split(':')[-1] + + # modifiers for some phase outputs + if self._calType == 'Clock': + self.mod = lambda Clock: 2. * numpy.pi * Clock * self._freq + elif self._calType == 'TEC': + self.mod = lambda TEC: -8.44797245e9 * TEC / self._freq + elif self._calType == 'RM': + self.mod = lambda RM: RM / (299792458.*self._freq*299792458.*self._freq) + else: + # everything else is plain + self.mod = lambda ph: ph + self._readDomain() - def name(self): - return self._name - - def isPolar(self): - return self._isPolar - def empty(self): return self._empty def domain(self): return self._domain - def value(self, domain=None, resolution=None, asPolar=True, unwrap_phase=False, phase_reference=None): - if self.empty(): - return (numpy.zeros((1,1)), numpy.zeros((1,1))) + def color(self): + if self._calType == 'Gain': return QColor('#ffbfca') + if self._calType == 'DirectionalGain': return QColor('#ebbfff') + if self._calType == 'CommonRotationAngle': return QColor('#bfcdff') + if self._calType == 'RotationAngle': return QColor('#bfe5ff') + if self._calType == 'Clock': return QColor('#fff1bf') + if self._calType == 'TEC': return QColor('#bfffda') + if self._calType == 'CommonScalarPhase': return QColor('#ffbfbf') + if self._calType == 'RM': return QColor('#84f0aa') - if self._value is None or self._value_domain != domain or self._value_resolution != resolution: - self._readValue(domain, resolution) - - # Correct negative amplitude solutions by taking the absolute value - # of the amplitude and rotating the phase by 180 deg. - if self.isPolar(): - self._value[1][self._value[0] < 0.0] += numpy.pi - self._value[0] = numpy.abs(self._value[0]) + def valueAmp(self, domain=None, resolution=None, asPolar=True): + self.updateValue(domain, resolution) if asPolar: - if self.isPolar(): + if self._isPolar: ampl = self._value[0] - phase = normalize(self._value[1]) else: ampl = numpy.sqrt(numpy.power(self._value[0], 2) + numpy.power(self._value[1], 2)) + + return ampl + + if not self._isPolar: + re = self._value[0] + else: + re = self._value[0] * numpy.cos(self._value[1]) + + return re + + def valuePhase(self, domain=None, resolution=None, asPolar=True, unwrap_phase=False, \ + reference_parm=None, sum_parms=[], mod=True): + self.updateValue(domain, resolution) + + if asPolar: + if self._isPolar: + phase = self._value[1] + else: phase = numpy.arctan2(self._value[1], self._value[0]) - if not phase_reference is None: - assert(phase_reference.shape == phase.shape) - phase = normalize(phase - phase_reference) + # apply modifiers for some solution types (e.g. clock, TEC, RM) + phase = self.mod(phase) + + if not reference_parm is None: + reference_val = reference_parm.valuePhase(domain, resolution) + assert(reference_val.shape == phase.shape) + phase = normalize(phase - reference_val) + + for sum_parm in sum_parms: + sum_val = sum_parm[0].valuePhase(domain, resolution) + sum_reference_val = sum_parm[1].valuePhase(domain, resolution) + assert(sum_val.shape == phase.shape) + assert(sum_reference_val.shape == phase.shape) + phase = normalize(phase + sum_val - sum_reference_val) + + if mod: phase = normalize(phase) if unwrap_phase: for i in range(0, phase.shape[1]): phase[:, i] = unwrap(phase[:, i]) - return [ampl, phase] + return phase - if not self.isPolar(): - re = self._value[0] + if not self._isPolar: im = self._value[1] else: - re = self._value[0] * numpy.cos(self._value[1]) im = self._value[0] * numpy.sin(self._value[1]) - return [re, im] + return im def _readDomain(self): if self._elements is None: - self._domain = self._db.getRange(self.name()) + self._domain = self._db.getRange(self._name) else: if self._elements[0] is None: self._domain = self._db.getRange(self._elements[1]) @@ -304,10 +350,24 @@ class Parm: self._empty = (self._domain[0] >= self._domain[1]) or (self._domain[2] >= self._domain[3]) + def updateValue(self, domain=None, resolution=None): + if self.empty(): + return (numpy.zeros((1,1)), numpy.zeros((1,1))) + + # if nothig changes, use cache + if self._value is None or self._value_domain != domain or self._value_resolution != resolution: + self._readValue(domain, resolution) + + # Correct negative amplitude solutions by taking the absolute value + # of the amplitude and rotating the phase by 180 deg. + if self._isPolar and (self._calType == 'Gain' or self._calType == 'DirectionalGain'): + self._value[1][self._value[0] < 0.0] += numpy.pi + self._value[0] = numpy.abs(self._value[0]) + def _readValue(self, domain=None, resolution=None): if self._elements is None: - value = numpy.array(self.__fetch_value(self.name(), domain, resolution)) - self._value = (value, numpy.zeros(value.shape)) + value = numpy.array(self.__fetch_value(self._name, domain, resolution)) + self._value = (numpy.zeros(value.shape), value) else: el0 = None if not self._elements[0] is None: @@ -335,15 +395,17 @@ class Parm: tmp = self._db.getValuesGrid(name)[name] else: if resolution is None: - tmp = self._db.getValues(name, domain[0], domain[1], domain[2], domain[3])[name] + tmp = self._db.getValuesGrid(name, domain[0], domain[1], domain[2], domain[3])[name] else: tmp = self._db.getValuesStep(name, domain[0], domain[1], resolution[0], domain[2], domain[3], resolution[1])[name] + + # store frequencies + self._freq = numpy.array([tmp["freqs"]] * len(tmp["values"])) - if type(tmp) is dict: - return tmp["values"] - - # Old parmdb interface. - return tmp + # store times and frequencies + self._freqs = numpy.array(tmp["freqs"]) + self._times = numpy.array(tmp["times"]) + return tmp["values"] class PlotWindow(QFrame): def __init__(self, parms, selection, resolution=None, parent=None, title=None): @@ -355,15 +417,23 @@ class PlotWindow(QFrame): self.parms = parms self.selected_parms = [self.parms[i] for i in selection] + self.calType = self.selected_parms[0]._calType + self.fig = Figure((5, 4), dpi=100) self.canvas = FigureCanvas(self.fig) self.canvas.setParent(self) - self.toolbar = NavigationToolbar(self.canvas, self) - self.axis = 0 self.index = 0 + self.zoom = 0 + self.valminmax = [[],[]] + self.blocked_valminmax = [[],[]] + + self.xminmax =[] + + self.toolbar = NavigationToolbar(self.canvas, self) + self.axisSelector = QComboBox() self.axisSelector.addItem("Frequency") self.axisSelector.addItem("Time") @@ -372,15 +442,33 @@ class PlotWindow(QFrame): self.spinner = QSpinBox() self.connect(self.spinner, SIGNAL('valueChanged(int)'), self.handle_spinner) -# self.slider = QSlider(Qt.Horizontal) -# self.slider.setMinimum(0) -# self.slider.setMaximum(159) -# self.connect(self.slider, SIGNAL('sliderReleased()'), self.handle_slider) + self.slider = QSlider(Qt.Vertical) + self.slider.setMinimum(0) + self.slider.setMaximum(100) + self.slider.setTracking(False) + self.slider.setToolTip('Zoom in on the median of amplitude') + self.connect(self.slider, SIGNAL('valueChanged(int)'), self.handle_slider) self.show_legend = False self.legendCheck = QCheckBox("Legend") self.connect(self.legendCheck, SIGNAL('stateChanged(int)'), self.handle_legend) + self.block_axis = False + self.blockaxisCheck = QCheckBox("Block y-axis") + self.blockaxisCheck.setToolTip('Block y-axis when stepping through time or frequency') + self.connect(self.blockaxisCheck, SIGNAL('stateChanged(int)'), self.handle_blockaxis) + + self.use_points = False + self.usepointsCheck = QCheckBox("Use points") + self.usepointsCheck.setToolTip('Use points for plotting amplitude') + self.connect(self.usepointsCheck, SIGNAL('stateChanged(int)'), self.handle_usepoints) + + self.valuesonxaxis = False + self.valuesonxaxisCheck = QCheckBox("Values on x-axis") + self.valuesonxaxisCheck.setChecked(False) + self.valuesonxaxisCheck.setToolTip('Plot the value of time / frequency on x-axis (if unchecked, the sample number is shown)') + self.connect(self.valuesonxaxisCheck, SIGNAL('stateChanged(int)'), self.handle_valuesonxaxis) + self.polar = True self.polarCheck = QCheckBox("Polar") self.polarCheck.setChecked(True) @@ -390,34 +478,34 @@ class PlotWindow(QFrame): self.unwrapCheck = QCheckBox("Unwrap phase") self.connect(self.unwrapCheck, SIGNAL('stateChanged(int)'), self.handle_unwrap) - self.reference = None + self.sum_parm = [] + self.sum_index = [] + self.sumSelector = QComboBox() + self.sumSelector.setToolTip('Show the sum of multiple phase effects (\'+\': include in sum)') + self.sumSelector.addItem('+'+self.selected_parms[0]._name) + # select only parm with the same antenna, exclude *RotationAngle: that's no phase + self.idxParmsSameAnt = [i for i, parm in enumerate(self.parms) if parm._antenna in \ + [parm2._antenna for parm2 in self.selected_parms] \ + and parm._calType!="CommonRotationAngle" and parm._calType!="RotationAngle" + and parm._name!=self.selected_parms[0]._name] + for idx in self.idxParmsSameAnt: + self.sumSelector.addItem(parms[idx]._name) + self.connect(self.sumSelector, SIGNAL('activated(int)'), self.handle_sum) + self.sumSelector.setEnabled(False) + + self.sumLabel = QLabel("Phase sum:") + self.sumLabel.setEnabled(False) + + self.reference_parm = None self.reference_index = 0 self.referenceSelector = QComboBox() self.referenceSelector.addItem("None") - for parm in self.parms: - if parm.isPolar(): - self.referenceSelector.addItem("%s (polar)" % parm.name()) - else: - self.referenceSelector.addItem(parm.name()) + self.idxParmsSameType = [i for i, parm in enumerate(self.parms) if parm._calType == self.calType] + for idx in self.idxParmsSameType: + self.referenceSelector.addItem(parms[idx]._name) self.connect(self.referenceSelector, SIGNAL('activated(int)'), self.handle_reference) - self.referenceLabel = QLabel("Phase reference") - - hbox = QHBoxLayout() - hbox.addWidget(self.axisSelector) - hbox.addWidget(self.spinner) - hbox.addWidget(self.legendCheck) - hbox.addWidget(self.polarCheck) - hbox.addWidget(self.unwrapCheck) - hbox.addWidget(self.referenceSelector) - hbox.addWidget(self.referenceLabel) - hbox.addStretch(1) - - layout = QVBoxLayout() - layout.addLayout(hbox); - layout.addWidget(self.canvas, 1) - layout.addWidget(self.toolbar) - self.setLayout(layout) + self.referenceLabel = QLabel("Phase reference:") self.domain = common_domain(self.selected_parms) @@ -428,51 +516,63 @@ class PlotWindow(QFrame): self.shape = (1, 1) if not self.domain is None: - self.shape = (self.selected_parms[0].value(self.domain, self.resolution)[0].shape) + self.shape = (self.selected_parms[0].valueAmp(self.domain, self.resolution).shape) assert(len(self.shape) == 2) self.spinner.setRange(0, self.shape[1 - self.axis] - 1) - self.plot() - def plot(self): - el0 = [] - el1 = [] - labels = [] - - if not self.domain is None: - for parm in self.selected_parms: - value = parm.value(self.domain, self.resolution, self.polar, self.unwrap_phase, self.reference) - if value[0].shape != self.shape or value[1].shape != self.shape: - print "warning: inconsistent shape; will skip parameter:", parm.name() - continue - - if self.axis == 0: - el0.append(value[0][:, self.index]) - el1.append(value[1][:, self.index]) - else: - el0.append(value[0][self.index, :]) - el1.append(value[1][self.index, :]) - labels.append(parm.name()) + # display widgets + hbox = QHBoxLayout() + hbox.addWidget(self.axisSelector) + hbox.addWidget(self.spinner) + hbox.addWidget(self.legendCheck) - legend = self.show_legend and len(labels) > 0 - xlabel = ["Time (sample)", "Freq (sample)"][self.axis] - if self.polar: - plot(self.fig, el0, sub="211", labels=labels, show_legend=legend, xlabel=xlabel, ylabel="Amplitude") - plot(self.fig, el1, clf=False, sub="212", stack=True, scatter=True, labels=labels, show_legend=legend, xlabel=xlabel, ylabel="Phase (rad)") - else: - plot(self.fig, el0, sub="211", labels=labels, show_legend=legend, xlabel=xlabel, ylabel="Real") - plot(self.fig, el1, clf=False, sub="212", labels=labels, show_legend=legend, xlabel=xlabel, ylabel="Imaginary") + # Re/Imag or Ampl/Phase only relevant for gains + if self.calType=="Gain" or self.calType=="DirectionalGain": + hbox.addWidget(self.polarCheck) + hbox.addWidget(self.unwrapCheck) + hbox.addWidget(self.blockaxisCheck) - # Set x-axis scale in number of samples. - for ax in self.fig.axes: - ax.set_xlim(0, self.shape[self.axis] - 1) + # Phases are always points, amplitudes (gains) are lines by default + if self.calType=="Gain" or self.calType=="DirectionalGain": + hbox.addWidget(self.usepointsCheck) + hbox.addWidget(self.valuesonxaxisCheck) + hbox.addStretch(1) + hbox2 = QHBoxLayout() + hbox2.addWidget(self.referenceLabel) + hbox2.addWidget(self.referenceSelector) + # For now, phase sum only works when only one parameter is shown + if len(self.selected_parms)==1: + hbox2.addWidget(self.sumLabel) + hbox2.addWidget(self.sumSelector) + hbox2.addStretch(1) + hbox3 = QHBoxLayout() + + # Zooming in only possible on amplitudes (gains) + if self.calType=="Gain" or self.calType=="DirectionalGain": + hbox3.addWidget(self.slider) + hbox3.addWidget(self.canvas) + layout = QVBoxLayout() + layout.addLayout(hbox) + + # RotationAngles do not have phase sum + if self.calType!="CommonRotationAngle" and self.calType!="RotationAngle": + layout.addLayout(hbox2) + layout.addLayout(hbox3) + layout.addWidget(self.toolbar) + self.setLayout(layout) - self.canvas.draw() + self.plot() def handle_spinner(self, index): self.index = index self.plot() + def handle_slider(self, zoom): + self.zoom = zoom + self.resize_plot() + self.canvas.draw() + def handle_axis(self, axis): if axis != self.axis: self.axis = axis @@ -480,6 +580,47 @@ class PlotWindow(QFrame): self.spinner.setValue(0) self.plot() + def find_reference(self, parm, ant_parm): + """ + find the parm which is a suitable reference for "parm". + The reference antenna is taken from ant_parm + """ + reference_antenna = ant_parm._antenna + reference_name = parm._name.replace(parm._antenna, reference_antenna) + reference_parm = next((p for p in self.parms if p._name == reference_name), None) + if reference_parm == None: + print "ERROR: cannot find a suitable reference (", reference_name ,") for", parm._name + return None + else: +# print "DEBUG: using reference ", reference_name ," for", parm._name + return reference_parm + + def handle_sum(self, index): + if index == 0: + a=3 #do nothing + # self.sum_parm = [] + # remove all the "+" from the drop down menu + # for sum_index in self.sum_index: + # parm = self.parms[self.idxParmsSameAnt[sum_index-1]] + # self.sumSelector.setItemText(sum_index, parm._name) + # self.sum_index = [] + elif index in self.sum_index: + parm = self.parms[self.idxParmsSameAnt[index-1]] + self.sum_parm.pop(self.sum_index.index(index)) + self.sum_index.remove(index) + # remove "+" from the selected element + self.sumSelector.setItemText(index, parm._name) + else: + parm = self.parms[self.idxParmsSameAnt[index-1]] + ant_parm = self.parms[self.idxParmsSameType[self.reference_index-1]] + sum_reference_parm = self.find_reference(parm, ant_parm) + # add a tuple of the parm to sum and its reference + self.sum_parm.append( (parm, sum_reference_parm) ) + self.sum_index.append(index) + # add "+" to the selected element + self.sumSelector.setItemText(index, '+'+parm._name) + self.plot() + def handle_legend(self, state): self.show_legend = (state == 2) self.plot() @@ -488,29 +629,153 @@ class PlotWindow(QFrame): self.unwrap_phase = (state == 2) self.plot() + def handle_blockaxis(self, state): + self.block_axis = (state == 2) + if self.block_axis: + self.slider.setEnabled(False) + for i, ax in enumerate(self.fig.axes): + self.blocked_valminmax[i] = (ax.axis()[2], ax.axis()[3], 0) + else: + self.slider.setEnabled(True) + self.plot() + + def handle_usepoints(self, state): + self.use_points = (state == 2) + self.plot() + + def handle_valuesonxaxis(self, state): + self.valuesonxaxis = (state == 2) + self.plot() + def handle_polar(self, state): self.polar = (state == 2) self.referenceSelector.setEnabled(self.polar) self.referenceLabel.setEnabled(self.polar) + self.sumSelector.setEnabled(self.polar) + self.sumLabel.setEnabled(self.polar) self.unwrapCheck.setEnabled(self.polar) self.plot() def handle_reference(self, index): if index != self.reference_index: if index == 0: - reference = None + self.reference_parm = None + self.sum_parm = [] + # remove all the "+" from the drop down menu + for sum_index in self.sum_index: + parm = self.parms[self.idxParmsSameAnt[sum_index-1]] + self.sumSelector.setItemText(sum_index, parm._name) + self.sum_index = [] else: - parm = self.parms[index - 1] - value = parm.value(self.domain, self.resolution) - if value[1].shape != self.shape: - print "warning: inconsistent shape; will not change phase reference to parameter:", parm.name() - return - reference = value[1] + self.reference_parm = self.parms[self.idxParmsSameType[index-1]] + + # update all the sums to use the new reference + for i, sum_parm in enumerate(self.sum_parm): + sum_reference_parm = self.find_reference(sum_parm[0], self.reference_parm) + # add a tuple of the parm to sum and its reference + self.sum_parm[i] = (sum_parm[0], sum_reference_parm) + self.sumSelector.setEnabled((index != 0)) + self.sumLabel.setEnabled((index != 0)) self.reference_index = index - self.reference = reference self.plot() + def resize_plot(self): + """ + Set y-axis scale looking the slider and the number of samples. + """ + for i, ax in enumerate(self.fig.axes): + # handle single x value (typically one freq) + if self.shape[self.axis] != 1: + #ax.set_xlim(0, self.shape[self.axis] - 1) + ax.set_xlim(self.xminmax[0],self.xminmax[1]) + if self.block_axis == True: + ax.set_ylim(self.blocked_valminmax[i][0], self.blocked_valminmax[i][1]) + else: + # handle single y value (typically y=0) + if self.valminmax[i][0] == self.valminmax[i][1]: + ax.set_ylim(self.valminmax[i][0]-1, self.valminmax[i][0]+1) + elif self.zoom == 0 or (self.polar and i>0): + ax.set_ylim(self.valminmax[i][0], self.valminmax[i][1]) + else: + rng = (self.valminmax[i][1] - self.valminmax[i][0])/numpy.exp(self.zoom/10.) + ax.set_ylim(ymin = self.valminmax[i][2] - rng/2., \ + ymax = self.valminmax[i][2] + rng/2.) + + def plot(self): + """ + Prepare the plotting space, double makes a double plot (only [Directional]Gain) + """ + + phase = [] + amp = [] + labels = [] + xvalues = [] + + # Selector for double plot + if self.calType == 'Gain' or self.calType == 'DirectionalGain': + double = True + else: + self.usepointsCheck.setEnabled(False) + double = False + + if not self.domain is None: + for parm in self.selected_parms: + + valuePhase = parm.valuePhase(self.domain, self.resolution, asPolar = self.polar, \ + unwrap_phase=self.unwrap_phase, reference_parm=self.reference_parm, sum_parms=self.sum_parm) + + if self.axis == 0: # time on x-axis + phase.append(valuePhase[:, self.index]) + else: # freq on x-axis + phase.append(valuePhase[self.index, :]) + + if self.valuesonxaxis: + if self.axis == 0: # time on x-axis + xvalues.append((parm._times-parm._times[0])/60.) + else: # freq on x-axis + xvalues.append(parm._freqs/1.e6) + else: + xvalues.append(range(len(phase[0]))) + + self.xminmax=[xvalues[0][0],xvalues[0][-1]] + + if double: + valueAmp = parm.valueAmp(self.domain, self.resolution, asPolar = self.polar) + + if self.axis == 0: # time on x-axis + amp.append(valueAmp[:, self.index]) + else: # freq on x-axis + amp.append(valueAmp[self.index, :]) + + labels.append(parm._name) + + legend = self.show_legend and len(labels) > 0 + if self.valuesonxaxis: + xlabel = ["Time (minutes since start)", "Freq (MHz)"][self.axis] + else: + xlabel = ["Time (sample)", "Freq (sample)"][self.axis] + + if self.calType == "CommonRotationAngle" or self.calType == "RotationAngle": + phaselabel = "Rotation angle (rad)" + else: + phaselabel = "Phase (rad)" + + + if double: + if self.polar: + self.valminmax[0] = plot(self.fig, amp, x=xvalues, sub="211", labels=labels, show_legend=legend, xlabel=xlabel, ylabel="Amplitude",scatter=self.use_points) + self.valminmax[1] = plot(self.fig, phase, x=xvalues, clf=False, sub="212", stack=True, scatter=True, labels=labels, show_legend=legend, xlabel=xlabel, ylabel=phaselabel) + else: + self.valminmax[0] = plot(self.fig, amp, x=xvalues, sub="211", labels=labels, show_legend=legend, xlabel=xlabel, ylabel="Real",scatter=self.use_points) + self.valminmax[1] = plot(self.fig, phase, x=xvalues, clf=False, sub="212", labels=labels, show_legend=legend, xlabel=xlabel, ylabel="Imaginary",scatter=self.use_points) + else: + self.valminmax[0] = plot(self.fig, phase, x=xvalues, sub="111", stack=True, scatter=True, labels=labels, show_legend=legend, xlabel=xlabel, ylabel=phaselabel) + + self.resize_plot() + self.canvas.draw() + + class MainWindow(QFrame): def __init__(self, db): QFrame.__init__(self) @@ -524,15 +789,12 @@ class MainWindow(QFrame): self.list.setSelectionMode(QAbstractItemView.ExtendedSelection) layout.addWidget(self.list, 1) - self.useResolution = True + self.useResolution = False checkResolution = QCheckBox("Use resolution") - checkResolution.setChecked(True) + checkResolution.setChecked(False) self.connect(checkResolution, SIGNAL('stateChanged(int)'), self.handle_resolution) self.resolution = [QLineEdit(), QLineEdit()] -# validator = QDoubleValidator(self.resolution[0]) -# validator.setRange(1.0, 2.0) -# self.resolution[0].setValidator(validator) self.resolution[0].setAlignment(Qt.AlignRight) self.resolution[1].setAlignment(Qt.AlignRight) @@ -563,6 +825,7 @@ class MainWindow(QFrame): parm = names.pop() split = parm.split(":") + # just one entry for Real/Imag or Amp/Ph if contains(split, "Real") or contains(split, "Imag"): if contains(split, "Real"): idx = split.index("Real") @@ -584,7 +847,7 @@ class MainWindow(QFrame): elements = [other, parm] split.pop(idx) - self.parms.append(Parm(self.db, ":".join(split), elements)) + self.parms.append(Parm(self.db, ":".join(split), elements, isPolar = False)) elif contains(split, "Ampl") or contains(split, "Phase"): if contains(split, "Ampl"): @@ -607,24 +870,25 @@ class MainWindow(QFrame): elements = [other, parm] split.pop(idx) - self.parms.append(Parm(self.db, ":".join(split), elements, True)) + self.parms.append(Parm(self.db, ":".join(split), elements, isPolar = True)) else: self.parms.append(Parm(self.db, parm)) self.parms = [parm for parm in self.parms if not parm.empty()] - self.parms.sort(cmp=lambda x, y: cmp(x.name(), y.name())) + self.parms.sort(cmp=lambda x, y: cmp(x._name, y._name)) domain = common_domain(self.parms) if not domain is None: - self.resolution[0].setText("%.6f" % ((domain[1] - domain[0]) / 100.0)) - self.resolution[1].setText("%.6f" % ((domain[3] - domain[2]) / 100.0)) + self.resolution[0].setText("%.3f" % ((domain[1] - domain[0]) / 100.0)) + self.resolution[1].setText("%.3f" % ((domain[3] - domain[2]) / 100.0)) for parm in self.parms: - name = parm.name() - if parm.isPolar(): + name = parm._name + if parm._isPolar and (parm._calType == 'Gain' or parm._calType == 'DirectionalGain'): name = "%s (polar)" % name - QListWidgetItem(name, self.list) + it = QListWidgetItem(name, self.list) + it.setBackground(parm.color()) def close_all_figures(self): for figure in self.figures: @@ -643,8 +907,14 @@ class MainWindow(QFrame): if self.useResolution: resolution = [float(item.text()) for item in self.resolution] - self.figures.append(PlotWindow(self.parms, selection, resolution, title="Figure %d" % (len(self.figures) + 1))) - self.figures[-1].show() + # plot together only parms of the same type + calTypes = set([self.parms[idx]._calType for idx in selection]) + for calType in calTypes: + this_selection = [idx for idx in selection if self.parms[idx]._calType == calType] + + self.figures.append(PlotWindow(self.parms, this_selection, resolution, title="Figure %d" % (len(self.figures) + 1))) + + self.figures[-1].show() def handle_close(self): self.close_all_figures() @@ -659,7 +929,11 @@ if __name__ == "__main__": print "usage: parmdbplot.py <parmdb>" sys.exit(1) - db = parmdb.parmdb(sys.argv[1]) + try: + db = parmdb.parmdb(sys.argv[1]) + except: + print "ERROR:", sys.argv[1], "is not a valid parmdb." + sys.exit(1) app = QApplication(sys.argv) window = MainWindow(db)