diff options
Diffstat (limited to 'vrt')
-rw-r--r-- | vrt/Makefile.am | 29 | ||||
-rw-r--r-- | vrt/apps/Makefile.am | 36 | ||||
-rw-r--r-- | vrt/apps/simple_rx_samples.cc | 394 | ||||
-rw-r--r-- | vrt/include/Makefile.am | 23 | ||||
-rw-r--r-- | vrt/include/vrt/Makefile.am | 30 | ||||
-rw-r--r-- | vrt/include/vrt/bits.h | 72 | ||||
-rw-r--r-- | vrt/include/vrt/copiers.h | 49 | ||||
-rw-r--r-- | vrt/include/vrt/expanded_header.h | 99 | ||||
-rw-r--r-- | vrt/include/vrt/quadradio.h | 129 | ||||
-rw-r--r-- | vrt/include/vrt/rx.h | 93 | ||||
-rw-r--r-- | vrt/include/vrt/rx_packet_handler.h | 62 | ||||
-rw-r--r-- | vrt/lib/Makefile.am | 50 | ||||
-rw-r--r-- | vrt/lib/copiers.cc | 69 | ||||
-rw-r--r-- | vrt/lib/data_handler.cc | 32 | ||||
-rw-r--r-- | vrt/lib/data_handler.h | 53 | ||||
-rw-r--r-- | vrt/lib/expanded_header.cc | 119 | ||||
-rw-r--r-- | vrt/lib/expanded_header_cw_tables.h | 14 | ||||
-rw-r--r-- | vrt/lib/expanded_header_switch_body.h | 256 | ||||
-rwxr-xr-x | vrt/lib/gen_cw_tables.py | 69 | ||||
-rwxr-xr-x | vrt/lib/gen_switch_body.py | 80 | ||||
-rw-r--r-- | vrt/lib/quadradio.cc | 408 | ||||
-rw-r--r-- | vrt/lib/rx.cc | 120 | ||||
-rw-r--r-- | vrt/lib/rx_packet_handler.cc | 41 | ||||
-rw-r--r-- | vrt/lib/socket_rx_buffer.cc | 278 | ||||
-rw-r--r-- | vrt/lib/socket_rx_buffer.h | 122 | ||||
-rw-r--r-- | vrt/vrt.pc.in | 11 |
26 files changed, 2738 insertions, 0 deletions
diff --git a/vrt/Makefile.am b/vrt/Makefile.am new file mode 100644 index 000000000..791b8d65c --- /dev/null +++ b/vrt/Makefile.am @@ -0,0 +1,29 @@ +# +# Copyright 2008,2009 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. +# + +include $(top_srcdir)/Makefile.common + +pkgconfigdir = $(libdir)/pkgconfig +pkgconfig_DATA = vrt.pc + +EXTRA_DIST = \ + vrt.pc.in + +SUBDIRS = include lib apps diff --git a/vrt/apps/Makefile.am b/vrt/apps/Makefile.am new file mode 100644 index 000000000..631d9020b --- /dev/null +++ b/vrt/apps/Makefile.am @@ -0,0 +1,36 @@ +# +# Copyright 2009 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 = \ + $(VRT_INCLUDES) \ + $(STD_DEFINES_AND_INCLUDES) \ + $(CPPUNIT_INCLUDES) + +LDADD = \ + $(VRT_LA) \ + $(GRUEL_LA) + + +bin_PROGRAMS = + +noinst_PROGRAMS = \ + simple_rx_samples + +simple_rx_samples_SOURCES = simple_rx_samples.cc + diff --git a/vrt/apps/simple_rx_samples.cc b/vrt/apps/simple_rx_samples.cc new file mode 100644 index 000000000..6b09afda5 --- /dev/null +++ b/vrt/apps/simple_rx_samples.cc @@ -0,0 +1,394 @@ +/* -*- c++ -*- */ +/* + * Copyright 2009 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. + */ + +#include <vrt/quadradio.h> +#include <vrt/rx.h> +#include <vrt/copiers.h> + +#include <errno.h> +#include <iostream> +#include <boost/scoped_ptr.hpp> +#include <boost/shared_ptr.hpp> +#include <stdexcept> +#include <signal.h> +#include <unistd.h> +#include <stdint.h> +#include <stdio.h> +#include <string.h> +#include <gruel/realtime.h> +#include <complex> + +#define MIN_IP_LOCAL_PORT 32768 +#define MAX_IP_LOCAL_PORT 61000 + +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"); + } +} + +// ------------------------------------------------------------------------ + +class rx_nop_handler : public vrt::rx_packet_handler +{ +private: + uint64_t d_max_samples; + uint64_t d_max_quantum; + uint64_t d_nsamples; + uint64_t d_npackets; + int d_last_pkt_cnt; + uint64_t d_nwrong_pkt_cnt; + +protected: + bool d_err; + +public: + + // Shared pointer to an instance of this class + typedef boost::shared_ptr<rx_nop_handler> sptr; + + /*! + * Constructor + * + * \param max_samples Maximum number of samples to copy. Use zero for no maximum. + * \param max_quantum Maximum number of samples required to accept in one call. + * Use 0 to indicate no maximum. + */ + rx_nop_handler(uint64_t max_samples, uint64_t max_quantum=0) + : d_max_samples(max_samples), d_max_quantum(max_quantum), + d_nsamples(0), d_npackets(0), + d_last_pkt_cnt(0xf), d_nwrong_pkt_cnt(0), + d_err(false){} + + + ~rx_nop_handler(); + + bool operator()(const uint32_t *payload, + size_t n32_bit_words, + const vrt::expanded_header *hdr); + + /*! + * \brief Returns number of packets this copier was called with + */ + uint64_t npackets() const { return d_npackets; } + + /*! + * \brief Returns actual number of samples copied + */ + uint64_t nsamples() const { return d_nsamples; } + + /*! + * \brief Returns maximum number of samples that will be copied + */ + uint64_t max_samples() const { return d_max_samples; } + + /*! + * Returns true if an error has occurred. Derived classes must set d_err to true + * when an error occurs in the () operator + */ + bool has_errored_p() const { return d_err; } + + /*! + * \brief Returns true if this instance has reached the maximum number of samples + */ + bool has_finished_p() const + { return d_max_samples == 0 ? false : d_nsamples >= d_max_samples-d_max_quantum; } + + uint64_t nwrong_pkt_cnt() const { return d_nwrong_pkt_cnt; } + + +}; + + +rx_nop_handler::~rx_nop_handler() +{ +} + +bool +rx_nop_handler::operator()(const uint32_t *payload, + size_t n32_bit_words, + const vrt::expanded_header *hdr) +{ + if (d_npackets != 0 && hdr->pkt_cnt() != ((d_last_pkt_cnt + 1) & 0xf)){ + d_nwrong_pkt_cnt++; + fprintf(stderr, "bad cnt (pkt %lld)\n", d_npackets); + } + d_last_pkt_cnt = hdr->pkt_cnt(); + + d_nsamples += n32_bit_words; + d_npackets++; + + return !has_finished_p(); +} + +// ------------------------------------------------------------------------ + +class file_writer_16sc : public rx_nop_handler +{ + FILE *d_fp; + std::string d_filename; + +public: + + file_writer_16sc(const std::string &filename, uint64_t max_samples) + : 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); + } + } + + ~file_writer_16sc(); + + bool + operator()(const uint32_t *items, size_t nitems, const vrt::expanded_header *hdr) + { + bool ok = rx_nop_handler::operator()(items, nitems, hdr); + + size_t host_nitems = nitems; + std::complex<int16_t> host_items[host_nitems]; + + vrt::copy_net_16sc_to_host_16sc(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; + } +}; + +file_writer_16sc::~file_writer_16sc() +{ + fclose(d_fp); +} + +// ------------------------------------------------------------------------ + +class file_writer_32fc : public rx_nop_handler +{ + FILE *d_fp; + std::string d_filename; + +public: + + file_writer_32fc(const std::string &filename, uint64_t max_samples) + : 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); + } + } + + ~file_writer_32fc(); + + bool + operator()(const uint32_t *items, size_t nitems, const vrt::expanded_header *hdr) + { + bool ok = rx_nop_handler::operator()(items, nitems, hdr); + + size_t host_nitems = nitems; + std::complex<float> host_items[host_nitems]; + + vrt::copy_net_16sc_to_host_32fc(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; + } +}; + +file_writer_32fc::~file_writer_32fc() +{ + 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, " -S samples_per_pkt specify # of samples per pkt [default=maximum]\n"); +//fprintf(stderr, " -v verbose output\n"); +} + + +int +main(int argc, char **argv) +{ + const char *quad_radio_ip = "192.168.123.123"; + size_t rx_bufsize = 62.5e6; // sizeof memory mapped network buffer + int samples_per_pkt = 0; // use default + uint64_t nsamples = 0; + char *output_filename = 0; + bool output_shorts = false; + int t; + + int ch; + + while ((ch = getopt(argc, argv, "hN:o:sS:")) != EOF){ + switch (ch){ + case 'N': + nsamples = (uint64_t) strtod(optarg, 0); + break; + + case 'o': + output_filename = optarg; + break; + + case 's': + output_shorts = true; + break; + + case 'S': + errno = 0; + t = strtol(optarg, 0, 0); + if (errno != 0){ + usage(argv[0]); + exit(1); + } + samples_per_pkt = t; + break; + + case 'h': + default: + usage(argv[0]); + exit(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; + + + vrt::quadradio::sptr qr; + try { + qr = vrt::quadradio::sptr(new vrt::quadradio(quad_radio_ip, rx_bufsize)); + } + catch (...){ + std::cerr << "Failed to create vrt::quadradio\n"; + return 1; + } + + + rx_nop_handler::sptr handler; + if (output_filename){ + if (output_shorts) + handler = rx_nop_handler::sptr(new file_writer_16sc(output_filename, nsamples)); + else + handler = rx_nop_handler::sptr(new file_writer_32fc(output_filename, nsamples)); + } + else + handler = rx_nop_handler::sptr(new rx_nop_handler(nsamples)); + + + printf("samples_per_pkt = %d\n", samples_per_pkt); + + if (!qr->start_streaming(samples_per_pkt)){ + fprintf(stderr, "failed to send_rx_command\n"); + return 1; + } + + // start receiving packets + + while(1 + && !signaled + && !handler->has_errored_p() + && !handler->has_finished_p()){ + bool ok = qr->vrt_rx()->rx_packets(handler.get()); + if (!ok){ + fprintf(stderr, "vrt->rx_packets failed\n"); + break; + } + } + + qr->stop_streaming(); + + printf("%llu packets received, %llu bad pkt_cnt field values, %llu samples\n", + handler->npackets(), handler->nwrong_pkt_cnt(), handler->nsamples()); + + //sleep(1); + + return 0; +} diff --git a/vrt/include/Makefile.am b/vrt/include/Makefile.am new file mode 100644 index 000000000..3ce6a8f32 --- /dev/null +++ b/vrt/include/Makefile.am @@ -0,0 +1,23 @@ +# +# Copyright 2009 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. +# + +include $(top_srcdir)/Makefile.common + +SUBDIRS = vrt diff --git a/vrt/include/vrt/Makefile.am b/vrt/include/vrt/Makefile.am new file mode 100644 index 000000000..b710547d9 --- /dev/null +++ b/vrt/include/vrt/Makefile.am @@ -0,0 +1,30 @@ +# +# Copyright 2008,2009 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 + +INCLUDES = + +vrtincludedir = $(includedir)/vrt + +vrtinclude_HEADERS = \ + bits.h \ + copiers.h \ + expanded_header.h \ + quadradio.h \ + rx.h \ + rx_packet_handler.h diff --git a/vrt/include/vrt/bits.h b/vrt/include/vrt/bits.h new file mode 100644 index 000000000..bb4227db4 --- /dev/null +++ b/vrt/include/vrt/bits.h @@ -0,0 +1,72 @@ +/* -*- c++ -*- */ +/* + * Copyright 2009 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. + */ + +#ifndef INCLUDED_VRT_BITS_H +#define INCLUDED_VRT_BITS_H + +#include <stdint.h> + + +/* VRT Header bits */ + +#define VRTH_PT_MASK (0xf << 28) +#define VRTH_PT_IF_DATA_NO_SID (0x0 << 28) // IF-Data, no stream id +#define VRTH_PT_IF_DATA_WITH_SID (0x1 << 28) // IF-Data, w/ stream id +#define VRTH_PT_EXT_DATA_NO_SID (0x2 << 28) +#define VRTH_PT_EXT_DATA_WITH_SID (0x3 << 28) +#define VRTH_PT_IF_CONTEXT (0x4 << 28) +#define VRTH_PT_EXT_CONTEXT (0x5 << 28) + +#define VRTH_HAS_CLASSID (1 << 27) +#define VRTH_HAS_TRAILER (1 << 26) // Data pkts only +#define VRTH_START_OF_BURST (1 << 25) // Data (Tx) pkts only +#define VRTH_END_OF_BURST (1 << 24) // Data (Tx) pkts only +#define VRTH_TSM (1 << 24) // Context pkts only + +#define VRTH_TSI_MASK (0x3 << 22) +#define VRTH_TSI_NONE (0x0 << 22) +#define VRTH_TSI_UTC (0x1 << 22) +#define VRTH_TSI_GPS (0x2 << 22) +#define VRTH_TSI_OTHER (0x3 << 22) + +#define VRTH_TSF_MASK (0x3 << 20) +#define VRTH_TSF_NONE (0x0 << 20) +#define VRTH_TSF_SAMPLE_CNT (0x1 << 20) +#define VRTH_TSF_REAL_TIME_PS (0x2 << 20) +#define VRTH_TSF_FREE_RUNNING (0x3 << 20) + +#define VRTH_PKT_CNT_MASK (0xf << 16) +#define VRTH_PKT_SIZE_MASK 0xffff + + +static inline int +vrth_pkt_cnt(uint32_t h) +{ + return (h & VRTH_PKT_CNT_MASK) >> 16; +} + +static inline int +vrth_pkt_size(uint32_t h) +{ + return h & VRTH_PKT_SIZE_MASK; +} + +#endif /* INCLUDED_VRT_BITS_H */ diff --git a/vrt/include/vrt/copiers.h b/vrt/include/vrt/copiers.h new file mode 100644 index 000000000..990538c42 --- /dev/null +++ b/vrt/include/vrt/copiers.h @@ -0,0 +1,49 @@ +/* -*- c++ -*- */ +/* + * Copyright 2009 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. + */ + +#ifndef INCLUDED_VRT_COPIERS_H +#define INCLUDED_VRT_COPIERS_H + +#include <stdint.h> +#include <stddef.h> +#include <complex> + +namespace vrt { + + /*! + * \brief Copy and convert from net format to host format + */ + void + copy_net_16sc_to_host_16sc(size_t nitems, + const uint32_t *items, + std::complex<int16_t> *host_items); + + + /*! + * \brief Copy and convert from net format to host format mapping [-32768, 32767] -> [1.0, +1.0) + */ + void + copy_net_16sc_to_host_32fc(size_t nitems, + const uint32_t *items, + std::complex<float> *host_items); +}; + +#endif /* INCLUDED_VRT_COPIERS_H */ diff --git a/vrt/include/vrt/expanded_header.h b/vrt/include/vrt/expanded_header.h new file mode 100644 index 000000000..0cfca04a1 --- /dev/null +++ b/vrt/include/vrt/expanded_header.h @@ -0,0 +1,99 @@ +/* -*- c++ -*- */ +/* + * Copyright 2009 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. + */ +#ifndef INCLUDED_VRT_EXPANDED_HEADER_H +#define INCLUDED_VRT_EXPANDED_HEADER_H + +#include <stdint.h> +#include <stddef.h> +#include <vrt/bits.h> + +namespace vrt { + + /*! + * \brief All headers and trailer for VRT IF-Data, Extension-Data, + * IF-Context and Extension-Context packets. + * + * There are fields allocated for each possible header. Their content may + * or may not be valid. Check the header field to confirm their validity. + * All values are in host-endian format. + */ + struct expanded_header { + uint32_t header; // first word of all packets + uint32_t stream_id; // optional stream identifier + uint64_t class_id; // optional class identifier + uint32_t integer_secs; // optional integer seconds timestamp + uint64_t fractional_secs; // optional fractional seconds timestamp + uint32_t trailer; // optional trailer (only possible in data pkts) + + expanded_header() + : header(0) /*, stream_id(0), class_id(0), + integer_secs(0), fractional_secs(0), trailer(0)*/ {} + + + int pkt_type() const { + return (header & VRTH_PT_MASK) >> 28; + } + + int pkt_cnt() const { return vrth_pkt_cnt(header); } + size_t pkt_size() const { return vrth_pkt_size(header); } + + + // packet type predicates + bool if_data_p() const { return s_if_data[pkt_type()]; } + bool ext_data_p() const { return s_ext_data[pkt_type()]; } + bool if_context_p() const { return (header & VRTH_PT_MASK) == VRTH_PT_IF_CONTEXT; } + bool ext_context_p() const { return (header & VRTH_PT_MASK) == VRTH_PT_EXT_CONTEXT; } + + bool data_p() const { return s_data[pkt_type()]; } // if_data_p() || ext_data_p() + bool context_p() const { return s_context[pkt_type()]; } // if_context_p() || ext_context_p() + + // optional info predicates + bool stream_id_p() const { return s_stream_id[pkt_type()]; } + bool class_id_p() const { return (header & VRTH_HAS_CLASSID) != 0; } + bool integer_secs_p() const { return (header & VRTH_TSI_MASK) != 0; } + bool fractional_secs_p() const { return (header & VRTH_TSF_MASK) != 0; } + bool trailer_p() const { return (header & VRTH_HAS_TRAILER) != 0 && data_p(); } + + + // parser + + /*! + * \brief parse packet, fill-in expanded header, start of payload and len of payload + */ + static bool parse(const uint32_t *packet, // in + size_t n32_bit_words_packet, // in + expanded_header *hdr, // out + const uint32_t **payload, // out + size_t *n32_bit_words_payload); // out + + private: + static unsigned char s_if_data[16]; + static unsigned char s_ext_data[16]; + static unsigned char s_data[16]; + static unsigned char s_context[16]; + static unsigned char s_stream_id[16]; + + }; + +}; // vrt + + +#endif /* INCLUDED_VRT_EXPANDED_HEADER_H */ diff --git a/vrt/include/vrt/quadradio.h b/vrt/include/vrt/quadradio.h new file mode 100644 index 000000000..747ca8ef4 --- /dev/null +++ b/vrt/include/vrt/quadradio.h @@ -0,0 +1,129 @@ +/* -*- c++ -*- */ +/* + * Copyright 2009 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. + */ +#ifndef INCLUDED_VRT_QUADRADIO_H +#define INCLUDED_VRT_QUADRADIO_H + +#include <vrt/rx.h> + +#include <arpa/inet.h> +#include <netinet/in.h> +#include <sys/types.h> +#include <sys/socket.h> + +typedef enum{ + VRT_TEST_SIG_NORMAL=0, + VRT_TEST_SIG_ZEROS=1, + VRT_TEST_SIG_ONES=2, + VRT_TEST_SIG_TOGGLE=3, + VRT_TEST_SIG_RAMP=4, + VRT_TEST_SIG_CUSTOM=5, + + } vrt_test_sig_t; + +namespace vrt { + + /* + * We're not committing to this interface. It's just here so we can make progress... + * + * This implements the ad-hoc control for bringup and has-a vrt::rx + */ + class quadradio + { + int d_ctrl_fd; // socket for control + struct in_addr d_ctrl_port_inaddr; // our ip addr + int d_data_fd; // socket for data (owned by d_rx) + int d_data_port; // our data port number + vrt::rx::sptr d_rx; // has-a rx + + int d_band_select; // band select setting + int d_rx_antenna; // antenna type rf/cal + int d_attenuation0; // attenuation setting + int d_attenuation1; // attenuation setting + bool d_10dB_atten; // 10dB attenuation on/of + + static bool + open_sockets(const char *quad_radio_ip, int quad_radio_ctrl_port, + int *ctrl_fd_ptr, struct in_addr *ctrl_port_inaddr, + int *data_fd_ptr, int *data_port_ptr); + + static bool + send_rx_command(int ctrl_fd, bool start, + struct in_addr addr, int data_port, int samples_per_pkt, int siggen_param); + + static bool + send_stop_rx_command(int ctrl_fd); + + static int control_port() { return 790; } + int data_socket_fd() const { return d_data_fd; } + + bool open(const char *ip); + + void update_dboard_pins(void); + + public: + typedef boost::shared_ptr<quadradio> sptr; + + quadradio(const std::string &ip, size_t rx_bufsize = 0); + ~quadradio(); + + vrt::rx::sptr vrt_rx() const { return d_rx; } + + bool start_streaming(int samples_per_pkt = 0); + bool stop_streaming(); + + + /* convenience methods that ultimately write the dboard pins */ + bool set_center_freq(double target_freq); + bool set_band_select(const std::string &band); + //void set_10dB_atten(bool on); + bool set_attenuation0(int attenuation); + bool select_rx_antenna(const std::string &ant); + bool set_attenuation1(int attenuation); + + /* convenience methods that ultimately call set_hsadc_conf */ + void set_adc_gain(bool on); + void set_dc_offset_comp(bool on); + void set_digital_gain(float gain); + void set_test_signal(vrt_test_sig_t type); + + /* primitives */ + bool set_setting_reg(int regno, int value); + bool set_mem32(int addr, int value); // poke a 32-bit value + bool set_lo_freq(double freq); + bool set_cal_freq(double freq); + bool set_beamforming(int32_t gains[8]); + /* + * The first parameter for these is a bitmask which indicates which + * daughterboard or daughterboards to apply the operation to. + * 0x1 -> dboard 0 + * 0x2 -> dboard 1 + * 0x3 -> dboard 0 and 1... + */ + bool set_dboard_pins(int dboard_bitmask, int v); + bool set_hsadc_conf(int dboard_bitmask, int regno, int value); + bool set_lsdac(int dboard_bitmask, int which_dac, int value); + + }; + +}; + + +#endif /* INCLUDED_QUADRADIO_H */ diff --git a/vrt/include/vrt/rx.h b/vrt/include/vrt/rx.h new file mode 100644 index 000000000..ff3ce85fb --- /dev/null +++ b/vrt/include/vrt/rx.h @@ -0,0 +1,93 @@ +/* -*- c++ -*- */ +/* + * Copyright 2009 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. + */ +#ifndef INCLUDED_VRT_RX_H +#define INCLUDED_VRT_RX_H + +#include <boost/shared_ptr.hpp> +#include <boost/utility.hpp> +#include <vrt/rx_packet_handler.h> + +namespace vrt { + + class socket_rx_buffer; + + /*! + * Relatively low-level interface to receive VRT packets over a datagram socket. + * + * (We'll refactor this if/when we use a non-UDP transport.) + * No VRT control issues are addressed here. + */ + class rx : boost::noncopyable + { + int d_socket_fd; + socket_rx_buffer *d_srb; + + public: + /*! + * Shared pointer to this class + */ + typedef boost::shared_ptr<rx> sptr; + + /*! + * \brief Static function to return an instance of rx as a shared pointer. + * + * \param socket_fd file descriptor that data grams will be received from. + * It is assumed that some higher-level control software + * opened the appropriate UDP socket for us. This object + * assumes management of the socket's lifetime. The + * socket will be closed when our destructor fires. + * + * \param rx_bufsize is a hint as to the number of bytes of memory + * to allocate for received ethernet frames (0 -> reasonable default) + */ + static sptr make(int socket_fd, size_t rx_bufsize = 0); + + /*! + * \param socket_fd file descriptor that data grams will be received from. + * It is assumed that some higher-level control software + * opened the appropriate UDP socket for us. This object + * assumes management of the socket's lifetime. The + * socket will be closed when our destructor fires. + * + * \param rx_bufsize is a hint as to the number of bytes of memory + * to allocate for received ethernet frames (0 -> reasonable default) + */ + rx(int socket_fd, size_t rx_bufsize = 0); + ~rx(); + + /*! + * \brief Receive packets from the given socket file descriptor. + * + * \p handler will be invoked for all available packets. + * Unless \p dont_wait is true, this function blocks until at + * least one packet has been processed. + */ + bool rx_packets(rx_packet_handler *handler, bool dont_wait = false); + + /* + * \returns the socket_fd. Useful for select or poll. + */ + int socket_fd() const { return d_socket_fd; } + }; + +} + +#endif /* INCLUDED_VRT_RX_H */ diff --git a/vrt/include/vrt/rx_packet_handler.h b/vrt/include/vrt/rx_packet_handler.h new file mode 100644 index 000000000..ad3407813 --- /dev/null +++ b/vrt/include/vrt/rx_packet_handler.h @@ -0,0 +1,62 @@ +/* -*- c++ -*- */ +/* + * Copyright 2009 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. + */ +#ifndef INCLUDED_VRT_RX_PACKET_HANDLER_H +#define INCLUDED_VRT_RX_PACKET_HANDLER_H + +#include <vrt/expanded_header.h> +#include <stddef.h> + +namespace vrt { + + /*! + * \brief Abstract function object called to handle received VRT packets. + * + * An object derived from this class is passed to vrt_rx_udp::rx_packets + * to process the received packets. + */ + class rx_packet_handler { + public: + virtual ~rx_packet_handler(); + + /*! + * \param payload points to the first 32-bit word of the payload field. + * \param n32_bit_words is the number of 32-bit words in the payload field. + * \param hdr is the expanded version of the mandatory and optional header fields (& trailer). + * + * \p payload points to the raw payload section of the packet received off + * the wire. The data is network-endian (aka big-endian) 32-bit integers. + * + * This is the general purpose, low level interface and relies on other + * functions to handle all required endian-swapping and format conversion + * of the payload. \sa FIXME. + * + * \returns true if the object wants to be called again with new data; + * false if no additional data is wanted. + */ + virtual bool operator()(const uint32_t *payload, + size_t n32_bit_words, + const expanded_header *hdr); + }; + +}; // vrt + + +#endif /* INCLUDED_VRT_RX_PACKET_HANDLER_H */ diff --git a/vrt/lib/Makefile.am b/vrt/lib/Makefile.am new file mode 100644 index 000000000..9832c0cd5 --- /dev/null +++ b/vrt/lib/Makefile.am @@ -0,0 +1,50 @@ +# +# Copyright 2007,2008,2009 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 = \ + $(VRT_INCLUDES) \ + $(BOOST_CPPFLAGS) \ + $(CPPUNIT_INCLUDES) + +bin_PROGRAMS = + +lib_LTLIBRARIES = \ + libvrt.la + +libvrt_la_SOURCES = \ + copiers.cc \ + data_handler.cc \ + expanded_header.cc \ + quadradio.cc \ + rx.cc \ + rx_packet_handler.cc \ + socket_rx_buffer.cc + +libvrt_la_LIBADD = + +# Private headers not needed for above the API development +noinst_HEADERS = \ + data_handler.h \ + expanded_header_cw_tables.h \ + expanded_header_switch_body.h \ + socket_rx_buffer.h + +EXTRA_DIST = \ + gen_cw_tables.py \ + gen_switch_body.py diff --git a/vrt/lib/copiers.cc b/vrt/lib/copiers.cc new file mode 100644 index 000000000..aa2846af5 --- /dev/null +++ b/vrt/lib/copiers.cc @@ -0,0 +1,69 @@ +/* -*- c++ -*- */ +/* + * Copyright 2009 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. + */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif +#include <vrt/copiers.h> +#include <arpa/inet.h> + +namespace vrt { + + void + copy_net_16sc_to_host_16sc(size_t nitems, + const uint32_t *items, + std::complex<int16_t> *host_items) + { +#ifdef WORDS_BIGENDIAN + + assert(sizeof(items[0]) == sizeof(host_items[0])); + memcpy(host_items, items, nitems * sizeof(items[0])); + +#else + + // FIXME SIMD welcome here + + for (size_t i = 0; i < nitems; i++){ + uint32_t t = ntohl(items[i]); + //printf("%9d\n", items[i]); + host_items[i] = std::complex<int16_t>((t >> 16), t & 0xffff); + } + +#endif + } + + void + copy_net_16sc_to_host_32fc(size_t nitems, + const uint32_t *items, + std::complex<float> *host_items) + { + // FIXME SIMD welcome here + + for (size_t i = 0; i < nitems; i++){ + uint32_t t = ntohl(items[i]); + int16_t re = (t >> 16) & 0xffff; + int16_t im = (t & 0xffff); + host_items[i] = std::complex<float>(re * 1.0/32768, im * 1.0/32768); + } + } + +}; + diff --git a/vrt/lib/data_handler.cc b/vrt/lib/data_handler.cc new file mode 100644 index 000000000..7d1f73a9a --- /dev/null +++ b/vrt/lib/data_handler.cc @@ -0,0 +1,32 @@ +/* -*- c++ -*- */ +/* + * Copyright 2008,2009 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. + */ + +#include "data_handler.h" + +namespace vrt { + + data_handler::~data_handler() + { + // default nop destructor + } + +} + diff --git a/vrt/lib/data_handler.h b/vrt/lib/data_handler.h new file mode 100644 index 000000000..c041e48be --- /dev/null +++ b/vrt/lib/data_handler.h @@ -0,0 +1,53 @@ +/* -*- c++ -*- */ +/* + * Copyright 2008,2009 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. + */ +#ifndef INCLUDED_VRT_DATA_HANDLER_H +#define INCLUDED_VRT_DATA_HANDLER_H + +#include <stdint.h> +#include <stddef.h> + +namespace vrt { + + /*! + * \brief Abstract function object called to handle received data blocks. + */ + class data_handler + { + public: + + enum result_bits { + DONE = 0x0002, //< do not call this object again + }; + + typedef int result; //< bitmask of result_bits + + /*! + * \param base points to the beginning of the data + * \param len is the length in bytes of the data + * \returns bitmask composed of DONE + */ + virtual result operator()(const void *base, size_t len) = 0; + virtual ~data_handler(); + }; + +} // namespace vrt + +#endif /* INCLUDED_VRT_DATA_HANDLER_H */ diff --git a/vrt/lib/expanded_header.cc b/vrt/lib/expanded_header.cc new file mode 100644 index 000000000..d5c4ea888 --- /dev/null +++ b/vrt/lib/expanded_header.cc @@ -0,0 +1,119 @@ +/* -*- c++ -*- */ +/* + * Copyright 2009 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. + */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif +#include <vrt/expanded_header.h> +#include <arpa/inet.h> // needs autoconf'ing +//#include <stdio.h> + +namespace vrt { + + // lookup tables indexed by packet type + unsigned char expanded_header::s_if_data[16] = { + 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 + }; + + unsigned char expanded_header::s_ext_data[16] = { + 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 + }; + + unsigned char expanded_header::s_data[16] = { + 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 + }; + + unsigned char expanded_header::s_context[16] = { + 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 + }; + + unsigned char expanded_header::s_stream_id[16] = { + 0, 1, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 + }; + + + // dispatch codeword bits + static const int HAS_STREAM_ID = 1 << 0; + static const int HAS_CLASS_ID = 1 << 1; + static const int HAS_INTEGER_SECS = 1 << 2; + static const int HAS_FRACTIONAL_SECS = 1 << 3; + static const int HAS_TRAILER = 1 << 4; + +#include "expanded_header_cw_tables.h" + + static int + compute_codeword(const expanded_header &h) + { + int cw = 0; + if (h.stream_id_p()) cw |= HAS_STREAM_ID; + if (h.class_id_p()) cw |= HAS_CLASS_ID; + if (h.integer_secs_p()) cw |= HAS_INTEGER_SECS; + if (h.fractional_secs_p()) cw |= HAS_FRACTIONAL_SECS; + if (h.trailer_p()) cw |= HAS_TRAILER; + return cw; + } + + bool + expanded_header::parse(const uint32_t *packet, // in + size_t n32_bit_words_packet, // in + expanded_header *h, // out + const uint32_t **payload, // out + size_t *n32_bit_words_payload) // out + { + size_t len = n32_bit_words_packet; + const uint32_t *p = packet; + + *payload = 0; + *n32_bit_words_payload = 0; + + // printf("parse: n32_bit_words_packet = %zd\n", n32_bit_words_packet); + + if (len < 1){ // must have at least the header word + h->header = 0; + return false; + } + + h->header = ntohl(p[0]); + + if (h->pkt_size() > len) + return false; // VRT header says packet is bigger than what we've got + + len = h->pkt_size(); // valid length of packet + + int cw = compute_codeword(*h); + if (cw_header_len(cw) + cw_trailer_len(cw) > len) + return false; // negative payload len + + *payload = p + cw_header_len(cw); + *n32_bit_words_payload = len - (cw_header_len(cw) + cw_trailer_len(cw)); + + // printf("parse: hdr = 0x%08x, cw = 0x%02x, cw_header_len(cw) = %d, cw_trailer_len(cw) = %d\n", + // h->header, cw, cw_header_len(cw), cw_trailer_len(cw)); + + switch (cw & 0x1f){ +#include "expanded_header_switch_body.h" + } + + return true; + } + + +}; // vrt diff --git a/vrt/lib/expanded_header_cw_tables.h b/vrt/lib/expanded_header_cw_tables.h new file mode 100644 index 000000000..fbb9c6b81 --- /dev/null +++ b/vrt/lib/expanded_header_cw_tables.h @@ -0,0 +1,14 @@ +inline static size_t cw_header_len(int cw){ + static const size_t s_cw_header_len[32] = { + 1, 2, 3, 4, 2, 3, 4, 5, 3, 4, 5, 6, 4, 5, 6, 7, 1, 2, 3, 4, 2, 3, 4, 5, 3, 4, 5, 6, 4, 5, 6, 7, + }; + return s_cw_header_len[cw]; +} + +inline static size_t cw_trailer_len(int cw){ + static const size_t s_cw_trailer_len[32] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + }; + return s_cw_trailer_len[cw]; +} + diff --git a/vrt/lib/expanded_header_switch_body.h b/vrt/lib/expanded_header_switch_body.h new file mode 100644 index 000000000..40e575c2c --- /dev/null +++ b/vrt/lib/expanded_header_switch_body.h @@ -0,0 +1,256 @@ + case 0: + h->stream_id = 0; + h->class_id = 0; + h->integer_secs = 0; + h->fractional_secs = 0; + h->trailer = 0; + break; + + case 1: + h->stream_id = ntohl(p[1]); + h->class_id = 0; + h->integer_secs = 0; + h->fractional_secs = 0; + h->trailer = 0; + break; + + case 2: + h->stream_id = 0; + h->class_id = ((uint64_t)(ntohl(p[1])) << 32) | ntohl(p[2]); + h->integer_secs = 0; + h->fractional_secs = 0; + h->trailer = 0; + break; + + case 3: + h->stream_id = ntohl(p[1]); + h->class_id = ((uint64_t)(ntohl(p[2])) << 32) | ntohl(p[3]); + h->integer_secs = 0; + h->fractional_secs = 0; + h->trailer = 0; + break; + + case 4: + h->stream_id = 0; + h->class_id = 0; + h->integer_secs = ntohl(p[1]); + h->fractional_secs = 0; + h->trailer = 0; + break; + + case 5: + h->stream_id = ntohl(p[1]); + h->class_id = 0; + h->integer_secs = ntohl(p[2]); + h->fractional_secs = 0; + h->trailer = 0; + break; + + case 6: + h->stream_id = 0; + h->class_id = ((uint64_t)(ntohl(p[1])) << 32) | ntohl(p[2]); + h->integer_secs = ntohl(p[3]); + h->fractional_secs = 0; + h->trailer = 0; + break; + + case 7: + h->stream_id = ntohl(p[1]); + h->class_id = ((uint64_t)(ntohl(p[2])) << 32) | ntohl(p[3]); + h->integer_secs = ntohl(p[4]); + h->fractional_secs = 0; + h->trailer = 0; + break; + + case 8: + h->stream_id = 0; + h->class_id = 0; + h->integer_secs = 0; + h->fractional_secs = ((uint64_t)(ntohl(p[1])) << 32) | ntohl(p[2]); + h->trailer = 0; + break; + + case 9: + h->stream_id = ntohl(p[1]); + h->class_id = 0; + h->integer_secs = 0; + h->fractional_secs = ((uint64_t)(ntohl(p[2])) << 32) | ntohl(p[3]); + h->trailer = 0; + break; + + case 10: + h->stream_id = 0; + h->class_id = ((uint64_t)(ntohl(p[1])) << 32) | ntohl(p[2]); + h->integer_secs = 0; + h->fractional_secs = ((uint64_t)(ntohl(p[3])) << 32) | ntohl(p[4]); + h->trailer = 0; + break; + + case 11: + h->stream_id = ntohl(p[1]); + h->class_id = ((uint64_t)(ntohl(p[2])) << 32) | ntohl(p[3]); + h->integer_secs = 0; + h->fractional_secs = ((uint64_t)(ntohl(p[4])) << 32) | ntohl(p[5]); + h->trailer = 0; + break; + + case 12: + h->stream_id = 0; + h->class_id = 0; + h->integer_secs = ntohl(p[1]); + h->fractional_secs = ((uint64_t)(ntohl(p[2])) << 32) | ntohl(p[3]); + h->trailer = 0; + break; + + case 13: + h->stream_id = ntohl(p[1]); + h->class_id = 0; + h->integer_secs = ntohl(p[2]); + h->fractional_secs = ((uint64_t)(ntohl(p[3])) << 32) | ntohl(p[4]); + h->trailer = 0; + break; + + case 14: + h->stream_id = 0; + h->class_id = ((uint64_t)(ntohl(p[1])) << 32) | ntohl(p[2]); + h->integer_secs = ntohl(p[3]); + h->fractional_secs = ((uint64_t)(ntohl(p[4])) << 32) | ntohl(p[5]); + h->trailer = 0; + break; + + case 15: + h->stream_id = ntohl(p[1]); + h->class_id = ((uint64_t)(ntohl(p[2])) << 32) | ntohl(p[3]); + h->integer_secs = ntohl(p[4]); + h->fractional_secs = ((uint64_t)(ntohl(p[5])) << 32) | ntohl(p[6]); + h->trailer = 0; + break; + + case 16: + h->stream_id = 0; + h->class_id = 0; + h->integer_secs = 0; + h->fractional_secs = 0; + h->trailer = ntohl(p[len-1]); + break; + + case 17: + h->stream_id = ntohl(p[1]); + h->class_id = 0; + h->integer_secs = 0; + h->fractional_secs = 0; + h->trailer = ntohl(p[len-1]); + break; + + case 18: + h->stream_id = 0; + h->class_id = ((uint64_t)(ntohl(p[1])) << 32) | ntohl(p[2]); + h->integer_secs = 0; + h->fractional_secs = 0; + h->trailer = ntohl(p[len-1]); + break; + + case 19: + h->stream_id = ntohl(p[1]); + h->class_id = ((uint64_t)(ntohl(p[2])) << 32) | ntohl(p[3]); + h->integer_secs = 0; + h->fractional_secs = 0; + h->trailer = ntohl(p[len-1]); + break; + + case 20: + h->stream_id = 0; + h->class_id = 0; + h->integer_secs = ntohl(p[1]); + h->fractional_secs = 0; + h->trailer = ntohl(p[len-1]); + break; + + case 21: + h->stream_id = ntohl(p[1]); + h->class_id = 0; + h->integer_secs = ntohl(p[2]); + h->fractional_secs = 0; + h->trailer = ntohl(p[len-1]); + break; + + case 22: + h->stream_id = 0; + h->class_id = ((uint64_t)(ntohl(p[1])) << 32) | ntohl(p[2]); + h->integer_secs = ntohl(p[3]); + h->fractional_secs = 0; + h->trailer = ntohl(p[len-1]); + break; + + case 23: + h->stream_id = ntohl(p[1]); + h->class_id = ((uint64_t)(ntohl(p[2])) << 32) | ntohl(p[3]); + h->integer_secs = ntohl(p[4]); + h->fractional_secs = 0; + h->trailer = ntohl(p[len-1]); + break; + + case 24: + h->stream_id = 0; + h->class_id = 0; + h->integer_secs = 0; + h->fractional_secs = ((uint64_t)(ntohl(p[1])) << 32) | ntohl(p[2]); + h->trailer = ntohl(p[len-1]); + break; + + case 25: + h->stream_id = ntohl(p[1]); + h->class_id = 0; + h->integer_secs = 0; + h->fractional_secs = ((uint64_t)(ntohl(p[2])) << 32) | ntohl(p[3]); + h->trailer = ntohl(p[len-1]); + break; + + case 26: + h->stream_id = 0; + h->class_id = ((uint64_t)(ntohl(p[1])) << 32) | ntohl(p[2]); + h->integer_secs = 0; + h->fractional_secs = ((uint64_t)(ntohl(p[3])) << 32) | ntohl(p[4]); + h->trailer = ntohl(p[len-1]); + break; + + case 27: + h->stream_id = ntohl(p[1]); + h->class_id = ((uint64_t)(ntohl(p[2])) << 32) | ntohl(p[3]); + h->integer_secs = 0; + h->fractional_secs = ((uint64_t)(ntohl(p[4])) << 32) | ntohl(p[5]); + h->trailer = ntohl(p[len-1]); + break; + + case 28: + h->stream_id = 0; + h->class_id = 0; + h->integer_secs = ntohl(p[1]); + h->fractional_secs = ((uint64_t)(ntohl(p[2])) << 32) | ntohl(p[3]); + h->trailer = ntohl(p[len-1]); + break; + + case 29: + h->stream_id = ntohl(p[1]); + h->class_id = 0; + h->integer_secs = ntohl(p[2]); + h->fractional_secs = ((uint64_t)(ntohl(p[3])) << 32) | ntohl(p[4]); + h->trailer = ntohl(p[len-1]); + break; + + case 30: + h->stream_id = 0; + h->class_id = ((uint64_t)(ntohl(p[1])) << 32) | ntohl(p[2]); + h->integer_secs = ntohl(p[3]); + h->fractional_secs = ((uint64_t)(ntohl(p[4])) << 32) | ntohl(p[5]); + h->trailer = ntohl(p[len-1]); + break; + + case 31: + h->stream_id = ntohl(p[1]); + h->class_id = ((uint64_t)(ntohl(p[2])) << 32) | ntohl(p[3]); + h->integer_secs = ntohl(p[4]); + h->fractional_secs = ((uint64_t)(ntohl(p[5])) << 32) | ntohl(p[6]); + h->trailer = ntohl(p[len-1]); + break; + diff --git a/vrt/lib/gen_cw_tables.py b/vrt/lib/gen_cw_tables.py new file mode 100755 index 000000000..803a392e8 --- /dev/null +++ b/vrt/lib/gen_cw_tables.py @@ -0,0 +1,69 @@ +#!/usr/bin/env python +# +# Copyright 2009 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 sys + +# dispatch codeword bits +HAS_STREAM_ID = 1 << 0; +HAS_CLASS_ID = 1 << 1; +HAS_INTEGER_SECS = 1 << 2; +HAS_FRACTIONAL_SECS = 1 << 3; +HAS_TRAILER = 1 << 4; + +def main(): + f = sys.stdout + header_len = 32 * [0] + trailer_len = 32 * [0] + + for cw in range(32): + t = 0 + if cw & HAS_TRAILER: t += 1 + trailer_len[cw] = t + + t = 1 + if cw & HAS_STREAM_ID: t += 1 + if cw & HAS_CLASS_ID: t += 2 + if cw & HAS_INTEGER_SECS: t += 1 + if cw & HAS_FRACTIONAL_SECS: t += 2 + header_len[cw] = t + + write_table(f, "cw_header_len", header_len) + write_table(f, "cw_trailer_len", trailer_len) + +def write_table(f, name, table): + f.write("inline static size_t ") + f.write(name) + f.write("(int cw){\n") + + f.write(" static const size_t s_") + f.write(name) + f.write("[32] = {\n ") + for t in table: + f.write("%d, " % (t,)) + f.write("\n };\n") + + f.write(" return s_") + f.write(name) + f.write("[cw];\n}\n\n") + + +if __name__ == '__main__': + main() diff --git a/vrt/lib/gen_switch_body.py b/vrt/lib/gen_switch_body.py new file mode 100755 index 000000000..105fa76a7 --- /dev/null +++ b/vrt/lib/gen_switch_body.py @@ -0,0 +1,80 @@ +#!/usr/bin/env python +# +# Copyright 2009 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 sys + +# dispatch codeword bits +HAS_STREAM_ID = 1 << 0; +HAS_CLASS_ID = 1 << 1; +HAS_INTEGER_SECS = 1 << 2; +HAS_FRACTIONAL_SECS = 1 << 3; +HAS_TRAILER = 1 << 4; + +def do_case(f, cw): + + def do32(name, mask, index): + f.write(" ") + if cw & mask: + f.write("h->%s = ntohl(p[%d]);\n" % (name, index)) + return 1 + else: + f.write("h->%s = 0;\n" % (name,)) + return 0 + + def do64(name, mask, index): + f.write(" ") + if cw & mask: + f.write("h->%s = ((uint64_t)(ntohl(p[%d])) << 32) | ntohl(p[%d]);\n" % (name, index, index+1)) + return 2 + else: + f.write("h->%s = 0;\n" % (name,)) + return 0 + + def dotrailer(name, mask): + f.write(" ") + if cw & mask: + f.write("h->%s = ntohl(p[len-1]);\n" % (name,)) + return 1 + else: + f.write("h->%s = 0;\n" % (name,)) + return 0 + + f.write(" case %d:\n" % (cw,)) + + index = 1 + index += do32("stream_id", HAS_STREAM_ID, index) + index += do64("class_id", HAS_CLASS_ID, index) + index += do32("integer_secs", HAS_INTEGER_SECS, index) + index += do64("fractional_secs", HAS_FRACTIONAL_SECS, index) + dotrailer("trailer", HAS_TRAILER) + + f.write(" break;\n\n") + + +def main(): + f = sys.stdout + + for cw in range(32): + do_case(f, cw) + + +if __name__ == '__main__': + main() diff --git a/vrt/lib/quadradio.cc b/vrt/lib/quadradio.cc new file mode 100644 index 000000000..ab5de89b8 --- /dev/null +++ b/vrt/lib/quadradio.cc @@ -0,0 +1,408 @@ +/* -*- c++ -*- */ +/* + * Copyright 2009 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. + */ +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif +#include <vrt/quadradio.h> +#include <errno.h> +#include <string.h> +#include <stdio.h> +#include <stdexcept> +#include <math.h> + +#define MIN_IP_LOCAL_PORT 32768 +#define MAX_IP_LOCAL_PORT 61000 + +#define ALL_DBOARDS 0xf + +static bool +send_and_check(int fd, void *buf, size_t len) +{ + int r = send(fd, buf, len, 0); + if (r < 0){ + perror("send"); + return false; + } + if ((size_t) r != len){ + fprintf(stderr, "send: short return value. expected %zd, got %d\n", len, r); + return false; + } + return true; +} + + +vrt::quadradio::quadradio(const std::string &ip, size_t rx_bufsize) + : d_ctrl_fd(0), d_data_fd(0), d_data_port(0), + d_band_select(0), d_rx_antenna(0), d_attenuation0(0), d_attenuation1(0)//d_10dB_atten(true) +{ + if (!open(ip.c_str())) + throw std::runtime_error("vrt::quadradio: failed to open " + ip + "\n"); + + d_rx = vrt::rx::make(data_socket_fd(), rx_bufsize); + set_test_signal(VRT_TEST_SIG_NORMAL); +} + +vrt::quadradio::~quadradio() +{ + ::close(d_ctrl_fd); +} + +bool +vrt::quadradio::open(const char *ip) +{ + return open_sockets(ip, control_port(), + &d_ctrl_fd, &d_ctrl_port_inaddr, + &d_data_fd, &d_data_port); +} + +bool +vrt::quadradio::start_streaming(int samples_per_pkt) +{ + return send_rx_command(d_ctrl_fd, true, d_ctrl_port_inaddr, + d_data_port, samples_per_pkt, 0); +} + +bool +vrt::quadradio::stop_streaming() +{ + return send_stop_rx_command(d_ctrl_fd); +} + +bool +vrt::quadradio::set_center_freq(double target_freq){ + if (target_freq < 700e6) return false; + if (target_freq <= 1.0e9) return set_band_select("A"); + if (target_freq <= 1.5e9) return set_band_select("B"); + if (target_freq <= 2.2e9) return set_band_select("C"); + if (target_freq <= 3.0e9) return set_band_select("D"); + return false; +} + +bool +vrt::quadradio::set_band_select(const std::string &band){ + if (band == "A") d_band_select = 3; + else if (band == "B") d_band_select = 2; + else if (band == "C") d_band_select = 1; + else if (band == "D") d_band_select = 0; + else return false; + update_dboard_pins(); + return true; +} + +//void +//vrt::quadradio::set_10dB_atten(bool on){ +// d_10dB_atten = on; +// update_dboard_pins(); +//} + +bool +vrt::quadradio::select_rx_antenna(const std::string &ant){ + if (ant == "rf") d_rx_antenna = 0; + else if (ant == "cal") d_rx_antenna = 1; + else return true; + update_dboard_pins(); + return true; +} + +bool +vrt::quadradio::set_attenuation0(int attenuation){ + if (attenuation < 0 || attenuation > 31) return false; + d_attenuation0 = attenuation; + update_dboard_pins(); + return true; +} + +bool +vrt::quadradio::set_attenuation1(int attenuation){ + if (attenuation < 0 || attenuation > 31) return false; + d_attenuation1 = attenuation; + update_dboard_pins(); + return true; +} + +//bit reversal, length in bits +static int reverse_bits(int input, int len){ + int reversed = 0; + for (int i = 0; i < len; i++){ + reversed += (input & (1<<i))?(1 << (len-i-1)):0; + } + return reversed; +} + +void +vrt::quadradio::update_dboard_pins(void){ + int db_ctrl = \ + ((reverse_bits(d_attenuation0, 5) & 0x1f) << 10) | \ + ((reverse_bits(~d_attenuation1, 5) & 0x1f) << 03) | \ + ((d_band_select & 0x03) << 01) | \ + ((d_rx_antenna & 0x01) << 00); + set_dboard_pins(ALL_DBOARDS, db_ctrl); // FIXME sets them all +} + +void +vrt::quadradio::set_adc_gain(bool on){ + set_hsadc_conf(ALL_DBOARDS, 0x14, on ? 0x90 : 0x80); +} + +void +vrt::quadradio::set_dc_offset_comp(bool on){ + if (on) { + set_hsadc_conf(ALL_DBOARDS, 0x1B, 0x80); + set_hsadc_conf(ALL_DBOARDS, 0x1A, 0x00); //bits 6:4 set time constant + } + else set_hsadc_conf(ALL_DBOARDS, 0x1B, 0x00); +} + +void +vrt::quadradio::set_digital_gain(float gain){ + int gain_q1 = static_cast<int>(round(gain*2.0)); + set_hsadc_conf(ALL_DBOARDS, 0x17, gain_q1); +} + +void +vrt::quadradio::set_test_signal(vrt_test_sig_t type){ + set_hsadc_conf(ALL_DBOARDS, 0x16, type); +} + +bool +vrt::quadradio::open_sockets(const char *quad_radio_ip, int quad_radio_ctrl_port, + int *ctrl_fd_ptr, struct in_addr *ctrl_port_inaddr, + int *data_fd_ptr, int *data_port_ptr) +{ + int ctrl_fd; // socket for control + int data_fd; // socket fd for data + int data_port; // our port number + + // + // create a udp socket and connect it to the quad radio control port + // + + ctrl_fd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); + if (ctrl_fd == -1){ + perror("socket: ctrl_fd"); + return false; + } + + struct sockaddr_in si_other; + memset(&si_other, 0, sizeof(si_other)); + si_other.sin_family = AF_INET; + si_other.sin_port = htons(quad_radio_ctrl_port); + if (inet_pton(AF_INET, quad_radio_ip, &si_other.sin_addr) <= 0){ + perror("inet_pton"); + return false; + } + + if (connect(ctrl_fd, (struct sockaddr *) &si_other, sizeof(si_other)) != 0){ + perror("connect"); + return false; + } + + // get our ip address associated with the interface connected to the control port + + struct sockaddr_in si_me; + memset(&si_me, 0, sizeof(si_me)); + socklen_t sockname_len = sizeof(si_me); + if (getsockname(ctrl_fd, (struct sockaddr *) &si_me, &sockname_len) != 0){ + perror("getsockname"); + } + + *ctrl_port_inaddr = si_me.sin_addr; + + if (1){ + char buf[128]; + const char *s = inet_ntop(si_me.sin_family, &si_me.sin_addr, buf, sizeof(buf)); + if (s == 0){ + perror("inet_ntop"); + return false; + } + // printf("our ip addr associated with ctrl port: %s\n", s); + } + + // + // create a udp socket to use to receive data + // + + data_fd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); + if (data_fd == -1){ + perror("socket: data_fd"); + return false; + } + + // bind it to a local port on the interface that connects to the ctrl port. + // FIXME this assumes that interface connected to the control port and the + // interface connected to the data port are the same. If we're using + // both ethernet ports on the quad radio, this may not be the case. + + data_port = -1; + for (int port = MIN_IP_LOCAL_PORT; port <= MAX_IP_LOCAL_PORT; port++){ + struct sockaddr_in si_me; + memset(&si_me, 0, sizeof(si_me)); + si_me.sin_family = AF_INET; + si_me.sin_port = htons(port); + si_me.sin_addr.s_addr = htonl(INADDR_ANY); + + if (bind(data_fd, (struct sockaddr *) &si_me, sizeof(si_me)) == 0){ // found one! + data_port = port; + break; + } + } + + if (data_port == -1){ + fprintf(stderr, "failed to bind to a local port\n"); + return false; + } + + // printf("our data port = %d\n", data_port); + + *ctrl_fd_ptr = ctrl_fd; + *data_fd_ptr = data_fd; + *data_port_ptr = data_port; + + return true; +} + +// ------------------------------------------------------------------------ + +bool +vrt::quadradio::send_rx_command(int ctrl_fd, bool start, + struct in_addr addr, int data_port, + int samples_per_pkt, int siggen_param) +{ + uint32_t cmd[7]; + cmd[0] = htonl(0); // verb: set + cmd[1] = htonl(0); // id: rx_streaming + cmd[2] = htonl(start ? 1: 0); // start or stop? + cmd[3] = addr.s_addr; // ip address to send data to (already network endian) + cmd[4] = htonl(data_port); // port to send data to + cmd[5] = htonl(samples_per_pkt); + cmd[6] = htonl(siggen_param); + + return send_and_check(ctrl_fd, cmd, sizeof(cmd)); +} + +bool +vrt::quadradio::send_stop_rx_command(int ctrl_fd) +{ + struct in_addr in_addr; + in_addr.s_addr = 0; + return send_rx_command(ctrl_fd, false, in_addr, 0, 0, 0); +} + +bool +vrt::quadradio::set_dboard_pins(int dboard_bitmask, int v) +{ + uint32_t cmd[4]; + cmd[0] = htonl(0); // verb: set + cmd[1] = htonl(1); // id: dboard_pins + cmd[2] = htonl(dboard_bitmask); + cmd[3] = htonl(v); // value + + return send_and_check(d_ctrl_fd, cmd, sizeof(cmd)); +} + +bool +vrt::quadradio::set_setting_reg(int regno, int value) +{ + uint32_t cmd[4]; + cmd[0] = htonl(0); // verb: set + cmd[1] = htonl(2); // id: SR + cmd[2] = htonl(regno); + cmd[3] = htonl(value); + + return send_and_check(d_ctrl_fd, cmd, sizeof(cmd)); +} + +bool +vrt::quadradio::set_hsadc_conf(int dboard_bitmask, int regno, int value) +{ + uint32_t cmd[5]; + cmd[0] = htonl(0); // verb: set + cmd[1] = htonl(3); // id: HSADC_CONF + cmd[2] = htonl(dboard_bitmask); + cmd[3] = htonl(regno); + cmd[4] = htonl(value); + + return send_and_check(d_ctrl_fd, cmd, sizeof(cmd)); +} + +bool +vrt::quadradio::set_lsdac(int dboard_bitmask, int which_dac, int value) +{ + uint32_t cmd[5]; + cmd[0] = htonl(0); // verb: set + cmd[1] = htonl(4); // id: LSDAC + cmd[2] = htonl(dboard_bitmask); + cmd[3] = htonl(which_dac); + cmd[4] = htonl(value); + + return send_and_check(d_ctrl_fd, cmd, sizeof(cmd)); +} + +bool +vrt::quadradio::set_mem32(int addr, int value) +{ + uint32_t cmd[4]; + cmd[0] = htonl(0); // verb: set + cmd[1] = htonl(5); // id: MEM32 + cmd[2] = htonl(addr); + cmd[3] = htonl(value); + + return send_and_check(d_ctrl_fd, cmd, sizeof(cmd)); +} + +bool +vrt::quadradio::set_lo_freq(double freq) +{ + uint64_t lo_freq = uint64_t(freq * (uint64_t(1)<<20)); //q20 format + uint32_t cmd[4]; + cmd[0] = htonl(0); // verb: set + cmd[1] = htonl(6); // id: lo freq + cmd[2] = htonl((lo_freq >> 32) & 0xffffffff); + cmd[3] = htonl((lo_freq >> 0) & 0xffffffff); + + return send_and_check(d_ctrl_fd, cmd, sizeof(cmd)); +} + +bool +vrt::quadradio::set_cal_freq(double freq) +{ + uint64_t cal_freq = uint64_t(freq * (uint64_t(1)<<20)); //q20 format + uint32_t cmd[4]; + cmd[0] = htonl(0); // verb: set + cmd[1] = htonl(7); // id: cal freq + cmd[2] = htonl((cal_freq >> 32) & 0xffffffff); + cmd[3] = htonl((cal_freq >> 0) & 0xffffffff); + + return send_and_check(d_ctrl_fd, cmd, sizeof(cmd)); +} + +bool +vrt::quadradio::set_beamforming(int32_t gains[8]){ + uint32_t cmd[2+8]; + cmd[0] = htonl(0); // verb: set + cmd[1] = htonl(8); // id: beamformin + for (int i = 0; i < 8; i++){ + //printf("%d\n", gains[i]); + cmd[i+2] = htonl(gains[i]); + } + return send_and_check(d_ctrl_fd, cmd, sizeof(cmd)); +} + diff --git a/vrt/lib/rx.cc b/vrt/lib/rx.cc new file mode 100644 index 000000000..f75db3111 --- /dev/null +++ b/vrt/lib/rx.cc @@ -0,0 +1,120 @@ +/* -*- c++ -*- */ +/* + * Copyright 2009 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. + */ +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif +#include <vrt/rx.h> +#include <vrt/expanded_header.h> +#include "socket_rx_buffer.h" +#include "data_handler.h" +#include <unistd.h> +#include <stdio.h> +#include <stdexcept> + +static void +print_words(FILE *fp, size_t offset, uint32_t *buf, size_t n) +{ + size_t i; + for (i = 0; i < n; i++){ + if (i % 4 == 0){ + fprintf(fp, "%04zx:", i); + } + + putc(' ', fp); + fprintf(fp, "%08x", buf[i]); + if (i % 4 == 3) + putc('\n', fp); + } + + putc('\n', fp); +} + + + +namespace vrt { + + rx::sptr + rx::make(int socket_fd, size_t rx_bufsize) + { + return sptr(new rx(socket_fd, rx_bufsize)); + } + + rx::rx(int socket_fd, size_t rx_bufsize) + : d_socket_fd(socket_fd), + d_srb(new socket_rx_buffer(socket_fd, rx_bufsize)) + { + } + + rx::~rx() + { + delete d_srb; + ::close(d_socket_fd); + } + + + class vrt_data_handler : public data_handler + { + rx_packet_handler *d_handler; + + public: + vrt_data_handler(rx_packet_handler *handler) + : d_handler(handler){} + + ~vrt_data_handler(); + + result operator()(const void *base, size_t len); + }; + + vrt_data_handler::~vrt_data_handler(){} + + data_handler::result + vrt_data_handler::operator()(const void *base, size_t len) + { +#if 0 + print_words(0, (uint32_t *)base, len/(sizeof(uint32_t))); + return 0; +#else + const uint32_t *payload; + size_t n32_bit_words; + expanded_header hdr; + if (!expanded_header::parse((const uint32_t*) base, len/(sizeof(uint32_t)), + &hdr, &payload, &n32_bit_words)){ + if (1){ + fprintf(stderr, "vrt_data_handler: malformed VRT packet!\n"); + print_words(stderr, 0, (uint32_t *)base, len/(sizeof(uint32_t))); + } + return 0; + } + bool want_more = (*d_handler)(payload, n32_bit_words, &hdr); + return !want_more ? data_handler::DONE : 0; +#endif + } + + + bool + rx::rx_packets(rx_packet_handler *handler, bool dont_wait) + { + vrt_data_handler h(handler); + socket_rx_buffer::result r = d_srb->rx_frames(&h, dont_wait ? 0 : -1); + return r == socket_rx_buffer::EB_OK || r == socket_rx_buffer::EB_WOULD_BLOCK; + } + +}; // vrt diff --git a/vrt/lib/rx_packet_handler.cc b/vrt/lib/rx_packet_handler.cc new file mode 100644 index 000000000..11f90278d --- /dev/null +++ b/vrt/lib/rx_packet_handler.cc @@ -0,0 +1,41 @@ +/* -*- c++ -*- */ +/* + * Copyright 2009 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. + */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif +#include <vrt/rx_packet_handler.h> + +namespace vrt { + + rx_packet_handler::~rx_packet_handler(){} + + // default operator is a NOP + bool + rx_packet_handler::operator()(const uint32_t *payload, + size_t n32_bit_words, + const expanded_header *hdr) + { + return true; + } + + +}; // vrt diff --git a/vrt/lib/socket_rx_buffer.cc b/vrt/lib/socket_rx_buffer.cc new file mode 100644 index 000000000..6ed211b9a --- /dev/null +++ b/vrt/lib/socket_rx_buffer.cc @@ -0,0 +1,278 @@ +/* -*- c++ -*- */ +/* + * Copyright 2008,2009 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. + */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include "socket_rx_buffer.h" +#include "data_handler.h" +#include <linux/if_packet.h> +#include <sys/socket.h> +#include <sys/mman.h> +#include <sys/poll.h> +#include <iostream> +#include <cmath> +#include <errno.h> +#include <stdexcept> +#include <string.h> +#include <fcntl.h> +#include <cstdio> + + +#define SOCKET_RX_BUFFER_DEBUG 1 // define to 0 or 1 +#if SOCKET_RX_BUFFER_DEBUG +#define DEBUG_LOG(x) ::write(2, (x), 1) +#else +#define DEBUG_LOG(X) +#endif + +#define DEFAULT_MEM_SIZE 62.5e6 // ~0.5s @ 125 MB/s +#define MAX_MEM_SIZE 1000e6 // ~10.00s @ 100 MB/s. +#define MAX_SLAB_SIZE 131072 // 128 KB (FIXME fish out of /proc/slabinfo) + + +namespace vrt { + + const unsigned int socket_rx_buffer::MAX_PKTLEN = 8192; + const unsigned int socket_rx_buffer::MIN_PKTLEN = 64; + + socket_rx_buffer::socket_rx_buffer(int socket_fd, size_t rx_bufsize) + : d_fd(socket_fd), d_using_tpring(false), d_buflen(0), d_buf(0), d_frame_nr(0), + d_frame_size(0), d_head(0), d_ring(0) + { + if (rx_bufsize == 0) + d_buflen = (size_t)DEFAULT_MEM_SIZE; + else + d_buflen = std::min((size_t)MAX_MEM_SIZE, rx_bufsize); + + if (!open()){ + throw std::runtime_error("socket_rx_buffer::open failed"); + } + } + + socket_rx_buffer::~socket_rx_buffer() + { + close(); + } + + bool + socket_rx_buffer::open() + { + if (try_packet_ring()){ + d_using_tpring = true; + // fprintf(stderr, "socket_rx_buffer: using memory mapped interface\n"); + } + else { + d_using_tpring = false; + // fprintf(stderr, "socket_rx_buffer: NOT using memory mapped interface\n"); + + // Increase socket buffer if possible + + int rcvbuf_size = d_buflen; +#if defined(SO_RCVBUFFORCE) + if (setsockopt(d_fd, SOL_SOCKET, SO_RCVBUFFORCE, &rcvbuf_size, sizeof(rcvbuf_size)) != 0){ + perror("setsockopt(SO_RCVBUFFORCE)"); + fprintf(stderr, "Are you running as root? If not, please do.\n"); + } + else { + fprintf(stderr, "SO_RCVBUFFORCE = %zd\n", d_buflen); + } +#endif + } + + return true; + } + + bool + socket_rx_buffer::try_packet_ring() + { + struct tpacket_req req; + size_t page_size = getpagesize(); + + // Calculate minimum power-of-two aligned size for frames + req.tp_frame_size = + (unsigned int)rint(pow(2, ceil(log2(TPACKET_ALIGN(TPACKET_HDRLEN)+TPACKET_ALIGN(MAX_PKTLEN))))); + d_frame_size = req.tp_frame_size; + + // Calculate minimum contiguous pages needed to enclose a frame + int npages = (page_size > req.tp_frame_size) ? 1 : ((req.tp_frame_size+page_size-1)/page_size); + req.tp_block_size = page_size << (int)ceil(log2(npages)); + + // Calculate number of blocks + req.tp_block_nr = (int)(d_buflen/req.tp_block_size); + + + // Recalculate buffer length + d_buflen = req.tp_block_nr*req.tp_block_size; + + // Finally, calculate total number of frames. Since frames, blocks, + // and pages are all power-of-two aligned, frames are contiguous + req.tp_frame_nr = d_buflen/req.tp_frame_size; + d_frame_nr = req.tp_frame_nr; + +#if 0 + if (SOCKET_RX_BUFFER_DEBUG) + std::cerr << "socket_rx_buffer:" + << " frame_size=" << req.tp_frame_size + << " block_size=" << req.tp_block_size + << " block_nr=" << req.tp_block_nr + << " frame_nr=" << req.tp_frame_nr + << " buflen=" << d_buflen + << std::endl; +#endif + + // Try to get kernel shared memory buffer + if (setsockopt(d_fd, SOL_PACKET, PACKET_RX_RING, (void *)&req, sizeof(req)) != 0){ + // perror("socket_rx_buffer: setsockopt"); + return false; + } + + void *p = mmap(0, d_buflen, PROT_READ|PROT_WRITE, MAP_SHARED, d_fd, 0); + if (p == MAP_FAILED){ + perror("socket_rx_buffer: mmap"); + return false; + } + d_buf = (uint8_t *) p; + + // Initialize our pointers into the packet ring + d_ring.resize(req.tp_frame_nr); + for (unsigned int i=0; i < req.tp_frame_nr; i++) + d_ring[i] = (uint8_t *)(d_buf+i*req.tp_frame_size); + + return true; + } + + bool + socket_rx_buffer::close() + { + return true; + } + + inline bool + socket_rx_buffer::frame_available() + { + return (((tpacket_hdr *)d_ring[d_head])->tp_status != TP_STATUS_KERNEL); + } + + socket_rx_buffer::result + socket_rx_buffer::rx_frames(data_handler *f, int timeout_in_ms) + { + if (!d_using_tpring){ + + // ---------------------------------------------------------------- + // Use recv instead of kernel Rx packet ring + // ---------------------------------------------------------------- + + unsigned char buf[MAX_PKTLEN]; + bool dont_wait = timeout_in_ms == 0; // FIXME treating timeout as 0 or inf + int flags = dont_wait ? MSG_DONTWAIT : 0; + + ssize_t rr = recv(d_fd, buf, sizeof(buf), flags); + if (rr == -1){ // error? + if (errno == EAGAIN){ // non-blocking, nothing there + return EB_WOULD_BLOCK; + } + perror("rx_frames: recv"); + return EB_ERROR; + } + + // Got first packet. Call handler + + data_handler::result r = (*f)(buf, rr); + if (r & data_handler::DONE) + return EB_OK; + + // Now do as many as we can without blocking + + while (1){ + rr = recv(d_fd, buf, sizeof(buf), MSG_DONTWAIT); + if (rr == -1){ // error? + if (errno == EAGAIN) // non-blocking, nothing there + return EB_OK; // return OK; we've processed >= 1 packets + perror("rx_frames: recv"); + return EB_ERROR; + } + + r = (*f)(buf, rr); + if (r & data_handler::DONE) + break; + } + return EB_OK; + } + + // ---------------------------------------------------------------- + // Use kernel Rx packet ring + // ---------------------------------------------------------------- + + DEBUG_LOG("\n"); + + while (!frame_available()) { + if (timeout_in_ms == 0) { + DEBUG_LOG("w"); + return EB_WOULD_BLOCK; + } + + struct pollfd pfd; + pfd.fd = d_fd; + pfd.revents = 0; + pfd.events = POLLIN; + + // DEBUG_LOG("P"); + + int pres = poll(&pfd, 1, timeout_in_ms); + if (pres == -1) { + perror("poll"); + return EB_ERROR; + } + + if (pres == 0) { + DEBUG_LOG("t"); + return EB_TIMED_OUT; + } + } + + // Iterate through available packets + while (frame_available()) { + // Get start of ethernet frame and length + tpacket_hdr *hdr = (tpacket_hdr *)d_ring[d_head]; + void *base = (uint8_t *)hdr+hdr->tp_mac; + size_t len = hdr->tp_len; + + if (1) + fprintf(stderr, "socket_rx_buffer: base = %p tp_mac = %3d tp_net = %3d\n", + base, hdr->tp_mac, hdr->tp_net); + + // Invoke data handler + data_handler::result r = (*f)(base, len); + hdr->tp_status = TP_STATUS_KERNEL; // mark it free + + inc_head(); + + if (r & data_handler::DONE) + break; + } + + DEBUG_LOG("|"); + return EB_OK; + } + +} // namespace vrt diff --git a/vrt/lib/socket_rx_buffer.h b/vrt/lib/socket_rx_buffer.h new file mode 100644 index 000000000..053c30c12 --- /dev/null +++ b/vrt/lib/socket_rx_buffer.h @@ -0,0 +1,122 @@ +/* -*- c++ -*- */ +/* + * Copyright 2008,2009 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. + */ +#ifndef INCLUDED_VRT_SOCKET_RX_BUFFER_H +#define INCLUDED_VRT_SOCKET_RX_BUFFER_H + +#include <boost/utility.hpp> +#include <vector> +#include <memory> +#include <stdint.h> + +namespace vrt { + + class data_handler; + + /*! + * \brief high-performance interface to receive datagrams + * + * On many systems it should be possible to implement this on top of libpcap + * + * \internal + */ + class socket_rx_buffer : boost::noncopyable + { + + int d_fd; // socket file descriptor + bool d_using_tpring; // using kernel mapped packet ring + size_t d_buflen; // length of our buffer + uint8_t *d_buf; // packet ring + unsigned int d_frame_nr; // max frames on ring + size_t d_frame_size; // frame storage size + unsigned int d_head; // pointer to next frame + + std::vector<uint8_t *> d_ring; // pointers into buffer + + bool frame_available(); + + void inc_head() + { + if (d_head + 1 >= d_frame_nr) + d_head = 0; + else + d_head = d_head + 1; + } + + bool open(); + bool close(); + bool try_packet_ring(); + + public: + + enum result { + EB_OK, //< everything's fine + EB_ERROR, //< A non-recoverable error occurred + EB_WOULD_BLOCK, //< A timeout of 0 was specified and nothing was ready + EB_TIMED_OUT, //< The timeout expired before anything was ready + }; + + static const unsigned int MAX_PKTLEN; + static const unsigned int MIN_PKTLEN; + + /*! + * \param socket_fd file descriptor that corresponds to a socket + * \param rx_bufsize is a hint as to the number of bytes of memory + * to allocate for received ethernet frames (0 -> reasonable default) + */ + socket_rx_buffer(int socket_fd, size_t rx_bufsize = 0); + ~socket_rx_buffer(); + + /*! + * \brief Call \p f for each frame in the receive buffer. + * \param f is the frame data handler + * \param timeout (in ms) controls behavior when there are no frames to read + * + * If \p timeout is 0, rx_frames will not wait for frames if none are + * available, and f will not be invoked. If \p timeout is -1 (the + * default), rx_frames will block indefinitely until frames are + * available. If \p timeout is positive, it indicates the number of + * milliseconds to wait for a frame to become available. Once the + * timeout has expired, rx_frames will return, f never having been + * invoked. + * + * \p f will be called on each frame that is available. + * \p f returns a bit mask with one of the following set or cleared: + * + * data_handler::DONE - return from rx_frames now even though more frames + * might be available; otherwise continue if more + * frames are ready. + * + * \returns EB_OK if at least one frame was received + * \returns EB_WOULD_BLOCK if \p timeout is 0 and the call would have blocked + * \returns EB_TIMED_OUT if timeout occurred + * \returns EB_ERROR if there was an unrecoverable error. + */ + result rx_frames(data_handler *f, int timeout=-1); + + /* + * \brief Returns maximum possible number of frames in buffer + */ + unsigned int max_frames() const { return d_using_tpring ? d_frame_nr : 0; } + }; + +}; // namespace vrt + +#endif /* INCLUDED_VRT_SOCKET_RX_BUFFER_H */ diff --git a/vrt/vrt.pc.in b/vrt/vrt.pc.in new file mode 100644 index 000000000..0f8cb938b --- /dev/null +++ b/vrt/vrt.pc.in @@ -0,0 +1,11 @@ +prefix=@prefix@ +exec_prefix=@exec_prefix@ +libdir=@libdir@ +includedir=@includedir@ + +Name: vrt +Description: Host implementation of Virtual Radio Transport (VITA-49) +Requires: +Version: @VERSION@ +Libs: -L${libdir} -lvrt +Cflags: -I${includedir} |