diff options
-rw-r--r-- | firmware/usrp3/x300/x300_main.c | 2 | ||||
-rw-r--r-- | host/examples/CMakeLists.txt | 1 | ||||
-rw-r--r-- | host/examples/sync_to_gps.cpp | 191 | ||||
-rw-r--r-- | host/include/uhd/types/serial.hpp | 13 | ||||
-rw-r--r-- | host/lib/transport/udp_simple.cpp | 11 | ||||
-rw-r--r-- | host/lib/usrp/b200/b200_impl.cpp | 8 | ||||
-rw-r--r-- | host/lib/usrp/b200/b200_uart.cpp | 36 | ||||
-rw-r--r-- | host/lib/usrp/b200/b200_uart.hpp | 1 | ||||
-rw-r--r-- | host/lib/usrp/e100/e100_ctrl.cpp | 15 | ||||
-rw-r--r-- | host/lib/usrp/gps_ctrl.cpp | 241 | ||||
-rw-r--r-- | host/lib/usrp/x300/x300_fw_uart.cpp | 32 | ||||
-rw-r--r-- | host/lib/usrp_clock/octoclock/octoclock_uart.cpp | 23 |
12 files changed, 391 insertions, 183 deletions
diff --git a/firmware/usrp3/x300/x300_main.c b/firmware/usrp3/x300/x300_main.c index 42ee3248b..f255fe96c 100644 --- a/firmware/usrp3/x300/x300_main.c +++ b/firmware/usrp3/x300/x300_main.c @@ -344,12 +344,12 @@ static void handle_uarts(void) static uint32_t rxoffset = 0; for (int rxch = wb_uart_getc(UART0_BASE); rxch != -1; rxch = wb_uart_getc(UART0_BASE)) { - rxoffset++; const int shift = ((rxoffset%4) * 8); static uint32_t rxword32 = 0; if (shift == 0) rxword32 = 0; rxword32 |= ((uint32_t) rxch & 0xFF) << shift; rxpool[(rxoffset/4) % NUM_POOL_WORDS32] = rxword32; + rxoffset++; shmem[X300_FW_SHMEM_UART_RX_INDEX] = rxoffset; } diff --git a/host/examples/CMakeLists.txt b/host/examples/CMakeLists.txt index 43e0db9c0..e61fd897f 100644 --- a/host/examples/CMakeLists.txt +++ b/host/examples/CMakeLists.txt @@ -36,6 +36,7 @@ SET(example_sources txrx_loopback_to_file.cpp latency_test.cpp gpio.cpp + sync_to_gps.cpp ) IF(ENABLE_OCTOCLOCK) diff --git a/host/examples/sync_to_gps.cpp b/host/examples/sync_to_gps.cpp new file mode 100644 index 000000000..f27c3eff5 --- /dev/null +++ b/host/examples/sync_to_gps.cpp @@ -0,0 +1,191 @@ +// +// Copyright 2016 Ettus Research +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see <http://www.gnu.org/licenses/>. +// + +#include <uhd/utils/thread_priority.hpp> +#include <uhd/utils/safe_main.hpp> +#include <uhd/usrp/multi_usrp.hpp> +#include <boost/format.hpp> +#include <boost/program_options.hpp> +#include <boost/thread.hpp> +#include <iostream> + +namespace po = boost::program_options; + +void print_notes(void) +{ + // Helpful notes + std::cout << boost::format("**************************************Helpful Notes on Clock/PPS Selection**************************************\n"); + std::cout << boost::format("As you can see, the default 10 MHz Reference and 1 PPS signals are now from the GPSDO.\n"); + std::cout << boost::format("If you would like to use the internal reference(TCXO) in other applications, you must configure that explicitly.\n"); + std::cout << boost::format("You can no longer select the external SMAs for 10 MHz or 1 PPS signaling.\n"); + std::cout << boost::format("****************************************************************************************************************\n"); +} + +int UHD_SAFE_MAIN(int argc, char *argv[]) +{ + uhd::set_thread_priority_safe(); + + std::string args; + + //Set up program options + po::options_description desc("Allowed options"); + desc.add_options() + ("help", "help message") + ("args", po::value<std::string>(&args)->default_value(""), "USRP device arguments") + ; + po::variables_map vm; + po::store(po::parse_command_line(argc, argv, desc), vm); + po::notify(vm); + + //Print the help message + if (vm.count("help")) + { + std::cout << boost::format("Synchronize USRP to GPS %s") % desc << std::endl; + return EXIT_FAILURE; + } + + //Create a USRP device + std::cout << boost::format("\nCreating the USRP device with: %s...\n") % args; + uhd::usrp::multi_usrp::sptr usrp = uhd::usrp::multi_usrp::make(args); + std::cout << boost::format("Using Device: %s\n") % usrp->get_pp_string(); + + try + { + size_t num_mboards = usrp->get_num_mboards(); + size_t num_gps_locked = 0; + for (size_t mboard = 0; mboard < num_mboards; mboard++) + { + std::cout << "Synchronizing mboard " << mboard << ": " << usrp->get_mboard_name(mboard) << std::endl; + + //Set references to GPSDO + usrp->set_clock_source("gpsdo", mboard); + usrp->set_time_source("gpsdo", mboard); + + std::cout << std::endl; + print_notes(); + std::cout << std::endl; + + //Check for 10 MHz lock + std::vector<std::string> sensor_names = usrp->get_mboard_sensor_names(mboard); + if(std::find(sensor_names.begin(), sensor_names.end(), "ref_locked") != sensor_names.end()) + { + std::cout << "Waiting for reference lock..." << std::flush; + bool ref_locked = false; + for (int i = 0; i < 30 and not ref_locked; i++) + { + ref_locked = usrp->get_mboard_sensor("ref_locked", mboard).to_bool(); + if (not ref_locked) + { + std::cout << "." << std::flush; + boost::this_thread::sleep(boost::posix_time::seconds(1)); + } + } + if(ref_locked) + { + std::cout << "LOCKED" << std::endl; + } else { + std::cout << "FAILED" << std::endl; + throw uhd::runtime_error("Failed to lock to GPSDO 10 MHz Reference"); + } + } + else + { + std::cout << boost::format("ref_locked sensor not present on this board.\n"); + } + + //Wait for GPS lock + bool gps_locked = usrp->get_mboard_sensor("gps_locked", mboard).to_bool(); + if(gps_locked) + { + num_gps_locked++; + std::cout << boost::format("GPS Locked\n"); + } + else + { + std::cerr << "WARNING: GPS not locked - time will not be accurate until locked" << std::endl; + } + + //Set to GPS time + uhd::time_spec_t gps_time = uhd::time_spec_t(time_t(usrp->get_mboard_sensor("gps_time", mboard).to_int())); + usrp->set_time_next_pps(gps_time+1.0, mboard); + + //Wait for it to apply + //The wait is 2 seconds because N-Series has a known issue where + //the time at the last PPS does not properly update at the PPS edge + //when the time is actually set. + boost::this_thread::sleep(boost::posix_time::seconds(2)); + + //Check times + gps_time = uhd::time_spec_t(time_t(usrp->get_mboard_sensor("gps_time", mboard).to_int())); + uhd::time_spec_t time_last_pps = usrp->get_time_last_pps(mboard); + std::cout << "USRP time: " << (boost::format("%0.9f") % time_last_pps.get_real_secs()) << std::endl; + std::cout << "GPSDO time: " << (boost::format("%0.9f") % gps_time.get_real_secs()) << std::endl; + if (gps_time.get_real_secs() == time_last_pps.get_real_secs()) + std::cout << std::endl << "SUCCESS: USRP time synchronized to GPS time" << std::endl << std::endl; + else + std::cerr << std::endl << "ERROR: Failed to synchronize USRP time to GPS time" << std::endl << std::endl; + } + + if (num_gps_locked == num_mboards and num_mboards > 1) + { + //Check to see if all USRP times are aligned + //First, wait for PPS. + uhd::time_spec_t time_last_pps = usrp->get_time_last_pps(); + while (time_last_pps == usrp->get_time_last_pps()) + { + boost::this_thread::sleep(boost::posix_time::milliseconds(1)); + } + + //Sleep a little to make sure all devices have seen a PPS edge + boost::this_thread::sleep(boost::posix_time::milliseconds(200)); + + //Compare times across all mboards + bool all_matched = true; + uhd::time_spec_t mboard0_time = usrp->get_time_last_pps(0); + for (size_t mboard = 1; mboard < num_mboards; mboard++) + { + uhd::time_spec_t mboard_time = usrp->get_time_last_pps(mboard); + if (mboard_time != mboard0_time) + { + all_matched = false; + std::cerr << (boost::format("ERROR: Times are not aligned: USRP 0=%0.9f, USRP %d=%0.9f") + % mboard0_time.get_real_secs() + % mboard + % mboard_time.get_real_secs()) << std::endl; + } + } + if (all_matched) + { + std::cout << "SUCCESS: USRP times aligned" << std::endl << std::endl; + } else { + std::cout << "ERROR: USRP times are not aligned" << std::endl << std::endl; + } + } + } + catch (std::exception& e) + { + std::cout << boost::format("\nError: %s") % e.what(); + std::cout << boost::format("This could mean that you have not installed the GPSDO correctly.\n\n"); + std::cout << boost::format("Visit one of these pages if the problem persists:\n"); + std::cout << boost::format(" * N2X0/E1X0: http://files.ettus.com/manual/page_gpsdo.html"); + std::cout << boost::format(" * X3X0: http://files.ettus.com/manual/page_gpsdo_x3x0.html\n\n"); + std::cout << boost::format(" * E3X0: http://files.ettus.com/manual/page_usrp_e3x0.html#e3x0_hw_gps\n\n"); + exit(EXIT_FAILURE); + } + + return EXIT_SUCCESS; +} diff --git a/host/include/uhd/types/serial.hpp b/host/include/uhd/types/serial.hpp index 5b7f34fbd..cb90d7c71 100644 --- a/host/include/uhd/types/serial.hpp +++ b/host/include/uhd/types/serial.hpp @@ -157,12 +157,12 @@ namespace uhd{ size_t num_bits, bool readback ) = 0; - + /*! * Read from the SPI bus. * \param which_slave the slave device number * \param config spi config args - * \param data the bits to write out (be sure to set write bit) + * \param data the bits to write out (be sure to set write bit) * \param num_bits how many bits in data * \return spi data */ @@ -172,7 +172,7 @@ namespace uhd{ boost::uint32_t data, size_t num_bits ); - + /*! * Write to the SPI bus. * \param which_slave the slave device number @@ -189,7 +189,7 @@ namespace uhd{ }; /*! - * UART interface to write and read bytes. + * UART interface to write and read strings. */ class UHD_API uart_iface{ public: @@ -204,10 +204,9 @@ namespace uhd{ virtual void write_uart(const std::string &buf) = 0; /*! - * Read from a serial port. - * Reads until complete line or timeout. + * Read a line from a serial port. * \param timeout the timeout in seconds - * \return the data read from the serial port + * \return the line or empty string upon timeout */ virtual std::string read_uart(double timeout) = 0; }; diff --git a/host/lib/transport/udp_simple.cpp b/host/lib/transport/udp_simple.cpp index 347373c71..43414feaa 100644 --- a/host/lib/transport/udp_simple.cpp +++ b/host/lib/transport/udp_simple.cpp @@ -114,9 +114,13 @@ public: do{ //drain anything in current buffer while (_off < _len){ - const char ch = _buf[_off]; _off++; - line += std::string(1, ch); - if (ch == '\n' or ch == '\r') return line; + const char ch = _buf[_off++]; + _line += ch; + if (ch == '\n') + { + line.swap(_line); + return line; + } } //recv a new packet into the buffer @@ -131,6 +135,7 @@ private: udp_simple::sptr _udp; size_t _len, _off; boost::uint8_t _buf[udp_simple::mtu]; + std::string _line; }; uhd::uart_iface::sptr udp_simple::make_uart(sptr udp){ diff --git a/host/lib/usrp/b200/b200_impl.cpp b/host/lib/usrp/b200/b200_impl.cpp index aed1204d3..33f0850eb 100644 --- a/host/lib/usrp/b200/b200_impl.cpp +++ b/host/lib/usrp/b200/b200_impl.cpp @@ -461,6 +461,10 @@ b200_impl::b200_impl(const uhd::device_addr_t& device_addr, usb_device_handle::s //////////////////////////////////////////////////////////////////// _async_task_data.reset(new AsyncTaskData()); _async_task_data->async_md.reset(new async_md_type(1000/*messages deep*/)); + if (_gpsdo_capable) + { + _async_task_data->gpsdo_uart = b200_uart::make(_ctrl_transport, B200_TX_GPS_UART_SID); + } _async_task = uhd::msg_task::make(boost::bind(&b200_impl::handle_async_task, this, _ctrl_transport, _async_task_data)); //////////////////////////////////////////////////////////////////// @@ -481,10 +485,6 @@ b200_impl::b200_impl(const uhd::device_addr_t& device_addr, usb_device_handle::s //////////////////////////////////////////////////////////////////// if (_gpsdo_capable) { - _async_task_data->gpsdo_uart = b200_uart::make(_ctrl_transport, B200_TX_GPS_UART_SID); - _async_task_data->gpsdo_uart->set_baud_divider(B200_BUS_CLOCK_RATE/115200); - _async_task_data->gpsdo_uart->write_uart("\n"); //cause the baud and response to be setup - boost::this_thread::sleep(boost::posix_time::seconds(1)); //allow for a little propagation if ((_local_ctrl->peek32(RB32_CORE_STATUS) & 0xff) != B200_GPSDO_ST_NONE) { diff --git a/host/lib/usrp/b200/b200_uart.cpp b/host/lib/usrp/b200/b200_uart.cpp index 4682a79b9..065aa49ce 100644 --- a/host/lib/usrp/b200/b200_uart.cpp +++ b/host/lib/usrp/b200/b200_uart.cpp @@ -16,12 +16,14 @@ // #include "b200_uart.hpp" +#include "b200_impl.hpp" #include <uhd/transport/bounded_buffer.hpp> #include <uhd/transport/vrt_if_packet.hpp> #include <uhd/utils/byteswap.hpp> #include <uhd/utils/msg.hpp> #include <uhd/types/time_spec.hpp> #include <uhd/exception.hpp> +#include <boost/foreach.hpp> using namespace uhd; using namespace uhd::transport; @@ -32,10 +34,10 @@ struct b200_uart_impl : b200_uart _xport(xport), _sid(sid), _count(0), - _char_queue(4096) + _baud_div(std::floor(B200_BUS_CLOCK_RATE/115200 + 0.5)), + _line_queue(4096) { - //this default baud divider is over 9000 - this->set_baud_divider(9001); + /*NOP*/ } void send_char(const char ch) @@ -67,23 +69,16 @@ struct b200_uart_impl : b200_uart void write_uart(const std::string &buff) { - for (size_t i = 0; i < buff.size(); i++) + BOOST_FOREACH(const char ch, buff) { - if (buff[i] == '\n') this->send_char('\r'); - this->send_char(buff[i]); + this->send_char(ch); } } std::string read_uart(double timeout) { std::string line; - char ch = '\0'; - while (_char_queue.pop_with_timed_wait(ch, timeout)) - { - if (ch == '\r') continue; - line += std::string(&ch, 1); - if (ch == '\n') return line; - } + _line_queue.pop_with_timed_wait(line, timeout); return line; } @@ -95,19 +90,20 @@ struct b200_uart_impl : b200_uart packet_info.num_packet_words32 = buff->size()/sizeof(boost::uint32_t); vrt::if_hdr_unpack_le(packet_buff, packet_info); const char ch = char(uhd::wtohx(packet_buff[packet_info.num_header_words32+1])); - _char_queue.push_with_pop_on_full(ch); - } - - void set_baud_divider(const double baud_div) - { - _baud_div = size_t(baud_div + 0.5); + _line += ch; + if (ch == '\n') + { + _line_queue.push_with_pop_on_full(_line); + _line.clear(); + } } const zero_copy_if::sptr _xport; const boost::uint32_t _sid; size_t _count; size_t _baud_div; - bounded_buffer<char> _char_queue; + bounded_buffer<std::string> _line_queue; + std::string _line; }; diff --git a/host/lib/usrp/b200/b200_uart.hpp b/host/lib/usrp/b200/b200_uart.hpp index 1c8e44ddc..f58479888 100644 --- a/host/lib/usrp/b200/b200_uart.hpp +++ b/host/lib/usrp/b200/b200_uart.hpp @@ -29,7 +29,6 @@ public: typedef boost::shared_ptr<b200_uart> sptr; static sptr make(uhd::transport::zero_copy_if::sptr, const boost::uint32_t sid); virtual void handle_uart_packet(uhd::transport::managed_recv_buffer::sptr buff) = 0; - virtual void set_baud_divider(const double baud_div) = 0; }; diff --git a/host/lib/usrp/e100/e100_ctrl.cpp b/host/lib/usrp/e100/e100_ctrl.cpp index cdbbff6dd..c164a6fb0 100644 --- a/host/lib/usrp/e100/e100_ctrl.cpp +++ b/host/lib/usrp/e100/e100_ctrl.cpp @@ -232,10 +232,13 @@ public: //got a character -> process it if (ret == 1){ - const bool flush = ch == '\n' or ch == '\r'; - if (flush and line.empty()) continue; //avoid flushing on empty lines - line += std::string(1, ch); - if (flush) break; + _line += ch; + if (ch == '\n') + { + line = _line; + _line.clear(); + break; + } } //didnt get a character, check the timeout @@ -251,7 +254,9 @@ public: return line; } -private: int _node_fd; +private: + int _node_fd; + std::string _line; }; uhd::uart_iface::sptr e100_ctrl::make_gps_uart_iface(const std::string &node){ diff --git a/host/lib/usrp/gps_ctrl.cpp b/host/lib/usrp/gps_ctrl.cpp index 207ef10ab..908f84a00 100644 --- a/host/lib/usrp/gps_ctrl.cpp +++ b/host/lib/usrp/gps_ctrl.cpp @@ -28,6 +28,7 @@ #include <boost/tokenizer.hpp> #include <boost/format.hpp> #include <boost/regex.hpp> +#include <boost/thread/mutex.hpp> #include "boost/tuple/tuple.hpp" #include "boost/foreach.hpp" @@ -48,24 +49,68 @@ gps_ctrl::~gps_ctrl(void){ class gps_ctrl_impl : public gps_ctrl{ private: - std::map<std::string, boost::tuple<std::string, boost::system_time, bool> > sensors; + std::map<std::string, boost::tuple<std::string, boost::system_time, bool> > sentences; + boost::mutex cache_mutex; + boost::system_time _last_cache_update; - std::string get_cached_sensor(const std::string sensor, const int freshness, const bool once, const bool touch=true) { - boost::system_time time = boost::get_system_time(); - try { - // this is nasty ... - //std::cout << boost::format("Requested %s - seen? ") % sensor << sensors[sensor].get<2>() << " once? " << once << std::endl; - if(time - sensors[sensor].get<1>() < milliseconds(freshness) && (!once or !sensors[sensor].get<2>())) { - sensors[sensor] = boost::make_tuple(sensors[sensor].get<0>(), sensors[sensor].get<1>(), touch); - return sensors[sensor].get<0>(); - } else { - return update_cached_sensors(sensor); - } - } catch(std::exception &e) { - UHD_MSG(warning) << "get_cached_sensor: " << e.what(); + std::string get_sentence(const std::string which, const int max_age_ms, const int timeout, const bool wait_for_next = false) + { + std::string sentence; + boost::system_time now = boost::get_system_time(); + boost::system_time exit_time = now + milliseconds(timeout); + boost::posix_time::time_duration age; + + if (wait_for_next) + { + boost::lock_guard<boost::mutex> lock(cache_mutex); + update_cache(); + //mark sentence as touched + if (sentences.find(which) != sentences.end()) + sentences[which].get<2>() = true; + } + while (1) + { + try + { + boost::lock_guard<boost::mutex> lock(cache_mutex); + + // update cache if older than a millisecond + if (now - _last_cache_update > milliseconds(1)) + { + update_cache(); + } + + if (sentences.find(which) == sentences.end()) + { + age = milliseconds(max_age_ms); + } else { + age = boost::get_system_time() - sentences[which].get<1>(); + } + if (age < milliseconds(max_age_ms) and (not (wait_for_next and sentences[which].get<2>()))) + { + sentence = sentences[which].get<0>(); + sentences[which].get<2>() = true; + } + } catch(std::exception &e) { + UHD_MSG(warning) << "get_sentence: " << e.what(); + } + + if (not sentence.empty() or now > exit_time) + { + break; + } + + sleep(boost::posix_time::milliseconds(1)); + now = boost::get_system_time(); + } + + if (sentence.empty()) + { + throw uhd::value_error("gps ctrl: No " + which + " message found"); + } + + return sentence; } - return std::string(); - } static bool is_nmea_checksum_ok(std::string nmea) { @@ -88,33 +133,39 @@ private: return (string_crc == calculated_crc); } - std::string update_cached_sensors(const std::string sensor) { - if(not gps_detected() || (gps_type != GPS_TYPE_INTERNAL_GPSDO)) { + void update_cache() { + if(not gps_detected() or (_gps_type != GPS_TYPE_INTERNAL_GPSDO)) { UHD_MSG(error) << "get_stat(): unsupported GPS or no GPS detected"; - return std::string(); + return; } - const std::list<std::string> list = boost::assign::list_of("GPGGA")("GPRMC")("SERVO"); - static const boost::regex status_regex("\\d\\d-\\d\\d-\\d\\d"); + const std::list<std::string> keys = boost::assign::list_of("GPGGA")("GPRMC")("SERVO"); + static const boost::regex servo_regex("^\\d\\d-\\d\\d-\\d\\d.*$"); static const boost::regex gp_msg_regex("^\\$GP.*,\\*[0-9A-F]{2}$"); std::map<std::string,std::string> msgs; // Get all GPSDO messages available // Creating a map here because we only want the latest of each message type - for (std::string msg = _recv(); msg.length(); msg = _recv()) + for (std::string msg = _recv(0); not msg.empty(); msg = _recv(0)) { // Strip any end of line characters erase_all(msg, "\r"); erase_all(msg, "\n"); + if (msg.empty()) + { + // Ignore empty strings + continue; + } + if (msg.length() < 6) { - UHD_LOGV(regularly) << __FUNCTION__ << ": Short NMEA string: " << msg << std::endl; + UHD_LOGV(regularly) << __FUNCTION__ << ": Short GPSDO string: " << msg << std::endl; continue; } // Look for SERVO message - if (boost::regex_search(msg, status_regex, boost::regex_constants::match_continuous)) + if (boost::regex_search(msg, servo_regex, boost::regex_constants::match_continuous)) { msgs["SERVO"] = msg; } @@ -124,62 +175,63 @@ private: } else { - UHD_LOGV(regularly) << __FUNCTION__ << ": Malformed NMEA string: " << msg << std::endl; + UHD_LOGV(regularly) << __FUNCTION__ << ": Malformed GPSDO string: " << msg << std::endl; } } boost::system_time time = boost::get_system_time(); - // Update sensors with newly read data - BOOST_FOREACH(std::string key, list) { - if (msgs[key].length()) - sensors[key] = boost::make_tuple(msgs[key], time, !sensor.compare(key)); + // Update sentences with newly read data + BOOST_FOREACH(std::string key, keys) + { + if (not msgs[key].empty()) + { + sentences[key] = boost::make_tuple(msgs[key], time, false); + } } - // Return requested sensor if it was updated - if (msgs[sensor].length()) - return msgs[sensor]; - - return std::string(); + _last_cache_update = time; } public: - gps_ctrl_impl(uart_iface::sptr uart){ - _uart = uart; - + gps_ctrl_impl(uart_iface::sptr uart) : + _uart(uart), + _gps_type(GPS_TYPE_NONE) + { std::string reply; bool i_heard_some_nmea = false, i_heard_something_weird = false; - gps_type = GPS_TYPE_NONE; //first we look for an internal GPSDO _flush(); //get whatever junk is in the rx buffer right now, and throw it away - _send("HAAAY GUYYYYS\n"); //to elicit a response from the GPSDO - - //wait for _send(...) to return - sleep(milliseconds(GPSDO_COMMAND_DELAY_MS)); + _send("*IDN?\r\n"); //request identity from the GPSDO //then we loop until we either timeout, or until we get a response that indicates we're a JL device const boost::system_time comm_timeout = boost::get_system_time() + milliseconds(GPS_COMM_TIMEOUT_MS); while(boost::get_system_time() < comm_timeout) { reply = _recv(); - if(reply.find("Command Error") != std::string::npos) { - gps_type = GPS_TYPE_INTERNAL_GPSDO; + //known devices are JL "FireFly" and "LC_XO" + if(reply.find("FireFly") != std::string::npos or reply.find("LC_XO") != std::string::npos) { + _gps_type = GPS_TYPE_INTERNAL_GPSDO; break; + } else if(reply.substr(0, 3) == "$GP") { + i_heard_some_nmea = true; //but keep looking + } else if(not reply.empty()) { + i_heard_something_weird = true; //probably wrong baud rate } - else if(reply.substr(0, 3) == "$GP") i_heard_some_nmea = true; //but keep looking for that "Command Error" response - else if(reply.length() != 0) i_heard_something_weird = true; //probably wrong baud rate } - if((i_heard_some_nmea) && (gps_type != GPS_TYPE_INTERNAL_GPSDO)) gps_type = GPS_TYPE_GENERIC_NMEA; + if((i_heard_some_nmea) && (_gps_type != GPS_TYPE_INTERNAL_GPSDO)) _gps_type = GPS_TYPE_GENERIC_NMEA; - if((gps_type == GPS_TYPE_NONE) && i_heard_something_weird) { + if((_gps_type == GPS_TYPE_NONE) && i_heard_something_weird) { UHD_MSG(error) << "GPS invalid reply \"" << reply << "\", assuming none available" << std::endl; } - switch(gps_type) { + switch(_gps_type) { case GPS_TYPE_INTERNAL_GPSDO: - UHD_MSG(status) << "Found an internal GPSDO" << std::endl; + erase_all(reply, "\r"); + erase_all(reply, "\n"); + UHD_MSG(status) << "Found an internal GPSDO: " << reply << std::endl; init_gpsdo(); break; @@ -193,6 +245,9 @@ public: break; } + + // initialize cache + update_cache(); } ~gps_ctrl_impl(void){ @@ -215,7 +270,7 @@ public: or key == "gps_gprmc") { return sensor_value_t( boost::to_upper_copy(key), - get_cached_sensor(boost::to_upper_copy(key.substr(4,8)), GPS_NMEA_NORMAL_FRESHNESS, false, false), + get_sentence(boost::to_upper_copy(key.substr(4,8)), GPS_NMEA_NORMAL_FRESHNESS, GPS_TIMEOUT_DELAY_MS), ""); } else if(key == "gps_time") { @@ -225,7 +280,10 @@ public: return sensor_value_t("GPS lock status", locked(), "locked", "unlocked"); } else if(key == "gps_servo") { - return sensor_value_t("GPS servo status", get_servo(), ""); + return sensor_value_t( + boost::to_upper_copy(key), + get_sentence(boost::to_upper_copy(key.substr(4,8)), GPS_SERVO_FRESHNESS, GPS_TIMEOUT_DELAY_MS), + ""); } else { throw uhd::value_error("gps ctrl get_sensor unknown key: " + key); @@ -238,40 +296,20 @@ private: //none of these should issue replies so we don't bother looking for them //we have to sleep between commands because the JL device, despite not acking, takes considerable time to process each command. sleep(milliseconds(GPSDO_COMMAND_DELAY_MS)); - _send("SYST:COMM:SER:ECHO OFF\n"); + _send("SYST:COMM:SER:ECHO OFF\r\n"); sleep(milliseconds(GPSDO_COMMAND_DELAY_MS)); - _send("SYST:COMM:SER:PRO OFF\n"); + _send("SYST:COMM:SER:PRO OFF\r\n"); sleep(milliseconds(GPSDO_COMMAND_DELAY_MS)); - _send("GPS:GPGGA 1\n"); + _send("GPS:GPGGA 1\r\n"); sleep(milliseconds(GPSDO_COMMAND_DELAY_MS)); - _send("GPS:GGAST 0\n"); + _send("GPS:GGAST 0\r\n"); sleep(milliseconds(GPSDO_COMMAND_DELAY_MS)); - _send("GPS:GPRMC 1\n"); + _send("GPS:GPRMC 1\r\n"); sleep(milliseconds(GPSDO_COMMAND_DELAY_MS)); - _send("SERV:TRAC 0\n"); + _send("SERV:TRAC 1\r\n"); sleep(milliseconds(GPSDO_COMMAND_DELAY_MS)); } - //retrieve a raw NMEA sentence - std::string get_nmea(std::string msgtype) { - std::string reply; - - const boost::system_time comm_timeout = boost::get_system_time() + milliseconds(GPS_COMM_TIMEOUT_MS); - while(boost::get_system_time() < comm_timeout) { - if(!msgtype.compare("GPRMC")) { - reply = get_cached_sensor(msgtype, GPS_NMEA_FRESHNESS, true); - } - else { - reply = get_cached_sensor(msgtype, GPS_NMEA_LOW_FRESHNESS, false); - } - if(reply.size()) { - if(reply.substr(1, 5) == msgtype) return reply; - } - boost::this_thread::sleep(milliseconds(GPS_TIMEOUT_DELAY_MS)); - } - throw uhd::value_error(str(boost::format("get_nmea(): no %s message found") % msgtype)); - } - //helper function to retrieve a field from an NMEA sentence std::string get_token(std::string sentence, size_t offset) { boost::tokenizer<boost::escaped_list_separator<char> > tok(sentence); @@ -286,12 +324,12 @@ private: } ptime get_time(void) { - _flush(); int error_cnt = 0; ptime gps_time; while(error_cnt < 2) { try { - std::string reply = get_nmea("GPRMC"); + // wait for next GPRMC string + std::string reply = get_sentence("GPRMC", GPS_NMEA_NORMAL_FRESHNESS, GPS_COMM_TIMEOUT_MS, true); std::string datestr = get_token(reply, 9); std::string timestr = get_token(reply, 1); @@ -314,7 +352,6 @@ private: } catch(std::exception &e) { UHD_MSG(warning) << "get_time: " << e.what(); - _flush(); error_cnt++; } } @@ -328,46 +365,24 @@ private: } bool gps_detected(void) { - return (gps_type != GPS_TYPE_NONE); + return (_gps_type != GPS_TYPE_NONE); } bool locked(void) { int error_cnt = 0; while(error_cnt < 3) { try { - std::string reply = get_cached_sensor("GPGGA", GPS_LOCK_FRESHNESS, false, false); - if(reply.size() <= 1) return false; - - return (get_token(reply, 6) != "0"); + std::string reply = get_sentence("GPGGA", GPS_LOCK_FRESHNESS, GPS_COMM_TIMEOUT_MS); + if(reply.empty()) + error_cnt++; + else + return (get_token(reply, 6) != "0"); } catch(std::exception &e) { UHD_MSG(warning) << "locked: " << e.what(); error_cnt++; } } - throw uhd::value_error("Timeout after no valid message found"); - return false; - } - - std::string get_servo(void) { - - //enable servo reporting - _send("SERV:TRAC 1\n"); - sleep(milliseconds(GPSDO_COMMAND_DELAY_MS)); - - std::string reply; - - const boost::system_time comm_timeout = boost::get_system_time() + milliseconds(GPS_COMM_TIMEOUT_MS); - while(boost::get_system_time() < comm_timeout) { - reply = get_cached_sensor("SERVO", GPS_NMEA_LOW_FRESHNESS, false); - if(reply.size()) - { - //disable it before leaving function - _send("SERV:TRAC 0\n"); - return reply; - } - boost::this_thread::sleep(milliseconds(GPS_TIMEOUT_DELAY_MS)); - } - throw uhd::value_error("get_stat(): no servo message found"); + throw uhd::value_error("locked(): unable to determine GPS lock status"); } uart_iface::sptr _uart; @@ -390,13 +405,11 @@ private: GPS_TYPE_INTERNAL_GPSDO, GPS_TYPE_GENERIC_NMEA, GPS_TYPE_NONE - } gps_type; + } _gps_type; static const int GPS_COMM_TIMEOUT_MS = 1300; - static const int GPS_NMEA_FRESHNESS = 10; - static const int GPS_NMEA_LOW_FRESHNESS = 2500; static const int GPS_NMEA_NORMAL_FRESHNESS = 1000; - static const int GPS_SERVO_FRESHNESS = 2500; + static const int GPS_SERVO_FRESHNESS = 1000; static const int GPS_LOCK_FRESHNESS = 2500; static const int GPS_TIMEOUT_DELAY_MS = 200; static const int GPSDO_COMMAND_DELAY_MS = 200; diff --git a/host/lib/usrp/x300/x300_fw_uart.cpp b/host/lib/usrp/x300/x300_fw_uart.cpp index b0fae124d..7f60e26fc 100644 --- a/host/lib/usrp/x300/x300_fw_uart.cpp +++ b/host/lib/usrp/x300/x300_fw_uart.cpp @@ -30,28 +30,36 @@ using namespace uhd; struct x300_uart_iface : uart_iface { x300_uart_iface(wb_iface::sptr iface): - rxoffset(0), txoffset(0), txword32(0), rxpool(0), txpool(0), poolsize(0) + _iface(iface), + rxoffset(0), + txword32(0), + _last_device_rxoffset(0) { - _iface = iface; - rxoffset = _iface->peek32(SR_ADDR(X300_FW_SHMEM_BASE, X300_FW_SHMEM_UART_RX_INDEX)); txoffset = _iface->peek32(SR_ADDR(X300_FW_SHMEM_BASE, X300_FW_SHMEM_UART_TX_INDEX)); rxpool = _iface->peek32(SR_ADDR(X300_FW_SHMEM_BASE, X300_FW_SHMEM_UART_RX_ADDR)); txpool = _iface->peek32(SR_ADDR(X300_FW_SHMEM_BASE, X300_FW_SHMEM_UART_TX_ADDR)); poolsize = _iface->peek32(SR_ADDR(X300_FW_SHMEM_BASE, X300_FW_SHMEM_UART_WORDS32)); _rxcache.resize(poolsize); - _last_device_rxoffset = rxoffset; //this->write_uart("HELLO UART\n"); //this->read_uart(0.1); } void putchar(const char ch) { - txoffset = (txoffset + 1) % (poolsize*4); const int shift = ((txoffset%4) * 8); if (shift == 0) txword32 = 0; txword32 |= boost::uint32_t(ch) << shift; - _iface->poke32(SR_ADDR(txpool, txoffset/4), txword32); - _iface->poke32(SR_ADDR(X300_FW_SHMEM_BASE, X300_FW_SHMEM_UART_TX_INDEX), txoffset); + // Write out full 32 bit words or whatever we have if end of string + if (txoffset % 4 == 3 or ch == '\n') + { + _iface->poke32(SR_ADDR(txpool, txoffset/4), txword32); + } + txoffset = (txoffset + 1) % (poolsize*4); + if (ch == '\n') + { + // Tell the X300 to write the string + _iface->poke32(SR_ADDR(X300_FW_SHMEM_BASE, X300_FW_SHMEM_UART_TX_INDEX), txoffset); + } } void write_uart(const std::string &buff) @@ -59,7 +67,6 @@ struct x300_uart_iface : uart_iface boost::mutex::scoped_lock(_write_mutex); BOOST_FOREACH(const char ch, buff) { - if (ch == '\n') this->putchar('\r'); this->putchar(ch); } } @@ -128,18 +135,13 @@ struct x300_uart_iface : uart_iface // Get available characters for (int ch = this->getchar(); ch != -1; ch = this->getchar()) { - // skip carriage returns - if (ch == '\r') - continue; - // store character to buffer - _rxbuff += std::string(1, (char)ch); + _rxbuff.append(1, ch); // newline found - return string if (ch == '\n') { - buff = _rxbuff; - _rxbuff.clear(); + buff.swap(_rxbuff); return buff; } } diff --git a/host/lib/usrp_clock/octoclock/octoclock_uart.cpp b/host/lib/usrp_clock/octoclock/octoclock_uart.cpp index 221a7e471..0b5b840d3 100644 --- a/host/lib/usrp_clock/octoclock/octoclock_uart.cpp +++ b/host/lib/usrp_clock/octoclock/octoclock_uart.cpp @@ -68,13 +68,12 @@ namespace uhd{ } void octoclock_uart_iface::write_uart(const std::string &buf){ - std::string to_send = boost::algorithm::replace_all_copy(buf, "\n", "\r\n"); size_t len = 0; octoclock_packet_t pkt_out; pkt_out.sequence = uhd::htonx<boost::uint32_t>(++_sequence); - pkt_out.len = to_send.size(); - memcpy(pkt_out.data, to_send.c_str(), to_send.size()); + pkt_out.len = buf.size(); + memcpy(pkt_out.data, buf.c_str(), buf.size()); boost::uint8_t octoclock_data[udp_simple::mtu]; const octoclock_packet_t *pkt_in = reinterpret_cast<octoclock_packet_t*>(octoclock_data); @@ -87,28 +86,26 @@ namespace uhd{ std::string octoclock_uart_iface::read_uart(double timeout){ std::string result; - bool first_time = true; boost::system_time exit_time = boost::get_system_time() + boost::posix_time::milliseconds(long(timeout*1e3)); - while(boost::get_system_time() < exit_time){ - if (first_time) - first_time = false; - else - boost::this_thread::sleep(boost::posix_time::milliseconds(1)); - + while(true) + { _update_cache(); for(char ch = _getchar(); ch != 0; ch = _getchar()){ - if(ch == '\r') continue; //Skip carriage returns _rxbuff += ch; //If newline found, return string if(ch == '\n'){ - result = _rxbuff; - _rxbuff.clear(); + result.swap(_rxbuff); return result; } } + if (boost::get_system_time() > exit_time) + { + break; + } + boost::this_thread::sleep(boost::posix_time::milliseconds(1)); } return result; |