aboutsummaryrefslogtreecommitdiffstats
path: root/host/lib/usrp_clock/octoclock/octoclock_uart.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'host/lib/usrp_clock/octoclock/octoclock_uart.cpp')
-rw-r--r--host/lib/usrp_clock/octoclock/octoclock_uart.cpp162
1 files changed, 162 insertions, 0 deletions
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));
+ }
+}