summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--gr-uhd/apps/uhd_app.py47
-rwxr-xr-xgr-uhd/apps/uhd_fft1
-rw-r--r--gr-uhd/apps/uhd_siggen_base.py74
-rwxr-xr-xgr-uhd/apps/uhd_siggen_gui38
4 files changed, 137 insertions, 23 deletions
diff --git a/gr-uhd/apps/uhd_app.py b/gr-uhd/apps/uhd_app.py
index f666e1e28f..d878a063c5 100644
--- a/gr-uhd/apps/uhd_app.py
+++ b/gr-uhd/apps/uhd_app.py
@@ -28,7 +28,13 @@ LONG_TPL = """{prefix} Motherboard: {mb_id} ({mb_serial})
{prefix} Antenna: {ant}
"""
+# PyLint can't reliably detect C++ exports in modules, so let's disable that
+# pylint: disable=no-member
+
class UHDApp:
+ GAIN_TYPE_GAIN = 'dB'
+ GAIN_TYPE_POWER = 'power_dbm'
+
" Base class for simple UHD-based applications "
def __init__(self, prefix=None, args=None):
self.prefix = prefix
@@ -37,7 +43,7 @@ class UHDApp:
if self.args.sync == 'auto' and len(self.args.channels) > 1:
self.args.sync = 'pps'
self.antenna = None
- self.gain_range = None
+ self.gain_range = None # Can also be power range
self.samp_rate = None
self.has_lo_sensor = None
self.async_msgq = None
@@ -55,6 +61,7 @@ class UHDApp:
self.lo_export = None
self.usrp = None
self.lo_source_channel = None
+ self.gain_type = self.GAIN_TYPE_GAIN
def vprint(self, *args):
"""
@@ -179,8 +186,20 @@ class UHDApp:
))
self.antenna = self.usrp.get_antenna(0)
# Set receive daughterboard gain:
- self.set_gain(args.gain)
- self.gain_range = self.usrp.get_gain_range(0)
+ if args.power:
+ if args.gain is not None:
+ print("[ERROR] Providing both --gain and --power is invalid!")
+ sys.exit(1)
+ self.gain_type = self.GAIN_TYPE_POWER
+ if not self.usrp.has_power_reference(0):
+ print("[ERROR] Device does not have power reference capabilities!")
+ sys.exit(1)
+ self.set_power_reference(args.power)
+ self.gain_range = self.usrp.get_power_range(0)
+ else:
+ self.set_gain(args.gain)
+ self.gain_range = self.usrp.get_gain_range(0)
+ self.gain_type = self.GAIN_TYPE_GAIN
# Set frequency (tune request takes lo_offset):
if hasattr(args, 'lo_offset') and args.lo_offset is not None:
treq = uhd.tune_request(args.freq, args.lo_offset)
@@ -268,11 +287,20 @@ class UHDApp:
chan=chan, g=self.usrp.get_gain(i)
))
else:
- self.vprint("Setting gain to {g} dB.".format(g=gain))
+ self.vprint("Setting gain to {g:.2f} dB.".format(g=gain))
for chan in range(len(self.channels)):
self.usrp.set_gain(gain, chan)
self.gain = self.usrp.get_gain(0)
+ def set_power_reference(self, power_dbm):
+ """
+ Safe power-ref-setter.
+ """
+ assert power_dbm is not None
+ self.vprint("Setting ref power to {g:.2f} dBm.".format(g=power_dbm))
+ self.usrp.set_power_reference(power_dbm)
+ self.gain = self.usrp.get_power_reference(0)
+
def set_freq(self, freq, skip_sync=False):
"""
Safely tune all channels to freq.
@@ -364,12 +392,11 @@ class UHDApp:
help="Sample rate")
group.add_argument("-g", "--gain", type=eng_arg.eng_float, default=None,
help="Gain (default is midpoint)")
- group.add_argument("--gain-type", choices=('db', 'normalized'), default='db',
- help="Gain Type (applies to -g)")
- group.add_argument("-p", "--power-ref", type=eng_arg.eng_float, default=None,
- help="Reference power level (in dBm). "
+ group.add_argument("-p", "--power", type=eng_arg.eng_float, default=None,
+ help="(Reference) power level (in dBm). "
"Not supported by all devices (see UHD manual). "
- "Will fail if not supported. Precludes --gain. ")
+ "Will fail if not supported. Precludes --gain. "
+ "Behaviour may differ between applications.")
if not skip_freq:
group.add_argument("-f", "--freq", type=eng_arg.eng_float, default=None, required=True,
help="Set carrier frequency to FREQ",
@@ -390,8 +417,6 @@ class UHDApp:
group.add_argument("--otw-format", choices=['sc16', 'sc12', 'sc8'], default='sc16',
help="Choose over-the-wire data format")
group.add_argument("--stream-args", default="", help="Set additional stream arguments")
- group.add_argument("-m", "--amplitude", type=eng_arg.eng_float, default=0.15,
- help="Set output amplitude to AMPL (0.0-1.0)", metavar="AMPL")
group.add_argument("-v", "--verbose", action="count", help="Use verbose console output")
group.add_argument("--show-async-msg", action="store_true",
help="Show asynchronous message notifications from UHD")
diff --git a/gr-uhd/apps/uhd_fft b/gr-uhd/apps/uhd_fft
index cc870e991c..f9b51723c6 100755
--- a/gr-uhd/apps/uhd_fft
+++ b/gr-uhd/apps/uhd_fft
@@ -89,7 +89,6 @@ class uhd_fft(UHDApp, gr.top_block, Qt.QWidget):
self.fft_size = args.fft_size
self.freq = args.freq
self.gain = args.gain
- self.gain_type = args.gain_type
self.samp_rate = args.samp_rate
self.spec = args.spec
self.stream_args = args.stream_args
diff --git a/gr-uhd/apps/uhd_siggen_base.py b/gr-uhd/apps/uhd_siggen_base.py
index 38d197e3ad..daf8184f56 100644
--- a/gr-uhd/apps/uhd_siggen_base.py
+++ b/gr-uhd/apps/uhd_siggen_base.py
@@ -22,6 +22,9 @@ from gnuradio import analog
from gnuradio import blocks
from gnuradio.gr.pubsub import pubsub
+# PyLint can't reliably detect C++ exports in modules, so let's disable that
+# pylint: disable=no-member
+
DESC_KEY = 'desc'
SAMP_RATE_KEY = 'samp_rate'
LINK_RATE_KEY = 'link_rate'
@@ -35,7 +38,6 @@ WAVEFORM_FREQ_KEY = 'waveform_freq'
WAVEFORM_OFFSET_KEY = 'waveform_offset'
WAVEFORM2_FREQ_KEY = 'waveform2_freq'
FREQ_RANGE_KEY = 'freq_range'
-GAIN_RANGE_KEY = 'gain_range'
TYPE_KEY = 'type'
WAVEFORMS = {
@@ -52,9 +54,25 @@ class USRPSiggen(gr.top_block, pubsub, UHDApp):
GUI-unaware GNU Radio flowgraph. This may be used either with command
line applications or GUI applications.
"""
+ MIN_AMP_POWER_MODE = .001
+
def __init__(self, args):
gr.top_block.__init__(self)
pubsub.__init__(self)
+ if not 0.0 <= args.amplitude <= 1.0:
+ raise ValueError(
+ "Invalid value for amplitude: {}. Must be in [0.0, 1.0]"
+ .format(args.amplitude))
+ # If the power argument is given, we need to turn that into a power
+ # *reference* level. This is a bit of a hack because we're assuming
+ # knowledge of UHDApp (i.e. we're leaking abstractions). But it's simple
+ # and harmless enough.
+ if args.power:
+ if args.amplitude < self.MIN_AMP_POWER_MODE:
+ raise RuntimeError(
+ "[ERROR] Invalid amplitude: In power mode, amplitude must be "
+ "larger than {}!".format(self.MIN_AMP_POWER_MODE))
+ args.power -= 20 * math.log10(args.amplitude)
UHDApp.__init__(self, args=args, prefix="UHD-SIGGEN")
self.extra_sink = None
@@ -78,8 +96,7 @@ class USRPSiggen(gr.top_block, pubsub, UHDApp):
self.publish(SAMP_RATE_KEY, lambda: self.usrp.get_samp_rate())
self.publish(DESC_KEY, lambda: self.usrp_description)
self.publish(FREQ_RANGE_KEY, lambda: self.usrp.get_freq_range(self.channels[0]))
- self.publish(GAIN_RANGE_KEY, lambda: self.usrp.get_gain_range(self.channels[0]))
- self.publish(GAIN_KEY, lambda: self.usrp.get_gain(self.channels[0]))
+ self.publish(GAIN_KEY, lambda: self.get_gain_or_power())
self[SAMP_RATE_KEY] = args.samp_rate
self[TX_FREQ_KEY] = args.freq
@@ -92,12 +109,13 @@ class USRPSiggen(gr.top_block, pubsub, UHDApp):
#subscribe set methods
self.subscribe(SAMP_RATE_KEY, self.set_samp_rate)
- self.subscribe(GAIN_KEY, self.set_gain)
+ self.subscribe(GAIN_KEY, self.set_gain_or_power)
self.subscribe(TX_FREQ_KEY, self.set_freq)
self.subscribe(AMPLITUDE_KEY, self.set_amplitude)
self.subscribe(WAVEFORM_FREQ_KEY, self.set_waveform_freq)
self.subscribe(WAVEFORM2_FREQ_KEY, self.set_waveform2_freq)
self.subscribe(TYPE_KEY, self.set_waveform)
+ self.subscribe(RF_FREQ_KEY, self.update_gain_range)
#force update on pubsub keys
for key in (SAMP_RATE_KEY, GAIN_KEY, TX_FREQ_KEY,
@@ -234,8 +252,51 @@ class USRPSiggen(gr.top_block, pubsub, UHDApp):
else:
return True # Waveform not yet set
self.vprint("Set amplitude to:", amplitude)
+ self.update_gain_range()
return True
+ def get_gain_or_power(self):
+ """
+ Depending on gain type, return either a power level or the current gain
+ """
+ if self.gain_type == self.GAIN_TYPE_GAIN:
+ return self.usrp.get_gain(self.channels[0])
+ return self.usrp.get_power_reference(self.channels[0]) \
+ + 20 * math.log10(self[AMPLITUDE_KEY])
+
+ def set_gain_or_power(self, gain_or_power):
+ """
+ Call this if a gain or power value changed, but you're not sure which it
+ is.
+
+ If it's a power, we subtract the signal offset to generate a reference
+ power.
+ """
+ if self.gain_type == self.GAIN_TYPE_POWER:
+ self.set_power_reference(
+ gain_or_power - 20 * math.log10(self[AMPLITUDE_KEY]))
+ else:
+ self.set_gain(gain_or_power)
+
+ def update_gain_range(self):
+ """
+ Update self.gain_range.
+ """
+ if self.gain_type == self.GAIN_TYPE_POWER:
+ if self[AMPLITUDE_KEY] < self.MIN_AMP_POWER_MODE:
+ raise RuntimeError(
+ "[ERROR] Invalid amplitude: In power mode, amplitude must be "
+ "larger than {}!".format(self.MIN_AMP_POWER_MODE))
+ power_range = self.usrp.get_power_range(self.channels[0])
+ ampl_offset = 20 * math.log10(self[AMPLITUDE_KEY])
+ self.gain_range = uhd.meta_range(
+ math.floor(power_range.start() + ampl_offset),
+ math.ceil(power_range.stop() + ampl_offset),
+ power_range.step()
+ )
+ self.vprint("Updated power range to {:.2f} ... {:.2f} dBm.".format(
+ self.gain_range.start(), self.gain_range.stop()))
+
def setup_argparser():
"""
@@ -246,6 +307,11 @@ def setup_argparser():
tx_or_rx="Tx",
)
group = parser.add_argument_group('Siggen Arguments')
+ group.add_argument("-m", "--amplitude", type=eng_arg.eng_float, default=0.15,
+ help="Set output amplitude to AMPL (0.0-1.0). Note that "
+ "if --power is given, UHD will attempt to match the "
+ "output power regardless of the amplitude.",
+ metavar="AMPL")
group.add_argument("-x", "--waveform-freq", type=eng_arg.eng_float, default=0.0,
help="Set baseband waveform frequency to FREQ")
group.add_argument("-y", "--waveform2-freq", type=eng_arg.eng_float, default=0.0,
diff --git a/gr-uhd/apps/uhd_siggen_gui b/gr-uhd/apps/uhd_siggen_gui
index 26d1bf647c..81bbd5a029 100755
--- a/gr-uhd/apps/uhd_siggen_gui
+++ b/gr-uhd/apps/uhd_siggen_gui
@@ -23,6 +23,7 @@ Signal Generator App
import sys
import threading
import time
+import math
from PyQt5 import Qt
from PyQt5.QtCore import pyqtSlot
import sip # Needs to be imported after PyQt5, could fail otherwise
@@ -190,7 +191,11 @@ class uhd_siggen_gui(Qt.QWidget):
self._freq2_offset_win.setEnabled(self._sg[uhd_siggen.TYPE_KEY] in self._freq2_enable_on)
self.top_grid_layout.addWidget(self._freq2_offset_win, 4, 3, 1, 2)
### Amplitude
- self._amplitude_range = Range(0, 1, .001, .7, 200)
+ min_ampl = \
+ self._sg.MIN_AMP_POWER_MODE \
+ if self._sg.gain_type == self._sg.GAIN_TYPE_POWER else 0
+ self._amplitude_range = \
+ Range(min_ampl, 1, .001, self._sg[uhd_siggen.AMPLITUDE_KEY], 200)
self._amplitude_win = RangeWidget(
self._amplitude_range,
self.set_amplitude,
@@ -199,18 +204,22 @@ class uhd_siggen_gui(Qt.QWidget):
float
)
self.top_grid_layout.addWidget(self._amplitude_win, 5, 0, 1, 5)
- ### Gain
+ ### Gain or power
self._gain_range = Range(
- self.usrp.get_gain_range(self._sg.channels[0]).start(),
- self.usrp.get_gain_range(self._sg.channels[0]).stop(),
+ math.floor(self._sg.gain_range.start()),
+ math.ceil(self._sg.gain_range.stop()),
.5,
- self.usrp.get_gain(self._sg.channels[0]),
+ self._sg.get_gain_or_power(),
200.,
)
self._gain_win = RangeWidget(
self._gain_range,
- self._sg.set_gain,
- "TX Gain", "counter_slider", float
+ self.set_gain_or_power,
+ "TX Gain (dB)"
+ if self._sg.gain_type == self._sg.GAIN_TYPE_GAIN
+ else "TX Power (dBm)",
+ "counter_slider",
+ float
)
self.top_grid_layout.addWidget(self._gain_win, 6, 0, 1, 5)
### Samp rate, LO sync, Antenna Select
@@ -375,6 +384,8 @@ class uhd_siggen_gui(Qt.QWidget):
if idx == 0:
self.set_label_dsp_freq(tune_res.actual_dsp_freq)
self.set_label_rf_freq(tune_res.actual_rf_freq)
+ if self._sg.gain_type == self._sg.GAIN_TYPE_POWER:
+ self._sg[uhd_siggen.RF_FREQ_KEY] = self._sg[uhd_siggen.RF_FREQ_KEY]
def set_freq1_offset(self, freq1_offset):
""" Execute when the freq1 slider is moved """
@@ -386,7 +397,20 @@ class uhd_siggen_gui(Qt.QWidget):
def set_amplitude(self, amplitude):
""" Execute when the amplitude slider is moved """
+ # Here's a catch-22: The current power is calculated using the amplitude
+ # so we need to stash it before the amplitude changes, then reapply
+ # later.
+ power = self._sg[uhd_siggen.GAIN_KEY]
self._sg[uhd_siggen.AMPLITUDE_KEY] = amplitude
+ if self._sg.gain_type == self._sg.GAIN_TYPE_POWER:
+ self._sg[uhd_siggen.GAIN_KEY] = power
+
+ def set_gain_or_power(self, gain_or_power):
+ """
+ Execute when the gain/power slider is moved
+ """
+ self._sg.set_gain_or_power(gain_or_power)
+ self._gain_win.d_widget.setValue(self._sg.get_gain_or_power())
def set_sync_phases(self, sync):
""" Execute when the sync-phases button is pushed """