Пример #1
0
    def __init__(self, ip_address):
        super(MyWifiWSA, self).__init__()
        self._init_hardware(ip_address)

        self._numpts = 4096
        self._center_MHz = 2447.
        self._span_MHz = 125.
        self._start_MHz = self._center_MHz - (self._span_MHz / 2)
        self._stop_MHz = self._center_MHz + (self._span_MHz / 2)
        self._frequencies_MHz = np.linspace(self._start_MHz, self._stop_MHz,
                                            self._numpts)

        self._last_power_spectrum_dBm = None
        self._capture_history_len = 2000
        self.capture_history = WaterfallModel(self._frequencies_MHz,
                                              self._capture_history_len)
        self._capture_count = 0

        self._capture_timer = QtCore.QTimer(
        )  #for app-side acquisition control
        self._capture_timer.timeout.connect(self._on_capture_timer)
        self.capturing = False

        self.reset()
Пример #2
0
 def __init__(self, ip_address):
     super(MyWifiWSA, self).__init__()
     self._init_hardware(ip_address)
     
     self._numpts = 4096
     self._center_MHz = 2447.
     self._span_MHz = 125.
     self._start_MHz = self._center_MHz - (self._span_MHz / 2)
     self._stop_MHz = self._center_MHz + (self._span_MHz / 2)
     self._frequencies_MHz = np.linspace(self._start_MHz,
                                         self._stop_MHz,
                                         self._numpts)
     
     self._last_power_spectrum_dBm = None
     self._capture_history_len = 2000
     self.capture_history = WaterfallModel(self._frequencies_MHz,
                                           self._capture_history_len)
     self._capture_count = 0
     
     self._capture_timer = QtCore.QTimer() #for app-side acquisition control
     self._capture_timer.timeout.connect(self._on_capture_timer)
     self.capturing = False
     
     self.reset()
Пример #3
0
class MyWifiWSA(object):
    """A cheap/simple/incomplete WSA instrument abstraction.
    
    Hides driver internals that people using the device as a spectrum
    analyzer may not care about.
    
    """
    def __init__(self, ip_address):
        super(MyWifiWSA, self).__init__()
        self._init_hardware(ip_address)
        
        self._numpts = 4096
        self._center_MHz = 2447.
        self._span_MHz = 125.
        self._start_MHz = self._center_MHz - (self._span_MHz / 2)
        self._stop_MHz = self._center_MHz + (self._span_MHz / 2)
        self._frequencies_MHz = np.linspace(self._start_MHz,
                                            self._stop_MHz,
                                            self._numpts)
        
        self._last_power_spectrum_dBm = None
        self._capture_history_len = 2000
        self.capture_history = WaterfallModel(self._frequencies_MHz,
                                              self._capture_history_len)
        self._capture_count = 0
        
        self._capture_timer = QtCore.QTimer() #for app-side acquisition control
        self._capture_timer.timeout.connect(self._on_capture_timer)
        self.capturing = False
        
        self.reset()
    
    def _init_hardware(self, ip_address):
        self._driver = WSA()
        self._driver.connect(ip_address)
        
    def reset(self):
        """Sets a known start state."""
        
        #Set our app-specific reset state (802.11 range)
        center_frequency_MHz = 2447
        self._reset_hardware()
    
    def _reset_hardware(self):
        drv = self._driver
        drv.reset()
        drv.request_read_perm()
        drv.freq(self._center_MHz)
        drv.decimation(1) # do decimate
        drv.attenuator(0) # don't attenuate

    def start_continuous_capture(self, capture_interval_s):
        #It would be nice to be able to set the capture interval on the
        #device itself so that we aren't slave to lagging issues on the OS.
        
        #We could also just calculate the number of pts required to achieve
        #the required capture interval (or close enough), and have the
        #resulting frequiency grid, res bw, et al adjust accordingly. But...
        #clearly out of scope for this cheap abstraction.
        
        #Timing will be managed by a QTimer in this case. As a result,
        #capture_interval_s is really done on best effort (although the
        #QTimer drivign it does make reasonable efforts to sync timing).
        self._capture_timer.stop()
        self._capture_timer.start(1000 * capture_interval_s)
        self.capturing = True
        
    def stop_continuous_capture(self):
        self.capturing = False
        self._capture_timer.stop()
        
        
    def acquire_single_power_spectrum(self):
        STAIRS_MODE = False
        self._capture_count += 1
        
        if STAIRS_MODE:
            STAIR_WIDTH = 50
            stair_start = self._capture_count % self._numpts
            stair_start = np.floor(stair_start / STAIR_WIDTH) * STAIR_WIDTH
            stair_stop = stair_start + STAIR_WIDTH
            ps_dBm = np.zeros(self._numpts) - 80
            ps_dBm[stair_start:stair_stop] = -20
            timestamp_s = self._capture_count
        else:
            drv = self._driver
            vrt_data, context = read_data_and_context(drv, self._numpts)
            
            assert isinstance(vrt_data, pyrf.vrt.DataPacket)
            ps_dBm = compute_fft(drv, vrt_data, context, convert_to_dbm=True)
            
            #Accumulate history...
            timestamp_s = calc_vrt_sample_time_s(vrt_data)
        self.capture_history.add_row(ps_dBm, timestamp_s = timestamp_s)
        
        self._last_power_spectrum_dBm = ps_dBm
        return ps_dBm
    
    def _on_capture_timer(self):
        #grab a spectrum! The historical model will be updated and events can
        #be driven from that directly.
        self.acquire_single_power_spectrum()
Пример #4
0
    def __init__(self, controller, layout):
        super(Plot, self).__init__()

        self.controller = controller
        controller.device_change.connect(self.device_changed)
        controller.state_change.connect(self.state_changed)
        controller.plot_change.connect(self.plot_changed)
        self.plot_state = {}

        # initialize main fft window
        self.spectral_window = SpectralWidget(controller)
        self.window = self.spectral_window.window

        self.window.setMenuEnabled(False)

        def widget_range_changed(widget, ranges):
            if hasattr(self, 'gui_state') and hasattr(self, 'plot_state'):
                # HDR mode has a tuning resolution almost the same as its usable bandwidth, making the tuning mouse tuning annoying to use
                if self.gui_state.mode == 'HDR' or not self.plot_state[
                        'mouse_tune']:
                    return
            if not hasattr(ranges, '__getitem__'):
                return  # we're not intereted in QRectF updates
            self.user_xrange_change.emit(ranges[0][0], ranges[0][1])

        self.window.sigRangeChanged.connect(widget_range_changed)

        self.view_box = self.window.plotItem.getViewBox()

        # initialize the y-axis of the plot
        self.window.setYRange(PLOT_BOTTOM, PLOT_TOP)
        labelStyle = fonts.AXIS_LABEL_FONT

        self.window.setLabel('left', 'Power', 'dBm', **labelStyle)
        self.window.setLabel('top')
        self.window.setLabel('bottom', 'Frequency', 'Hz', **labelStyle)

        # horizontal cursor line
        cursor_pen = pg.mkPen(color=colors.YELLOW_NUM, width=2)
        self.cursor_line = pg.InfiniteLine(pos=-100,
                                           angle=0,
                                           movable=True,
                                           pen=cursor_pen)

        self.channel_power_region = pg.LinearRegionItem()
        self._trig_enable = False
        self.grid(True)

        # IQ constellation window
        self.const_window = pg.PlotWidget(name='const_plot')
        self.const_plot = pg.ScatterPlotItem(pen='y')
        self.const_window.setMenuEnabled(False)
        self.const_window.addItem(self.const_plot)
        self.const_window.setYRange(IQ_PLOT_YMIN, IQ_PLOT_YMAX)
        self.const_window.setXRange(IQ_PLOT_YMIN, IQ_PLOT_YMAX)

        # IQ time domain  window
        self.iq_window = pg.PlotWidget(name='const_plot')
        self.iq_window.setYRange(IQ_PLOT_YMIN, IQ_PLOT_YMAX)
        self.iq_window.setMenuEnabled(False)
        self.update_iq_range = True
        self.i_curve = self.iq_window.plot(pen='g')
        self.q_curve = self.iq_window.plot(pen='r')

        # add traces
        self.traces = []
        first_trace = labels.TRACES[0]

        for trace_name, trace_color in zip(labels.TRACES, colors.TRACE_COLORS):
            trace = Trace(self,
                          trace_name,
                          trace_color,
                          blank=True,
                          write=False)
            self.traces.append(trace)
        self.traces[0].blank = False
        self.traces[0].write = True

        self.markers = []
        for name in labels.MARKERS:
            self.markers.append(
                Marker(self, name, colors.WHITE_NUM, self.controller))

        self.waterfall_data = WaterfallModel(max_len=600)

        self.waterfall_window = ThreadedWaterfallPlotWidget(
            self.waterfall_data,
            scale_limits=(PLOT_YMIN, PLOT_YMAX),
            max_frame_rate_fps=30,
            mouse_move_crosshair=False,
        )
        self.persistence_window = PersistencePlotWidget(
            decay_fn=decay_fn_EXPONENTIAL, data_model=self.waterfall_data)
        self.persistence_window.getAxis('bottom').setScale(1e-9)
        self.persistence_window.showGrid(True, True)

        self.trigger_control = triggerControl()
        self.connect_plot_controls()
        self.update_waterfall_levels(PLOT_BOTTOM, PLOT_TOP)
Пример #5
0
class MyWifiWSA(object):
    """A cheap/simple/incomplete WSA instrument abstraction.
    
    Hides driver internals that people using the device as a spectrum
    analyzer may not care about.
    
    """
    def __init__(self, ip_address):
        super(MyWifiWSA, self).__init__()
        self._init_hardware(ip_address)

        self._numpts = 4096
        self._center_MHz = 2447.
        self._span_MHz = 125.
        self._start_MHz = self._center_MHz - (self._span_MHz / 2)
        self._stop_MHz = self._center_MHz + (self._span_MHz / 2)
        self._frequencies_MHz = np.linspace(self._start_MHz, self._stop_MHz,
                                            self._numpts)

        self._last_power_spectrum_dBm = None
        self._capture_history_len = 2000
        self.capture_history = WaterfallModel(self._frequencies_MHz,
                                              self._capture_history_len)
        self._capture_count = 0

        self._capture_timer = QtCore.QTimer(
        )  #for app-side acquisition control
        self._capture_timer.timeout.connect(self._on_capture_timer)
        self.capturing = False

        self.reset()

    def _init_hardware(self, ip_address):
        self._driver = WSA()
        self._driver.connect(ip_address)

    def reset(self):
        """Sets a known start state."""

        #Set our app-specific reset state (802.11 range)
        center_frequency_MHz = 2447
        self._reset_hardware()

    def _reset_hardware(self):
        drv = self._driver
        drv.reset()
        drv.request_read_perm()
        drv.freq(self._center_MHz)
        drv.decimation(1)  # do decimate
        drv.attenuator(0)  # don't attenuate

    def start_continuous_capture(self, capture_interval_s):
        #It would be nice to be able to set the capture interval on the
        #device itself so that we aren't slave to lagging issues on the OS.

        #We could also just calculate the number of pts required to achieve
        #the required capture interval (or close enough), and have the
        #resulting frequiency grid, res bw, et al adjust accordingly. But...
        #clearly out of scope for this cheap abstraction.

        #Timing will be managed by a QTimer in this case. As a result,
        #capture_interval_s is really done on best effort (although the
        #QTimer drivign it does make reasonable efforts to sync timing).
        self._capture_timer.stop()
        self._capture_timer.start(1000 * capture_interval_s)
        self.capturing = True

    def stop_continuous_capture(self):
        self.capturing = False
        self._capture_timer.stop()

    def acquire_single_power_spectrum(self):
        STAIRS_MODE = False
        self._capture_count += 1

        if STAIRS_MODE:
            STAIR_WIDTH = 50
            stair_start = self._capture_count % self._numpts
            stair_start = np.floor(stair_start / STAIR_WIDTH) * STAIR_WIDTH
            stair_stop = stair_start + STAIR_WIDTH
            ps_dBm = np.zeros(self._numpts) - 80
            ps_dBm[stair_start:stair_stop] = -20
            timestamp_s = self._capture_count
        else:
            drv = self._driver
            vrt_data, context = read_data_and_context(drv, self._numpts)

            assert isinstance(vrt_data, pyrf.vrt.DataPacket)
            ps_dBm = compute_fft(drv, vrt_data, context, convert_to_dbm=True)

            #Accumulate history...
            timestamp_s = calc_vrt_sample_time_s(vrt_data)
        self.capture_history.add_row(ps_dBm, timestamp_s=timestamp_s)

        self._last_power_spectrum_dBm = ps_dBm
        return ps_dBm

    def _on_capture_timer(self):
        #grab a spectrum! The historical model will be updated and events can
        #be driven from that directly.
        self.acquire_single_power_spectrum()