aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--doc/example.ini7
-rw-r--r--src/output/SDR.cpp14
-rw-r--r--src/output/SDRDevice.h11
-rw-r--r--src/output/UHD.cpp8
-rw-r--r--src/output/USRPTime.cpp183
-rw-r--r--src/output/USRPTime.h23
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