diff options
-rw-r--r-- | doc/example.ini | 7 | ||||
-rw-r--r-- | src/output/SDR.cpp | 14 | ||||
-rw-r--r-- | src/output/SDRDevice.h | 11 | ||||
-rw-r--r-- | src/output/UHD.cpp | 8 | ||||
-rw-r--r-- | src/output/USRPTime.cpp | 183 | ||||
-rw-r--r-- | src/output/USRPTime.h | 23 |
6 files changed, 145 insertions, 101 deletions
diff --git a/doc/example.ini b/doc/example.ini index ebd4ec1..b94825c 100644 --- a/doc/example.ini +++ b/doc/example.ini @@ -261,11 +261,10 @@ pps_source=none ; possible values: ignore, crash behaviour_refclk_lock_lost=ignore -; The maximum accepted holdover time for the gpsdo. +; The maximum accepted holdover time for the gpsdo once it +; started operating. Initial check interval for GPSDO lock +; at startup is always 180s. ; Valid only if the refclk and pps_source are set to gpsdo. -; This value is also used for the initial lock check, and must -; be at least a minute so that the GPSOD has enough time to lock -; and to start disciplining its oscillator. ; Units: seconds ; Set to 0 to disable holdover check ; default value: 0 diff --git a/src/output/SDR.cpp b/src/output/SDR.cpp index 7c1b585..9be9aa2 100644 --- a/src/output/SDR.cpp +++ b/src/output/SDR.cpp @@ -78,6 +78,8 @@ SDR::SDR(SDRDeviceConfig& config, std::shared_ptr<SDRDevice> device) : RC_ADD_PARAMETER(underruns, "Counter of number of underruns"); RC_ADD_PARAMETER(latepackets, "Counter of number of late packets"); RC_ADD_PARAMETER(frames, "Counter of number of frames modulated"); + RC_ADD_PARAMETER(gpsdo_num_sv, "Number of Satellite Vehicles tracked by GPSDO"); + RC_ADD_PARAMETER(gpsdo_holdover, "1 if the GPSDO is in holdover, 0 if it is using gnss"); } SDR::~SDR() @@ -381,7 +383,9 @@ void SDR::set_parameter(const string& parameter, const string& value) } else if (parameter == "underruns" or parameter == "latepackets" or - parameter == "frames") { + parameter == "frames" or + parameter == "gpsdo_num_sv" or + parameter == "gpsdo_holdover") { throw ParameterError("Parameter " + parameter + " is read-only."); } else { @@ -426,6 +430,14 @@ const string SDR::get_parameter(const string& parameter) const ss << stat.num_frames_modulated; } } + else if (parameter == "gpsdo_num_sv") { + const auto stat = m_device->get_run_statistics(); + ss << stat.gpsdo_num_sv; + } + else if (parameter == "gpsdo_holdover") { + const auto stat = m_device->get_run_statistics(); + ss << (stat.gpsdo_holdover ? 1 : 0); + } else { ss << "Parameter '" << parameter << "' is not exported by controllable " << get_rc_name(); diff --git a/src/output/SDRDevice.h b/src/output/SDRDevice.h index bd1a518..9e052b0 100644 --- a/src/output/SDRDevice.h +++ b/src/output/SDRDevice.h @@ -106,10 +106,13 @@ struct FrameData { class SDRDevice { public: struct RunStatistics { - size_t num_underruns; - size_t num_late_packets; - size_t num_overruns; - size_t num_frames_modulated; + size_t num_underruns = 0; + size_t num_late_packets = 0; + size_t num_overruns = 0; + size_t num_frames_modulated = 0; + + int gpsdo_num_sv = 0; + bool gpsdo_holdover = false; }; virtual void tune(double lo_offset, double frequency) = 0; diff --git a/src/output/UHD.cpp b/src/output/UHD.cpp index 711e218..e4b578e 100644 --- a/src/output/UHD.cpp +++ b/src/output/UHD.cpp @@ -362,6 +362,12 @@ SDRDevice::RunStatistics UHD::get_run_statistics(void) const rs.num_overruns = num_overflows; rs.num_late_packets = num_late_packets; rs.num_frames_modulated = num_frames_modulated; + + if (m_device_time) { + const auto gpsdo_stat = m_device_time->get_gnss_stats(); + rs.gpsdo_holdover = gpsdo_stat.holdover; + rs.gpsdo_num_sv = gpsdo_stat.num_sv; + } return rs; } @@ -433,7 +439,7 @@ bool UHD::is_clk_source_ok(void) const } if (m_device_time) { - ok |= m_device_time->verify_time(); + ok &= m_device_time->verify_time(); } return ok; diff --git a/src/output/USRPTime.cpp b/src/output/USRPTime.cpp index 39b0e5a..232fa58 100644 --- a/src/output/USRPTime.cpp +++ b/src/output/USRPTime.cpp @@ -39,64 +39,6 @@ namespace Output { using namespace std; - -// Check function for GPS TIMELOCK sensor from the ODR LEA-M8F board GPSDO -static bool check_gps_timelock(uhd::usrp::multi_usrp::sptr& usrp) -{ - try { - const string sensor_value = - usrp->get_mboard_sensor("gps_timelock", 0).to_pp_string(); - - if (sensor_value.find("TIME LOCKED") == string::npos) { - - const string gngga = - usrp->get_mboard_sensor("gps_gngga", 0).to_pp_string(); - - std::stringstream ss(gngga); - std::string item; - std::vector<std::string> elems; - while (std::getline(ss, item, ',')) { - elems.push_back(item); - } - - const auto num_svs = (elems.size() >= 7) ? elems[7] : "?"; - - etiLog.level(info) << "OutputUHD: " << num_svs << " SVs," - " gps_timelock " << sensor_value; - return false; - } - - return true; - } - catch (const uhd::exception &e) { - etiLog.level(warn) << "OutputUHD: no gps_timelock sensor: " << - e.what(); - return false; - } -} - -// Check function for GPS LOCKED sensor from the Ettus GPSDO -static bool check_gps_locked(uhd::usrp::multi_usrp::sptr& usrp) -{ - try { - const uhd::sensor_value_t sensor_value( - usrp->get_mboard_sensor("gps_locked", 0)); - if (not sensor_value.to_bool()) { - etiLog.level(warn) << "OutputUHD: gps_locked " << - sensor_value.to_pp_string(); - return false; - } - - return true; - } - catch (const uhd::exception &e) { - etiLog.level(warn) << "OutputUHD: no gps_locked sensor" << - e.what(); - return false; - } -} - - USRPTime::USRPTime( uhd::usrp::multi_usrp::sptr usrp, SDRDeviceConfig& conf) : @@ -104,26 +46,6 @@ USRPTime::USRPTime( m_conf(conf), time_last_check(timepoint_t::clock::now()) { - - if (m_conf.pps_src == "gpsdo") { - using namespace std::chrono; - auto now = system_clock::now(); - auto expiry = now + seconds(m_conf.maxGPSHoldoverTime); - auto checkfunc = gpsdo_is_ettus() ? check_gps_locked : check_gps_timelock; - while (now < expiry) { - if (checkfunc(m_usrp)) { - break; - } - - now = system_clock::now(); - this_thread::sleep_for(seconds(2)); - } - - if (not checkfunc(m_usrp)) { - throw runtime_error("GPSDO did not lock during startup"); - } - } - if (m_conf.pps_src == "none") { if (m_conf.enableSync) { etiLog.level(warn) << @@ -133,8 +55,18 @@ USRPTime::USRPTime( set_usrp_time_from_localtime(); } - else if (m_conf.pps_src == "pps" or m_conf.pps_src == "gpsdo") { - set_usrp_time_from_pps(); + else if (m_conf.pps_src == "pps") { + // let verify_time handle time setup + } + else if (m_conf.pps_src == "gpsdo") { + if (gpsdo_is_ettus() ? check_gps_locked() : check_gps_timelock()) { + set_usrp_time_from_pps(); + gps_state = gps_state_e::monitor_fix; + num_checks_without_gps_fix = 0; + } + else { + // let verify_time handle time setup + } } else { throw std::runtime_error("USRPTime not implemented yet: " + @@ -193,6 +125,11 @@ bool USRPTime::verify_time() throw logic_error("End of USRPTime::verify_time() reached"); } +gnss_stats_t USRPTime::get_gnss_stats(void) const +{ + return gnss_stats; +} + void USRPTime::check_gps() { timepoint_t time_now = timepoint_t::clock::now(); @@ -224,11 +161,11 @@ void USRPTime::check_gps() // time, it has to be done in a separate thread. if (gpsdo_is_ettus()) { gps_fix_future = std::async(std::launch::async, - std::bind(check_gps_locked, m_usrp) ); + std::bind(&USRPTime::check_gps_locked, this) ); } else { gps_fix_future = std::async(std::launch::async, - std::bind(check_gps_timelock, m_usrp) ); + std::bind(&USRPTime::check_gps_timelock, this) ); } } } @@ -239,11 +176,15 @@ bool USRPTime::gpsfix_needs_check() const if (m_conf.refclk_src == "internal") { return false; } - else if (m_conf.refclk_src == "gpsdo") { + else if (gps_state == gps_state_e::monitor_fix and + (m_conf.refclk_src == "gpsdo" or + m_conf.refclk_src == "gpsdo-ettus")) { return (m_conf.maxGPSHoldoverTime != 0); } - else if (m_conf.refclk_src == "gpsdo-ettus") { - return (m_conf.maxGPSHoldoverTime != 0); + else if (gps_state == gps_state_e::bootup and + (m_conf.refclk_src == "gpsdo" or + m_conf.refclk_src == "gpsdo-ettus")) { + return true; } else { return false; @@ -308,6 +249,78 @@ void USRPTime::set_usrp_time_from_pps() } } + +// Check functionality of GPS sensors applicable to the ODR LEA-M8F board GPSDO +bool USRPTime::check_gps_timelock() +{ + bool locked = false; + + try { + const string sensor_value = + m_usrp->get_mboard_sensor("gps_timelock", 0).to_pp_string(); + + const string gngga = + m_usrp->get_mboard_sensor("gps_gngga", 0).to_pp_string(); + + std::stringstream ss(gngga); + std::string item; + std::vector<std::string> elems; + while (std::getline(ss, item, ',')) { + elems.push_back(item); + } + + const auto num_svs = (elems.size() > 7) ? elems[7] : "0"; + gnss_stats.num_sv = std::stoi(num_svs); + + locked = (sensor_value.find("TIME LOCKED") != string::npos); + } + catch (const uhd::exception &e) { + etiLog.level(warn) << "OutputUHD: no gps_timelock sensor: " << + e.what(); + } + + // LEA-M8F-patched UHD 3.12.0 has this additional sensor, that can + // be used to distinguish holdover operation. Previous versions + // did a config reset at startup to ensure we would not startup while + // in holdover. + try { + const string disc_src = + m_usrp->get_mboard_sensor("gps_discsrc", 0).to_pp_string(); + + locked &= (disc_src.find("gnss") != string::npos); + } + catch (const uhd::exception &e) { + etiLog.level(warn) << "OutputUHD: no gps_timelock sensor: " << + e.what(); + } + + gnss_stats.holdover = not locked; + + return locked; +} + +// Check function for GPS LOCKED sensor from the Ettus GPSDO +bool USRPTime::check_gps_locked() +{ + try { + const uhd::sensor_value_t sensor_value( + m_usrp->get_mboard_sensor("gps_locked", 0)); + if (not sensor_value.to_bool()) { + etiLog.level(warn) << "OutputUHD: gps_locked " << + sensor_value.to_pp_string(); + return false; + } + + return true; + } + catch (const uhd::exception &e) { + etiLog.level(warn) << "OutputUHD: no gps_locked sensor" << + e.what(); + return false; + } +} + + } // namespace Output #endif // HAVE_OUTPUT_UHD diff --git a/src/output/USRPTime.h b/src/output/USRPTime.h index 51ca800..bc862fd 100644 --- a/src/output/USRPTime.h +++ b/src/output/USRPTime.h @@ -2,7 +2,7 @@ Copyright (C) 2005, 2006, 2007, 2008, 2009, 2010 Her Majesty the Queen in Right of Canada (Communications Research Center Canada) - Copyright (C) 2017 + Copyright (C) 2018 Matthias P. Braendli, matthias.braendli@mpb.li http://opendigitalradio.org @@ -54,16 +54,24 @@ DESCRIPTION: namespace Output { +struct gnss_stats_t { + int num_sv = 0; // Number of Satellite Vehicles used + bool holdover = false; // True if LEA-M8F uses its internal time reference +}; + class USRPTime { public: USRPTime( uhd::usrp::multi_usrp::sptr usrp, SDRDeviceConfig& conf); // Verifies the GPSDO state, that the device time is ok. - // Returns true if all ok. + // Returns true if all ok. Needs to be called so the device + // time gets properly set. // Should be called more often than the gps_fix_check_interval bool verify_time(void); + gnss_stats_t get_gnss_stats(void) const; + // Wait time in seconds to get fix static const int initial_gps_fix_wait = 180; @@ -72,10 +80,8 @@ class USRPTime { private: enum class gps_state_e { - /* At startup, the LEA-M8F GPSDO gets issued a hotstart request to - * make sure we will not sync time on a PPS edge that is generated - * while the GPSDO is in holdover. In the bootup state, we wait for - * the first PPS after hotstart, and then sync time. + /* In the bootup state, we wait for correct time lock and + * the first PPS, and then sync time. */ bootup, @@ -93,6 +99,8 @@ class USRPTime { gps_state_e gps_state = gps_state_e::bootup; int num_checks_without_gps_fix = 1; + gnss_stats_t gnss_stats; + using timepoint_t = std::chrono::time_point<std::chrono::steady_clock>; timepoint_t time_last_check; @@ -107,6 +115,9 @@ class USRPTime { void set_usrp_time_from_localtime(void); void set_usrp_time_from_pps(void); + + bool check_gps_timelock(void); + bool check_gps_locked(void); }; } // namespace Output |