diff options
Diffstat (limited to 'usrp2/host/apps')
-rw-r--r-- | usrp2/host/apps/Makefile.am | 44 | ||||
-rw-r--r-- | usrp2/host/apps/find_usrps.cc | 70 | ||||
-rwxr-xr-x | usrp2/host/apps/gen_2tone.py | 75 | ||||
-rw-r--r-- | usrp2/host/apps/gen_const.cc | 27 | ||||
-rwxr-xr-x | usrp2/host/apps/gen_sine.py | 63 | ||||
-rw-r--r-- | usrp2/host/apps/rx_samples.cc | 382 | ||||
-rw-r--r-- | usrp2/host/apps/rx_streaming_samples.cc | 372 | ||||
-rwxr-xr-x | usrp2/host/apps/stdin_int32_fft.py | 201 | ||||
-rwxr-xr-x | usrp2/host/apps/streaming_fft.py | 76 | ||||
-rwxr-xr-x | usrp2/host/apps/test.sh | 4 | ||||
-rw-r--r-- | usrp2/host/apps/tx_samples.cc | 263 | ||||
-rw-r--r-- | usrp2/host/apps/usrp2_burn_mac_addr.cc | 176 |
12 files changed, 1753 insertions, 0 deletions
diff --git a/usrp2/host/apps/Makefile.am b/usrp2/host/apps/Makefile.am new file mode 100644 index 000000000..744dd5702 --- /dev/null +++ b/usrp2/host/apps/Makefile.am @@ -0,0 +1,44 @@ +# +# Copyright 2007, 2008 Free Software Foundation, Inc. +# +# This program 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 of the License, or +# (at your option) any later version. +# +# This program 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 this program. If not, see <http://www.gnu.org/licenses/>. +# + +include $(top_srcdir)/Makefile.common + +AM_CPPFLAGS = \ + $(USRP2_INCLUDES) \ + $(STD_DEFINES_AND_INCLUDES) \ + $(CPPUNIT_INCLUDES) \ + $(GRUEL_INCLUDES) + +LDADD = \ + $(USRP2_LA) \ + $(GRUEL_LA) \ + $(OMNITHREAD_LA) + +bin_PROGRAMS = \ + find_usrps \ + usrp2_burn_mac_addr + +noinst_PROGRAMS = \ + gen_const \ + rx_streaming_samples \ + tx_samples + +find_usrps_SOURCES = find_usrps.cc +usrp2_burn_mac_addr_SOURCES = usrp2_burn_mac_addr.cc +rx_streaming_samples_SOURCES = rx_streaming_samples.cc +gen_const_SOURCES = gen_const.cc +tx_samples_SOURCES = tx_samples.cc diff --git a/usrp2/host/apps/find_usrps.cc b/usrp2/host/apps/find_usrps.cc new file mode 100644 index 000000000..1010c4aff --- /dev/null +++ b/usrp2/host/apps/find_usrps.cc @@ -0,0 +1,70 @@ +/* -*- c++ -*- */ +/* + * Copyright 2007,2008 Free Software Foundation, Inc. + * + * This program 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 of the License, or + * (at your option) any later version. + * + * This program 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 this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <usrp2/usrp2.h> +#include <iostream> +#include <getopt.h> + +static void +usage(const char *progname) +{ + fprintf(stderr, "usage: %s [-e ethN]\n", + progname); +} + +int +main(int argc, char **argv) +{ + int ch; + const char *interface = "eth0"; + + while ((ch = getopt(argc, argv, "he:")) != EOF){ + switch (ch){ + case 'e': + interface = optarg; + break; + + case 'h': + default: + usage(argv[0]); + exit(1); + } + } + + if (argc - optind != 0){ + usage(argv[0]); + exit(1); + } + + usrp2::props_vector_t r = usrp2::find(interface); + + for (size_t i = 0; i < r.size(); i++){ + std::cout << r[i] << std::endl; + } + + if (r.size() == 0){ + std::cerr << "No USRP2 found.\n"; + return 1; + } + + return 0; +} diff --git a/usrp2/host/apps/gen_2tone.py b/usrp2/host/apps/gen_2tone.py new file mode 100755 index 000000000..ec681d3e0 --- /dev/null +++ b/usrp2/host/apps/gen_2tone.py @@ -0,0 +1,75 @@ +#!/usr/bin/env python +# +# Copyright 2007 Free Software Foundation, Inc. +# +# This program 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 of the License, or +# (at your option) any later version. +# +# This program 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 this program. If not, see <http://www.gnu.org/licenses/>. +# + +from gnuradio import gr, eng_notation +from gnuradio.eng_option import eng_option +from optparse import OptionParser +import sys + +master_clock = 100e6 + +class my_top_block(gr.top_block): + + def __init__(self): + gr.top_block.__init__(self) + + parser = OptionParser(option_class=eng_option) + parser.add_option("-f", "--freq1", type="eng_float", default=1e6, + help="set waveform frequency to FREQ [default=%default]") + parser.add_option("-g", "--freq2", type="eng_float", default=1e6, + help="set waveform frequency to FREQ [default=%default]") + parser.add_option ("-a", "--amplitude1", type="eng_float", default=16e3, + help="set waveform amplitude to AMPLITUDE [default=%default]", metavar="AMPL") + parser.add_option ("-b", "--amplitude2", type="eng_float", default=16e3, + help="set waveform amplitude to AMPLITUDE [default=%default]", metavar="AMPL") + + parser.add_option("-i", "--interp", type="int", default=32, + help="assume fgpa interpolation rate is INTERP [default=%default]") + + (options, args) = parser.parse_args () + if len(args) != 0: + parser.print_help() + raise SystemExit, 1 + + + src0 = gr.sig_source_c(master_clock/options.interp, + gr.GR_SIN_WAVE, + options.freq1, + options.amplitude1) + src1 = gr.sig_source_c(master_clock/options.interp, + gr.GR_SIN_WAVE, + options.freq2, + options.amplitude2) + + adder = gr.add_cc() + + + c2s = gr.complex_to_interleaved_short() + + stdout_sink = gr.file_descriptor_sink(gr.sizeof_short, 1) + + self.connect(src0, (adder,0)) + self.connect(src1, (adder,1)) + self.connect(adder, c2s, stdout_sink) + + +if __name__ == '__main__': + try: + my_top_block().run() + except KeyboardInterrupt: + pass diff --git a/usrp2/host/apps/gen_const.cc b/usrp2/host/apps/gen_const.cc new file mode 100644 index 000000000..d2c36ebba --- /dev/null +++ b/usrp2/host/apps/gen_const.cc @@ -0,0 +1,27 @@ +#include <stdio.h> +#include <stdint.h> +#include <stdlib.h> +#include <unistd.h> + +int +main(int argc, char **argv) +{ + if (argc != 3){ + fprintf(stderr, "usage: %s i-val q-val\n", argv[0]); + return 1; + } + + int i_val = strtol(argv[1], 0, 0); + int q_val = strtol(argv[2], 0, 0); + + static const int NSAMPLES = 16384; + + uint32_t sample[NSAMPLES]; + sample[0] = ((i_val & 0xffff) << 16) | (q_val & 0xffff); + for (int i = 1; i < NSAMPLES; i++) + sample[i] = sample[0]; + + while(1){ + write(1, sample, sizeof(sample)); + } +} diff --git a/usrp2/host/apps/gen_sine.py b/usrp2/host/apps/gen_sine.py new file mode 100755 index 000000000..6a44dd110 --- /dev/null +++ b/usrp2/host/apps/gen_sine.py @@ -0,0 +1,63 @@ +#!/usr/bin/env python +# +# Copyright 2007 Free Software Foundation, Inc. +# +# This program 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 of the License, or +# (at your option) any later version. +# +# This program 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 this program. If not, see <http://www.gnu.org/licenses/>. +# + +from gnuradio import gr, eng_notation +from gnuradio.eng_option import eng_option +from optparse import OptionParser +import sys + +master_clock = 100e6 + +class my_top_block(gr.top_block): + + def __init__(self): + gr.top_block.__init__(self) + + parser = OptionParser(option_class=eng_option) + parser.add_option("-f", "--freq", type="eng_float", default=1e6, + help="set waveform frequency to FREQ [default=%default]") + parser.add_option ("-a", "--amplitude", type="eng_float", default=16e3, + help="set waveform amplitude to AMPLITUDE [default=%default]", metavar="AMPL") + + parser.add_option("-i", "--interp", type="int", default=32, + help="assume fgpa interpolation rate is INTERP [default=%default]") + + (options, args) = parser.parse_args () + if len(args) != 0: + parser.print_help() + raise SystemExit, 1 + + + src0 = gr.sig_source_c(master_clock/options.interp, + gr.GR_SIN_WAVE, + options.freq, + options.amplitude) + + + c2s = gr.complex_to_interleaved_short() + + stdout_sink = gr.file_descriptor_sink(gr.sizeof_short, 1) + + self.connect(src0, c2s, stdout_sink) + + +if __name__ == '__main__': + try: + my_top_block().run() + except KeyboardInterrupt: + pass diff --git a/usrp2/host/apps/rx_samples.cc b/usrp2/host/apps/rx_samples.cc new file mode 100644 index 000000000..c1c3b5917 --- /dev/null +++ b/usrp2/host/apps/rx_samples.cc @@ -0,0 +1,382 @@ +/* -*- c++ -*- */ +/* + * Copyright 2007 Free Software Foundation, Inc. + * + * This program 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 of the License, or + * (at your option) any later version. + * + * This program 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 this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif +#include "usrp2_basic.h" +#include <iostream> +#include <complex> +#include <getopt.h> +#include <string.h> +#include "strtod_si.h" +#include <signal.h> +#include <stdexcept> +#include "gri_if_stats.h" +#include <gr_realtime.h> + + +typedef std::complex<float> fcomplex; + +static volatile bool signaled = false; + +static void +sig_handler(int sig) +{ + signaled = true; +} + +static void +install_sig_handler(int signum, + void (*new_handler)(int)) +{ + struct sigaction new_action; + memset (&new_action, 0, sizeof (new_action)); + + new_action.sa_handler = new_handler; + sigemptyset (&new_action.sa_mask); + new_action.sa_flags = 0; + + if (sigaction (signum, &new_action, 0) < 0){ + perror ("sigaction (install new)"); + throw std::runtime_error ("sigaction"); + } +} + + +/* + * Vectorize me! + */ +void +convert_samples_to_complex(size_t nsamples, + uint32_t *i_samples, + fcomplex *c_samples) +{ + uint32_t *p = i_samples; + for (size_t i = 0; i < nsamples; i++){ + int16_t si = ((int16_t) (p[i] >> 16)); + int16_t sq = ((int16_t) (p[i] & 0xffff)); + c_samples[i] = fcomplex((float) si, (float) sq); + } +} + + +static void +usage(const char *progname) +{ + const char *p = strrchr(progname, '/'); // drop leading directory path + if (p) + p++; + + if (strncmp(p, "lt-", 3) == 0) // drop lt- libtool prefix + p += 3; + + fprintf(stderr, "Usage: %s [options]\n\n", p); + fprintf(stderr, "Options:\n"); + fprintf(stderr, " -h show this message and exit\n"); + fprintf(stderr, " -e ETH_INTERFACE specify ethernet interface [default=eth0]\n"); + fprintf(stderr, " -m MAC_ADDR mac address of USRP2 HH:HH [default=first one found]\n"); + fprintf(stderr, " -o OUTPUT_FILE set output filename [default=NONE]\n"); + fprintf(stderr, " -f FREQ set frequency to FREQ [default=0]\n"); + fprintf(stderr, " -d DECIM set decimation rate to DECIM [default=32]\n"); + fprintf(stderr, " -N NSAMPLES total number of samples to receive [default=2.5e6]\n"); + fprintf(stderr, " -F SAMPLES_PER_FRAME number of samples in each frame [default=371]\n"); + fprintf(stderr, " -S SCALE fpga scaling factor for I & Q [default=1024]\n"); + fprintf(stderr, " -M DONT_LOCK|LOCK_TO_SMA|LOCK_TO_MIMO specify MIMO clock source\n"); + fprintf(stderr, " -P provide clock to MIMO connector\n"); +} + +struct pkt_info { + int d_nsamples; + int d_timestamp; + unsigned int d_seqno; + + pkt_info(int nsamples, int timestamp, int seqno) + : d_nsamples(nsamples), + d_timestamp(timestamp), + d_seqno(seqno) {} +}; + +int +main(int argc, char **argv) +{ + + // options and their defaults + const char *interface = "eth0"; + const char *mac_addr_str = 0; + const char *output_filename = 0; + double freq = 0; + int32_t decim = 32; + int32_t nsamples = static_cast<int32_t>(2.5e6); + int32_t samples_per_frame = 371; + int32_t scale = 1024; + int mimo_config = MC_WE_DONT_LOCK; + bool provide_clock = false; + + int ch; + double tmp; + u2_mac_addr_t mac_addr; + + setvbuf(stdout, 0, _IOFBF, 64 * 1024); // make stdout fully buffered + + while ((ch = getopt(argc, argv, "he:m:o:f:d:N:F:S:M:P")) != EOF){ + switch (ch){ + + case 'e': + interface = optarg; + break; + + case 'm': + mac_addr_str = optarg; + if (!usrp2_basic::parse_mac_addr(optarg, &mac_addr)){ + std::cerr << "invalid mac addr: " << optarg << std::endl; + usage(argv[0]); + exit(1); + } + break; + + case 'o': + output_filename = optarg; + break; + + case 'f': + if (!strtod_si(optarg, &freq)){ + std::cerr << "invalid number: " << optarg << std::endl; + usage(argv[0]); + exit(1); + } + break; + + case 'N': + if (!strtod_si(optarg, &tmp)){ + std::cerr << "invalid number: " << optarg << std::endl; + usage(argv[0]); + exit(1); + } + nsamples = static_cast<int32_t>(tmp); + break; + + case 'F': + samples_per_frame = strtol(optarg, 0, 0); + break; + + case 'd': + decim = strtol(optarg, 0, 0); + break; + + case 'S': + if (!strtod_si(optarg, &tmp)){ + std::cerr << "invalid number: " << optarg << std::endl; + usage(argv[0]); + exit(1); + } + scale = static_cast<int32_t>(tmp); + break; + + case 'M': + if (strcmp(optarg, "DONT_LOCK") == 0) + mimo_config = MC_WE_DONT_LOCK; + else if (strcmp(optarg, "LOCK_TO_SMA") == 0) + mimo_config = MC_WE_LOCK_TO_SMA; + else if (strcmp(optarg, "LOCK_TO_MIMO") == 0) + mimo_config = MC_WE_LOCK_TO_MIMO; + else { + usage(argv[0]); + exit(1); + } + break; + + case 'P': + provide_clock = true; + break; + + case 'h': + default: + usage(argv[0]); + exit(1); + } + } + + if (argc - optind != 0){ + usage(argv[0]); + exit(1); + } + + FILE *of = 0; + if (output_filename) + of = fopen(output_filename, "wb"); + + usrp2_basic *u2 = new usrp2_basic(); + + if (!u2->open(interface)){ + std::cerr << "couldn't open " << interface << std::endl; + return 0; + } + + + install_sig_handler(SIGINT, sig_handler); + if (1){ + install_sig_handler(SIGALRM, sig_handler); + alarm(5); + } + + + std::vector<op_id_reply_t> r = u2->find_usrps(); + + for (size_t i = 0; i < r.size(); i++){ + std::cout << r[i] << std::endl; + } + + if (r.size() == 0){ + std::cerr << "No USRP2 found.\n"; + return 1; + } + + u2_mac_addr_t which = r[0].addr; // pick the first one + + + gr_rt_status_t rt = gr_enable_realtime_scheduling(); + if (rt != RT_OK) + std::cerr << "failed to enable realtime scheduling\n"; + + if (provide_clock) + mimo_config |= MC_PROVIDE_CLK_TO_MIMO; + + u2->config_mimo(which, mimo_confg); + + + gri_if_stats start, stop; + gri_get_if_stats(interface, &start); + + if (!u2->start_rx(which, freq, decim, nsamples, samples_per_frame, scale, scale)){ + std::cerr << "start_rx failed\n"; + return 1; + } + + + std::vector<pkt_info> history; + history.reserve(64*1024); // preallocate 64K entries + + + long total_samples_recvd = 0; + + while (!signaled && total_samples_recvd < nsamples){ + u2_eth_samples_t pkt; + // fcomplex c_samples[U2_MAX_SAMPLES]; + + // read samples + int n = u2->read_samples(which, &pkt); + if (n <= 0) + break; + + total_samples_recvd += n; + + history.push_back(pkt_info(n, u2p_timestamp(&pkt.hdrs.fixed), pkt.hdrs.thdr.seqno)); + + // convert_samples_to_complex(n, pkt.samples, c_samples); + // size_t r = fwrite(c_samples, sizeof(fcomplex), n, of); + + if (of){ + fwrite(pkt.samples, sizeof(uint32_t), n, of); + fflush(of); + } + } + + + gri_get_if_stats(interface, &stop); + + if (!u2->stop_rx(which)){ + std::cerr << "stop_rx failed\n"; + return 1; + } + + + long expected_rx_packets = + (nsamples + samples_per_frame - 1)/samples_per_frame; + + long expected_rx_bytes = + expected_rx_packets * sizeof(u2_eth_packet_t) + nsamples * 4; + + + long total_pkts_recvd = 0; + total_samples_recvd = 0; + + int nbad_seqno = 0; + + for (unsigned i = 0; i < history.size(); i++){ + total_pkts_recvd++; + total_samples_recvd += history[i].d_nsamples; + + bool bad_seqno = history[i].d_seqno != (i & 0xff); + if (bad_seqno) + nbad_seqno++; + + printf("%3d %8d %8ld %8ld %3d %s\n", + history[i].d_nsamples, + history[i].d_timestamp, + total_pkts_recvd, total_samples_recvd, + history[i].d_seqno, + bad_seqno ? "BAD SEQNO" : "" + ); + } + + if (nbad_seqno == 0) + printf("\nAll sequence numbers are correct\n"); + else + printf("\n%d sequence numbers were INCORRECT\n", nbad_seqno); + + + printf("\nUser space statistics:\n"); + printf(" rx_samples: %8ld", total_samples_recvd); + printf(" expected %8d %s\n", + nsamples, + nsamples - total_samples_recvd == 0 ? "OK" : "NOT OK"); + + printf(" rx_packets: %8ld", total_pkts_recvd); + printf(" expected %8ld %s\n", + expected_rx_packets, + expected_rx_packets - total_pkts_recvd == 0 ? "OK" : "NOT OK"); + + + fflush(stdout); + + printf("\nKernel interface statistics:\n"); + + long long delta; + delta = stop.rx_bytes - start.rx_bytes; + printf(" rx_bytes: %8Ld", delta); + printf(" expected %8ld %s\n", + expected_rx_bytes, + expected_rx_bytes - delta == 0 ? "OK" : "NOT OK"); + + delta = stop.rx_packets - start.rx_packets; + printf(" rx_packets: %8Ld", delta); + printf(" expected %8ld %s\n", + expected_rx_packets, + expected_rx_packets - delta == 0 ? "OK" : "NOT OK"); + + printf(" rx_errs: %8Ld\n", stop.rx_errs - start.rx_errs); + printf(" rx_drop: %8Ld\n", stop.rx_drop - start.rx_drop); + printf(" tx_bytes: %8Ld\n", stop.tx_bytes - start.tx_bytes); + printf(" tx_packets: %8Ld\n", stop.tx_packets - start.tx_packets); + printf(" tx_errs: %8Ld\n", stop.tx_errs - start.tx_errs); + printf(" tx_drop: %8Ld\n", stop.tx_drop - start.tx_drop); + + + return 0; +} diff --git a/usrp2/host/apps/rx_streaming_samples.cc b/usrp2/host/apps/rx_streaming_samples.cc new file mode 100644 index 000000000..70f2c5d73 --- /dev/null +++ b/usrp2/host/apps/rx_streaming_samples.cc @@ -0,0 +1,372 @@ +/* -*- c++ -*- */ +/* + * Copyright 2007,2008 Free Software Foundation, Inc. + * + * This program 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 of the License, or + * (at your option) any later version. + * + * This program 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 this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <usrp2/usrp2.h> +#include <usrp2/strtod_si.h> +#include <usrp2/copiers.h> +#include <usrp2/rx_nop_handler.h> +#include <gruel/realtime.h> +#include <sys/time.h> +#include <iostream> +#include <string.h> +#include <boost/scoped_ptr.hpp> +#include <boost/shared_ptr.hpp> +#include <stdexcept> +#include <signal.h> + +static volatile bool signaled = false; + +static void +sig_handler(int sig) +{ + signaled = true; +} + +static void +install_sig_handler(int signum, + void (*new_handler)(int)) +{ + struct sigaction new_action; + memset (&new_action, 0, sizeof (new_action)); + + new_action.sa_handler = new_handler; + sigemptyset (&new_action.sa_mask); + new_action.sa_flags = 0; + + if (sigaction (signum, &new_action, 0) < 0){ + perror ("sigaction (install new)"); + throw std::runtime_error ("sigaction"); + } +} + +// ------------------------------------------------------------------------ + +// FIXME make this a template + +class complex_16_file_writer : public usrp2::rx_nop_handler +{ + FILE *d_fp; + std::string d_filename; + +public: + + complex_16_file_writer(const std::string &filename, uint64_t max_samples) + : usrp2::rx_nop_handler(max_samples), d_filename(filename) + { + d_fp = fopen(filename.c_str(), "wb"); + if (d_fp == 0){ + perror(filename.c_str()); + throw std::invalid_argument(filename); + } + } + + ~complex_16_file_writer(); + + bool + operator()(const uint32_t *items, size_t nitems, const usrp2::rx_metadata *metadata) + { + bool ok = rx_nop_handler::operator()(items, nitems, metadata); + + size_t host_nitems = nitems; + std::complex<int16_t> host_items[host_nitems]; + + usrp2::copy_u2_complex_16_to_host_complex_16(nitems, items, host_items); + + size_t n = 0; + while (n < host_nitems){ + size_t r = fwrite(&host_items[n], sizeof(host_items[0]), host_nitems - n, d_fp); + n += r; + if (r == 0){ // out of space? + d_err = true; + perror(d_filename.c_str()); + ok = false; + break; + } + } + + return ok; + } +}; + +complex_16_file_writer::~complex_16_file_writer() +{ + fclose(d_fp); +} + +// ------------------------------------------------------------------------ + +class complex_float_file_writer : public usrp2::rx_nop_handler +{ + FILE *d_fp; + std::string d_filename; + +public: + + complex_float_file_writer(const std::string &filename, uint64_t max_samples) + : usrp2::rx_nop_handler(max_samples), d_filename(filename) + { + d_fp = fopen(filename.c_str(), "wb"); + if (d_fp == 0){ + perror(filename.c_str()); + throw std::invalid_argument(filename); + } + } + + ~complex_float_file_writer(); + + bool + operator()(const uint32_t *items, size_t nitems, const usrp2::rx_metadata *metadata) + { + bool ok = rx_nop_handler::operator()(items, nitems, metadata); + + size_t host_nitems = nitems; + std::complex<float> host_items[host_nitems]; + + usrp2::copy_u2_complex_16_to_host_complex_float(nitems, items, host_items); + + size_t n = 0; + while (n < host_nitems){ + size_t r = fwrite(&host_items[n], sizeof(host_items[0]), host_nitems - n, d_fp); + n += r; + if (r == 0){ // out of space? + d_err = true; + perror(d_filename.c_str()); + ok = false; + break; + } + } + + return ok; + } +}; + +complex_float_file_writer::~complex_float_file_writer() +{ + fclose(d_fp); +} + +// ------------------------------------------------------------------------ + +static void +usage(const char *progname) +{ + const char *p = strrchr(progname, '/'); // drop leading directory path + if (p) + p++; + + if (strncmp(p, "lt-", 3) == 0) // drop lt- libtool prefix + p += 3; + + fprintf(stderr, "Usage: %s [options]\n\n", p); + fprintf(stderr, "Options:\n"); + fprintf(stderr, " -h show this message and exit\n"); + fprintf(stderr, " -e ETH_INTERFACE specify ethernet interface [default=eth0]\n"); + fprintf(stderr, " -m MAC_ADDR mac address of USRP2 HH:HH [default=first one found]\n"); + fprintf(stderr, " -f FREQUENCY specify receive center frequency in Hz [default=0.0]\n"); + fprintf(stderr, " -d DECIM specify receive decimation rate [default=5]\n"); + fprintf(stderr, " -g GAIN specify receive daughterboard gain [default=0]\n"); + fprintf(stderr, " -N NSAMPLES specify number of samples to receive [default=infinite]\n"); + fprintf(stderr, " -o OUTPUT_FILENAME specify file to receive samples [default=none]\n"); + fprintf(stderr, " -s write complex<short> [default=complex<float>]\n"); + fprintf(stderr, " -v verbose output\n"); +} + +int +main(int argc, char **argv) +{ + // options and their defaults + const char *interface = "eth0"; + const char *mac_addr_str = ""; + double rx_freq = 0.0; + int rx_decim = 5; + double rx_gain = 0.0; + uint64_t nsamples = 0; + bool output_shorts = false; + char *output_filename = 0; + bool verbose = false; + + int ch; + + while ((ch = getopt(argc, argv, "he:m:f:d:g:N:o:sv")) != EOF){ + double tmp; + switch (ch){ + + case 'e': + interface = optarg; + break; + + case 'm': + mac_addr_str = optarg; + break; + + case 'f': + if (!strtod_si(optarg, &rx_freq)) { + std::cerr << "invalid number: " << optarg << std::endl; + usage(argv[0]); + exit(1); + } + break; + + case 'g': + if (!strtod_si(optarg, &rx_gain)) { + std::cerr << "invalid number: " << optarg << std::endl; + usage(argv[0]); + exit(1); + } + break; + + case 'd': + rx_decim = strtol(optarg, 0, 0); + if (rx_decim < 4 or rx_decim > 512) { + std::cerr << "invalid decimation rate: " << optarg << std::endl; + usage(argv[0]); + exit(1); + } + break; + + case 'N': + if (!strtod_si(optarg, &tmp)) { + std::cerr << "invalid number: " << optarg << std::endl; + usage(argv[0]); + exit(1); + } + nsamples = static_cast<uint64_t>(tmp); + break; + + case 's': + output_shorts = true; + break; + + case 'o': + output_filename = optarg; + break; + + case 'v': + verbose = true; + break; + + case 'h': + default: + usage(argv[0]); + exit(1); + } + } + + + install_sig_handler(SIGINT, sig_handler); + + usrp2::rx_nop_handler::sptr handler; + + if (output_filename){ + if (output_shorts) + handler = usrp2::rx_nop_handler::sptr(new complex_16_file_writer(output_filename, nsamples)); + else + handler = usrp2::rx_nop_handler::sptr(new complex_float_file_writer(output_filename, nsamples)); + } + else + handler = usrp2::rx_nop_handler::sptr(new usrp2::rx_nop_handler(nsamples)); + + gruel::rt_status_t rt = gruel::enable_realtime_scheduling(); + if (rt != gruel::RT_OK) + std::cerr << "Failed to enable realtime scheduling" << std::endl; + + usrp2::usrp2::sptr u2 = usrp2::usrp2::make(interface, mac_addr_str); + + // FIXME in case it was left running... + if (!u2->stop_rx_streaming()){ + fprintf(stderr, "stop_rx_streaming failed\n"); + } + + if (!u2->set_rx_gain(rx_gain)){ + fprintf(stderr, "set_rx_gain(%f) failed\n", rx_gain); + exit(1); + } + + usrp2::tune_result tr; + if (!u2->set_rx_center_freq(rx_freq, &tr)){ + fprintf(stderr, "set_rx_center_freq(%g) failed\n", rx_freq); + exit(1); + } + + if (verbose){ + printf("USRP2 MAC address: %s\n\n", u2->mac_addr().c_str()); + printf("Daughterboard configuration:\n"); + printf(" baseband_freq=%f\n", tr.baseband_freq); + printf(" ddc_freq=%f\n", tr.dxc_freq); + printf(" residual_freq=%f\n", tr.residual_freq); + printf(" inverted=%s\n\n", tr.spectrum_inverted ? "yes" : "no"); + } + + if (!u2->set_rx_decim(rx_decim)) { + fprintf(stderr, "set_rx_decim(%d) failed\n", rx_decim); + exit(1); + } + + if (verbose) + printf("USRP2 using decimation rate of %d\n", rx_decim); + + if (!u2->start_rx_streaming(0)){ + fprintf(stderr, "start_rx_streaming failed\n"); + exit(1); + } + + if (verbose) { + if (nsamples > 0) + printf("Receiving %zd samples\n\n", nsamples); + else + printf("Receiving infinite samples\n\n"); + } + + struct timeval start, end; + gettimeofday(&start, 0); + + while (!signaled && + !handler->has_errored_p() && + !handler->has_finished_p()) { + bool ok = u2->rx_samples(0, handler.get()); + if (!ok){ + fprintf(stderr, "u2->rx_samples failed\n"); + return 1; + } + } + + gettimeofday(&end, 0); + long n_usecs = end.tv_usec-start.tv_usec; + long n_secs = end.tv_sec-start.tv_sec; + double elapsed = (double)n_secs + (double)n_usecs*1e-6; + double mbs = handler->nsamples()*sizeof(uint32_t)/elapsed/1e6; + double pps = handler->nframes()/elapsed; + + u2->stop_rx_streaming(); + + if (verbose){ + printf("\nCopy handler called %li times.\n", handler->nframes()); + printf("Copy handler called with %li bytes.\n\n", handler->nsamples()*sizeof(uint32_t)); + printf("Elapsed time was %5.3f seconds.\n", elapsed); + printf("Packet rate was %1.0f pkts/sec.\n", pps); + printf("Approximate throughput was %5.2f MB/sec.\n", mbs); + printf("Total instances of overruns was %d.\n", u2->rx_overruns()); + printf("Total missing frames was %d.\n", u2->rx_missing()); + } + + return 0; +} diff --git a/usrp2/host/apps/stdin_int32_fft.py b/usrp2/host/apps/stdin_int32_fft.py new file mode 100755 index 000000000..5391863a5 --- /dev/null +++ b/usrp2/host/apps/stdin_int32_fft.py @@ -0,0 +1,201 @@ +#!/usr/bin/env python +# +# Copyright 2004,2005,2007,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. +# + +from gnuradio import gr, gru +from gnuradio import eng_notation +from gnuradio.eng_option import eng_option +from gnuradio.wxgui import stdgui2, fftsink2, waterfallsink2, scopesink2, form, slider +from optparse import OptionParser +import wx +import sys +import numpy + + +class app_top_block(stdgui2.std_top_block): + def __init__(self, frame, panel, vbox, argv): + stdgui2.std_top_block.__init__(self, frame, panel, vbox, argv) + + self.frame = frame + self.panel = panel + + parser = OptionParser(option_class=eng_option) + parser.add_option("-d", "--decim", type="int", default=16, + help="set fgpa decimation rate to DECIM [default=%default]") + parser.add_option("-f", "--freq", type="eng_float", default=None, + help="set frequency to FREQ", metavar="FREQ") + parser.add_option("-W", "--waterfall", action="store_true", default=False, + help="Enable waterfall display") + parser.add_option("-S", "--oscilloscope", action="store_true", default=False, + help="Enable oscilloscope display") + (options, args) = parser.parse_args() + if len(args) != 0: + parser.print_help() + sys.exit(1) + self.options = options + self.options.gain = 1.0 + self.show_debug_info = True + + + input_rate = 100e6 / options.decim + + self.src = gr.file_descriptor_source(gr.sizeof_short, 0, False); + self.s2c = gr.interleaved_short_to_complex() + + if options.waterfall: + self.scope = \ + waterfallsink2.waterfall_sink_c (panel, fft_size=1024, sample_rate=input_rate) + elif options.oscilloscope: + self.scope = scopesink2.scope_sink_c(panel, sample_rate=input_rate) + else: + self.scope = fftsink2.fft_sink_c (panel, fft_size=1024, y_divs=12, sample_rate=input_rate,ref_level=110,fft_rate=20) + + self.connect(self.src, self.s2c, self.scope) + + self._build_gui(vbox) + self._setup_events() + + # set initial values + + if options.freq is None: + # if no freq was specified, use the mid-point + options.freq = 0.0 + + if self.show_debug_info: + self.myform['decim'].set_value(self.options.decim) + + + def _set_status_msg(self, msg): + self.frame.GetStatusBar().SetStatusText(msg, 0) + + def _build_gui(self, vbox): + + def _form_set_freq(kv): + return self.set_freq(kv['freq']) + + vbox.Add(self.scope.win, 10, wx.EXPAND) + + # add control area at the bottom + self.myform = myform = form.form() + hbox = wx.BoxSizer(wx.HORIZONTAL) + hbox.Add((5,0), 0, 0) + myform['freq'] = form.float_field( + parent=self.panel, sizer=hbox, label="Center freq", weight=1, + callback=myform.check_input_and_call(_form_set_freq, self._set_status_msg)) + + hbox.Add((5,0), 0, 0) + vbox.Add(hbox, 0, wx.EXPAND) + + self._build_subpanel(vbox) + + def _build_subpanel(self, vbox_arg): + # build a secondary information panel (sometimes hidden) + + # FIXME figure out how to have this be a subpanel that is always + # created, but has its visibility controlled by foo.Show(True/False) + + def _form_set_decim(kv): + return self.set_decim(kv['decim']) + + if not(self.show_debug_info): + return + + panel = self.panel + vbox = vbox_arg + myform = self.myform + + #panel = wx.Panel(self.panel, -1) + #vbox = wx.BoxSizer(wx.VERTICAL) + + hbox = wx.BoxSizer(wx.HORIZONTAL) + hbox.Add((5,0), 0) + + myform['decim'] = form.int_field( + parent=panel, sizer=hbox, label="Decim", + callback=myform.check_input_and_call(_form_set_decim, self._set_status_msg)) + + hbox.Add((5,0), 0) + vbox.Add(hbox, 0, wx.EXPAND) + + + def set_freq(self, target_freq): + """ + Set the center frequency we're interested in. + + @param target_freq: frequency in Hz + @rypte: bool + + Tuning is a two step process. First we ask the front-end to + tune as close to the desired frequency as it can. Then we use + the result of that operation and our target_frequency to + determine the value for the digital down converter. + """ + + if True: + self.myform['freq'].set_value(target_freq) # update displayed value + if not self.options.waterfall and not self.options.oscilloscope: + self.scope.win.set_baseband_freq(target_freq) + return True + + return False + + def set_gain(self, gain): + self.myform['gain'].set_value(gain) # update displayed value + + def set_decim(self, decim): + input_rate = 100e6 / self.options.decim + self.scope.set_sample_rate(input_rate) + if self.show_debug_info: # update displayed values + self.myform['decim'].set_value(self.u.decim_rate()) + return ok + + def _setup_events(self): + if not self.options.waterfall and not self.options.oscilloscope: + self.scope.win.Bind(wx.EVT_LEFT_DCLICK, self.evt_left_dclick) + + def evt_left_dclick(self, event): + (ux, uy) = self.scope.win.GetXY(event) + if event.CmdDown(): + # Re-center on maximum power + points = self.scope.win._points + if self.scope.win.peak_hold: + if self.scope.win.peak_vals is not None: + ind = numpy.argmax(self.scope.win.peak_vals) + else: + ind = int(points.shape()[0]/2) + else: + ind = numpy.argmax(points[:,1]) + (freq, pwr) = points[ind] + target_freq = freq/self.scope.win._scale_factor + print ind, freq, pwr + self.set_freq(target_freq) + else: + # Re-center on clicked frequency + target_freq = ux/self.scope.win._scale_factor + self.set_freq(target_freq) + + +def main (): + app = stdgui2.stdapp(app_top_block, "USRP FFT", nstatus=1) + app.MainLoop() + +if __name__ == '__main__': + main () diff --git a/usrp2/host/apps/streaming_fft.py b/usrp2/host/apps/streaming_fft.py new file mode 100755 index 000000000..cd3606c98 --- /dev/null +++ b/usrp2/host/apps/streaming_fft.py @@ -0,0 +1,76 @@ +#!/usr/bin/env python +# +# Copyright 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 this program; if not, write to the Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +# + +import os +import os.path +import sys +from gnuradio.eng_option import eng_option +from optparse import OptionParser + +def main(): + parser = OptionParser(option_class=eng_option) + parser.add_option("-d", "--decim", type="int", default=16, + help="set fgpa decimation rate to DECIM [default=%default]") + parser.add_option("-f", "--freq", type="eng_float", default=0.0, + help="set frequency to FREQ", metavar="FREQ") + parser.add_option("-g", "--gain", type="string", default=None, + help="set gain to GAIN [default=%default]") + parser.add_option("-W", "--waterfall", action="store_true", default=False, + help="Enable waterfall display") + parser.add_option("-S", "--oscilloscope", action="store_true", default=False, + help="Enable oscilloscope display") + parser.add_option("-F", "--samples-per-frame", type="int", default=250, + help="[default=%default]") + parser.add_option("-e", "--eth", default="eth0", + help="specify ethernet interface [default=%default]") + + (options, args) = parser.parse_args() + if len(args) != 0: + parser.print_help() + sys.exit(1) + + + path = os.path.dirname(sys.argv[0]) + if path == '': + path = '.' + + + display_type = '' + if options.waterfall: + display_type = '-W' + if options.oscilloscope: + display_type = '-S' + + gain_clause = '' + if options.gain: + gain_clause = '-g ' + options.gain + + # FIXME: restore -F + cmd = "sudo %s/rx_streaming_samples -s -e %s -f %g -d %d %s -o /proc/self/fd/1 | %s/stdin_int32_fft.py %s -f %g -d %d" % ( + path, options.eth, options.freq, options.decim, gain_clause, + path, display_type, options.freq, options.decim) + + print cmd + os.system(cmd) + + +if __name__ == '__main__': + main() diff --git a/usrp2/host/apps/test.sh b/usrp2/host/apps/test.sh new file mode 100755 index 000000000..c533c3339 --- /dev/null +++ b/usrp2/host/apps/test.sh @@ -0,0 +1,4 @@ +#!/bin/sh + +sudo ./rx_streaming_samples -d 4 -v -N 100M + diff --git a/usrp2/host/apps/tx_samples.cc b/usrp2/host/apps/tx_samples.cc new file mode 100644 index 000000000..b4a3c8ed6 --- /dev/null +++ b/usrp2/host/apps/tx_samples.cc @@ -0,0 +1,263 @@ +/* -*- c++ -*- */ +/* + * Copyright 2007,2008 Free Software Foundation, Inc. + * + * This program 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 of the License, or + * (at your option) any later version. + * + * This program 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 this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif +#include <usrp2/usrp2.h> +#include <usrp2/strtod_si.h> +#include <iostream> +#include <complex> +#include <getopt.h> +#include <gruel/realtime.h> +#include <signal.h> +#include <string.h> +#include <stdexcept> + + +typedef std::complex<float> fcomplex; + +static volatile bool signaled = false; + +static void +sig_handler(int sig) +{ + signaled = true; +} + +static void +install_sig_handler(int signum, + void (*new_handler)(int)) +{ + struct sigaction new_action; + memset (&new_action, 0, sizeof (new_action)); + + new_action.sa_handler = new_handler; + sigemptyset (&new_action.sa_mask); + new_action.sa_flags = 0; + + if (sigaction (signum, &new_action, 0) < 0){ + perror ("sigaction (install new)"); + throw std::runtime_error ("sigaction"); + } +} + + +static const char * +prettify_progname(const char *progname) // that's probably almost a word ;) +{ + const char *p = strrchr(progname, '/'); // drop leading directory path + if (p) + p++; + + if (strncmp(p, "lt-", 3) == 0) // drop lt- libtool prefix + p += 3; + + return p; +} + +static void +usage(const char *progname) +{ + fprintf(stderr, "Usage: %s [options]\n\n", prettify_progname(progname)); + fprintf(stderr, "Options:\n"); + fprintf(stderr, " -h show this message and exit\n"); + fprintf(stderr, " -e ETH_INTERFACE specify ethernet interface [default=eth0]\n"); + fprintf(stderr, " -m MAC_ADDR mac address of USRP2 HH:HH [default=first one found]\n"); + fprintf(stderr, " -I INPUT_FILE set input filename [default=stdin]\n"); + fprintf(stderr, " -r repeat. When EOF of input file is reached, seek to beginning\n"); + fprintf(stderr, " -f FREQ set frequency to FREQ [default=0]\n"); + fprintf(stderr, " -i INTERP set interpolation rate to INTERP [default=32]\n"); + fprintf(stderr, " -g gain set tx gain\n"); + fprintf(stderr, " -S SCALE fpga scaling factor for I & Q [default=256]\n"); +} + +#define GAIN_NOT_SET (-1000) +#define MAX_SAMPLES (371) + +int +main(int argc, char **argv) +{ + const char *interface = "eth0"; + const char *input_filename = 0; + bool repeat = false; + const char *mac_addr_str = ""; + double freq = 0; + int32_t interp = 32; + int32_t samples_per_frame = MAX_SAMPLES; + int32_t scale = 3000; + double gain = GAIN_NOT_SET; + + int ch; + double tmp; + + + while ((ch = getopt(argc, argv, "he:m:I:rf:i:S:F:g:")) != EOF){ + switch (ch){ + + case 'e': + interface = optarg; + break; + + case 'm': + mac_addr_str = optarg; +#if 0 + if (!usrp2_basic::parse_mac_addr(optarg, &mac_addr)){ + std::cerr << "invalid mac addr: " << optarg << std::endl; + usage(argv[0]); + return 1; + } +#endif + break; + + case 'I': + input_filename = optarg; + break; + + case 'r': + repeat = true; + break; + + case 'f': + if (!strtod_si(optarg, &freq)){ + std::cerr << "invalid number: " << optarg << std::endl; + usage(argv[0]); + return 1; + } + break; + + case 'F': + samples_per_frame = strtol(optarg, 0, 0); + break; + + case 'i': + interp = strtol(optarg, 0, 0); + break; + + case 'S': + if (!strtod_si(optarg, &tmp)){ + std::cerr << "invalid number: " << optarg << std::endl; + usage(argv[0]); + return 1; + } + scale = static_cast<int32_t>(tmp); + break; + + case 'h': + default: + usage(argv[0]); + return 1; + } + } + + + if (argc - optind != 0){ + usage(argv[0]); + return 1; + } + + if (samples_per_frame < 9 || samples_per_frame > MAX_SAMPLES){ + std::cerr << prettify_progname(argv[0]) + << ": samples_per_frame is out of range. " + << "Must be in [9, " << MAX_SAMPLES << "].\n"; + usage(argv[0]); + return 1; + } + + + FILE *fp = 0; + if (input_filename == 0) + fp = stdin; + else { + fp = fopen(input_filename, "rb"); + if (fp == 0){ + perror(input_filename); + return 1; + } + } + + install_sig_handler(SIGINT, sig_handler); + + + gruel::rt_status_t rt = gruel::enable_realtime_scheduling(); + if (rt != gruel::RT_OK) + std::cerr << "Failed to enable realtime scheduling" << std::endl; + + + usrp2::usrp2::sptr u2 = usrp2::usrp2::make(interface, mac_addr_str); + + if (gain != GAIN_NOT_SET){ + if (!u2->set_tx_gain(gain)){ + std::cerr << "set_tx_gain failed\n"; + return 1; + } + } + + usrp2::tune_result tr; + if (!u2->set_tx_center_freq(freq, &tr)){ + fprintf(stderr, "set_tx_center_freq(%g) failed\n", freq); + return 1; + } + + printf("Daughterboard configuration:\n"); + printf(" baseband_freq=%f\n", tr.baseband_freq); + printf(" duc_freq=%f\n", tr.dxc_freq); + printf(" residual_freq=%f\n", tr.residual_freq); + printf(" inverted=%s\n\n", tr.spectrum_inverted ? "yes" : "no"); + + if (!u2->set_tx_interp(interp)){ + fprintf(stderr, "set_tx_interp(%d) failed\n", interp); + return 1; + } + + if (!u2->set_tx_scale_iq(scale, scale)){ + std::cerr << "set_tx_scale_iq failed\n"; + return 1; + } + + + usrp2::tx_metadata md; + md.timestamp = -1; + md.start_of_burst = 1; + md.send_now = 1; + + while (!signaled){ + + std::complex<int16_t> samples[MAX_SAMPLES]; + + int r = fread(samples, sizeof(uint32_t), samples_per_frame, fp); + + // fprintf(stderr, "fread -> %d\n", r); + + if (r == 0){ + if (!repeat) + break; + if (fseek(fp, 0, SEEK_SET) == -1) + break; + } + + // FIXME if r < 9, pad to 9 for minimum packet size constraint + + if (!u2->tx_complex_int16(0, samples, r, &md)){ + fprintf(stderr, "tx_complex_int16 failed\n"); + break; + } + } + + return 0; +} diff --git a/usrp2/host/apps/usrp2_burn_mac_addr.cc b/usrp2/host/apps/usrp2_burn_mac_addr.cc new file mode 100644 index 000000000..6ee76c130 --- /dev/null +++ b/usrp2/host/apps/usrp2_burn_mac_addr.cc @@ -0,0 +1,176 @@ +/* -*- c++ -*- */ +/* + * Copyright 2007,2008 Free Software Foundation, Inc. + * + * This program 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 of the License, or + * (at your option) any later version. + * + * This program 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 this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif +#include <usrp2/usrp2.h> +#include <iostream> +#include <getopt.h> +#include <string.h> +#include <time.h> +#include <stdio.h> +#include <signal.h> +#include <stdexcept> + + +static volatile bool signaled = false; + +static void +sig_handler(int sig) +{ + signaled = true; +} + +static void +install_sig_handler(int signum, + void (*new_handler)(int)) +{ + struct sigaction new_action; + memset (&new_action, 0, sizeof (new_action)); + + new_action.sa_handler = new_handler; + sigemptyset (&new_action.sa_mask); + new_action.sa_flags = 0; + + if (sigaction (signum, &new_action, 0) < 0){ + perror ("sigaction (install new)"); + throw std::runtime_error ("sigaction"); + } +} + + +static void +usage(const char *progname) +{ + fprintf(stderr, "usage: %s [-e ethN] [-m old_mac_addr] new_mac_addr\n", + progname); + fprintf(stderr, " old_mac_addr defaults to 00:50:c2:85:3f:ff\n"); + fprintf(stderr, " new_mac_address must be HH:HH or HH:HH:HH:HH:HH:HH\n"); +} + +static bool +check_mac_addr_syntax(const std::string &s) +{ + unsigned char addr[6]; + + addr[0] = 0x00; // Matt's IAB + addr[1] = 0x50; + addr[2] = 0xC2; + addr[3] = 0x85; + addr[4] = 0x30; + addr[5] = 0x00; + + int len = s.size(); + + switch (len){ + + case 5: + return sscanf(s.c_str(), "%hhx:%hhx", &addr[4], &addr[5]) == 2; + + case 17: + return sscanf(s.c_str(), "%hhx:%hhx:%hhx:%hhx:%hhx:%hhx", + &addr[0], &addr[1], &addr[2], + &addr[3], &addr[4], &addr[5]) == 6; + default: + return false; + } + + return true; +} + + +int +main(int argc, char **argv) +{ + int ch; + const char *interface = "eth0"; + const char *old_mac_addr = "00:50:c2:85:3f:ff"; + const char *new_mac_addr = 0; + + while ((ch = getopt(argc, argv, "he:m:")) != EOF){ + switch (ch){ + case 'e': + interface = optarg; + break; + + case 'm': + old_mac_addr = optarg; + break; + + case 'h': + default: + usage(argv[0]); + exit(1); + } + } + + if (argc - optind != 1){ + usage(argv[0]); + exit(1); + } + + new_mac_addr = argv[optind]; + + if (!check_mac_addr_syntax(old_mac_addr)){ + fprintf(stderr, "invalid mac address: %s\n", old_mac_addr); + exit(1); + } + if (!check_mac_addr_syntax(new_mac_addr)){ + fprintf(stderr, "invalid mac address: %s\n", new_mac_addr); + exit(1); + } + + install_sig_handler(SIGINT, sig_handler); + + usrp2::usrp2::sptr u2; + + try { + u2 = usrp2::usrp2::make(interface, old_mac_addr); + } + catch (std::exception const &e){ + std::cerr << e.what() << std::endl; + return 1; + } + + if (!u2->burn_mac_addr(new_mac_addr)){ + std::cerr << "Failed to burn mac address: " + << new_mac_addr << std::endl; + return 1; + } + + u2.reset(); // close + + // wait 250 ms + struct timespec ts; + ts.tv_sec = 0; + ts.tv_nsec = 250000000; + nanosleep(&ts, 0); + + try { + u2 = usrp2::usrp2::make(interface, new_mac_addr); + } + catch (std::exception const &e){ + std::cerr << "Failed to connect to USRP2 using new addr: " + << new_mac_addr << std::endl; + std::cerr << e.what() << std::endl; + return 1; + } + + return 0; +} |