mirror of https://gerrit.osmocom.org/libusrp
320 lines
7.1 KiB
C++
320 lines
7.1 KiB
C++
/* -*- c++ -*- */
|
|
/*
|
|
* Copyright 2003,2004 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.
|
|
*/
|
|
|
|
#ifdef HAVE_CONFIG_H
|
|
#include "config.h"
|
|
#endif
|
|
|
|
#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 "time_stuff.h"
|
|
#include "usrp_standard.h"
|
|
#include "usrp_bytesex.h"
|
|
|
|
#ifdef HAVE_SCHED_H
|
|
#include <sched.h>
|
|
#endif
|
|
|
|
char *prog_name;
|
|
|
|
static bool test_output (usrp_standard_tx *utx, int max_bytes, double ampl,
|
|
bool dc_p, bool counting_p);
|
|
|
|
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] [-d] [-c] [-a <ampl>][-I <interp>] [-F freq] [-D]\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, " [-d] dump registers\n");
|
|
// fprintf (stderr, " [-l] digital loopback in FPGA\n");
|
|
fprintf (stderr, " [-c] Tx counting sequence\n");
|
|
fprintf (stderr, " [-D] DC output\n");
|
|
|
|
fprintf (stderr, " [-B <fusb_block_size>] set fast usb block_size\n");
|
|
fprintf (stderr, " [-N <fusb_nblocks>] set fast usb nblocks\n");
|
|
fprintf (stderr, " [-R] set real time scheduling: SCHED_FIFO; pri = midpoint\n");
|
|
|
|
exit (1);
|
|
}
|
|
|
|
static void
|
|
die (const char *msg)
|
|
{
|
|
fprintf (stderr, "die: %s: %s\n", prog_name, msg);
|
|
exit (1);
|
|
}
|
|
|
|
static void
|
|
dump_codec_regs (usrp_basic *u, int which_codec, FILE *fp)
|
|
{
|
|
for (int i = 0; i < 64; i++){
|
|
unsigned char v;
|
|
u->_read_9862 (which_codec, i, &v);
|
|
fprintf (fp, "%2d: 0x%02x\n", i, v);
|
|
}
|
|
fflush (fp);
|
|
}
|
|
|
|
static void
|
|
do_dump_codec_regs (usrp_basic *u)
|
|
{
|
|
char name[100];
|
|
strcpy (name, "regsXXXXXX");
|
|
int fd = mkstemp (name);
|
|
if (fd == -1){
|
|
perror (name);
|
|
}
|
|
else {
|
|
FILE *fp = fdopen (fd, "w");
|
|
dump_codec_regs (u, 0, fp);
|
|
fclose (fp);
|
|
}
|
|
}
|
|
|
|
int
|
|
main (int argc, char **argv)
|
|
{
|
|
bool verbose_p = false;
|
|
bool dump_regs_p = false;
|
|
bool dc_p = false;
|
|
// bool loopback_p = false;
|
|
bool counting_p = false;
|
|
int max_bytes = 128 * (1L << 20);
|
|
int ch;
|
|
int which_board = 0;
|
|
int interp = 16; // 32.0 MB/sec
|
|
double center_freq = 0;
|
|
double ampl = 10000;
|
|
int fusb_block_size = 0;
|
|
int fusb_nblocks = 0;
|
|
bool realtime_p = false;
|
|
|
|
|
|
set_progname (argv[0]);
|
|
|
|
while ((ch = getopt (argc, argv, "vfdcI:F:a:DM:B:N:R")) != EOF){
|
|
switch (ch){
|
|
case 'f':
|
|
max_bytes = 0;
|
|
break;
|
|
|
|
case 'v':
|
|
verbose_p = true;
|
|
break;
|
|
|
|
case 'd':
|
|
dump_regs_p = true;
|
|
break;
|
|
|
|
case 'D':
|
|
dc_p = true;
|
|
break;
|
|
|
|
#if 0
|
|
case 'l':
|
|
loopback_p = true;
|
|
break;
|
|
#endif
|
|
|
|
case 'c':
|
|
counting_p = true;
|
|
break;
|
|
|
|
case 'I':
|
|
interp = strtol (optarg, 0, 0);
|
|
break;
|
|
|
|
case 'F':
|
|
center_freq = strtod (optarg, 0);
|
|
break;
|
|
|
|
case 'a':
|
|
ampl = strtod (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
|
|
|
|
usrp_standard_tx *utx;
|
|
|
|
utx = usrp_standard_tx::make (which_board,
|
|
interp,
|
|
1, // nchan
|
|
-1, // mux
|
|
fusb_block_size,
|
|
fusb_nblocks);
|
|
|
|
if (utx == 0)
|
|
die ("usrp_standard_tx::make");
|
|
|
|
if (!utx->set_tx_freq (0, center_freq))
|
|
die ("utx->set_tx_freq");
|
|
|
|
if (dump_regs_p)
|
|
do_dump_codec_regs (utx);
|
|
|
|
|
|
fflush (stdout);
|
|
fflush (stderr);
|
|
|
|
utx->start(); // start data xfers
|
|
|
|
test_output (utx, max_bytes, ampl, dc_p, counting_p);
|
|
|
|
delete utx;
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
static bool
|
|
test_output (usrp_standard_tx *utx, int max_bytes, double ampl,
|
|
bool dc_p, bool counting_p)
|
|
{
|
|
static const int BUFSIZE = utx->block_size();
|
|
static const int N = BUFSIZE/sizeof (short);
|
|
|
|
short buf[N];
|
|
int nbytes = 0;
|
|
int counter = 0;
|
|
|
|
static const int PERIOD = 65; // any value is valid
|
|
static const int PATLEN = 2 * PERIOD;
|
|
short pattern[PATLEN];
|
|
|
|
for (int i = 0; i < PERIOD; i++){
|
|
if (dc_p){
|
|
pattern[2*i+0] = host_to_usrp_short ((short) ampl);
|
|
pattern[2*i+1] = host_to_usrp_short ((short) 0);
|
|
}
|
|
else {
|
|
pattern[2*i+0] = host_to_usrp_short ((short) (ampl * cos (2*M_PI * i / PERIOD)));
|
|
pattern[2*i+1] = host_to_usrp_short ((short) (ampl * sin (2*M_PI * i / PERIOD)));
|
|
}
|
|
}
|
|
|
|
double start_wall_time = get_elapsed_time ();
|
|
double start_cpu_time = get_cpu_usage ();
|
|
|
|
bool underrun;
|
|
int nunderruns = 0;
|
|
int pi = 0;
|
|
|
|
for (nbytes = 0; max_bytes == 0 || nbytes < max_bytes; nbytes += BUFSIZE){
|
|
|
|
if (counting_p){
|
|
for (int i = 0; i < N; i++)
|
|
buf[i] = host_to_usrp_short (counter++ & 0xffff);
|
|
}
|
|
else {
|
|
for (int i = 0; i < N; i++){
|
|
buf[i] = pattern[pi];
|
|
pi++;
|
|
if (pi >= PATLEN)
|
|
pi = 0;
|
|
}
|
|
}
|
|
|
|
int ret = utx->write (buf, sizeof (buf), &underrun);
|
|
if ((unsigned) ret != sizeof (buf)){
|
|
fprintf (stderr, "test_output: error, ret = %d\n", ret);
|
|
}
|
|
|
|
if (underrun){
|
|
nunderruns++;
|
|
printf ("tx_underrun\n");
|
|
//printf ("tx_underrun %9d %6d\n", nbytes, nbytes/BUFSIZE);
|
|
}
|
|
}
|
|
|
|
utx->wait_for_completion ();
|
|
|
|
double stop_wall_time = get_elapsed_time ();
|
|
double stop_cpu_time = get_cpu_usage ();
|
|
|
|
double delta_wall = stop_wall_time - start_wall_time;
|
|
double delta_cpu = stop_cpu_time - start_cpu_time;
|
|
|
|
printf ("xfered %.3g bytes in %.3g seconds. %.4g bytes/sec. cpu time = %.3g\n",
|
|
(double) max_bytes, delta_wall, max_bytes / delta_wall, delta_cpu);
|
|
|
|
printf ("%d underruns\n", nunderruns);
|
|
|
|
return true;
|
|
}
|