aboutsummaryrefslogtreecommitdiffstats
path: root/host/lib/usrp_clock/octoclock
diff options
context:
space:
mode:
Diffstat (limited to 'host/lib/usrp_clock/octoclock')
-rw-r--r--host/lib/usrp_clock/octoclock/CMakeLists.txt33
-rw-r--r--host/lib/usrp_clock/octoclock/common.h149
-rw-r--r--host/lib/usrp_clock/octoclock/octoclock_eeprom.cpp184
-rw-r--r--host/lib/usrp_clock/octoclock/octoclock_impl.cpp436
-rw-r--r--host/lib/usrp_clock/octoclock/octoclock_impl.hpp80
-rw-r--r--host/lib/usrp_clock/octoclock/octoclock_uart.cpp162
-rw-r--r--host/lib/usrp_clock/octoclock/octoclock_uart.hpp57
7 files changed, 1101 insertions, 0 deletions
diff --git a/host/lib/usrp_clock/octoclock/CMakeLists.txt b/host/lib/usrp_clock/octoclock/CMakeLists.txt
new file mode 100644
index 000000000..e363bb9da
--- /dev/null
+++ b/host/lib/usrp_clock/octoclock/CMakeLists.txt
@@ -0,0 +1,33 @@
+#
+# Copyright 2011-2014 Ettus Research LLC
+#
+# 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/>.
+#
+
+########################################################################
+# This file included, use CMake directory variables
+########################################################################
+
+########################################################################
+# Conditionally configure the OctoClock support
+########################################################################
+LIBUHD_REGISTER_COMPONENT("OctoClock" ENABLE_OCTOCLOCK ON "ENABLE_LIBUHD" OFF)
+
+IF(ENABLE_OCTOCLOCK)
+ LIBUHD_APPEND_SOURCES(
+ ${CMAKE_CURRENT_SOURCE_DIR}/octoclock_eeprom.cpp
+ ${CMAKE_CURRENT_SOURCE_DIR}/octoclock_impl.cpp
+ ${CMAKE_CURRENT_SOURCE_DIR}/octoclock_uart.cpp
+ )
+ENDIF(ENABLE_OCTOCLOCK)
diff --git a/host/lib/usrp_clock/octoclock/common.h b/host/lib/usrp_clock/octoclock/common.h
new file mode 100644
index 000000000..96acbb30f
--- /dev/null
+++ b/host/lib/usrp_clock/octoclock/common.h
@@ -0,0 +1,149 @@
+/*
+ * Copyright 2014 Ettus Research LLC
+ *
+ * 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/>.
+ */
+
+#ifndef _OCTOCLOCK_COMMON_H_
+#define _OCTOCLOCK_COMMON_H_
+
+#include <stdint.h>
+
+/*
+ * C++ macros used for code cleanliness and extern "C" declaration.
+ */
+#ifdef __cplusplus
+
+#define UHD_OCTOCLOCK_SEND_AND_RECV(xport, pkt_code, pkt_out, len, data) pkt_out.proto_ver = OCTOCLOCK_FW_COMPAT_NUM; \
+ pkt_out.code = pkt_code; \
+ xport->send(boost::asio::buffer(&pkt_out, sizeof(octoclock_packet_t))); \
+ len = xport->recv(boost::asio::buffer(data), 2);
+
+#define UHD_OCTOCLOCK_PACKET_MATCHES(pkt_code, pkt_out, pkt_in, len) (len > offsetof(octoclock_packet_t, data) and \
+ pkt_in->sequence == pkt_out.sequence and \
+ pkt_in->code == pkt_code)
+
+extern "C" {
+#endif
+
+/*
+ * This code is used by both the C firmware and C++ host driver, so
+ * only valid C code should go in this section.
+ */
+
+//These values are placed in the octoclock_packet_t.proto_ver field
+#define OCTOCLOCK_BOOTLOADER_PROTO_VER 1234
+#define OCTOCLOCK_FW_COMPAT_NUM 2
+
+//UDP ports assigned for different tasks
+#define OCTOCLOCK_UDP_CTRL_PORT 50000
+#define OCTOCLOCK_UDP_GPSDO_PORT 50001
+#define OCTOCLOCK_UDP_FW_PORT 50002
+#define OCTOCLOCK_UDP_EEPROM_PORT 50003
+
+typedef enum {
+ NO_CODE,
+
+ OCTOCLOCK_QUERY_CMD,
+ OCTOCLOCK_QUERY_ACK,
+
+ SEND_EEPROM_CMD,
+ SEND_EEPROM_ACK,
+ BURN_EEPROM_CMD,
+ BURN_EEPROM_SUCCESS_ACK,
+ BURN_EEPROM_FAILURE_ACK,
+ CLEAR_EEPROM_CMD,
+ CLEAR_EEPROM_ACK,
+
+ SEND_STATE_CMD,
+ SEND_STATE_ACK,
+
+ RESET_CMD,
+ RESET_ACK,
+
+ HOST_SEND_TO_GPSDO_CMD,
+ HOST_SEND_TO_GPSDO_ACK,
+ SEND_POOLSIZE_CMD,
+ SEND_POOLSIZE_ACK,
+ SEND_CACHE_STATE_CMD,
+ SEND_CACHE_STATE_ACK,
+ SEND_GPSDO_CACHE_CMD,
+ SEND_GPSDO_CACHE_ACK,
+
+ PREPARE_FW_BURN_CMD,
+ FW_BURN_READY_ACK,
+ FILE_TRANSFER_CMD,
+ FILE_TRANSFER_ACK,
+ READ_FW_CMD,
+ READ_FW_ACK,
+ FINALIZE_BURNING_CMD,
+ FINALIZE_BURNING_ACK,
+} packet_code_t;
+
+typedef enum {
+ NO_REF,
+ INTERNAL,
+ EXTERNAL
+} ref_t;
+
+typedef enum {
+ UP,
+ DOWN
+} switch_pos_t;
+
+#pragma pack(push,1)
+
+// Structure of values in EEPROM, starting in location 0
+typedef struct {
+ uint8_t mac_addr[6];
+ uint32_t ip_addr;
+ uint32_t dr_addr;
+ uint32_t netmask;
+ uint8_t serial[10];
+ uint8_t name[10];
+ uint8_t revision;
+} octoclock_fw_eeprom_t;
+
+typedef struct {
+ uint8_t external_detected;
+ uint8_t gps_detected;
+ uint8_t which_ref;
+ uint8_t switch_pos;
+} octoclock_state_t;
+
+typedef struct {
+ uint8_t num_wraps;
+ uint8_t pos;
+} gpsdo_cache_state_t;
+
+typedef struct {
+ uint32_t proto_ver;
+ uint32_t sequence;
+ uint8_t code;
+ union {
+ uint16_t len;
+ gpsdo_cache_state_t state;
+ uint16_t poolsize;
+ uint16_t addr;
+ };
+ uint8_t data[256];
+} octoclock_packet_t;
+
+#pragma pack(pop)
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _OCTOCLOCK_COMMON_H_ */
diff --git a/host/lib/usrp_clock/octoclock/octoclock_eeprom.cpp b/host/lib/usrp_clock/octoclock/octoclock_eeprom.cpp
new file mode 100644
index 000000000..6d54cac70
--- /dev/null
+++ b/host/lib/usrp_clock/octoclock/octoclock_eeprom.cpp
@@ -0,0 +1,184 @@
+//
+// Copyright 2014 Ettus Research LLC
+//
+// 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/exception.hpp>
+#include <uhd/usrp_clock/octoclock_eeprom.hpp>
+#include <uhd/transport/udp_simple.hpp>
+#include <uhd/usrp/mboard_eeprom.hpp>
+#include <uhd/types/mac_addr.hpp>
+#include <uhd/utils/byteswap.hpp>
+#include <boost/assign/list_of.hpp>
+#include <boost/asio.hpp>
+#include <boost/lexical_cast.hpp>
+#include <boost/foreach.hpp>
+
+#include <iostream>
+
+#include "common.h"
+
+typedef boost::asio::ip::address_v4 ip_v4;
+
+using namespace uhd;
+using namespace uhd::usrp_clock;
+using namespace uhd::transport;
+
+/***********************************************************************
+ * Utility functions
+ **********************************************************************/
+
+//! A wrapper around std::copy that takes ranges instead of iterators.
+template<typename RangeSrc, typename RangeDst> inline
+void byte_copy(const RangeSrc &src, RangeDst &dst){
+ std::copy(boost::begin(src), boost::end(src), boost::begin(dst));
+}
+
+//! create a string from a byte vector, return empty if invalid ascii
+static const std::string bytes_to_string(const byte_vector_t &bytes){
+ std::string out;
+ BOOST_FOREACH(boost::uint8_t byte, bytes){
+ if (byte < 32 or byte > 127) return out;
+ out += byte;
+ }
+ return out;
+}
+
+/***********************************************************************
+ * Implementation
+ **********************************************************************/
+void octoclock_eeprom_t::_load(){
+ boost::uint32_t octoclock_data[udp_simple::mtu];
+ const octoclock_packet_t *pkt_in = reinterpret_cast<const octoclock_packet_t*>(octoclock_data);
+ const octoclock_fw_eeprom_t *eeprom_in = reinterpret_cast<const octoclock_fw_eeprom_t*>(pkt_in->data);
+
+ octoclock_packet_t pkt_out;
+ pkt_out.sequence = uhd::htonx<boost::uint32_t>(std::rand());
+ size_t len = 0;
+
+ UHD_OCTOCLOCK_SEND_AND_RECV(xport, SEND_EEPROM_CMD, pkt_out, len, octoclock_data);
+ if(UHD_OCTOCLOCK_PACKET_MATCHES(SEND_EEPROM_ACK, pkt_out, pkt_in, len)){
+ //MAC address
+ byte_vector_t mac_bytes(eeprom_in->mac_addr, eeprom_in->mac_addr+6);
+ (*this)["mac-addr"] = mac_addr_t::from_bytes(mac_bytes).to_string();
+
+ //IP address
+ boost::uint32_t ip_addr = uhd::htonx<boost::uint32_t>(eeprom_in->ip_addr);
+ ip_v4::bytes_type ip_addr_bytes;
+ memcpy(&ip_addr_bytes, &ip_addr, 4);
+ (*this)["ip-addr"] = ip_v4(ip_addr_bytes).to_string();
+
+ //Default router
+ boost::uint32_t dr_addr = uhd::htonx<boost::uint32_t>(eeprom_in->dr_addr);
+ ip_v4::bytes_type dr_addr_bytes;
+ memcpy(&dr_addr_bytes, &dr_addr, 4);
+ (*this)["gateway"] = ip_v4(dr_addr_bytes).to_string();
+
+ //Netmask
+ boost::uint32_t netmask = uhd::htonx<boost::uint32_t>(eeprom_in->netmask);
+ ip_v4::bytes_type netmask_bytes;
+ memcpy(&netmask_bytes, &netmask, 4);
+ (*this)["netmask"] = ip_v4(netmask_bytes).to_string();
+
+ //Serial
+ std::string raw_serial((char*)eeprom_in->serial, 10);
+ byte_vector_t serial_bytes(raw_serial.begin(), raw_serial.end());
+ (*this)["serial"] = bytes_to_string(serial_bytes);
+
+ //Name
+ std::string raw_name((char*)eeprom_in->name, 10);
+ byte_vector_t name_bytes(raw_name.begin(), raw_name.end());
+ (*this)["name"] = bytes_to_string(name_bytes);
+
+ //Revision
+ (*this)["revision"] = boost::lexical_cast<std::string>(int(eeprom_in->revision));
+ }
+ else throw uhd::runtime_error("Error loading OctoClock EEPROM.");
+}
+
+void octoclock_eeprom_t::_store() const {
+ boost::uint32_t octoclock_data[udp_simple::mtu];
+ const octoclock_packet_t *pkt_in = reinterpret_cast<const octoclock_packet_t *>(octoclock_data);
+
+ octoclock_packet_t pkt_out;
+ pkt_out.sequence = uhd::htonx<boost::uint32_t>(std::rand());
+ pkt_out.len = sizeof(octoclock_fw_eeprom_t);
+ size_t len = 0;
+
+ octoclock_fw_eeprom_t *eeprom_out = reinterpret_cast<octoclock_fw_eeprom_t *>(&pkt_out.data);
+ memset(eeprom_out, 0xFF, sizeof(octoclock_fw_eeprom_t));
+
+ //MAC address
+ if((*this).has_key("mac-addr")){
+ byte_copy(mac_addr_t::from_string((*this)["mac-addr"]).to_bytes(), eeprom_out->mac_addr);
+ }
+
+ //IP address
+ if((*this).has_key("ip-addr")){
+ ip_v4::bytes_type ip_addr_bytes = ip_v4::from_string((*this)["ip-addr"]).to_bytes();
+ memcpy(&eeprom_out->ip_addr, &ip_addr_bytes, 4);
+ eeprom_out->ip_addr = uhd::htonx<boost::uint32_t>(eeprom_out->ip_addr);
+ }
+
+ //Default router
+ if((*this).has_key("gateway")){
+ ip_v4::bytes_type dr_addr_bytes = ip_v4::from_string((*this)["gateway"]).to_bytes();
+ memcpy(&eeprom_out->dr_addr, &dr_addr_bytes, 4);
+ eeprom_out->dr_addr = uhd::htonx<boost::uint32_t>(eeprom_out->dr_addr);
+ }
+
+ //Netmask
+ if((*this).has_key("netmask")){
+ ip_v4::bytes_type netmask_bytes = ip_v4::from_string((*this)["netmask"]).to_bytes();
+ memcpy(&eeprom_out->netmask, &netmask_bytes, 4);
+ eeprom_out->netmask = uhd::htonx<boost::uint32_t>(eeprom_out->netmask);
+ }
+
+ //Serial
+ if((*this).has_key("serial")){
+ byte_copy(byte_vector_t((*this)["serial"].begin(), (*this)["serial"].end()), eeprom_out->serial);
+ }
+
+ //Name
+ if((*this).has_key("name")){
+ byte_copy(byte_vector_t((*this)["name"].begin(), (*this)["name"].end()), eeprom_out->name);
+ }
+
+ //Revision
+ if((*this).has_key("revision")){
+ eeprom_out->revision = (*this)["revision"][0]-'0';
+ }
+
+ UHD_OCTOCLOCK_SEND_AND_RECV(xport, BURN_EEPROM_CMD, pkt_out, len, octoclock_data);
+ if(not UHD_OCTOCLOCK_PACKET_MATCHES(BURN_EEPROM_SUCCESS_ACK, pkt_out, pkt_in, len))
+ throw uhd::runtime_error("Error writing to OctoClock EEPROM.");
+}
+
+/***********************************************************************
+ * Implementation of OctoClock EEPROM
+ **********************************************************************/
+octoclock_eeprom_t::octoclock_eeprom_t(void){
+ /* NOP */
+}
+
+octoclock_eeprom_t::octoclock_eeprom_t(udp_simple::sptr transport){
+ xport = transport;
+ _load();
+}
+
+void octoclock_eeprom_t::commit() const{
+ if(!xport) throw uhd::runtime_error("There is no set device communication.");
+ _store();
+}
diff --git a/host/lib/usrp_clock/octoclock/octoclock_impl.cpp b/host/lib/usrp_clock/octoclock/octoclock_impl.cpp
new file mode 100644
index 000000000..add5d7931
--- /dev/null
+++ b/host/lib/usrp_clock/octoclock/octoclock_impl.cpp
@@ -0,0 +1,436 @@
+//
+// Copyright 2014 Ettus Research LLC
+//
+// 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 <iostream>
+
+#include <boost/asio.hpp>
+#include <boost/assign.hpp>
+#include <boost/cstdint.hpp>
+#include <boost/filesystem.hpp>
+#include <boost/foreach.hpp>
+#include <boost/format.hpp>
+#include <boost/thread.hpp>
+
+#include <uhd/device.hpp>
+#include <uhd/exception.hpp>
+#include <uhd/transport/udp_simple.hpp>
+#include <uhd/transport/if_addrs.hpp>
+#include <uhd/types/dict.hpp>
+#include <uhd/usrp/gps_ctrl.hpp>
+#include <uhd/usrp_clock/octoclock_eeprom.hpp>
+#include <uhd/utils/byteswap.hpp>
+#include <uhd/utils/images.hpp>
+#include <uhd/utils/msg.hpp>
+#include <uhd/utils/paths.hpp>
+#include <uhd/utils/static.hpp>
+
+#include "octoclock_impl.hpp"
+#include "octoclock_uart.hpp"
+#include "common.h"
+
+using namespace uhd;
+using namespace uhd::usrp_clock;
+using namespace uhd::transport;
+namespace asio = boost::asio;
+namespace fs = boost::filesystem;
+
+/***********************************************************************
+ * Discovery
+ **********************************************************************/
+device_addrs_t octoclock_find(const device_addr_t &hint){
+ //Handle the multi-device discovery
+ device_addrs_t hints = separate_device_addr(hint);
+ if (hints.size() > 1){
+ device_addrs_t found_devices;
+ std::string error_msg;
+ BOOST_FOREACH(const device_addr_t &hint_i, hints){
+ device_addrs_t found_devices_i = octoclock_find(hint_i);
+ if (found_devices_i.size() != 1) error_msg += str(boost::format(
+ "Could not resolve device hint \"%s\" to a single device."
+ ) % hint_i.to_string());
+ else found_devices.push_back(found_devices_i[0]);
+ }
+ if (found_devices.empty()) return device_addrs_t();
+ if (not error_msg.empty()) throw uhd::value_error(error_msg);
+ return device_addrs_t(1, combine_device_addrs(found_devices));
+ }
+
+ //Initialize the hint for a single device case
+ UHD_ASSERT_THROW(hints.size() <= 1);
+ hints.resize(1); //In case it was empty
+ device_addr_t _hint = hints[0];
+ device_addrs_t octoclock_addrs;
+
+ //return an empty list of addresses when type is set to non-usrp2
+ if (hint.has_key("type") and hint["type"].find("octoclock") == std::string::npos) return octoclock_addrs;
+
+ //Return an empty list of addresses when a resource is specified,
+ //since a resource is intended for a different, non-USB, device.
+ if (hint.has_key("resource")) return octoclock_addrs;
+
+ //If no address was specified, send a broadcast on each interface
+ if (not _hint.has_key("addr")){
+ BOOST_FOREACH(const if_addrs_t &if_addrs, get_if_addrs()){
+ //avoid the loopback device
+ if (if_addrs.inet == asio::ip::address_v4::loopback().to_string()) continue;
+
+ //create a new hint with this broadcast address
+ device_addr_t new_hint = hint;
+ new_hint["addr"] = if_addrs.bcast;
+
+ //call discover with the new hint and append results
+ device_addrs_t new_octoclock_addrs = octoclock_find(new_hint);
+ octoclock_addrs.insert(octoclock_addrs.begin(),
+ new_octoclock_addrs.begin(), new_octoclock_addrs.end()
+ );
+ }
+ return octoclock_addrs;
+ }
+
+ //Create a UDP transport to communicate
+ udp_simple::sptr udp_transport = udp_simple::make_broadcast(
+ _hint["addr"],
+ BOOST_STRINGIZE(OCTOCLOCK_UDP_CTRL_PORT)
+ );
+
+ //Send a query packet
+ octoclock_packet_t pkt_out;
+ pkt_out.proto_ver = OCTOCLOCK_FW_COMPAT_NUM;
+ pkt_out.sequence = uhd::htonx<boost::uint32_t>(std::rand());
+ pkt_out.len = 0;
+ pkt_out.code = OCTOCLOCK_QUERY_CMD;
+ try{
+ udp_transport->send(boost::asio::buffer(&pkt_out, sizeof(pkt_out)));
+ }
+ catch(const std::exception &ex){
+ UHD_MSG(error) << "OctoClock network discovery error - " << ex.what() << std::endl;
+ }
+ catch(...){
+ UHD_MSG(error) << "OctoClock network discovery unknown error" << std::endl;
+ }
+
+ boost::uint8_t octoclock_data[udp_simple::mtu];
+ const octoclock_packet_t *pkt_in = reinterpret_cast<octoclock_packet_t*>(octoclock_data);
+
+ while(true){
+ size_t len = udp_transport->recv(asio::buffer(octoclock_data), 2);
+ if(UHD_OCTOCLOCK_PACKET_MATCHES(OCTOCLOCK_QUERY_ACK, pkt_out, pkt_in, len)){
+ device_addr_t new_addr;
+ new_addr["type"] = (pkt_in->proto_ver == OCTOCLOCK_FW_COMPAT_NUM) ? "octoclock"
+ : "octoclock-bootloader";
+ new_addr["addr"] = udp_transport->get_recv_addr();
+
+ //Attempt direct communication with OctoClock
+ udp_simple::sptr ctrl_xport = udp_simple::make_connected(
+ new_addr["addr"],
+ BOOST_STRINGIZE(OCTOCLOCK_UDP_CTRL_PORT)
+ );
+ UHD_OCTOCLOCK_SEND_AND_RECV(ctrl_xport, OCTOCLOCK_QUERY_CMD, pkt_out, len, octoclock_data);
+ if(UHD_OCTOCLOCK_PACKET_MATCHES(OCTOCLOCK_QUERY_ACK, pkt_out, pkt_in, len)){
+ //If the OctoClock is in its bootloader, don't ask for details
+ if(pkt_in->proto_ver == OCTOCLOCK_BOOTLOADER_PROTO_VER){
+ octoclock_addrs.push_back(new_addr);
+ }
+ else{
+ octoclock_eeprom_t oc_eeprom(ctrl_xport);
+ new_addr["name"] = oc_eeprom["name"];
+ new_addr["serial"] = oc_eeprom["serial"];
+
+ //Filter based on optional keys (if any)
+ if(
+ (not _hint.has_key("name") or (_hint["name"] == new_addr["name"])) and
+ (not _hint.has_key("serial") or (_hint["serial"] == new_addr["serial"]))
+ ){
+ octoclock_addrs.push_back(new_addr);
+ }
+ }
+ }
+ else continue;
+ }
+
+ if(len == 0) break;
+ }
+
+ return octoclock_addrs;
+}
+
+device::sptr octoclock_make(const device_addr_t &device_addr){
+ return device::sptr(new octoclock_impl(device_addr));
+}
+
+UHD_STATIC_BLOCK(register_octoclock_device){
+ device::register_device(&octoclock_find, &octoclock_make, device::CLOCK);
+}
+
+/***********************************************************************
+ * Structors
+ **********************************************************************/
+octoclock_impl::octoclock_impl(const device_addr_t &_device_addr){
+ UHD_MSG(status) << "Opening an OctoClock device..." << std::endl;
+ _type = device::CLOCK;
+ device_addrs_t device_args = separate_device_addr(_device_addr);
+ _sequence = std::rand();
+
+ ////////////////////////////////////////////////////////////////////
+ // Initialize the property tree
+ ////////////////////////////////////////////////////////////////////
+ _tree = property_tree::make();
+ _tree->create<std::string>("/name").set("OctoClock Device");
+
+ for(size_t oci = 0; oci < device_args.size(); oci++){
+ const device_addr_t device_args_i = device_args[oci];
+ const std::string addr = device_args_i["addr"];
+ //Can't make a device out of an OctoClock in bootloader state
+ if(device_args_i["type"] == "octoclock-bootloader"){
+ throw uhd::runtime_error(str(boost::format(
+ "\n\nThis device is in its bootloader state and cannot be used by UHD.\n"
+ "This may mean the firmware on the device has been corrupted and will\n"
+ "need to be burned again.\n\n"
+ "%s\n"
+ ) % _get_images_help_message(addr)));
+ }
+
+ const std::string oc = boost::lexical_cast<std::string>(oci);
+
+ ////////////////////////////////////////////////////////////////////
+ // Set up UDP transports
+ ////////////////////////////////////////////////////////////////////
+ _oc_dict[oc].ctrl_xport = udp_simple::make_connected(addr, BOOST_STRINGIZE(OCTOCLOCK_UDP_CTRL_PORT));
+ _oc_dict[oc].gpsdo_xport = udp_simple::make_connected(addr, BOOST_STRINGIZE(OCTOCLOCK_UDP_GPSDO_PORT));
+
+ const fs_path oc_path = "/mboards/" + oc;
+ _tree->create<std::string>(oc_path / "name").set("OctoClock");
+
+ ////////////////////////////////////////////////////////////////////
+ // Check the firmware compatibility number
+ ////////////////////////////////////////////////////////////////////
+ boost::uint32_t fw_version = _get_fw_version(oc);
+ if(fw_version != OCTOCLOCK_FW_COMPAT_NUM){
+ throw uhd::runtime_error(str(boost::format(
+ "\n\nPlease update your OctoClock's firmware.\n"
+ "Expected firmware compatibility number %d, but got %d:\n"
+ "The firmware build is not compatible with the host code build.\n\n"
+ "%s\n"
+ ) % int(OCTOCLOCK_FW_COMPAT_NUM) % int(fw_version) % _get_images_help_message(addr)));
+ }
+ _tree->create<std::string>(oc_path / "fw_version").set(boost::lexical_cast<std::string>(int(fw_version)));
+
+ ////////////////////////////////////////////////////////////////////
+ // Set up EEPROM
+ ////////////////////////////////////////////////////////////////////
+ _oc_dict[oc].eeprom = octoclock_eeprom_t(_oc_dict[oc].ctrl_xport);
+ _tree->create<octoclock_eeprom_t>(oc_path / "eeprom")
+ .set(_oc_dict[oc].eeprom)
+ .subscribe(boost::bind(&octoclock_impl::_set_eeprom, this, oc, _1));
+
+ ////////////////////////////////////////////////////////////////////
+ // Initialize non-GPSDO sensors
+ ////////////////////////////////////////////////////////////////////
+ _tree->create<boost::uint32_t>(oc_path / "time")
+ .publish(boost::bind(&octoclock_impl::_get_time, this, oc));
+ _tree->create<sensor_value_t>(oc_path / "sensors/ext_ref_detected")
+ .publish(boost::bind(&octoclock_impl::_ext_ref_detected, this, oc));
+ _tree->create<sensor_value_t>(oc_path / "sensors/gps_detected")
+ .publish(boost::bind(&octoclock_impl::_gps_detected, this, oc));
+ _tree->create<sensor_value_t>(oc_path / "sensors/using_ref")
+ .publish(boost::bind(&octoclock_impl::_which_ref, this, oc));
+ _tree->create<sensor_value_t>(oc_path / "sensors/switch_pos")
+ .publish(boost::bind(&octoclock_impl::_switch_pos, this, oc));
+
+ ////////////////////////////////////////////////////////////////////
+ // Check reference and GPSDO
+ ////////////////////////////////////////////////////////////////////
+ std::string asterisk = (device_args.size() > 1) ? " * " : "";
+
+ if(device_args.size() > 1){
+ UHD_MSG(status) << std::endl << "Checking status of " << addr << ":" << std::endl;
+ }
+ UHD_MSG(status) << boost::format("%sDetecting internal GPSDO...") % asterisk << std::flush;
+
+ _get_state(oc);
+ if(_oc_dict[oc].state.gps_detected){
+ try{
+ _oc_dict[oc].gps = gps_ctrl::make(octoclock_make_uart_iface(_oc_dict[oc].gpsdo_xport));
+
+ if(_oc_dict[oc].gps and _oc_dict[oc].gps->gps_detected()){
+ BOOST_FOREACH(const std::string &name, _oc_dict[oc].gps->get_sensors()){
+ _tree->create<sensor_value_t>(oc_path / "sensors" / name)
+ .publish(boost::bind(&gps_ctrl::get_sensor, _oc_dict[oc].gps, name));
+ }
+ }
+ else{
+ //If GPSDO communication failed, set gps_detected to false
+ _oc_dict[oc].state.gps_detected = 0;
+ UHD_MSG(warning) << "Device reports that it has a GPSDO, but we cannot communicate with it." << std::endl;
+ std::cout << std::endl;
+ }
+ }
+ catch(std::exception &e){
+ UHD_MSG(error) << "An error occurred making GPSDO control: " << e.what() << std::endl;
+ }
+ }
+ else UHD_MSG(status) << "No GPSDO found" << std::endl;
+ UHD_MSG(status) << boost::format("%sDetecting external reference...%s") % asterisk
+ % _ext_ref_detected(oc).value
+ << std::endl;
+ UHD_MSG(status) << boost::format("%sDetecting switch position...%s") % asterisk
+ % _switch_pos(oc).value
+ << std::endl;
+ std::string ref = _which_ref(oc).value;
+ if(ref == "none") UHD_MSG(status) << boost::format("%sDevice is not using any reference") % asterisk << std::endl;
+ else UHD_MSG(status) << boost::format("%sDevice is using %s reference") % asterisk
+ % _which_ref(oc).value
+ << std::endl;
+ }
+}
+
+rx_streamer::sptr octoclock_impl::get_rx_stream(UHD_UNUSED(const stream_args_t &args)){
+ throw uhd::not_implemented_error("This function is incompatible with this device.");
+}
+
+tx_streamer::sptr octoclock_impl::get_tx_stream(UHD_UNUSED(const stream_args_t &args)){
+ throw uhd::not_implemented_error("This function is incompatible with this device.");
+}
+
+bool octoclock_impl::recv_async_msg(UHD_UNUSED(uhd::async_metadata_t&), UHD_UNUSED(double)){
+ throw uhd::not_implemented_error("This function is incompatible with this device.");
+}
+
+void octoclock_impl::_set_eeprom(const std::string &oc, const octoclock_eeprom_t &oc_eeprom){
+ /*
+ * The OctoClock needs a full octoclock_eeprom_t so as to not erase
+ * what it currently has in the EEPROM, so store the relevant values
+ * from the user's input and send that instead.
+ */
+ BOOST_FOREACH(const std::string &key, oc_eeprom.keys()){
+ if(_oc_dict[oc].eeprom.has_key(key)) _oc_dict[oc].eeprom[key] = oc_eeprom[key];
+ }
+ _oc_dict[oc].eeprom.commit();
+}
+
+boost::uint32_t octoclock_impl::_get_fw_version(const std::string &oc){
+ octoclock_packet_t pkt_out;
+ pkt_out.sequence = _sequence++;
+ pkt_out.len = 0;
+ size_t len;
+
+ boost::uint8_t octoclock_data[udp_simple::mtu];
+ const octoclock_packet_t *pkt_in = reinterpret_cast<octoclock_packet_t*>(octoclock_data);
+
+ UHD_OCTOCLOCK_SEND_AND_RECV(_oc_dict[oc].ctrl_xport, OCTOCLOCK_QUERY_CMD, pkt_out, len, octoclock_data);
+ if(UHD_OCTOCLOCK_PACKET_MATCHES(OCTOCLOCK_QUERY_ACK, pkt_out, pkt_in, len)){
+ return pkt_in->proto_ver;
+ }
+ else throw uhd::runtime_error("Failed to retrive firmware version from OctoClock.");
+}
+
+void octoclock_impl::_get_state(const std::string &oc){
+ octoclock_packet_t pkt_out;
+ pkt_out.sequence = _sequence++;
+ pkt_out.len = 0;
+ size_t len = 0;
+
+ boost::uint8_t octoclock_data[udp_simple::mtu];
+ const octoclock_packet_t *pkt_in = reinterpret_cast<octoclock_packet_t*>(octoclock_data);
+
+ UHD_OCTOCLOCK_SEND_AND_RECV(_oc_dict[oc].ctrl_xport, SEND_STATE_CMD, pkt_out, len, octoclock_data);
+ if(UHD_OCTOCLOCK_PACKET_MATCHES(SEND_STATE_ACK, pkt_out, pkt_in, len)){
+ const octoclock_state_t *state = reinterpret_cast<const octoclock_state_t*>(pkt_in->data);
+ _oc_dict[oc].state = *state;
+ }
+ else throw uhd::runtime_error("Failed to retrieve state information from OctoClock.");
+}
+
+uhd::dict<ref_t, std::string> _ref_strings = boost::assign::map_list_of
+ (NO_REF, "none")
+ (INTERNAL, "internal")
+ (EXTERNAL, "external")
+;
+
+uhd::dict<switch_pos_t, std::string> _switch_pos_strings = boost::assign::map_list_of
+ (UP, "Prefer internal")
+ (DOWN, "Prefer external")
+;
+
+sensor_value_t octoclock_impl::_ext_ref_detected(const std::string &oc){
+ _get_state(oc);
+
+ return sensor_value_t("External reference detected", (_oc_dict[oc].state.external_detected > 0),
+ "true", "false");
+}
+
+sensor_value_t octoclock_impl::_gps_detected(const std::string &oc){
+ //Don't check, this shouldn't change once device is turned on
+
+ return sensor_value_t("GPSDO detected", (_oc_dict[oc].state.gps_detected > 0),
+ "true", "false");
+}
+
+sensor_value_t octoclock_impl::_which_ref(const std::string &oc){
+ _get_state(oc);
+
+ if(not _ref_strings.has_key(ref_t(_oc_dict[oc].state.which_ref))){
+ throw uhd::runtime_error("Invalid reference detected.");
+ }
+
+ return sensor_value_t("Using reference", _ref_strings[ref_t(_oc_dict[oc].state.which_ref)], "");
+}
+
+sensor_value_t octoclock_impl::_switch_pos(const std::string &oc){
+ _get_state(oc);
+
+ if(not _switch_pos_strings.has_key(switch_pos_t(_oc_dict[oc].state.switch_pos))){
+ throw uhd::runtime_error("Invalid switch position detected.");
+ }
+
+ return sensor_value_t("Switch position", _switch_pos_strings[switch_pos_t(_oc_dict[oc].state.switch_pos)], "");
+}
+
+boost::uint32_t octoclock_impl::_get_time(const std::string &oc){
+ if(_oc_dict[oc].state.gps_detected){
+ std::string time_str = _oc_dict[oc].gps->get_sensor("gps_time").value;
+ return boost::lexical_cast<boost::uint32_t>(time_str);
+ }
+ else throw uhd::runtime_error("This device cannot return a time.");
+}
+
+std::string octoclock_impl::_get_images_help_message(const std::string &addr){
+ const std::string image_name = "octoclock_r4_fw.bin";
+
+ //Check to see if image is in default location
+ std::string image_location;
+ try{
+ image_location = uhd::find_image_path(image_name);
+ }
+ catch(const std::exception &e){
+ return str(boost::format("Could not find %s in your images path.\n%s")
+ % image_name
+ % uhd::print_images_error());
+ }
+
+ //Get escape character
+ #ifdef UHD_PLATFORM_WIN32
+ const std::string ml = "^\n ";
+ #else
+ const std::string ml = "\\\n ";
+ #endif
+
+ //Get burner command
+ const std::string burner_path = (fs::path(uhd::get_pkg_path()) / "bin" / "octoclock_firmware_burner").string();
+ const std::string burner_cmd = str(boost::format("%s %s--addr=\"%s\"") % burner_path % ml % addr);
+ return str(boost::format("%s\n%s") % uhd::print_images_error() % burner_cmd);
+}
diff --git a/host/lib/usrp_clock/octoclock/octoclock_impl.hpp b/host/lib/usrp_clock/octoclock/octoclock_impl.hpp
new file mode 100644
index 000000000..ab45cd5f0
--- /dev/null
+++ b/host/lib/usrp_clock/octoclock/octoclock_impl.hpp
@@ -0,0 +1,80 @@
+//
+// Copyright 2014 Ettus Research LLC
+//
+// 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/>.
+//
+
+#ifndef INCLUDED_OCTOCLOCK_IMPL_HPP
+#define INCLUDED_OCTOCLOCK_IMPL_HPP
+
+#include <boost/shared_ptr.hpp>
+#include <boost/thread.hpp>
+
+#include <uhd/device.hpp>
+#include <uhd/stream.hpp>
+#include <uhd/usrp/gps_ctrl.hpp>
+#include <uhd/usrp_clock/octoclock_eeprom.hpp>
+#include <uhd/types/device_addr.hpp>
+#include <uhd/types/dict.hpp>
+#include <uhd/types/sensors.hpp>
+
+#include "common.h"
+
+/*!
+ * OctoClock implementation guts
+ */
+class octoclock_impl : public uhd::device{
+public:
+ octoclock_impl(const uhd::device_addr_t &);
+ ~octoclock_impl(void) {};
+
+ uhd::rx_streamer::sptr get_rx_stream(const uhd::stream_args_t &args);
+
+ uhd::tx_streamer::sptr get_tx_stream(const uhd::stream_args_t &args);
+
+ bool recv_async_msg(uhd::async_metadata_t&, double);
+
+private:
+ struct oc_container_type{
+ uhd::usrp_clock::octoclock_eeprom_t eeprom;
+ octoclock_state_t state;
+ uhd::transport::udp_simple::sptr ctrl_xport;
+ uhd::transport::udp_simple::sptr gpsdo_xport;
+ uhd::gps_ctrl::sptr gps;
+ };
+ uhd::dict<std::string, oc_container_type> _oc_dict;
+ boost::uint32_t _sequence;
+
+ void _set_eeprom(const std::string &oc, const uhd::usrp_clock::octoclock_eeprom_t &oc_eeprom);
+
+ boost::uint32_t _get_fw_version(const std::string &oc);
+
+ void _get_state(const std::string &oc);
+
+ uhd::sensor_value_t _ext_ref_detected(const std::string &oc);
+
+ uhd::sensor_value_t _gps_detected(const std::string &oc);
+
+ uhd::sensor_value_t _which_ref(const std::string &oc);
+
+ uhd::sensor_value_t _switch_pos(const std::string &oc);
+
+ boost::uint32_t _get_time(const std::string &oc);
+
+ std::string _get_images_help_message(const std::string &addr);
+
+ boost::mutex _device_mutex;
+};
+
+#endif /* INCLUDED_OCTOCLOCK_IMPL_HPP */
diff --git a/host/lib/usrp_clock/octoclock/octoclock_uart.cpp b/host/lib/usrp_clock/octoclock/octoclock_uart.cpp
new file mode 100644
index 000000000..eb3f40d9c
--- /dev/null
+++ b/host/lib/usrp_clock/octoclock/octoclock_uart.cpp
@@ -0,0 +1,162 @@
+//
+// Copyright 2014 Ettus Research LLC
+//
+// 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 <iostream>
+#include <string>
+#include <string.h>
+
+#include <boost/algorithm/string.hpp>
+#include <boost/asio.hpp>
+#include <boost/cstdint.hpp>
+#include <boost/format.hpp>
+#include <boost/thread/thread.hpp>
+
+#include <uhd/exception.hpp>
+#include <uhd/utils/byteswap.hpp>
+
+#include "common.h"
+#include "octoclock_uart.hpp"
+
+namespace asio = boost::asio;
+using namespace uhd::transport;
+
+#define NUM_WRAPS_EQUAL (_state.num_wraps == _device_state.num_wraps)
+#define POS_EQUAL (_state.pos == _device_state.pos)
+#define STATES_EQUAL (NUM_WRAPS_EQUAL && POS_EQUAL)
+#define LOCAL_STATE_AHEAD (_state.num_wraps > _device_state.num_wraps || \
+ (NUM_WRAPS_EQUAL && _state.pos > _device_state.pos))
+
+namespace uhd{
+ octoclock_uart_iface::octoclock_uart_iface(udp_simple::sptr udp): uart_iface(){
+ _udp = udp;
+ _state.num_wraps = 0;
+ _state.pos = 0;
+ _device_state.num_wraps = 0;
+ _device_state.pos = 0;
+ size_t len = 0;
+
+ //Get pool size from device
+ octoclock_packet_t pkt_out;
+ pkt_out.sequence = uhd::htonx<boost::uint16_t>(std::rand());
+ pkt_out.len = 0;
+
+ boost::uint8_t octoclock_data[udp_simple::mtu];
+ const octoclock_packet_t *pkt_in = reinterpret_cast<octoclock_packet_t*>(octoclock_data);
+
+ UHD_OCTOCLOCK_SEND_AND_RECV(_udp, SEND_POOLSIZE_CMD, pkt_out, len, octoclock_data);
+ if(UHD_OCTOCLOCK_PACKET_MATCHES(SEND_POOLSIZE_ACK, pkt_out, pkt_in, len)){
+ _poolsize = pkt_in->poolsize;
+ _cache.resize(_poolsize);
+ }
+ else throw uhd::runtime_error("Failed to communicate with GPSDO.");
+ }
+
+ 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>(std::rand());
+ pkt_out.len = to_send.size();
+ memcpy(pkt_out.data, to_send.c_str(), to_send.size());
+
+ boost::uint8_t octoclock_data[udp_simple::mtu];
+ const octoclock_packet_t *pkt_in = reinterpret_cast<octoclock_packet_t*>(octoclock_data);
+
+ UHD_OCTOCLOCK_SEND_AND_RECV(_udp, HOST_SEND_TO_GPSDO_CMD, pkt_out, len, octoclock_data);
+ if(not UHD_OCTOCLOCK_PACKET_MATCHES(HOST_SEND_TO_GPSDO_ACK, pkt_out, pkt_in, len)){
+ throw uhd::runtime_error("Failed to send commands to GPSDO.");
+ }
+ }
+
+ std::string octoclock_uart_iface::read_uart(double timeout){
+ std::string result;
+
+ boost::system_time exit_time = boost::get_system_time() + boost::posix_time::milliseconds(long(timeout*1e3));
+
+ while(boost::get_system_time() < exit_time){
+ _update_cache();
+
+ for(char ch = _getchar(); ch != -1; ch = _getchar()){
+ if(ch == '\r') continue; //Skip carriage returns
+ _rxbuff += ch;
+
+ //If newline found, return string
+ if(ch == '\n'){
+ result = _rxbuff;
+ _rxbuff.clear();
+ return result;
+ }
+ }
+ boost::this_thread::sleep(boost::posix_time::milliseconds(1));
+ }
+
+ return result;
+ }
+
+ void octoclock_uart_iface::_update_cache(){
+ octoclock_packet_t pkt_out;
+ pkt_out.len = 0;
+ size_t len = 0;
+
+ boost::uint8_t octoclock_data[udp_simple::mtu];
+ const octoclock_packet_t *pkt_in = reinterpret_cast<octoclock_packet_t*>(octoclock_data);
+
+ if(STATES_EQUAL or LOCAL_STATE_AHEAD){
+ pkt_out.sequence++;
+ UHD_OCTOCLOCK_SEND_AND_RECV(_udp, SEND_GPSDO_CACHE_CMD, pkt_out, len, octoclock_data);
+ if(UHD_OCTOCLOCK_PACKET_MATCHES(SEND_GPSDO_CACHE_ACK, pkt_out, pkt_in, len)){
+ memcpy(&_cache[0], pkt_in->data, _poolsize);
+ _device_state = pkt_in->state;
+ }
+
+ boost::uint8_t delta_wraps = (_device_state.num_wraps - _state.num_wraps);
+ if(delta_wraps > 1 or
+ ((delta_wraps == 1) and (_device_state.pos >= _state.pos))){
+
+ _state.pos = (_device_state.pos+1) % _poolsize;
+ _state.num_wraps = (_device_state.num_wraps-1);
+
+ while((_cache[_state.pos] != '\n') and (_state.pos != _device_state.pos)){
+ _state.pos = (_state.pos+1) % _poolsize;
+ //We may have wrapped around locally
+ if(_state.pos == 0) _state.num_wraps++;
+ }
+ _state.pos = (_state.pos+1) % _poolsize;
+ //We may have wrapped around locally
+ if(_state.pos == 0) _state.num_wraps++;
+ }
+ }
+ }
+
+ char octoclock_uart_iface::_getchar(){
+ if(LOCAL_STATE_AHEAD){
+ return -1;
+ }
+
+ char ch = _cache[_state.pos];
+ _state.pos = ((_state.pos+1) % _poolsize);
+ //We may have wrapped around locally
+ if(_state.pos == 0) _state.num_wraps++;
+
+ return ch;
+ }
+
+ uart_iface::sptr octoclock_make_uart_iface(udp_simple::sptr udp){
+ return uart_iface::sptr(new octoclock_uart_iface(udp));
+ }
+}
diff --git a/host/lib/usrp_clock/octoclock/octoclock_uart.hpp b/host/lib/usrp_clock/octoclock/octoclock_uart.hpp
new file mode 100644
index 000000000..05d1bb121
--- /dev/null
+++ b/host/lib/usrp_clock/octoclock/octoclock_uart.hpp
@@ -0,0 +1,57 @@
+//
+// Copyright 2014 Ettus Research LLC
+//
+// 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 uart_ifaceied 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/>.
+//
+
+#ifndef INCLUDED_OCTOCLOCK_UART_HPP
+#define INCLUDED_OCTOCLOCK_UART_HPP
+
+#include <vector>
+
+#include <uhd/transport/udp_simple.hpp>
+#include <uhd/types/serial.hpp>
+
+/*!
+ * The OctoClock doesn't take UART input per se but reads a specific
+ * packet type and sends the string from there through its own serial
+ * functions.
+ */
+namespace uhd{
+class octoclock_uart_iface : public uhd::uart_iface{
+public:
+ octoclock_uart_iface(uhd::transport::udp_simple::sptr udp);
+ ~octoclock_uart_iface(void) {};
+
+ void write_uart(const std::string &buf);
+ std::string read_uart(double timeout);
+
+private:
+ uhd::transport::udp_simple::sptr _udp;
+
+ boost::uint16_t _poolsize;
+ gpsdo_cache_state_t _state;
+ gpsdo_cache_state_t _device_state;
+ std::vector<boost::uint8_t> _cache;
+ std::string _rxbuff;
+
+ void _update_cache();
+ char _getchar();
+};
+
+uart_iface::sptr octoclock_make_uart_iface(uhd::transport::udp_simple::sptr udp);
+
+}
+
+#endif /* INCLUDED_OCTOCLOCK_UART_HPP */