// // Copyright 2014-2016 Ettus Research LLC // Copyright 2018 Ettus Research, a National Instruments Company // // SPDX-License-Identifier: GPL-3.0-or-later // #include "octoclock_uart.hpp" #include "common.h" #include "octoclock_impl.hpp" #include #include #include #include #include #include #include #include #include #include #include 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 MAX_CACHE_AGE 256 // seconds namespace uhd { octoclock_uart_iface::octoclock_uart_iface(udp_simple::sptr udp, uint32_t proto_ver) : uart_iface() { _udp = udp; _state.num_wraps = 0; _state.pos = 0; _device_state.num_wraps = 0; _device_state.pos = 0; _proto_ver = proto_ver; // To avoid replicating sequence numbers between sessions _sequence = uint32_t(std::rand()); size_t len = 0; // Get pool size from device auto pkt_out = make_octoclock_packet(uhd::htonx(_sequence)); uint8_t octoclock_data[udp_simple::mtu]; const octoclock_packet_t* pkt_in = reinterpret_cast(octoclock_data); UHD_OCTOCLOCK_SEND_AND_RECV( _udp, _proto_ver, 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) { size_t len = 0; auto pkt_out = make_octoclock_packet(uhd::htonx(++_sequence)); pkt_out.len = buf.size(); memcpy(pkt_out.data, buf.c_str(), buf.size()); uint8_t octoclock_data[udp_simple::mtu]; const octoclock_packet_t* pkt_in = reinterpret_cast(octoclock_data); UHD_OCTOCLOCK_SEND_AND_RECV( _udp, _proto_ver, 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; const auto exit_time = std::chrono::steady_clock::now() + std::chrono::milliseconds(int64_t(timeout * 1e3)); while (true) { _update_cache(); for (char ch = _getchar(); ch != 0; ch = _getchar()) { _rxbuff += ch; // If newline found, return string if (ch == '\n') { result.swap(_rxbuff); return result; } } if (std::chrono::steady_clock::now() > exit_time) { break; } std::this_thread::sleep_for(std::chrono::milliseconds(1)); } return result; } void octoclock_uart_iface::_update_cache() { auto pkt_out = make_octoclock_packet(); size_t len = 0; uint8_t octoclock_data[udp_simple::mtu]; const octoclock_packet_t* pkt_in = reinterpret_cast(octoclock_data); if (STATES_EQUAL) { boost::system_time time = boost::get_system_time(); boost::posix_time::time_duration age = time - _last_cache_update; bool cache_expired = (age > boost::posix_time::seconds(MAX_CACHE_AGE)); pkt_out.sequence = uhd::htonx(++_sequence); UHD_OCTOCLOCK_SEND_AND_RECV( _udp, _proto_ver, 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; _last_cache_update = time; } uint8_t delta_wraps = (_device_state.num_wraps - _state.num_wraps); if (cache_expired or delta_wraps > 1 or ((delta_wraps == 1) and (_device_state.pos > _state.pos))) { _state.pos = _device_state.pos; _state.num_wraps = (_device_state.num_wraps - 1); _rxbuff.clear(); while ((_cache[_state.pos] != '\n')) { _state.pos = (_state.pos + 1) % _poolsize; // We may have wrapped around locally if (_state.pos == 0) _state.num_wraps++; if (STATES_EQUAL) break; } if (_cache[_state.pos] == '\n') { _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 (STATES_EQUAL) { return 0; } 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, uint32_t proto_ver) { return uart_iface::sptr(new octoclock_uart_iface(udp, proto_ver)); } } // namespace uhd