Hi- I'm having trouble with the gain control on a TVRX (actually, on a whole bunch of TVRX's). There are two problems:
1. The effect of the gain setting is far from linear. I measured this with the attached program, at 1 dB increments of requested gain, with antennas (with LNAs) attached to both channels. (I'm not completely sure I have the full scale right, but that's just an offset.) I get essentially identical results using usrp_rx_cfile.py and a little Python script to do the same calculation (also attached). 2. The system acts like there's some kind of automatic gain control. My setup is an antenna (with built-in LNA), connected through a combiner, to the USRP. On the other input to the combiner is a signal generator. The entire setup is duplicated on sides A and B. I did two runs. First, I had the signal generator on and the antenna connected. Then I replaced the antenna with a terminator and left the signal generator on. (The USRP was tuned to 106MHz and the signal was a sine wave at 106.25MHz.) The power I see in MATLAB at 0.25MHz from center is dramatically (12 dB) less with the antenna connected. Any ideas what's going on? This occurs with gnuradio trunk r10579. I'm updating now and will try again with a newer trunk shortly. Thanks, Andy
<<attachment: gain_test_090407.png>>
/* -*- c++ -*- */
/*
* Copyright 2003,2006,2008 Free Software Foundation, Inc.
*
* This file is part of GNU Radio
*
* GNU Radio is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3, or (at your option)
* any later version.
*
* GNU Radio 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 GNU Radio; see the file COPYING. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street,
* Boston, MA 02110-1301, USA.
*/
#include "usrp_tree_config.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <usb.h> /* needed for usb functions */
#include <getopt.h>
#include <assert.h>
#include <math.h>
#include <iostream>
#include "usrp_standard.h"
#include "usrp_bytesex.h"
#include "fpga_regs_common.h"
#include "fpga_regs_standard.h"
#ifdef HAVE_SCHED_H
#include <sched.h>
#endif
using std::cerr;
using std::endl;
char *prog_name;
static bool test_input (usrp_standard_rx_sptr urx, int max_bytes, FILE *fp);
static void
set_progname (char *path)
{
char *p = strrchr (path, '/');
if (p != 0)
prog_name = p+1;
else
prog_name = path;
}
static void
usage ()
{
fprintf (stderr, "usage: %s [-f] [-v] [-l] [-c] [-D <decim>] [-F freq] [-o output_file]\n", prog_name);
fprintf (stderr, " [-f] loop forever\n");
fprintf (stderr, " [-M] how many Megabytes to transfer (default 128)\n");
fprintf (stderr, " [-v] verbose\n");
fprintf (stderr, " [-l] digital loopback in FPGA\n");
fprintf (stderr, " [-c] counting in FPGA\n");
fprintf (stderr, " [-p] output DDC phase\n");
fprintf (stderr, " [-S] use external synchronization\n");
// fprintf (stderr, " [-8] 8-bit samples across USB\n");
fprintf (stderr, " [-B <fusb_block_size>] set fast usb block_size\n");
fprintf (stderr, " [-N <fusb_nblocks>] set fast usb nblocks\n");
#ifdef HAVE_SCHED_H
fprintf (stderr, " [-R] set real time scheduling: SCHED_FIFO; pri = midpoint\n");
#endif
exit (1);
}
static void
die (const char *msg)
{
fprintf (stderr, "die: %s: %s\n", prog_name, msg);
exit (1);
}
void show_db_info(usrp_standard_rx_sptr urx, int which)
{
cerr << "Daughterboard " << which << ":\n";
db_base_sptr db = urx->db()[which][0];
if (!db) {
cerr << " None found\n";
return;
}
cerr << " Daughterboard is a " << db->name() << endl;
cerr << " Gain range is " << db->gain_min() << " - " << db->gain_max()
<< " with " << db->gain_db_per_step() << "dB per step.\n";
fprintf(stderr, " Freq range is %.2f - %.2f MHz.\n",
db->freq_min() * 1e-6, db->freq_max() * 1e-6);
cerr << " Device is " << (db->is_quadrature() ? "" : "not ")
<< "quadrature.\n";
cerr << " Spectrum is " << (db->spectrum_inverted() ? "" : "not ")
<< "inverted.\n";
}
void tune_one_channel(usrp_standard_rx_sptr urx, int which, double center_freq)
{
db_base_sptr db = urx->db()[which][0];
// The "obvious" way is db->set_freq(center_freq),
// but that ends up with a huge offset because the baseband
// is probably not DC. So we use the magic helper, which does
// further processing in the FPGA.
// A future incarnation might want to skip the latter
// step and do it in software for added control, which is problematic
// due to bandwidth issues.
usrp_tune_result result;
if (!urx->tune(which, db, center_freq, &result))
die("Tuning failed");
fprintf(stderr, "Tune request for channel %d to %.6lfMHz:\n",
which, center_freq * 1e-6);
fprintf(stderr, " baseband=%.6lfMHz, dxc=%.6fMHz\n",
result.baseband_freq * 1e-6, result.dxc_freq * 1e-6);
fprintf(stderr, " residual error=%.4fHz\n", result.residual_freq);
fprintf(stderr, " %sinverted\n", result.inverted ? "" : "not ");
}
void tune_both_channels(usrp_standard_rx_sptr urx, double center_freq)
{
if (!urx->set_rx_freq (0, center_freq))
die ("urx->set_rx_freq");
tune_one_channel(urx, 0, center_freq);
tune_one_channel(urx, 1, center_freq);
}
int
main (int argc, char **argv)
{
bool verbose_p = false;
bool loopback_p = false;
bool counting_p = false;
bool phase_p = false;
bool sync_p = false;
// bool width_8_p = false;
int max_bytes = 128 * (1L << 20);
int ch;
char *output_filename = 0;
int decim = 16; // 32 MB/sec
double center_freq = 0;
float gain = 0;
int fusb_block_size = 0;
int fusb_nblocks = 0;
bool realtime_p = false;
set_progname (argv[0]);
while ((ch = getopt (argc, argv, "fvlcpSo:g:D:F:M:B:N:R")) != EOF){
switch (ch){
case 'f':
max_bytes = 0;
break;
case 'v':
verbose_p = true;
break;
case 'l':
loopback_p = true;
break;
case 'c':
counting_p = true;
break;
case 'p':
phase_p = true;
break;
case 'S':
sync_p = true;
break;
/*
case '8':
width_8_p = true;
break;
*/
case 'o':
output_filename = optarg;
break;
case 'D':
decim = strtol (optarg, 0, 0);
break;
case 'F':
center_freq = strtod (optarg, 0);
break;
case 'g':
gain = strtof (optarg, 0);
break;
case 'M':
max_bytes = strtol (optarg, 0, 0) * (1L << 20);
if (max_bytes < 0) max_bytes = 0;
break;
case 'B':
fusb_block_size = strtol (optarg, 0, 0);
break;
case 'N':
fusb_nblocks = strtol (optarg, 0, 0);
break;
case 'R':
realtime_p = true;
break;
default:
usage ();
}
}
#ifdef HAVE_SCHED_SETSCHEDULER
if (realtime_p){
int policy = SCHED_FIFO;
int pri = (sched_get_priority_max (policy) - sched_get_priority_min (policy)) / 2;
int pid = 0; // this process
struct sched_param param;
memset(¶m, 0, sizeof(param));
param.sched_priority = pri;
int result = sched_setscheduler(pid, policy, ¶m);
if (result != 0){
perror ("sched_setscheduler: failed to set real time priority");
}
else
printf("SCHED_FIFO enabled with priority = %d\n", pri);
}
#endif
FILE *fp = 0;
if (output_filename){
fp = fopen (output_filename, "wb");
if (fp == 0)
perror (output_filename);
}
int mode = 0;
if (loopback_p)
mode |= usrp_standard_rx::FPGA_MODE_LOOPBACK;
if (counting_p)
mode |= usrp_standard_rx::FPGA_MODE_COUNTING;
if (phase_p)
mode |= 0x4;
if (sync_p)
mode |= 0x8;
usrp_standard_rx_sptr urx =
usrp_standard_rx::make (0, decim, 2, -1, mode,
fusb_block_size, fusb_nblocks);
if (!urx)
die ("usrp_standard_rx::make");
// Enumerate the daughterboards (sorry no BasicRX supported yet)
if (verbose_p) {
cerr << "Connected to USRP with s/n " << urx->serial_number() << endl;
cerr << "Decimation is " << decim << endl << endl;
show_db_info(urx, 0);
show_db_info(urx, 1);
cerr << endl;
}
if (center_freq == 0) {
cerr << "WARNING: No center freq specified (mystery data)!\n";
} else {
tune_both_channels(urx, center_freq);
}
// Note: if we hacked around a bit, we might get
// *separate* RF, IF, and AGC gain controls. This
// may be tweakable to get better performance.
if (!urx->db()[0][0]->set_gain(gain))
die("Failed to set channel 0 gain");
if (!urx->db()[1][0]->set_gain(gain))
die("Failed to set channel 1 gain");
if (verbose_p)
cerr << "Gain set on both channels to " << gain << endl;
/*
if (width_8_p){
int width = 8;
int shift = 8;
bool want_q = true;
if (!urx->set_format(usrp_standard_rx::make_format(width, shift, want_q)))
die("urx->set_format");
}
*/
{
timeval tv;
tv.tv_sec = 2;
tv.tv_usec = 0;
if (verbose_p)
fprintf(stderr, "Waiting 2 seconds for oscillators to settle... ");
select(0, 0, 0, 0, &tv);
if (verbose_p)
fprintf(stderr, "done\n");
}
if (verbose_p && 0) {
// Oddly this screws everything up. So it's disabled.
int mux_val;
if (!urx->_read_fpga_reg(FR_RX_MUX, &mux_val))
die("Failed to read HW mux register");
fprintf(stderr, "HW mux value = 0x%08X\n", (unsigned int)mux_val);
}
urx->start(); // start data xfers
test_input (urx, max_bytes, fp);
if (fp)
fclose (fp);
return 0;
}
struct timespec read_clock(clockid_t clk_id)
{
timespec ret;
if (clock_gettime(clk_id, &ret) != 0)
die("clock_gettime");
return ret;
}
int64_t operator - (const timespec &end, const timespec &start)
{
return (end.tv_nsec - start.tv_nsec)
+ (end.tv_sec - start.tv_sec) * 1000000000LL;
}
static bool
test_input (usrp_standard_rx_sptr urx, int max_bytes, FILE *fp)
{
int fd = -1;
static const int BUFSIZE = urx->block_size();
static const int N = BUFSIZE/sizeof (short);
short buf[N];
int nbytes = 0;
timespec start_monotonic = read_clock(CLOCK_MONOTONIC);
timespec start_cpu = read_clock(CLOCK_THREAD_CPUTIME_ID);
if (fp)
fd = fileno (fp);
bool overrun;
int noverruns = 0;
double sumsquares = 0.0;
uint64_t n = 0;
for (nbytes = 0; max_bytes == 0 || nbytes < max_bytes; nbytes += BUFSIZE){
unsigned int ret = urx->read (buf, sizeof (buf), &overrun);
if (ret != sizeof (buf)){
fprintf (stderr, "test_input: error, ret = %d\n", ret);
}
if (overrun){
printf ("rx_overrun\n");
noverruns++;
}
double localsumsquares = 0.0;
for (unsigned int i = 0; i < sizeof (buf) / sizeof (short); i++) {
buf[i] = usrp_to_host_short (buf[i]);
localsumsquares += buf[i] * buf[i];
}
sumsquares += localsumsquares;
n += sizeof(buf) / sizeof(short);
if (fd != -1){
if (write (fd, buf, sizeof (buf)) == -1){
perror ("write");
fd = -1;
}
}
}
timespec stop_cpu = read_clock(CLOCK_THREAD_CPUTIME_ID);
timespec stop_monotonic = read_clock(CLOCK_MONOTONIC);
double delta_wall = (stop_monotonic - start_monotonic) * 1e-9;
double delta_cpu = (stop_cpu - start_cpu) * 1e-9;
printf ("xfered %.2f MB in %.3f seconds. %.2f MB/sec. cpu time = %.4f\n",
max_bytes * 1e-6, delta_wall,
max_bytes / delta_wall * 1e-6, delta_cpu);
printf ("noverruns = %d\n", noverruns);
if (n) {
double meansquare = sumsquares / n;
printf ("mean power = %.2fdBfs\n",
10*log10(meansquare / ((1<<14) * (1<<14))));
}
return true;
}
avg_power
Description: Binary data
_______________________________________________ Discuss-gnuradio mailing list [email protected] http://lists.gnu.org/mailman/listinfo/discuss-gnuradio
