diff options
Diffstat (limited to 'usrp/limbo')
49 files changed, 12272 insertions, 0 deletions
diff --git a/usrp/limbo/apps-inband/Makefile.am b/usrp/limbo/apps-inband/Makefile.am new file mode 100644 index 000000000..0a44d8112 --- /dev/null +++ b/usrp/limbo/apps-inband/Makefile.am @@ -0,0 +1,77 @@ +# +# Copyright 2003,2006,2008 Free Software Foundation, Inc. +# +# This file is part of GNU Radio +# +# GNU Radio is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3, or (at your option) +# any later version. +# +# GNU Radio is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with GNU Radio; see the file COPYING. If not, write to +# the Free Software Foundation, Inc., 51 Franklin Street, +# Boston, MA 02110-1301, USA. +# + +include $(top_srcdir)/Makefile.common + +AM_CPPFLAGS = \ + $(DEFINES) $(OMNITHREAD_INCLUDES) $(PMT_INCLUDES) $(MBLOCK_INCLUDES) \ + $(USRP_INCLUDES) $(USRP_INBAND_INCLUDES) $(BOOST_CPPFLAGS) \ + $(CPPUNIT_INCLUDES) $(WITH_INCLUDES) -I$(top_srcdir)/mblock/src/lib + + +bin_PROGRAMS = + +noinst_PROGRAMS = \ + test_usrp_inband_ping \ + test_usrp_inband_registers \ + test_usrp_inband_rx \ + test_usrp_inband_2rx \ + test_usrp_inband_tx \ + test_usrp_inband_2tx \ + test_usrp_inband_timestamps \ + test_usrp_inband_overrun \ + test_usrp_inband_underrun \ + read_packets + +noinst_HEADERS = \ + ui_nco.h \ + ui_sincos.h + + +test_usrp_inband_ping_SOURCES = test_usrp_inband_ping.cc +test_usrp_inband_ping_LDADD = $(USRP_LA) $(USRP_INBAND_LA) + +test_usrp_inband_tx_SOURCES = test_usrp_inband_tx.cc ui_sincos.c +test_usrp_inband_tx_LDADD = $(USRP_LA) $(USRP_INBAND_LA) + +test_usrp_inband_2tx_SOURCES = test_usrp_inband_2tx.cc ui_sincos.c +test_usrp_inband_2tx_LDADD = $(USRP_LA) $(USRP_INBAND_LA) + +test_usrp_inband_timestamps_SOURCES = test_usrp_inband_timestamps.cc ui_sincos.c +test_usrp_inband_timestamps_LDADD = $(USRP_LA) $(USRP_INBAND_LA) + +test_usrp_inband_registers_SOURCES = test_usrp_inband_registers.cc ui_sincos.c +test_usrp_inband_registers_LDADD = $(USRP_LA) $(USRP_INBAND_LA) + +test_usrp_inband_overrun_SOURCES = test_usrp_inband_overrun.cc +test_usrp_inband_overrun_LDADD = $(USRP_LA) $(USRP_INBAND_LA) + +test_usrp_inband_underrun_SOURCES = test_usrp_inband_underrun.cc +test_usrp_inband_underrun_LDADD = $(USRP_LA) $(USRP_INBAND_LA) + +test_usrp_inband_rx_SOURCES = test_usrp_inband_rx.cc ui_sincos.c +test_usrp_inband_rx_LDADD = $(USRP_LA) $(USRP_INBAND_LA) + +test_usrp_inband_2rx_SOURCES = test_usrp_inband_2rx.cc ui_sincos.c +test_usrp_inband_2rx_LDADD = $(USRP_LA) $(USRP_INBAND_LA) + +read_packets_SOURCES = read_packets.cc +read_packets_LDADD = $(USRP_LA) $(USRP_INBAND_LA) diff --git a/usrp/limbo/apps-inband/read_packets.cc b/usrp/limbo/apps-inband/read_packets.cc new file mode 100644 index 000000000..24a1e88b5 --- /dev/null +++ b/usrp/limbo/apps-inband/read_packets.cc @@ -0,0 +1,109 @@ +/* -*- c++ -*- */ +/* + * Copyright 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 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 <iostream> +#include <usrp_inband_usb_packet.h> +#include <mblock/class_registry.h> +#include <vector> +#include <usrp_usb_interface.h> +#include <fstream> + +typedef usrp_inband_usb_packet transport_pkt; // makes conversion to gigabit easy + +int main(int argc, char *argv[]) { + + if(argc !=2) { + std::cout << "Usage: ./read_packets <data_file>\n"; + return -1; + } + + std::ifstream infile; + std::ofstream outfile; + + unsigned int pkt_size = transport_pkt::max_pkt_size(); + unsigned int pkt_num=0; + + transport_pkt *pkt; + char pkt_data[pkt_size]; // allocate the number of bytes for a single packet + + pkt = (transport_pkt *)pkt_data; // makes operations cleaner to read + + // Open the file and read the packets, dumping information + infile.open(argv[1], std::ios::binary|std::ios::in); + if(!infile.is_open()) + exit(-1); + + //outfile.open("dump.dat",std::ios::out|std::ios::binary); + + // read 1 packet in to the memory + infile.read(pkt_data, pkt_size); + + while(!infile.eof()) { + + printf("Packet %u\n", pkt_num); + + if(pkt->start_of_burst()) + printf("\tstart of burst\n"); + + if(pkt->end_of_burst()) + printf("\tend of burst\n"); + +// if(pkt->carrier_sense()) +// printf("\tcarrier sense\n"); + + if(pkt->underrun()) + printf("\tunderrun\n"); + + if(pkt->overrun()) + printf("\toverrun\n"); + + printf("\tchannel: \t0x%x\n", pkt->chan()); + printf("\ttimestamp: \t0x%x\n", pkt->timestamp()); + //printf("\ttimestamp: \t%u\n", pkt->timestamp()); + printf("\tlength: \t%u\n", pkt->payload_len()); + printf("\trssi: \t%u\n", pkt->rssi()); + + printf("\tpayload: \n"); + for(int i=0; i < pkt->payload_len(); i++) + //for(int i=0; i < pkt->max_payload(); i++) + { + printf("\t%d\t0x%x\n", i, *(pkt->payload()+i)); + //outfile.write((const char*)(pkt->payload()+i),1); + //printf("\t\t0x%x\n", pkt->payload()+i); + + } + printf("\n\n"); + + pkt_num++; + + // read 1 packet in to the memory + infile.read(pkt_data, pkt_size); + + } + + infile.close(); + //outfile.close(); + +} diff --git a/usrp/limbo/apps-inband/test_usrp_inband_2rx.cc b/usrp/limbo/apps-inband/test_usrp_inband_2rx.cc new file mode 100644 index 000000000..c210f196a --- /dev/null +++ b/usrp/limbo/apps-inband/test_usrp_inband_2rx.cc @@ -0,0 +1,371 @@ +/* -*- c++ -*- */ +/* + * Copyright 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 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 <mblock/mblock.h> +#include <mblock/runtime.h> +#include <mb_runtime_nop.h> // QA only +#include <mblock/protocol_class.h> +#include <mblock/exception.h> +#include <mblock/msg_queue.h> +#include <mblock/message.h> +#include <mb_mblock_impl.h> +#include <mblock/msg_accepter.h> +#include <mblock/class_registry.h> +#include <pmt.h> +#include <stdio.h> +#include <string.h> +#include <iostream> +#include <fstream> + +// Include the symbols needed for communication with USRP server +#include <symbols_usrp_server_cs.h> +#include <symbols_usrp_channel.h> +#include <symbols_usrp_low_level_cs.h> +#include <symbols_usrp_rx.h> + +static bool verbose = true; + +class test_usrp_rx : public mb_mblock +{ + mb_port_sptr d_rx; + mb_port_sptr d_cs; + pmt_t d_rx_chan0, d_rx_chan1; + + enum state_t { + INIT, + OPENING_USRP, + ALLOCATING_CHANNEL, + RECEIVING, + CLOSING_CHANNEL, + CLOSING_USRP, + }; + + state_t d_state; + + std::ofstream d_ofile; + + long d_samples_recvd; + long d_samples_to_recv; + + public: + test_usrp_rx(mb_runtime *runtime, const std::string &instance_name, pmt_t user_arg); + ~test_usrp_rx(); + void initial_transition(); + void handle_message(mb_message_sptr msg); + + protected: + void open_usrp(); + void close_usrp(); + void allocate_channel(); + void send_packets(); + void enter_receiving(); + void build_and_send_next_frame(); + void handle_response_recv_raw_samples(pmt_t invocation_handle); + void enter_closing_channel(); +}; + +test_usrp_rx::test_usrp_rx(mb_runtime *runtime, const std::string &instance_name, pmt_t user_arg) + : mb_mblock(runtime, instance_name, user_arg), + d_rx_chan0(PMT_NIL), d_rx_chan1(PMT_NIL), + d_samples_recvd(0), + d_samples_to_recv(20e6) +{ + d_rx = define_port("rx0", "usrp-rx", false, mb_port::INTERNAL); + d_cs = define_port("cs", "usrp-server-cs", false, mb_port::INTERNAL); + + // Pass a dictionary to usrp_server which specifies which interface to use, the stub or USRP + pmt_t usrp_dict = pmt_make_dict(); + + // To test the application without a USRP + bool fake_usrp_p = false; + if(fake_usrp_p) { + pmt_dict_set(usrp_dict, + pmt_intern("fake-usrp"), + PMT_T); + } + + // Specify the RBF to use + pmt_dict_set(usrp_dict, + pmt_intern("rbf"), + pmt_intern("inband_2rxhb_2tx.rbf")); + + pmt_dict_set(usrp_dict, + pmt_intern("decim-rx"), + pmt_from_long(64)); + + define_component("server", "usrp_server", usrp_dict); + + connect("self", "rx0", "server", "rx0"); + connect("self", "cs", "server", "cs"); + +} + +test_usrp_rx::~test_usrp_rx() +{ +} + +void +test_usrp_rx::initial_transition() +{ + open_usrp(); +} + +void +test_usrp_rx::handle_message(mb_message_sptr msg) +{ + pmt_t event = msg->signal(); + pmt_t data = msg->data(); + + pmt_t handle = PMT_F; + pmt_t status = PMT_F; + std::string error_msg; + + switch(d_state){ + + //----------------------------- OPENING_USRP ----------------------------// + // We only expect a response from opening the USRP which should be succesful + // or failed. + case OPENING_USRP: + if (pmt_eq(event, s_response_open)){ + status = pmt_nth(1, data); + if (pmt_eq(status, PMT_T)){ + allocate_channel(); + return; + } + else { + error_msg = "failed to open usrp:"; + goto bail; + } + } + goto unhandled; + + //----------------------- ALLOCATING CHANNELS --------------------// + // Allocate an RX channel to perform the overrun test. + case ALLOCATING_CHANNEL: + if (pmt_eq(event, s_response_allocate_channel)){ + status = pmt_nth(1, data); + if(pmt_eqv(d_rx_chan0, PMT_NIL)) + d_rx_chan0 = pmt_nth(2, data); + else + d_rx_chan1 = pmt_nth(2, data); + + if (pmt_eq(status, PMT_T) && !pmt_eqv(d_rx_chan1, PMT_NIL)){ + enter_receiving(); + return; + } + else if(pmt_eq(status, PMT_F)){ + error_msg = "failed to allocate channel:"; + goto bail; + } + return; + } + goto unhandled; + + //--------------------------- RECEIVING ------------------------------// + // In the receiving state, we receive samples until the specified amount + // while counting the number of overruns. + case RECEIVING: + if (pmt_eq(event, s_response_recv_raw_samples)){ + status = pmt_nth(1, data); + + if (pmt_eq(status, PMT_T)){ + handle_response_recv_raw_samples(data); + return; + } + else { + error_msg = "bad response-xmit-raw-frame:"; + goto bail; + } + } + goto unhandled; + + //------------------------- CLOSING CHANNEL ----------------------------// + // Check deallocation response for the RX channel + case CLOSING_CHANNEL: + if (pmt_eq(event, s_response_deallocate_channel)){ + status = pmt_nth(1, data); + + if (pmt_eq(status, PMT_T)){ + close_usrp(); + return; + } + else { + error_msg = "failed to deallocate channel:"; + goto bail; + } + } + + // Alternately, we ignore all response recv samples while waiting for the + // channel to actually close + if (pmt_eq(event, s_response_recv_raw_samples)) + return; + + goto unhandled; + + //--------------------------- CLOSING USRP ------------------------------// + // Once we have received a successful USRP close response, we shutdown all + // mblocks and exit. + case CLOSING_USRP: + if (pmt_eq(event, s_response_close)){ + status = pmt_nth(1, data); + + if (pmt_eq(status, PMT_T)){ + fflush(stdout); + shutdown_all(PMT_T); + return; + } + else { + error_msg = "failed to close USRP:"; + goto bail; + } + } + goto unhandled; + + default: + goto unhandled; + } + return; + + // An error occured, print it, and shutdown all m-blocks + bail: + std::cerr << error_msg << data + << "status = " << status << std::endl; + shutdown_all(PMT_F); + return; + + // Received an unhandled message for a specific state + unhandled: + if(verbose && !pmt_eq(event, pmt_intern("%shutdown"))) + std::cout << "test_usrp_inband_tx: unhandled msg: " << msg + << "in state "<< d_state << std::endl; +} + + +void +test_usrp_rx::open_usrp() +{ + pmt_t which_usrp = pmt_from_long(0); + + d_cs->send(s_cmd_open, pmt_list2(PMT_NIL, which_usrp)); + d_state = OPENING_USRP; + + if(verbose) + std::cout << "[TEST_USRP_INBAND_RX] Opening the USRP\n"; +} + +void +test_usrp_rx::close_usrp() +{ + + d_cs->send(s_cmd_close, pmt_list1(PMT_NIL)); + d_state = CLOSING_USRP; + + if(verbose) + std::cout << "[TEST_USRP_INBAND_RX] Closing the USRP\n"; +} + +void +test_usrp_rx::allocate_channel() +{ + long capacity = (long) 16e6; + d_rx->send(s_cmd_allocate_channel, pmt_list2(PMT_T, pmt_from_long(capacity))); + d_rx->send(s_cmd_allocate_channel, pmt_list2(PMT_T, pmt_from_long(capacity))); + d_state = ALLOCATING_CHANNEL; + + if(verbose) + std::cout << "[TEST_USRP_INBAND_RX] Requesting RX channel allocation\n"; +} + +void +test_usrp_rx::enter_receiving() +{ + d_state = RECEIVING; + + d_rx->send(s_cmd_start_recv_raw_samples, + pmt_list2(PMT_F, + d_rx_chan0)); + + d_rx->send(s_cmd_start_recv_raw_samples, + pmt_list2(PMT_F, + d_rx_chan1)); + + if(verbose) + std::cout << "[TEST_USRP_INBAND_RX] Receiving...\n"; +} + +void +test_usrp_rx::handle_response_recv_raw_samples(pmt_t data) +{ + pmt_t invocation_handle = pmt_nth(0, data); + pmt_t status = pmt_nth(1, data); + pmt_t v_samples = pmt_nth(2, data); + pmt_t timestamp = pmt_nth(3, data); + pmt_t channel = pmt_nth(4, data); + pmt_t properties = pmt_nth(5, data); + + d_samples_recvd += pmt_length(v_samples) / 4; + + // Check for overrun + if(!pmt_is_dict(properties)) { + std::cout << "[TEST_USRP_INBAND_RX] Recv samples dictionary is improper\n"; + return; + } + + // Check if the number samples we have received meets the test + if(d_samples_recvd >= d_samples_to_recv) { + d_rx->send(s_cmd_stop_recv_raw_samples, pmt_list2(PMT_NIL, d_rx_chan0)); + d_rx->send(s_cmd_stop_recv_raw_samples, pmt_list2(PMT_NIL, d_rx_chan1)); + enter_closing_channel(); + return; + } + +} + +void +test_usrp_rx::enter_closing_channel() +{ + d_state = CLOSING_CHANNEL; + + d_rx->send(s_cmd_deallocate_channel, pmt_list2(PMT_NIL, d_rx_chan0)); + d_rx->send(s_cmd_deallocate_channel, pmt_list2(PMT_NIL, d_rx_chan1)); + + if(verbose) + std::cout << "[TEST_USRP_INBAND_RX] Deallocating RX channel\n"; +} + +REGISTER_MBLOCK_CLASS(test_usrp_rx); + + +// ---------------------------------------------------------------- + +int +main (int argc, char **argv) +{ + mb_runtime_sptr rt = mb_make_runtime(); + pmt_t result = PMT_NIL; + + rt->run("top", "test_usrp_rx", PMT_F, &result); + +} diff --git a/usrp/limbo/apps-inband/test_usrp_inband_2tx.cc b/usrp/limbo/apps-inband/test_usrp_inband_2tx.cc new file mode 100644 index 000000000..11a1a491c --- /dev/null +++ b/usrp/limbo/apps-inband/test_usrp_inband_2tx.cc @@ -0,0 +1,430 @@ +/* -*- c++ -*- */ +/* + * Copyright 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 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 <mblock/mblock.h> +#include <mblock/runtime.h> +#include <mb_runtime_nop.h> // QA only +#include <mblock/protocol_class.h> +#include <mblock/exception.h> +#include <mblock/msg_queue.h> +#include <mblock/message.h> +#include <mb_mblock_impl.h> +#include <mblock/msg_accepter.h> +#include <mblock/class_registry.h> +#include <pmt.h> +#include <stdio.h> +#include <string.h> +#include <iostream> + +#include <ui_nco.h> +#include <symbols_usrp_server_cs.h> +#include <symbols_usrp_channel.h> +#include <symbols_usrp_low_level_cs.h> +#include <symbols_usrp_tx.h> + +static bool verbose = true; + +class test_usrp_tx : public mb_mblock +{ + mb_port_sptr d_tx; + mb_port_sptr d_cs; + pmt_t d_tx_chan0, d_tx_chan1; + + enum state_t { + INIT, + OPENING_USRP, + ALLOCATING_CHANNEL, + TRANSMITTING, + CLOSING_CHANNEL, + CLOSING_USRP, + }; + + state_t d_state; + long d_nsamples_to_send; + long d_nsamples_xmitted; + long d_nframes_xmitted; + long d_samples_per_frame; + bool d_done_sending; + + // for generating sine wave output + ui_nco<float,float> d_nco; + double d_amplitude; + + public: + test_usrp_tx(mb_runtime *runtime, const std::string &instance_name, pmt_t user_arg); + ~test_usrp_tx(); + void initial_transition(); + void handle_message(mb_message_sptr msg); + + protected: + void open_usrp(); + void close_usrp(); + void allocate_channel(); + void send_packets(); + void enter_transmitting(); + void build_and_send_next_frame(); + void handle_xmit_response(pmt_t invocation_handle); + void enter_closing_channel(); +}; + +test_usrp_tx::test_usrp_tx(mb_runtime *runtime, const std::string &instance_name, pmt_t user_arg) + : mb_mblock(runtime, instance_name, user_arg), + d_tx_chan0(PMT_NIL), d_tx_chan1(PMT_NIL), + d_state(INIT), d_nsamples_to_send((long) 80e6), + d_nsamples_xmitted(0), + d_nframes_xmitted(0), + d_samples_per_frame((long)(126 * 4)), // full packet + d_done_sending(false), + d_amplitude(16384) +{ + // std::cout << "[TEST_USRP_TX] Initializing...\n"; + + d_tx = define_port("tx0", "usrp-tx", false, mb_port::INTERNAL); + d_cs = define_port("cs", "usrp-server-cs", false, mb_port::INTERNAL); + + //bool fake_usrp_p = true; + bool fake_usrp_p = false; + + // Test the TX side + + pmt_t usrp_dict = pmt_make_dict(); + + if(fake_usrp_p) { + pmt_dict_set(usrp_dict, + pmt_intern("fake-usrp"), + PMT_T); + } + + // Specify the RBF to use + pmt_dict_set(usrp_dict, + pmt_intern("rbf"), + pmt_intern("inband_2rxhb_2tx.rbf")); + + // Set TX and RX interpolations + pmt_dict_set(usrp_dict, + pmt_intern("interp-tx"), + pmt_from_long(128)); + +// pmt_dict_set(usrp_dict, +// pmt_intern("rf-freq"), +// pmt_from_long(10e6)); + + define_component("server", "usrp_server", usrp_dict); + + connect("self", "tx0", "server", "tx0"); + connect("self", "cs", "server", "cs"); + + // initialize NCO + double freq = 100e3; + int interp = 32; // 32 -> 4MS/s + double sample_rate = 128e6 / interp; + d_nco.set_freq(2*M_PI * freq/sample_rate); + + // FIXME need to somehow set the interp rate in the USRP. + // for now, we'll have the low-level code hardwire it. +} + +test_usrp_tx::~test_usrp_tx() +{ +} + +void +test_usrp_tx::initial_transition() +{ + open_usrp(); +} + +void +test_usrp_tx::handle_message(mb_message_sptr msg) +{ + pmt_t event = msg->signal(); + pmt_t data = msg->data(); + + pmt_t handle = PMT_F; + pmt_t status = PMT_F; + std::string error_msg; + + //std::cout << msg << std::endl; + + switch(d_state){ + case OPENING_USRP: + if (pmt_eq(event, s_response_open)){ + status = pmt_nth(1, data); + if (pmt_eq(status, PMT_T)){ + allocate_channel(); + return; + } + else { + error_msg = "failed to open usrp:"; + goto bail; + } + } + goto unhandled; + + case ALLOCATING_CHANNEL: + if (pmt_eq(event, s_response_allocate_channel)){ + status = pmt_nth(1, data); + if(pmt_eqv(d_tx_chan0, PMT_NIL)) + d_tx_chan0 = pmt_nth(2, data); + else + d_tx_chan1 = pmt_nth(2, data); + + if (pmt_eq(status, PMT_T) && !pmt_eqv(d_tx_chan1, PMT_NIL)){ + enter_transmitting(); + return; + } + else if(pmt_eq(status, PMT_F)){ + error_msg = "failed to allocate channel:"; + goto bail; + } + return; + } + goto unhandled; + + case TRANSMITTING: + if (pmt_eq(event, s_response_xmit_raw_frame)){ + handle = pmt_nth(0, data); + status = pmt_nth(1, data); + + if (pmt_eq(status, PMT_T)){ + handle_xmit_response(handle); + return; + } + else { + error_msg = "bad response-xmit-raw-frame:"; + goto bail; + } + } + goto unhandled; + + case CLOSING_CHANNEL: + if (pmt_eq(event, s_response_deallocate_channel)){ + status = pmt_nth(1, data); + + if (pmt_eq(status, PMT_T)){ + close_usrp(); + return; + } + else { + error_msg = "failed to deallocate channel:"; + goto bail; + } + } + goto unhandled; + + case CLOSING_USRP: + if (pmt_eq(event, s_response_close)){ + status = pmt_nth(1, data); + + if (pmt_eq(status, PMT_T)){ + shutdown_all(PMT_T); + return; + } + else { + error_msg = "failed to close USRP:"; + goto bail; + } + } + goto unhandled; + + default: + goto unhandled; + } + return; + + bail: + std::cerr << error_msg << data + << "status = " << status << std::endl; + shutdown_all(PMT_F); + return; + + unhandled: + std::cout << "test_usrp_inband_tx: unhandled msg: " << msg + << "in state "<< d_state << std::endl; +} + + +void +test_usrp_tx::open_usrp() +{ + pmt_t which_usrp = pmt_from_long(0); + + d_cs->send(s_cmd_open, pmt_list2(PMT_NIL, which_usrp)); + d_state = OPENING_USRP; + + if(verbose) + std::cout << "[TEST_USRP_INBAND_TX] Opening the USRP\n"; +} + +void +test_usrp_tx::close_usrp() +{ + d_cs->send(s_cmd_close, pmt_list1(PMT_NIL)); + d_state = CLOSING_USRP; + + if(verbose) + std::cout << "[TEST_USRP_INBAND_TX] Closing the USRP\n"; +} + +void +test_usrp_tx::allocate_channel() +{ + long capacity = (long) 16e6; + + // Send two capacity requests, which will allocate us two channels + d_tx->send(s_cmd_allocate_channel, pmt_list2(PMT_T, pmt_from_long(capacity))); + d_tx->send(s_cmd_allocate_channel, pmt_list2(PMT_T, pmt_from_long(capacity))); + d_state = ALLOCATING_CHANNEL; + + if(verbose) + std::cout << "[TEST_USRP_INBAND_TX] Requesting TX channel allocation\n"; +} + +void +test_usrp_tx::enter_transmitting() +{ + d_state = TRANSMITTING; + d_nsamples_xmitted = 0; + + if(verbose) + std::cout << "[TEST_USRP_INBAND_TX] Transmitting...\n"; + + build_and_send_next_frame(); // fire off 4 to start pipeline + build_and_send_next_frame(); + build_and_send_next_frame(); + build_and_send_next_frame(); +} + +void +test_usrp_tx::build_and_send_next_frame() +{ + // allocate the uniform vector for the samples + // FIXME perhaps hold on to this between calls + +#if 1 + long nsamples_this_frame = + std::min(d_nsamples_to_send - d_nsamples_xmitted, + d_samples_per_frame); +#else + long nsamples_this_frame = d_samples_per_frame; +#endif + + if (nsamples_this_frame == 0){ + d_done_sending = true; + return; + } + + + size_t nshorts = 2 * nsamples_this_frame; // 16-bit I & Q + pmt_t uvec = pmt_make_s16vector(nshorts, 0); + size_t ignore; + int16_t *samples = pmt_s16vector_writable_elements(uvec, ignore); + + // fill in the complex sinusoid + + for (int i = 0; i < nsamples_this_frame; i++){ + + if (1){ + gr_complex s; + d_nco.sincos(&s, 1, d_amplitude); + // write 16-bit i & q + samples[2*i] = (int16_t) s.real(); + samples[2*i+1] = (int16_t) s.imag(); + } + else { + gr_complex s(d_amplitude, d_amplitude); + + // write 16-bit i & q + samples[2*i] = (int16_t) s.real(); + samples[2*i+1] = (int16_t) s.imag(); + } + } + + pmt_t tx_properties = pmt_make_dict(); + + pmt_t timestamp = pmt_from_long(0xffffffff); // NOW + d_tx->send(s_cmd_xmit_raw_frame, + pmt_list5(pmt_from_long(d_nframes_xmitted), // invocation-handle + d_tx_chan0, // channel + uvec, // the samples + timestamp, + tx_properties)); + + // Resend on channel 1 + d_tx->send(s_cmd_xmit_raw_frame, + pmt_list5(pmt_from_long(d_nframes_xmitted), // invocation-handle + d_tx_chan1, // channel + uvec, // the samples + timestamp, + tx_properties)); + + d_nsamples_xmitted += nsamples_this_frame; + d_nframes_xmitted++; + + if(verbose && 0) + std::cout << "[TEST_USRP_INBAND_TX] Transmitted frame\n"; +} + + +void +test_usrp_tx::handle_xmit_response(pmt_t handle) +{ + if (d_done_sending && + pmt_to_long(handle) == (d_nframes_xmitted - 1)){ + // We're done sending and have received all responses + enter_closing_channel(); + } + + build_and_send_next_frame(); +} + +void +test_usrp_tx::enter_closing_channel() +{ + d_state = CLOSING_CHANNEL; + + // Deallocate both channels + d_tx->send(s_cmd_deallocate_channel, pmt_list2(PMT_NIL, d_tx_chan0)); + d_tx->send(s_cmd_deallocate_channel, pmt_list2(PMT_NIL, d_tx_chan1)); + + if(verbose) + std::cout << "[TEST_USRP_INBAND_tX] Deallocating TX channel\n"; +} + +REGISTER_MBLOCK_CLASS(test_usrp_tx); + + +// ---------------------------------------------------------------- + +int +main (int argc, char **argv) +{ + // handle any command line args here + + mb_runtime_sptr rt = mb_make_runtime(); + pmt_t result = PMT_NIL; + + rt->run("top", "test_usrp_tx", PMT_F, &result); +} diff --git a/usrp/limbo/apps-inband/test_usrp_inband_overrun.cc b/usrp/limbo/apps-inband/test_usrp_inband_overrun.cc new file mode 100644 index 000000000..cd0fa525a --- /dev/null +++ b/usrp/limbo/apps-inband/test_usrp_inband_overrun.cc @@ -0,0 +1,375 @@ +/* -*- c++ -*- */ +/* + * Copyright 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 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 <mblock/mblock.h> +#include <mblock/runtime.h> +#include <mblock/protocol_class.h> +#include <mblock/exception.h> +#include <mblock/msg_queue.h> +#include <mblock/message.h> +#include <mblock/msg_accepter.h> +#include <mblock/class_registry.h> +#include <pmt.h> +#include <stdio.h> +#include <string.h> +#include <iostream> +#include <fstream> + +// Include the symbols needed for communication with USRP server +#include <symbols_usrp_server_cs.h> +#include <symbols_usrp_channel.h> +#include <symbols_usrp_low_level_cs.h> +#include <symbols_usrp_rx.h> + +static bool verbose = true; + +class test_usrp_rx : public mb_mblock +{ + mb_port_sptr d_rx; + mb_port_sptr d_cs; + pmt_t d_rx_chan; // returned tx channel handle + + enum state_t { + INIT, + OPENING_USRP, + ALLOCATING_CHANNEL, + RECEIVING, + CLOSING_CHANNEL, + CLOSING_USRP, + }; + + state_t d_state; + + std::ofstream d_ofile; + + long d_n_overruns; + + long d_samples_recvd; + long d_samples_to_recv; + + public: + test_usrp_rx(mb_runtime *runtime, const std::string &instance_name, pmt_t user_arg); + ~test_usrp_rx(); + void initial_transition(); + void handle_message(mb_message_sptr msg); + + protected: + void open_usrp(); + void close_usrp(); + void allocate_channel(); + void send_packets(); + void enter_receiving(); + void build_and_send_next_frame(); + void handle_response_recv_raw_samples(pmt_t invocation_handle); + void enter_closing_channel(); +}; + +test_usrp_rx::test_usrp_rx(mb_runtime *runtime, const std::string &instance_name, pmt_t user_arg) + : mb_mblock(runtime, instance_name, user_arg), + d_n_overruns(0), + d_samples_recvd(0), + d_samples_to_recv(10e6) +{ + + d_rx = define_port("rx0", "usrp-rx", false, mb_port::INTERNAL); + d_cs = define_port("cs", "usrp-server-cs", false, mb_port::INTERNAL); + + // Pass a dictionary to usrp_server which specifies which interface to use, the stub or USRP + pmt_t usrp_dict = pmt_make_dict(); + + // Specify the RBF to use + pmt_dict_set(usrp_dict, + pmt_intern("rbf"), + pmt_intern("inband_1rxhb_1tx.rbf")); + + pmt_dict_set(usrp_dict, + pmt_intern("decim-rx"), + pmt_from_long(128)); + + define_component("server", "usrp_server", usrp_dict); + + connect("self", "rx0", "server", "rx0"); + connect("self", "cs", "server", "cs"); + +} + +test_usrp_rx::~test_usrp_rx() +{ +} + +void +test_usrp_rx::initial_transition() +{ + open_usrp(); +} + +void +test_usrp_rx::handle_message(mb_message_sptr msg) +{ + pmt_t event = msg->signal(); + pmt_t data = msg->data(); + + pmt_t handle = PMT_F; + pmt_t status = PMT_F; + std::string error_msg; + + switch(d_state){ + + //----------------------------- OPENING_USRP ----------------------------// + // We only expect a response from opening the USRP which should be succesful + // or failed. + case OPENING_USRP: + if (pmt_eq(event, s_response_open)){ + status = pmt_nth(1, data); + if (pmt_eq(status, PMT_T)){ + allocate_channel(); + return; + } + else { + error_msg = "failed to open usrp:"; + goto bail; + } + } + goto unhandled; + + //----------------------- ALLOCATING CHANNELS --------------------// + // Allocate an RX channel to perform the overrun test. + case ALLOCATING_CHANNEL: + if (pmt_eq(event, s_response_allocate_channel)){ + status = pmt_nth(1, data); + d_rx_chan = pmt_nth(2, data); + + if (pmt_eq(status, PMT_T)){ + enter_receiving(); + return; + } + else { + error_msg = "failed to allocate channel:"; + goto bail; + } + } + goto unhandled; + + //--------------------------- RECEIVING ------------------------------// + // In the receiving state, we receive samples until the specified amount + // while counting the number of overruns. + case RECEIVING: + if (pmt_eq(event, s_response_recv_raw_samples)){ + status = pmt_nth(1, data); + + if (pmt_eq(status, PMT_T)){ + handle_response_recv_raw_samples(data); + return; + } + else { + error_msg = "bad response-xmit-raw-frame:"; + goto bail; + } + } + goto unhandled; + + //------------------------- CLOSING CHANNEL ----------------------------// + // Check deallocation response for the RX channel + case CLOSING_CHANNEL: + if (pmt_eq(event, s_response_deallocate_channel)){ + status = pmt_nth(1, data); + + if (pmt_eq(status, PMT_T)){ + close_usrp(); + return; + } + else { + error_msg = "failed to deallocate channel:"; + goto bail; + } + } + + // Alternately, we ignore all response recv samples while waiting for the + // channel to actually close + if (pmt_eq(event, s_response_recv_raw_samples)) + return; + + goto unhandled; + + //--------------------------- CLOSING USRP ------------------------------// + // Once we have received a successful USRP close response, we shutdown all + // mblocks and exit. + case CLOSING_USRP: + if (pmt_eq(event, s_response_close)){ + status = pmt_nth(1, data); + + if (pmt_eq(status, PMT_T)){ + std::cout << "\nOverruns: " << d_n_overruns << std::endl; + fflush(stdout); + shutdown_all(PMT_T); + return; + } + else { + error_msg = "failed to close USRP:"; + goto bail; + } + } + goto unhandled; + + default: + goto unhandled; + } + return; + + // An error occured, print it, and shutdown all m-blocks + bail: + std::cerr << error_msg << data + << "status = " << status << std::endl; + shutdown_all(PMT_F); + return; + + // Received an unhandled message for a specific state + unhandled: + if(verbose && !pmt_eq(event, pmt_intern("%shutdown"))) + std::cout << "test_usrp_inband_tx: unhandled msg: " << msg + << "in state "<< d_state << std::endl; +} + + +void +test_usrp_rx::open_usrp() +{ + pmt_t which_usrp = pmt_from_long(0); + + d_cs->send(s_cmd_open, pmt_list2(PMT_NIL, which_usrp)); + d_state = OPENING_USRP; + + if(verbose) + std::cout << "[TEST_USRP_INBAND_OVERRUN] Opening the USRP\n"; +} + +void +test_usrp_rx::close_usrp() +{ + d_cs->send(s_cmd_close, pmt_list1(PMT_NIL)); + d_state = CLOSING_USRP; + + if(verbose) + std::cout << "[TEST_USRP_INBAND_OVERRUN] Closing the USRP\n"; +} + +void +test_usrp_rx::allocate_channel() +{ + long capacity = (long) 16e6; + d_rx->send(s_cmd_allocate_channel, pmt_list2(PMT_T, pmt_from_long(capacity))); + d_state = ALLOCATING_CHANNEL; + + if(verbose) + std::cout << "[TEST_USRP_INBAND_OVERRUN] Requesting RX channel allocation\n"; +} + +void +test_usrp_rx::enter_receiving() +{ + d_state = RECEIVING; + + d_rx->send(s_cmd_start_recv_raw_samples, + pmt_list2(PMT_F, + d_rx_chan)); + + if(verbose) + std::cout << "[TEST_USRP_INBAND_OVERRUN] Receiving...\n"; +} + +void +test_usrp_rx::handle_response_recv_raw_samples(pmt_t data) +{ + pmt_t invocation_handle = pmt_nth(0, data); + pmt_t status = pmt_nth(1, data); + pmt_t v_samples = pmt_nth(2, data); + pmt_t timestamp = pmt_nth(3, data); + pmt_t channel = pmt_nth(4, data); + pmt_t properties = pmt_nth(5, data); + + d_samples_recvd += pmt_length(v_samples) / 4; + + // Check for overrun + if(!pmt_is_dict(properties)) { + std::cout << "[TEST_USRP_INBAND_OVERRUN] Recv samples dictionary is improper\n"; + return; + } + + if(pmt_t overrun = pmt_dict_ref(properties, + pmt_intern("overrun"), + PMT_NIL)) { + if(pmt_eqv(overrun, PMT_T)) { + d_n_overruns++; + + if(verbose && 0) + std::cout << "[TEST_USRP_INBAND_OVERRUN] Underrun\n"; + } + else { + if(verbose && 0) + std::cout << "[TEST_USRP_INBAND_OVERRUN] No overrun\n" << overrun <<std::endl; + } + } else { + + if(verbose && 0) + std::cout << "[TEST_USRP_INBAND_OVERRUN] No overrun\n"; + } + + // Check if the number samples we have received meets the test + if(d_samples_recvd >= d_samples_to_recv) { + d_rx->send(s_cmd_stop_recv_raw_samples, pmt_list2(PMT_NIL, d_rx_chan)); + enter_closing_channel(); + return; + } + +} + +void +test_usrp_rx::enter_closing_channel() +{ + d_state = CLOSING_CHANNEL; + + sleep(2); + + d_rx->send(s_cmd_deallocate_channel, pmt_list2(PMT_NIL, d_rx_chan)); + + if(verbose) + std::cout << "[TEST_USRP_INBAND_OVERRUN] Deallocating RX channel\n"; +} + +REGISTER_MBLOCK_CLASS(test_usrp_rx); + + +// ---------------------------------------------------------------- + +int +main (int argc, char **argv) +{ + // handle any command line args here + + mb_runtime_sptr rt = mb_make_runtime(); + pmt_t result = PMT_NIL; + + rt->run("top", "test_usrp_rx", PMT_F, &result); +} diff --git a/usrp/limbo/apps-inband/test_usrp_inband_ping.cc b/usrp/limbo/apps-inband/test_usrp_inband_ping.cc new file mode 100644 index 000000000..d779c9a64 --- /dev/null +++ b/usrp/limbo/apps-inband/test_usrp_inband_ping.cc @@ -0,0 +1,374 @@ +/* -*- c++ -*- */ +/* + * Copyright 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 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 <mblock/mblock.h> +#include <mblock/runtime.h> +#include <mblock/protocol_class.h> +#include <mblock/exception.h> +#include <mblock/msg_queue.h> +#include <mblock/message.h> +#include <mblock/msg_accepter.h> +#include <mblock/class_registry.h> +#include <pmt.h> +#include <stdio.h> +#include <string.h> +#include <sys/time.h> +#include <iostream> + +// Include the symbols needed for communication with USRP server +#include <symbols_usrp_server_cs.h> +#include <symbols_usrp_channel.h> +#include <symbols_usrp_low_level_cs.h> +#include <symbols_usrp_tx.h> +#include <symbols_usrp_rx.h> + +static bool verbose = false; + +class test_usrp_inband_ping : public mb_mblock +{ + + mb_port_sptr d_tx; // Ports connected to the USRP server + mb_port_sptr d_rx; + mb_port_sptr d_cs; + + pmt_t d_tx_chan; // Returned channel from TX allocation + pmt_t d_rx_chan; // Returned channel from RX allocation + + pmt_t d_which_usrp; // The USRP to use for the test + + long d_warm_msgs; // The number of messages to 'warm' the USRP + long d_warm_recvd; // The number of msgs received in the 'warm' state + + // Keep track of current state + enum state_t { + INIT, + OPENING_USRP, + ALLOCATING_CHANNELS, + WARMING_USRP, + PINGING, + CLOSING_CHANNELS, + CLOSING_USRP, + }; + state_t d_state; + + public: + test_usrp_inband_ping(mb_runtime *runtime, const std::string &instance_name, pmt_t user_arg); + ~test_usrp_inband_ping(); + void initial_transition(); + void handle_message(mb_message_sptr msg); + + protected: + void opening_usrp(); + void allocating_channels(); + void enter_warming_usrp(); + void enter_pinging(); + void build_and_send_ping(); + void closing_channels(); + void closing_usrp(); +}; + + +int +main (int argc, char **argv) +{ + // handle any command line args here + + mb_runtime_sptr rt = mb_make_runtime(); + pmt_t result = PMT_NIL; + + rt->run("top", "test_usrp_inband_ping", PMT_F, &result); +} + + +test_usrp_inband_ping::test_usrp_inband_ping(mb_runtime *runtime, const std::string &instance_name, pmt_t user_arg) + : mb_mblock(runtime, instance_name, user_arg), + d_tx_chan(PMT_NIL), + d_rx_chan(PMT_NIL), + d_which_usrp(pmt_from_long(0)), + d_state(INIT) +{ + + // A dictionary is used to pass parameters to the USRP + pmt_t usrp_dict = pmt_make_dict(); + + // Specify the RBF to use + pmt_dict_set(usrp_dict, + pmt_intern("rbf"), + pmt_intern("fixed1.rbf")); + + // Set TX and RX interpolations + pmt_dict_set(usrp_dict, + pmt_intern("interp-tx"), + pmt_from_long(128)); + + pmt_dict_set(usrp_dict, + pmt_intern("decim-rx"), + pmt_from_long(16)); + + d_tx = define_port("tx0", "usrp-tx", false, mb_port::INTERNAL); + d_rx = define_port("rx0", "usrp-rx", false, mb_port::INTERNAL); + d_cs = define_port("cs", "usrp-server-cs", false, mb_port::INTERNAL); + + // Create an instance of USRP server and connect ports + define_component("server", "usrp_server", usrp_dict); + connect("self", "tx0", "server", "tx0"); + connect("self", "rx0", "server", "rx0"); + connect("self", "cs", "server", "cs"); + +} + +test_usrp_inband_ping::~test_usrp_inband_ping() +{ +} + +void +test_usrp_inband_ping::initial_transition() +{ + opening_usrp(); +} + +// Handle message reads all incoming messages from USRP server which will be +// initialization and ping responses. We perform actions based on the current +// state and the event (ie, ping response) +void +test_usrp_inband_ping::handle_message(mb_message_sptr msg) +{ + pmt_t event = msg->signal(); + pmt_t data = msg->data(); + pmt_t port_id = msg->port_id(); + + pmt_t handle = PMT_F; + pmt_t status = PMT_F; + std::string error_msg; + + // Dispatch based on state + switch(d_state) { + + //----------------------------- OPENING_USRP ----------------------------// + // We only expect a response from opening the USRP which should be succesful + // or failed. + case OPENING_USRP: + + if(pmt_eq(event, s_response_open)) { + + status = pmt_nth(1, data); // failed/succes + + if(pmt_eq(status, PMT_T)) { + allocating_channels(); + return; + } + else { + error_msg = "failed to open usrp:"; + goto bail; + } + + } + + goto unhandled; // all other messages not handled in this state + + + //----------------------- ALLOCATING CHANNELS --------------------// + // When allocating channels, we need to wait for 2 responses from + // USRP server: one for TX and one for RX. Both are initialized to + // NIL so we know to continue to the next state once both are set. + case ALLOCATING_CHANNELS: + + // A TX allocation response + if(pmt_eq(event, s_response_allocate_channel) + && pmt_eq(d_tx->port_symbol(), port_id)) + { + status = pmt_nth(1, data); + + // If successful response, extract the channel + if(pmt_eq(status, PMT_T)) { + + d_tx_chan = pmt_nth(2, data); + + if(verbose) + std::cout << "[TEST_USRP_INBAND_PING] Received TX allocation" + << " on channel " << d_tx_chan << std::endl; + + // If the RX has also been allocated already, we can continue + if(!pmt_eqv(d_rx_chan, PMT_NIL)) + enter_warming_usrp(); + + return; + } + else { // TX allocation failed + error_msg = "failed to allocate TX channel:"; + goto bail; + } + } + + // A RX allocation response + if(pmt_eq(event, s_response_allocate_channel) + && pmt_eq(d_rx->port_symbol(), port_id)) + { + status = pmt_nth(1, data); + + // If successful response, extract the channel + if(pmt_eq(status, PMT_T)) { + + d_rx_chan = pmt_nth(2, data); + + if(verbose) + std::cout << "[TEST_USRP_INBAND_PING] Received RX allocation" + << " on channel " << d_rx_chan << std::endl; + + // If the TX has also been allocated already, we can continue + if(!pmt_eqv(d_tx_chan, PMT_NIL)) + enter_warming_usrp(); + + return; + } + else { // RX allocation failed + error_msg = "failed to allocate RX channel:"; + goto bail; + } + } + + goto unhandled; + + //----------------------- WARMING USRP --------------------// + // The FX2 seems to need some amount of data to be buffered + // before it begins reading. We use this state to simply + // warm up the USRP before benchmarking pings. + case WARMING_USRP: + + // We really don't care about the responses from the + // control channel in the warming stage, but once we receive + // the proper number of responses we switch states. + if(pmt_eq(event, s_response_from_control_channel) + && pmt_eq(d_rx->port_symbol(), port_id)) + { + d_warm_recvd++; + + if(d_warm_recvd > d_warm_msgs) + enter_pinging(); + + return; + } + + goto unhandled; + + case PINGING: + goto unhandled; + + case CLOSING_CHANNELS: + goto unhandled; + + case CLOSING_USRP: + goto unhandled; + + case INIT: + goto unhandled; + + } + + // An error occured, print it, and shutdown all m-blocks + bail: + std::cerr << error_msg << data + << "status = " << status << std::endl; + shutdown_all(PMT_F); + return; + + // Received an unhandled message for a specific state + unhandled: + if(verbose) + std::cout << "test_usrp_inband_tx: unhandled msg: " << msg + << "in state "<< d_state << std::endl; + +} + + +// Sends a command to USRP server to open up a connection to the +// specified USRP, which is defaulted to USRP 0 on the system +void +test_usrp_inband_ping::opening_usrp() +{ + + if(verbose) + std::cout << "[TEST_USRP_INBAND_PING] Opening USRP " + << d_which_usrp << std::endl; + + d_cs->send(s_cmd_open, pmt_list2(PMT_NIL, d_which_usrp)); + d_state = OPENING_USRP; +} + +// RX and TX channels must be allocated so that the USRP server can +// properly share bandwidth across multiple USRPs. No commands will be +// successful to the USRP through the USRP server on the TX or RX channels until +// a bandwidth allocation has been received. +void +test_usrp_inband_ping::allocating_channels() +{ + d_state = ALLOCATING_CHANNELS; + + long capacity = (long) 16e6; + d_tx->send(s_cmd_allocate_channel, pmt_list2(PMT_T, pmt_from_long(capacity))); + d_rx->send(s_cmd_allocate_channel, pmt_list2(PMT_T, pmt_from_long(capacity))); +} + +// The USRP needs some amount of initial data to pass a buffering point such +// that it begins to pull and read data from the FX2. We send an arbitrary +// amount of data to start the pipeline, which are just pings. +void +test_usrp_inband_ping::enter_warming_usrp() +{ + d_state = WARMING_USRP; + + for(int i=0; i < d_warm_msgs; i++) + build_and_send_ping(); +} + +void +test_usrp_inband_ping::enter_pinging() +{ + d_state = PINGING; + + if(verbose) + std::cout << "[TEST_USRP_INBAND_PING] Running ping tests\n"; + +} + +// Pings are sent over the TX channel using the signal 'cmd-to-control-channel' +// to the USRP server. Within this message there can be infinite subpackets +// stored as a list (the second parameter) and sent. The only subpacket we send +// is a ping, interpreted by the 'op-ping-fixed' signal. +void +test_usrp_inband_ping::build_and_send_ping() +{ + + d_tx->send(s_cmd_to_control_channel, // USRP server signal + pmt_list2(PMT_NIL, // invocation handle + pmt_list1(pmt_list3(s_op_ping_fixed, + pmt_from_long(0), + pmt_from_long(0))))); + + if(verbose) + std::cout << "[TEST_USRP_INBAND_PING] Ping!!" << std::endl; +} + +REGISTER_MBLOCK_CLASS(test_usrp_inband_ping); diff --git a/usrp/limbo/apps-inband/test_usrp_inband_registers.cc b/usrp/limbo/apps-inband/test_usrp_inband_registers.cc new file mode 100644 index 000000000..d9bd2db17 --- /dev/null +++ b/usrp/limbo/apps-inband/test_usrp_inband_registers.cc @@ -0,0 +1,435 @@ +/* -*- c++ -*- */ +/* + * Copyright 2007 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 <mblock/mblock.h> +#include <mblock/runtime.h> +#include <mblock/protocol_class.h> +#include <mblock/exception.h> +#include <mblock/msg_queue.h> +#include <mblock/message.h> +//#include <mb_mblock_impl.h> +#include <mblock/msg_accepter.h> +#include <mblock/class_registry.h> +#include <pmt.h> +#include <stdio.h> +#include <string.h> +#include <sys/time.h> +#include <iostream> + +// Include the symbols needed for communication with USRP server +#include <symbols_usrp_server_cs.h> +#include <symbols_usrp_channel.h> +#include <symbols_usrp_low_level_cs.h> +#include <symbols_usrp_tx.h> +#include <symbols_usrp_rx.h> + +static bool verbose = true; + +class test_usrp_inband_registers : public mb_mblock +{ + + mb_port_sptr d_tx; // Ports connected to the USRP server + mb_port_sptr d_rx; + mb_port_sptr d_cs; + + pmt_t d_tx_chan; // Returned channel from TX allocation + pmt_t d_rx_chan; // Returned channel from RX allocation + + pmt_t d_which_usrp; // The USRP to use for the test + + long d_warm_msgs; // The number of messages to 'warm' the USRP + long d_warm_recvd; // The number of msgs received in the 'warm' state + + // Keep track of current state + enum state_t { + INIT, + OPENING_USRP, + ALLOCATING_CHANNELS, + WRITE_REGISTER, + READ_REGISTER, + CLOSING_CHANNELS, + CLOSING_USRP, + }; + state_t d_state; + + public: + test_usrp_inband_registers(mb_runtime *runtime, const std::string &instance_name, pmt_t user_arg); + ~test_usrp_inband_registers(); + void initial_transition(); + void handle_message(mb_message_sptr msg); + + protected: + void opening_usrp(); + void allocating_channels(); + void write_register(); + void read_register(); + void closing_channels(); + void closing_usrp(); + void enter_receiving(); + void build_and_send_ping(); +}; + + +int +main (int argc, char **argv) +{ + // handle any command line args here + + mb_runtime_sptr rt = mb_make_runtime(); + pmt_t result = PMT_NIL; + + rt->run("top", "test_usrp_inband_registers", PMT_F, &result); +} + + +test_usrp_inband_registers::test_usrp_inband_registers(mb_runtime *runtime, const std::string &instance_name, pmt_t user_arg) + : mb_mblock(runtime, instance_name, user_arg), + d_tx_chan(PMT_NIL), + d_rx_chan(PMT_NIL), + d_which_usrp(pmt_from_long(0)), + d_state(INIT) +{ + + // A dictionary is used to pass parameters to the USRP + pmt_t usrp_dict = pmt_make_dict(); + + // Specify the RBF to use + pmt_dict_set(usrp_dict, + pmt_intern("rbf"), + pmt_intern("inband_1rxhb_1tx.rbf")); + + // Set TX and RX interpolations + pmt_dict_set(usrp_dict, + pmt_intern("interp-tx"), + pmt_from_long(128)); + + pmt_dict_set(usrp_dict, + pmt_intern("decim-rx"), + pmt_from_long(16)); + + d_tx = define_port("tx0", "usrp-tx", false, mb_port::INTERNAL); + d_rx = define_port("rx0", "usrp-rx", false, mb_port::INTERNAL); + d_cs = define_port("cs", "usrp-server-cs", false, mb_port::INTERNAL); + + // Create an instance of USRP server and connect ports + define_component("server", "usrp_server", usrp_dict); + connect("self", "tx0", "server", "tx0"); + connect("self", "rx0", "server", "rx0"); + connect("self", "cs", "server", "cs"); + +} + +test_usrp_inband_registers::~test_usrp_inband_registers() +{ +} + +void +test_usrp_inband_registers::initial_transition() +{ + opening_usrp(); +} + +// Handle message reads all incoming messages from USRP server which will be +// initialization and ping responses. We perform actions based on the current +// state and the event (ie, ping response) +void +test_usrp_inband_registers::handle_message(mb_message_sptr msg) +{ + pmt_t event = msg->signal(); + pmt_t data = msg->data(); + pmt_t port_id = msg->port_id(); + + pmt_t handle = PMT_F; + pmt_t status = PMT_F; + std::string error_msg; + + // Dispatch based on state + switch(d_state) { + + //----------------------------- OPENING_USRP ----------------------------// + // We only expect a response from opening the USRP which should be succesful + // or failed. + case OPENING_USRP: + + if(pmt_eq(event, s_response_open)) { + + status = pmt_nth(1, data); // failed/succes + + if(pmt_eq(status, PMT_T)) { + allocating_channels(); + return; + } + else { + error_msg = "failed to open usrp:"; + goto bail; + } + + } + + goto unhandled; // all other messages not handled in this state + + + //----------------------- ALLOCATING CHANNELS --------------------// + // When allocating channels, we need to wait for 2 responses from + // USRP server: one for TX and one for RX. Both are initialized to + // NIL so we know to continue to the next state once both are set. + case ALLOCATING_CHANNELS: + + // A TX allocation response + if(pmt_eq(event, s_response_allocate_channel) + && pmt_eq(d_tx->port_symbol(), port_id)) + { + status = pmt_nth(1, data); + + // If successful response, extract the channel + if(pmt_eq(status, PMT_T)) { + + d_tx_chan = pmt_nth(2, data); + + if(verbose) + std::cout << "[TEST_USRP_INBAND_PING] Received TX allocation" + << " on channel " << d_tx_chan << std::endl; + + // If the RX has also been allocated already, we can continue + if(!pmt_eqv(d_rx_chan, PMT_NIL)) { + enter_receiving(); + write_register(); + } + + return; + } + else { // TX allocation failed + error_msg = "failed to allocate TX channel:"; + goto bail; + } + } + + // A RX allocation response + if(pmt_eq(event, s_response_allocate_channel) + && pmt_eq(d_rx->port_symbol(), port_id)) + { + status = pmt_nth(1, data); + + // If successful response, extract the channel + if(pmt_eq(status, PMT_T)) { + + d_rx_chan = pmt_nth(2, data); + + if(verbose) + std::cout << "[TEST_USRP_INBAND_PING] Received RX allocation" + << " on channel " << d_rx_chan << std::endl; + + // If the TX has also been allocated already, we can continue + if(!pmt_eqv(d_tx_chan, PMT_NIL)) { + enter_receiving(); + write_register(); + } + + return; + } + else { // RX allocation failed + error_msg = "failed to allocate RX channel:"; + goto bail; + } + } + + goto unhandled; + + //-------------------------- WRITE REGISTER ----------------------------// + // In the write register state, we do not expect to receive any messages + // since the write does not directly generate a response until the USRP + // responds. + case WRITE_REGISTER: + goto unhandled; + + //-------------------------- READ REGISTER ----------------------------// + // In the read register state, we only expect a read register response back + // that has the value we expect to have in it. We read the response, ensure + // that the read was successful and display the register value. + case READ_REGISTER: + + if(pmt_eq(event, s_response_from_control_channel) + && pmt_eq(d_tx->port_symbol(), port_id)) + { + status = pmt_nth(1, data); + + // If the read was successful, we extract the subpacket information + if(pmt_eq(status, PMT_T)) { + + pmt_t subp = pmt_nth(2, data); // subpacket should be the read reg reply + + pmt_t subp_sig = pmt_nth(0, subp); + pmt_t subp_data = pmt_nth(1, subp); + + if(!pmt_eqv(subp_sig, s_op_read_reg_reply)) { + error_msg = "received improper subpacket when expecting reg reply."; + goto bail; + } + + pmt_t rid = pmt_nth(0, subp_data); + pmt_t reg_num = pmt_nth(1, subp_data); + pmt_t reg_val = pmt_nth(2, subp_data); + + if(verbose) + std::cout << "[TEST_USRP_INBAND_REGISTERS] Received read reg reply " + << "(" + << "RID: " << rid << ", " + << "Reg: " << reg_num << ", " + << "Val: " << reg_val + << ")\n"; + + // read_register(); FIX ME STATE TRANSITION + return; + + } else { // bail on unsuccessful write + error_msg = "failed to write to register."; + goto bail; + } + } + goto unhandled; + + case CLOSING_CHANNELS: + goto unhandled; + + case CLOSING_USRP: + goto unhandled; + + case INIT: + goto unhandled; + + } + + // An error occured, print it, and shutdown all m-blocks + bail: + std::cerr << error_msg << data + << "status = " << status << std::endl; + shutdown_all(PMT_F); + return; + + // Received an unhandled message for a specific state + unhandled: + if(verbose && !pmt_eq(event, s_response_recv_raw_samples)) + std::cout << "test_usrp_inband_tx: unhandled msg: " << msg + << "in state "<< d_state << std::endl; + +} + + +// Sends a command to USRP server to open up a connection to the +// specified USRP, which is defaulted to USRP 0 on the system +void +test_usrp_inband_registers::opening_usrp() +{ + + if(verbose) + std::cout << "[TEST_USRP_INBAND_PING] Opening USRP " + << d_which_usrp << std::endl; + + d_cs->send(s_cmd_open, pmt_list2(PMT_NIL, d_which_usrp)); + d_state = OPENING_USRP; +} + +// RX and TX channels must be allocated so that the USRP server can +// properly share bandwidth across multiple USRPs. No commands will be +// successful to the USRP through the USRP server on the TX or RX channels until +// a bandwidth allocation has been received. +void +test_usrp_inband_registers::allocating_channels() +{ + d_state = ALLOCATING_CHANNELS; + + long capacity = (long) 16e6; + d_tx->send(s_cmd_allocate_channel, pmt_list2(PMT_T, pmt_from_long(capacity))); + d_rx->send(s_cmd_allocate_channel, pmt_list2(PMT_T, pmt_from_long(capacity))); +} + +// After allocating the channels, a write register command will be sent to the +// USRP. +void +test_usrp_inband_registers::write_register() +{ + d_state = WRITE_REGISTER; + + long reg = 0; + + d_tx->send(s_cmd_to_control_channel, // C/S packet + pmt_list2(PMT_NIL, // invoc handle + pmt_list1( + pmt_list2(s_op_write_reg, + pmt_list2( + pmt_from_long(reg), + pmt_from_long(0xbeef)))))); + + if(verbose) + std::cout << "[TEST_USRP_INBAND_REGISTERS] Writing 0xbeef to " + << reg << std::endl; + + read_register(); // immediately transition to read the register +} + +// Temporary: for testing pings +void +test_usrp_inband_registers::build_and_send_ping() +{ + + d_tx->send(s_cmd_to_control_channel, + pmt_list2(PMT_NIL, pmt_list1(pmt_list2(s_op_ping_fixed, + pmt_list2(pmt_from_long(0), + pmt_from_long(0)))))); + + std::cout << "[TEST_USRP_INBAND_CS] Ping sent" << std::endl; +} + +// After writing to the register, we want to read the value back and ensure that +// it is the same value that we wrote. +void +test_usrp_inband_registers::read_register() +{ + d_state = READ_REGISTER; + + long reg = 9; + + d_tx->send(s_cmd_to_control_channel, // C/S packet + pmt_list2(PMT_NIL, // invoc handle + pmt_list1( + pmt_list2(s_op_read_reg, + pmt_list2( + pmt_from_long(0), // rid + pmt_from_long(reg)))))); + if(verbose) + std::cout << "[TEST_USRP_INBAND_REGISTERS] Reading from register " + << reg << std::endl; +} + +// Used to enter the receiving state +void +test_usrp_inband_registers::enter_receiving() +{ + d_rx->send(s_cmd_start_recv_raw_samples, + pmt_list2(PMT_F, + d_rx_chan)); +} + +REGISTER_MBLOCK_CLASS(test_usrp_inband_registers); diff --git a/usrp/limbo/apps-inband/test_usrp_inband_rx.cc b/usrp/limbo/apps-inband/test_usrp_inband_rx.cc new file mode 100644 index 000000000..4f21e4afc --- /dev/null +++ b/usrp/limbo/apps-inband/test_usrp_inband_rx.cc @@ -0,0 +1,362 @@ +/* -*- c++ -*- */ +/* + * Copyright 2007 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 <mblock/mblock.h> +#include <mblock/runtime.h> +#include <mblock/protocol_class.h> +#include <mblock/exception.h> +#include <mblock/msg_queue.h> +#include <mblock/message.h> +#include <mblock/msg_accepter.h> +#include <mblock/class_registry.h> +#include <pmt.h> +#include <stdio.h> +#include <string.h> +#include <iostream> +#include <fstream> + +// Include the symbols needed for communication with USRP server +#include <symbols_usrp_server_cs.h> +#include <symbols_usrp_channel.h> +#include <symbols_usrp_low_level_cs.h> +#include <symbols_usrp_rx.h> + +static bool verbose = true; + +class test_usrp_rx : public mb_mblock +{ + mb_port_sptr d_rx; + mb_port_sptr d_cs; + pmt_t d_rx_chan; // returned tx channel handle + + enum state_t { + INIT, + OPENING_USRP, + ALLOCATING_CHANNEL, + RECEIVING, + CLOSING_CHANNEL, + CLOSING_USRP, + }; + + state_t d_state; + + std::ofstream d_ofile; + + long d_samples_recvd; + long d_samples_to_recv; + + public: + test_usrp_rx(mb_runtime *runtime, const std::string &instance_name, pmt_t user_arg); + ~test_usrp_rx(); + void initial_transition(); + void handle_message(mb_message_sptr msg); + + protected: + void open_usrp(); + void close_usrp(); + void allocate_channel(); + void send_packets(); + void enter_receiving(); + void build_and_send_next_frame(); + void handle_response_recv_raw_samples(pmt_t invocation_handle); + void enter_closing_channel(); +}; + +test_usrp_rx::test_usrp_rx(mb_runtime *runtime, const std::string &instance_name, pmt_t user_arg) + : mb_mblock(runtime, instance_name, user_arg), + d_samples_recvd(0), + d_samples_to_recv(20e6) +{ + d_rx = define_port("rx0", "usrp-rx", false, mb_port::INTERNAL); + d_cs = define_port("cs", "usrp-server-cs", false, mb_port::INTERNAL); + + // Pass a dictionary to usrp_server which specifies which interface to use, the stub or USRP + pmt_t usrp_dict = pmt_make_dict(); + + // To test the application without a USRP + bool fake_usrp_p = false; + if(fake_usrp_p) { + pmt_dict_set(usrp_dict, + pmt_intern("fake-usrp"), + PMT_T); + } + + // Specify the RBF to use + pmt_dict_set(usrp_dict, + pmt_intern("rbf"), + pmt_intern("inband_1rxhb_1tx.rbf")); + + pmt_dict_set(usrp_dict, + pmt_intern("decim-rx"), + pmt_from_long(64)); + +// If unspecified, chooses center frequency from range +// pmt_dict_set(usrp_dict, +// pmt_intern("rf-freq"), +// pmt_from_long(10e6)); + + define_component("server", "usrp_server", usrp_dict); + + connect("self", "rx0", "server", "rx0"); + connect("self", "cs", "server", "cs"); + +} + +test_usrp_rx::~test_usrp_rx() +{ +} + +void +test_usrp_rx::initial_transition() +{ + open_usrp(); +} + +void +test_usrp_rx::handle_message(mb_message_sptr msg) +{ + pmt_t event = msg->signal(); + pmt_t data = msg->data(); + + pmt_t handle = PMT_F; + pmt_t status = PMT_F; + std::string error_msg; + + switch(d_state){ + + //----------------------------- OPENING_USRP ----------------------------// + // We only expect a response from opening the USRP which should be succesful + // or failed. + case OPENING_USRP: + if (pmt_eq(event, s_response_open)){ + status = pmt_nth(1, data); + if (pmt_eq(status, PMT_T)){ + allocate_channel(); + return; + } + else { + error_msg = "failed to open usrp:"; + goto bail; + } + } + goto unhandled; + + //----------------------- ALLOCATING CHANNELS --------------------// + // Allocate an RX channel to perform the overrun test. + case ALLOCATING_CHANNEL: + if (pmt_eq(event, s_response_allocate_channel)){ + status = pmt_nth(1, data); + d_rx_chan = pmt_nth(2, data); + + if (pmt_eq(status, PMT_T)){ + enter_receiving(); + return; + } + else { + error_msg = "failed to allocate channel:"; + goto bail; + } + } + goto unhandled; + + //--------------------------- RECEIVING ------------------------------// + // In the receiving state, we receive samples until the specified amount + // while counting the number of overruns. + case RECEIVING: + if (pmt_eq(event, s_response_recv_raw_samples)){ + status = pmt_nth(1, data); + + if (pmt_eq(status, PMT_T)){ + handle_response_recv_raw_samples(data); + return; + } + else { + error_msg = "bad response-xmit-raw-frame:"; + goto bail; + } + } + goto unhandled; + + //------------------------- CLOSING CHANNEL ----------------------------// + // Check deallocation response for the RX channel + case CLOSING_CHANNEL: + if (pmt_eq(event, s_response_deallocate_channel)){ + status = pmt_nth(1, data); + + if (pmt_eq(status, PMT_T)){ + close_usrp(); + return; + } + else { + error_msg = "failed to deallocate channel:"; + goto bail; + } + } + + // Alternately, we ignore all response recv samples while waiting for the + // channel to actually close + if (pmt_eq(event, s_response_recv_raw_samples)) + return; + + goto unhandled; + + //--------------------------- CLOSING USRP ------------------------------// + // Once we have received a successful USRP close response, we shutdown all + // mblocks and exit. + case CLOSING_USRP: + if (pmt_eq(event, s_response_close)){ + status = pmt_nth(1, data); + + if (pmt_eq(status, PMT_T)){ + fflush(stdout); + shutdown_all(PMT_T); + return; + } + else { + error_msg = "failed to close USRP:"; + goto bail; + } + } + goto unhandled; + + default: + goto unhandled; + } + return; + + // An error occured, print it, and shutdown all m-blocks + bail: + std::cerr << error_msg << data + << "status = " << status << std::endl; + shutdown_all(PMT_F); + return; + + // Received an unhandled message for a specific state + unhandled: + if(verbose && !pmt_eq(event, pmt_intern("%shutdown"))) + std::cout << "test_usrp_inband_tx: unhandled msg: " << msg + << "in state "<< d_state << std::endl; +} + + +void +test_usrp_rx::open_usrp() +{ + pmt_t which_usrp = pmt_from_long(0); + + d_cs->send(s_cmd_open, pmt_list2(PMT_NIL, which_usrp)); + d_state = OPENING_USRP; + + if(verbose) + std::cout << "[TEST_USRP_INBAND_RX] Opening the USRP\n"; +} + +void +test_usrp_rx::close_usrp() +{ + + d_cs->send(s_cmd_close, pmt_list1(PMT_NIL)); + d_state = CLOSING_USRP; + + if(verbose) + std::cout << "[TEST_USRP_INBAND_RX] Closing the USRP\n"; +} + +void +test_usrp_rx::allocate_channel() +{ + long capacity = (long) 16e6; + d_rx->send(s_cmd_allocate_channel, pmt_list2(PMT_T, pmt_from_long(capacity))); + d_state = ALLOCATING_CHANNEL; + + if(verbose) + std::cout << "[TEST_USRP_INBAND_RX] Requesting RX channel allocation\n"; +} + +void +test_usrp_rx::enter_receiving() +{ + d_state = RECEIVING; + + d_rx->send(s_cmd_start_recv_raw_samples, + pmt_list2(PMT_F, + d_rx_chan)); + + if(verbose) + std::cout << "[TEST_USRP_INBAND_RX] Receiving...\n"; +} + +void +test_usrp_rx::handle_response_recv_raw_samples(pmt_t data) +{ + pmt_t invocation_handle = pmt_nth(0, data); + pmt_t status = pmt_nth(1, data); + pmt_t v_samples = pmt_nth(2, data); + pmt_t timestamp = pmt_nth(3, data); + pmt_t channel = pmt_nth(4, data); + pmt_t properties = pmt_nth(5, data); + + d_samples_recvd += pmt_length(v_samples) / 4; + + // Check for overrun + if(!pmt_is_dict(properties)) { + std::cout << "[TEST_USRP_INBAND_RX] Recv samples dictionary is improper\n"; + return; + } + + // Check if the number samples we have received meets the test + if(d_samples_recvd >= d_samples_to_recv) { + d_rx->send(s_cmd_stop_recv_raw_samples, pmt_list2(PMT_NIL, d_rx_chan)); + enter_closing_channel(); + return; + } + +} + +void +test_usrp_rx::enter_closing_channel() +{ + d_state = CLOSING_CHANNEL; + + d_rx->send(s_cmd_deallocate_channel, pmt_list2(PMT_NIL, d_rx_chan)); + + if(verbose) + std::cout << "[TEST_USRP_INBAND_RX] Deallocating RX channel\n"; +} + +REGISTER_MBLOCK_CLASS(test_usrp_rx); + + +// ---------------------------------------------------------------- + +int +main (int argc, char **argv) +{ + mb_runtime_sptr rt = mb_make_runtime(); + pmt_t result = PMT_NIL; + + rt->run("top", "test_usrp_rx", PMT_F, &result); + +} diff --git a/usrp/limbo/apps-inband/test_usrp_inband_timestamps.cc b/usrp/limbo/apps-inband/test_usrp_inband_timestamps.cc new file mode 100644 index 000000000..3b874d1a5 --- /dev/null +++ b/usrp/limbo/apps-inband/test_usrp_inband_timestamps.cc @@ -0,0 +1,506 @@ +/* -*- c++ -*- */ +/* + * Copyright 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 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 <mblock/mblock.h> +#include <mblock/runtime.h> +#include <mblock/protocol_class.h> +#include <mblock/exception.h> +#include <mblock/msg_queue.h> +#include <mblock/message.h> +#include <mblock/msg_accepter.h> +#include <mblock/class_registry.h> +#include <pmt.h> +#include <stdio.h> +#include <string.h> +#include <sys/time.h> +#include <iostream> + +#include <ui_nco.h> +#include <symbols_usrp_server_cs.h> +#include <symbols_usrp_channel.h> +#include <symbols_usrp_low_level_cs.h> +#include <symbols_usrp_tx.h> +#include <symbols_usrp_rx.h> + +#define NBPING 10 + +static bool verbose = true; +bool bskip = false; +long bstep = 10000; +long bcurr = 0; +long incr = 0x500; +long ptime = 0x000; + +class test_usrp_inband_timestamps : public mb_mblock +{ + mb_port_sptr d_tx; + mb_port_sptr d_rx; + mb_port_sptr d_cs; + pmt_t d_tx_chan; // returned tx channel handle + pmt_t d_rx_chan; // returned tx channel handle + + struct timeval times[NBPING]; + + enum state_t { + INIT, + OPENING_USRP, + ALLOCATING_CHANNEL, + TRANSMITTING, + CLOSING_CHANNEL, + CLOSING_USRP, + }; + + state_t d_state; + long d_nsamples_to_send; + long d_nsamples_xmitted; + long d_nframes_xmitted; + long d_samples_per_frame; + bool d_done_sending; + + // for generating sine wave output + ui_nco<float,float> d_nco; + double d_amplitude; + + public: + test_usrp_inband_timestamps(mb_runtime *runtime, const std::string &instance_name, pmt_t user_arg); + ~test_usrp_inband_timestamps(); + void initial_transition(); + void handle_message(mb_message_sptr msg); + + protected: + void open_usrp(); + void close_usrp(); + void allocate_channel(); + void send_packets(); + void enter_receiving(); + void enter_transmitting(); + void build_and_send_ping(); + void build_and_send_next_frame(); + void handle_xmit_response(pmt_t invocation_handle); + void enter_closing_channel(); +}; + +test_usrp_inband_timestamps::test_usrp_inband_timestamps(mb_runtime *runtime, const std::string &instance_name, pmt_t user_arg) + : mb_mblock(runtime, instance_name, user_arg), + d_tx_chan(PMT_NIL), + d_rx_chan(PMT_NIL), + d_state(INIT), d_nsamples_to_send((long) 40e6), + d_nsamples_xmitted(0), + d_nframes_xmitted(0), + //d_samples_per_frame((long)(126)), + d_samples_per_frame((long)(126 * 2)), // non-full packet + //d_samples_per_frame((long)(126 * 3.5)), // non-full packet + //d_samples_per_frame((long)(126 * 4)), // full packet + d_done_sending(false), + d_amplitude(16384) +{ + if(verbose) + std::cout << "[TEST_USRP_INBAND_TIMESTAMPS] Initializing...\n"; + + d_tx = define_port("tx0", "usrp-tx", false, mb_port::INTERNAL); + d_rx = define_port("rx0", "usrp-rx", false, mb_port::INTERNAL); + d_cs = define_port("cs", "usrp-server-cs", false, mb_port::INTERNAL); + + bool fake_usrp_p = false; + + // Test the TX side + + pmt_t usrp_dict = pmt_make_dict(); + + if(fake_usrp_p) { + pmt_dict_set(usrp_dict, + pmt_intern("fake-usrp"), + PMT_T); + } + + // Set TX and RX interpolations + pmt_dict_set(usrp_dict, + pmt_intern("interp-tx"), + pmt_from_long(128)); + + pmt_dict_set(usrp_dict, + pmt_intern("decim-rx"), + pmt_from_long(16)); + + // Specify the RBF to use + pmt_dict_set(usrp_dict, + pmt_intern("rbf"), + pmt_intern("inband_1rxhb_1tx.rbf")); + + define_component("server", "usrp_server", usrp_dict); + + connect("self", "tx0", "server", "tx0"); + connect("self", "rx0", "server", "rx0"); + connect("self", "cs", "server", "cs"); + + // initialize NCO + double freq = 100e3; + int interp = 32; // 32 -> 4MS/s + double sample_rate = 128e6 / interp; + d_nco.set_freq(2*M_PI * freq/sample_rate); + +} + +test_usrp_inband_timestamps::~test_usrp_inband_timestamps() +{ +} + +void +test_usrp_inband_timestamps::initial_transition() +{ + open_usrp(); +} + +void +test_usrp_inband_timestamps::handle_message(mb_message_sptr msg) +{ + pmt_t event = msg->signal(); + pmt_t data = msg->data(); + pmt_t port_id = msg->port_id(); + + pmt_t handle = PMT_F; + pmt_t status = PMT_F; + std::string error_msg; + + //std::cout << msg << std::endl; + + switch(d_state){ + case OPENING_USRP: + if (pmt_eq(event, s_response_open)){ + status = pmt_nth(1, data); + if (pmt_eq(status, PMT_T)){ + allocate_channel(); + return; + } + else { + error_msg = "failed to open usrp:"; + goto bail; + } + } + goto unhandled; + + case ALLOCATING_CHANNEL: + if (pmt_eq(event, s_response_allocate_channel)){ + + if(pmt_eq(d_tx->port_symbol(), port_id)) { + status = pmt_nth(1, data); + d_tx_chan = pmt_nth(2, data); + + if (pmt_eq(status, PMT_T)){ + + if(verbose) + std::cout << "[TEST_USRP_INBAND_TIMESTAMPS] Received allocation for TX\n"; + + if(!pmt_eqv(d_rx_chan, PMT_NIL)) { + enter_receiving(); + enter_transmitting(); + } + return; + } + else { + error_msg = "failed to allocate channel:"; + goto bail; + } + } + + if(pmt_eq(d_rx->port_symbol(), port_id)) { + status = pmt_nth(1, data); + d_rx_chan = pmt_nth(2, data); + + if (pmt_eq(status, PMT_T)){ + + if(verbose) + std::cout << "[TEST_USRP_INBAND_TIMESTAMPS] Received allocation for TX\n"; + + if(!pmt_eqv(d_tx_chan, PMT_NIL)) { + enter_receiving(); + enter_transmitting(); + } + return; + } + else { + error_msg = "failed to allocate channel:"; + goto bail; + } + } + } + goto unhandled; + + case TRANSMITTING: + if (pmt_eq(event, s_response_xmit_raw_frame)){ + handle = pmt_nth(0, data); + status = pmt_nth(1, data); + + if (pmt_eq(status, PMT_T)){ + handle_xmit_response(handle); + return; + } + else { + error_msg = "bad response-xmit-raw-frame:"; + goto bail; + } + } + + if (pmt_eq(event, s_response_from_control_channel)) { + std::cout << "ping response!\n"; + } + goto unhandled; + + case CLOSING_CHANNEL: + if (pmt_eq(event, s_response_deallocate_channel)){ + status = pmt_nth(1, data); + + if (pmt_eq(status, PMT_T)){ + close_usrp(); + return; + } + else { + error_msg = "failed to deallocate channel:"; + goto bail; + } + } + goto unhandled; + + case CLOSING_USRP: + if (pmt_eq(event, s_response_close)){ + status = pmt_nth(1, data); + + if (pmt_eq(status, PMT_T)){ + shutdown_all(PMT_T); + return; + } + else { + error_msg = "failed to close USRP:"; + goto bail; + } + } + goto unhandled; + + default: + goto unhandled; + } + return; + + bail: + std::cerr << error_msg << data + << "status = " << status << std::endl; + shutdown_all(PMT_F); + return; + + unhandled: + if(verbose && 0) + std::cout << "test_usrp_inband_tx: unhandled msg: " << msg + << "in state "<< d_state << std::endl; +} + + +void +test_usrp_inband_timestamps::open_usrp() +{ + pmt_t which_usrp = pmt_from_long(0); + + d_cs->send(s_cmd_open, pmt_list2(PMT_NIL, which_usrp)); + d_state = OPENING_USRP; +} + +void +test_usrp_inband_timestamps::close_usrp() +{ + d_cs->send(s_cmd_close, pmt_list1(PMT_NIL)); + d_state = CLOSING_USRP; + + if(verbose) + std::cout << "[TEST_USRP_INBAND_TIMESTAMPS] Closing USRP\n"; +} + +void +test_usrp_inband_timestamps::allocate_channel() +{ + long capacity = (long) 16e6; + d_tx->send(s_cmd_allocate_channel, pmt_list2(PMT_T, pmt_from_long(capacity))); + d_rx->send(s_cmd_allocate_channel, pmt_list2(PMT_T, pmt_from_long(capacity))); + d_state = ALLOCATING_CHANNEL; +} + +void +test_usrp_inband_timestamps::enter_receiving() +{ + d_rx->send(s_cmd_start_recv_raw_samples, + pmt_list2(PMT_F, + d_rx_chan)); +} + +void +test_usrp_inband_timestamps::enter_transmitting() +{ + d_state = TRANSMITTING; + d_nsamples_xmitted = 0; + + if(verbose) + std::cout << "[TEST_USRP_INBAND_TIMESTAMPS] Beginning transmission\n"; + + sleep(1); + + build_and_send_next_frame(); + build_and_send_next_frame(); + build_and_send_next_frame(); + build_and_send_next_frame(); + +} + +void +test_usrp_inband_timestamps::build_and_send_ping() +{ + + d_tx->send(s_cmd_to_control_channel, + pmt_list2(PMT_NIL, pmt_list1(pmt_list2(s_op_ping_fixed, + pmt_list2(pmt_from_long(0), + pmt_from_long(0)))))); + if(verbose && 0) + std::cout << "[TEST_USRP_INBAND_TIMESTAMPS] Ping sent" << std::endl; +} + +void +test_usrp_inband_timestamps::build_and_send_next_frame() +{ + // allocate the uniform vector for the samples + // FIXME perhaps hold on to this between calls + +#if 0 + long nsamples_this_frame = + std::min(d_nsamples_to_send - d_nsamples_xmitted, + d_samples_per_frame); +#else + long nsamples_this_frame = d_samples_per_frame; +#endif + + if (nsamples_this_frame == 0){ + d_done_sending = true; + return; + } + + + size_t nshorts = 2 * nsamples_this_frame; // 16-bit I & Q + pmt_t uvec = pmt_make_s16vector(nshorts, 0); + size_t ignore; + int16_t *samples = pmt_s16vector_writable_elements(uvec, ignore); + + // fill in the complex sinusoid + + for (int i = 0; i < nsamples_this_frame; i++){ + + if (1){ + gr_complex s; + d_nco.sincos(&s, 1, d_amplitude); + // write 16-bit i & q + samples[2*i] = (int16_t) s.real(); + samples[2*i+1] = (int16_t) s.imag(); + } + else { + gr_complex s(d_amplitude, d_amplitude); + + // write 16-bit i & q + samples[2*i] = (int16_t) s.real(); + samples[2*i+1] = (int16_t) s.imag(); + } + } + + pmt_t timestamp; + + if(bskip) { + timestamp = pmt_from_long(0x0); // throw away + bcurr++; + if(bcurr == bstep) { + bskip = false; + bcurr = 0; + } + } else { + timestamp = pmt_from_long(0xffffffff); // NOW + timestamp = pmt_from_long(ptime); + ptime += incr; + bcurr++; + if(bcurr == bstep) { + //bskip = true; + bcurr = 0; + } + } + + std::cout << bskip << " -- " << bcurr << std::endl; + + d_tx->send(s_cmd_xmit_raw_frame, + pmt_list4(pmt_from_long(d_nframes_xmitted), // invocation-handle + d_tx_chan, // channel + uvec, // the samples + timestamp)); + + d_nsamples_xmitted += nsamples_this_frame; + d_nframes_xmitted++; + + if(verbose && 0) + std::cout << "[TEST_USRP_INBAND_TIMESTAMPS] Transmitted frame\n"; + + //build_and_send_next_frame(); +} + + +void +test_usrp_inband_timestamps::handle_xmit_response(pmt_t handle) +{ + if (d_done_sending && + pmt_to_long(handle) == (d_nframes_xmitted - 1)){ + // We're done sending and have received all responses + enter_closing_channel(); + } + + build_and_send_next_frame(); + //build_and_send_ping(); +} + +void +test_usrp_inband_timestamps::enter_closing_channel() +{ + d_state = CLOSING_CHANNEL; + + d_tx->send(s_cmd_deallocate_channel, pmt_list2(PMT_NIL, d_tx_chan)); + + if(verbose) + std::cout << "[TEST_USRP_INBAND_TIMESTAMPS] Closing channel\n"; +} + +REGISTER_MBLOCK_CLASS(test_usrp_inband_timestamps); + + +// ---------------------------------------------------------------- + +int +main (int argc, char **argv) +{ + // handle any command line args here + + mb_runtime_sptr rt = mb_make_runtime(); + pmt_t result = PMT_NIL; + + rt->run("top", "test_usrp_inband_timestamps", PMT_F, &result); +} diff --git a/usrp/limbo/apps-inband/test_usrp_inband_tx.cc b/usrp/limbo/apps-inband/test_usrp_inband_tx.cc new file mode 100644 index 000000000..9f294e770 --- /dev/null +++ b/usrp/limbo/apps-inband/test_usrp_inband_tx.cc @@ -0,0 +1,411 @@ +/* -*- c++ -*- */ +/* + * Copyright 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 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 <mblock/mblock.h> +#include <mblock/runtime.h> +#include <mblock/protocol_class.h> +#include <mblock/exception.h> +#include <mblock/msg_queue.h> +#include <mblock/message.h> +#include <mblock/msg_accepter.h> +#include <mblock/class_registry.h> +#include <pmt.h> +#include <stdio.h> +#include <string.h> +#include <iostream> + +#include <ui_nco.h> +#include <symbols_usrp_server_cs.h> +#include <symbols_usrp_channel.h> +#include <symbols_usrp_low_level_cs.h> +#include <symbols_usrp_tx.h> + +static bool verbose = true; + +class test_usrp_tx : public mb_mblock +{ + mb_port_sptr d_tx; + mb_port_sptr d_cs; + pmt_t d_tx_chan; // returned tx channel handle + + enum state_t { + INIT, + OPENING_USRP, + ALLOCATING_CHANNEL, + TRANSMITTING, + CLOSING_CHANNEL, + CLOSING_USRP, + }; + + state_t d_state; + long d_nsamples_to_send; + long d_nsamples_xmitted; + long d_nframes_xmitted; + long d_samples_per_frame; + bool d_done_sending; + + // for generating sine wave output + ui_nco<float,float> d_nco; + double d_amplitude; + + public: + test_usrp_tx(mb_runtime *runtime, const std::string &instance_name, pmt_t user_arg); + ~test_usrp_tx(); + void initial_transition(); + void handle_message(mb_message_sptr msg); + + protected: + void open_usrp(); + void close_usrp(); + void allocate_channel(); + void send_packets(); + void enter_transmitting(); + void build_and_send_next_frame(); + void handle_xmit_response(pmt_t invocation_handle); + void enter_closing_channel(); +}; + +test_usrp_tx::test_usrp_tx(mb_runtime *runtime, const std::string &instance_name, pmt_t user_arg) + : mb_mblock(runtime, instance_name, user_arg), + d_state(INIT), d_nsamples_to_send((long) 80e6), + d_nsamples_xmitted(0), + d_nframes_xmitted(0), + d_samples_per_frame((long)(126 * 4)), // full packet + d_done_sending(false), + d_amplitude(16384) +{ + // std::cout << "[TEST_USRP_TX] Initializing...\n"; + + d_tx = define_port("tx0", "usrp-tx", false, mb_port::INTERNAL); + d_cs = define_port("cs", "usrp-server-cs", false, mb_port::INTERNAL); + + //bool fake_usrp_p = true; + bool fake_usrp_p = false; + + // Test the TX side + + pmt_t usrp_dict = pmt_make_dict(); + + if(fake_usrp_p) { + pmt_dict_set(usrp_dict, + pmt_intern("fake-usrp"), + PMT_T); + } + + // Specify the RBF to use + pmt_dict_set(usrp_dict, + pmt_intern("rbf"), + pmt_intern("inband_1rxhb_1tx.rbf")); + + // Set TX and RX interpolations + pmt_dict_set(usrp_dict, + pmt_intern("interp-tx"), + pmt_from_long(64)); + +// If unspecified, chooses center frequency from range +// pmt_dict_set(usrp_dict, +// pmt_intern("rf-freq"), +// pmt_from_long(10e6)); + + define_component("server", "usrp_server", usrp_dict); + + connect("self", "tx0", "server", "tx0"); + connect("self", "cs", "server", "cs"); + + // initialize NCO + double freq = 100e3; + int interp = 32; // 32 -> 4MS/s + double sample_rate = 128e6 / interp; + d_nco.set_freq(2*M_PI * freq/sample_rate); + + // FIXME need to somehow set the interp rate in the USRP. + // for now, we'll have the low-level code hardwire it. +} + +test_usrp_tx::~test_usrp_tx() +{ +} + +void +test_usrp_tx::initial_transition() +{ + open_usrp(); +} + +void +test_usrp_tx::handle_message(mb_message_sptr msg) +{ + pmt_t event = msg->signal(); + pmt_t data = msg->data(); + + pmt_t handle = PMT_F; + pmt_t status = PMT_F; + std::string error_msg; + + //std::cout << msg << std::endl; + + switch(d_state){ + case OPENING_USRP: + if (pmt_eq(event, s_response_open)){ + status = pmt_nth(1, data); + if (pmt_eq(status, PMT_T)){ + allocate_channel(); + return; + } + else { + error_msg = "failed to open usrp:"; + goto bail; + } + } + goto unhandled; + + case ALLOCATING_CHANNEL: + if (pmt_eq(event, s_response_allocate_channel)){ + status = pmt_nth(1, data); + d_tx_chan = pmt_nth(2, data); + + if (pmt_eq(status, PMT_T)){ + enter_transmitting(); + return; + } + else { + error_msg = "failed to allocate channel:"; + goto bail; + } + } + goto unhandled; + + case TRANSMITTING: + if (pmt_eq(event, s_response_xmit_raw_frame)){ + handle = pmt_nth(0, data); + status = pmt_nth(1, data); + + if (pmt_eq(status, PMT_T)){ + handle_xmit_response(handle); + return; + } + else { + error_msg = "bad response-xmit-raw-frame:"; + goto bail; + } + } + goto unhandled; + + case CLOSING_CHANNEL: + if (pmt_eq(event, s_response_deallocate_channel)){ + status = pmt_nth(1, data); + + if (pmt_eq(status, PMT_T)){ + close_usrp(); + return; + } + else { + error_msg = "failed to deallocate channel:"; + goto bail; + } + } + goto unhandled; + + case CLOSING_USRP: + if (pmt_eq(event, s_response_close)){ + status = pmt_nth(1, data); + + if (pmt_eq(status, PMT_T)){ + shutdown_all(PMT_T); + return; + } + else { + error_msg = "failed to close USRP:"; + goto bail; + } + } + goto unhandled; + + default: + goto unhandled; + } + return; + + bail: + std::cerr << error_msg << data + << "status = " << status << std::endl; + shutdown_all(PMT_F); + return; + + unhandled: + std::cout << "test_usrp_inband_tx: unhandled msg: " << msg + << "in state "<< d_state << std::endl; +} + + +void +test_usrp_tx::open_usrp() +{ + pmt_t which_usrp = pmt_from_long(0); + + d_cs->send(s_cmd_open, pmt_list2(PMT_NIL, which_usrp)); + d_state = OPENING_USRP; + + if(verbose) + std::cout << "[TEST_USRP_INBAND_TX] Opening the USRP\n"; +} + +void +test_usrp_tx::close_usrp() +{ + d_cs->send(s_cmd_close, pmt_list1(PMT_NIL)); + d_state = CLOSING_USRP; + + if(verbose) + std::cout << "[TEST_USRP_INBAND_TX] Closing the USRP\n"; +} + +void +test_usrp_tx::allocate_channel() +{ + long capacity = (long) 16e6; + d_tx->send(s_cmd_allocate_channel, pmt_list2(PMT_T, pmt_from_long(capacity))); + d_state = ALLOCATING_CHANNEL; + + if(verbose) + std::cout << "[TEST_USRP_INBAND_TX] Requesting TX channel allocation\n"; +} + +void +test_usrp_tx::enter_transmitting() +{ + d_state = TRANSMITTING; + d_nsamples_xmitted = 0; + + if(verbose) + std::cout << "[TEST_USRP_INBAND_TX] Transmitting...\n"; + + build_and_send_next_frame(); // fire off 4 to start pipeline + build_and_send_next_frame(); + build_and_send_next_frame(); + build_and_send_next_frame(); +} + +void +test_usrp_tx::build_and_send_next_frame() +{ + // allocate the uniform vector for the samples + // FIXME perhaps hold on to this between calls + +#if 1 + long nsamples_this_frame = + std::min(d_nsamples_to_send - d_nsamples_xmitted, + d_samples_per_frame); +#else + long nsamples_this_frame = d_samples_per_frame; +#endif + + if (nsamples_this_frame == 0){ + d_done_sending = true; + return; + } + + + size_t nshorts = 2 * nsamples_this_frame; // 16-bit I & Q + pmt_t uvec = pmt_make_s16vector(nshorts, 0); + size_t ignore; + int16_t *samples = pmt_s16vector_writable_elements(uvec, ignore); + + // fill in the complex sinusoid + + for (int i = 0; i < nsamples_this_frame; i++){ + + if (1){ + gr_complex s; + d_nco.sincos(&s, 1, d_amplitude); + // write 16-bit i & q + samples[2*i] = (int16_t) s.real(); + samples[2*i+1] = (int16_t) s.imag(); + } + else { + gr_complex s(d_amplitude, d_amplitude); + + // write 16-bit i & q + samples[2*i] = (int16_t) s.real(); + samples[2*i+1] = (int16_t) s.imag(); + } + } + + pmt_t tx_properties = pmt_make_dict(); + + pmt_t timestamp = pmt_from_long(0xffffffff); // NOW + d_tx->send(s_cmd_xmit_raw_frame, + pmt_list5(pmt_from_long(d_nframes_xmitted), // invocation-handle + d_tx_chan, // channel + uvec, // the samples + timestamp, + tx_properties)); + + d_nsamples_xmitted += nsamples_this_frame; + d_nframes_xmitted++; + + if(verbose && 0) + std::cout << "[TEST_USRP_INBAND_TX] Transmitted frame\n"; +} + + +void +test_usrp_tx::handle_xmit_response(pmt_t handle) +{ + if (d_done_sending && + pmt_to_long(handle) == (d_nframes_xmitted - 1)){ + // We're done sending and have received all responses + enter_closing_channel(); + } + + build_and_send_next_frame(); +} + +void +test_usrp_tx::enter_closing_channel() +{ + d_state = CLOSING_CHANNEL; + + d_tx->send(s_cmd_deallocate_channel, pmt_list2(PMT_NIL, d_tx_chan)); + + if(verbose) + std::cout << "[TEST_USRP_INBAND_tX] Deallocating TX channel\n"; +} + +REGISTER_MBLOCK_CLASS(test_usrp_tx); + + +// ---------------------------------------------------------------- + +int +main (int argc, char **argv) +{ + // handle any command line args here + + mb_runtime_sptr rt = mb_make_runtime(); + pmt_t result = PMT_NIL; + + rt->run("top", "test_usrp_tx", PMT_F, &result); +} diff --git a/usrp/limbo/apps-inband/test_usrp_inband_underrun.cc b/usrp/limbo/apps-inband/test_usrp_inband_underrun.cc new file mode 100644 index 000000000..11babb048 --- /dev/null +++ b/usrp/limbo/apps-inband/test_usrp_inband_underrun.cc @@ -0,0 +1,674 @@ +/* -*- c++ -*- */ +/* + * Copyright 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 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 <mblock/mblock.h> +#include <mblock/runtime.h> +#include <mblock/protocol_class.h> +#include <mblock/exception.h> +#include <mblock/msg_queue.h> +#include <mblock/message.h> +#include <mblock/msg_accepter.h> +#include <mblock/class_registry.h> +#include <pmt.h> +#include <stdio.h> +#include <string.h> +#include <sys/time.h> +#include <iostream> +#include <ui_nco.h> + +// Include the symbols needed for communication with USRP server +#include <symbols_usrp_server_cs.h> +#include <symbols_usrp_channel.h> +#include <symbols_usrp_low_level_cs.h> +#include <symbols_usrp_tx.h> +#include <symbols_usrp_rx.h> + +static bool verbose = true; + +class test_usrp_inband_underrun : public mb_mblock +{ + + mb_port_sptr d_tx; // Ports connected to the USRP server + mb_port_sptr d_rx; + mb_port_sptr d_cs; + + pmt_t d_tx_chan; // Returned channel from TX allocation + pmt_t d_rx_chan; // Returned channel from RX allocation + + pmt_t d_which_usrp; // The USRP to use for the test + + long d_warm_msgs; // The number of messages to 'warm' the USRP + long d_warm_recvd; // The number of msgs received in the 'warm' state + + // Keep track of current state + enum state_t { + INIT, + OPENING_USRP, + ALLOCATING_CHANNELS, + WRITE_REGISTER, + READ_REGISTER, + TRANSMITTING, + CLOSING_CHANNELS, + CLOSING_USRP, + }; + state_t d_state; + + long d_nsamples_to_send; + long d_nsamples_xmitted; + long d_nframes_xmitted; + long d_samples_per_frame; + bool d_done_sending; + + // for generating sine wave output + ui_nco<float,float> d_nco; + double d_amplitude; + + long d_n_underruns; + + public: + test_usrp_inband_underrun(mb_runtime *runtime, const std::string &instance_name, pmt_t user_arg); + ~test_usrp_inband_underrun(); + void initial_transition(); + void handle_message(mb_message_sptr msg); + + protected: + void opening_usrp(); + void allocating_channels(); + void write_register(); + void read_register(); + void closing_channels(); + void closing_usrp(); + void enter_receiving(); + void enter_transmitting(); + void build_and_send_ping(); + void build_and_send_next_frame(); + void handle_xmit_response(pmt_t handle); + void handle_recv_response(pmt_t dict); +}; + + +int +main (int argc, char **argv) +{ + // handle any command line args here + + mb_runtime_sptr rt = mb_make_runtime(); + pmt_t result = PMT_NIL; + + rt->run("top", "test_usrp_inband_underrun", PMT_F, &result); +} + + +test_usrp_inband_underrun::test_usrp_inband_underrun(mb_runtime *runtime, const std::string &instance_name, pmt_t user_arg) + : mb_mblock(runtime, instance_name, user_arg), + d_tx_chan(PMT_NIL), + d_rx_chan(PMT_NIL), + d_which_usrp(pmt_from_long(0)), + d_state(INIT), + d_nsamples_to_send((long) 27e6), + d_nsamples_xmitted(0), + d_nframes_xmitted(0), + d_samples_per_frame(d_nsamples_to_send), // full packet + + d_done_sending(false), + d_amplitude(16384), + d_n_underruns(0) +{ + + // A dictionary is used to pass parameters to the USRP + pmt_t usrp_dict = pmt_make_dict(); + + // Specify the RBF to use + pmt_dict_set(usrp_dict, + pmt_intern("rbf"), + pmt_intern("inband_1rxhb_1tx.rbf")); + + // Set TX and RX interpolations + pmt_dict_set(usrp_dict, + pmt_intern("interp-tx"), + pmt_from_long(64)); + + pmt_dict_set(usrp_dict, + pmt_intern("decim-rx"), + pmt_from_long(128)); + + d_tx = define_port("tx0", "usrp-tx", false, mb_port::INTERNAL); + d_rx = define_port("rx0", "usrp-rx", false, mb_port::INTERNAL); + d_cs = define_port("cs", "usrp-server-cs", false, mb_port::INTERNAL); + + // Create an instance of USRP server and connect ports + define_component("server", "usrp_server", usrp_dict); + connect("self", "tx0", "server", "tx0"); + connect("self", "rx0", "server", "rx0"); + connect("self", "cs", "server", "cs"); + + // initialize NCO + double freq = 100e3; + int interp = 32; // 32 -> 4MS/s + double sample_rate = 128e6 / interp; + d_nco.set_freq(2*M_PI * freq/sample_rate); +} + +test_usrp_inband_underrun::~test_usrp_inband_underrun() +{ +} + +void +test_usrp_inband_underrun::initial_transition() +{ + opening_usrp(); +} + +// Handle message reads all incoming messages from USRP server which will be +// initialization and ping responses. We perform actions based on the current +// state and the event (ie, ping response) +void +test_usrp_inband_underrun::handle_message(mb_message_sptr msg) +{ + pmt_t event = msg->signal(); + pmt_t data = msg->data(); + pmt_t port_id = msg->port_id(); + + pmt_t handle = PMT_F; + pmt_t status = PMT_F; + pmt_t dict = PMT_NIL; + std::string error_msg; + + // Check the recv sample responses for underruns and count + if(pmt_eq(event, s_response_recv_raw_samples)) { + handle = pmt_nth(0, data); + status = pmt_nth(1, data); + dict = pmt_nth(4, data); + + if(pmt_eq(status, PMT_T)) { + handle_recv_response(dict); + return; + } + else { + error_msg = "error while receiving samples:"; + goto bail; + } + } + + + // Dispatch based on state + switch(d_state) { + + //----------------------------- OPENING_USRP ----------------------------// + // We only expect a response from opening the USRP which should be succesful + // or failed. + case OPENING_USRP: + + if(pmt_eq(event, s_response_open)) { + + status = pmt_nth(1, data); // failed/succes + + if(pmt_eq(status, PMT_T)) { + allocating_channels(); + return; + } + else { + error_msg = "failed to open usrp:"; + goto bail; + } + + } + + goto unhandled; // all other messages not handled in this state + + + //----------------------- ALLOCATING CHANNELS --------------------// + // When allocating channels, we need to wait for 2 responses from + // USRP server: one for TX and one for RX. Both are initialized to + // NIL so we know to continue to the next state once both are set. + case ALLOCATING_CHANNELS: + + // A TX allocation response + if(pmt_eq(event, s_response_allocate_channel) + && pmt_eq(d_tx->port_symbol(), port_id)) + { + status = pmt_nth(1, data); + + // If successful response, extract the channel + if(pmt_eq(status, PMT_T)) { + + d_tx_chan = pmt_nth(2, data); + + if(verbose) + std::cout << "[TEST_USRP_INBAND_UNDERRUN] Received TX allocation" + << " on channel " << d_tx_chan << std::endl; + + // If the RX has also been allocated already, we can continue + if(!pmt_eqv(d_rx_chan, PMT_NIL)) { + enter_receiving(); + enter_transmitting(); + } + + return; + } + else { // TX allocation failed + error_msg = "failed to allocate TX channel:"; + goto bail; + } + } + + // A RX allocation response + if(pmt_eq(event, s_response_allocate_channel) + && pmt_eq(d_rx->port_symbol(), port_id)) + { + status = pmt_nth(1, data); + + // If successful response, extract the channel + if(pmt_eq(status, PMT_T)) { + + d_rx_chan = pmt_nth(2, data); + + if(verbose) + std::cout << "[TEST_USRP_INBAND_UNDERRUN] Received RX allocation" + << " on channel " << d_rx_chan << std::endl; + + // If the TX has also been allocated already, we can continue + if(!pmt_eqv(d_tx_chan, PMT_NIL)) { + enter_receiving(); + enter_transmitting(); + } + + return; + } + else { // RX allocation failed + error_msg = "failed to allocate RX channel:"; + goto bail; + } + } + + goto unhandled; + + case WRITE_REGISTER: + goto unhandled; + + case READ_REGISTER: + goto unhandled; + + //-------------------------- TRANSMITTING ----------------------------// + // In the transmit state we count the number of underruns received and + // ballpark the number with an expected count (something >1 for starters) + case TRANSMITTING: + + // Check that the transmits are OK + if (pmt_eq(event, s_response_xmit_raw_frame)){ + handle = pmt_nth(0, data); + status = pmt_nth(1, data); + + if (pmt_eq(status, PMT_T)){ + handle_xmit_response(handle); + return; + } + else { + error_msg = "bad response-xmit-raw-frame:"; + goto bail; + } + } + + goto unhandled; + + //------------------------- CLOSING CHANNELS ----------------------------// + // Check deallocation responses, once the TX and RX channels are both + // deallocated then we close the USRP. + case CLOSING_CHANNELS: + + if (pmt_eq(event, s_response_deallocate_channel) + && pmt_eq(d_tx->port_symbol(), port_id)) + { + status = pmt_nth(1, data); + + // If successful, set the port to NIL + if(pmt_eq(status, PMT_T)) { + d_tx_chan = PMT_NIL; + + if(verbose) + std::cout << "[TEST_USRP_INBAND_UNDERRUN] Received TX deallocation\n"; + + // If the RX is also deallocated, we can close the USRP + if(pmt_eq(d_rx_chan, PMT_NIL)) + closing_usrp(); + + return; + + } else { + + error_msg = "failed to deallocate TX channel:"; + goto bail; + + } + } + + if (pmt_eq(event, s_response_deallocate_channel) + && pmt_eq(d_rx->port_symbol(), port_id)) + { + status = pmt_nth(1, data); + + // If successful, set the port to NIL + if(pmt_eq(status, PMT_T)) { + d_rx_chan = PMT_NIL; + + if(verbose) + std::cout << "[TEST_USRP_INBAND_UNDERRUN] Received RX deallocation\n"; + + // If the TX is also deallocated, we can close the USRP + if(pmt_eq(d_tx_chan, PMT_NIL)) + closing_usrp(); + + return; + + } else { + + error_msg = "failed to deallocate RX channel:"; + goto bail; + + } + } + + goto unhandled; + + //--------------------------- CLOSING USRP ------------------------------// + // Once we have received a successful USRP close response, we shutdown all + // mblocks and exit. + case CLOSING_USRP: + + if (pmt_eq(event, s_response_close)) { + + status = pmt_nth(1, data); + + if(pmt_eq(status, PMT_T)) { + + if(verbose) + std::cout << "[TEST_USRP_INBAND_UNDERRUN] Successfully closed USRP\n"; + + std::cout << "\nUnderruns: " << d_n_underruns << std::endl; + fflush(stdout); + + shutdown_all(PMT_T); + return; + + } else { + + error_msg = "failed to close USRP:"; + goto bail; + } + } + + goto unhandled; + + case INIT: + goto unhandled; + + } + + // An error occured, print it, and shutdown all m-blocks + bail: + std::cerr << error_msg << data + << "status = " << status << std::endl; + shutdown_all(PMT_F); + return; + + // Received an unhandled message for a specific state + unhandled: + if(verbose && !pmt_eq(event, pmt_intern("%shutdown"))) + std::cout << "test_usrp_inband_tx: unhandled msg: " << msg + << "in state "<< d_state << std::endl; + +} + + +// Sends a command to USRP server to open up a connection to the +// specified USRP, which is defaulted to USRP 0 on the system +void +test_usrp_inband_underrun::opening_usrp() +{ + + if(verbose) + std::cout << "[TEST_USRP_INBAND_UNDERRUN] Opening USRP " + << d_which_usrp << std::endl; + + d_cs->send(s_cmd_open, pmt_list2(PMT_NIL, d_which_usrp)); + d_state = OPENING_USRP; +} + +// RX and TX channels must be allocated so that the USRP server can +// properly share bandwidth across multiple USRPs. No commands will be +// successful to the USRP through the USRP server on the TX or RX channels until +// a bandwidth allocation has been received. +void +test_usrp_inband_underrun::allocating_channels() +{ + d_state = ALLOCATING_CHANNELS; + + long capacity = (long) 16e6; + d_tx->send(s_cmd_allocate_channel, pmt_list2(PMT_T, pmt_from_long(capacity))); + d_rx->send(s_cmd_allocate_channel, pmt_list2(PMT_T, pmt_from_long(capacity))); +} + +// After allocating the channels, a write register command will be sent to the +// USRP. +void +test_usrp_inband_underrun::write_register() +{ + d_state = WRITE_REGISTER; + + long reg = 0; + + d_tx->send(s_cmd_to_control_channel, // C/S packet + pmt_list2(PMT_NIL, // invoc handle + pmt_list1( + pmt_list2(s_op_write_reg, + pmt_list2( + pmt_from_long(reg), + pmt_from_long(0xbeef)))))); + + if(verbose) + std::cout << "[TEST_USRP_INBAND_REGISTERS] Writing 0xbeef to " + << reg << std::endl; + + read_register(); // immediately transition to read the register +} + +// Temporary: for testing pings +void +test_usrp_inband_underrun::build_and_send_ping() +{ + + d_tx->send(s_cmd_to_control_channel, + pmt_list2(PMT_NIL, pmt_list1(pmt_list2(s_op_ping_fixed, + pmt_list2(pmt_from_long(0), + pmt_from_long(0)))))); + + std::cout << "[TEST_USRP_INBAND_UNDERRUN] Ping sent" << std::endl; +} + +// After writing to the register, we want to read the value back and ensure that +// it is the same value that we wrote. +void +test_usrp_inband_underrun::read_register() +{ + d_state = READ_REGISTER; + + long reg = 9; + + d_tx->send(s_cmd_to_control_channel, // C/S packet + pmt_list2(PMT_NIL, // invoc handle + pmt_list1( + pmt_list2(s_op_read_reg, + pmt_list2( + pmt_from_long(0), // rid + pmt_from_long(reg)))))); + if(verbose) + std::cout << "[TEST_USRP_INBAND_UNDERRUN] Reading from register " + << reg << std::endl; +} + +// Used to enter the receiving state +void +test_usrp_inband_underrun::enter_receiving() +{ + d_rx->send(s_cmd_start_recv_raw_samples, + pmt_list2(PMT_F, + d_rx_chan)); + + if(verbose) + std::cout << "[TEST_USRP_INBAND_UNDERRUN] Started RX sample stream\n"; +} + +void +test_usrp_inband_underrun::enter_transmitting() +{ + d_state = TRANSMITTING; + d_nsamples_xmitted = 0; + + if(verbose) + std::cout << "[TEST_USRP_INBAND_UNDERRUN] Entering transmit state...\n"; + + build_and_send_next_frame(); // fire off 4 to start pipeline + build_and_send_next_frame(); + build_and_send_next_frame(); + build_and_send_next_frame(); +} + +void +test_usrp_inband_underrun::build_and_send_next_frame() +{ + + long nsamples_this_frame = + std::min(d_nsamples_to_send - d_nsamples_xmitted, + d_samples_per_frame); + + if (nsamples_this_frame == 0){ + d_done_sending = true; + return; + } + + size_t nshorts = 2 * nsamples_this_frame; // 16-bit I & Q + pmt_t uvec = pmt_make_s16vector(nshorts, 0); + size_t ignore; + int16_t *samples = pmt_s16vector_writable_elements(uvec, ignore); + + // fill in the complex sinusoid + + for (int i = 0; i < nsamples_this_frame; i++){ + + if (1){ + gr_complex s; + d_nco.sincos(&s, 1, d_amplitude); + // write 16-bit i & q + samples[2*i] = (int16_t) s.real(); + samples[2*i+1] = (int16_t) s.imag(); + } + else { + gr_complex s(d_amplitude, d_amplitude); + + // write 16-bit i & q + samples[2*i] = (int16_t) s.real(); + samples[2*i+1] = (int16_t) s.imag(); + } + } + + if(verbose) + std::cout << "[TEST_USRP_INBAND_TX] Transmitting frame...\n"; + + pmt_t timestamp = pmt_from_long(0xffffffff); // NOW + d_tx->send(s_cmd_xmit_raw_frame, + pmt_list4(pmt_from_long(d_nframes_xmitted), // invocation-handle + d_tx_chan, // channel + uvec, // the samples + timestamp)); + + d_nsamples_xmitted += nsamples_this_frame; + d_nframes_xmitted++; + + if(verbose) + std::cout << "[TEST_USRP_INBAND_TX] Transmitted frame\n"; + +} + +void +test_usrp_inband_underrun::handle_xmit_response(pmt_t handle) +{ + if (d_done_sending && + pmt_to_long(handle) == (d_nframes_xmitted - 1)){ + // We're done sending and have received all responses + closing_channels(); + return; + } + + build_and_send_next_frame(); +} + +void +test_usrp_inband_underrun::handle_recv_response(pmt_t dict) +{ + if(!pmt_is_dict(dict)) { + std::cout << "[TEST_USRP_INBAND_UNDERRUN] Recv samples dictionary is improper\n"; + return; + } + + // Read the TX interpolations + if(pmt_t underrun = pmt_dict_ref(dict, + pmt_intern("underrun"), + PMT_NIL)) { + if(pmt_eqv(underrun, PMT_T)) { + d_n_underruns++; + + if(verbose && 0) + std::cout << "[TEST_USRP_INBAND_UNDERRUN] Underrun\n"; + } + else { + if(verbose && 0) + std::cout << "[TEST_USRP_INBAND_UNDERRUN] No underrun\n" << underrun <<std::endl; + } + } else { + + if(verbose && 0) + std::cout << "[TEST_USRP_INBAND_UNDERRUN] No underrun\n"; + } + +} + +void +test_usrp_inband_underrun::closing_channels() +{ + d_state = CLOSING_CHANNELS; + + d_tx->send(s_cmd_deallocate_channel, pmt_list2(PMT_NIL, d_tx_chan)); + d_rx->send(s_cmd_deallocate_channel, pmt_list2(PMT_NIL, d_rx_chan)); +} + +void +test_usrp_inband_underrun::closing_usrp() +{ + d_state = CLOSING_USRP; + + d_cs->send(s_cmd_close, pmt_list1(PMT_NIL)); +} + +REGISTER_MBLOCK_CLASS(test_usrp_inband_underrun); diff --git a/usrp/limbo/apps-inband/ui_nco.h b/usrp/limbo/apps-inband/ui_nco.h new file mode 100644 index 000000000..e6d7814ab --- /dev/null +++ b/usrp/limbo/apps-inband/ui_nco.h @@ -0,0 +1,202 @@ +/* -*- c++ -*- */ +/* + * Copyright 2002 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. + */ +#ifndef INCLUDED_UI_NCO_H +#define INCLUDED_UI_NCO_H + + +#include <vector> +#include <ui_sincos.h> +#include <cmath> + +#include <complex> +typedef std::complex<float> gr_complex; + + +/*! + * \brief base class template for Numerically Controlled Oscillator (NCO) + */ + + +//FIXME Eventually generalize this to fixed point + +template<class o_type, class i_type> +class ui_nco { +public: + ui_nco () : phase (0), phase_inc(0) {} + + virtual ~ui_nco () {} + + // radians + void set_phase (double angle) { + phase = angle; + } + + void adjust_phase (double delta_phase) { + phase += delta_phase; + } + + + // angle_rate is in radians / step + void set_freq (double angle_rate){ + phase_inc = angle_rate; + } + + // angle_rate is a delta in radians / step + void adjust_freq (double delta_angle_rate) + { + phase_inc += delta_angle_rate; + } + + // increment current phase angle + + void step () + { + phase += phase_inc; + if (fabs (phase) > M_PI){ + + while (phase > M_PI) + phase -= 2*M_PI; + + while (phase < -M_PI) + phase += 2*M_PI; + } + } + + void step (int n) + { + phase += phase_inc * n; + if (fabs (phase) > M_PI){ + + while (phase > M_PI) + phase -= 2*M_PI; + + while (phase < -M_PI) + phase += 2*M_PI; + } + } + + // units are radians / step + double get_phase () const { return phase; } + double get_freq () const { return phase_inc; } + + // compute sin and cos for current phase angle + void sincos (float *sinx, float *cosx) const; + + // compute cos or sin for current phase angle + float cos () const { return std::cos (phase); } + float sin () const { return std::sin (phase); } + + // compute a block at a time + void sin (float *output, int noutput_items, double ampl = 1.0); + void cos (float *output, int noutput_items, double ampl = 1.0); + void sincos (gr_complex *output, int noutput_items, double ampl = 1.0); + void sin (short *output, int noutput_items, double ampl = 1.0); + void cos (short *output, int noutput_items, double ampl = 1.0); + void sin (int *output, int noutput_items, double ampl = 1.0); + void cos (int *output, int noutput_items, double ampl = 1.0); + +protected: + double phase; + double phase_inc; +}; + +template<class o_type, class i_type> +void +ui_nco<o_type,i_type>::sincos (float *sinx, float *cosx) const +{ + ui_sincosf (phase, sinx, cosx); +} + +template<class o_type, class i_type> +void +ui_nco<o_type,i_type>::sin (float *output, int noutput_items, double ampl) +{ + for (int i = 0; i < noutput_items; i++){ + output[i] = (float)(sin () * ampl); + step (); + } +} + +template<class o_type, class i_type> +void +ui_nco<o_type,i_type>::cos (float *output, int noutput_items, double ampl) +{ + for (int i = 0; i < noutput_items; i++){ + output[i] = (float)(cos () * ampl); + step (); + } +} + +template<class o_type, class i_type> +void +ui_nco<o_type,i_type>::sin (short *output, int noutput_items, double ampl) +{ + for (int i = 0; i < noutput_items; i++){ + output[i] = (short)(sin() * ampl); + step (); + } +} + +template<class o_type, class i_type> +void +ui_nco<o_type,i_type>::cos (short *output, int noutput_items, double ampl) +{ + for (int i = 0; i < noutput_items; i++){ + output[i] = (short)(cos () * ampl); + step (); + } +} + +template<class o_type, class i_type> +void +ui_nco<o_type,i_type>::sin (int *output, int noutput_items, double ampl) +{ + for (int i = 0; i < noutput_items; i++){ + output[i] = (int)(sin () * ampl); + step (); + } +} + +template<class o_type, class i_type> +void +ui_nco<o_type,i_type>::cos (int *output, int noutput_items, double ampl) +{ + for (int i = 0; i < noutput_items; i++){ + output[i] = (int)(cos () * ampl); + step (); + } +} + +template<class o_type, class i_type> +void +ui_nco<o_type,i_type>::sincos (gr_complex *output, int noutput_items, double ampl) +{ + for (int i = 0; i < noutput_items; i++){ + float cosx, sinx; + sincos (&sinx, &cosx); + output[i] = gr_complex(cosx * ampl, sinx * ampl); + step (); + } +} + +#endif /* INCLUDED_UI_NCO_H */ + diff --git a/usrp/limbo/apps-inband/ui_sincos.c b/usrp/limbo/apps-inband/ui_sincos.c new file mode 100644 index 000000000..27841f010 --- /dev/null +++ b/usrp/limbo/apps-inband/ui_sincos.c @@ -0,0 +1,81 @@ +/* -*- c++ -*- */ +/* + * Copyright 2004 Free Software Foundation, Inc. + * + * This file is part of GNU Radio + * + * GNU Radio is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Radio; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#define _GNU_SOURCE // ask for GNU extensions if available + +#include "ui_sincos.h" +#include <math.h> + +// ---------------------------------------------------------------- + +#if defined (HAVE_SINCOS) + +void +ui_sincos (double x, double *sinx, double *cosx) +{ + sincos (x, sinx, cosx); +} + +#else + +void +ui_sincos (double x, double *sinx, double *cosx) +{ + *sinx = sin (x); + *cosx = cos (x); +} + +#endif + +// ---------------------------------------------------------------- + +#if defined (HAVE_SINCOSF) + +void +ui_sincosf (float x, float *sinx, float *cosx) +{ + sincosf (x, sinx, cosx); +} + +#elif defined (HAVE_SINF) && defined (HAVE_COSF) + +void +ui_sincosf (float x, float *sinx, float *cosx) +{ + *sinx = sinf (x); + *cosx = cosf (x); +} + +#else + +void +ui_sincosf (float x, float *sinx, float *cosx) +{ + *sinx = sin (x); + *cosx = cos (x); +} + +#endif diff --git a/usrp/limbo/apps-inband/ui_sincos.h b/usrp/limbo/apps-inband/ui_sincos.h new file mode 100644 index 000000000..d2d6e4b76 --- /dev/null +++ b/usrp/limbo/apps-inband/ui_sincos.h @@ -0,0 +1,39 @@ +/* -*- c++ -*- */ +/* + * Copyright 2002,2004 Free Software Foundation, Inc. + * + * This file is part of GNU Radio + * + * GNU Radio is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Radio; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +#ifndef INCLUDED_UI_SINCOS_H +#define INCLUDED_UI_SINCOS_H + +#ifdef __cplusplus +extern "C" { +#endif + +// compute sine and cosine at the same time + +void ui_sincos (double x, double *sin, double *cos); +void ui_sincosf (float x, float *sin, float *cos); + +#ifdef __cplusplus +}; +#endif + +#endif /* INCLUDED_UI_SINCOS_H */ diff --git a/usrp/limbo/inband/Makefile.am b/usrp/limbo/inband/Makefile.am new file mode 100644 index 000000000..650a25ff9 --- /dev/null +++ b/usrp/limbo/inband/Makefile.am @@ -0,0 +1,114 @@ +# +# Copyright 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 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 + +AM_CPPFLAGS = \ + $(DEFINES) $(OMNITHREAD_INCLUDES) $(PMT_INCLUDES) $(MBLOCK_INCLUDES) \ + $(USRP_INCLUDES) $(BOOST_CPPFLAGS) $(CPPUNIT_INCLUDES) \ + -I$(srcdir)/../../apps-inband $(WITH_INCLUDES) + +TESTS = test_inband + +EXTRA_DIST = \ + usrp_server.mbh \ + usrp_interface.mbh + +lib_LTLIBRARIES = \ + libusrp-inband.la \ + libusrp-inband-qa.la + +# ------------------------------------------------------------------------ +# Build the inband library + +BUILT_SOURCES = \ + usrp_server_mbh.cc \ + usrp_interface_mbh.cc + +usrp_server_mbh.cc : usrp_server.mbh + $(COMPILE_MBH) $(srcdir)/usrp_server.mbh usrp_server_mbh.cc + +usrp_interface_mbh.cc : usrp_interface.mbh + $(COMPILE_MBH) $(srcdir)/usrp_interface.mbh usrp_interface_mbh.cc + +libusrp_inband_la_SOURCES = \ + $(BUILT_SOURCES) \ + $(srcdir)/../../apps-inband/ui_sincos.c \ + usrp_inband_usb_packet.cc \ + usrp_rx.cc \ + usrp_rx_stub.cc \ + usrp_server.cc \ + usrp_tx.cc \ + usrp_tx_stub.cc \ + usrp_usb_interface.cc + +libusrp_inband_la_LDFLAGS = $(NO_UNDEFINED) -version-info 0:0:0 + +libusrp_inband_la_LIBADD = \ + $(MBLOCK_LA) \ + $(USRP_LA) \ + -lstdc++ + +include_HEADERS = \ + usrp_inband_usb_packet.h \ + usrp_rx.h \ + usrp_rx_stub.h \ + usrp_server.h \ + usrp_tx.h \ + usrp_tx_stub.h \ + usrp_usb_interface.h + +noinst_HEADERS = \ + qa_inband.h \ + qa_inband_packet_prims.h \ + qa_inband_usrp_server.h \ + symbols_usrp_channel.h \ + symbols_usrp_interface_cs.h \ + symbols_usrp_low_level_cs.h \ + symbols_usrp_rx.h \ + symbols_usrp_rx_cs.h \ + symbols_usrp_server_cs.h \ + symbols_usrp_tx.h \ + symbols_usrp_tx_cs.h + +# ------------------------------------------------------------------------ +# Build the qa code in its own library + +libusrp_inband_qa_la_SOURCES = \ + qa_inband.cc \ + qa_inband_packet_prims.cc \ + qa_inband_usrp_server.cc + +# magic flags +libusrp_inband_qa_la_LDFLAGS = $(NO_UNDEFINED) -avoid-version + +libusrp_inband_qa_la_LIBADD = \ + libusrp-inband.la \ + $(PMT_LA) \ + $(CPPUNIT_LIBS) \ + -lstdc++ + +# ------------------------------------------------------------------------ + +noinst_PROGRAMS = \ + test_inband + +test_inband_SOURCES = test_inband.cc +test_inband_LDADD = libusrp-inband-qa.la diff --git a/usrp/limbo/inband/dump_packets.py b/usrp/limbo/inband/dump_packets.py new file mode 100755 index 000000000..237362406 --- /dev/null +++ b/usrp/limbo/inband/dump_packets.py @@ -0,0 +1,65 @@ +#!/usr/bin/env python +# +# Copyright 2007 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 +import struct +from optparse import OptionParser + +from usb_packet import * + +def dump_packet(raw_pkt, outfile, dump_payload): + pkt = usb_packet(raw_pkt) + outfile.write(pkt.decoded_flags()) + outfile.write(' chan= %2d len= %3d timestamp= 0x%08x rssi= % 2d tag= %2d\n' % ( + pkt.chan(), pkt.payload_len(), pkt.timestamp(), pkt.rssi(), pkt.tag())) + if dump_payload: + assert pkt.payload_len() % 4 == 0 + shorts = struct.unpack('<%dh' % (pkt.payload_len() // 2), pkt.payload()) + for i in range(0, len(shorts), 2): + outfile.write(' %6d, %6d\n' % (shorts[i], shorts[i+1])) + + +def dump_packets(infile, outfile, dump_payload): + raw_pkt = infile.read(512) + while raw_pkt: + if len(raw_pkt) != 512: + sys.stderr.write("File length is not a multiple of 512 bytes") + raise SystemExit, 1 + + dump_packet(raw_pkt, outfile, dump_payload) + raw_pkt = infile.read(512) + + +def main(): + parser = OptionParser() + parser.add_option('-p', '--dump-payload', action='store_true', default=False, + help='dump payload in decimal and hex') + + (options, files) = parser.parse_args() + if len(files) == 0: + dump_packets(sys.stdin, sys.stdout, options.dump_payload) + else: + for f in files: + dump_packets(open(f, "r"), sys.stdout, options.dump_payload) + + +if __name__ == '__main__': + main() diff --git a/usrp/limbo/inband/gen_test_packets.py b/usrp/limbo/inband/gen_test_packets.py new file mode 100755 index 000000000..2ee646384 --- /dev/null +++ b/usrp/limbo/inband/gen_test_packets.py @@ -0,0 +1,88 @@ +#!/usr/bin/env python + +import random +import struct +from pprint import pprint +from usb_packet import * + +MAX_PAYLOAD = 504 +TIME_NOW = 0xffffffff + + +class sequence_generator(object): + def __init__(self): + self.i = 0 + + def __call__(self): + t = self.i + self.i += 1 + return t + +def gen_shuffled_lengths(): + valid_lengths = range(0, MAX_PAYLOAD+1, 4) # [0, 4, 8, ... 504] + random.shuffle(valid_lengths) + return valid_lengths + + +class packet_sequence_generator(object): + def __init__(self, channel, lengths): + self.next = sequence_generator() + self.channel = channel + self.lengths = lengths + + def __call__(self, output_file): + gen_packet(output_file, self.channel, self.next, self.lengths[0]) + del self.lengths[0] + + +def gen_packet(output_file, channel, content_generator, payload_len): + assert (payload_len % 4) == 0 + payload = [] + n_iq = payload_len // 4 + for n in range(n_iq): + payload.append(content_generator()) # I + payload.append(content_generator()) # Q + for n in range(MAX_PAYLOAD // 4 - n_iq): + payload.append(0x0000) + payload.append(0xffff) + + assert (len(payload) == MAX_PAYLOAD // 2) + + #print "\npayload_len =", payload_len + #pprint(payload) + + output_file.write(make_header(FL_START_OF_BURST|FL_END_OF_BURST, + channel, payload_len, TIME_NOW)) + output_file.write(struct.pack('<252h', *payload)) + + +def gen_all_valid_packet_lengths_1_channel(output_file): + lengths = gen_shuffled_lengths() + npkts = len(lengths) # number of packets we'll generator on each stream + pkt_gen_0 = packet_sequence_generator(0, lengths) + for i in range(npkts): + pkt_gen_0(output_file) + + assert pkt_gen_0.next() == 16002 # 2*sum(1, 2, ..., 126) == 126 * 127 + + +def gen_all_valid_packet_lengths_2_channels(output_file): + lengths = gen_shuffled_lengths() + npkts = len(lengths) # number of packets we'll generator on each stream + pkt_gen_0 = packet_sequence_generator(0, lengths) + pkt_gen_1 = packet_sequence_generator(0x1f, gen_shuffled_lengths()) + pkt_gen = (pkt_gen_0, pkt_gen_1) + + which_gen = (npkts * [0]) + (npkts * [1]) + random.shuffle(which_gen) + + for i in which_gen: + pkt_gen[i](output_file) + + assert pkt_gen_0.next() == 16002 # 2*sum(1, 2, ..., 126) == 126 * 127 + assert pkt_gen_1.next() == 16002 # 2*sum(1, 2, ..., 126) == 126 * 127 + +if __name__ == '__main__': + random.seed(0) + gen_all_valid_packet_lengths_1_channel(open("all_valid_packet_lengths_1_channel.dat", "w")) + gen_all_valid_packet_lengths_2_channels(open("all_valid_packet_lengths_2_channels.dat", "w")) diff --git a/usrp/limbo/inband/qa_inband.cc b/usrp/limbo/inband/qa_inband.cc new file mode 100644 index 000000000..6f33a6ead --- /dev/null +++ b/usrp/limbo/inband/qa_inband.cc @@ -0,0 +1,35 @@ +/* -*- c++ -*- */ +/* + * Copyright 2007 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 <qa_inband.h> +#include <qa_inband_packet_prims.h> +#include <qa_inband_usrp_server.h> + +CppUnit::TestSuite * +qa_inband::suite() +{ + CppUnit::TestSuite *s = new CppUnit::TestSuite("inband"); + + s->addTest (qa_inband_packet_prims::suite()); + s->addTest (qa_inband_usrp_server::suite()); + + return s; +} diff --git a/usrp/limbo/inband/qa_inband.h b/usrp/limbo/inband/qa_inband.h new file mode 100644 index 000000000..ab8f7f250 --- /dev/null +++ b/usrp/limbo/inband/qa_inband.h @@ -0,0 +1,35 @@ +/* -*- c++ -*- */ +/* + * Copyright 2007 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_QA_INBAND_H +#define INCLUDED_QA_INBAND_H + +#include <cppunit/TestSuite.h> + +//! collect all the tests for the user server + +class qa_inband { + public: + //! return suite of tests for all of usrp server + static CppUnit::TestSuite *suite(); +}; + +#endif /* INCLUDED_QA_INBAND_H */ diff --git a/usrp/limbo/inband/qa_inband_packet_prims.cc b/usrp/limbo/inband/qa_inband_packet_prims.cc new file mode 100644 index 000000000..d9bbbec22 --- /dev/null +++ b/usrp/limbo/inband/qa_inband_packet_prims.cc @@ -0,0 +1,162 @@ +/* -*- c++ -*- */ +/* + * Copyright 2007 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 <qa_inband_packet_prims.h> +#include <cppunit/TestAssert.h> +#include <stdio.h> +#include <string.h> +#include <usrp_inband_usb_packet.h> // will change on gigabit crossover + +typedef usrp_inband_usb_packet transport_pkt; + +void +qa_inband_packet_prims::test_flags() +{ + transport_pkt pkt; + + // Test each one of the flags while ensuring no other fields become set in the process + pkt.set_header(pkt.FL_START_OF_BURST,0,0,0); + CPPUNIT_ASSERT_EQUAL(1, pkt.start_of_burst()); + CPPUNIT_ASSERT_EQUAL(0, pkt.end_of_burst()); + CPPUNIT_ASSERT_EQUAL(0, pkt.overrun()); + CPPUNIT_ASSERT_EQUAL(0, pkt.underrun()); + CPPUNIT_ASSERT_EQUAL(0, pkt.dropped()); + CPPUNIT_ASSERT_EQUAL(0, pkt.chan()); + CPPUNIT_ASSERT_EQUAL(0, pkt.tag()); + CPPUNIT_ASSERT_EQUAL(0, pkt.payload_len()); + + pkt.set_header(pkt.FL_END_OF_BURST,0,0,0); + CPPUNIT_ASSERT_EQUAL(0, pkt.start_of_burst()); + CPPUNIT_ASSERT_EQUAL(1, pkt.end_of_burst()); + CPPUNIT_ASSERT_EQUAL(0, pkt.overrun()); + CPPUNIT_ASSERT_EQUAL(0, pkt.underrun()); + CPPUNIT_ASSERT_EQUAL(0, pkt.dropped()); + CPPUNIT_ASSERT_EQUAL(0, pkt.chan()); + CPPUNIT_ASSERT_EQUAL(0, pkt.tag()); + CPPUNIT_ASSERT_EQUAL(0, pkt.payload_len()); + + pkt.set_header(pkt.FL_OVERRUN,0,0,0); + CPPUNIT_ASSERT_EQUAL(0, pkt.start_of_burst()); + CPPUNIT_ASSERT_EQUAL(0, pkt.end_of_burst()); + CPPUNIT_ASSERT_EQUAL(1, pkt.overrun()); + CPPUNIT_ASSERT_EQUAL(0, pkt.underrun()); + CPPUNIT_ASSERT_EQUAL(0, pkt.dropped()); + CPPUNIT_ASSERT_EQUAL(0, pkt.chan()); + CPPUNIT_ASSERT_EQUAL(0, pkt.tag()); + CPPUNIT_ASSERT_EQUAL(0, pkt.payload_len()); + + pkt.set_header(pkt.FL_UNDERRUN,0,0,0); + CPPUNIT_ASSERT_EQUAL(0, pkt.start_of_burst()); + CPPUNIT_ASSERT_EQUAL(0, pkt.end_of_burst()); + CPPUNIT_ASSERT_EQUAL(0, pkt.overrun()); + CPPUNIT_ASSERT_EQUAL(1, pkt.underrun()); + CPPUNIT_ASSERT_EQUAL(0, pkt.dropped()); + CPPUNIT_ASSERT_EQUAL(0, pkt.chan()); + CPPUNIT_ASSERT_EQUAL(0, pkt.tag()); + CPPUNIT_ASSERT_EQUAL(0, pkt.payload_len()); + + pkt.set_header(pkt.FL_DROPPED,0,0,0); + CPPUNIT_ASSERT_EQUAL(0, pkt.start_of_burst()); + CPPUNIT_ASSERT_EQUAL(0, pkt.end_of_burst()); + CPPUNIT_ASSERT_EQUAL(0, pkt.overrun()); + CPPUNIT_ASSERT_EQUAL(0, pkt.underrun()); + CPPUNIT_ASSERT_EQUAL(1, pkt.dropped()); + CPPUNIT_ASSERT_EQUAL(0, pkt.chan()); + CPPUNIT_ASSERT_EQUAL(0, pkt.tag()); + CPPUNIT_ASSERT_EQUAL(0, pkt.payload_len()); + + // test of all fields set + pkt.set_header( + pkt.FL_START_OF_BURST | + pkt.FL_END_OF_BURST | + pkt.FL_UNDERRUN | + pkt.FL_OVERRUN | + pkt.FL_DROPPED + ,0,0,0); + CPPUNIT_ASSERT_EQUAL(1, pkt.start_of_burst()); + CPPUNIT_ASSERT_EQUAL(1, pkt.end_of_burst()); + CPPUNIT_ASSERT_EQUAL(1, pkt.overrun()); + CPPUNIT_ASSERT_EQUAL(1, pkt.underrun()); + CPPUNIT_ASSERT_EQUAL(1, pkt.dropped()); + CPPUNIT_ASSERT_EQUAL(0, pkt.chan()); + CPPUNIT_ASSERT_EQUAL(0, pkt.tag()); + CPPUNIT_ASSERT_EQUAL(0, pkt.payload_len()); + + +} +////////////////////////////////////////////////////////////////////// + +void +qa_inband_packet_prims::test_fields() +{ + transport_pkt pkt; + void * payload; + + // test word0 field exclusiveness + // + // I want to test max values of each field to ensure field boundaries + // but these max values could change based on technology? The + // max payload is returned by a private method so the code is not + // technology dependent + pkt.set_header(0,16,0,0); + CPPUNIT_ASSERT_EQUAL(16, pkt.chan()); + CPPUNIT_ASSERT_EQUAL(0, pkt.tag()); + CPPUNIT_ASSERT_EQUAL(0, pkt.payload_len()); + + pkt.set_header(0,0,8,0); + CPPUNIT_ASSERT_EQUAL(0, pkt.chan()); + CPPUNIT_ASSERT_EQUAL(8, pkt.tag()); + CPPUNIT_ASSERT_EQUAL(0,pkt.payload_len()); + + pkt.set_header(0,0,0,pkt.max_payload()); + CPPUNIT_ASSERT_EQUAL(0, pkt.chan()); + CPPUNIT_ASSERT_EQUAL(0, pkt.tag()); + CPPUNIT_ASSERT_EQUAL(pkt.max_payload(), pkt.payload_len()); + + // test timestamp, shouldn't have to test other fields since + // setting the timestamp only has the ability to affect one word + pkt.set_timestamp(54); + CPPUNIT_ASSERT_EQUAL(uint32_t(54), pkt.timestamp()); + + // test the payload, ensure no other fields overwritten + // + // is there a better test for this? + pkt.set_header(0,0,0,0); + payload = malloc(pkt.payload_len()); + memset(payload, 'f', pkt.payload_len()); + memcpy(pkt.payload(), payload, pkt.payload_len()); + CPPUNIT_ASSERT_EQUAL(0, memcmp(pkt.payload(), payload, pkt.payload_len())); + CPPUNIT_ASSERT_EQUAL(0, pkt.start_of_burst()); + CPPUNIT_ASSERT_EQUAL(0, pkt.end_of_burst()); + CPPUNIT_ASSERT_EQUAL(0, pkt.overrun()); + CPPUNIT_ASSERT_EQUAL(0, pkt.underrun()); + CPPUNIT_ASSERT_EQUAL(0, pkt.dropped()); + CPPUNIT_ASSERT_EQUAL(0, pkt.chan()); + CPPUNIT_ASSERT_EQUAL(0, pkt.tag()); + CPPUNIT_ASSERT_EQUAL(0, pkt.payload_len()); + free(payload); + +} +////////////////////////////////////////////////////////////////////// diff --git a/usrp/limbo/inband/qa_inband_packet_prims.h b/usrp/limbo/inband/qa_inband_packet_prims.h new file mode 100644 index 000000000..71c0d737d --- /dev/null +++ b/usrp/limbo/inband/qa_inband_packet_prims.h @@ -0,0 +1,41 @@ +/* -*- c++ -*- */ +/* + * Copyright 2007 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 QA_INBAND_PACKET_PRIMS_H +#define QA_INBAND_PACKET_PRIMS_H + +#include <cppunit/extensions/HelperMacros.h> +#include <cppunit/TestCase.h> + +class qa_inband_packet_prims : public CppUnit::TestCase { + + CPPUNIT_TEST_SUITE(qa_inband_packet_prims); + CPPUNIT_TEST(test_flags); + CPPUNIT_TEST(test_fields); + CPPUNIT_TEST_SUITE_END(); + + private: + void test_flags(); + void test_fields(); + +}; + +#endif /* INCLUDED_QA_INBAND_PACKET_PRIMS_H */ diff --git a/usrp/limbo/inband/qa_inband_usrp_server.cc b/usrp/limbo/inband/qa_inband_usrp_server.cc new file mode 100644 index 000000000..6049a8a87 --- /dev/null +++ b/usrp/limbo/inband/qa_inband_usrp_server.cc @@ -0,0 +1,1575 @@ +/* -*- c++ -*- */ +/* + * Copyright 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 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 <usrp_inband_usb_packet.h> +#include <qa_inband_usrp_server.h> +#include <cppunit/TestAssert.h> +#include <stdio.h> +#include <usrp_server.h> +#include <mblock/mblock.h> +#include <mblock/runtime.h> +#include <mblock/protocol_class.h> +#include <mblock/class_registry.h> +#include <vector> +#include <iostream> +#include <pmt.h> + +#include <symbols_usrp_server_cs.h> +#include <symbols_usrp_tx.h> +#include <symbols_usrp_rx.h> +#include <symbols_usrp_channel.h> +#include <symbols_usrp_low_level_cs.h> + +typedef usrp_inband_usb_packet transport_pkt; // makes conversion to gigabit easy + +static bool verbose = false; + +static pmt_t s_timeout = pmt_intern("%timeout"); + +// ---------------------------------------------------------------------------------------------- + +class qa_alloc_top : public mb_mblock +{ + mb_port_sptr d_tx; + mb_port_sptr d_rx; + mb_port_sptr d_cs; + + long d_nmsgs_to_recv; + long d_nrecvd; + + long d_max_capacity; + long d_ntx_chan, d_nrx_chan; + + long d_nstatus; + long d_nstatus_to_recv; + + public: + qa_alloc_top(mb_runtime *runtime, const std::string &instance_name, pmt_t user_arg); + ~qa_alloc_top(); + void initial_transition(); + void handle_message(mb_message_sptr msg); + + protected: + void check_message(mb_message_sptr msg); + void run_tests(); +}; + +qa_alloc_top::qa_alloc_top(mb_runtime *runtime, const std::string &instance_name, pmt_t user_arg) + : mb_mblock(runtime, instance_name, user_arg) +{ + d_nrecvd=0; + d_nmsgs_to_recv = 6; + d_nstatus=0; + d_nstatus_to_recv = 50; + + d_rx = define_port("rx0", "usrp-rx", false, mb_port::INTERNAL); + d_tx = define_port("tx0", "usrp-tx", false, mb_port::INTERNAL); + d_cs = define_port("cs", "usrp-server-cs", false, mb_port::INTERNAL); + + // Use the stub with the usrp_server + pmt_t usrp_server_dict = pmt_make_dict(); + pmt_dict_set(usrp_server_dict, pmt_intern("fake-usrp"),PMT_T); + + // Test the TX side + define_component("server", "usrp_server", usrp_server_dict); + connect("self", "tx0", "server", "tx0"); + connect("self", "rx0", "server", "rx0"); + connect("self", "cs", "server", "cs"); + +} + +qa_alloc_top::~qa_alloc_top(){} + +void +qa_alloc_top::initial_transition() +{ + // Allocations should fail before open + d_tx->send(s_cmd_allocate_channel, + pmt_list2(pmt_list2(s_response_allocate_channel, + s_err_usrp_not_opened), + pmt_from_long(1))); + + d_rx->send(s_cmd_allocate_channel, + pmt_list2(pmt_list2(s_response_allocate_channel, + s_err_usrp_not_opened), + pmt_from_long(1))); + + // Retrieve information about the USRP, then run tests + d_cs->send(s_cmd_open, + pmt_list2(pmt_list2(s_response_open, PMT_T), + pmt_from_long(0))); + + d_cs->send(s_cmd_max_capacity, + pmt_list1(pmt_list2(s_response_max_capacity, PMT_T))); + + d_cs->send(s_cmd_ntx_chan, + pmt_list1(pmt_list2(s_response_ntx_chan, PMT_T))); + + d_cs->send(s_cmd_nrx_chan, + pmt_list1(pmt_list2(s_response_nrx_chan,PMT_T))); +} + +void +qa_alloc_top::run_tests() +{ + if(verbose) + std::cout << "[qa_alloc_top] Starting tests...\n"; + + // should be able to allocate 1 byte + d_tx->send(s_cmd_allocate_channel, + pmt_list2(PMT_T, pmt_from_long(1))); + + // should not be able to allocate max capacity after 100 bytes were allocated + d_tx->send(s_cmd_allocate_channel, + pmt_list2(s_err_requested_capacity_unavailable, + pmt_from_long(d_max_capacity))); + + // keep allocating a little more until all of the channels are used and test + // the error response we start at 1 since we've already allocated 1 channel + for(int i=1; i < d_ntx_chan; i++) { + + if(verbose) + std::cout << "[qa_alloc_top] Sent allocation request...\n"; + + d_tx->send(s_cmd_allocate_channel, pmt_list2(PMT_T, pmt_from_long(1))); + + d_nmsgs_to_recv++; + } + + // No more channels after allocating all of them is expected + d_tx->send(s_cmd_allocate_channel, + pmt_list2(s_err_channel_unavailable, + pmt_from_long(1))); + + // test out the same on the RX side + d_rx->send(s_cmd_allocate_channel, pmt_list2(PMT_T, pmt_from_long(1))); + + d_rx->send(s_cmd_allocate_channel, + pmt_list2(s_err_requested_capacity_unavailable, + pmt_from_long(d_max_capacity))); + + for(int i=1; i < d_nrx_chan; i++) { + + d_rx->send(s_cmd_allocate_channel, pmt_list2(PMT_T, pmt_from_long(1))); + + d_nmsgs_to_recv++; + } + + d_rx->send(s_cmd_allocate_channel, + pmt_list2(s_err_channel_unavailable, + pmt_from_long(1))); + + // when all is said and done, there should be d_ntx_chan+d_ntx_chan bytes + // allocated + d_cs->send(s_cmd_current_capacity_allocation, + pmt_list1(pmt_from_long(d_ntx_chan+d_nrx_chan))); +} + +void +qa_alloc_top::handle_message(mb_message_sptr msg) +{ + pmt_t data = msg->data(); + + if ((pmt_eq(msg->port_id(), d_tx->port_symbol()) + || pmt_eq(msg->port_id(), d_rx->port_symbol())) + && pmt_eq(msg->signal(), s_response_allocate_channel)) + check_message(msg); + + if (pmt_eq(msg->port_id(), d_cs->port_symbol())) { + + if(pmt_eq(msg->signal(), s_response_max_capacity)) { + d_max_capacity = pmt_to_long(pmt_nth(2, data)); + if(verbose) + std::cout << "[qa_alloc_top] USRP has max capacity of " + << d_max_capacity << "\n"; + } + else if(pmt_eq(msg->signal(), s_response_ntx_chan)) { + d_ntx_chan = pmt_to_long(pmt_nth(2, data)); + if(verbose) + std::cout << "[qa_alloc_top] USRP tx channels: " + << d_ntx_chan << "\n"; + } + else if(pmt_eq(msg->signal(), s_response_nrx_chan)) { + d_nrx_chan = pmt_to_long(pmt_nth(2, data)); + if(verbose) + std::cout << "[qa_alloc_top] USRP rx channels: " + << d_nrx_chan << "\n"; + } + else if(pmt_eq(msg->signal(), s_response_current_capacity_allocation)) { + check_message(msg); + } + + d_nstatus++; + + check_message(msg); + + if(d_nstatus==d_nstatus_to_recv) + run_tests(); + } +} + +void +qa_alloc_top::check_message(mb_message_sptr msg) +{ + pmt_t data = msg->data(); + pmt_t event = msg->signal(); + + pmt_t expected = pmt_nth(0, data); + pmt_t status = pmt_nth(1, data); + + pmt_t e_event = pmt_nth(0, expected); + pmt_t e_status = pmt_nth(1, expected); + + d_nrecvd++; + + + if(!pmt_eqv(e_status, status) || !pmt_eqv(e_event, event)) { + if(verbose) + std::cout << "Got: " << status << " Expected: " << e_status << "\n"; + shutdown_all(PMT_F); + return; + } else { + if(verbose) + std::cout << "[qa_alloc_top] Received expected response for message " + << d_nrecvd << " (" << event << ")\n"; + } + + if(d_nrecvd == d_nmsgs_to_recv) + shutdown_all(PMT_T); +} + +REGISTER_MBLOCK_CLASS(qa_alloc_top); + +// ---------------------------------------------------------------------------------------------- + +class qa_dealloc_top : public mb_mblock +{ + mb_port_sptr d_tx; + mb_port_sptr d_rx; + mb_port_sptr d_cs; + + long d_max_capacity; + long d_ntx_chan, d_nrx_chan; + + long d_nstatus; + long d_nstatus_to_recv; + + long d_nalloc_to_recv; + long d_nalloc_recvd; + + long d_ndealloc_to_recv; + long d_ndealloc_recvd; + + std::vector<long> d_tx_chans; + std::vector<long> d_rx_chans; + + public: + qa_dealloc_top(mb_runtime *runtime, const std::string &instance_name, pmt_t user_arg); + ~qa_dealloc_top(); + void initial_transition(); + void handle_message(mb_message_sptr msg); + + protected: + void check_allocation(mb_message_sptr msg); + void check_deallocation(mb_message_sptr msg); + void allocate_max(); + void deallocate_all(); +}; + +qa_dealloc_top::qa_dealloc_top(mb_runtime *runtime, const std::string &instance_name, pmt_t user_arg) + : mb_mblock(runtime, instance_name, user_arg) +{ + d_ndealloc_recvd=0; + d_ndealloc_to_recv = 0; + d_nalloc_recvd=0; + d_nalloc_to_recv = 0; // auto-set + d_nstatus=0; + d_nstatus_to_recv = 4; + + d_rx = define_port("rx0", "usrp-rx", false, mb_port::INTERNAL); + d_tx = define_port("tx0", "usrp-tx", false, mb_port::INTERNAL); + d_cs = define_port("cs", "usrp-server-cs", false, mb_port::INTERNAL); + + // Use the stub with the usrp_server + pmt_t usrp_server_dict = pmt_make_dict(); + pmt_dict_set(usrp_server_dict, pmt_intern("fake-usrp"),PMT_T); + + // Test the TX side + define_component("server", "usrp_server", usrp_server_dict); + connect("self", "tx0", "server", "tx0"); + connect("self", "rx0", "server", "rx0"); + connect("self", "cs", "server", "cs"); +} + +qa_dealloc_top::~qa_dealloc_top(){} + +void +qa_dealloc_top::initial_transition() +{ + + if(verbose) + std::cout << "[qa_dealloc_top] Initializing...\n"; + + // Retrieve information about the USRP, then run tests + d_cs->send(s_cmd_open, + pmt_list2(pmt_list2(s_response_open,PMT_T), + pmt_from_long(0))); + + d_cs->send(s_cmd_max_capacity, + pmt_list1(pmt_list2(s_response_max_capacity,PMT_T))); + + d_cs->send(s_cmd_ntx_chan, + pmt_list1(pmt_list2(s_response_ntx_chan,PMT_T))); + + d_cs->send(s_cmd_nrx_chan, + pmt_list1(pmt_list2(s_response_nrx_chan,PMT_T))); +} + +void +qa_dealloc_top::allocate_max() +{ + + // Keep allocating until we hit the maximum number of channels + for(int i=0; i < d_ntx_chan; i++) { + d_tx->send(s_cmd_allocate_channel, + pmt_list2(pmt_list2(s_response_allocate_channel,PMT_T), + pmt_from_long(1))); // 1 byte is good enough + + d_nalloc_to_recv++; + } + + for(int i=0; i < d_nrx_chan; i++) { + d_rx->send(s_cmd_allocate_channel, + pmt_list2(pmt_list2(s_response_allocate_channel,PMT_T), + pmt_from_long(1))); + + d_nalloc_to_recv++; + } + +} + +void +qa_dealloc_top::deallocate_all() { + + // Deallocate all of the channels that were allocated from allocate_max() + for(int i=0; i < (int)d_tx_chans.size(); i++) { + + if(verbose) + std::cout << "[qa_dealloc_top] Trying to dealloc TX " + << d_tx_chans[i] << std::endl; + + d_tx->send(s_cmd_deallocate_channel, + pmt_list2(pmt_list2(s_response_deallocate_channel,PMT_T), + pmt_from_long(d_tx_chans[i]))); + + d_ndealloc_to_recv++; + } + + // Deallocate the RX side now + for(int i=0; i < (int)d_rx_chans.size(); i++) { + + if(verbose) + std::cout << "[qa_dealloc_top] Trying to dealloc RX " + << d_tx_chans[i] << std::endl; + + d_rx->send(s_cmd_deallocate_channel, + pmt_list2(pmt_list2(s_response_deallocate_channel,PMT_T), + pmt_from_long(d_rx_chans[i]))); + + d_ndealloc_to_recv++; + } + + // Should get permission denied errors trying to re-dealloc the channels, as + // we no longer have permission to them after deallocating + for(int i=0; i < (int)d_tx_chans.size(); i++) { + + d_tx->send(s_cmd_deallocate_channel, + pmt_list2(pmt_list2(s_response_deallocate_channel, + s_err_channel_permission_denied), + pmt_from_long(d_tx_chans[i]))); + + d_ndealloc_to_recv++; + } + + // Same for RX + for(int i=0; i < (int)d_rx_chans.size(); i++) { + + d_rx->send(s_cmd_deallocate_channel, + pmt_list2(pmt_list2(s_response_deallocate_channel, + s_err_channel_permission_denied), + pmt_from_long(d_rx_chans[i]))); + + d_ndealloc_to_recv++; + } + + // Try to deallocate a channel that doesn't exist on both sides, the last + // element in the vectors is the highest channel number, so we take that plus + // 1 + d_ndealloc_to_recv+=2; + d_tx->send(s_cmd_deallocate_channel, + pmt_list2(pmt_list2(s_response_deallocate_channel, + s_err_channel_invalid), + pmt_from_long(d_rx_chans.back()+1))); + + d_rx->send(s_cmd_deallocate_channel, + pmt_list2(pmt_list2(s_response_deallocate_channel, + s_err_channel_invalid), + pmt_from_long(d_rx_chans.back()+1))); + + + // The used capacity should be back to 0 now that we've deallocated everything + d_cs->send(s_cmd_current_capacity_allocation, + pmt_list1(pmt_list2(s_response_current_capacity_allocation, + PMT_T))); +} + +void +qa_dealloc_top::handle_message(mb_message_sptr msg) +{ + pmt_t data = msg->data(); + pmt_t event = msg->signal(); + + if(pmt_eq(event, pmt_intern("%shutdown"))) + return; + + pmt_t expected = pmt_nth(0, data); + pmt_t status = pmt_nth(1, data); + + pmt_t e_event = pmt_nth(0, expected); + pmt_t e_status = pmt_nth(1, expected); + + if(!pmt_eqv(e_status, status) || !pmt_eqv(e_event, event)) { + if(verbose) + std::cout << "Got: " << status << " Expected: " << e_status << "\n"; + shutdown_all(PMT_F); + return; + } else { + if(verbose) + std::cout << "[qa_alloc_top] Received expected response for message " + << d_ndealloc_recvd + << " (" << event << ")\n"; + } + + if (pmt_eq(msg->port_id(), d_tx->port_symbol()) + || pmt_eq(msg->port_id(), d_rx->port_symbol())) { + + if(pmt_eq(msg->signal(), s_response_allocate_channel)) { + check_allocation(msg); + } + + } + + if (pmt_eq(msg->port_id(), d_cs->port_symbol())) { + + if(pmt_eq(msg->signal(), s_response_max_capacity)) { + d_max_capacity = pmt_to_long(pmt_nth(2, data)); + } + else if(pmt_eq(msg->signal(), s_response_ntx_chan)) { + d_ntx_chan = pmt_to_long(pmt_nth(2, data)); + } + else if(pmt_eq(msg->signal(), s_response_nrx_chan)) { + d_nrx_chan = pmt_to_long(pmt_nth(2, data)); + } + else if(pmt_eq(msg->signal(), s_response_current_capacity_allocation)) { + // the final command is a capacity check which should be 0, then we + // shutdown + pmt_t expected_result = pmt_from_long(0); + pmt_t result = pmt_nth(2, data); + + if(pmt_eqv(expected_result, result)) { + shutdown_all(PMT_T); + return; + } else { + shutdown_all(PMT_F); + return; + } + } + + d_nstatus++; + + if(d_nstatus==d_nstatus_to_recv) + allocate_max(); + } +} + + +void +qa_dealloc_top::check_allocation(mb_message_sptr msg) +{ + pmt_t data = msg->data(); + pmt_t event = msg->signal(); + + pmt_t expected = pmt_nth(0, data); + pmt_t status = pmt_nth(1, data); + pmt_t channel = pmt_nth(2, data); + + d_nalloc_recvd++; + + if(!pmt_eqv(status, PMT_T)) { + shutdown_all(PMT_F); + return; + } else { + // store all of the allocate channel numbers + if(pmt_eq(msg->port_id(), d_tx->port_symbol())) + d_tx_chans.push_back(pmt_to_long(channel)); + if(pmt_eq(msg->port_id(), d_rx->port_symbol())) + d_rx_chans.push_back(pmt_to_long(channel)); + } + + if(d_nalloc_recvd == d_nalloc_to_recv) { + + if(verbose) { + std::cout << "[qa_dealloc_top] Allocated TX channels: "; + for(int i=0; i < (int)d_tx_chans.size(); i++) + std::cout << d_tx_chans[i] << " "; + + std::cout << "\n[qa_dealloc_top] Allocated RX channels: "; + for(int i=0; i < (int)d_rx_chans.size(); i++) + std::cout << d_rx_chans[i] << " "; + std::cout << "\n"; + } + + deallocate_all(); // once we've allocated all of our channels, try to + // dealloc them + } +} + +REGISTER_MBLOCK_CLASS(qa_dealloc_top); + +// ---------------------------------------------------------------------------------------------- + +class qa_open_close_top : public mb_mblock +{ + mb_port_sptr d_cs; + + long d_max_capacity; + + long d_nmsg_to_recv; + long d_nmsg_recvd; + + public: + qa_open_close_top(mb_runtime *runtime, const std::string &instance_name, pmt_t user_arg); + ~qa_open_close_top(); + void initial_transition(); + void handle_message(mb_message_sptr msg); + + protected: + void check_cs(mb_message_sptr msg); + void run_tests(); +}; + +qa_open_close_top::qa_open_close_top(mb_runtime *runtime, const std::string &instance_name, pmt_t user_arg) + : mb_mblock(runtime, instance_name, user_arg) +{ + + d_nmsg_to_recv=7; + d_nmsg_recvd=0; + + d_cs = define_port("cs", "usrp-server-cs", false, mb_port::INTERNAL); + + // Use the stub with the usrp_server + pmt_t usrp_server_dict = pmt_make_dict(); + pmt_dict_set(usrp_server_dict, pmt_intern("fake-usrp"),PMT_T); + + // Test the TX side + define_component("server", "usrp_server", usrp_server_dict); + connect("self", "cs", "server", "cs"); +} + +qa_open_close_top::~qa_open_close_top(){} + +void +qa_open_close_top::initial_transition() +{ + run_tests(); +} + +void +qa_open_close_top::run_tests() +{ + // std::cout << "[qa_open_close_top] Starting tests\n"; + + // A close before an open should fail + d_cs->send(s_cmd_close, pmt_list1(pmt_list2(s_response_close, + s_err_usrp_already_closed))); + + // Perform an open, and a second open which should fail + d_cs->send(s_cmd_open, + pmt_list2(pmt_list2(s_response_open,PMT_T), + pmt_from_long(0))); + + d_cs->send(s_cmd_open, + pmt_list2(pmt_list2(s_response_open, + s_err_usrp_already_opened), + pmt_from_long(0))); + + // A close should now be successful since the interface is open + d_cs->send(s_cmd_close, pmt_list1(pmt_list2(s_response_close,PMT_T))); + + // But, a second close should fail + d_cs->send(s_cmd_close, pmt_list1(pmt_list2(s_response_close, + s_err_usrp_already_closed))); + + // Just to be thorough, try an open and close again + d_cs->send(s_cmd_open, + pmt_list2(pmt_list2(s_response_open,PMT_T), + pmt_from_long(0))); + + d_cs->send(s_cmd_close, pmt_list1(pmt_list2(s_response_close,PMT_T))); + +} + + +void +qa_open_close_top::handle_message(mb_message_sptr msg) +{ + pmt_t data = msg->data(); + + if (pmt_eq(msg->port_id(), d_cs->port_symbol())) { + check_cs(msg); + } + + d_nmsg_recvd++; + + if(d_nmsg_to_recv == d_nmsg_recvd) + shutdown_all(PMT_T); +} + +void +qa_open_close_top::check_cs(mb_message_sptr msg) +{ + pmt_t data = msg->data(); + pmt_t event = msg->signal(); + + pmt_t expected = pmt_nth(0, data); + pmt_t status = pmt_nth(1, data); + + pmt_t e_event = pmt_nth(0, expected); + pmt_t e_status = pmt_nth(1, expected); + + if(!pmt_eqv(e_status, status) || !pmt_eqv(e_event, event)) { + + if(verbose) + std::cout << "[qa_open_close_top] FAILED check_cs... Got: " << status + << " Expected: " << e_status + << " for event " << event << "\n"; + + shutdown_all(PMT_F); + } else { + if(verbose) + std::cout << "[qa_open_close_top] Received expected CS response (" + << event << ")\n"; + } + +} + +REGISTER_MBLOCK_CLASS(qa_open_close_top); + +// ---------------------------------------------------------------------------------------------- + +class qa_tx_top : public mb_mblock +{ + mb_port_sptr d_tx; + mb_port_sptr d_rx; + mb_port_sptr d_cs; + + long d_max_capacity; + long d_ntx_chan, d_nrx_chan; + + long d_tx_chan; + long d_rx_chan; + + long d_nmsg_to_recv; + long d_nmsg_recvd; + + public: + qa_tx_top(mb_runtime *runtime, const std::string &instance_name, pmt_t user_arg); + ~qa_tx_top(); + void initial_transition(); + void handle_message(mb_message_sptr msg); + + protected: + void check_allocation(mb_message_sptr msg); + void check_deallocation(mb_message_sptr msg); + void check_xmit(mb_message_sptr msg); + void check_cs(mb_message_sptr msg); + void run_tests(); +}; + +qa_tx_top::qa_tx_top(mb_runtime *runtime, const std::string &instance_name, pmt_t user_arg) + : mb_mblock(runtime, instance_name, user_arg) +{ + + d_nmsg_to_recv=10; + d_nmsg_recvd=0; + + d_rx = define_port("rx0", "usrp-rx", false, mb_port::INTERNAL); + d_tx = define_port("tx0", "usrp-tx", false, mb_port::INTERNAL); + d_cs = define_port("cs", "usrp-server-cs", false, mb_port::INTERNAL); + + // Use the stub with the usrp_server + pmt_t usrp_server_dict = pmt_make_dict(); + pmt_dict_set(usrp_server_dict, pmt_intern("fake-usrp"),PMT_T); + + // Test the TX side + define_component("server", "usrp_server", usrp_server_dict); + connect("self", "tx0", "server", "tx0"); + connect("self", "rx0", "server", "rx0"); + connect("self", "cs", "server", "cs"); +} + +qa_tx_top::~qa_tx_top(){} + +void +qa_tx_top::initial_transition() +{ + run_tests(); +} + +void +qa_tx_top::run_tests() +{ + if(verbose) + std::cout << "[qa_tx_top] Starting tests\n"; + + // A transmit before an open should fail + d_tx->send(s_cmd_xmit_raw_frame, + pmt_list4(pmt_list2(s_response_xmit_raw_frame, + s_err_usrp_not_opened), + pmt_from_long(0), + pmt_make_u32vector(transport_pkt::max_payload()/4, 0), + pmt_from_long(0))); + + // Now open + d_cs->send(s_cmd_open, + pmt_list2(pmt_list2(s_response_open,PMT_T), + pmt_from_long(0))); + + // Try to transmit on a channel that we have no allocation for + d_tx->send(s_cmd_xmit_raw_frame, + pmt_list4(pmt_list2(s_response_xmit_raw_frame, + s_err_channel_permission_denied), + pmt_from_long(0), + pmt_make_u32vector(transport_pkt::max_payload()/4, 0), + pmt_from_long(0))); + + // Get a channel allocation and send on it, we assume 0 (FIXME) until 'defer' + // is implemented for simplicity + d_tx->send(s_cmd_allocate_channel, + pmt_list2(pmt_list2(s_response_allocate_channel, PMT_T), + pmt_from_long(1))); + + d_tx->send(s_cmd_xmit_raw_frame, + pmt_list4(pmt_list2(s_response_xmit_raw_frame, PMT_T), + pmt_from_long(0), + pmt_make_u32vector(transport_pkt::max_payload()/4, 0), + pmt_from_long(0))); + + // Close should be successful + d_cs->send(s_cmd_close, pmt_list1(pmt_list2(s_response_close,PMT_T))); + + // After closing, a new transmit raw frame should fail again + d_tx->send(s_cmd_xmit_raw_frame, + pmt_list4(pmt_list2(s_response_xmit_raw_frame, + s_err_usrp_not_opened), + pmt_from_long(0), + pmt_make_u32vector(transport_pkt::max_payload()/4, 0), + pmt_from_long(0))); + + // Reopen and retry before getting an allocation, the first xmit should fail, + // after we allocate it should work again + d_cs->send(s_cmd_open, + pmt_list2(pmt_list2(s_response_open, PMT_T), + pmt_from_long(0))); + + d_tx->send(s_cmd_xmit_raw_frame, + pmt_list4(pmt_list2(s_response_xmit_raw_frame, + s_err_channel_permission_denied), + pmt_from_long(0), + pmt_make_u32vector(transport_pkt::max_payload()/4, 0), + pmt_from_long(0))); + + d_tx->send(s_cmd_allocate_channel, + pmt_list2(pmt_list2(s_response_allocate_channel, PMT_T), + pmt_from_long(1))); + + d_tx->send(s_cmd_xmit_raw_frame, + pmt_list4(pmt_list2(s_response_xmit_raw_frame,PMT_T), + pmt_from_long(0), + pmt_make_u32vector(transport_pkt::max_payload()/4, 0), + pmt_from_long(0))); + + // A final close which should be successful + d_cs->send(s_cmd_close, pmt_list1(pmt_list2(s_response_close,PMT_T))); + +} + + +void +qa_tx_top::handle_message(mb_message_sptr msg) +{ + pmt_t data = msg->data(); + pmt_t event = msg->signal(); + + if(pmt_eq(event, pmt_intern("%shutdown"))) + return; + + pmt_t expected = pmt_nth(0, data); + pmt_t status = pmt_nth(1, data); + + pmt_t e_event = pmt_nth(0, expected); + pmt_t e_status = pmt_nth(1, expected); + + if(!pmt_eqv(e_status, status) || !pmt_eqv(e_event, event)) { + if(verbose) + std::cout << "[qa_xmit_top] Got: " << status + << " Expected: " << e_status + << "For signal: " << event << "\n"; + shutdown_all(PMT_F); + return; + } else { + if(verbose) + std::cout << "[qa_xmit_top] Received expected response for message " + << d_nmsg_recvd + << " (" << event << ")\n"; + } + + if (pmt_eq(msg->port_id(), d_tx->port_symbol()) + || pmt_eq(msg->port_id(), d_rx->port_symbol())) { + + if(pmt_eq(msg->signal(), s_response_allocate_channel)) + check_allocation(msg); + + } + + d_nmsg_recvd++; + + if(d_nmsg_to_recv == d_nmsg_recvd){ + shutdown_all(PMT_T); + return; + } +} + +void +qa_tx_top::check_allocation(mb_message_sptr msg) +{ + pmt_t data = msg->data(); + pmt_t event = msg->signal(); + + pmt_t expected = pmt_nth(0, data); + pmt_t status = pmt_nth(1, data); + pmt_t channel = pmt_nth(2, data); + + if(pmt_eqv(status, PMT_T)) { + // store all of the allocate channel numbers + if(pmt_eq(msg->port_id(), d_tx->port_symbol())) + d_tx_chan = pmt_to_long(channel); + if(pmt_eq(msg->port_id(), d_rx->port_symbol())) + d_rx_chan = pmt_to_long(channel); + } +} + +REGISTER_MBLOCK_CLASS(qa_tx_top); + +// ---------------------------------------------------------------------------------------------- + +class qa_rx_top : public mb_mblock +{ + mb_port_sptr d_rx; + mb_port_sptr d_cs; + + long d_max_capacity; + long d_ntx_chan, d_nrx_chan; + + long d_rx_chan; + + bool d_got_response_recv; + + mb_time d_t0; + double d_delta_t; + + public: + qa_rx_top(mb_runtime *runtime, const std::string &instance_name, pmt_t user_arg); + ~qa_rx_top(); + void initial_transition(); + void handle_message(mb_message_sptr msg); + + protected: + void check_allocation(mb_message_sptr msg); + void check_deallocation(mb_message_sptr msg); + void check_xmit(mb_message_sptr msg); + void check_cs(mb_message_sptr msg); + void run_tests(); +}; + +qa_rx_top::qa_rx_top(mb_runtime *runtime, const std::string &instance_name, pmt_t user_arg) + : mb_mblock(runtime, instance_name, user_arg), + d_got_response_recv(false) +{ + + d_rx = define_port("rx0", "usrp-rx", false, mb_port::INTERNAL); + d_cs = define_port("cs", "usrp-server-cs", false, mb_port::INTERNAL); + + // Use the stub with the usrp_server + pmt_t usrp_dict = pmt_make_dict(); + // Set TX and RX interpolations + pmt_dict_set(usrp_dict, + pmt_intern("decim-rx"), + pmt_from_long(128)); + pmt_dict_set(usrp_dict, pmt_intern("fake-usrp"), PMT_T); + + // Test the TX side + define_component("server", "usrp_server", usrp_dict); + connect("self", "rx0", "server", "rx0"); + connect("self", "cs", "server", "cs"); +} + +qa_rx_top::~qa_rx_top(){} + +void +qa_rx_top::initial_transition() +{ + run_tests(); +} + +void +qa_rx_top::run_tests() +{ + if(verbose) + std::cout << "[qa_rx_top] Starting tests\n"; + + d_cs->send(s_cmd_open, pmt_list2(pmt_list2(s_response_open,PMT_T), pmt_from_long(0))); + + d_rx->send(s_cmd_allocate_channel, + pmt_list2(pmt_list2(s_response_allocate_channel,PMT_T), + pmt_from_long(1))); + + d_rx->send(s_cmd_start_recv_raw_samples, + pmt_list2(PMT_NIL, + pmt_from_long(0))); + + // Schedule a small timeout in which we expect to have received at least one + // packet worth of samples from the stub + d_t0 = mb_time::time(); + schedule_one_shot_timeout(d_t0 + 0.01, PMT_NIL); +} + + +void +qa_rx_top::handle_message(mb_message_sptr msg) +{ + pmt_t data = msg->data(); + pmt_t event = msg->signal(); + + if(pmt_eq(event, pmt_intern("%shutdown"))) + return; + + pmt_t expected = pmt_nth(0, data); + pmt_t status = pmt_nth(1, data); + + // If we get a timeout we shutdown + if(pmt_eq(event, s_timeout)) { + if(verbose) + std::cout << "[qa_rx_top] Got timeout\n"; + d_rx->send(s_cmd_stop_recv_raw_samples, + pmt_list2(PMT_NIL, + pmt_from_long(0))); + + d_cs->send(s_cmd_close, pmt_list1(pmt_list2(s_response_close,PMT_T))); + return; + } + + // For testing RX, an invocation handle is not generated by the stub, + // therefore the same approach for testing is not used. We simply + // expect all responses to be true. + if(pmt_eq(event, s_response_recv_raw_samples)) { + if(pmt_eqv(status, PMT_T)) { + + if(verbose) + std::cout << "[qa_rx_top] Received expected response for message " + << " (" << event << ")\n"; + + // All we want is 1 response receive! Can't guarantee exact numbers + d_got_response_recv = true; + } + else { + if(verbose) + std::cout << "Got: " << status << " Expected: " << PMT_T << "\n"; + shutdown_all(PMT_F); + } + return; + } + + pmt_t e_event = pmt_nth(0, expected); + pmt_t e_status = pmt_nth(1, expected); + + if(!pmt_eqv(e_status, status) || !pmt_eqv(e_event, event)) { + if(verbose) + std::cout << "Got: " << status << " Expected: " << e_status << "\n"; + shutdown_all(PMT_F); + return; + } else { + if(verbose) + std::cout << "[qa_rx_top] Received expected response for message " + << " (" << event << ")\n"; + } + + if (pmt_eq(msg->port_id(), d_rx->port_symbol())) { + + if(pmt_eq(msg->signal(), s_response_allocate_channel)) + check_allocation(msg); + + } + + // We stop when we get a close, we are successful if we + // got a response from recv, fail if we never got a recv response + if(pmt_eq(msg->signal(), s_response_close)) { + + if(d_got_response_recv) { + shutdown_all(PMT_T); + return; + } + else { + shutdown_all(PMT_F); + if(verbose) + std::cout << "[qa_rx_top] No response message before close\n"; + return; + } + } +} + + +void +qa_rx_top::check_allocation(mb_message_sptr msg) +{ + pmt_t data = msg->data(); + pmt_t event = msg->signal(); + + pmt_t expected = pmt_nth(0, data); + pmt_t status = pmt_nth(1, data); + pmt_t channel = pmt_nth(2, data); + + if(pmt_eqv(status, PMT_T)) { + // store all of the allocate channel numbers + if(pmt_eq(msg->port_id(), d_rx->port_symbol())) + d_rx_chan = pmt_to_long(channel); + } +} + +REGISTER_MBLOCK_CLASS(qa_rx_top); + +// ---------------------------------------------------------------------------------------------- + +class qa_rid_top : public mb_mblock +{ + mb_port_sptr d_tx; + mb_port_sptr d_rx; + mb_port_sptr d_cs; + + long d_npongs; + long d_tcycles; + long d_cycles; + long d_max_rid; + + mb_time d_t0; + double d_delta_t; + + public: + qa_rid_top(mb_runtime *runtime, const std::string &instance_name, pmt_t user_arg); + ~qa_rid_top(); + void initial_transition(); + void handle_message(mb_message_sptr msg); + + protected: + void run_tests(); + void send_max_pings(); +}; + +qa_rid_top::qa_rid_top(mb_runtime *runtime, const std::string &instance_name, pmt_t user_arg) + : mb_mblock(runtime, instance_name, user_arg) +{ + d_npongs = 0; + d_tcycles = 3; + d_cycles = d_tcycles; + d_max_rid = usrp_server::D_MAX_RID; + d_delta_t = 0.1; + + + d_rx = define_port("rx0", "usrp-rx", false, mb_port::INTERNAL); + d_tx = define_port("tx0", "usrp-tx", false, mb_port::INTERNAL); + d_cs = define_port("cs", "usrp-server-cs", false, mb_port::INTERNAL); + + // Use the stub with the usrp_server + pmt_t usrp_server_dict = pmt_make_dict(); + pmt_dict_set(usrp_server_dict, pmt_intern("fake-usrp"),PMT_T); + + // Test the TX side + define_component("server", "usrp_server", usrp_server_dict); + connect("self", "tx0", "server", "tx0"); + connect("self", "rx0", "server", "rx0"); + connect("self", "cs", "server", "cs"); + +} + +qa_rid_top::~qa_rid_top(){} + +void +qa_rid_top::initial_transition() +{ + run_tests(); +} + +void +qa_rid_top::run_tests() +{ + if(verbose) + std::cout << "[qa_rid_top] Starting tests...\n"; + + // Retrieve information about the USRP, then run tests + d_cs->send(s_cmd_open, + pmt_list2(pmt_list2(s_response_open, PMT_T), + pmt_from_long(0))); + + // should be able to allocate 1 byte + d_tx->send(s_cmd_allocate_channel, + pmt_list2(pmt_list2(s_response_allocate_channel, PMT_T), + pmt_from_long(1))); + + d_rx->send(s_cmd_allocate_channel, + pmt_list2(pmt_list2(s_response_allocate_channel, PMT_T), + pmt_from_long(1))); + + // Need to start receiving to read from the USRP to get C/S responses + d_rx->send(s_cmd_start_recv_raw_samples, + pmt_list2(PMT_NIL, + pmt_from_long(0))); + + // Build a subpacket of MAX_RID pings and wait a small amount for all of the + // responses and fire off another MAX_RID. If MAX_RID*2 responses are + // received, the RID recycling is working correctly. + // Schedule a timer in which we expect to have received all of the responses, + // which will send off another MAX_RID worth. + send_max_pings(); + d_t0 = mb_time::time(); + schedule_one_shot_timeout(d_t0 + d_delta_t, PMT_NIL); +} + +void +qa_rid_top::send_max_pings() +{ + pmt_t ping = pmt_list2(s_op_ping_fixed, + pmt_list2(pmt_from_long(0), + pmt_from_long(0))); + + pmt_t sub_packets = PMT_NIL; + + for(int i=0; i<d_max_rid; i++) + sub_packets = pmt_list_add(sub_packets, ping); + + d_tx->send(s_cmd_to_control_channel, + pmt_list2(pmt_list2(s_response_from_control_channel, PMT_T), + sub_packets)); +} + +void +qa_rid_top::handle_message(mb_message_sptr msg) +{ + pmt_t data = msg->data(); + pmt_t event = msg->signal(); + + // If we get a timeout we ensure we got a maximum RID number of responses. + if(pmt_eq(event, s_timeout)) { + if(verbose) + std::cout << "[qa_rid_top] Got timeout, received so far: " + << d_npongs << "\n"; + + d_cycles--; + + if(d_cycles==0 && d_npongs == d_max_rid*d_tcycles) { + shutdown_all(PMT_T); + } + else if(d_cycles==0) { + + std::cout << "[qa_rid_top] d_npongs: " << d_npongs + << " expected: " << d_max_rid*d_tcycles + << std::endl; + + shutdown_all(PMT_F); + } + else { + send_max_pings(); + d_t0 = mb_time::time(); + schedule_one_shot_timeout(d_t0 + d_delta_t, PMT_NIL); + } + + } + else if(pmt_eq(event, s_response_from_control_channel)) + { + d_npongs++; + } + +} + +REGISTER_MBLOCK_CLASS(qa_rid_top); + + +// ---------------------------------------------------------------------------------------------- + +class qa_cs_top : public mb_mblock +{ + mb_port_sptr d_tx; + mb_port_sptr d_rx; + mb_port_sptr d_cs; + + long d_nmsgs_to_recv; + long d_nrecvd; + + long d_max_capacity; + long d_ntx_chan, d_nrx_chan; + + long d_nstatus; + long d_nstatus_to_recv; + + public: + qa_cs_top(mb_runtime *runtime, const std::string &instance_name, pmt_t user_arg); + ~qa_cs_top(); + void initial_transition(); + void handle_message(mb_message_sptr msg); + + protected: + void check_message(mb_message_sptr msg); + void run_tests(); +}; + +qa_cs_top::qa_cs_top(mb_runtime *runtime, const std::string &instance_name, pmt_t user_arg) + : mb_mblock(runtime, instance_name, user_arg) +{ + d_nrecvd=0; + d_nmsgs_to_recv = 8; + d_nstatus=0; + d_nstatus_to_recv = 50; + + d_rx = define_port("rx0", "usrp-rx", false, mb_port::INTERNAL); + d_tx = define_port("tx0", "usrp-tx", false, mb_port::INTERNAL); + d_cs = define_port("cs", "usrp-server-cs", false, mb_port::INTERNAL); + + // Use the stub with the usrp_server + pmt_t usrp_server_dict = pmt_make_dict(); + pmt_dict_set(usrp_server_dict, pmt_intern("fake-usrp"),PMT_T); + + // Test the TX side + define_component("server", "usrp_server", usrp_server_dict); + connect("self", "tx0", "server", "tx0"); + connect("self", "rx0", "server", "rx0"); + connect("self", "cs", "server", "cs"); + +} + +qa_cs_top::~qa_cs_top(){} + +void +qa_cs_top::initial_transition() +{ + run_tests(); +} + +void +qa_cs_top::run_tests() +{ + if(verbose) + std::cout << "[qa_cs_top] Starting tests...\n"; + + // Retrieve information about the USRP, then run tests + d_cs->send(s_cmd_open, + pmt_list2(pmt_list2(s_response_open, PMT_T), + pmt_from_long(0))); + + // should be able to allocate 1 byte + d_tx->send(s_cmd_allocate_channel, + pmt_list2(pmt_list2(s_response_allocate_channel, PMT_T), + pmt_from_long(1))); + + d_rx->send(s_cmd_allocate_channel, + pmt_list2(pmt_list2(s_response_allocate_channel, PMT_T), + pmt_from_long(1))); + + // Need to start receiving to read from the USRP to get C/S responses + d_rx->send(s_cmd_start_recv_raw_samples, + pmt_list2(PMT_NIL, + pmt_from_long(0))); + + d_tx->send(s_cmd_to_control_channel, + pmt_list2(pmt_list2(s_response_from_control_channel, PMT_T), + pmt_list1( + pmt_list2(s_op_ping_fixed, + pmt_list2(pmt_from_long(3), + pmt_from_long(0)))))); + + d_tx->send(s_cmd_to_control_channel, + pmt_list2(pmt_list2(s_response_from_control_channel, PMT_T), + pmt_list1( + pmt_list2(s_op_write_reg, + pmt_list2( + pmt_from_long(0x3), + pmt_from_long(0x4)))))); + + d_tx->send(s_cmd_to_control_channel, + pmt_list2(pmt_list2(s_response_from_control_channel, PMT_T), + pmt_list1( + pmt_list2(s_op_write_reg_masked, + pmt_list3( + pmt_from_long(0x3), + pmt_from_long(0x4), + pmt_from_long(0x5)))))); + + d_tx->send(s_cmd_to_control_channel, + pmt_list2(pmt_list2(s_response_from_control_channel, PMT_T), + pmt_list1( + pmt_list2(s_op_read_reg, + pmt_list2(pmt_from_long(0), + pmt_from_long(0x6)))))); + + d_tx->send(s_cmd_to_control_channel, + pmt_list2(pmt_list2(s_response_from_control_channel, PMT_T), + pmt_list1( + pmt_list2(s_op_delay, + pmt_list1(pmt_from_long(0x7)))))); + + pmt_t subpackets = pmt_list5( + pmt_list2(s_op_ping_fixed, pmt_list2(pmt_from_long(0), pmt_from_long(0))), + pmt_list2(s_op_delay, pmt_list1(pmt_from_long(0x7))), + pmt_list2(s_op_write_reg_masked, pmt_list3(pmt_from_long(3), + pmt_from_long(4), + pmt_from_long(5))), + pmt_list2(s_op_write_reg, pmt_list2(pmt_from_long(3), + pmt_from_long(4))), + pmt_list2(s_op_read_reg, pmt_list2(pmt_from_long(0), + pmt_from_long(6))) + ); + + d_tx->send(s_cmd_to_control_channel, + pmt_list2(pmt_list2(s_response_from_control_channel, PMT_T), + subpackets)); + + pmt_t i2c_data = pmt_make_u8vector(8, 0xff); + + subpackets = pmt_list2( + pmt_list2(s_op_i2c_write, + pmt_list2(pmt_from_long(8), i2c_data)), + pmt_list2(s_op_i2c_read, + pmt_list3(pmt_from_long(0), pmt_from_long(9), pmt_from_long(1))) + + ); + + d_tx->send(s_cmd_to_control_channel, + pmt_list2(pmt_list2(s_response_from_control_channel, PMT_T), + subpackets)); + +} + +void +qa_cs_top::handle_message(mb_message_sptr msg) +{ + pmt_t data = msg->data(); + + if ((pmt_eq(msg->port_id(), d_tx->port_symbol()) + || pmt_eq(msg->port_id(), d_rx->port_symbol())) + && pmt_eq(msg->signal(), s_response_allocate_channel)) + check_message(msg); + + if (pmt_eq(msg->port_id(), d_tx->port_symbol()) + && pmt_eq(msg->signal(), s_response_from_control_channel)) + check_message(msg); + + if (pmt_eq(msg->port_id(), d_cs->port_symbol())) { + + if(pmt_eq(msg->signal(), s_response_max_capacity)) { + d_max_capacity = pmt_to_long(pmt_nth(2, data)); + if(verbose) + std::cout << "[qa_cs_top] USRP has max capacity of " + << d_max_capacity << "\n"; + } + else if(pmt_eq(msg->signal(), s_response_ntx_chan)) { + d_ntx_chan = pmt_to_long(pmt_nth(2, data)); + if(verbose) + std::cout << "[qa_cs_top] USRP tx channels: " + << d_ntx_chan << "\n"; + } + else if(pmt_eq(msg->signal(), s_response_nrx_chan)) { + d_nrx_chan = pmt_to_long(pmt_nth(2, data)); + if(verbose) + std::cout << "[qa_cs_top] USRP rx channels: " + << d_nrx_chan << "\n"; + } + else if(pmt_eq(msg->signal(), s_response_current_capacity_allocation)) { + check_message(msg); + } + + d_nstatus++; + + check_message(msg); + + if(d_nstatus==d_nstatus_to_recv) + run_tests(); + } +} + +void +qa_cs_top::check_message(mb_message_sptr msg) +{ + pmt_t data = msg->data(); + pmt_t event = msg->signal(); + + pmt_t expected = pmt_nth(0, data); + pmt_t status = pmt_nth(1, data); + + pmt_t e_event = pmt_nth(0, expected); + pmt_t e_status = pmt_nth(1, expected); + + d_nrecvd++; + + + if(!pmt_eqv(e_status, status) || !pmt_eqv(e_event, event)) { + if(verbose) + std::cout << "[qa_cs_top] Got: " << status << " Expected: " << e_status << "\n"; + shutdown_all(PMT_F); + return; + } else { + if(verbose) + std::cout << "[qa_cs_top] Received expected response for message " + << d_nrecvd << " (" << event << ")\n"; + } + + if(d_nrecvd == d_nmsgs_to_recv) + shutdown_all(PMT_T); +} + +REGISTER_MBLOCK_CLASS(qa_cs_top); + +// ---------------------------------------------------------------------------------------------- + +void +qa_inband_usrp_server::test_open_close() +{ + mb_runtime_sptr rt = mb_make_runtime(); + pmt_t result = PMT_T; + + // std::cout << "\n\n----------------------------\n"; + // std::cout << " RUNNING OPEN/CLOSE TESTS \n"; + + rt->run("top", "qa_open_close_top", PMT_F, &result); + + CPPUNIT_ASSERT(pmt_equal(PMT_T, result)); +} + +void +qa_inband_usrp_server::test_chan_allocation() +{ + mb_runtime_sptr rt = mb_make_runtime(); + pmt_t result = PMT_T; + + // std::cout << "\n\n----------------------------\n"; + // std::cout << " RUNNING ALLOCATION TESTS \n"; + + rt->run("qa_alloc_top", "qa_alloc_top", PMT_F, &result); + + CPPUNIT_ASSERT(pmt_equal(PMT_T, result)); +} + +void +qa_inband_usrp_server::test_chan_deallocation() +{ + mb_runtime_sptr rt = mb_make_runtime(); + pmt_t result = PMT_T; + + // std::cout << "\n\n----------------------------\n"; + // std::cout << " RUNNING DEALLOCATION TESTS \n"; + + rt->run("qa_dealloc_top", "qa_dealloc_top", PMT_F, &result); + + CPPUNIT_ASSERT(pmt_equal(PMT_T, result)); +} + +void +qa_inband_usrp_server::test_tx() +{ + mb_runtime_sptr rt = mb_make_runtime(); + pmt_t result = PMT_T; + + // std::cout << "\n\n-----------------\n"; + // std::cout << " RUNNING TX TESTS \n"; + + rt->run("top", "qa_tx_top", PMT_F, &result); + + CPPUNIT_ASSERT(pmt_equal(PMT_T, result)); +} + +void +qa_inband_usrp_server::test_rx() +{ + mb_runtime_sptr rt = mb_make_runtime(); + pmt_t result = PMT_T; + + // std::cout << "\n\n-----------------\n"; + // std::cout << " RUNNING RX TESTS \n"; + + rt->run("top", "qa_rx_top", PMT_F, &result); + + CPPUNIT_ASSERT(pmt_equal(PMT_T, result)); +} + +void +qa_inband_usrp_server::test_cs() +{ + // FIXME This test is disabled because it hangs with the change to use usrp_standard_*_sptr's + return; + + mb_runtime_sptr rt = mb_make_runtime(); + pmt_t result = PMT_T; + + // std::cout << "\n\n-----------------\n"; + // std::cout << " RUNNING CS TESTS \n"; + + rt->run("top", "qa_cs_top", PMT_F, &result); + + CPPUNIT_ASSERT(pmt_equal(PMT_T, result)); +} + +void +qa_inband_usrp_server::test_rid() +{ + // FIXME This test is disabled because it hangs with the change to use usrp_standard_*_sptr's + return; + + mb_runtime_sptr rt = mb_make_runtime(); + pmt_t result = PMT_T; + + // std::cout << "\n\n-----------------\n"; + // std::cout << " RUNNING RID TESTS \n"; + + rt->run("top", "qa_rid_top", PMT_F, &result); + + CPPUNIT_ASSERT(pmt_equal(PMT_T, result)); +} diff --git a/usrp/limbo/inband/qa_inband_usrp_server.h b/usrp/limbo/inband/qa_inband_usrp_server.h new file mode 100644 index 000000000..52a4a0b06 --- /dev/null +++ b/usrp/limbo/inband/qa_inband_usrp_server.h @@ -0,0 +1,50 @@ +/* -*- c++ -*- */ +/* + * Copyright 2007 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 QA_INBAND_USRP_SERVER_H +#define QA_INBAND_USRP_SERVER_H + +#include <cppunit/extensions/HelperMacros.h> +#include <cppunit/TestCase.h> + +class qa_inband_usrp_server : public CppUnit::TestCase { + + CPPUNIT_TEST_SUITE(qa_inband_usrp_server); + CPPUNIT_TEST(test_open_close); + CPPUNIT_TEST(test_chan_allocation); + CPPUNIT_TEST(test_chan_deallocation); + CPPUNIT_TEST(test_tx); + CPPUNIT_TEST(test_rx); + CPPUNIT_TEST(test_cs); + CPPUNIT_TEST(test_rid); + CPPUNIT_TEST_SUITE_END(); + + private: + void test_chan_allocation(); + void test_chan_deallocation(); + void test_open_close(); + void test_tx(); + void test_rx(); + void test_cs(); + void test_rid(); +}; + +#endif /* INCLUDED_QA_INBAND_USRP_SERVER_H */ diff --git a/usrp/limbo/inband/symbols_usrp_channel.h b/usrp/limbo/inband/symbols_usrp_channel.h new file mode 100644 index 000000000..a0114cf3c --- /dev/null +++ b/usrp/limbo/inband/symbols_usrp_channel.h @@ -0,0 +1,40 @@ +/* -*- c++ -*- */ +/* + * Copyright 2007 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_SYMBOLS_USRP_CHANNEL_H +#define INCLUDED_SYMBOLS_USRP_CHANNEL_H + +#include <pmt.h> + +// Outgoing +static pmt_t s_cmd_allocate_channel = pmt_intern("cmd-allocate-channel"); +static pmt_t s_cmd_deallocate_channel = pmt_intern("cmd-deallocate-channel"); + +// Incoming +static pmt_t s_response_allocate_channel = pmt_intern("response-allocate-channel"); +static pmt_t s_response_deallocate_channel = pmt_intern("response-deallocate-channel"); + +// Errors +static pmt_t s_err_requested_capacity_unavailable = pmt_intern("err-requested-capacity-unavailable"); +static pmt_t s_err_channel_unavailable = pmt_intern("err-channel-unavailable"); +static pmt_t s_err_channel_invalid = pmt_intern("err-channel-invalid"); +static pmt_t s_err_channel_permission_denied = pmt_intern("err-channel-permission-denied"); + +#endif /* INCLUDED_SYMBOLS_USRP_CHANNEL_H */ diff --git a/usrp/limbo/inband/symbols_usrp_interface_cs.h b/usrp/limbo/inband/symbols_usrp_interface_cs.h new file mode 100644 index 000000000..72c8fcc91 --- /dev/null +++ b/usrp/limbo/inband/symbols_usrp_interface_cs.h @@ -0,0 +1,43 @@ +/* -*- c++ -*- */ +/* + * Copyright 2007 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_SYMBOLS_USRP_INTERFACE_CS_H +#define INCLUDED_SYMBOLS_USRP_INTERFACE_CS_H + +#include <pmt.h> + +// Outgoing +static pmt_t s_cmd_usrp_open = pmt_intern("cmd-usrp-open"); +static pmt_t s_cmd_usrp_close = pmt_intern("cmd-usrp-close"); +static pmt_t s_cmd_usrp_ntx_chan = pmt_intern("cmd-usrp-ntx-chan"); +static pmt_t s_cmd_usrp_nrx_chan = pmt_intern("cmd-usrp-nrx-chan"); +static pmt_t s_cmd_usrp_write = pmt_intern("cmd-usrp-write"); +static pmt_t s_cmd_usrp_start_reading = pmt_intern("cmd-usrp-start-reading"); +static pmt_t s_cmd_usrp_stop_reading = pmt_intern("cmd-usrp-stop-reading"); + +// Incoming +static pmt_t s_response_usrp_open = pmt_intern("response-usrp-open"); +static pmt_t s_response_usrp_close = pmt_intern("response-usrp-close"); +static pmt_t s_response_usrp_ntx_chan = pmt_intern("response-usrp-ntx-chan"); +static pmt_t s_response_usrp_nrx_chan = pmt_intern("response-usrp-nrx-chan"); +static pmt_t s_response_usrp_write = pmt_intern("response-usrp-write"); +static pmt_t s_response_usrp_read = pmt_intern("response-usrp-read"); + +#endif /* INCLUDED_SYMBOLS_USRP_INTERFACE_CS_H */ diff --git a/usrp/limbo/inband/symbols_usrp_low_level_cs.h b/usrp/limbo/inband/symbols_usrp_low_level_cs.h new file mode 100644 index 000000000..a7260603a --- /dev/null +++ b/usrp/limbo/inband/symbols_usrp_low_level_cs.h @@ -0,0 +1,47 @@ +/* -*- c++ -*- */ +/* + * Copyright 2007 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_SYMBOLS_USRP_LOW_LEVEL_CS_H +#define INCLUDED_SYMBOLS_USRP_LOW_LEVEL_CS_H + +#include <pmt.h> + +// Outgoing +static pmt_t s_cmd_to_control_channel = pmt_intern("cmd-to-control-channel"); + +// Incoming +static pmt_t s_response_from_control_channel = pmt_intern("response-from-control-channel"); + +// Subpackets +static pmt_t s_op_ping_fixed = pmt_intern("op-ping-fixed"); +static pmt_t s_op_ping_fixed_reply = pmt_intern("op-ping-fixed-reply"); +static pmt_t s_op_write_reg = pmt_intern("op-write-reg"); +static pmt_t s_op_write_reg_masked = pmt_intern("op-write-reg-masked"); +static pmt_t s_op_read_reg = pmt_intern("op-read-reg"); +static pmt_t s_op_read_reg_reply = pmt_intern("op-read-reg-reply"); +static pmt_t s_op_i2c_write = pmt_intern("op-i2c-write"); +static pmt_t s_op_i2c_read = pmt_intern("op-i2c-read"); +static pmt_t s_op_i2c_read_reply = pmt_intern("op-i2c-read-reply"); +static pmt_t s_op_spi_write = pmt_intern("op-spi-write"); +static pmt_t s_op_spi_read = pmt_intern("op-spi-read"); +static pmt_t s_op_spi_read_reply = pmt_intern("op-spi-read-reply"); +static pmt_t s_op_delay = pmt_intern("op-delay"); + +#endif /* INCLUDED_SYMBOLS_USRP_LOW_LEVEL_CS_H */ diff --git a/usrp/limbo/inband/symbols_usrp_rx.h b/usrp/limbo/inband/symbols_usrp_rx.h new file mode 100644 index 000000000..07d58a3f5 --- /dev/null +++ b/usrp/limbo/inband/symbols_usrp_rx.h @@ -0,0 +1,36 @@ +/* -*- c++ -*- */ +/* + * Copyright 2007 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_SYMBOLS_USRP_RX_H +#define INCLUDED_SYMBOLS_USRP_RX_H + +#include <pmt.h> + +// Outgoing +static pmt_t s_cmd_start_recv_raw_samples = pmt_intern("cmd-start-recv-raw-samples"); +static pmt_t s_cmd_stop_recv_raw_samples = pmt_intern("cmd-stop-recv-raw-samples"); + +// Incoming +static pmt_t s_response_recv_raw_samples = pmt_intern("response-recv-raw-samples"); + +// Errors +static pmt_t s_err_already_receiving = pmt_intern("err-already-receiving"); + +#endif /* INCLUDED_SYMBOLS_USRP_RX_H */ diff --git a/usrp/limbo/inband/symbols_usrp_rx_cs.h b/usrp/limbo/inband/symbols_usrp_rx_cs.h new file mode 100644 index 000000000..bf4f0338b --- /dev/null +++ b/usrp/limbo/inband/symbols_usrp_rx_cs.h @@ -0,0 +1,32 @@ +/* -*- c++ -*- */ +/* + * Copyright 2007 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_SYMBOLS_USRP_RX_CS_H +#define INCLUDED_SYMBOLS_USRP_RX_CS_H + +#include <pmt.h> + +// Outgoing +static pmt_t s_cmd_usrp_rx_start_reading = pmt_intern("cmd-usrp-rx-start-reading"); + +// Incoming +static pmt_t s_response_usrp_rx_read = pmt_intern("response-usrp-rx-read"); + +#endif /* INCLUDED_SYMBOLS_USRP_RX_CS_H */ diff --git a/usrp/limbo/inband/symbols_usrp_server_cs.h b/usrp/limbo/inband/symbols_usrp_server_cs.h new file mode 100644 index 000000000..e612e24ea --- /dev/null +++ b/usrp/limbo/inband/symbols_usrp_server_cs.h @@ -0,0 +1,47 @@ +/* -*- c++ -*- */ +/* + * Copyright 2007 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_SYMBOLS_USRP_SERVER_CS_H +#define INCLUDED_SYMBOLS_USRP_SERVER_CS_H + +#include <pmt.h> + +// Outgoing +static pmt_t s_cmd_open = pmt_intern("cmd-open"); +static pmt_t s_cmd_close = pmt_intern("cmd-close"); +static pmt_t s_cmd_max_capacity = pmt_intern("cmd-max-capacity"); +static pmt_t s_cmd_ntx_chan = pmt_intern("cmd-ntx-chan"); +static pmt_t s_cmd_nrx_chan = pmt_intern("cmd-nrx-chan"); +static pmt_t s_cmd_current_capacity_allocation = pmt_intern("cmd-current-capacity-allocation"); + +// Incoming +static pmt_t s_response_open = pmt_intern("response-open"); +static pmt_t s_response_close = pmt_intern("response-close"); +static pmt_t s_response_max_capacity = pmt_intern("response-max-capacity"); +static pmt_t s_response_ntx_chan = pmt_intern("response-ntx-chan"); +static pmt_t s_response_nrx_chan = pmt_intern("response-nrx-chan"); +static pmt_t s_response_current_capacity_allocation = pmt_intern("response-current-capacity-allocation"); + +// Errors +static pmt_t s_err_usrp_not_opened = pmt_intern("err-usrp-not-opened"); +static pmt_t s_err_usrp_already_opened = pmt_intern("err-usrp-already-opened"); +static pmt_t s_err_usrp_already_closed = pmt_intern("err-usrp-already-closed"); + +#endif /* INCLUDED_SYMBOLS_USRP_SERVER_CS_H */ diff --git a/usrp/limbo/inband/symbols_usrp_tx.h b/usrp/limbo/inband/symbols_usrp_tx.h new file mode 100644 index 000000000..4e58e2cf9 --- /dev/null +++ b/usrp/limbo/inband/symbols_usrp_tx.h @@ -0,0 +1,32 @@ +/* -*- c++ -*- */ +/* + * Copyright 2007 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_SYMBOLS_USRP_TX_H +#define INCLUDED_SYMBOLS_USRP_TX_H + +#include <pmt.h> + +// Outgoing +static pmt_t s_cmd_xmit_raw_frame = pmt_intern("cmd-xmit-raw-frame"); + +// Incoming +static pmt_t s_response_xmit_raw_frame = pmt_intern("response-xmit-raw-frame"); + +#endif /* INCLUDED_SYMBOLS_USRP_TX_H */ diff --git a/usrp/limbo/inband/symbols_usrp_tx_cs.h b/usrp/limbo/inband/symbols_usrp_tx_cs.h new file mode 100644 index 000000000..d02ef1d26 --- /dev/null +++ b/usrp/limbo/inband/symbols_usrp_tx_cs.h @@ -0,0 +1,32 @@ +/* -*- c++ -*- */ +/* + * Copyright 2007 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_SYMBOLS_USRP_TX_CS_H +#define INCLUDED_SYMBOLS_USRP_TX_CS_H + +#include <pmt.h> + +// Outgoing +static pmt_t s_cmd_usrp_tx_write = pmt_intern("cmd-usrp-tx-write"); + +// Incoming +static pmt_t s_response_usrp_tx_write = pmt_intern("response-usrp-tx-write"); + +#endif /* INCLUDED_SYMBOLS_USRP_TX_CS_H */ diff --git a/usrp/limbo/inband/test_inband.cc b/usrp/limbo/inband/test_inband.cc new file mode 100644 index 000000000..77cdca556 --- /dev/null +++ b/usrp/limbo/inband/test_inband.cc @@ -0,0 +1,36 @@ +/* -*- c++ -*- */ +/* + * Copyright 2007 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 <cppunit/TextTestRunner.h> +#include <qa_inband.h> + +int +main(int argc, char **argv) +{ + + CppUnit::TextTestRunner runner; + + runner.addTest(qa_inband::suite ()); + + bool was_successful = runner.run("", false); + + return was_successful ? 0 : 1; +} diff --git a/usrp/limbo/inband/usb_packet.py b/usrp/limbo/inband/usb_packet.py new file mode 100644 index 000000000..6dfcf8638 --- /dev/null +++ b/usrp/limbo/inband/usb_packet.py @@ -0,0 +1,115 @@ +# +# Copyright 2007 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 struct + + +FL_OVERRUN = 0x80000000 +FL_UNDERRUN = 0x40000000 +FL_DROPPED = 0x20000000 +FL_END_OF_BURST = 0x10000000 +FL_START_OF_BURST = 0x08000000 + +FL_ALL_FLAGS = 0xf8000000 + +FL_OVERRUN_SHIFT = 31 +FL_UNDERRUN_SHIFT = 30 +FL_DROPPED_SHIFT = 29 +FL_END_OF_BURST_SHIFT = 28 +FL_START_OF_BURST_SHIFT = 27 + +RSSI_MASK = 0x3f +RSSI_SHIFT = 21 + +CHAN_MASK = 0x1f +CHAN_SHIFT = 16 + +TAG_MASK = 0xf +TAG_SHIFT = 9 + +PAYLOAD_LEN_MASK = 0x1ff +PAYLOAD_LEN_SHIFT = 0 + +def make_header(flags, chan, payload_len, timestamp, rssi=0, tag=0): + word0 = ((flags & FL_ALL_FLAGS) + | ((rssi & RSSI_MASK) << RSSI_SHIFT) + | ((chan & CHAN_MASK) << CHAN_SHIFT) + | ((tag & TAG_MASK) << TAG_SHIFT) + | ((payload_len & PAYLOAD_LEN_MASK) << PAYLOAD_LEN_SHIFT)) + word1 = timestamp + return struct.pack('<2I', word0, word1) + + +def _decode(pred, indicator): + if pred: + return indicator + else: + return '-' + + +class usb_packet(object): + def __init__(self, raw_pkt): + assert isinstance(raw_pkt, str) and len(raw_pkt) == 512 + self._raw_pkt = raw_pkt; + (self._word0, self._word1) = struct.unpack('<2I', self._raw_pkt[0:8]) + + def timestamp(self): + return self._word1 + + def rssi(self): + return (self._word0 >> RSSI_SHIFT) & RSSI_MASK + + def chan(self): + return (self._word0 >> CHAN_SHIFT) & CHAN_MASK + + def tag(self): + return (self._word0 >> TAG_SHIFT) & TAG_MASK + + def payload_len(self): + return (self._word0 >> PAYLOAD_LEN_SHIFT) & PAYLOAD_LEN_MASK + + def flags(self): + return self._word0 & FL_ALL_FLAGS + + def overrun(self): + return (self._word0 >> FL_OVERRUN_SHIFT) & 0x1 + + def underrun(self): + return (self._word0 >> FL_UNDERRUN_SHIFT) & 0x1 + + def start_of_burst(self): + return (self._word0 >> FL_START_OF_BURST_SHIFT) & 0x1 + + def end_of_burst(self): + return (self._word0 >> FL_END_OF_BURST_SHIFT) & 0x1 + + def dropped(self): + return (self._word0 >> FL_DROPPED_SHIFT) & 0x1 + + def payload(self): + return self._raw_pkt[8:8+self.payload_len()] + + def decoded_flags(self): + s = (_decode(self.overrun(), 'O') + + _decode(self.underrun(), 'U') + + _decode(self.dropped(), 'D') + + _decode(self.end_of_burst(), 'E') + + _decode(self.start_of_burst(), 'S')) + return s diff --git a/usrp/limbo/inband/usrp_inband_usb_packet.cc b/usrp/limbo/inband/usrp_inband_usb_packet.cc new file mode 100644 index 000000000..72bc45ccb --- /dev/null +++ b/usrp/limbo/inband/usrp_inband_usb_packet.cc @@ -0,0 +1,793 @@ +/* -*- c++ -*- */ +/* + * Copyright 2007 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 <usrp_inband_usb_packet.h> + +#include <usrp_bytesex.h> +#include <iostream> +#include <stdio.h> +#include <string.h> + +/*! + * \brief Aligns the packet payload on a 32 bit boundary. This is essential to + * all control/status packets so that the inband FPGA code can parse them + * easily. + * + * \returns true if successful or if the packet was already aligned; false if it + * cannot be aligned. + */ +bool usrp_inband_usb_packet::align32() +{ + int p_len = payload_len(); + + int bytes_needed = 4 - (p_len % 4); + + if(bytes_needed == 4) + return true; + + // If the room left in the packet is less than the number of bytes + // needed, return false to indicate no room to align + if((MAX_PAYLOAD - p_len) < bytes_needed) + return false; + + incr_header_len(bytes_needed); + + return true; +} + +/*! + * \brief Adds a ping command to the current control packet. + * + * The \p rid is the rid to be associated with the ping response and \p ping_val + * is currently unused. + * + * \returns true if adding the ping command was successful, false otherwise + * (i.e. no space in the current packet). + */ +bool usrp_inband_usb_packet::cs_ping(long rid, long ping_val) +{ + if(!align32()) + return false; + + int p_len = payload_len(); + + if((MAX_PAYLOAD - p_len) < (CS_PING_LEN + CS_FIXED_LEN)) + return false; + + uint32_t ping = ( + ((OP_PING_FIXED & CS_OPCODE_MASK) << CS_OPCODE_SHIFT) + | ((CS_PING_LEN & CS_LEN_MASK) << CS_LEN_SHIFT) + | ((rid & CS_RID_MASK) << CS_RID_SHIFT) + | (ping_val & CS_PINGVAL_MASK) + + ); + + uint32_t *payload = (uint32_t *) (d_payload + p_len); + *payload = host_to_usrp_u32(ping); + + // Update payload length + incr_header_len(CS_FIXED_LEN + CS_PING_LEN); + + return true; +} + +/*! + * \brief Adds a ping response to the packet. This is used by the fake USRP + * code to generate fake responses for pings. + * + * The \p rid is the RID to be associated with the response and \p ping_val is + * currently unused. + * + * \returns true if the ping reply was added successfully, false otherwise. + */ +bool usrp_inband_usb_packet::cs_ping_reply(long rid, long ping_val) +{ + if(!align32()) + return false; + + int p_len = payload_len(); + + if((MAX_PAYLOAD - p_len) < (CS_PING_LEN + CS_FIXED_LEN)) + return false; + + uint32_t ping = ( + ((OP_PING_FIXED_REPLY & CS_OPCODE_MASK) << CS_OPCODE_SHIFT) + | ((CS_PING_LEN & CS_LEN_MASK) << CS_LEN_SHIFT) + | ((rid & CS_RID_MASK) << CS_RID_SHIFT) + | ((ping_val & CS_PINGVAL_MASK) << CS_PINGVAL_SHIFT) + + ); + + uint32_t *payload = (uint32_t *) (d_payload + p_len); + *payload = host_to_usrp_u32(ping); + + // Update payload length + incr_header_len(CS_FIXED_LEN + CS_PING_LEN); + + return true; +} + +/*! + * \brief Adds a write register command to the packet. + * + * The \p reg_num is the register number for which the value \p val will be + * written to. + * + * \returns true if the command was added to the packet successfully, false + * otherwise. + */ +bool usrp_inband_usb_packet::cs_write_reg(long reg_num, long val) +{ + if(!align32()) + return false; + + int p_len = payload_len(); + + if((MAX_PAYLOAD - p_len) < (CS_WRITEREG_LEN + CS_FIXED_LEN)) + return false; + + uint32_t word0 = 0; + + // Build the first word which includes the register number + word0 = ( + ((OP_WRITE_REG & CS_OPCODE_MASK) << CS_OPCODE_SHIFT) + | ((CS_WRITEREG_LEN & CS_LEN_MASK) << CS_LEN_SHIFT) + | ((reg_num & CS_REGNUM_MASK) << CS_REGNUM_SHIFT) + ); + + uint32_t *payload = (uint32_t *) (d_payload + p_len); + *payload = host_to_usrp_u32(word0); + + // The second word is solely the register value to be written + // FIXME: should this be unsigned? + payload += 1; + *payload = host_to_usrp_u32((uint32_t) val); + + // Rebuild the header to update the payload length + incr_header_len(CS_FIXED_LEN + CS_WRITEREG_LEN); + + return true; +} + +/*! + * \brief Adds a write register masked command to the packet. + * + * The \p reg_num is the register number for which the value \p val will be + * written, masked by \p mask + * + * \returns true if the command was added to the packet, false otherwise. + */ +bool usrp_inband_usb_packet::cs_write_reg_masked(long reg_num, long val, long mask) +{ + if(!align32()) + return false; + + int p_len = payload_len(); + + if((MAX_PAYLOAD - p_len) < (CS_WRITEREGMASKED_LEN + CS_FIXED_LEN)) + return false; + + uint32_t word0 = 0; + + // Build the first word which includes the register number + word0 = ( + ((OP_WRITE_REG_MASKED & CS_OPCODE_MASK) << CS_OPCODE_SHIFT) + | ((CS_WRITEREGMASKED_LEN & CS_LEN_MASK) << CS_LEN_SHIFT) + | ((reg_num & CS_REGNUM_MASK) << CS_REGNUM_SHIFT) + ); + + uint32_t *payload = (uint32_t *) (d_payload + p_len); + *payload = host_to_usrp_u32(word0); + + // Skip over the first word and write the register value + payload += 1; + *payload = host_to_usrp_u32((uint32_t) val); + + // Skip over the register value and write the mask + payload += 1; + *payload = host_to_usrp_u32((uint32_t) mask); + + // Rebuild the header to update the payload length + incr_header_len(CS_FIXED_LEN + CS_WRITEREGMASKED_LEN); + + return true; +} + +/*! + * \brief Adds a read register message to the packet. + * + * The \p rid will be the associated RID returned with the response, and \p + * reg_num is the register to be read. + * + * \returns true if the command was added to the packet, false otherwise. + */ +bool usrp_inband_usb_packet::cs_read_reg(long rid, long reg_num) +{ + if(!align32()) + return false; + + int p_len = payload_len(); + + if((MAX_PAYLOAD - p_len) < (CS_READREG_LEN + CS_FIXED_LEN)) + return false; + + uint32_t read_reg = ( + ((OP_READ_REG & CS_OPCODE_MASK) << CS_OPCODE_SHIFT) + | ((CS_READREG_LEN & CS_LEN_MASK) << CS_LEN_SHIFT) + | ((rid & CS_RID_MASK) << CS_RID_SHIFT) + | ((reg_num & CS_REGNUM_MASK) << CS_REGNUM_SHIFT) + + ); + + uint32_t *payload = (uint32_t *) (d_payload + p_len); + *payload = host_to_usrp_u32(read_reg); + + // Update payload length + incr_header_len(CS_FIXED_LEN + CS_READREG_LEN); + + return true; +} + +/*! + * \brief Adds a read register reply response to the current packet. This is + * used by the fake USRP code to generate fake register read responses for + * testing. + * + * The \p rid is the associated RID to be included in the response, \p reg_num + * is the register the read is coming from, and \p reg_val is the value of the + * read. + * + * \returns true if the command was added to the packet, false otherwise. + */ +bool usrp_inband_usb_packet::cs_read_reg_reply(long rid, long reg_num, long reg_val) +{ + if(!align32()) + return false; + + int p_len = payload_len(); + + if((MAX_PAYLOAD - p_len) < (CS_READREGREPLY_LEN + CS_FIXED_LEN)) + return false; + + uint32_t word0 = ( + ((OP_READ_REG_REPLY & CS_OPCODE_MASK) << CS_OPCODE_SHIFT) + | ((CS_READREGREPLY_LEN & CS_LEN_MASK) << CS_LEN_SHIFT) + | ((rid & CS_RID_MASK) << CS_RID_SHIFT) + | ((reg_num & CS_REGNUM_MASK) << CS_REGNUM_SHIFT) + + ); + + uint32_t *payload = (uint32_t *) (d_payload + p_len); + *payload = host_to_usrp_u32(word0); + + // Hop to the next word and write the reg value + payload += 1; + *payload = host_to_usrp_u32((uint32_t) reg_val); + + // Update payload length + incr_header_len(CS_FIXED_LEN + CS_READREGREPLY_LEN); + + return true; +} + +/*! + * \brief Adds a delay command to the current packet. + * + * The \p ticks parameter is the number of clock ticks the FPGA should delay + * parsing for, which is added to the packet. + * + * \returns true if the command was added to the packet, false otherwise. + */ +bool usrp_inband_usb_packet::cs_delay(long ticks) +{ + if(!align32()) + return false; + + int p_len = payload_len(); + + if((MAX_PAYLOAD - p_len) < (CS_DELAY_LEN + CS_FIXED_LEN)) + return false; + + uint32_t delay = ( + ((OP_DELAY & CS_OPCODE_MASK) << CS_OPCODE_SHIFT) + | ((CS_DELAY_LEN & CS_LEN_MASK) << CS_LEN_SHIFT) + | ((ticks & CS_DELAY_MASK) << CS_DELAY_SHIFT) + + ); + + uint32_t *payload = (uint32_t *) (d_payload + p_len); + *payload = host_to_usrp_u32(delay); + + // Update payload length + incr_header_len(CS_FIXED_LEN + CS_DELAY_LEN); + + return true; +} + +/*! + * \brief + * + * \returns true if the command was added to the packet, false otherwise. + */ +bool usrp_inband_usb_packet::cs_i2c_write(long i2c_addr, uint8_t *i2c_data, size_t data_len) +{ + if(!align32()) + return false; + + int p_len = payload_len(); + + int i2c_len = data_len + 2; // 2 bytes between mbz and addr + + if((MAX_PAYLOAD - p_len) < (i2c_len + CS_FIXED_LEN)) + return false; + + uint32_t word0 = 0; + + word0 = ( + ((OP_I2C_WRITE & CS_OPCODE_MASK) << CS_OPCODE_SHIFT) + | ((i2c_len & CS_LEN_MASK) << CS_LEN_SHIFT) + | ((i2c_addr & CS_I2CADDR_MASK) << CS_I2CADDR_SHIFT) + ); + + uint32_t *payload = (uint32_t *) (d_payload + p_len); + *payload = host_to_usrp_u32(word0); + + // Jump over the first word and write the data + // FIXME: Should the data be changed to usrp byte order? + payload += 1; + memcpy(payload, i2c_data, data_len); + + // Update payload length + incr_header_len(CS_FIXED_LEN + i2c_len); + + return true; +} + +/*! + * \brief Adds an I2C read command to the current packet. + * + * The \p rid is the associated RID to return with the read response, \p + * i2c_addr is the address to read from on the I2C bus, and \p n_bytes is the + * number of bytes to be read from the bus. + * + * \returns true if the command was added to the packet, false otherwise. + */ +bool usrp_inband_usb_packet::cs_i2c_read(long rid, long i2c_addr, long n_bytes) +{ + if(!align32()) + return false; + + int p_len = payload_len(); + + if((MAX_PAYLOAD - p_len) < (CS_I2CREAD_LEN + CS_FIXED_LEN)) + return false; + + uint32_t word0 = 0; + + word0 = ( + ((OP_I2C_READ & CS_OPCODE_MASK) << CS_OPCODE_SHIFT) + | ((CS_I2CREAD_LEN & CS_LEN_MASK) << CS_LEN_SHIFT) + | ((rid & CS_RID_MASK) << CS_RID_SHIFT) + | ((i2c_addr & CS_I2CADDR_MASK) << CS_I2CADDR_SHIFT) + ); + + uint32_t *payload = (uint32_t *) (d_payload + p_len); + *payload = host_to_usrp_u32(word0); + + // Jump a word and write the number of bytes to read + payload += 1; + uint32_t word1 = + (n_bytes & CS_I2CREADBYTES_MASK) << CS_I2CREADBYTES_SHIFT; + *payload = host_to_usrp_u32(word1); + + // Update payload length + incr_header_len(CS_FIXED_LEN + CS_I2CREAD_LEN); + + return true; +} + +/*! + * \brief Adds an I2C read reply response to the current packet. This is used + * by the fake USRP code to generate fake I2C responses. + * + * The \p rid is the RID to be associated with the response, \p i2c_addr is the + * address on the I2C bus that the \p i2c_data of \p i2c_data_len was read from. + * + * \returns true if the command was added to the packet, false otherwise. + */ +bool usrp_inband_usb_packet::cs_i2c_read_reply(long rid, long i2c_addr, uint8_t *i2c_data, long i2c_data_len) +{ + if(!align32()) + return false; + + int p_len = payload_len(); + + int i2c_len = i2c_data_len + 2; + + if((MAX_PAYLOAD - p_len) < (i2c_len + CS_FIXED_LEN)) + return false; + + uint32_t word0 = 0; + + word0 = ( + ((OP_I2C_READ_REPLY & CS_OPCODE_MASK) << CS_OPCODE_SHIFT) + | ((i2c_len & CS_LEN_MASK) << CS_LEN_SHIFT) + | ((rid & CS_RID_MASK) << CS_RID_SHIFT) + | ((i2c_addr & CS_I2CADDR_MASK) << CS_I2CADDR_SHIFT) + ); + + uint32_t *payload = (uint32_t *) (d_payload + p_len); + *payload = host_to_usrp_u32(word0); + + // Jump a word and write the actual data + payload += 1; + memcpy(payload, i2c_data, i2c_data_len); + + // Update payload length + incr_header_len(CS_FIXED_LEN + i2c_len); + + return true; +} + +/*! + * \brief Adds a SPI write command to the current packet. + * + * \returns true if the command was added to the packet, false otherwise. + */ +bool usrp_inband_usb_packet::cs_spi_write(long enables, long format, long opt_header_bytes, uint8_t *spi_data, long spi_data_len) +{ + if(!align32()) + return false; + + int p_len = payload_len(); + + int spi_len = spi_data_len + 6; + + if((MAX_PAYLOAD - p_len) < (spi_len + CS_FIXED_LEN)) + return false; + + uint32_t word = 0; + + // First word contains the opcode and length, then mbz + word = ( + ((OP_SPI_WRITE & CS_OPCODE_MASK) << CS_OPCODE_SHIFT) + | ((spi_len & CS_LEN_MASK) << CS_LEN_SHIFT) + ); + uint32_t *payload = (uint32_t *) (d_payload + p_len); + *payload = host_to_usrp_u32(word); + + payload += 1; + + // Second word contains the enables, format, and optional tx bytes + word = 0; + word = ( + ((enables & CS_SPIENABLES_MASK) << CS_SPIENABLES_SHIFT) + | ((format & CS_SPIFORMAT_MASK) << CS_SPIFORMAT_SHIFT) + | ((opt_header_bytes & CS_SPIOPT_MASK) << CS_SPIOPT_SHIFT) + ); + payload = (uint32_t *) (d_payload + p_len); + *payload = host_to_usrp_u32(word); + + payload += 1; + memcpy(payload, spi_data, spi_data_len); + + // Update payload length + incr_header_len(CS_FIXED_LEN + spi_len); + + return true; +} + +/*! + * \brief Adds a SPI bus read command to the packet. + * + * \returns true if the command was added to the packet, false otherwise. + */ +bool usrp_inband_usb_packet::cs_spi_read(long rid, long enables, long format, long opt_header_bytes, long n_bytes) +{ + if(!align32()) + return false; + + int p_len = payload_len(); + + if((MAX_PAYLOAD - p_len) < (CS_SPIREAD_LEN + CS_FIXED_LEN)) + return false; + + uint32_t word = 0; + + // First word contains the opcode, length, and RID + word = ( + ((OP_SPI_READ & CS_OPCODE_MASK) << CS_OPCODE_SHIFT) + | ((CS_SPIREAD_LEN & CS_LEN_MASK) << CS_LEN_SHIFT) + | ((rid & CS_RID_MASK) << CS_RID_SHIFT) + ); + uint32_t *payload = (uint32_t *) (d_payload + p_len); + *payload = host_to_usrp_u32(word); + + payload += 1; + + // Second word contains the enables, format, and optional tx bytes + word = 0; + word = ( + ((enables & CS_SPIENABLES_MASK) << CS_SPIENABLES_SHIFT) + | ((format & CS_SPIFORMAT_MASK) << CS_SPIFORMAT_SHIFT) + | ((opt_header_bytes & CS_SPIOPT_MASK) << CS_SPIOPT_SHIFT) + ); + payload = (uint32_t *) (d_payload + p_len); + *payload = host_to_usrp_u32(word); + + payload += 1; + + // The third word contains the number of bytes + word = 0; + word = ( + ((n_bytes & CS_SPINBYTES_MASK) << CS_SPINBYTES_SHIFT) + ); + payload = (uint32_t *) (d_payload + p_len); + *payload = host_to_usrp_u32(word); + + // Update payload length + incr_header_len(CS_FIXED_LEN + CS_SPIREAD_LEN); + + return true; +} + +/*! + * \brief Adds an SPI read reply to the current packet. This is used by the + * fake USRP code to generate fake responses for SPI reads. + * + * \returns true if the command was added to the packet, false otherwise. + */ +bool usrp_inband_usb_packet::cs_spi_read_reply(long rid, uint8_t *spi_data, long spi_data_len) +{ + if(!align32()) + return false; + + int p_len = payload_len(); + + int spi_len = spi_data_len + 2; + + if((MAX_PAYLOAD - p_len) < (spi_len + CS_FIXED_LEN)) + return false; + + uint32_t word = 0; + + // First word contains the opcode, length, and RID + word = ( + ((OP_SPI_READ_REPLY & CS_OPCODE_MASK) << CS_OPCODE_SHIFT) + | ((spi_len & CS_LEN_MASK) << CS_LEN_SHIFT) + | ((rid & CS_RID_MASK) << CS_RID_SHIFT) + ); + uint32_t *payload = (uint32_t *) (d_payload + p_len); + *payload = host_to_usrp_u32(word); + + // Jump a word and write the actual data + payload += 1; + memcpy(payload, spi_data, spi_data_len); + + // Update payload length + incr_header_len(CS_FIXED_LEN + spi_len); + + return true; +} + +/*! + * \brief Since all control packets contain subpackets which have the length of + * the subpacket at a uniform location in the subpacket, this will return the + * subpacket length given a byte offset of the start of the subpacket from the beginning of the packet. + * + * \returns the length of the subpacket + */ +int usrp_inband_usb_packet::cs_len(int payload_offset) { + uint32_t subpkt = usrp_to_host_u32(*((uint32_t *)(d_payload + payload_offset))); + return (subpkt >> CS_LEN_SHIFT) & CS_LEN_MASK; +} + +/*! + * \brief The following method takes an offset within the packet payload to + * extract a control/status subpacket and constructs a pmt response which + * includes the proper signal and arguments specified by usrp-low-level-cs. The + * USRP server could therefore use this to read subpackets and pass them + * responses back up to the application. It's arguable that only reply packets + * should be parsed here, however we parse others for use in debugging or + * failure reporting on the transmit side of packets. + */ +pmt_t usrp_inband_usb_packet::read_subpacket(int payload_offset) { + + uint32_t subpkt = usrp_to_host_u32(*((uint32_t *)(d_payload + payload_offset))); + uint32_t opcode = (subpkt >> CS_OPCODE_SHIFT) & CS_OPCODE_MASK; + uint32_t len = (subpkt >> CS_LEN_SHIFT) & CS_LEN_MASK; + + switch(opcode) { + + case OP_PING_FIXED_REPLY: + { + pmt_t rid = pmt_from_long((subpkt >> CS_RID_SHIFT) & CS_RID_MASK); + pmt_t pingval = pmt_from_long((subpkt >> CS_PINGVAL_SHIFT) & CS_PINGVAL_MASK); + return pmt_list3(s_op_ping_fixed_reply, rid, pingval); + } + + case OP_READ_REG_REPLY: + { + pmt_t rid = pmt_from_long((subpkt >> CS_RID_SHIFT) & CS_RID_MASK); + pmt_t reg_num = pmt_from_long((subpkt >> CS_REGNUM_SHIFT) & CS_REGNUM_MASK); + + // To get the register value we just read the next 32 bits + uint32_t val = usrp_to_host_u32(*((uint32_t *)(d_payload + payload_offset + 4))); + pmt_t reg_val = pmt_from_long(val); + + return pmt_list4(s_op_read_reg_reply, rid, reg_num, reg_val); + } + + case OP_I2C_READ_REPLY: + { + pmt_t rid = pmt_from_long((subpkt >> CS_RID_SHIFT) & CS_RID_MASK); + pmt_t i2c_addr = pmt_from_long((subpkt >> CS_I2CADDR_SHIFT) & CS_I2CADDR_MASK); + + // Make a u8 vector to dump the data from the packet into + size_t i2c_data_len; + pmt_t i2c_data = pmt_make_u8vector(len - 2, 0); // skip rid+mbz+addr = 2 bytes + uint8_t *w_data = + (uint8_t *) pmt_u8vector_writable_elements(i2c_data, i2c_data_len); + + memcpy(w_data, d_payload + payload_offset + 4, i2c_data_len); // skip first word + + return pmt_list4(s_op_i2c_read_reply, rid, i2c_addr, i2c_data); + } + + case OP_SPI_READ_REPLY: + { + pmt_t rid = pmt_from_long((subpkt >> CS_RID_SHIFT) & CS_RID_MASK); + + // Make a u8 vector to dump the data from the packet into + size_t spi_data_len; + pmt_t spi_data = pmt_make_u8vector(len - 2, 0); // skip rid+mbz+addr = 2 bytes + uint8_t *w_data = + (uint8_t *) pmt_u8vector_writable_elements(spi_data, spi_data_len); + + memcpy(w_data, d_payload + payload_offset + 4, spi_data_len); // skip first word + + return pmt_list3(s_op_spi_read_reply, rid, spi_data); + } + + case OP_PING_FIXED: + { + pmt_t rid = pmt_from_long((subpkt >> CS_RID_SHIFT) & CS_RID_MASK); + pmt_t pingval = pmt_from_long((subpkt >> CS_PINGVAL_SHIFT) & CS_PINGVAL_MASK); + return pmt_list3(s_op_ping_fixed, rid, pingval); + } + + case OP_WRITE_REG: + { + pmt_t reg_num = pmt_from_long((subpkt >> CS_REGNUM_SHIFT) & CS_REGNUM_MASK); + + // To get the register value we just read the next 32 bits + uint32_t val = usrp_to_host_u32(*((uint32_t *)(d_payload + payload_offset + 4))); + pmt_t reg_val = pmt_from_long(val); + + return pmt_list3(s_op_write_reg, reg_num, reg_val); + } + + case OP_WRITE_REG_MASKED: + { + pmt_t reg_num = pmt_from_long((subpkt >> CS_REGNUM_SHIFT) & CS_REGNUM_MASK); + + // To get the register value we just read the next 32 bits + uint32_t val = usrp_to_host_u32(*((uint32_t *)(d_payload + payload_offset + 4))); + pmt_t reg_val = pmt_from_long(val); + + // The mask is the next 32 bits + uint32_t mask = usrp_to_host_u32(*((uint32_t *)(d_payload + payload_offset + 8))); + pmt_t reg_mask = pmt_from_long(mask); + + return pmt_list4(s_op_write_reg_masked, reg_num, reg_val, reg_mask); + } + + case OP_READ_REG: + { + pmt_t rid = pmt_from_long((subpkt >> CS_RID_SHIFT) & CS_RID_MASK); + pmt_t reg_num = pmt_from_long((subpkt >> CS_REGNUM_SHIFT) & CS_REGNUM_MASK); + + return pmt_list3(s_op_read_reg, rid, reg_num); + } + + case OP_I2C_WRITE: + { + pmt_t i2c_addr = pmt_from_long((subpkt >> CS_I2CADDR_SHIFT) & CS_I2CADDR_MASK); + + // The length includes an extra 2 bytes for storing the mbz and addr + pmt_t i2c_data = pmt_make_u8vector(len-2, 0); + + // Get a writable address to copy the data from the packet + size_t ignore; + uint8_t *w_data = (uint8_t *) pmt_u8vector_writable_elements(i2c_data, ignore); + memcpy(w_data, d_payload + payload_offset + 4, len-2); + + + return pmt_list3(s_op_i2c_write, i2c_addr, i2c_data); + } + + case OP_I2C_READ: + { + pmt_t rid = pmt_from_long((subpkt >> CS_RID_SHIFT) & CS_RID_MASK); + pmt_t i2c_addr = pmt_from_long((subpkt >> CS_I2CADDR_SHIFT) & CS_I2CADDR_MASK); + + // The number of bytes is in the next word + uint32_t bytes = usrp_to_host_u32(*((uint32_t *)(d_payload + payload_offset + 4))); + bytes = (bytes >> CS_I2CREADBYTES_SHIFT) & CS_I2CREADBYTES_MASK; + pmt_t i2c_bytes = pmt_from_long(bytes); + + return pmt_list4(s_op_i2c_read, rid, i2c_addr, i2c_bytes); + } + + case OP_SPI_WRITE: + { + // Nothing interesting in the first word, skip to the next + uint32_t word = usrp_to_host_u32(*((uint32_t *)(d_payload + payload_offset + 4))); + pmt_t enables = pmt_from_long((word >> CS_SPIENABLES_SHIFT) & CS_SPIENABLES_MASK); + pmt_t format = pmt_from_long((word >> CS_SPIFORMAT_SHIFT) & CS_SPIFORMAT_MASK); + pmt_t opt = pmt_from_long((word >> CS_SPIOPT_SHIFT) & CS_SPIOPT_MASK); + + // From the next word and on is data + size_t spi_data_len; + pmt_t spi_data = pmt_make_u8vector(len - 6, 0); // skip rid+mbz+addr = 2 bytes + uint8_t *w_data = + (uint8_t *) pmt_u8vector_writable_elements(spi_data, spi_data_len); + + memcpy(w_data, d_payload + payload_offset + 8, spi_data_len); // skip first 2 words + + return pmt_list5(s_op_spi_write, enables, format, opt, spi_data); + } + + case OP_SPI_READ: + { + // Read the RID from the first word, the rest is mbz + pmt_t rid = pmt_from_long((subpkt >> CS_RID_SHIFT) & CS_RID_MASK); + + // Continue at the next word... + uint32_t word = usrp_to_host_u32(*((uint32_t *)(d_payload + payload_offset + 4))); + pmt_t enables = pmt_from_long((word >> CS_SPIENABLES_SHIFT) & CS_SPIENABLES_MASK); + pmt_t format = pmt_from_long((word >> CS_SPIFORMAT_SHIFT) & CS_SPIFORMAT_MASK); + pmt_t opt = pmt_from_long((word >> CS_SPIOPT_SHIFT) & CS_SPIOPT_MASK); + + // The number of bytes is the only thing to read in the next word + word = usrp_to_host_u32(*((uint32_t *)(d_payload + payload_offset + 8))); + pmt_t n_bytes = pmt_from_long((word >> CS_SPINBYTES_SHIFT) & CS_SPINBYTES_MASK); + + return pmt_list6(s_op_spi_read, rid, enables, format, opt, n_bytes); + } + + case OP_DELAY: + { + pmt_t ticks = pmt_from_long((subpkt >> CS_DELAY_SHIFT) & CS_DELAY_MASK); + + return pmt_list2(s_op_delay, ticks); + } + + default: + return PMT_NIL; + + } +} + diff --git a/usrp/limbo/inband/usrp_inband_usb_packet.h b/usrp/limbo/inband/usrp_inband_usb_packet.h new file mode 100644 index 000000000..6f1a3feb3 --- /dev/null +++ b/usrp/limbo/inband/usrp_inband_usb_packet.h @@ -0,0 +1,240 @@ +/* -*- c++ -*- */ +/* + * Copyright 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 this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#ifndef INCLUDED_USRP_INBAND_USB_PACKET_H_ +#define INCLUDED_USRP_INBAND_USB_PACKET_H_ + +#include <usrp_bytesex.h> +#include <mblock/mblock.h> +#include <pmt.h> +#include <iostream> + +#include <symbols_usrp_low_level_cs.h> + +static const int USB_PKT_SIZE = 512; // bytes +static const int MAX_PAYLOAD = USB_PKT_SIZE-2*sizeof(uint32_t); +static const int CONTROL_CHAN = 0x1f; + +class usrp_inband_usb_packet { + // + // keep raw packet in USRP-endian order + // + uint32_t d_word0; + uint32_t d_timestamp; + unsigned char d_payload[MAX_PAYLOAD]; + +public: + + enum opcodes { + OP_PING_FIXED = 0x00, + OP_PING_FIXED_REPLY = 0x01, + OP_WRITE_REG = 0x02, + OP_WRITE_REG_MASKED = 0x03, + OP_READ_REG = 0x04, + OP_READ_REG_REPLY = 0x05, + OP_I2C_WRITE = 0x06, + OP_I2C_READ = 0x07, + OP_I2C_READ_REPLY = 0x08, + OP_SPI_WRITE = 0x09, + OP_SPI_READ = 0x0a, + OP_SPI_READ_REPLY = 0x0b, + OP_DELAY = 0x0c + }; + + enum flags { + FL_OVERRUN = 0x80000000, + FL_UNDERRUN = 0x40000000, + FL_DROPPED = 0x20000000, + FL_START_OF_BURST = 0x10000000, + FL_END_OF_BURST = 0x08000000, + FL_CARRIER_SENSE = 0x04000000, + + FL_ALL_FLAGS = 0xfc000000 + }; + + static const int FL_OVERRUN_SHIFT = 31; + static const int FL_UNDERRUN_SHIFT = 30; + static const int FL_DROPPED_SHIFT = 29; + static const int FL_END_OF_BURST_SHIFT = 27; + static const int FL_START_OF_BURST_SHIFT = 28; + + static const int RSSI_MASK = 0x3f; + static const int RSSI_SHIFT = 21; + + static const int CHAN_MASK = 0x1f; + static const int CHAN_SHIFT = 16; + + static const int TAG_MASK = 0xf; + static const int TAG_SHIFT = 9; + + static const int PAYLOAD_LEN_MASK = 0x1ff; + static const int PAYLOAD_LEN_SHIFT = 0; + + // Fixed size for opcode and length fields + static const int CS_FIXED_LEN = 2; + + static const int CS_OPCODE_MASK = 0xff; + static const int CS_OPCODE_SHIFT = 24; + + static const int CS_LEN_MASK = 0xff; + static const int CS_LEN_SHIFT = 16; + + static const int CS_RID_MASK = 0x3f; + static const int CS_RID_SHIFT = 10; + + static const int CS_PING_LEN = 2; + static const int CS_PINGVAL_MASK = 0x3ff; + static const int CS_PINGVAL_SHIFT = 0; + + static const int CS_WRITEREG_LEN = 6; + static const int CS_WRITEREGMASKED_LEN = 10; + static const int CS_READREG_LEN = 2; + static const int CS_READREGREPLY_LEN = 6; + static const int CS_REGNUM_MASK = 0x3ff; + static const int CS_REGNUM_SHIFT = 0; + + static const int CS_DELAY_LEN = 2; + static const int CS_DELAY_MASK = 0xffff; + static const int CS_DELAY_SHIFT = 0; + + static const int CS_I2CADDR_MASK = 0x7f; + static const int CS_I2CADDR_SHIFT = 0; + + static const int CS_I2CREAD_LEN = 3; + static const int CS_I2CREADBYTES_MASK = 0x7f; + static const int CS_I2CREADBYTES_SHIFT = 24; + + static const int CS_SPIOPT_MASK = 0xffff; + static const int CS_SPIOPT_SHIFT = 0; + static const int CS_SPIFORMAT_MASK = 0xff; + static const int CS_SPIFORMAT_SHIFT = 16; + static const int CS_SPIENABLES_MASK = 0xff; + static const int CS_SPIENABLES_SHIFT = 24; + static const int CS_SPIREAD_LEN = 7; + static const int CS_SPINBYTES_MASK = 0xff; + static const int CS_SPINBYTES_SHIFT = 24; + +public: + + void set_timestamp(uint32_t timestamp){ + d_timestamp = host_to_usrp_u32(timestamp); + } + + void set_end_of_burst() { + uint32_t word0 = usrp_to_host_u32(d_word0); + word0 |= 1<<FL_END_OF_BURST_SHIFT; + d_word0 = host_to_usrp_u32(word0); + } + + void set_header(int flags, int chan, int tag, int payload_len){ + uint32_t word0 = ((flags & FL_ALL_FLAGS) + | ((chan & CHAN_MASK) << CHAN_SHIFT) + | ((tag & TAG_MASK) << TAG_SHIFT) + | ((payload_len & PAYLOAD_LEN_MASK) << PAYLOAD_LEN_SHIFT)); + d_word0 = host_to_usrp_u32(word0); + } + + void incr_header_len(int val) { + set_header(flags(), chan(), tag(), payload_len() + val); + } + + uint32_t timestamp() const { + return usrp_to_host_u32(d_timestamp); + } + + int rssi() const { + uint32_t word0 = usrp_to_host_u32(d_word0); + return (word0 >> RSSI_SHIFT) & RSSI_MASK; + } + + int chan() const { + uint32_t word0 = usrp_to_host_u32(d_word0); + return (word0 >> CHAN_SHIFT) & CHAN_MASK; + } + + int tag() const { + uint32_t word0 = usrp_to_host_u32(d_word0); + return (word0 >> TAG_SHIFT) & TAG_MASK; + } + + int payload_len() const { + uint32_t word0 = usrp_to_host_u32(d_word0); + return (word0 >> PAYLOAD_LEN_SHIFT) & PAYLOAD_LEN_MASK; + } + + int flags() const { + return usrp_to_host_u32(d_word0) & FL_ALL_FLAGS; + } + + int overrun() const { + return (usrp_to_host_u32(d_word0) & FL_OVERRUN) >> FL_OVERRUN_SHIFT; + } + + + int underrun() const { + return (usrp_to_host_u32(d_word0) & FL_UNDERRUN) >> FL_UNDERRUN_SHIFT; + } + + + int start_of_burst() const { + return (usrp_to_host_u32(d_word0) & FL_START_OF_BURST) >> FL_START_OF_BURST_SHIFT; + } + + int end_of_burst() const { + return (usrp_to_host_u32(d_word0) & FL_END_OF_BURST) >> FL_END_OF_BURST_SHIFT; + } + + int dropped() const { + return (usrp_to_host_u32(d_word0) & FL_DROPPED) >> FL_DROPPED_SHIFT; + } + + unsigned char *payload() { + return d_payload; + } + + static int max_payload() { + return MAX_PAYLOAD; + } + + static int max_pkt_size() { + return USB_PKT_SIZE; + } + + // C/S methods + bool align32(); + bool cs_ping(long rid, long ping_val); + bool cs_ping_reply(long rid, long ping_val); + bool cs_write_reg(long reg_num, long val); + bool cs_write_reg_masked(long reg_num, long val, long mask); + bool cs_read_reg(long rid, long reg_num); + bool cs_read_reg_reply(long rid, long reg_num, long reg_val); + bool cs_delay(long ticks); + bool cs_i2c_write(long i2c_addr, uint8_t *i2c_data, size_t data_len); + bool cs_i2c_read(long rid, long i2c_addr, long n_bytes); + bool cs_i2c_read_reply(long rid, long i2c_addr, uint8_t *i2c_data, long i2c_data_len); + bool cs_spi_write(long enables, long format, long opt_header_bytes, uint8_t *spi_data, long spi_data_len); + bool cs_spi_read(long rid, long enables, long format, long opt_header_bytes, long n_bytes); + bool cs_spi_read_reply(long rid, uint8_t *spi_data, long spi_data_len); + int cs_len(int payload_offset); + pmt_t read_subpacket(int payload_offset); +}; + +#endif diff --git a/usrp/limbo/inband/usrp_interface.mbh b/usrp/limbo/inband/usrp_interface.mbh new file mode 100644 index 000000000..ad0f78b4e --- /dev/null +++ b/usrp/limbo/inband/usrp_interface.mbh @@ -0,0 +1,88 @@ +;; -*- scheme -*- ; not really, but tells emacs how to format this +;; +;; Copyright 2007 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 2, 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. +;; + +;; ---------------------------------------------------------------- +;; This is an mblock header file +;; +;; The format is very much a work-in-progress. +;; It'll be compiled to C++. +;; ---------------------------------------------------------------- + +;; ---------------------------------------------------------------- +;; usrp-interface-cs +;; +;; Handles interaction between the usrp_sever and the USB interface + +(define-protocol-class usrp-interface-cs + + (:outgoing + (cmd-usrp-open invocation-handle which-usrp) + (cmd-usrp-close invocation-handle) + (cmd-usrp-ntx-chan invocation-handle) + (cmd-usrp-nrx-chan invocation-handle) + (cmd-usrp-write invocation-handle channel data) + (cmd-usrp-start-reading invocation-handle channel) + ) + + (:incoming + (response-usrp-open invocation-handle status) + (response-usrp-close invocation-handle status) + (response-usrp-ntx-chan invocation-handle ntx-chan) + (response-usrp-nrx-chan invocation-handle nrx-chan) + (response-usrp-write invocation-handle status channel) + (response-usrp-read invocation-handle status data) + ) + ) + +;; ---------------------------------------------------------------- +;; usrp-tx-cs +;; +;; Handles interaction between the USB interface and TX interface + +(define-protocol-class usrp-tx-cs + + (:outgoing + (cmd-usrp-tx-write invocation-handle channel data tx-handle) + ) + + (:incoming + (response-usrp-tx-write invocation-handle status channel) + ) + ) + +;; ---------------------------------------------------------------- +;; usrp-rx-cs +;; +;; Handles interaction between the USB interface and RX interface + +(define-protocol-class usrp-rx-cs + + (:outgoing + (cmd-usrp-rx-start-reading invocation-handle rx-handle) + (cmd-usrp-rx-stop-reading invocation-handle) + ) + + (:incoming + (response-usrp-rx-read invocation-handle status data) + + ;; There is currently no response to a stop reading + ) + ) diff --git a/usrp/limbo/inband/usrp_rx.cc b/usrp/limbo/inband/usrp_rx.cc new file mode 100644 index 000000000..fe9486cf6 --- /dev/null +++ b/usrp/limbo/inband/usrp_rx.cc @@ -0,0 +1,184 @@ +/* -*- c++ -*- */ +/* + * Copyright 2007 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 <usrp_rx.h> + +#include <usrp_standard.h> +#include <iostream> +#include <vector> +#include <usb.h> +#include <mblock/class_registry.h> +#include <usrp_inband_usb_packet.h> +#include <fpga_regs_common.h> +#include <stdio.h> + +#include <symbols_usrp_rx_cs.h> + +typedef usrp_inband_usb_packet transport_pkt; + +static const bool verbose = false; + +bool usrp_rx_stop; + +usrp_rx::usrp_rx(mb_runtime *rt, const std::string &instance_name, pmt_t user_arg) + : mb_mblock(rt, instance_name, user_arg), + d_disk_write(false), + d_disk_write_pkt(false) // if true, writes full packet, else just the payload +{ + d_cs = define_port("cs", "usrp-rx-cs", true, mb_port::EXTERNAL); + + if(d_disk_write) { + d_ofile0.open("rx_data_chan0.dat",std::ios::binary|std::ios::out); + d_ofile1.open("rx_data_chan1.dat",std::ios::binary|std::ios::out); + d_cs_ofile.open("rx_cs.dat",std::ios::binary|std::ios::out); + } + + usrp_rx_stop = false; + +} + +usrp_rx::~usrp_rx() +{ + if(d_disk_write) { + d_ofile0.close(); + d_ofile1.close(); + d_cs_ofile.close(); + } +} + +void +usrp_rx::initial_transition() +{ + +} + +/*! + * \brief Handles incoming signals to to the m-block, wihch should only ever be + * a single message: cmd-usrrp-rx-start-reading. There is no signal to stop + * reading as the m-block goes in to a forever loop to read inband packets from + * the bus. + */ +void +usrp_rx::handle_message(mb_message_sptr msg) +{ + pmt_t event = msg->signal(); + pmt_t port_id = msg->port_id(); + pmt_t data = msg->data(); + + // Theoretically only have 1 message to ever expect, but + // want to make sure its at least what we want + if(pmt_eq(port_id, d_cs->port_symbol())) { + + if(pmt_eqv(event, s_cmd_usrp_rx_start_reading)) + read_and_respond(data); + } +} + +/*! + * \brief Performs the actual reading of data from the USB bus, called by + * handle_message() when a cmd-usrp-rx-start-reading signal is received. + * + * The method enters a forever loop where it continues to read data from the bus + * and generate read responses to the higher layer. Currently, shared memory is + * used to exit this loop. + * + * The \p data parameter is a PMT list which contains only a single element, an + * invocation handle which will be returned with all read respones. + */ +void +usrp_rx::read_and_respond(pmt_t data) +{ + size_t ignore; + bool underrun; + unsigned int n_read; + unsigned int pkt_size = sizeof(transport_pkt); + + pmt_t invocation_handle = pmt_nth(0, data); + + // Need the handle to the RX port to send responses, this is passed + // by the USRP interface m-block + pmt_t handle = pmt_nth(1, data); + d_urx = + boost::any_cast<usrp_standard_rx_sptr>(pmt_any_ref(handle)); + + if(verbose) + std::cout << "[usrp_rx] Waiting for packets..\n"; + + // Read by 512 which is packet size and send them back up + while(!usrp_rx_stop) { + + pmt_t v_pkt = pmt_make_u8vector(pkt_size, 0); + transport_pkt *pkt = + (transport_pkt *) pmt_u8vector_writable_elements(v_pkt, ignore); + + n_read = d_urx->read(pkt, pkt_size, &underrun); + + if(n_read != pkt_size) { + std::cerr << "[usrp_rx] Error reading packet, shutting down\n"; + d_cs->send(s_response_usrp_rx_read, + pmt_list3(PMT_NIL, PMT_F, PMT_NIL)); + return; + } + + if(underrun && verbose && 0) + std::cout << "[usrp_rx] Underrun\n"; + + d_cs->send(s_response_usrp_rx_read, + pmt_list3(PMT_NIL, PMT_T, v_pkt)); + if(verbose && 0) + std::cout << "[usrp_rx] Read 1 packet\n"; + + if(d_disk_write) { + if(pkt->chan() == CONTROL_CHAN) + d_cs_ofile.write((const char *)pkt, transport_pkt::max_pkt_size()); + else { + if(d_disk_write_pkt) { + if(pkt->chan() == 0) + d_ofile0.write((const char *)pkt, transport_pkt::max_pkt_size()); + else if(pkt->chan() == 1) + d_ofile1.write((const char *)pkt, transport_pkt::max_pkt_size()); + } else { + if(pkt->chan() == 0) + d_ofile0.write((const char *)pkt->payload(), transport_pkt::max_payload()); + else if(pkt->chan() == 1) + d_ofile1.write((const char *)pkt->payload(), transport_pkt::max_payload()); + } + } + + d_cs_ofile.flush(); + d_ofile0.flush(); + d_ofile1.flush(); + } + } + + usrp_rx_stop = false; + + if(verbose) { + std::cout << "[USRP_RX] Stopping...\n"; + fflush(stdout); + } +} + +REGISTER_MBLOCK_CLASS(usrp_rx); diff --git a/usrp/limbo/inband/usrp_rx.h b/usrp/limbo/inband/usrp_rx.h new file mode 100644 index 000000000..1006235bf --- /dev/null +++ b/usrp/limbo/inband/usrp_rx.h @@ -0,0 +1,58 @@ +/* -*- c++ -*- */ +/* + * Copyright 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 this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +#ifndef INCLUDED_USRP_RX_H +#define INCLUDED_USRP_RX_H + +#include <mblock/mblock.h> +#include <fstream> +#include "usrp_standard.h" + +extern bool usrp_rx_stop; // used to communicate a 'stop' to the RX stub + +/*! + * \brief Implements the low level usb interface to the USRP + */ +class usrp_rx : public mb_mblock +{ + mb_port_sptr d_cs; + usrp_standard_rx_sptr d_urx; + + bool d_disk_write; + bool d_disk_write_pkt; + std::ofstream d_ofile0; + std::ofstream d_ofile1; + std::ofstream d_cs_ofile; + + public: + usrp_rx(mb_runtime *rt, const std::string &instance_name, pmt_t user_arg); + ~usrp_rx(); + void initial_transition(); + void handle_message(mb_message_sptr msg); + + private: + void read_and_respond(pmt_t data); + void read_data(); + +}; + + +#endif /* INCLUDED_USRP_RX_H */ + diff --git a/usrp/limbo/inband/usrp_rx_stub.cc b/usrp/limbo/inband/usrp_rx_stub.cc new file mode 100644 index 000000000..e5c454d24 --- /dev/null +++ b/usrp/limbo/inband/usrp_rx_stub.cc @@ -0,0 +1,227 @@ +/* -*- c++ -*- */ +/* + * Copyright 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 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 <usrp_rx_stub.h> + +#include <iostream> +#include <vector> +#include <usb.h> +#include <mblock/class_registry.h> +#include <usrp_inband_usb_packet.h> +#include <fpga_regs_common.h> +#include "usrp_standard.h" +#include <stdio.h> +#include <string.h> +#include <ui_nco.h> +#include <fstream> + +#include <symbols_usrp_rx_cs.h> + +typedef usrp_inband_usb_packet transport_pkt; + +static const bool verbose = false; + +bool usrp_rx_stop_stub; + +// Used for the fake control packet response code to send the responses back up +// the RX. The TX stub dumps responses in to this queue. +std::queue<pmt_t> d_cs_queue; + +usrp_rx_stub::usrp_rx_stub(mb_runtime *rt, const std::string &instance_name, pmt_t user_arg) + : mb_mblock(rt, instance_name, user_arg), + d_samples_per_frame((long)(126)), + d_decim_rx(128), + d_amplitude(16384), + d_disk_write(false) +{ + + // Information about the rates are passed all the way from the app in the form + // of a dictionary. We use this to read the RX decimation rate and compute + // the approximate number of MS/s as a form of flow control for the stub. + pmt_t usrp_dict = user_arg; + + if (pmt_is_dict(usrp_dict)) { + // Read the RX decimation rate + if(pmt_t decim_rx = pmt_dict_ref(usrp_dict, + pmt_intern("decim-rx"), + PMT_NIL)) { + if(!pmt_eqv(decim_rx, PMT_NIL)) + d_decim_rx = pmt_to_long(decim_rx); + } + } + + d_cs = define_port("cs", "usrp-rx-cs", true, mb_port::EXTERNAL); + + // initialize NCO + double freq = 100e3; + int interp = 32; // 32 -> 4MS/s + double sample_rate = 64e6 / interp; + d_nco.set_freq(2*M_PI * freq/sample_rate); + + //d_disk_write = true; + + if(d_disk_write) + d_ofile.open("raw_rx.dat",std::ios::binary|std::ios::out); + + usrp_rx_stop_stub = false; +} + +usrp_rx_stub::~usrp_rx_stub() +{ + if(d_disk_write) + d_ofile.close(); +} + +void +usrp_rx_stub::initial_transition() +{ +} + +void +usrp_rx_stub::handle_message(mb_message_sptr msg) +{ + pmt_t event = msg->signal(); + pmt_t port_id = msg->port_id(); + pmt_t data = msg->data(); + + if (pmt_eq(msg->signal(), s_timeout) + && !pmt_eq(msg->data(), s_done)) { + + if(!usrp_rx_stop_stub) + read_and_respond(); + else { // requested to stop + cancel_timeout(msg->metadata()); + usrp_rx_stop_stub=false; + if(verbose) + std::cout << "[USRP_RX_STUB] Stopping RX stub\n"; + } + + } + + // Theoretically only have 1 message to ever expect, but + // want to make sure its at least what we want + if(pmt_eq(port_id, d_cs->port_symbol()) + && pmt_eqv(event, s_cmd_usrp_rx_start_reading)) { + + if(verbose) + std::cout << "[USRP_RX_STUB] Starting with decim @ " + << d_decim_rx << std::endl; + + start_packet_timer(); + } +} + +// Setup a periodic timer which will drive packet generation +void +usrp_rx_stub::start_packet_timer() +{ + d_t0 = mb_time::time(); // current time + + // Calculate the inter-packet arrival time. + double samples_per_sec = (64.0/(double)d_decim_rx)*1000000.0; + double frames_per_sec = samples_per_sec / (double)d_samples_per_frame; + double frame_rate = 1.0 / frames_per_sec; + + if(verbose) { + std::cout << "[USRP_RX_STUB] Scheduling periodic packet generator\n"; + std::cout << "\tsamples_per_sec: " << samples_per_sec << std::endl; + std::cout << "\tframes_per_sec: " << frames_per_sec << std::endl; + std::cout << "\tframe_rate: " << frame_rate << std::endl; + } + + schedule_periodic_timeout(d_t0 + frame_rate, mb_time(frame_rate), PMT_T); +} + +void +usrp_rx_stub::read_and_respond() +{ + + long nsamples_this_frame = d_samples_per_frame; + + size_t nshorts = 2 * nsamples_this_frame; // 16-bit I & Q + long channel = 0; + long n_bytes = nshorts*2; + pmt_t uvec = pmt_make_s16vector(nshorts, 0); + size_t ignore; + int16_t *samples = pmt_s16vector_writable_elements(uvec, ignore); + + // fill in the complex sinusoid + + for (int i = 0; i < nsamples_this_frame; i++){ + + if (1){ + gr_complex s; + d_nco.sincos(&s, 1, d_amplitude); + // write 16-bit i & q + samples[2*i] = (int16_t) s.real(); + samples[2*i+1] = (int16_t) s.imag(); + } + else { + gr_complex s(d_amplitude, d_amplitude); + + // write 16-bit i & q + samples[2*i] = (int16_t) s.real(); + samples[2*i+1] = (int16_t) s.imag(); + } + } + + if(d_disk_write) + d_ofile.write((const char *)samples, n_bytes); + + pmt_t v_pkt = pmt_make_u8vector(sizeof(transport_pkt), 0); + transport_pkt *pkt = + (transport_pkt *) pmt_u8vector_writable_elements(v_pkt, ignore); + + pkt->set_header(0, channel, 0, n_bytes); + pkt->set_timestamp(0xffffffff); + memcpy(pkt->payload(), samples, n_bytes); + + d_cs->send(s_response_usrp_rx_read, pmt_list3(PMT_NIL, PMT_T, v_pkt)); + + // Now lets check the shared CS queue between the TX and RX stub. Each + // element in a queue is a list where the first element is an invocation + // handle and the second element is a PMT u8 vect representation of the + // CS packet response which can just be passed transparently. + while(!d_cs_queue.empty()) { + + pmt_t cs_pkt = d_cs_queue.front(); + d_cs_queue.pop(); + + pmt_t invocation_handle = pmt_nth(0, cs_pkt); + pmt_t v_pkt = pmt_nth(1, cs_pkt); + + d_cs->send(s_response_usrp_rx_read, + pmt_list3(invocation_handle, + PMT_T, + v_pkt)); // Take the front CS pkt + + + if(verbose) + std::cout << "[USRP_RX_STUB] Received CS response from TX stub\n"; + } + +} + +REGISTER_MBLOCK_CLASS(usrp_rx_stub); diff --git a/usrp/limbo/inband/usrp_rx_stub.h b/usrp/limbo/inband/usrp_rx_stub.h new file mode 100644 index 000000000..238b4568f --- /dev/null +++ b/usrp/limbo/inband/usrp_rx_stub.h @@ -0,0 +1,79 @@ +/* -*- c++ -*- */ +/* + * Copyright 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 this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +#ifndef INCLUDED_USRP_RX_STUB_H +#define INCLUDED_USRP_RX_STUB_H + +#include <mblock/mblock.h> +#include <vector> +#include "usrp_standard.h" +#include <ui_nco.h> +#include <fstream> +#include <queue> +#include <usrp_inband_usb_packet.h> + +typedef usrp_inband_usb_packet transport_pkt; + +extern bool usrp_rx_stop_stub; // used to communicate a 'stop' to the RX stub +extern std::queue<pmt_t> d_cs_queue; + +static pmt_t s_timeout = pmt_intern("%timeout"); +static pmt_t s_done = pmt_intern("done"); + +/*! + * \brief Implements the low level usb interface to the USRP + */ +class usrp_rx_stub : public mb_mblock +{ + public: + + mb_port_sptr d_cs; + usrp_standard_rx* d_urx; + + long d_samples_per_frame; + long d_decim_rx; + + mb_time d_t0; + double d_delta_t; + + // for generating sine wave output + ui_nco<float,float> d_nco; + double d_amplitude; + + bool d_disk_write; + + std::ofstream d_ofile; + + public: + usrp_rx_stub(mb_runtime *rt, const std::string &instance_name, pmt_t user_arg); + ~usrp_rx_stub(); + void initial_transition(); + void handle_message(mb_message_sptr msg); + + private: + void read_and_respond(); + void read_data(); + void start_packet_timer(); + +}; + + +#endif /* INCLUDED_USRP_RX_H */ + diff --git a/usrp/limbo/inband/usrp_server.cc b/usrp/limbo/inband/usrp_server.cc new file mode 100644 index 000000000..ac2636308 --- /dev/null +++ b/usrp/limbo/inband/usrp_server.cc @@ -0,0 +1,1860 @@ +/* -*- c++ -*- */ +/* + * Copyright 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 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 <usrp_server.h> +#include <iostream> +#include <usrp_inband_usb_packet.h> +#include <mblock/class_registry.h> +#include <vector> +#include <usrp_usb_interface.h> +#include <string.h> +#include <fpga_regs_common.h> +#include <fpga_regs_standard.h> + +#include <symbols_usrp_server_cs.h> +#include <symbols_usrp_channel.h> +#include <symbols_usrp_tx.h> +#include <symbols_usrp_rx.h> +#include <symbols_usrp_low_level_cs.h> +#include <symbols_usrp_interface_cs.h> + +static pmt_t s_shutdown = pmt_intern("%shutdown"); + +typedef usrp_inband_usb_packet transport_pkt; // makes conversion to gigabit easy + +const static bool verbose = false; + +static std::string +str(long x) +{ + std::ostringstream s; + s << x; + return s.str(); +} + +usrp_server::usrp_server(mb_runtime *rt, const std::string &instance_name, pmt_t user_arg) + : mb_mblock(rt, instance_name, user_arg), + d_fpga_debug(false), + d_interp_tx(128), // these should match the lower level defaults (rx also) + d_decim_rx(128), + d_fake_rx(false) +{ + if(verbose) + std::cout << "[USRP_SERVER] Initializing...\n"; + + // Dictionary for arguments to all of the components + d_usrp_dict = user_arg; + + if (pmt_is_dict(d_usrp_dict)) { + + if(pmt_t fpga_debug = pmt_dict_ref(d_usrp_dict, + pmt_intern("fpga-debug"), + PMT_NIL)) { + if(pmt_eqv(fpga_debug, PMT_T)) + d_fpga_debug=true; + } + + // Read the TX interpolations + if(pmt_t interp_tx = pmt_dict_ref(d_usrp_dict, + pmt_intern("interp-tx"), + PMT_NIL)) { + if(!pmt_eqv(interp_tx, PMT_NIL)) + d_interp_tx = pmt_to_long(interp_tx); + } + + // Read the RX decimation rate + if(pmt_t decim_rx = pmt_dict_ref(d_usrp_dict, + pmt_intern("decim-rx"), + PMT_NIL)) { + if(!pmt_eqv(decim_rx, PMT_NIL)) + d_decim_rx = pmt_to_long(decim_rx); + } + } + + // control & status port + d_cs = define_port("cs", "usrp-server-cs", true, mb_port::EXTERNAL); + d_cs_usrp = define_port("cs_usrp", "usrp-interface-cs", false, mb_port::INTERNAL); + + // ports + // + // (if/when we do replicated ports, these will be replaced by a + // single replicated port) + for(int port=0; port < N_PORTS; port++) { + + d_tx.push_back(define_port("tx"+str(port), + "usrp-tx", + true, + mb_port::EXTERNAL)); + + d_rx.push_back(define_port("rx"+str(port), + "usrp-rx", + true, + mb_port::EXTERNAL)); + } + + define_component("usrp", "usrp_usb_interface", d_usrp_dict); + connect("self", "cs_usrp", "usrp", "cs"); + + d_defer=false; + d_opened=false; + + // FIXME: needs to be returned from open, if we want to use this + d_nrx_chan = 2; + d_ntx_chan = 2; + + // Initialize capacity on each channel to 0 and to no owner + // Also initialize the USRP standard tx/rx pointers to NULL + for(int chan=0; chan < d_ntx_chan; chan++) + d_chaninfo_tx.push_back(channel_info()); + + for(int chan=0; chan < d_nrx_chan; chan++) + d_chaninfo_rx.push_back(channel_info()); + + d_rx_chan_mask = 0; + + for(int i=0; i < D_MAX_RID; i++) + d_rids.push_back(rid_info()); + + //d_fake_rx=true; +} + +/*! + * \brief resets the assigned capacity and owners of each RX and TX channel from + * allocations. + */ +void +usrp_server::reset_channels() +{ + + for(int chan=0; chan < d_ntx_chan; chan++) { + d_chaninfo_tx[chan].assigned_capacity = 0; + d_chaninfo_tx[chan].owner = PMT_NIL; + } + + for(int chan=0; chan < d_nrx_chan; chan++) { + d_chaninfo_rx[chan].assigned_capacity = 0; + d_chaninfo_rx[chan].owner = PMT_NIL; + } + + d_rx_chan_mask = 0; +} + +usrp_server::~usrp_server() +{ +} + + +void +usrp_server::initial_transition() +{ + // the initial transition +} + +/*! + * \brief Reads all incoming messages to USRP server from the TX, RX, and the CS + * ports. This drives the state of USRP server and dispatches based on the + * message. + */ +void +usrp_server::handle_message(mb_message_sptr msg) +{ + pmt_t event = msg->signal(); // the "name" of the message + pmt_t port_id = msg->port_id(); // which port it came in on + pmt_t data = msg->data(); + pmt_t invocation_handle; + pmt_t metadata = msg->metadata(); + pmt_t status; + + long port; + + if (pmt_eq(event, s_shutdown)) // ignore (for now) + return; + + invocation_handle = pmt_nth(0, data); + + if (0){ + std::cout << "[USRP_SERVER] event: " << event << std::endl; + std::cout << "[USRP_SERVER] port_id: " << port_id << std::endl; + } + + // It would be nice if this were all table driven, and we could compute our + // state transition as f(current_state, port_id, signal) + + // A message from the USRP CS, which should *only* be responses + // + // It is important that this set come before checking messages of any other + // components. This is since we always want to listen to the low level USRP + // server, even if we aren't initialized we are waiting for responses to + // become initialized. Likewise, after the usrp_server is "closed", we still + // want to pass responses back from the low level. + + //---------------- USRP RESPONSE ---------------// + if (pmt_eq(port_id, d_cs_usrp->port_symbol())) { + + //-------------- USRP OPEN ------------------// + if(pmt_eq(event, s_response_usrp_open)) { + // pass the response back over the regular CS port + pmt_t status = pmt_nth(1, data); + d_cs->send(s_response_open, pmt_list2(invocation_handle, status)); + + //reset_all_registers(); + //initialize_registers(); + + if(pmt_eqv(status,PMT_T)) { + d_opened = true; + d_defer = false; + recall_defer_queue(); + } + + return; + } + //------------- USRP CLOSE -------------------// + else if (pmt_eq(event, s_response_usrp_close)) { + pmt_t status = pmt_nth(1, data); + d_cs->send(s_response_close, pmt_list2(invocation_handle, status)); + + if(pmt_eqv(status,PMT_T)) { + d_opened = false; + d_defer = false; + reset_channels(); + recall_defer_queue(); + } + + return; + } + //--------------- USRP WRITE --------------// + else if (pmt_eq(event, s_response_usrp_write)) { + + pmt_t status = pmt_nth(1, data); + long channel = pmt_to_long(pmt_nth(2, data)); + long port; + + // Do not report back responses if they were generated from a + // command packet + if(channel == CONTROL_CHAN) + return; + + // Find the port through the owner of the channel + if((port = tx_port_index(d_chaninfo_tx[channel].owner)) !=-1 ) + d_tx[port]->send(s_response_xmit_raw_frame, + pmt_list2(invocation_handle, status)); + return; + } + //--------------- USRP READ ---------------// + else if (pmt_eq(event, s_response_usrp_read)) { + + pmt_t status = pmt_nth(1, data); + + if(!pmt_eqv(status, PMT_T)) { + std::cerr << "[USRP_SERVER] Error receiving packet\n"; + return; + } + else { + handle_response_usrp_read(data); + return; + } + } + + goto unhandled; + } + + // Checking for defer on all other messages + if(d_defer) { + if (verbose) + std::cout << "[USRP_SERVER] Received msg while deferring (" + << msg->signal() << ")\n"; + d_defer_queue.push(msg); + return; + } + + //--------- CONTROL / STATUS ------------// + if (pmt_eq(port_id, d_cs->port_symbol())){ + + //----------- OPEN -----------// + if (pmt_eq(event, s_cmd_open)){ + + // Reject if already open + if(d_opened) { + d_cs->send(s_response_open, pmt_list2(invocation_handle, s_err_usrp_already_opened)); + return; + } + + // the parameters are the same to the low level interface, so we just pass 'data' along + d_cs_usrp->send(s_cmd_usrp_open, data); + + d_defer = true; + + return; + } + //---------- CLOSE -----------// + else if (pmt_eq(event, s_cmd_close)){ + + if(!d_opened) { + d_cs->send(s_response_close, pmt_list2(invocation_handle, s_err_usrp_already_closed)); + return; + } + + d_defer = true; + d_cs_usrp->send(s_cmd_usrp_close, pmt_list1(invocation_handle)); + + return; + } + //---------- MAX CAPACITY ----------// + else if (pmt_eq(event, s_cmd_max_capacity)) { + + if(!d_opened) { + d_cs->send(s_response_max_capacity, + pmt_list3(invocation_handle, s_err_usrp_not_opened, pmt_from_long(0))); + return; + } + + d_cs->send(s_response_max_capacity, + pmt_list3(invocation_handle, + PMT_T, + pmt_from_long(max_capacity()))); + return; + } + //---------- NTX CHAN --------------// + else if (pmt_eq(event, s_cmd_ntx_chan)) { + + if(!d_opened) { + d_cs->send(s_response_ntx_chan, + pmt_list3(invocation_handle, s_err_usrp_not_opened, pmt_from_long(0))); + return; + } + + d_cs->send(s_response_ntx_chan, + pmt_list3(invocation_handle, + PMT_T, + pmt_from_long(d_ntx_chan))); + return; + } + //---------- NRX CHAN -----------// + else if (pmt_eq(event, s_cmd_nrx_chan)) { + + if(!d_opened) { + d_cs->send(s_response_nrx_chan, + pmt_list3(invocation_handle, s_err_usrp_not_opened, pmt_from_long(0))); + return; + } + + d_cs->send(s_response_nrx_chan, + pmt_list3(invocation_handle, + PMT_T, + pmt_from_long(d_nrx_chan))); + return; + } + //--------- ALLOCATION? -----------// + else if (pmt_eq(event, s_cmd_current_capacity_allocation)) { + + if(!d_opened) { + d_cs->send(s_response_current_capacity_allocation, + pmt_list3(invocation_handle, + s_err_usrp_not_opened, + pmt_from_long(0))); + return; + } + + d_cs->send(s_response_current_capacity_allocation, + pmt_list3(invocation_handle, + PMT_T, + pmt_from_long(current_capacity_allocation()))); + return; + } + goto unhandled; + } + + //-------------- TX ---------------// + if ((port = tx_port_index(port_id)) != -1) { + + //------------ ALLOCATE (TX) ----------------// + if (pmt_eq(event, s_cmd_allocate_channel)){ + + if(!d_opened) { + d_tx[port]->send(s_response_allocate_channel, + pmt_list3(invocation_handle, + s_err_usrp_not_opened, + pmt_from_long(0))); + return; + } + + handle_cmd_allocate_channel(d_tx[port], d_chaninfo_tx, data); + return; + } + + //----------- DEALLOCATE (TX) ---------------// + if (pmt_eq(event, s_cmd_deallocate_channel)) { + + if(!d_opened) { + d_tx[port]->send(s_response_deallocate_channel, + pmt_list3(invocation_handle, + s_err_usrp_not_opened, + pmt_from_long(0))); + return; + } + + handle_cmd_deallocate_channel(d_tx[port], d_chaninfo_tx, data); + return; + } + + //-------------- XMIT RAW FRAME -----------------/ + if (pmt_eq(event, s_cmd_xmit_raw_frame)){ + + if(!d_opened) { + d_tx[port]->send(s_response_xmit_raw_frame, + pmt_list2(invocation_handle, s_err_usrp_not_opened)); + return; + } + + handle_cmd_xmit_raw_frame(d_tx[port], d_chaninfo_tx, data); + return; + } + + //-------------- CONTROL PACKET -----------------/ + if (pmt_eq(event, s_cmd_to_control_channel)) { + + if(!d_opened) { + d_tx[port]->send(s_response_xmit_raw_frame, + pmt_list2(invocation_handle, s_err_usrp_not_opened)); + return; + } + + handle_cmd_to_control_channel(d_tx[port], d_chaninfo_tx, data); + return; + + } + + goto unhandled; + } + + //-------------- RX ---------------// + if ((port = rx_port_index(port_id)) != -1) { + + //------------ ALLOCATE (RX) ----------------// + if (pmt_eq(event, s_cmd_allocate_channel)) { + + if(!d_opened) { + d_rx[port]->send(s_response_allocate_channel, + pmt_list3(invocation_handle, + s_err_usrp_not_opened, + pmt_from_long(0))); + return; + } + + handle_cmd_allocate_channel(d_rx[port], d_chaninfo_rx, data); + return; + } + + //----------- DEALLOCATE (RX) ---------------// + if (pmt_eq(event, s_cmd_deallocate_channel)) { + + if(!d_opened) { + d_rx[port]->send(s_response_deallocate_channel, + pmt_list3(invocation_handle, + s_err_usrp_not_opened, + pmt_from_long(0))); + return; + } + + handle_cmd_deallocate_channel(d_rx[port], d_chaninfo_rx, data); + return; + } + + //-------------- START RECV ----------------// + if (pmt_eq(event, s_cmd_start_recv_raw_samples)) { + + if(!d_opened) { + d_rx[port]->send(s_response_recv_raw_samples, + pmt_list2(invocation_handle, s_err_usrp_not_opened)); + return; + } + + handle_cmd_start_recv_raw_samples(d_rx[port], d_chaninfo_rx, data); + return; + } + + //-------------- STOP RECV ----------------// + if (pmt_eq(event, s_cmd_stop_recv_raw_samples)) { + + if(!d_opened) + return; + + // FIX ME : no response for stopping? even if error? (permissions) + handle_cmd_stop_recv_raw_samples(d_rx[port], d_chaninfo_rx, data); + + return; + } + + goto unhandled; + } + + unhandled: + std::cout << "[USRP_SERVER] unhandled msg: " << msg << std::endl; +} + +/*! + * \brief Takes a port_symbol() as parameter \p port_id and is used to determine + * if the port is a TX port, or to find an index in the d_tx vector which stores + * the port. + * + * \returns -1 if \p port_id is not in the d_tx vector (i.e., it's not a TX + * port), otherwise returns an index in the d_tx vector which stores the port. + */ +int usrp_server::tx_port_index(pmt_t port_id) { + + for(int i=0; i < (int) d_tx.size(); i++) + if(pmt_eq(d_tx[i]->port_symbol(), port_id)) + return i; + + return -1; +} + +/*! + * \brief Takes a port_symbol() as parameter \p port_id and is used to determine + * if the port is an RX port, or to find an index in the d_rx vector which + * stores the port. + * + * \returns -1 if \p port_id is not in the d_rx vector (i.e., it's not an RX + * port), otherwise returns an index in the d_rx vector which stores the port. + */ +int usrp_server::rx_port_index(pmt_t port_id) { + + for(int i=0; i < (int) d_rx.size(); i++) + if(pmt_eq(d_rx[i]->port_symbol(), port_id)) + return i; + + return -1; +} + +/*! + * \brief Determines the current total capacity allocated by all RX and TX + * channels. + * + * \returns the total allocated capacity + */ +long usrp_server::current_capacity_allocation() { + long capacity = 0; + + for(int chan=0; chan < d_ntx_chan; chan++) + capacity += d_chaninfo_tx[chan].assigned_capacity; + + for(int chan=0; chan < d_nrx_chan; chan++) + capacity += d_chaninfo_rx[chan].assigned_capacity; + + return capacity; +} + + +/*! + * \brief Called by the handle_message() method if the incoming message to + * usrp_server is to allocate a channel (cmd-allocate-channel). The method + * checks if the requested capacity exists and if so it will reserve it for the + * caller on the channel that is returned via a response-allocate-channel + * signal. + */ +void +usrp_server::handle_cmd_allocate_channel( + mb_port_sptr port, + std::vector<struct channel_info> &chan_info, + pmt_t data) +{ + pmt_t invocation_handle = pmt_nth(0, data); + long rqstd_capacity = pmt_to_long(pmt_nth(1, data)); + long chan; + + // Check capacity exists + if((D_USB_CAPACITY - current_capacity_allocation()) < rqstd_capacity) { + + // no capacity available + port->send(s_response_allocate_channel, + pmt_list3(invocation_handle, + s_err_requested_capacity_unavailable, + PMT_NIL)); + return; + } + + // Find a free channel, assign the capacity and respond + for(chan=0; chan < (long)chan_info.size(); chan++) { + + if(verbose) + std::cout << "[USRP_SERVER] Checking chan: " << chan + << " owner " << chan_info[chan].owner + << " size " << chan_info.size() + << std::endl; + + if(chan_info[chan].owner == PMT_NIL) { + + chan_info[chan].owner = port->port_symbol(); + chan_info[chan].assigned_capacity = rqstd_capacity; + + port->send(s_response_allocate_channel, + pmt_list3(invocation_handle, + PMT_T, + pmt_from_long(chan))); + + if(verbose) + std::cout << "[USRP_SERVER] Assigning channel: " << chan + << " to " << chan_info[chan].owner + << std::endl; + return; + } + + } + + if (verbose) + std::cout << "[USRP_SERVER] Couldnt find a TX chan\n"; + + // no free TX chan found + port->send(s_response_allocate_channel, + pmt_list3(invocation_handle, + s_err_channel_unavailable, + PMT_NIL)); + return; +} + +/*! + * \brief Called by the handle_message() method if the incoming message to + * usrp_server is to deallocate a channel (cmd-deallocate-channel). The method + * ensures that the sender of the signal owns the channel and that the channel + * number is valid. A response-deallocate-channel signal is sent back with the + * result of the deallocation. + */ +void +usrp_server::handle_cmd_deallocate_channel( + mb_port_sptr port, + std::vector<struct channel_info> &chan_info, + pmt_t data) +{ + + pmt_t invocation_handle = pmt_nth(0, data); + long channel = pmt_to_long(pmt_nth(1, data)); + + // Ensure the channel is valid and the caller owns the port + if(!check_valid(port, channel, chan_info, + pmt_list2(s_response_deallocate_channel, invocation_handle))) + return; + + chan_info[channel].assigned_capacity = 0; + chan_info[channel].owner = PMT_NIL; + + port->send(s_response_deallocate_channel, + pmt_list2(invocation_handle, + PMT_T)); + return; +} + +/*! + * \brief Called by the handle_message() method if the incoming message to + * usrp_server is to transmit a frame (cmd-xmit-raw-frame). The method + * allocates enough memory to support a burst of packets which contain the frame + * over the bus of the frame, sets the packet headers, and sends a signal to the + * lower block for the data (packets) to be written to the bus. + * + * The \p port the command was sent on and the channel info (\p chan_info) of + * the channel the frame is to be transmitted on are passed to ensure that the + * caller owns the channel. + * + * The \p data parameter is in the format of a cmd-xmit-raw-frame signal. + * + * The properties + */ +void usrp_server::handle_cmd_xmit_raw_frame( + mb_port_sptr port, + std::vector<struct channel_info> &chan_info, + pmt_t data) +{ + size_t n_bytes, psize; + long max_payload_len = transport_pkt::max_payload(); + + pmt_t invocation_handle = pmt_nth(0, data); + long channel = pmt_to_long(pmt_nth(1, data)); + const void *samples = pmt_uniform_vector_elements(pmt_nth(2, data), n_bytes); + long timestamp = pmt_to_long(pmt_nth(3, data)); + pmt_t properties = pmt_nth(4, data); + + // Ensure the channel is valid and the caller owns the port + if(!check_valid(port, channel, chan_info, + pmt_list2(s_response_xmit_raw_frame, invocation_handle))) + return; + + // Read information from the properties of the packet + bool carrier_sense = false; + if(pmt_is_dict(properties)) { + + // Check if carrier sense is enabled for the frame + if(pmt_t p_carrier_sense = pmt_dict_ref(properties, + pmt_intern("carrier-sense"), + PMT_NIL)) { + if(pmt_eqv(p_carrier_sense, PMT_T)) + carrier_sense = true; + } + } + + + // Determine the number of packets to allocate contiguous memory for + // bursting over the USB and get a pointer to the memory to be used in + // building the packets + long n_packets = + static_cast<long>(std::ceil(n_bytes / (double)max_payload_len)); + + pmt_t v_packets = pmt_make_u8vector(sizeof(transport_pkt) * n_packets, 0); + + transport_pkt *pkts = + (transport_pkt *) pmt_u8vector_writable_elements(v_packets, psize); + + for(int n=0; n < n_packets; n++) { + + long payload_len = + std::min((long)(n_bytes-(n*max_payload_len)), (long)max_payload_len); + + if(n == 0) { // first packet gets start of burst flag and timestamp + + if(carrier_sense) + pkts[n].set_header(pkts[n].FL_START_OF_BURST + | pkts[n].FL_CARRIER_SENSE, + channel, 0, payload_len); + else + pkts[n].set_header(pkts[n].FL_START_OF_BURST, channel, 0, payload_len); + + pkts[n].set_timestamp(timestamp); + + } else { + pkts[n].set_header(0, channel, 0, payload_len); + pkts[n].set_timestamp(0xffffffff); + } + + memcpy(pkts[n].payload(), + (uint8_t *)samples+(max_payload_len * n), + payload_len); + + } + + + pkts[n_packets-1].set_end_of_burst(); // set the last packet's end of burst + + if (verbose && 0) + std::cout << "[USRP_SERVER] Received raw frame invocation: " + << invocation_handle << std::endl; + + // The actual response to the write will be generated by a + // s_response_usrp_write since we cannot determine whether to transmit was + // successful until we hear from the lower layers. + d_cs_usrp->send(s_cmd_usrp_write, + pmt_list3(invocation_handle, + pmt_from_long(channel), + v_packets)); + + return; +} + +/*! + * \brief Called by the handle_message() method to parse incoming control/status + * signals (cmd-to-control-channel). + * + * The \p port the command was sent on and the channel info (\p chan_info) of + * the channel are passed to ensure that the caller owns the channel. + * + * The \p data parameter is in the format of a PMT list, where each element + * follows the format of a control/status signal (i.e. op-ping-fixed). + * + * The method will parse all of the C/S commands included in \p data and place + * the commands in to a lower level packet sent to the control channel. The + * method will pack as many commands as possible in t oa single packet, and once + * it is fill generate as many lower level packets as needed. + * + * Anything that needs to be returned to the sender of the signal (i.e. the + * value of a register) will be generated by the parse_control_pkt() method as + * the responses to the commands are read back from the USRP. + */ +void usrp_server::handle_cmd_to_control_channel( + mb_port_sptr port, + std::vector<struct channel_info> &chan_info, + pmt_t data) +{ + + pmt_t invocation_handle = pmt_nth(0, data); + pmt_t subpackets = pmt_nth(1, data); + + long n_subpkts = pmt_length(subpackets); + long curr_subpkt = 0; + + size_t psize; + long payload_len = 0; + long channel = CONTROL_CHAN; + + if(verbose) + std::cout << "[USRP_SERVER] Handling " << n_subpkts << " commands\n"; + + // The design of the following code is optimized for simplicity, not + // performance. To performance optimize this code, the total size in bytes + // needed for all of the CS packets is needed to allocate contiguous memory + // which contains the USB packets for bursting over the bus. However to do + // this the packets subpackets would need to be parsed twice and their sizes + // would need to be determined. + // + // The approach taken is to keep parsing the subpackets and putting them in to + // USB packets. Once the USB packet is full, a write is sent for it and + // another packet is created. + // + // The subpacket creation methods will return false if the subpacket will not + // fit in to the current USB packet. In these cases a new USB packet is + // created and the old is sent. + + new_packet: + // This code needs to become "smart" and only make a new packet when full + pmt_t v_packet = pmt_make_u8vector(sizeof(transport_pkt), 0); + transport_pkt *pkt = (transport_pkt *) pmt_u8vector_writable_elements(v_packet, psize); + payload_len = 0; + + pkt->set_header(0, channel, 0, payload_len); + pkt->set_timestamp(0xffffffff); + + while(curr_subpkt < n_subpkts) { + + pmt_t subp = pmt_nth(curr_subpkt, subpackets); + pmt_t subp_cmd = pmt_nth(0, subp); + pmt_t subp_data = pmt_nth(1, subp); + + //--------- PING FIXED --------------// + if(pmt_eq(subp_cmd, s_op_ping_fixed)) { + + long urid = pmt_to_long(pmt_nth(0, subp_data)); + long pingval = pmt_to_long(pmt_nth(1, subp_data)); + + // USRP server sets request ID's to keep track of which application gets + // what response back. To allow a full 6-bits for an RID to the user, we + // keep a mapping and replace the RID's as the packets go in and out. If + // there are no RID's available, the command is thrown away silently. + long srid; + if((srid = next_rid()) == -1) + goto subpkt_bail; + + // We use a vector to store the owner of the ping request and will use it + // to send the request on any RX port they own. + d_rids[srid].owner = port->port_symbol(); + d_rids[srid].user_rid = urid; + + // Adds a ping after the previous command in the pkt + if(!pkt->cs_ping(srid, pingval)) + { + d_cs_usrp->send(s_cmd_usrp_write, + pmt_list3(invocation_handle, + pmt_from_long(channel), + v_packet)); + + // Return the RID + d_rids[srid].owner = PMT_NIL; + + goto new_packet; + } + + if(verbose) + std::cout << "[USRP_SERVER] Received ping command request" + << " assigning RID " << srid << std::endl; + + } + + //----------- WRITE REG ---------------// + if(pmt_eq(subp_cmd, s_op_write_reg)) { + + long reg_num = pmt_to_long(pmt_nth(0, subp_data)); + long val = pmt_to_long(pmt_nth(1, subp_data)); + + if(!pkt->cs_write_reg(reg_num, val)) + { + d_cs_usrp->send(s_cmd_usrp_write, + pmt_list3(invocation_handle, + pmt_from_long(channel), + v_packet)); + + goto new_packet; + } + + if(verbose) + std::cout << "[USRP_SERVER] Received write register request " + << "(" + << "Reg: " << reg_num << ", " + << "Val: " << val + << ")\n"; + } + + //------- WRITE REG MASKED ----------// + if(pmt_eq(subp_cmd, s_op_write_reg_masked)) { + + long reg_num = pmt_to_long(pmt_nth(0, subp_data)); + long val = pmt_to_long(pmt_nth(1, subp_data)); + long mask = pmt_to_long(pmt_nth(2, subp_data)); + + if(!pkt->cs_write_reg_masked(reg_num, val, mask)) + { + d_cs_usrp->send(s_cmd_usrp_write, + pmt_list3(invocation_handle, + pmt_from_long(channel), + v_packet)); + + goto new_packet; + } + + if(verbose) + std::cout << "[USRP_SERVER] Received write register masked request\n"; + } + + //------------ READ REG --------------// + if(pmt_eq(subp_cmd, s_op_read_reg)) { + + long urid = pmt_to_long(pmt_nth(0, subp_data)); + long reg_num = pmt_to_long(pmt_nth(1, subp_data)); + + long srid; + if((srid = next_rid()) == -1) + goto subpkt_bail; + + d_rids[srid].owner = port->port_symbol(); + d_rids[srid].user_rid = urid; + + if(!pkt->cs_read_reg(srid, reg_num)) + { + d_cs_usrp->send(s_cmd_usrp_write, + pmt_list3(invocation_handle, + pmt_from_long(channel), + v_packet)); + + // Return the rid + d_rids[srid].owner = PMT_NIL; + + goto new_packet; + } + + if(verbose) + std::cout << "[USRP_SERVER] Received read register request" + << " assigning RID " << srid << std::endl; + } + + //------------ DELAY --------------// + if(pmt_eq(subp_cmd, s_op_delay)) { + + long ticks = pmt_to_long(pmt_nth(0, subp_data)); + + if(!pkt->cs_delay(ticks)) + { + d_cs_usrp->send(s_cmd_usrp_write, + pmt_list3(invocation_handle, + pmt_from_long(channel), + v_packet)); + + goto new_packet; + } + + if(verbose) + std::cout << "[USRP_SERVER] Received delay request of " + << ticks << " ticks\n"; + } + + //--------- I2C WRITE -----------// + // FIXME: could check that byte count does not exceed 2^8 which + // is the max length in the subpacket for # of bytes to read. + if(pmt_eq(subp_cmd, s_op_i2c_write)) { + + long i2c_addr = pmt_to_long(pmt_nth(0, subp_data)); + pmt_t data = pmt_nth(1, subp_data); + + // Get a readable address to the data which also gives us the length + size_t data_len; + uint8_t *i2c_data = (uint8_t *) pmt_u8vector_writable_elements(data, data_len); + + // Make the USB packet + if(!pkt->cs_i2c_write(i2c_addr, i2c_data, data_len)) + { + d_cs_usrp->send(s_cmd_usrp_write, + pmt_list3(invocation_handle, + pmt_from_long(channel), + v_packet)); + + goto new_packet; + } + + if(verbose) + std::cout << "[USRP_SERVER] Received I2C write\n"; + } + + //----------- I2C Read -------------// + if(pmt_eq(subp_cmd, s_op_i2c_read)) { + + long urid = pmt_to_long(pmt_nth(0, subp_data)); + long i2c_addr = pmt_to_long(pmt_nth(1, subp_data)); + long i2c_bytes = pmt_to_long(pmt_nth(2, subp_data)); + + long srid; + if((srid = next_rid()) == -1) + goto subpkt_bail; + + d_rids[srid].owner = port->port_symbol(); + d_rids[srid].user_rid = urid; + + if(!pkt->cs_i2c_read(srid, i2c_addr, i2c_bytes)) + { + + d_cs_usrp->send(s_cmd_usrp_write, + pmt_list3(invocation_handle, + pmt_from_long(channel), + v_packet)); + + d_rids[srid].owner = PMT_NIL; + + goto new_packet; + } + + if(verbose) + std::cout << "[USRP_SERVER] Received I2C read\n"; + } + + //--------- SPI WRITE -----------// + if(pmt_eq(subp_cmd, s_op_spi_write)) { + + long enables = pmt_to_long(pmt_nth(0, subp_data)); + long format = pmt_to_long(pmt_nth(1, subp_data)); + long opt = pmt_to_long(pmt_nth(2, subp_data)); + pmt_t data = pmt_nth(3, subp_data); + + // Get a readable address to the data which also gives us the length + size_t data_len; + uint8_t *spi_data = (uint8_t *) pmt_u8vector_writable_elements(data, data_len); + + // Make the USB packet + if(!pkt->cs_spi_write(enables, format, opt, spi_data, data_len)) + { + d_cs_usrp->send(s_cmd_usrp_write, + pmt_list3(invocation_handle, + pmt_from_long(channel), + v_packet)); + + goto new_packet; + } + + if(verbose) + std::cout << "[USRP_SERVER] Received SPI write\n"; + } + + //--------- SPI READ -----------// + if(pmt_eq(subp_cmd, s_op_spi_read)) { + + long urid = pmt_to_long(pmt_nth(0, subp_data)); + long enables = pmt_to_long(pmt_nth(1, subp_data)); + long format = pmt_to_long(pmt_nth(2, subp_data)); + long opt = pmt_to_long(pmt_nth(3, subp_data)); + long n_bytes = pmt_to_long(pmt_nth(4, subp_data)); + + long srid; + if((srid = next_rid()) == -1) + goto subpkt_bail; + + d_rids[srid].owner = port->port_symbol(); + d_rids[srid].user_rid = urid; + + // Make the USB packet + if(!pkt->cs_spi_read(srid, enables, format, opt, n_bytes)) + { + d_cs_usrp->send(s_cmd_usrp_write, + pmt_list3(invocation_handle, + pmt_from_long(channel), + v_packet)); + + // Return the rid + d_rids[srid].owner = PMT_NIL; + + goto new_packet; + } + + if(verbose) + std::cout << "[USRP_SERVER] Received SPI read\n"; + } + + subpkt_bail: + curr_subpkt++; + + } + + + // If the current packets length is > 0, we know there are subpackets that + // need to be sent out still. + if(pkt->payload_len() > 0) + d_cs_usrp->send(s_cmd_usrp_write, + pmt_list3(invocation_handle, + pmt_from_long(channel), + v_packet)); + + return; +} + +/*! + * \brief Called by the handle_message() method when the incoming signal is a + * command to start reading samples from the USRP (cmd-start-recv-raw-samples). + * + * The \p port the command was sent on and the channel info (\p chan_info) of + * the channel are passed to ensure that the caller owns the channel. + * + * The \p data parameter should be in the format of a cmd-start-recv-raw-samples + * command where the first element in the list is an invocation handle, and the + * second is the channel the signal generator wants to receive the samples on. + */ +void +usrp_server::handle_cmd_start_recv_raw_samples( + mb_port_sptr port, + std::vector<struct channel_info> &chan_info, + pmt_t data) +{ + pmt_t invocation_handle = pmt_nth(0, data); + long channel = pmt_to_long(pmt_nth(1, data)); + + // Ensure the channel is valid and the caller owns the port + if(!check_valid(port, channel, chan_info, + pmt_list2(s_response_xmit_raw_frame, invocation_handle))) + return; + + // Already started receiving samples? (another start before a stop) + // Check the RX channel bitmask. + if(d_rx_chan_mask & (1 << channel)) { + port->send(s_response_recv_raw_samples, + pmt_list5(invocation_handle, + s_err_already_receiving, + PMT_NIL, + PMT_NIL, + PMT_NIL)); + return; + } + + // We only need to generate a 'start reading' command down to the + // low level interface if no other channel is already reading + // + // We carry this over the CS interface because the lower level + // interface does not care about the channel, we only demux it + // at the usrp_server on responses. + if(d_rx_chan_mask == 0) { + + if(verbose) + std::cout << "[USRP_SERVER] Sending read request down to start recv\n"; + + d_cs_usrp->send(s_cmd_usrp_start_reading, pmt_list1(invocation_handle)); + } + + d_rx_chan_mask |= 1<<channel; + + return; +} + +/*! + * \brief Called by the handle_message() method when the incoming signal is to + * stop receiving samples from the USRP (cmd-stop-recv-raw-samples). + * + * The \p port the command was sent on and the channel info (\p chan_info) of + * the channel are passed to ensure that the caller owns the channel. + * + * The \p data parameter should be in the format of a cmd-stop-recv-raw-samples + * command where the first element in the list is an invocation handle, and the + * second is the channel the signal generator wants to stop receiving the + * samples from. + */ +void +usrp_server::handle_cmd_stop_recv_raw_samples( + mb_port_sptr port, + std::vector<struct channel_info> &chan_info, + pmt_t data) +{ + pmt_t invocation_handle = pmt_nth(0, data); + long channel = pmt_to_long(pmt_nth(1, data)); + + // FIX ME : we have no responses to send an error... + // Ensure the channel is valid and the caller owns the port + //if(!check_valid(port, channel, chan_info, + // pmt_list2(s_response_xmit_raw_frame, invocation_handle))) + // return; + + // Remove this hosts bit from the receiver mask + d_rx_chan_mask &= ~(1<<channel); + + // We only need to generate a 'start reading' command down to the + // low level interface if no other channel is already reading + // + // We carry this over the CS interface because the lower level + // interface does not care about the channel, we only demux it + // at the usrp_server on responses. + if(d_rx_chan_mask == 0) { + + if(verbose) + std::cout << "[USRP_SERVER] Sending stop reading request down\n"; + + d_cs_usrp->send(s_cmd_usrp_stop_reading, pmt_list1(invocation_handle)); + } + + return; +} + +/*! + * \brief Called by the handle_message() method when an incoming signal is + * generated to USRP server that contains raw samples from the USRP. This + * method generates the response-recv-raw-samples signals that are the result of + * a cmd-start-recv-raw-samples signal. + * + * The raw lower-level packet is extracted from \p data, where the format for \p + * data is a PMT list. The PMT \p data list should contain an invocation handle + * as the first element, the status of the lower-level read as the second + * element, and a uniform vector representation of the packets as the third + * element. + * + * The packet contains a channel field that the samples are destined to, and the + * method determines where to send the samples based on this channel since each + * channel has an associated port which allocated it. + */ +void +usrp_server::handle_response_usrp_read(pmt_t data) +{ + + pmt_t invocation_handle = pmt_nth(0, data); + pmt_t status = pmt_nth(1, data); + pmt_t v_pkt = pmt_nth(2, data); + + size_t n_bytes; + size_t ignore; + + if (d_fake_rx) { + + pmt_t pkt = pmt_nth(2, data); + + d_rx[0]->send(s_response_recv_raw_samples, + pmt_list5(PMT_F, + PMT_T, + pkt, + pmt_from_long(0xffff), + PMT_NIL)); + + return; + } + + // Extract the packet and return appropriately + transport_pkt *pkt = (transport_pkt *) pmt_u8vector_writable_elements(v_pkt, n_bytes); + + // The channel is used to find the port to pass the samples on + long channel = pkt->chan(); + long payload_len = pkt->payload_len(); + long port; + + // Ignore packets which seem to have incorrect size or size 0 + if(payload_len > pkt->max_payload() || payload_len == 0) + return; + + // If the packet is a C/S packet, parse it separately + if(channel == CONTROL_CHAN) { + parse_control_pkt(invocation_handle, pkt); + return; + } + + if((port = rx_port_index(d_chaninfo_rx[channel].owner)) == -1) + return; // Don't know where to send the sample... possibility on abrupt close + + pmt_t v_samples = pmt_make_u8vector(payload_len, 0); + uint8_t *samples = pmt_u8vector_writable_elements(v_samples, ignore); + + memcpy(samples, pkt->payload(), payload_len); + + // Build a properties dictionary to store things such as the RSSI + pmt_t properties = pmt_make_dict(); + + pmt_dict_set(properties, + pmt_intern("rssi"), + pmt_from_long(pkt->rssi())); + + if(pkt->overrun()) + pmt_dict_set(properties, + pmt_intern("overrun"), + PMT_T); + + if(pkt->underrun()) + pmt_dict_set(properties, + pmt_intern("underrun"), + PMT_T); + + d_rx[port]->send(s_response_recv_raw_samples, + pmt_list6(invocation_handle, + status, + v_samples, + pmt_from_long(pkt->timestamp()), + pmt_from_long(channel), + properties)); + return; +} + +/*! + * \brief Called by handle_response_usrp_read() when the incoming packet has a + * channel of CONTROL_CHAN. This means that the incoming packet contains a + * response for a command sent to the control channel, which this method will + * parse. + * + * The \p pkt parameter is a pointer to the full packet (transport_pkt) in + * memory. + * + * Given that all commands sent to the control channel that require responses + * will carry an RID (request ID), the method will use the RID passed back with + * the response to determine which port the response should be sent on. + */ +void +usrp_server::parse_control_pkt(pmt_t invocation_handle, transport_pkt *pkt) +{ + + long payload_len = pkt->payload_len(); + long curr_payload = 0; + long port; + + // We dispatch based on the control packet type, however we can extract the + // opcode and the length immediately which is consistent in all responses. + // + // Since each control packet can have multiple responses, we keep reading the + // lengths of each subpacket until we reach the payload length. + while(curr_payload < payload_len) { + + pmt_t sub_packet = pkt->read_subpacket(curr_payload); + pmt_t op_symbol = pmt_nth(0, sub_packet); + + int len = pkt->cs_len(curr_payload); + + if(verbose) + std::cout << "[USRP_SERVER] Parsing subpacket " + << op_symbol << " ... length " << len << std::endl; + + //----------------- PING RESPONSE ------------------// + if(pmt_eq(op_symbol, s_op_ping_fixed_reply)) { + + long srid = pmt_to_long(pmt_nth(1, sub_packet)); + pmt_t pingval = pmt_nth(2, sub_packet); + + long urid = d_rids[srid].user_rid; + + if(verbose) + std::cout << "[USRP_SERVER] Found ping response " + << "(" + << "URID: " << urid << ", " + << "SRID: " << srid << ", " + << "VAL: " << pingval + << ")\n"; + + // Do some bounds checking incase of bogus/corrupt responses + if(srid > D_MAX_RID) + return; + + pmt_t owner = d_rids[srid].owner; + + // Return the RID + d_rids[srid].owner = PMT_NIL; + + // FIXME: should be 1 response for all subpackets here ? + if((port = tx_port_index(owner)) != -1) + d_tx[port]->send(s_response_from_control_channel, + pmt_list4(invocation_handle, + PMT_T, + pmt_list2(s_op_ping_fixed_reply, // subp + pmt_list2(pmt_from_long(urid), + pingval)), + pmt_from_long(pkt->timestamp()))); + } + + //----------------- READ REG RESPONSE ------------------// + else if(pmt_eq(op_symbol, s_op_read_reg_reply)) { + + long srid = pmt_to_long(pmt_nth(1, sub_packet)); + pmt_t reg_num = pmt_nth(2, sub_packet); + pmt_t reg_val = pmt_nth(3, sub_packet); + + long urid = d_rids[srid].user_rid; + + if(verbose) + std::cout << "[USRP_SERVER] Found read register response " + << "(" + << "URID: " << urid << ", " + << "SRID: " << srid << ", " + << "REG: " << reg_num << ", " + << "VAL: " << reg_val + << ")\n"; + + // Do some bounds checking to avoid seg faults + if(srid > D_MAX_RID) + return; + + pmt_t owner = d_rids[srid].owner; + + // Return the RID + d_rids[srid].owner = PMT_NIL; + + // FIXME: should be 1 response for all subpackets here ? + if((port = tx_port_index(owner)) != -1) + d_tx[port]->send(s_response_from_control_channel, + pmt_list4(invocation_handle, + PMT_T, + pmt_list2(s_op_read_reg_reply, // subp + pmt_list3(pmt_from_long(urid), + reg_num, + reg_val)), + pmt_from_long(pkt->timestamp()))); + } + + //------------------ I2C READ REPLY -------------------// + else if(pmt_eq(op_symbol, s_op_i2c_read_reply)) { + + long srid = pmt_to_long(pmt_nth(1, sub_packet)); + pmt_t i2c_addr = pmt_nth(2, sub_packet); + pmt_t i2c_data = pmt_nth(3, sub_packet); + + long urid = d_rids[srid].user_rid; + + if(verbose) + std::cout << "[USRP_SERVER] Found i2c read reply " + << "(" + << "URID: " << urid << ", " + << "SRID: " << srid << ", " + << "Addr: " << i2c_addr << ", " + << "Data: " << i2c_data + << ")\n"; + + // Do some bounds checking to avoid seg faults + if(srid > D_MAX_RID) + return; + + pmt_t owner = d_rids[srid].owner; + + // Return the RID + d_rids[srid].owner = PMT_NIL; + + if((port = tx_port_index(owner)) != -1) + d_tx[port]->send(s_response_from_control_channel, + pmt_list4(invocation_handle, + PMT_T, + pmt_list2(s_op_i2c_read_reply, + pmt_list3(pmt_from_long(urid), + i2c_addr, + i2c_data)), + pmt_from_long(pkt->timestamp()))); + } + + //------------------ SPI READ REPLY -------------------// + else if(pmt_eq(op_symbol, s_op_spi_read_reply)) { + + long srid = pmt_to_long(pmt_nth(1, sub_packet)); + pmt_t spi_data = pmt_nth(2, sub_packet); + + long urid = d_rids[srid].user_rid; + + if(verbose) + std::cout << "[USRP_SERVER] Found SPI read reply " + << "(" + << "URID: " << urid << ", " + << "SRID: " << srid << ", " + << "Data: " << spi_data + << ")\n"; + + // Bounds check the RID + if(srid > D_MAX_RID) + return; + + pmt_t owner = d_rids[srid].owner; + + // Return the RID + d_rids[srid].owner = PMT_NIL; + + if((port = tx_port_index(owner)) != -1) + d_tx[port]->send(s_response_from_control_channel, + pmt_list4(invocation_handle, + PMT_T, + pmt_list2(s_op_spi_read_reply, + pmt_list2(pmt_from_long(urid), + spi_data)), + pmt_from_long(pkt->timestamp()))); + } + + // Each subpacket has an unaccounted for 2 bytes which is the opcode + // and the length field + curr_payload += len + 2; + + // All subpackets are 32-bit aligned + int align_offset = 4 - (curr_payload % 4); + + if(align_offset != 4) + curr_payload += align_offset; + } +} + +/*! + * \brief Used to recall all incoming signals that were deferred when USRP + * server was in the initialization state. + */ +void +usrp_server::recall_defer_queue() +{ + + std::vector<mb_message_sptr> recall; + + while(!d_defer_queue.empty()) { + recall.push_back(d_defer_queue.front()); + d_defer_queue.pop(); + } + + // Parse the messages that were queued while waiting for an open response + for(int i=0; i < (int)recall.size(); i++) + handle_message(recall[i]); + + return; +} + +/*! + * \brief Commonly called by any method which handles outgoing frames or control + * packets to the USRP to check if the port on which the signal was sent owns + * the channel the outgoing packet will be associated with. This helps ensure + * that applications do not send data on other application's ports. + * + * The \p port parameter is the port symbol that the caller wishes to determine + * owns the channel specified by \p chan_info. + * + * The \p signal_info parameter is a PMT list containing two elements: the + * response signal to use if the permissions are invalid, and the invocation + * handle that was passed. This allows the method to generate detailed failure + * responses to signals without having to return some sort of structured + * information which the caller must then parse and interpret to determine the + * failure type. + * + * \returns true if \p port owns the channel specified by \p chan_info, false + * otherwise. + */ +bool +usrp_server::check_valid(mb_port_sptr port, + long channel, + std::vector<struct channel_info> &chan_info, + pmt_t signal_info) +{ + + pmt_t response_signal = pmt_nth(0, signal_info); + pmt_t invocation_handle = pmt_nth(1, signal_info); + + // not a valid channel number? + if(channel >= (long)chan_info.size() && channel != CONTROL_CHAN) { + port->send(response_signal, + pmt_list2(invocation_handle, + s_err_channel_invalid)); + + if(verbose) + std::cout << "[USRP_SERVER] Invalid channel number for event " + << response_signal << std::endl; + return false; + } + + // not the owner of the port? + if(chan_info[channel].owner != port->port_symbol()) { + port->send(response_signal, + pmt_list2(invocation_handle, + s_err_channel_permission_denied)); + + if(verbose) + std::cout << "[USRP_SERVER] Invalid permissions" + << " for " << response_signal + << " from " << port->port_symbol() + << " proper owner is " << chan_info[channel].owner + << " on channel " << channel + << " invocation " << invocation_handle + << std::endl; + return false; + } + + return true; +} + +/*! + * \brief Finds the next available RID for internal USRP server use with control + * and status packets. + * + * \returns the next valid RID or -1 if no more RIDs are available. + */ +long +usrp_server::next_rid() +{ + for(int i = 0; i < D_MAX_RID; i++) + if(pmt_eqv(d_rids[i].owner, PMT_NIL)) + return i; + + if(verbose) + std::cout << "[USRP_SERVER] No RIDs left\n"; + return -1; +} + +/*! + * \brief Called by handle_message() when USRP server gets a response that the + * USRP was opened successfully to initialize the registers using the new + * register read/write control packets. + */ +void +usrp_server::initialize_registers() +{ + // We use handle_cmd_to_control_channel() to create the register writes using + // PMT_NIL as the response port to tell usrp_server not to pass the response + // up to any application. + if(verbose) + std::cout << "[USRP_SERVER] Initializing registers...\n"; + + // RX mode to normal (0) + set_register(FR_MODE, 0); + + // FPGA debugging? + if(d_fpga_debug) { + set_register(FR_DEBUG_EN, 1); + // FIXME: need to figure out exact register writes to control daughterboard + // pins that need to be written to + } else { + set_register(FR_DEBUG_EN, 0); + } + + // Set the transmit sample rate divisor, which is 4-1 + set_register(FR_TX_SAMPLE_RATE_DIV, 3); + + // Dboard IO buffer and register settings + set_register(FR_OE_0, (0xffff << 16) | 0x0000); + set_register(FR_IO_0, (0xffff << 16) | 0x0000); + set_register(FR_OE_1, (0xffff << 16) | 0x0000); + set_register(FR_IO_1, (0xffff << 16) | 0x0000); + set_register(FR_OE_2, (0xffff << 16) | 0x0000); + set_register(FR_IO_2, (0xffff << 16) | 0x0000); + set_register(FR_OE_3, (0xffff << 16) | 0x0000); + set_register(FR_IO_3, (0xffff << 16) | 0x0000); + + // zero Tx side Auto Transmit/Receive regs + set_register(FR_ATR_MASK_0, 0); + set_register(FR_ATR_TXVAL_0, 0); + set_register(FR_ATR_RXVAL_0, 0); + set_register(FR_ATR_MASK_1, 0); + set_register(FR_ATR_TXVAL_1, 0); + set_register(FR_ATR_RXVAL_1, 0); + set_register(FR_ATR_MASK_2, 0); + set_register(FR_ATR_TXVAL_2, 0); + set_register(FR_ATR_RXVAL_2, 0); + set_register(FR_ATR_MASK_3, 0); + set_register(FR_ATR_TXVAL_3, 0); + set_register(FR_ATR_RXVAL_3, 0); + + // Configure TX mux, this is a hacked value + set_register(FR_TX_MUX, 0x00000081); + + // Set the interpolation rate, which is the rate divided by 4, minus 1 + set_register(FR_INTERP_RATE, (d_interp_tx/4)-1); + + // Apparently this register changes again + set_register(FR_TX_MUX, 0x00000981); + + // Set the receive sample rate divisor, which is 2-1 + set_register(FR_RX_SAMPLE_RATE_DIV, 1); + + // DC offset + set_register(FR_DC_OFFSET_CL_EN, 0x0000000f); + + // Reset the DC correction offsets + set_register(FR_ADC_OFFSET_0, 0); + set_register(FR_ADC_OFFSET_1, 0); + + // Some hard-coded RX configuration + set_register(FR_RX_FORMAT, 0x00000300); + set_register(FR_RX_MUX, 1); + + // RX decimation rate is divided by two, then subtract 1 + set_register(FR_DECIM_RATE, (d_decim_rx/2)-1); + + // More hard coding + set_register(FR_RX_MUX, 0x000e4e41); + + // Resetting RX registers + set_register(FR_RX_PHASE_0, 0); + set_register(FR_RX_PHASE_1, 0); + set_register(FR_RX_PHASE_2, 0); + set_register(FR_RX_PHASE_3, 0); + set_register(FR_RX_FREQ_0, 0x28000000); + set_register(FR_RX_FREQ_1, 0); + set_register(FR_RX_FREQ_2, 0); + set_register(FR_RX_FREQ_3, 0); + + // Enable debug bus + set_register(FR_DEBUG_EN, 0xf); + set_register(FR_OE_0, -1); + set_register(FR_OE_1, -1); + set_register(FR_OE_2, -1); + set_register(FR_OE_3, -1); + + // DEBUGGING + //check_register_initialization(); +} + +// FIXME: used for debugging to determine if all the registers are actually +// being set correctly +void +usrp_server::check_register_initialization() +{ + // RX mode to normal (0) + read_register(FR_MODE); + + // FPGA debugging? + if(d_fpga_debug) { + read_register(FR_DEBUG_EN); + // FIXME: need to figure out exact register writes to control daughterboard + // pins that need to be written to + } else { + read_register(FR_DEBUG_EN); + } + + // Set the transmit sample rate divisor, which is 4-1 + read_register(FR_TX_SAMPLE_RATE_DIV); + + // Dboard IO buffer and register settings + read_register(FR_OE_0); + read_register(FR_IO_0); + read_register(FR_OE_1); + read_register(FR_IO_1); + read_register(FR_OE_2); + read_register(FR_IO_2); + read_register(FR_OE_3); + read_register(FR_IO_3); + + // zero Tx side Auto Transmit/Receive regs + read_register(FR_ATR_MASK_0); + read_register(FR_ATR_TXVAL_0); + read_register(FR_ATR_RXVAL_0); + read_register(FR_ATR_MASK_1); + read_register(FR_ATR_TXVAL_1); + read_register(FR_ATR_RXVAL_1); + read_register(FR_ATR_MASK_2); + read_register(FR_ATR_TXVAL_2); + read_register(FR_ATR_RXVAL_2); + read_register(FR_ATR_MASK_3); + read_register(FR_ATR_TXVAL_3); + read_register(FR_ATR_RXVAL_3); + + // Configure TX mux, this is a hacked value + read_register(FR_TX_MUX); + + // Set the interpolation rate, which is the rate divided by 4, minus 1 + read_register(FR_INTERP_RATE); + + // Apparently this register changes again + read_register(FR_TX_MUX); + + // Set the receive sample rate divisor, which is 2-1 + read_register(FR_RX_SAMPLE_RATE_DIV); + + // DC offset + read_register(FR_DC_OFFSET_CL_EN); + + // Reset the DC correction offsets + read_register(FR_ADC_OFFSET_0); + read_register(FR_ADC_OFFSET_1); + + // Some hard-coded RX configuration + read_register(FR_RX_FORMAT); + read_register(FR_RX_MUX); + + // RX decimation rate is divided by two, then subtract 1 + read_register(FR_DECIM_RATE); + + // More hard coding + read_register(FR_RX_MUX); + + // Resetting RX registers + read_register(FR_RX_PHASE_0); + read_register(FR_RX_PHASE_1); + read_register(FR_RX_PHASE_2); + read_register(FR_RX_PHASE_3); + read_register(FR_RX_FREQ_0); + read_register(FR_RX_FREQ_1); + read_register(FR_RX_FREQ_2); + read_register(FR_RX_FREQ_3); +} + +/*! + * \brief Used to generate FPGA register write commands to reset all of the FPGA + * registers to a value of 0. + */ +void +usrp_server::reset_all_registers() +{ + for(int i=0; i<64; i++) + set_register(i, 0); +} + +/*! + * \brief Used internally by USRP server to generate a control/status packet + * which contains a register write. + * + * The \p reg parameter is the register number that the value \p val will be + * written to. + */ +void +usrp_server::set_register(long reg, long val) +{ + size_t psize; + long payload_len = 0; + + pmt_t v_packet = pmt_make_u8vector(sizeof(transport_pkt), 0); + transport_pkt *pkt = (transport_pkt *) pmt_u8vector_writable_elements(v_packet, psize); + + pkt->set_header(0, CONTROL_CHAN, 0, payload_len); + pkt->set_timestamp(0xffffffff); + + pkt->cs_write_reg(reg, val); + + d_cs_usrp->send(s_cmd_usrp_write, + pmt_list3(PMT_NIL, + pmt_from_long(CONTROL_CHAN), + v_packet)); +} + +/*! + * \brief Used internally by USRP server to generate a control/status packet + * which contains a register read. This is important to use internally so that + * USRP server can bypass the use of RIDs with register reads, as they are not + * needed and it would use up the finite number of RIDs available for use for + * applications to receive responses. + * + * The \p reg parameter is the register number that the value should be read + * from. + */ +void +usrp_server::read_register(long reg) +{ + size_t psize; + long payload_len = 0; + + pmt_t v_packet = pmt_make_u8vector(sizeof(transport_pkt), 0); + transport_pkt *pkt = (transport_pkt *) pmt_u8vector_writable_elements(v_packet, psize); + + pkt->set_header(0, CONTROL_CHAN, 0, payload_len); + pkt->set_timestamp(0xffffffff); + + pkt->cs_read_reg(0, reg); + + d_cs_usrp->send(s_cmd_usrp_write, + pmt_list3(PMT_NIL, + pmt_from_long(CONTROL_CHAN), + v_packet)); +} + +REGISTER_MBLOCK_CLASS(usrp_server); diff --git a/usrp/limbo/inband/usrp_server.h b/usrp/limbo/inband/usrp_server.h new file mode 100644 index 000000000..dd1825d57 --- /dev/null +++ b/usrp/limbo/inband/usrp_server.h @@ -0,0 +1,131 @@ +/* -*- c++ -*- */ +/* + * Copyright 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 this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +#ifndef INCLUDED_USRP_SERVER_H +#define INCLUDED_USRP_SERVER_H + +#include <mblock/mblock.h> +#include <vector> +#include <queue> +#include <fstream> +#include <usrp_inband_usb_packet.h> + +typedef usrp_inband_usb_packet transport_pkt; // makes conversion to gigabit easy + +/*! + * \brief Implements the lowest-level mblock usb_interface to the USRP + */ +class usrp_server : public mb_mblock +{ +public: + + // our ports + enum port_types { + RX_PORT = 0, + TX_PORT = 1 + }; + static const int N_PORTS = 4; + std::vector<mb_port_sptr> d_tx, d_rx; + mb_port_sptr d_cs; + mb_port_sptr d_cs_usrp; + + static const int D_USB_CAPACITY = 32 * 1024 * 1024; + static const int D_MAX_CHANNELS = 16; + long d_ntx_chan; + long d_nrx_chan; + + pmt_t d_usrp_dict; + + bool d_fpga_debug; + + long d_interp_tx; + long d_decim_rx; + + // Keep track of the request IDs + struct rid_info { + pmt_t owner; + long user_rid; + + rid_info() { + owner = PMT_NIL; + user_rid = 0; + } + }; + + static const long D_MAX_RID = 64; + std::vector<rid_info> d_rids; + + struct channel_info { + long assigned_capacity; // the capacity currently assignedby the channel + pmt_t owner; // port ID of the owner of the channel + + channel_info() { + assigned_capacity = 0; + owner = PMT_NIL; + } + }; + + long d_rx_chan_mask; // A bitmask representing the channels in the + // receiving state + + std::vector<struct channel_info> d_chaninfo_tx; + std::vector<struct channel_info> d_chaninfo_rx; + + std::queue<mb_message_sptr> d_defer_queue; + + bool d_defer; + bool d_opened; + + bool d_fake_rx; + +public: + usrp_server(mb_runtime *rt, const std::string &instance_name, pmt_t user_arg); + ~usrp_server(); + + void initial_transition(); + void handle_message(mb_message_sptr msg); + +protected: + static int max_capacity() { return D_USB_CAPACITY; } + +private: + void handle_cmd_allocate_channel(mb_port_sptr port, std::vector<struct channel_info> &chan_info, pmt_t data); + void handle_cmd_deallocate_channel(mb_port_sptr port, std::vector<struct channel_info> &chan_info, pmt_t data); + void handle_cmd_xmit_raw_frame(mb_port_sptr port, std::vector<struct channel_info> &chan_info, pmt_t data); + void handle_cmd_to_control_channel(mb_port_sptr port, std::vector<struct channel_info> &chan_info, pmt_t data); + void handle_cmd_start_recv_raw_samples(mb_port_sptr port, std::vector<struct channel_info> &chan_info, pmt_t data); + void handle_cmd_stop_recv_raw_samples(mb_port_sptr port, std::vector<struct channel_info> &chan_info, pmt_t data); + int rx_port_index(pmt_t port_id); + int tx_port_index(pmt_t port_id); + long current_capacity_allocation(); + void recall_defer_queue(); + void reset_channels(); + void handle_response_usrp_read(pmt_t data); + bool check_valid(mb_port_sptr port, long channel, std::vector<struct channel_info> &chan_info, pmt_t signal_info); + void parse_control_pkt(pmt_t invocation_handle, transport_pkt *pkt); + long next_rid(); + void initialize_registers(); + void set_register(long reg, long val); + void read_register(long reg); + void check_register_initialization(); + void reset_all_registers(); +}; + +#endif /* INCLUDED_USRP_SERVER_H */ diff --git a/usrp/limbo/inband/usrp_server.mbh b/usrp/limbo/inband/usrp_server.mbh new file mode 100644 index 000000000..ed7943fc3 --- /dev/null +++ b/usrp/limbo/inband/usrp_server.mbh @@ -0,0 +1,255 @@ +;; -*- scheme -*- ; not really, but tells emacs how to format this +;; +;; Copyright 2007 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. +;; + +;; ---------------------------------------------------------------- +;; This is an mblock header file +;; +;; The format is very much a work-in-progress. +;; It'll be compiled to C++. +;; ---------------------------------------------------------------- + +;; In the outgoing messages described below, invocation-handle is an +;; identifier provided by the client to tag the method invocation. +;; The identifier will be returned with the response, to provide the +;; client with a mechanism to match asynchronous responses with the +;; commands that generate them. The value of the invocation-handle is +;; opaque the the server, and is not required by the server to be +;; unique. +;; +;; In the incoming messages described below, invocation-handle is the +;; identifier provided by the client in the prompting invocation. The +;; identifier is returned with the response, so that the client has a +;; mechanism to match asynchronous responses with the commands that +;; generated them. +;; +;; status is either #t, indicating success, or a symbol indicating an error. +;; All symbol's names shall begin with %error- + + +;; ---------------------------------------------------------------- +;; usrp-channel +;; +;; The protocol class is defined from the client's point-of-view. +;; (The client port is unconjugated, the server port is conjugated.) + +(define-protocol-class usrp-channel + + (:outgoing + + (cmd-allocate-channel invocation-handle capacity-reservation) + + ;; The cmd-allocate-channel message requests that the server + ;; allocates a logical channel in the FPGA for use. + ;; capacity-reservation specifies the number of bytes/s of + ;; interconnect capacity (USB or ethernet) to reserve for this + ;; channel. (The reservation is just a sanity check, no OS + ;; specific mechanism is used.) + + (cmd-deallocate-channel invocation-handle channel) + + ;; The integer channel specifies the channel to deallocate. + + ) + + (:incoming + + + (response-allocate-channel invocation-handle status channel) + + ;; If successful, a channel the specified capacity was allocated. + ;; channel, an integer, indicates which channel was allocated. + + (response-deallocate-channel invocation-handle status) + + ;; If successful, the specified channel and associated interconnect + ;; capacity were deallocated. + + ) + ) + +;; ---------------------------------------------------------------- +;; usrp-low-level-cs +;; +;; The protocol class is defined from the client's point-of-view. +;; (The client port is unconjugated, the server port is conjugated.) +;; +;; This defines a low level control and status interface to the usrp. +;; This will probably be replaced (or at least augmented) with a +;; higher level interface. For now, this will allow us to get on +;; the air. +;; +;; The subpackets are lists containing the relevant parameters. The +;; server will marshall them appropriately. Below is a list of +;; subpackets. See inband-signaling-usb for details. The opcodes are +;; symbols; unless otherwise indicated the remaining parameters are +;; integers. rid values are limited to 3-bits. +;; +;; (op-ping-fixed rid ping-value) +;; (op-ping-fixed-reply rid ping-value) +;; (op-write-reg reg-number reg-value) +;; (op-write-reg-masked reg-number reg-value mask-value) +;; (op-read-reg rid reg-number) +;; (op-read-reg-reply rid reg-number reg-value) +;; (op-i2c-write i2c-addr u8-vec) +;; (op-i2c-read rid i2c-addr nbytes) +;; (op-i2c-read-reply rid i2c-addr u8-vec) +;; (op-spi-write enables format opt-header-bytes u8-vec) +;; (op-spi-read rid enables format opt-header-bytes nbytes) +;; (op-spi-read-reply rid u8-vec) +;; (op-delay ticks) + + +(define-protocol-class usrp-low-level-cs + + (:outgoing + + (cmd-to-control-channel invocation-handle list-of-subpackets) + + ) + + (:incoming + + (response-from-control-channel invocation-handle status list-of-subpackets timestamp) + + ) + ) + +;; ---------------------------------------------------------------- +;; usrp-tx +;; +;; The protocol class is defined from the client's point-of-view. +;; (The client port is unconjugated, the server port is conjugated.) + +(define-protocol-class usrp-tx + (:include usrp-channel) + (:include usrp-low-level-cs) + + (:outgoing + + (cmd-xmit-raw-frame invocation-handle channel samples timestamp properties) + + ;; The argument channel must be an integer. It specifies the + ;; channel on which the frame of samples will be be sent. + ;; + ;; samples must be a uniform numeric vector. The contents of the + ;; sample vector is treated as opaque and is passed on to the FPGA + ;; unmodified. It is the responsibility of the sender to ensure + ;; that the binary format is sensible for the current FPGA + ;; configuration. + ;; + ;; timestamp is a 32-bit integer that specifies the time at which + ;; the first sample in samples shall be sent to the D/A converter. + ;; The format and interpration of time is specified in the file + ;; inband-signaling-usb + ) + + (:incoming + + (response-xmit-raw-frame invocation-handle status) + + ;; If successful, the samples of the associated frame have been + ;; transmitted to the USRP. This message may be used to implement + ;; Tx flow control. The client could for example implement a + ;; policy of never having more than 4 unacknowledged + ;; cmd-xmit-raw-frame's outstanding. + + ) + ) + +;; ---------------------------------------------------------------- +;; usrp-rx +;; +;; The protocol class is defined from the client's point-of-view. +;; (The client port is unconjugated, the server port is conjugated.) + +(define-protocol-class usrp-rx + (:include usrp-channel) + (:include usrp-low-level-cs) + + (:outgoing + + (cmd-start-recv-raw-samples invocation-handle channel) + + ;; The argument channel must be an integer. It specifies the + ;; channel from which frames of samples will be be received. The + ;; server will return response-recv-raw-samples messages until a + ;; cmd-stop-recv-raw-samples message is received. + + (cmd-stop-recv-raw-samples invocation-handle channel) + + ;; The argument channel must be an integer. There is no reply to + ;; this message. + + ) + + (:incoming + + (response-recv-raw-samples invocation-handle status samples timestamp channel properties) + + ;; samples is a uniform numeric vector. The contents of the sample + ;; vector is treated as opaque and is passed from the FPGA + ;; unmodified. It is the responsibility of the receiver to decode + ;; the binary format as appropriate for the current FPGA + ;; configuration. + ;; + ;; timestamp is a 32-bit integer that specifies the time at which + ;; the first sample in samples was received from the A/D converter. + ;; The format and interpretation of time is as specified in the + ;; file inband-signaling-usb. + ;; + ;; properties is a dictionary containing additional (key, value) + ;; pairs associated with the reception of these samples. In + ;; particular, the map may contain the Received Signal Strength + ;; Indication (RSSI) reported by the front end at the time the + ;; first sample was received from the A/D. + + ) + ) + + +;; ---------------------------------------------------------------- +;; usrp-server-cs +;; +;; Control and status port for usrp-server +;; +;; The protocol class is defined from the client's point-of-view. +;; (The client port is unconjugated, the server port is conjugated.) + +(define-protocol-class usrp-server-cs + + (:outgoing + (cmd-open invocation-handle which-usrp) + (cmd-close invocation-handle) + (cmd-max-capacity invocation-handle) + (cmd-ntx-chan invocation-handle) + (cmd-nrx-chan invocation-handle) + (cmd-current-capacity-allocation invocation-handle) + ) + + (:incoming + (response-open invocation-handle status) + (response-close invocation-handle status) + (response-max-capacity invocation-handle status capacity) + (response-ntx-chan invocation-handle status ntx-chan) + (response-nrx-chan invocation-handle status nrx-chan) + (response-current-capacity-allocation invocation-handle status capacity) + ) + ) diff --git a/usrp/limbo/inband/usrp_tx.cc b/usrp/limbo/inband/usrp_tx.cc new file mode 100644 index 000000000..0d4a84619 --- /dev/null +++ b/usrp/limbo/inband/usrp_tx.cc @@ -0,0 +1,150 @@ +/* -*- c++ -*- */ +/* + * Copyright 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 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 <usrp_tx.h> +#include <iostream> +#include <usb.h> +#include <mblock/class_registry.h> +#include <usrp_inband_usb_packet.h> +#include <fpga_regs_common.h> +#include <usrp_standard.h> +#include <stdio.h> + +#include <symbols_usrp_tx_cs.h> + +typedef usrp_inband_usb_packet transport_pkt; + +static const bool verbose = false; + +usrp_tx::usrp_tx(mb_runtime *rt, const std::string &instance_name, pmt_t user_arg) + : mb_mblock(rt, instance_name, user_arg), + d_disk_write(false) +{ + d_cs = define_port("cs", "usrp-tx-cs", true, mb_port::EXTERNAL); + + //d_disk_write=true; + + if(d_disk_write) { + d_ofile.open("tx_data.dat",std::ios::binary|std::ios::out); + d_cs_ofile.open("tx_cs.dat",std::ios::binary|std::ios::out); + } +} + +usrp_tx::~usrp_tx() +{ + if(d_disk_write) { + d_ofile.close(); + d_cs_ofile.close(); + } +} + +void +usrp_tx::initial_transition() +{ + +} + +/*! + * \brief Handles incoming signals to to the m-block, wihch should only ever be + * a single message: cmd-usrp-tx-write. + */ +void +usrp_tx::handle_message(mb_message_sptr msg) +{ + pmt_t event = msg->signal(); + pmt_t port_id = msg->port_id(); + pmt_t data = msg->data(); + + // Theoretically only have 1 message to ever expect, but + // want to make sure its at least what we want + if(pmt_eq(port_id, d_cs->port_symbol())) { + + if(pmt_eqv(event, s_cmd_usrp_tx_write)) + write(data); + } +} + +/*! + * \brief Performs the actual writing of data to the USB bus, called by + * handle_message() when a cmd-usrp-tx-write signal is received. + * + * The \p data parameter is a PMT list which contains three mandatory elements, + * in the following order: an invocation handle, a channel, and a uniform vector + * of memory which contains the packets to be written to the bus. + */ +void +usrp_tx::write(pmt_t data) +{ + pmt_t invocation_handle = pmt_nth(0, data); + pmt_t channel = pmt_nth(1, data); + pmt_t v_packets = pmt_nth(2, data); + d_utx = boost::any_cast<usrp_standard_tx_sptr>(pmt_any_ref(pmt_nth(3, data))); + + size_t n_bytes; + bool underrun; // this will need to go, as it is taken care of in the packet headers + + transport_pkt *pkts = (transport_pkt *) pmt_u8vector_writable_elements(v_packets, n_bytes); + + int ret = d_utx->write (pkts, n_bytes, &underrun); + + if (0 && underrun) + fprintf(stderr, "uU"); + + if (ret == (int) n_bytes) { + if (verbose) + std::cout << "[usrp_server] Write of " << n_bytes << " successful\n"; + // need to respond with the channel so the USRP server knows who to forward the result of + // the write to by looking up the owner of the channel + d_cs->send(s_response_usrp_tx_write, + pmt_list3(invocation_handle, PMT_T, channel)); + } + else { + if (verbose) + std::cout << "[usrp_server] Error writing " << n_bytes << " bytes to USB bus\n"; + d_cs->send(s_response_usrp_tx_write, + pmt_list3(invocation_handle, PMT_F, channel)); + } + + long n_packets = + static_cast<long>(std::ceil(n_bytes / (double)transport_pkt::max_pkt_size())); + + for(int i=0; i < n_packets; i++) { + + if(d_disk_write) { + if(pkts[i].chan() == CONTROL_CHAN) + d_cs_ofile.write((const char *)&pkts[i], transport_pkt::max_pkt_size()); + else + d_ofile.write((const char *)&pkts[i], transport_pkt::max_pkt_size()); + + d_cs_ofile.flush(); + d_ofile.flush(); + } + } + + + return; +} + +REGISTER_MBLOCK_CLASS(usrp_tx); diff --git a/usrp/limbo/inband/usrp_tx.h b/usrp/limbo/inband/usrp_tx.h new file mode 100644 index 000000000..d3a6f8b54 --- /dev/null +++ b/usrp/limbo/inband/usrp_tx.h @@ -0,0 +1,52 @@ +/* -*- c++ -*- */ +/* + * Copyright 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 this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +#ifndef INCLUDED_USRP_TX_H +#define INCLUDED_USRP_TX_H + +#include <mblock/mblock.h> +#include <fstream> +#include "usrp_standard.h" + +/*! + * \brief Implements the low level usb interface to the USRP + */ +class usrp_tx : public mb_mblock +{ + mb_port_sptr d_cs; + usrp_standard_tx_sptr d_utx; + + bool d_disk_write; + std::ofstream d_ofile; + std::ofstream d_cs_ofile; + + public: + usrp_tx(mb_runtime *rt, const std::string &instance_name, pmt_t user_arg); + ~usrp_tx(); + void initial_transition(); + void handle_message(mb_message_sptr msg); + + private: + void write(pmt_t data); +}; + + +#endif /* INCLUDED_USRP_TX_H */ + diff --git a/usrp/limbo/inband/usrp_tx_stub.cc b/usrp/limbo/inband/usrp_tx_stub.cc new file mode 100644 index 000000000..c78b1a7b8 --- /dev/null +++ b/usrp/limbo/inband/usrp_tx_stub.cc @@ -0,0 +1,344 @@ +/* -*- c++ -*- */ +/* + * Copyright 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 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 <iostream> +#include <vector> +#include <usb.h> +#include <mblock/class_registry.h> +#include <usrp_tx_stub.h> +#include <usrp_inband_usb_packet.h> +#include <fpga_regs_common.h> +#include "usrp_standard.h" +#include <stdio.h> +#include <fstream> +#include <usrp_rx_stub.h> + +#include <symbols_usrp_tx_cs.h> + +typedef usrp_inband_usb_packet transport_pkt; + +static const bool verbose = false; + +usrp_tx_stub::usrp_tx_stub(mb_runtime *rt, const std::string &instance_name, pmt_t user_arg) + : mb_mblock(rt, instance_name, user_arg), + d_disk_write(false) +{ + d_cs = define_port("cs", "usrp-tx-cs", true, mb_port::EXTERNAL); + + //d_disk_write=true; + + if(d_disk_write) { + d_ofile.open("tx_stub_data.dat",std::ios::binary|std::ios::out); + d_cs_ofile.open("tx_stub_cs.dat",std::ios::binary|std::ios::out); + } +} + +usrp_tx_stub::~usrp_tx_stub() +{ + if(d_disk_write) { + d_ofile.close(); + d_cs_ofile.close(); + } +} + +void +usrp_tx_stub::initial_transition() +{ + +} + +void +usrp_tx_stub::handle_message(mb_message_sptr msg) +{ + pmt_t event = msg->signal(); + pmt_t port_id = msg->port_id(); + pmt_t data = msg->data(); + + // Theoretically only have 1 message to ever expect, but + // want to make sure its at least what we want + if(pmt_eq(port_id, d_cs->port_symbol())) { + + if(pmt_eqv(event, s_cmd_usrp_tx_write)) + write(data); + } +} + +void +usrp_tx_stub::write(pmt_t data) +{ + pmt_t invocation_handle = pmt_nth(0, data); + pmt_t channel = pmt_nth(1, data); + pmt_t v_packets = pmt_nth(2, data); + d_utx = boost::any_cast<usrp_standard_tx *>(pmt_any_ref(pmt_nth(3, data))); + + size_t n_bytes; + + transport_pkt *pkts = (transport_pkt *) pmt_u8vector_writable_elements(v_packets, n_bytes); + long n_packets = static_cast<long>(std::ceil(n_bytes / (double)transport_pkt::max_pkt_size())); + + // Parse the packets looking for C/S packets and dump them to a disk if + // necessary + for(long i=0; i<n_packets; i++) { + + if(d_disk_write) { + if(pkts[i].chan() == CONTROL_CHAN) + d_cs_ofile.write((const char *)&pkts[i], transport_pkt::max_pkt_size()); + else + d_ofile.write((const char *)&pkts[i], transport_pkt::max_pkt_size()); + + d_cs_ofile.flush(); + d_ofile.flush(); + } + + if(pkts[i].chan() == CONTROL_CHAN) + parse_cs(invocation_handle, pkts[i]); + } + + d_cs->send(s_response_usrp_tx_write, + pmt_list3(invocation_handle, PMT_T, channel)); + + return; +} + +void +usrp_tx_stub::parse_cs(pmt_t invocation_handle, transport_pkt pkt) +{ + + long payload_len = pkt.payload_len(); + long curr_payload = 0; + + size_t ignore; + + // There is the possibility that the responses for a single USB packet full of + // CS packets will not fit back in a single USB packet, considering some + // responses are greater than their commands (read registers). + new_packet: + pmt_t v_pkt = pmt_make_u8vector(sizeof(transport_pkt), 0); + + transport_pkt *q_pkt = + (transport_pkt *) pmt_u8vector_writable_elements(v_pkt, ignore); + + q_pkt->set_header(0, CONTROL_CHAN, 0, 0); + q_pkt->set_timestamp(0xffffffff); + + // We dispatch based on the control packet type, however we can extract the + // opcode and the length immediately which is consistent in all responses. + // + // Since each control packet can have multiple responses, we keep reading the + // lengths of each subpacket until we reach the payload length. + while(curr_payload < payload_len) { + + pmt_t sub_packet = pkt.read_subpacket(curr_payload); + pmt_t op_symbol = pmt_nth(0, sub_packet); + + int len = pkt.cs_len(curr_payload); + + if(verbose) + std::cout << "[USRP_TX_STUB] Parsing subpacket " + << op_symbol << " ... length " << len << std::endl; + + //----------------- PING FIXED ------------------// + if(pmt_eq(op_symbol, s_op_ping_fixed)) { + + long rid = pmt_to_long(pmt_nth(1, sub_packet)); + long pingval = pmt_to_long(pmt_nth(2, sub_packet)); + + // Generate a reply and put it in the queue for the RX stub to read + if(!q_pkt->cs_ping_reply(rid, pingval)) + goto new_packet; + + if(verbose) + std::cout << "[USRP_TX_STUB] Generated ping response " + << "(" + << "RID: " << rid << ", " + << "VAL: " << pingval + << ")\n"; + } + + //----------------- READ REG ------------------// + if(pmt_eq(op_symbol, s_op_read_reg)) { + + long rid = pmt_to_long(pmt_nth(1, sub_packet)); + long reg_num = pmt_to_long(pmt_nth(2, sub_packet)); + long reg_val = 0xdeef; + + // Generate a reply and put it in the queue for the RX stub to read + if(!q_pkt->cs_read_reg_reply(rid, reg_num, reg_val)) + goto new_packet; + + if(verbose) + std::cout << "[USRP_TX_STUB] Generated read register response " + << "(" + << "RID: " << rid << ", " + << "REG: " << reg_num << ", " + << "VAL: " << reg_val + << ")\n"; + } + + //----------------- DELAY ------------------// + if(pmt_eq(op_symbol, s_op_delay)) { + + long ticks = pmt_to_long(pmt_nth(1, sub_packet)); + + if(verbose) + std::cout << "[USRP_TX_STUB] Received delay command " + << "(" + << "Ticks: " << ticks + << ")\n"; + } + + //----------------- WRITE REG ------------------// + if(pmt_eq(op_symbol, s_op_write_reg)) { + + pmt_t reg_num = pmt_nth(1, sub_packet); + pmt_t reg_val = pmt_nth(2, sub_packet); + + if(verbose) + std::cout << "[USRP_TX_STUB] Received write register command " + << "(" + << "RegNum: " << reg_num << ", " + << "Val: " << reg_val + << ")\n"; + } + + //----------------- WRITE REG MASK ---------------// + if(pmt_eq(op_symbol, s_op_write_reg_masked)) { + + pmt_t reg_num = pmt_nth(1, sub_packet); + pmt_t reg_val = pmt_nth(2, sub_packet); + pmt_t mask = pmt_nth(3, sub_packet); + + if(verbose) + std::cout << "[USRP_TX_STUB] Received write register command " + << "(" + << "RegNum: " << reg_num << ", " + << "Val: " << reg_val << ", " + << "Mask: " << mask + << ")\n"; + } + + //---------------- I2C WRITE ------------------// + if(pmt_eq(op_symbol, s_op_i2c_write)) { + pmt_t i2c_addr = pmt_nth(1, sub_packet); + pmt_t i2c_data = pmt_nth(2, sub_packet); + + if(verbose) + std::cout << "[USRP_TX_STUB] Received i2c write command " + << "(" + << "Addr: " << i2c_addr << ", " + << "Data: " << i2c_data + << ")\n"; + } + + //---------------- I2C READ ------------------// + if(pmt_eq(op_symbol, s_op_i2c_read)) { + long rid = pmt_to_long(pmt_nth(1, sub_packet)); + long i2c_addr = pmt_to_long(pmt_nth(2, sub_packet)); + long i2c_bytes = pmt_to_long(pmt_nth(3, sub_packet)); + + // Create data to place as a response, filled with 0xff + size_t ignore; + pmt_t i2c_data = pmt_make_u8vector(i2c_bytes, 0xff); + uint8_t *w_data = (uint8_t *) pmt_u8vector_writable_elements(i2c_data, ignore); + + // Generate a reply and put it in the queue for the RX stub to read + if(!q_pkt->cs_i2c_read_reply(rid, i2c_addr, w_data, i2c_bytes)) + goto new_packet; + + if(verbose) + std::cout << "[USRP_TX_STUB] Received i2c read " + << "(" + << "RID: " << rid << ", " + << "Addr: " << i2c_addr << ", " + << "Bytes: " << i2c_bytes + << ")\n"; + } + + //---------------- SPI WRITE ------------------// + if(pmt_eq(op_symbol, s_op_spi_write)) { + long enables = pmt_to_long(pmt_nth(1, sub_packet)); + long format = pmt_to_long(pmt_nth(2, sub_packet)); + long opt = pmt_to_long(pmt_nth(3, sub_packet)); + pmt_t data = pmt_nth(4, sub_packet); + + if(verbose) + std::cout << "[USRP_TX_STUB] Received spi write command " + << "(" + << "Enables: " << enables << ", " + << "Format: " << format << ", " + << "Options: " << opt << ", " + << "Data: " << data + << ")\n"; + } + + //---------------- SPI READ ------------------// + if(pmt_eq(op_symbol, s_op_spi_read)) { + long rid = pmt_to_long(pmt_nth(1, sub_packet)); + long enables = pmt_to_long(pmt_nth(2, sub_packet)); + long format = pmt_to_long(pmt_nth(3, sub_packet)); + long opt = pmt_to_long(pmt_nth(4, sub_packet)); + long n_bytes = pmt_to_long(pmt_nth(5, sub_packet)); + + // Create data to place as a fake response + size_t ignore; + pmt_t spi_data = pmt_make_u8vector(n_bytes, 0xff); + uint8_t *w_data = (uint8_t *) pmt_u8vector_writable_elements(spi_data, ignore); + + // Generate a reply and put it in the queue for the RX stub to read + if(!q_pkt->cs_spi_read_reply(rid, w_data, n_bytes)) + goto new_packet; + + if(verbose) + std::cout << "[USRP_TX_STUB] Received spi read command " + << "(" + << "RID: " << rid << ", " + << "Enables: " << enables << ", " + << "Format: " << format << ", " + << "Options: " << opt << ", " + << "Bytes: " << n_bytes + << ")\n"; + + } + + // Each subpacket has an unaccounted for 2 bytes which is the opcode + // and the length field + curr_payload += len + 2; + + // All subpackets are 32-bit aligned + int align_offset = 4 - (curr_payload % 4); + + if(align_offset != 4) + curr_payload += align_offset; + + } + + // If the packet has data in the payload, it needs queued + if(q_pkt->payload_len() > 0) + d_cs_queue.push(pmt_list2(invocation_handle, v_pkt)); + + return; +} + +REGISTER_MBLOCK_CLASS(usrp_tx_stub); diff --git a/usrp/limbo/inband/usrp_tx_stub.h b/usrp/limbo/inband/usrp_tx_stub.h new file mode 100644 index 000000000..b81037ad7 --- /dev/null +++ b/usrp/limbo/inband/usrp_tx_stub.h @@ -0,0 +1,61 @@ +/* -*- c++ -*- */ +/* + * Copyright 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 this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +#ifndef INCLUDED_USRP_TX_STUB_H +#define INCLUDED_USRP_TX_STUB_H + +#include <mblock/mblock.h> +#include <vector> +#include "usrp_standard.h" +#include <fstream> +#include <usrp_inband_usb_packet.h> + +typedef usrp_inband_usb_packet transport_pkt; + +/*! + * \brief Implements the low level usb interface to the USRP + */ +class usrp_tx_stub : public mb_mblock +{ + public: + + mb_port_sptr d_cs; + usrp_standard_tx* d_utx; + + std::ofstream d_ofile; + std::ofstream d_cs_ofile; + + bool d_disk_write; + + public: + usrp_tx_stub(mb_runtime *rt, const std::string &instance_name, pmt_t user_arg); + ~usrp_tx_stub(); + void initial_transition(); + void handle_message(mb_message_sptr msg); + + private: + void write(pmt_t data); + void parse_cs(pmt_t invocation_handle, transport_pkt pkt); + +}; + + +#endif /* INCLUDED_USRP_TX_STUB_H */ + diff --git a/usrp/limbo/inband/usrp_usb_interface.cc b/usrp/limbo/inband/usrp_usb_interface.cc new file mode 100644 index 000000000..fb7109a5a --- /dev/null +++ b/usrp/limbo/inband/usrp_usb_interface.cc @@ -0,0 +1,601 @@ +/* -*- c++ -*- */ +/* + * Copyright 2007,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 <usrp_usb_interface.h> + +#include <iostream> +#include <vector> +#include <usb.h> +#include <mblock/class_registry.h> +#include <usrp_inband_usb_packet.h> +#include <fpga_regs_common.h> +#include "usrp_rx.h" +#include <usrp_rx_stub.h> +#include "usrp_tx.h" +#include "usrp_standard.h" +#include <stdio.h> +#include <usrp_dbid.h> + +typedef usrp_inband_usb_packet transport_pkt; + +#include <symbols_usrp_interface_cs.h> +#include <symbols_usrp_tx_cs.h> +#include <symbols_usrp_rx_cs.h> +static pmt_t s_shutdown = pmt_intern("%shutdown"); + +static const bool verbose = false; + + +/*! + * \brief Initializes the USB interface m-block. + * + * The \p user_arg should be a PMT dictionary which can contain optional + * arguments for the block, such as the decimatoin and interpolation rate. + */ +usrp_usb_interface::usrp_usb_interface(mb_runtime *rt, const std::string &instance_name, pmt_t user_arg) + : mb_mblock(rt, instance_name, user_arg), + d_fake_usrp(false), + d_rx_reading(false), + d_interp_tx(128), + d_decim_rx(128), + d_rf_freq(-1), + d_rbf("inband_tx_rx.rbf") +{ + // Dictionary for arguments to all of the components + pmt_t usrp_dict = user_arg; + + // Default TX/RX interface + std::string tx_interface = "usrp_tx"; + std::string rx_interface = "usrp_rx"; + + if (pmt_is_dict(usrp_dict)) { + + // The 'fake-usrp' key enables the TX and RX stubs if PMT_T + if(pmt_t fake_usrp = pmt_dict_ref(usrp_dict, + pmt_intern("fake-usrp"), + PMT_NIL)) { + if(pmt_eqv(fake_usrp, PMT_T)) { + tx_interface = "usrp_tx_stub"; + rx_interface = "usrp_rx_stub"; + d_fake_usrp=true; + } + } + + // Read the TX interpolations + if(pmt_t interp_tx = pmt_dict_ref(usrp_dict, + pmt_intern("interp-tx"), + PMT_NIL)) { + if(!pmt_eqv(interp_tx, PMT_NIL)) + d_interp_tx = pmt_to_long(interp_tx); + } + + // Read the RX decimation rate + if(pmt_t decim_rx = pmt_dict_ref(usrp_dict, + pmt_intern("decim-rx"), + PMT_NIL)) { + if(!pmt_eqv(decim_rx, PMT_NIL)) + d_decim_rx = pmt_to_long(decim_rx); + } + + // Read the RBF + if(pmt_t rbf = pmt_dict_ref(usrp_dict, + pmt_intern("rbf"), + PMT_NIL)) { + if(!pmt_eqv(rbf, PMT_NIL)) + d_rbf = pmt_symbol_to_string(rbf); + } + + // The RF center frequency + if(pmt_t rf_freq = pmt_dict_ref(usrp_dict, + pmt_intern("rf-freq"), + PMT_NIL)) { + if(!pmt_eqv(rf_freq, PMT_NIL)) + d_rf_freq = pmt_to_double(rf_freq); + } + } + + if (verbose) { + std::cout << "[USRP_USB_INTERFACE] Setting USRP RBF to " + << d_rbf << std::endl; + + std::cout << "[USRP_USB_INTERFACE] Setting TX interpolation to " + << d_interp_tx << std::endl; + + std::cout << "[USRP_USB_INTERFACE] Setting RX interpolation to " + << d_decim_rx << std::endl; + + std::cout << "[USRP_USB_INTERFACE] Using TX interface: " + << tx_interface << "\n"; + + std::cout << "[USRP_USB_INTERFACE] Using RX interface: " + << rx_interface << "\n"; + + } + + d_cs = define_port("cs", "usrp-interface-cs", true, mb_port::EXTERNAL); + d_rx_cs = define_port("rx_cs", "usrp-rx-cs", false, mb_port::INTERNAL); + d_tx_cs = define_port("tx_cs", "usrp-tx-cs", false, mb_port::INTERNAL); + + // Connect to TX and RX + define_component("tx", tx_interface, usrp_dict); + define_component("rx", rx_interface, usrp_dict); + connect("self", "rx_cs", "rx", "cs"); + connect("self", "tx_cs", "tx", "cs"); + + // FIXME: the code should query the FPGA to retrieve the number of channels and such + d_ntx_chan = 2; + d_nrx_chan = 2; +} + +usrp_usb_interface::~usrp_usb_interface() +{ + +} + +void +usrp_usb_interface::initial_transition() +{ + +} + +/*! + * \brief Handles all incoming signals to the block from the lowest m-blocks + * which read/write to the bus, or the higher m-block which is the USRP server. + */ +void +usrp_usb_interface::handle_message(mb_message_sptr msg) +{ + pmt_t event = msg->signal(); // the "name" of the message + pmt_t port_id = msg->port_id(); // which port it came in on + pmt_t data = msg->data(); + pmt_t invocation_handle; + + if (pmt_eq(event, s_shutdown)) // ignore (for now) + return; + + //------------- CONTROL / STATUS -------------// + if (pmt_eq(port_id, d_cs->port_symbol())) { + + //------------ OPEN --------------// + if (pmt_eq(event, s_cmd_usrp_open)){ + handle_cmd_open(data); + return; + } + //----------- CLOSE -------------// + else if (pmt_eq(event, s_cmd_usrp_close)) { + handle_cmd_close(data); + return; + } + //---------- NTX CHAN ----------// + else if (pmt_eq(event, s_cmd_usrp_ntx_chan)) { + invocation_handle = pmt_nth(0, data); + d_cs->send(s_response_usrp_ntx_chan, + pmt_list2(invocation_handle, + pmt_from_long(d_ntx_chan))); + return; + } + //---------- NRX CHAN ----------// + else if (pmt_eq(event, s_cmd_usrp_nrx_chan)) { + invocation_handle = pmt_nth(0, data); + d_cs->send(s_response_usrp_nrx_chan, + pmt_list2(invocation_handle, + pmt_from_long(d_nrx_chan))); + return; + } + //------------ WRITE -----------// + else if(pmt_eq(event, s_cmd_usrp_write)) { + handle_cmd_write(data); + return; + } + //-------- START READING --------// + else if(pmt_eq(event, s_cmd_usrp_start_reading)) { + handle_cmd_start_reading(data); + return; + } + //-------- STOP READING --------// + else if(pmt_eq(event, s_cmd_usrp_stop_reading)) { + handle_cmd_stop_reading(data); + return; + } + + goto unhandled; + } + + //---------------- RX ------------------// + if (pmt_eq(port_id, d_rx_cs->port_symbol())) { + + // Relay reads back up + if(pmt_eq(event, s_response_usrp_rx_read)) { + d_cs->send(s_response_usrp_read, data); + return; + } + + goto unhandled; + } + + //---------------- TX ------------------// + if (pmt_eq(port_id, d_tx_cs->port_symbol())) { + + if(pmt_eq(event, s_response_usrp_tx_write)) { + + pmt_t invocation_handle = pmt_nth(0, data); + pmt_t status = pmt_nth(1, data); + pmt_t channel = pmt_nth(2, data); + + d_cs->send(s_response_usrp_write, + pmt_list3(invocation_handle, + status, + channel)); + + return; + } + + goto unhandled; + } + + unhandled: + std::cout << "[USRP_USB_INTERFACE] unhandled msg: " << msg << std::endl; +} + +/*! + * \brief Called by the handle_message() method when the incoming signal is to + * open a USB connection to the USRP (cmd-usrp-open). + * + * The \p data parameter is a PMT list, where the elements are an invocation + * handle and the USRP number. + */ +void +usrp_usb_interface::handle_cmd_open(pmt_t data) +{ + pmt_t invocation_handle = pmt_nth(0, data); + long which_usrp = pmt_to_long(pmt_nth(1, data)); + pmt_t reply_data; + + if(d_fake_usrp) { + d_cs->send(s_response_usrp_open, pmt_list2(invocation_handle, PMT_T)); + return; + } + + if (verbose) + std::cout << "[USRP_USB_INTERFACE] Handling open request for USRP " << which_usrp << "\n"; + + // Open up a standard RX and TX for communication with the USRP + + d_utx = usrp_standard_tx::make(which_usrp, + d_interp_tx, + 1, // 1 channel + -1, // mux + 4096, // USB block size + 16, // nblocks for async transfers + d_rbf + ); + + if(d_utx==0) { + if (verbose) + std::cout << "[USRP_USB_INTERFACE] Failed to open TX\n"; + reply_data = pmt_list2(invocation_handle, PMT_F); + d_cs->send(s_response_usrp_open, reply_data); + return; + } + + // Perform TX daughterboard tuning + double target_freq; + unsigned int mux; + int tgain, rgain; + float input_rate; + bool ok; + usrp_tune_result r; + + // Cast to usrp_basic and then detect daughterboards + d_ub_tx = d_utx; + usrp_subdev_spec tspec = pick_tx_subdevice(); + db_base_sptr tsubdev = d_ub_tx->selected_subdev(tspec); + + // Set the TX mux value + mux = d_utx->determine_tx_mux_value(tspec); + d_utx->set_mux(mux); + + // Set the TX gain and determine rate + tgain = tsubdev->gain_max(); + tsubdev->set_gain(tgain); + input_rate = d_ub_tx->converter_rate() / d_utx->interp_rate(); + + // Perform the actual tuning, if no frequency specified then pick + if(d_rf_freq==-1) + target_freq = tsubdev->freq_min()+((tsubdev->freq_max()-tsubdev->freq_min())/2.0); + else + target_freq = d_rf_freq; + ok = d_utx->tune(tsubdev->which(), tsubdev, target_freq, &r); + tsubdev->set_enable(true); + + if(verbose) { + printf("TX Subdevice name is %s\n", tsubdev->name().c_str()); + printf("TX Subdevice freq range: (%g, %g)\n", + tsubdev->freq_min(), tsubdev->freq_max()); + printf("mux: %#08x\n", mux); + printf("target_freq: %f\n", target_freq); + printf("ok: %s\n", ok ? "true" : "false"); + printf("gain: %d\n", tgain); + printf("r.baseband_freq: %f\n", r.baseband_freq); + printf("r.dxc_freq: %f\n", r.dxc_freq); + printf("r.residual_freq: %f\n", r.residual_freq); + printf("r.inverted: %d\n", r.inverted); + } + + if(!ok) { + std::cerr << "[USRP_USB_INTERFACE] Failed to set center frequency on TX\n"; + reply_data = pmt_list2(invocation_handle, PMT_F); + d_cs->send(s_response_usrp_open, reply_data); + return; + } + + d_utx->start(); + + if (verbose) + std::cout << "[USRP_USB_INTERFACE] Setup TX channel\n"; + + d_urx = + usrp_standard_rx::make (which_usrp, + d_decim_rx, + 1, // nchan + -1, // mux + 0, // set blank mode to start + 4096, // USB block size + 16, // number of blocks for async transfers + d_rbf); + + if(!d_urx) { + if (verbose) + std::cout << "[usrp_server] Failed to open RX\n"; + reply_data = pmt_list2(invocation_handle, PMT_F); + d_cs->send(s_response_usrp_open, reply_data); + return; + } + + // Cast to usrp_basic and then detect daughterboards + d_ub_rx = d_urx; + usrp_subdev_spec rspec = pick_rx_subdevice(); + db_base_sptr rsubdev = d_ub_rx->selected_subdev(rspec); + + // Set the RX mux value + mux = d_urx->determine_rx_mux_value(rspec); + d_urx->set_mux(mux); + + // Set the RX gain and determine rate + rgain = rsubdev->gain_max()/2.0; + rsubdev->set_gain(rgain); + input_rate = d_ub_rx->converter_rate() / d_urx->decim_rate(); + + ok = d_urx->tune(rsubdev->which(), rsubdev, target_freq, &r); + rsubdev->set_enable(true); + + if(verbose) { + printf("RX Subdevice name is %s\n", rsubdev->name().c_str()); + printf("RX Subdevice freq range: (%g, %g)\n", + rsubdev->freq_min(), rsubdev->freq_max()); + printf("mux: %#08x\n", mux); + printf("target_freq: %f\n", target_freq); + printf("ok: %s\n", ok ? "true" : "false"); + printf("gain: %d\n", rgain); + printf("r.baseband_freq: %f\n", r.baseband_freq); + printf("r.dxc_freq: %f\n", r.dxc_freq); + printf("r.residual_freq: %f\n", r.residual_freq); + printf("r.inverted: %d\n", r.inverted); + } + + if(!ok) { + std::cerr << "[USRP_USB_INTERFACE] Failed to set center frequency on RX\n"; + reply_data = pmt_list2(invocation_handle, PMT_F); + d_cs->send(s_response_usrp_open, reply_data); + return; + } + + if (verbose) + std::cout << "[USRP_USB_INTERFACE] Setup RX channel\n"; + +// d_utx->_write_fpga_reg(FR_DEBUG_EN,0xf); +// d_utx->_write_oe(0, 0xffff, 0xffff); +// d_urx->_write_oe(0, 0xffff, 0xffff); +// d_utx->_write_oe(1, 0xffff, 0xffff); +// d_urx->_write_oe(1, 0xffff, 0xffff); + + d_cs->send(s_response_usrp_open, pmt_list2(invocation_handle, PMT_T)); +} + +/*! + * \brief Called by the handle_message() method when the incoming signal is to + * write data to the USB bus (cmd-usrp-write). + * + * The \p data parameter is a PMT list containing 3 mandatory elements in the + * following order: an invocation handle, channel, and a uniform vector + * representation of the packets. + */ +void +usrp_usb_interface::handle_cmd_write(pmt_t data) +{ + pmt_t invocation_handle = pmt_nth(0, data); + pmt_t channel = pmt_nth(1, data); + pmt_t pkts = pmt_nth(2, data); + + pmt_t tx_handle = pmt_make_any(d_utx); + + d_tx_cs->send(s_cmd_usrp_tx_write, + pmt_list4(invocation_handle, + channel, + pkts, + tx_handle)); +} + +/*! + * \brief Called by the handle_message() method when the incoming signal is to + * start reading data from the USB bus (cmd-usrp-start-reading). + * + * The \p data parameter is a PMT list with a single element: an invocation + * handle which can be returned with the response. + */ +void +usrp_usb_interface::handle_cmd_start_reading(pmt_t data) +{ + pmt_t invocation_handle = pmt_nth(0, data); + + if(verbose) + std::cout << "[USRP_USB_INTERFACE] Starting RX...\n"; + + if(!d_fake_usrp) + d_urx->start(); + + pmt_t rx_handle = pmt_make_any(d_urx); + + d_rx_cs->send(s_cmd_usrp_rx_start_reading, pmt_list2(PMT_NIL, rx_handle)); + + d_rx_reading = true; + + return; +} + +/*! + * \brief Called by the handle_message() method when the incoming signal is to + * stop reading data from the USB bus (cmd-usrp-stop-reading). + * + * The \p data parameter is a PMT list with a single element: an invocation + * handle which can be returned with the response. + */ +void +usrp_usb_interface::handle_cmd_stop_reading(pmt_t data) +{ + pmt_t invocation_handle = pmt_nth(0, data); + + if(!d_fake_usrp) { + if(verbose) + std::cout << "[USRP_USB_INTERFACE] Stopping RX...\n"; + usrp_rx_stop = true; + + // Used to allow a read() being called by a lower layer to complete before + // stopping, else there can be partial data left on the bus and can generate + // errors. + while(usrp_rx_stop) {usleep(1);} + d_urx->stop(); + } + else { + if(verbose) + std::cout << "[USRP_USB_INTERFACE] Stopping fake RX...\n"; + usrp_rx_stop_stub = true; // extern to communicate with stub to wait + } + + d_rx_reading = false; + + return; +} + +/*! + * \brief Called by the handle_message() method when the incoming signal is to + * close the USB connection to the USRP. + * + * The \p data parameter is a PMT list with a single element: an invocation + * handle which can be returned with the response. + */ +void +usrp_usb_interface::handle_cmd_close(pmt_t data) +{ + pmt_t invocation_handle = pmt_nth(0, data); + + if(d_rx_reading) + handle_cmd_stop_reading(PMT_NIL); + + if(d_fake_usrp) { + d_cs->send(s_response_usrp_close, pmt_list2(invocation_handle, PMT_T)); + return; + } + + if (verbose) + std::cout << "[USRP_USB_INTERFACE] Handling close request for USRP\n"; + + d_utx.reset(); + d_urx.reset(); + + d_cs->send(s_response_usrp_close, pmt_list2(invocation_handle, PMT_T)); + + // FIXME This seems like a _very_ strange place to be calling shutdown_all. + // That decision should be left to high-level code, not low-level code like this. + shutdown_all(PMT_T); +} + +usrp_subdev_spec +usrp_usb_interface::pick_rx_subdevice() +{ + int dbids[] = { + USRP_DBID_FLEX_400_RX, + USRP_DBID_FLEX_900_RX, + USRP_DBID_FLEX_1200_RX, + USRP_DBID_FLEX_2400_RX, + USRP_DBID_TV_RX, + USRP_DBID_TV_RX_REV_2, + USRP_DBID_DBS_RX, + USRP_DBID_BASIC_RX + }; + + std::vector<int> candidates(dbids, dbids+(sizeof(dbids)/sizeof(int))); + return pick_subdev(d_ub_rx, candidates); +} + +usrp_subdev_spec +usrp_usb_interface::pick_tx_subdevice() +{ + int dbids[] = { + USRP_DBID_FLEX_400_TX, + USRP_DBID_FLEX_900_TX, + USRP_DBID_FLEX_1200_TX, + USRP_DBID_FLEX_2400_TX, + USRP_DBID_BASIC_TX + }; + + std::vector<int> candidates(dbids, dbids+(sizeof(dbids)/sizeof(int))); + return pick_subdev(d_ub_tx, candidates); +} + +usrp_subdev_spec +usrp_usb_interface::pick_subdev(boost::shared_ptr<usrp_basic> d_usrp_basic, std::vector<int> candidates) +{ + int dbid0 = d_usrp_basic->selected_subdev(usrp_subdev_spec(0, 0))->dbid(); + int dbid1 = d_usrp_basic->selected_subdev(usrp_subdev_spec(1, 0))->dbid(); + + for (int i = 0; i < candidates.size(); i++) { + int dbid = candidates[i]; + if (dbid0 == dbid) + return usrp_subdev_spec(0, 0); + if (dbid1 == dbid) + return usrp_subdev_spec(1, 0); + } + + if (dbid0 >= 0) + return usrp_subdev_spec(0, 0); + if (dbid1 >= 0) + return usrp_subdev_spec(1, 0); + + throw std::runtime_error("No suitable daughterboard found!"); +} + + +REGISTER_MBLOCK_CLASS(usrp_usb_interface); diff --git a/usrp/limbo/inband/usrp_usb_interface.h b/usrp/limbo/inband/usrp_usb_interface.h new file mode 100644 index 000000000..4d7750a7d --- /dev/null +++ b/usrp/limbo/inband/usrp_usb_interface.h @@ -0,0 +1,78 @@ +/* -*- c++ -*- */ +/* + * Copyright 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 this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +#ifndef INCLUDED_USRP_USB_INTERFACE_H +#define INCLUDED_USRP_USB_INTERFACE_H + +#include <mblock/mblock.h> +#include <vector> +#include "usrp_standard.h" + +/*! + * \brief Implements the low level usb interface to the USRP + */ +class usrp_usb_interface : public mb_mblock +{ + public: + + usrp_standard_tx_sptr d_utx; + usrp_standard_rx_sptr d_urx; + + boost::shared_ptr<usrp_basic> d_ub_tx; + boost::shared_ptr<usrp_basic> d_ub_rx; + + mb_port_sptr d_cs; + mb_port_sptr d_rx_cs; + mb_port_sptr d_tx_cs; + + long d_ntx_chan; + long d_nrx_chan; + + bool d_fake_usrp; + + bool d_rx_reading; + + long d_interp_tx; + long d_decim_rx; + + double d_rf_freq; + + std::string d_rbf; + + public: + usrp_usb_interface(mb_runtime *rt, const std::string &instance_name, pmt_t user_arg); + ~usrp_usb_interface(); + void initial_transition(); + void handle_message(mb_message_sptr msg); + usrp_subdev_spec pick_rx_subdevice(); + usrp_subdev_spec pick_tx_subdevice(); + usrp_subdev_spec pick_subdev(boost::shared_ptr<usrp_basic> d_usrp_basic, std::vector<int> candidates); + + private: + void handle_cmd_open(pmt_t data); + void handle_cmd_close(pmt_t data); + void handle_cmd_write(pmt_t data); + void handle_cmd_start_reading(pmt_t data); + void handle_cmd_stop_reading(pmt_t data); + +}; + + +#endif /* INCLUDED_USRP_USB_INTERFACE_H */ |