29 Commits

Author SHA1 Message Date
Alexander Chemeris
c0b8fc6687 bump version to 1.0.12 2017-07-27 23:51:25 +03:00
Alexander Chemeris
f14e3022fc zpu: Checking in a new firmware version.
* BUGFIX: Fix GPSDO rounding issue to stabilize frequency down to +-1 Hz @ 52MHz.
* Export measured frequency and last PPS time over the control socket.
* Allow GPSDO debug enable/disable over the control socket.
* Print firmware minor version and git hash on startup.
* Make debug output on wrong packets less scary for users.
2017-07-27 23:39:52 +03:00
Alexander Chemeris
20b2a2f322 host: Send out only UmTRX ID request on startup, limit compatibility number.
Previously we were sending out USRP control ID request, which is wrong, because
we're only interested in UmTRX.

Also lets use our proper compatibility number in the first message to reduce
error output on the UmTRX (it would complain about a compatibility number
mismatch otherwise). And we're now rejecting old compatibility numbers - we
don't maintain backward compatibility.
2017-07-27 13:42:12 +03:00
Alexander Chemeris
88df605601 host: Refer to UmTRX instead of USRP-N in an error message. 2017-07-27 13:41:35 +03:00
Alexander Chemeris
a4afe97d54 zpu: Make debug output on wrong packets less scary.
Users think that these messages indicate some sort of an issue, but
they are part of a normal UHD startup process. So we make them look
rather informational and less scary.
2017-07-27 13:38:54 +03:00
Alexander Chemeris
f4f02a51c0 zpu: Get basi GPSDO information and en/dis debug over the control socket. 2017-07-27 01:18:28 +03:00
Alexander Chemeris
907c28f849 zpu: Print firmware minor version and git hash on bootup. 2017-07-27 01:14:25 +03:00
Alexander Chemeris
dfa6aa0a0c zpu: Reset PID when setting TCXO DAC from the control socket. 2017-07-26 23:20:36 +03:00
Alexander Chemeris
aa35f5cc40 zpu: Make GPSDO debug runtime configurable, add more of it. 2017-07-26 22:39:12 +03:00
Alexander Chemeris
dfc98a6900 zpu: More precision for GPSDO.
Now we really get to +-1 Hz of the base TCXO frequency precision.

Previously we ignored least significant bits of the frequency due to integer
arithmetics rounding, which meant that we could get up to about 80 ppb of
static frequency error which is not good enough for telco systems.
2017-07-26 22:03:20 +03:00
Alexander Chemeris
7e351e3465 zpu: dac_value is only used in this file. 2017-07-26 22:00:38 +03:00
Alexander Chemeris
cc70a46fe4 host: Export DC calibration registers through the property tree. 2017-07-26 21:56:17 +03:00
Alexander Chemeris
0bf55bbca2 umtrx_property_tree: Various fixes for rarely used methods. 2017-07-26 21:49:01 +03:00
sergforce
230d9c22c8 Merge pull request #20 from lpmi-13/typofix
typo fix
2017-07-19 18:08:46 +03:00
lpmi-13
2be52f5741 fix simple typo 2017-06-29 19:40:36 +01:00
Josh Blum
a59b9c381a host: fix include with newer uhd
Added explicit include of #include <boost/cstdint.hpp>
The uhd header wb_iface.hpp has moved from boost cstdint
to regular stdint.h and thus is lacking the boost include.
2017-01-17 22:55:35 -08:00
Kirill Zakharenko
53526ef9b1 Revert "Add latest images that pases timing constrains"
d0a74632a8
2016-12-10 12:17:02 +03:00
Kirill Zakharenko
ce44a4ce47 umtrx2collectd: lowercase sensor names
it was done in nsq2collectd before, but this here is a proper place for it
2016-12-05 07:20:36 +03:00
Alexander Chemeris
4386d5938d host: Add support for complex values in the status monitor. 2016-11-16 15:56:45 -08:00
Kirill Zakharenko
204a7862d8 cmake: make Boost a required library 2016-10-22 16:17:17 +05:30
Josh Blum
19c125a8ef uhd v3.10.0 compatibility 2016-08-13 12:20:42 -07:00
Sergey Kostanbaev
d0a74632a8 Add latest images that pases timing constrains 2016-06-30 16:42:21 +03:00
Sergey Kostanbaev
3dac709c49 add map config for latest successful timing build 2016-06-30 16:41:07 +03:00
Kirill Zakharenko
50a1b928ec bump version to 1.0.11 2016-03-28 11:52:21 +02:00
Kirill Zakharenko
41a94769fe collectd: use 'fairwaves-monitoring' user to run plugin 2016-03-27 18:36:25 +02:00
Kirill Zakharenko
b1e80e5c27 bumped version to 1.0.10 2016-03-21 19:23:45 +01:00
Kirill Zakharenko
014e8123a3 collectd: rewritten collection script
* no longer conflicts over 'temperature' type with default types.db
* auto-adapts to any new or missing sensors
* simple! (e.g. doesn't read voltages twice)
2016-03-21 19:06:45 +01:00
Kirill Zakharenko
16bc2bdd18 bump version to 1.0.9 2016-02-27 20:52:08 +03:00
Kirill Zakharenko
9cf7377866 collectd: plugin to collect umtrx sensors 2016-02-26 17:52:07 +03:00
25 changed files with 814 additions and 55 deletions

2
README
View File

@@ -120,5 +120,5 @@ then RX VITA time alignment will not be possible due to ambiguity.
Currently, the adc sample strobes are driven from a common sigal.
simple_gemac_wrapper.v was modified to have an axi_packet_gate in the eth transmit direction.
This guarantees thats a packet is 100% buffered before being send out over ethernet.
This guarantees thats a packet is 100% buffered before being sent out over ethernet.
This avoid in-packet underflow when the mac is fed from a slower clock domain.

33
debian/changelog vendored
View File

@@ -1,3 +1,36 @@
umtrx (1.0.12) trusty; urgency=low
* [ZPU] BUGFIX: Fix GPSDO rounding issue to stabilize frequency down to +-1 Hz @ 52MHz.
* [ZPU] Export measured frequency and last PPS time over the control socket.
* [ZPU] Allow GPSDO debug enable/disable over the control socket.
* [ZPU] Print firmware minor version and git hash on startup.
* [ZPU] Make debug output on wrong packets less scary for users.
* [host] Cleanup UmTRX detection on UHD startup.
* [host] Export DC calibration registers through the property tree.
* [host] Fix build with newer UHD versions.
* [utils] BUGIX: Multiple fixes for the umtrx_ty_tree.py module.
* [utils] Lowercase sensor names in umtrx2collectd.
-- Alexander Chemeris <Alexander.Chemeris@fairwaves.co> Thu, 27 Jul 2017 23:46:07 +0300
umtrx (1.0.11) trusty; urgency=low
* collectd: use 'fairwaves-monitoring' user to run plugin
-- Kirill Zakharenko <earwin@gmail.com> Sun, 27 Mar 2016 19:37:39 +0100
umtrx (1.0.10) trusty; urgency=low
* collectd: rewritten counter collection plugin
-- Kirill Zakharenko <earwin@gmail.com> Mon, 21 Mar 2016 19:21:00 +0100
umtrx (1.0.9) trusty; urgency=low
* collectd: osmo-nitb counter collection plugin
-- Kirill Zakharenko <earwin@gmail.com> Mon, 24 Feb 2016 19:35:56 +0300
umtrx (1.0.8) trusty; urgency=low
* host: integrate support class for umsel2

View File

@@ -2,3 +2,7 @@ usr/bin
images/u2plus_umtrx_v2.bin images/umtrx_txrx_uhd.bin usr/share/umtrx/firmware
host/utils/umtrx_property_tree.py host/utils/umtrx_vswr.py usr/share/umtrx
host/utils/umtrx_query_sensors.py host/utils/umtrx_query_versions.py host/utils/umtrx_net_burner.py usr/share/umtrx
host/utils/collectd/umtrx.types.db usr/share/collectd
host/utils/collectd/umtrx2collectd.py usr/share/umtrx
host/utils/collectd/umtrx.conf etc/collectd/collectd.conf.d

View File

@@ -0,0 +1,28 @@
<DesignStrategy goal="Timing Performance" strategy="SmartXplorer - mapextraeffortioreg" version="14.7">
<Description><![CDATA[ This is a Timing Performance optimized strategy.This strategy is also one of the strategies used by SmartXplorer. If you wish to run SmartXplorer to automatically try multiple Timing Performance strategies, including this one, close this dialog and then select Tools -> SmartXplorer -> Launch SmartXplorer . ]]></Description>
<DeviceList devices="spartan6,spartan6l,aspartan6,qspartan6,qspartan6l" />
<Properties>
<property name = "Map:Placer Effort Level"
value = "High" />
<property name = "Map:Placer Extra Effort"
value = "Normal" />
<property name = "Map:Pack I/O Registers/Latches into IOBs"
value = "For Inputs and Outputs" />
<property name = "Map:Starting Placer Cost Table (1-100)"
value = "7" />
<property name = "Place &amp; Route:Place &amp; Route Effort Level (Overall)"
value = "High" />
<property name = "Place &amp; Route:Extra Effort (Highest PAR level only)"
value = "Normal" />
<property name = "Generate Post-Place &amp; Route Static Timing:Number of Paths in Error/Verbose Report"
value = "10" />
<property name = "Generate Post-Place &amp; Route Static Timing:Report Type"
value = "Error Report" />
<property name = "Generate Post-Place &amp; Route Static Timing:Change Device Speed To"
value = "-2" />
<property name = "Generate Post-Place &amp; Route Static Timing:Report Paths by Endpoint"
value = "3" />
<property name = "Generate Post-Place &amp; Route Static Timing:Report Fastest Path(s) in Each Constraint"
value = "true" />
</Properties>
</DesignStrategy>

View File

@@ -112,7 +112,7 @@ SET(Boost_ADDITIONAL_VERSIONS
"1.60.0" "1.60" "1.61.0" "1.61" "1.62.0" "1.62" "1.63.0" "1.63" "1.64.0" "1.64"
"1.65.0" "1.65" "1.66.0" "1.66" "1.67.0" "1.67" "1.68.0" "1.68" "1.69.0" "1.69"
)
FIND_PACKAGE(Boost 1.36 COMPONENTS ${BOOST_REQUIRED_COMPONENTS})
FIND_PACKAGE(Boost 1.36 REQUIRED COMPONENTS ${BOOST_REQUIRED_COMPONENTS})
INCLUDE_DIRECTORIES(${Boost_INCLUDE_DIRS})
LINK_DIRECTORIES(${Boost_LIBRARY_DIRS})
@@ -151,7 +151,7 @@ if(CMAKE_COMPILER_IS_GNUCXX)
endif()
########################################################################
# Look for device filter
# UHD compatibility checks
########################################################################
message(STATUS "Checking uhd::device::register_device() API...")
message(STATUS " Reading ${UHD_INCLUDE_DIRS}/uhd/device.hpp...")
@@ -164,6 +164,19 @@ else()
message(STATUS " has filter API")
endif()
message(STATUS "Checking uhd::property::set_publisher() API...")
message(STATUS " Reading ${UHD_INCLUDE_DIRS}/uhd/property_tree.hpp...")
file(READ ${UHD_INCLUDE_DIRS}/uhd/property_tree.hpp property_tree_hpp)
string(FIND "${property_tree_hpp}" "set_publisher" has_set_publisher)
if ("${has_set_publisher}" STREQUAL "-1")
message(STATUS " missing set_publisher() API")
else()
add_definitions(-Dpublish=set_publisher)
add_definitions(-Dsubscribe=add_desired_subscriber)
add_definitions(-Dcoerce=add_coerced_subscriber)
message(STATUS " has set_publisher() API")
endif()
########################################################################
# Build the UmTRX module
########################################################################

View File

@@ -202,6 +202,141 @@ void lms6002d_dev::set_txrx_polarity_and_interleaving(int rx_fsync_polarity,
lms_write_bits(0x5A, 0xD8, data);
}
void lms6002d_dev::set_dc_calibration_value(uint8_t dc_addr, uint8_t calibration_reg_base, uint8_t value)
{
uint8_t reg_val;
if (verbosity > 0) printf("Manually setting DC Offset Calibration for base %x, ADDR %d: %d\n", calibration_reg_base, dc_addr, value);
// DC_CNTVAL[5:0] = value
write_reg(calibration_reg_base+0x02, value&0x3f);
// Save old register value
reg_val = read_reg(calibration_reg_base+0x03);
// DC_ADDR := ADDR
reg_val = (reg_val & 0xf8) | dc_addr;
write_reg(calibration_reg_base+0x03, reg_val);
// DC_LOAD := 1
reg_val = reg_val | (1 << 4);
write_reg(calibration_reg_base+0x03, reg_val);
// DC_LOAD := 0
reg_val = reg_val ^ (1 << 4);
write_reg(calibration_reg_base+0x03, reg_val);
}
uint8_t lms6002d_dev::get_dc_calibration_value(uint8_t dc_addr, uint8_t calibration_reg_base)
{
// DC_ADDR := ADDR
lms_write_bits(calibration_reg_base+0x03, 0xf8, dc_addr);
uint8_t res = read_reg(calibration_reg_base);
if (verbosity > 0) printf("Reading DC Offset Calibration for base %x, ADDR %d: %d\n", calibration_reg_base, dc_addr, res);
return res;
}
void lms6002d_dev::set_rxfe_dc_i(int8_t value)
{
uint8_t coded_value = (value<0)?(64-value):value;
if (verbosity > 0) printf("Setting DC Offset Calibration for RxFE Channel I to %d (0x%X)\n", value, coded_value);
// DCOFF_I_RXFE := value
lms_write_bits(0x71, 0x7f, coded_value);
}
int8_t lms6002d_dev::get_rxfe_dc_i()
{
uint8_t res = lms_read_shift(0x71, 0x3f, 0);
uint8_t sign = lms_read_shift(0x71, 0x40, 6);
if (verbosity > 0) printf("Reading DC Offset Calibration for RxFE Channel I: sign=%d val=%d)\n", sign, res);
return sign?-res:res;
}
void lms6002d_dev::set_rxfe_dc_q(int8_t value)
{
uint8_t coded_value = (value<0)?(64-value):value;
if (verbosity > 0) printf("Setting DC Offset Calibration for RxFE Channel Q to %d (0x%X)\n", value, coded_value);
// DCOFF_I_RXFE := value
lms_write_bits(0x72, 0x7f, coded_value);
}
int8_t lms6002d_dev::get_rxfe_dc_q()
{
uint8_t res = lms_read_shift(0x72, 0x3f, 0);
uint8_t sign = lms_read_shift(0x72, 0x40, 6);
if (verbosity > 0) printf("Reading DC Offset Calibration for RxFE Channel Q: sign=%d val=%d)\n", sign, res);
return sign?-res:res;
}
void lms6002d_dev::set_rxlpf_dc_i(uint8_t value)
{
set_dc_calibration_value(0, 0x50, value);
}
uint8_t lms6002d_dev::get_rxlpf_dc_i()
{
return get_dc_calibration_value(0, 0x50);
}
void lms6002d_dev::set_rxlpf_dc_q(uint8_t value)
{
set_dc_calibration_value(1, 0x50, value);
}
uint8_t lms6002d_dev::get_rxlpf_dc_q()
{
return get_dc_calibration_value(1, 0x50);
}
void lms6002d_dev::set_rxvga2_dc_reference(uint8_t value)
{
set_dc_calibration_value(0, 0x60, value);
}
uint8_t lms6002d_dev::get_rxvga2_dc_reference()
{
return get_dc_calibration_value(0, 0x60);
}
void lms6002d_dev::set_rxvga2a_dc_i(uint8_t value)
{
set_dc_calibration_value(1, 0x60, value);
}
uint8_t lms6002d_dev::get_rxvga2a_dc_i()
{
return get_dc_calibration_value(1, 0x60);
}
void lms6002d_dev::set_rxvga2a_dc_q(uint8_t value)
{
set_dc_calibration_value(2, 0x60, value);
}
uint8_t lms6002d_dev::get_rxvga2a_dc_q()
{
return get_dc_calibration_value(2, 0x60);
}
void lms6002d_dev::set_rxvga2b_dc_i(uint8_t value)
{
set_dc_calibration_value(3, 0x60, value);
}
uint8_t lms6002d_dev::get_rxvga2b_dc_i()
{
return get_dc_calibration_value(3, 0x60);
}
void lms6002d_dev::set_rxvga2b_dc_q(uint8_t value)
{
set_dc_calibration_value(4, 0x60, value);
}
uint8_t lms6002d_dev::get_rxvga2b_dc_q()
{
return get_dc_calibration_value(4, 0x60);
}
int lms6002d_dev::general_dc_calibration_loop(uint8_t dc_addr, uint8_t calibration_reg_base)
{
uint8_t reg_val;

View File

@@ -461,6 +461,66 @@ public:
lms_clear_bits(0x70, (1 << 1));
}
/** Load value from 0x52/0x62 register to a selected DC calibration register */
void set_dc_calibration_value(uint8_t dc_addr, uint8_t calibration_reg_base, uint8_t value);
/** Get selected DC calibration register value */
uint8_t get_dc_calibration_value(uint8_t dc_addr, uint8_t calibration_reg_base);
/** Set value for the Rx FE DC calibration, I channel */
void set_rxfe_dc_i(int8_t value);
/** Get value for the Rx FE DC calibration, I channel */
int8_t get_rxfe_dc_i();
/** Set value for the Rx FE DC calibration, Q channel */
void set_rxfe_dc_q(int8_t value);
/** Get value for the Rx FE DC calibration, Q channel */
int8_t get_rxfe_dc_q();
/** Set value for the Rx LPF DC calibration, I channel */
void set_rxlpf_dc_i(uint8_t value);
/** Get value for the Rx LPF DC calibration, I channel */
uint8_t get_rxlpf_dc_i();
/** Set value for the Rx LPF DC calibration, Q channel */
void set_rxlpf_dc_q(uint8_t value);
/** Get value for the Rx LPF DC calibration, Q channel */
uint8_t get_rxlpf_dc_q();
/** Set value for the Rx VGA2 DC reference module */
void set_rxvga2_dc_reference(uint8_t value);
/** Get value for the Rx VGA2 DC reference module */
uint8_t get_rxvga2_dc_reference();
/** Set value for the Rx First gain stage (VGA2A), I channel */
void set_rxvga2a_dc_i(uint8_t value);
/** Get value for the Rx First gain stage (VGA2A), I channel */
uint8_t get_rxvga2a_dc_i();
/** Set value for the Rx First gain stage (VGA2A), Q channel */
void set_rxvga2a_dc_q(uint8_t value);
/** Get value for the Rx First gain stage (VGA2A), Q channel */
uint8_t get_rxvga2a_dc_q();
/** Set value for the Rx Second gain stage (VGA2B), I channel */
void set_rxvga2b_dc_i(uint8_t value);
/** Get value for the Rx Second gain stage (VGA2A), I channel */
uint8_t get_rxvga2b_dc_i();
/** Set value for the Rx Second gain stage (VGA2B), Q channel */
void set_rxvga2b_dc_q(uint8_t value);
/** Get value for the Rx Second gain stage (VGA2B), Q channel */
uint8_t get_rxvga2b_dc_q();
/** Programming and Calibration Guide: 4.1 General DC Calibration Procedure */
int general_dc_calibration_loop(uint8_t dc_addr, uint8_t calibration_reg_base);

View File

@@ -393,6 +393,114 @@ protected:
return offset;
}
void set_rxfe_dc_i(uint8_t value) {
boost::recursive_mutex::scoped_lock l(_mutex);
if (verbosity>0) printf("lms6002d_ctrl_impl::set_rxfe_dc_i(%d)\n", value);
lms.set_rxfe_dc_i(value);
}
uint8_t get_rxfe_dc_i() {
boost::recursive_mutex::scoped_lock l(_mutex);
if (verbosity>0) printf("lms6002d_ctrl_impl::get_rxfe_dc_i()\n");
return lms.get_rxfe_dc_i();
}
void set_rxfe_dc_q(uint8_t value) {
boost::recursive_mutex::scoped_lock l(_mutex);
if (verbosity>0) printf("lms6002d_ctrl_impl::set_rxfe_dc_q(%d)\n", value);
lms.set_rxfe_dc_q(value);
}
uint8_t get_rxfe_dc_q() {
boost::recursive_mutex::scoped_lock l(_mutex);
if (verbosity>0) printf("lms6002d_ctrl_impl::get_rxfe_dc_q()\n");
return lms.get_rxfe_dc_q();
}
void set_rxlpf_dc_i(uint8_t value) {
boost::recursive_mutex::scoped_lock l(_mutex);
if (verbosity>0) printf("lms6002d_ctrl_impl::set_rxlpf_dc_i(%d)\n", value);
lms.set_rxlpf_dc_i(value);
}
uint8_t get_rxlpf_dc_i() {
boost::recursive_mutex::scoped_lock l(_mutex);
if (verbosity>0) printf("lms6002d_ctrl_impl::get_rxlpf_dc_i()\n");
return lms.get_rxlpf_dc_i();
}
void set_rxlpf_dc_q(uint8_t value) {
boost::recursive_mutex::scoped_lock l(_mutex);
if (verbosity>0) printf("lms6002d_ctrl_impl::set_rxlpf_dc_q(%d)\n", value);
lms.set_rxlpf_dc_q(value);
}
uint8_t get_rxlpf_dc_q() {
boost::recursive_mutex::scoped_lock l(_mutex);
if (verbosity>0) printf("lms6002d_ctrl_impl::get_rxlpf_dc_q()\n");
return lms.get_rxlpf_dc_q();
}
void set_rxvga2_dc_reference(uint8_t value) {
boost::recursive_mutex::scoped_lock l(_mutex);
if (verbosity>0) printf("lms6002d_ctrl_impl::set_rxvga2_dc_reference(%d)\n", value);
lms.set_rxvga2_dc_reference(value);
}
uint8_t get_rxvga2_dc_reference() {
boost::recursive_mutex::scoped_lock l(_mutex);
if (verbosity>0) printf("lms6002d_ctrl_impl::get_rxvga2_dc_reference()\n");
return lms.get_rxvga2_dc_reference();
}
void set_rxvga2a_dc_i(uint8_t value) {
boost::recursive_mutex::scoped_lock l(_mutex);
if (verbosity>0) printf("lms6002d_ctrl_impl::set_rxvga2a_dc_i(%d)\n", value);
lms.set_rxvga2a_dc_i(value);
}
uint8_t get_rxvga2a_dc_i() {
boost::recursive_mutex::scoped_lock l(_mutex);
if (verbosity>0) printf("lms6002d_ctrl_impl::get_rxvga2a_dc_i()\n");
return lms.get_rxvga2a_dc_i();
}
void set_rxvga2a_dc_q(uint8_t value) {
boost::recursive_mutex::scoped_lock l(_mutex);
if (verbosity>0) printf("lms6002d_ctrl_impl::set_rxvga2a_dc_q(%d)\n", value);
lms.set_rxvga2a_dc_q(value);
}
uint8_t get_rxvga2a_dc_q() {
boost::recursive_mutex::scoped_lock l(_mutex);
if (verbosity>0) printf("lms6002d_ctrl_impl::get_rxvga2a_dc_q()\n");
return lms.get_rxvga2a_dc_q();
}
void set_rxvga2b_dc_i(uint8_t value) {
boost::recursive_mutex::scoped_lock l(_mutex);
if (verbosity>0) printf("lms6002d_ctrl_impl::set_rxvga2b_dc_i(%d)\n", value);
lms.set_rxvga2b_dc_i(value);
}
uint8_t get_rxvga2b_dc_i() {
boost::recursive_mutex::scoped_lock l(_mutex);
if (verbosity>0) printf("lms6002d_ctrl_impl::get_rxvga2b_dc_i()\n");
return lms.get_rxvga2b_dc_i();
}
void set_rxvga2b_dc_q(uint8_t value) {
boost::recursive_mutex::scoped_lock l(_mutex);
if (verbosity>0) printf("lms6002d_ctrl_impl::set_rxvga2b_dc_q(%d)\n", value);
lms.set_rxvga2b_dc_q(value);
}
uint8_t get_rxvga2b_dc_q() {
boost::recursive_mutex::scoped_lock l(_mutex);
if (verbosity>0) printf("lms6002d_ctrl_impl::get_rxvga2b_dc_q()\n");
return lms.get_rxvga2b_dc_q();
}
private:
umtrx_lms6002d_dev lms; // Interface to the LMS chip.
int tx_vga1gain, tx_vga2gain; // Stored values of Tx VGA1 and VGA2 gains.

View File

@@ -55,6 +55,25 @@ public:
virtual uint8_t get_tx_vga1dc_i_int(void) = 0;
virtual uint8_t get_tx_vga1dc_q_int(void) = 0;
virtual void set_rxfe_dc_i(uint8_t value) = 0;
virtual uint8_t get_rxfe_dc_i() = 0;
virtual void set_rxfe_dc_q(uint8_t value) = 0;
virtual uint8_t get_rxfe_dc_q() = 0;
virtual void set_rxlpf_dc_i(uint8_t value) = 0;
virtual uint8_t get_rxlpf_dc_i() = 0;
virtual void set_rxlpf_dc_q(uint8_t value) = 0;
virtual uint8_t get_rxlpf_dc_q() = 0;
virtual void set_rxvga2_dc_reference(uint8_t value) = 0;
virtual uint8_t get_rxvga2_dc_reference() = 0;
virtual void set_rxvga2a_dc_i(uint8_t value) = 0;
virtual uint8_t get_rxvga2a_dc_i() = 0;
virtual void set_rxvga2a_dc_q(uint8_t value) = 0;
virtual uint8_t get_rxvga2a_dc_q() = 0;
virtual void set_rxvga2b_dc_i(uint8_t value) = 0;
virtual uint8_t get_rxvga2b_dc_i() = 0;
virtual void set_rxvga2b_dc_q(uint8_t value) = 0;
virtual uint8_t get_rxvga2b_dc_q() = 0;
};
#endif /* INCLUDED_LMS6002D_CTRL_HPP */

View File

@@ -23,6 +23,7 @@
#include <uhd/transport/zero_copy.hpp>
#include <boost/shared_ptr.hpp>
#include <boost/utility.hpp>
#include <boost/cstdint.hpp>
#include <uhd/types/wb_iface.hpp>
#include <string>

View File

@@ -70,21 +70,16 @@ public:
umtrx_iface_impl(udp_simple::sptr ctrl_transport):
_ctrl_transport(ctrl_transport),
_ctrl_seq_num(0),
_protocol_compat(0) //initialized below...
_protocol_compat(USRP2_FW_COMPAT_NUM)
{
//Obtain the firmware's compat number.
//Save the response compat number for communication.
//TODO can choose to reject certain older compat numbers
usrp2_ctrl_data_t ctrl_data;
ctrl_data.id = htonl(USRP2_CTRL_ID_WAZZUP_BRO);
ctrl_data = ctrl_send_and_recv(ctrl_data, 0, ~0);
if (ntohl(ctrl_data.id) != USRP2_CTRL_ID_WAZZUP_DUDE) {
ctrl_data.id = htonl(UMTRX_CTRL_ID_REQUEST);
ctrl_data = ctrl_send_and_recv(ctrl_data, 0, ~0);
if (ntohl(ctrl_data.id) != UMTRX_CTRL_ID_RESPONSE)
throw uhd::runtime_error(str(boost::format("unexpected firmware response: -->%c<--") % (char)ntohl(ctrl_data.id)));
}
ctrl_data.id = htonl(UMTRX_CTRL_ID_REQUEST);
ctrl_data = ctrl_send_and_recv(ctrl_data, _protocol_compat, ~0);
if (ntohl(ctrl_data.id) != UMTRX_CTRL_ID_RESPONSE)
throw uhd::runtime_error(str(boost::format("unexpected firmware response: -->%c<--") % (char)ntohl(ctrl_data.id)));
//Save the response compat number for future communication.
_protocol_compat = ntohl(ctrl_data.proto_ver);
// Read EEPROM with UMTRX extensions
@@ -321,7 +316,7 @@ public:
if(len >= sizeof(boost::uint32_t) and (hi < compat or lo > compat)){
throw uhd::runtime_error(str(boost::format(
"\nPlease update the firmware and FPGA images for your device.\n"
"See the application notes for USRP2/N-Series for instructions.\n"
"See the application notes for UmTRX for instructions.\n"
"Expected protocol compatibility number %s, but got %d:\n"
"The firmware build is not compatible with the host code build."
) % ((lo == hi)? (boost::format("%d") % hi) : (boost::format("[%d to %d]") % lo % hi)) % compat));

View File

@@ -707,6 +707,35 @@ umtrx_impl::umtrx_impl(const device_addr_t &device_addr)
.subscribe(boost::bind(&umtrx_impl::set_dc_offset_correction, this, fe_name, _1))
.set(std::complex<double>(dc_i, dc_q));
//rx cal props
_tree->create<uint8_t>(rx_rf_fe_path / "lms6002d" / "rx_fe_dc_i" / "value")
.publish(boost::bind(&lms6002d_ctrl::get_rxfe_dc_i, ctrl))
.subscribe(boost::bind(&lms6002d_ctrl::set_rxfe_dc_i, ctrl, _1));
_tree->create<uint8_t>(rx_rf_fe_path / "lms6002d" / "rx_fe_dc_q" / "value")
.publish(boost::bind(&lms6002d_ctrl::get_rxfe_dc_q, ctrl))
.subscribe(boost::bind(&lms6002d_ctrl::set_rxfe_dc_q, ctrl, _1));
_tree->create<uint8_t>(rx_rf_fe_path / "lms6002d" / "rx_lpf_dc_i" / "value")
.publish(boost::bind(&lms6002d_ctrl::get_rxlpf_dc_i, ctrl))
.subscribe(boost::bind(&lms6002d_ctrl::set_rxlpf_dc_i, ctrl, _1));
_tree->create<uint8_t>(rx_rf_fe_path / "lms6002d" / "rx_lpf_dc_q" / "value")
.publish(boost::bind(&lms6002d_ctrl::get_rxlpf_dc_q, ctrl))
.subscribe(boost::bind(&lms6002d_ctrl::set_rxlpf_dc_q, ctrl, _1));
_tree->create<uint8_t>(rx_rf_fe_path / "lms6002d" / "rxvga2_dc_reference" / "value")
.publish(boost::bind(&lms6002d_ctrl::get_rxvga2_dc_reference, ctrl))
.subscribe(boost::bind(&lms6002d_ctrl::set_rxvga2_dc_reference, ctrl, _1));
_tree->create<uint8_t>(rx_rf_fe_path / "lms6002d" / "rxvga2a_dc_i" / "value")
.publish(boost::bind(&lms6002d_ctrl::get_rxvga2a_dc_i, ctrl))
.subscribe(boost::bind(&lms6002d_ctrl::set_rxvga2a_dc_i, ctrl, _1));
_tree->create<uint8_t>(rx_rf_fe_path / "lms6002d" / "rxvga2a_dc_q" / "value")
.publish(boost::bind(&lms6002d_ctrl::get_rxvga2a_dc_q, ctrl))
.subscribe(boost::bind(&lms6002d_ctrl::set_rxvga2a_dc_q, ctrl, _1));
_tree->create<uint8_t>(rx_rf_fe_path / "lms6002d" / "rxvga2b_dc_i" / "value")
.publish(boost::bind(&lms6002d_ctrl::get_rxvga2b_dc_i, ctrl))
.subscribe(boost::bind(&lms6002d_ctrl::set_rxvga2b_dc_i, ctrl, _1));
_tree->create<uint8_t>(rx_rf_fe_path / "lms6002d" / "rxvga2b_dc_q" / "value")
.publish(boost::bind(&lms6002d_ctrl::get_rxvga2b_dc_q, ctrl))
.subscribe(boost::bind(&lms6002d_ctrl::set_rxvga2b_dc_q, ctrl, _1));
// Alias diversity switch control from mb_path
property_alias<bool>(_tree, mb_path / "divsw"+(fe_name=="A"?"1":"2"), rx_rf_fe_path / "diversity");
}

View File

@@ -49,15 +49,20 @@ namespace asio = boost::asio;
* print json.loads(f.readline())
* {u'result': u'true'}
*
* #get the value of a tree entry, types can be BOOL, INT, DOUBLE, SENSOR, RANGE
* #get the value of a tree entry, types can be BOOL, INT, DOUBLE, COMPLEX, SENSOR, RANGE
* s.send(json.dumps(dict(action='GET', path='/mboards/0/sensors/tempA', type='SENSOR'))+'\n')
* print json.loads(f.readline())
* {u'result': {u'unit': u'C', u'name': u'TempA', u'value': u'61.625000'}}
*
* #set the value of a tree entry, types can be BOOL, INT, DOUBLE
* #set the value of a tree entry, types can be BOOL, INT, DOUBLE, COMPLEX
* s.send(json.dumps(dict(action='SET', path='/mboards/0/dboards/A/rx_frontends/0/freq/value', type='DOUBLE', value=1e9))+'\n')
* print json.loads(f.readline())
* {} #empty response means no error
*
* #in case of COMPLEX 'value' is an array of [real, imag] values
* s.send(json.dumps(dict(action='SET', path='/mboards/0/rx_frontends/A/dc_offset/value', type='COMPLEX', value=[0.1, 0.0]))+'\n')
* print json.loads(f.readline())
* {} #empty response means no error
*/
void umtrx_impl::status_monitor_start(const uhd::device_addr_t &device_addr)
@@ -187,11 +192,22 @@ void umtrx_impl::client_query_handle1(const boost::property_tree::ptree &request
else if (action == "GET")
{
const std::string type = request.get("type", "");
if (type.empty()) response.put("error", "type field not specified: STRING, BOOL, INT, DOUBLE, SENSOR, RANGE");
if (type.empty()) response.put("error", "type field not specified: STRING, BOOL, INT, DOUBLE, COMPLEX, SENSOR, RANGE");
else if (type == "STRING") response.put("result", _tree->access<std::string>(path).get());
else if (type == "BOOL") response.put("result", _tree->access<bool>(path).get());
else if (type == "INT") response.put("result", _tree->access<int>(path).get());
else if (type == "DOUBLE") response.put("result", _tree->access<double>(path).get());
else if (type == "COMPLEX")
{
boost::property_tree::ptree result;
boost::property_tree::ptree ptree_i, ptree_q;
const std::complex<double> c = _tree->access<std::complex<double> >(path).get();
ptree_i.put("", c.real());
ptree_q.put("", c.imag());
result.push_back(std::make_pair("", ptree_i));
result.push_back(std::make_pair("", ptree_q));
response.add_child("result", result);
}
else if (type == "SENSOR")
{
boost::property_tree::ptree result;
@@ -219,11 +235,18 @@ void umtrx_impl::client_query_handle1(const boost::property_tree::ptree &request
else if (action == "SET")
{
const std::string type = request.get("type", "");
if (type.empty()) response.put("error", "type field not specified: STRING, BOOL, INT, DOUBLE");
if (type.empty()) response.put("error", "type field not specified: STRING, BOOL, INT, DOUBLE, COMPLEX");
else if (type == "STRING") _tree->access<std::string>(path).set(request.get<std::string>("value"));
else if (type == "BOOL") _tree->access<bool>(path).set(request.get<bool>("value"));
else if (type == "INT") _tree->access<int>(path).set(request.get<int>("value"));
else if (type == "DOUBLE") _tree->access<double>(path).set(request.get<double>("value"));
else if (type == "COMPLEX")
{
boost::property_tree::ptree value = request.get_child("value");
double i = value.front().second.get<double>("");
double q = value.back().second.get<double>("");
_tree->access<std::complex<double> >(path).set(std::complex<double>(i, q));
}
else response.put("error", "unknown type: " + type);
}
else if (action == "HAS")

View File

@@ -32,7 +32,7 @@ extern "C" {
//fpga and firmware compatibility numbers
#define USRP2_FPGA_COMPAT_NUM 9
#define USRP2_FW_COMPAT_NUM 12
#define USRP2_FW_VER_MINOR 2
#define USRP2_FW_VER_MINOR 3
//used to differentiate control packets over data port
#define USRP2_INVALID_VRT_HEADER 0
@@ -127,8 +127,12 @@ typedef enum{
typedef enum{
UMTRX_ZPU_REQUEST_GET_VCTCXO_DAC = 1,
UMTRX_ZPU_REQUEST_SET_VCTCXO_DAC = 2
/* GPSDO control to be here */
UMTRX_ZPU_REQUEST_SET_VCTCXO_DAC = 2,
UMTRX_ZPU_REQUEST_SET_GPSDO_DEBUG = 3,
UMTRX_ZPU_REQUEST_GET_GPSDO_FREQ = 4,
UMTRX_ZPU_REQUEST_GET_GPSDO_FREQ_LPF = 5,
UMTRX_ZPU_REQUEST_GET_GPSDO_PPS_SECS = 6,
UMTRX_ZPU_REQUEST_SET_GPSDO_PPS_TICKS = 7
} umtrx_zpu_action_t;
typedef struct{

View File

@@ -0,0 +1,6 @@
TypesDB "/usr/share/collectd/umtrx.types.db"
LoadPlugin exec
<Plugin exec>
Exec "fairwaves-monitoring" "/usr/share/umtrx/umtrx2collectd.py"
</Plugin>

View File

@@ -0,0 +1 @@
sensor value:GAUGE:U:U

View File

@@ -0,0 +1,54 @@
#!/usr/bin/python -u
# -*- coding: utf-8 -*-
import os
import sched, time
from umtrx_property_tree import umtrx_property_tree
from umtrx_vswr import umtrx_vswr
BOARD_ID = "0"
SENSORS_PATH = "/mboards/{id}/sensors".format(id=BOARD_ID)
VSWR_CALIBRATION = 0 # = TM10_VSWR_cal
HOSTNAME = os.environ['COLLECTD_HOSTNAME'] if 'COLLECTD_HOSTNAME' in os.environ else 'localhost'
INTERVAL = os.environ['COLLECTD_INTERVAL'] if 'COLLECTD_INTERVAL' in os.environ else '60'
umtrx = umtrx_property_tree()
umtrx.connect()
# typically this yields: ['tempA', 'tempB', 'voltagePR1', 'voltagePF1', 'voltagePR2', 'voltagePF2', 'voltagezero', 'voltageVin', 'voltageVinPA', 'voltageDCOUT']
sensors_list = umtrx.list_path_raw(SENSORS_PATH).get("result", [])
def publish():
now = time.time()
current_sensors = {sensor: umtrx.query_sensor_value(SENSORS_PATH + "/" + sensor) for sensor in sensors_list}
for channel in ["1", "2"]:
vpf_name = "voltagePF" + channel
vpr_name = "voltagePR" + channel
if vpf_name in current_sensors and vpr_name in current_sensors:
vswr = umtrx_vswr(float(current_sensors[vpf_name]), float(current_sensors[vpr_name]), VSWR_CALIBRATION)
current_sensors["VSWR" + channel] = vswr.vswr()
current_sensors["ReturnLoss" + channel] = vswr.return_loss()
for name, value in current_sensors.items():
print "PUTVAL {host}/umtrx-{id}/sensor-{name} interval={interval} {now}:{value}".format(
host=HOSTNAME, id=BOARD_ID, name=name.lower(), interval=INTERVAL, now=now, value=value)
s = sched.scheduler(time.time, time.sleep)
def timer_loop():
s.enter(float(INTERVAL), 1, timer_loop, ())
publish()
timer_loop()
s.run()
umtrx.close()

View File

@@ -26,7 +26,7 @@ class umtrx_property_tree:
d = dict(action=action, path=path)
if value_type is not None: d['type'] = value_type
if value is not None: d['value'] = value
return self.s.send(json.dumps(d)+'\n')
return self.s.send(bytes(json.dumps(d)+'\n', 'UTF-8'))
def _recv_response(self):
resp = self.f.readline().strip()
@@ -63,21 +63,25 @@ class umtrx_property_tree:
self._send_request('GET', path, value_type='STRING')
return self._recv_response()
def query_complex_raw(self, path):
self._send_request('GET', path, value_type='COMPLEX')
return self._recv_response()
#
# Getters (value)
#
def query_bool_value(self, path):
res = self.query_bool_raw(path)
return res['result']['value']
return res['result']
def query_int_value(self, path):
res = self.query_int_raw(path)
return res['result']['value']
return int(res['result'])
def query_double_value(self, path):
res = self.query_double_raw(path)
return res['result']['value']
return float(res['result'])
def query_sensor_value(self, path):
res = self.query_sensor_raw(path)
@@ -91,6 +95,12 @@ class umtrx_property_tree:
res = self.query_string_raw(path)
return res['result']
def query_complex_value(self, path):
res = self.query_complex_raw(path)
i = float(res['result'][0])
q = float(res['result'][1])
return complex(i, q)
#
# Setters
#
@@ -111,6 +121,13 @@ class umtrx_property_tree:
self._send_request('SET', path, value_type='STRING', value=val)
return self._recv_response()
def set_complex(self, path, val):
if type(val) is complex:
# Convert complex to an array
val = [val.real, val.imag]
self._send_request('SET', path, value_type='COMPLEX', value=val)
return self._recv_response()
#
# Check path presence and list paths
#

Binary file not shown.

View File

@@ -111,8 +111,8 @@ static void handle_udp_ctrl_packet(
//ensure that the protocol versions match
if (payload_len >= sizeof(uint32_t) && ctrl_data_in->proto_ver != USRP2_FW_COMPAT_NUM){
if (ctrl_data_in->proto_ver) printf("!Error in control packet handler: Expected compatibility number %d, but got %d\n",
USRP2_FW_COMPAT_NUM, ctrl_data_in->proto_ver
if (ctrl_data_in->proto_ver) printf("Received a control packet with compatibility number %d. Replying with our compatibility number %d.\n",
ctrl_data_in->proto_ver, USRP2_FW_COMPAT_NUM
);
#ifdef UMTRX
ctrl_data_in_id = UMTRX_CTRL_ID_REQUEST;
@@ -123,7 +123,7 @@ static void handle_udp_ctrl_packet(
//ensure that this is not a short packet
if (payload_len < sizeof(usrp2_ctrl_data_t)){
printf("!Error in control packet handler: Expected payload length %d, but got %d\n",
printf("Received a short packet: Expected payload length %d, but got %d. Sending USRP2_CTRL_ID_HUH_WHAT\n",
(int)sizeof(usrp2_ctrl_data_t), payload_len
);
ctrl_data_in_id = USRP2_CTRL_ID_HUH_WHAT;
@@ -162,10 +162,25 @@ static void handle_udp_ctrl_packet(
ctrl_data_out.data.zpu_action.action = ctrl_data_in->data.zpu_action.action;
switch (ctrl_data_in->data.zpu_action.action) {
case UMTRX_ZPU_REQUEST_GET_VCTCXO_DAC:
ctrl_data_out.data.zpu_action.data = (uint32_t)get_vctcxo_dac();
ctrl_data_out.data.zpu_action.data = (uint32_t)gpsdo_get_dac();
break;
case UMTRX_ZPU_REQUEST_SET_VCTCXO_DAC:
set_vctcxo_dac((uint16_t)ctrl_data_in->data.zpu_action.data);
gpsdo_set_dac((uint16_t)ctrl_data_in->data.zpu_action.data);
break;
case UMTRX_ZPU_REQUEST_SET_GPSDO_DEBUG:
gpsdo_set_debug((bool)ctrl_data_in->data.zpu_action.data);
break;
case UMTRX_ZPU_REQUEST_GET_GPSDO_FREQ:
ctrl_data_out.data.zpu_action.data = gpsdo_get_last_freq();
break;
case UMTRX_ZPU_REQUEST_GET_GPSDO_FREQ_LPF:
ctrl_data_out.data.zpu_action.data = gpsdo_get_lpf_freq();
break;
case UMTRX_ZPU_REQUEST_GET_GPSDO_PPS_SECS:
ctrl_data_out.data.zpu_action.data = gpsdo_get_last_pps_secs();
break;
case UMTRX_ZPU_REQUEST_SET_GPSDO_PPS_TICKS:
ctrl_data_out.data.zpu_action.data = gpsdo_get_last_pps_ticks();
break;
}
}
@@ -320,6 +335,8 @@ main(void)
#endif
printf("FPGA compatibility number: %d\n", USRP2_FPGA_COMPAT_NUM);
printf("Firmware compatibility number: %d\n", USRP2_FW_COMPAT_NUM);
printf("Firmware version minor: %d\n", USRP2_FW_VER_MINOR);
printf("Firmware GIT commit hash: %x\n", GITHASH);
//init readback for firmware minor version number
fw_regs[U2_FW_REG_VER_MINOR] = USRP2_FW_VER_MINOR;

View File

@@ -47,4 +47,5 @@ SET(COMMON_SRCS
${CMAKE_SOURCE_DIR}/lib/banal.c
${CMAKE_SOURCE_DIR}/lib/udp_uart.c
${CMAKE_SOURCE_DIR}/lib/gpsdo.c
${CMAKE_SOURCE_DIR}/lib/time64.c
)

View File

@@ -19,6 +19,7 @@
#include "u2_init.h"
#include "pic.h"
#include "spi.h"
#include "time64.h"
#include "memory_map.h"
@@ -31,7 +32,7 @@
#include <string.h>
#include <stdbool.h>
/* #define GPSDO_DEBUG 1 */
static int gpsdo_debug = 0;
/* DAC */
@@ -39,14 +40,12 @@
#define DAC_BITS 12
uint16_t dac_value; /* Current DAC value */
static uint16_t dac_value; /* Current DAC value */
int
set_vctcxo_dac(uint16_t v)
static int
_set_vctcxo_dac(uint16_t v)
{
#ifdef GPSDO_DEBUG
printf("DAC: %d\n", v);
#endif
if (gpsdo_debug) printf("DAC: %d\n", v);
dac_value = v;
return spi_transact(
SPI_TXRX, SPI_SS_DAC,
@@ -55,8 +54,8 @@ set_vctcxo_dac(uint16_t v)
);
}
uint16_t
get_vctcxo_dac(void)
static uint16_t
_get_vctcxo_dac(void)
{
return dac_value;
}
@@ -139,8 +138,10 @@ _gpsdo_pid_step(int32_t val)
else if (tot < -PID_MAX_DEV)
tot = -PID_MAX_DEV;
if (gpsdo_debug) printf("GPSDO: correction = %d (P=%d, I=%d, D=%d)>>%d limit +-%d\n", tot, p_term, i_term, d_term, PID_SCALE_SHIFT, PID_MAX_DEV);
/* Update DAC */
set_vctcxo_dac( PID_MID_VAL + tot );
_set_vctcxo_dac( PID_MID_VAL + tot );
}
@@ -148,7 +149,14 @@ _gpsdo_pid_step(int32_t val)
/* Driver */
/* ------ */
static int32_t g_val_lpf = PID_TARGET;
/* Extra bits of precision for g_val_lpf */
#define VAL_LPF_PRECISION 3
#define VAL_LPF_INIT_VALUE (PID_TARGET<<VAL_LPF_PRECISION)
static uint32_t g_val_lpf = VAL_LPF_INIT_VALUE;
static uint32_t g_prev_secs = 0;
static uint32_t g_prev_ticks = 0;
static uint32_t g_last_calc_freq = 0; /* Last calculated VCTCXO frequency */
static void
_gpsdo_irq_handler(unsigned irq)
@@ -156,36 +164,47 @@ _gpsdo_irq_handler(unsigned irq)
if (gpsdo_regs->csr & GPSDO_CSR_RDY)
{
/* Counter value */
int32_t val = gpsdo_regs->cnt;
#ifdef GPSDO_DEBUG
printf("GPSDO Count: %d\n", val);
#endif
uint32_t val = gpsdo_regs->cnt;
/* Read the current wall time */
uint32_t cur_secs, cur_ticks;
time64_read(&cur_secs, &cur_ticks);
/* Next request */
gpsdo_regs->csr = GPSDO_CSR_REQ;
/* TODO:: Save the current wall time to be able check
time passed since the last lock later. This is useful
e.g. to check whether we still have a GPS lock.*/
if (gpsdo_debug) printf("GPSDO: Counter = %u @ %u sec %u ticks\n", val, cur_secs, cur_ticks);
/* Check validity of value */
if (abs(val - PID_TARGET) < 100000)
{
/* Save calculated frequency */
g_last_calc_freq = val;
/* LPF the value */
g_val_lpf = (g_val_lpf * 7 + val + 4) >> 3;
/* Integer overlow warning! */
/* This works for val ~= 52M, but don't try to use it with much larger values - it will overflow */
g_val_lpf = (g_val_lpf * 7 + (val<<VAL_LPF_PRECISION) + 4) >> 3;
if (gpsdo_debug) printf("GPSDO: Filtered counter = %u + %u/8\n",
(g_val_lpf>>VAL_LPF_PRECISION), (g_val_lpf&((1<<VAL_LPF_PRECISION)-1)));
/* Update PID */
_gpsdo_pid_step(g_val_lpf);
_gpsdo_pid_step(g_val_lpf>>VAL_LPF_PRECISION);
}
/* Save the current wall time */
g_prev_secs = cur_secs;
g_prev_ticks = cur_ticks;
}
}
void
gpsdo_init(void)
{
/* Set last saved freq to an invalid value */
g_last_calc_freq = 0;
/* Set the DAC to mid value */
set_vctcxo_dac( PID_MID_VAL );
_set_vctcxo_dac( PID_MID_VAL );
/* Register IRQ handler */
pic_register_handler(IRQ_GPSDO, _gpsdo_irq_handler);
@@ -195,4 +214,46 @@ gpsdo_init(void)
/* Start request */
gpsdo_regs->csr = GPSDO_CSR_REQ;
/* Save the current wall time.
* We can use it to estimate time to lock */
time64_read(&g_prev_secs, &g_prev_ticks);
}
void gpsdo_set_debug(int level)
{
gpsdo_debug = level;
}
void gpsdo_set_dac(uint16_t v)
{
/* Reset PID */
_gpsdo_pid_init();
/* Set the DAC value */
_set_vctcxo_dac(v);
}
uint16_t gpsdo_get_dac(void)
{
return _get_vctcxo_dac();
}
uint32_t gpsdo_get_last_freq(void)
{
return g_last_calc_freq;
}
uint32_t gpsdo_get_lpf_freq(void)
{
return g_val_lpf;
}
uint32_t gpsdo_get_last_pps_secs(void)
{
return g_prev_secs;
}
uint32_t gpsdo_get_last_pps_ticks(void)
{
return g_prev_ticks;
}

View File

@@ -21,10 +21,25 @@
void gpsdo_init(void);
/* Enable/disable GPSDO debug printing */
void gpsdo_set_debug(int level);
/* Set value of the VCTCXO DAC */
int set_vctcxo_dac(uint16_t v);
void gpsdo_set_dac(uint16_t v);
/* Get the current VCTCXO DAC value */
uint16_t get_vctcxo_dac(void);
uint16_t gpsdo_get_dac(void);
/* Get the last calculated VCTCXO frequency */
uint32_t gpsdo_get_last_freq(void);
/* Get the last alpha/8 filtered VCTCXO frequency (29.3 fixed point) */
uint32_t gpsdo_get_lpf_freq(void);
/* Get time (seconds part) of the last PPS pulse */
uint32_t gpsdo_get_last_pps_secs(void);
/* Get time (ticks part) of the last PPS pulse */
uint32_t gpsdo_get_last_pps_ticks(void);
#endif /* INCLUDED_GPSDO_H */

93
zpu/lib/time64.c Normal file
View File

@@ -0,0 +1,93 @@
/*
* Copyright 2017 Alexander Chemeris <Alexander.Chemeris@fairwaves.co>
*
* 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 "memory_map.h"
/* printf headers */
//#include "nonstdio.h"
/* standard headers */
#include <stddef.h>
//#include <stdlib.h>
//#include <string.h>
#include <stdbool.h>
#include <limits.h>
#define UINT32_MAX UINT_MAX
void
time64_read(uint32_t *secs, uint32_t *ticks)
{
uint32_t cur_secs, cur_secs2;
cur_secs = readback_mux->time64_secs_rb;
*ticks = readback_mux->time64_ticks_rb;
cur_secs2 = readback_mux->time64_secs_rb;
/* Check for seconds wrap */
if (cur_secs2 != cur_secs) {
/* Decide which seconds reading is correct.
* Here we assume that we're reading fast and time between
* two readings is negligible compared to a 32-bit counter
* wrap time.
*/
if (*ticks < UINT32_MAX/2) {
/* First half of the time - wrap has just happened */
*secs = cur_secs2;
} else {
/* Second half - wrap has not happened yet */
*secs = cur_secs;
}
}
}
int
time64_compare(uint32_t secs1, uint32_t ticks1,
uint32_t secs2, uint32_t ticks2)
{
if (secs1 == secs2) {
if (ticks1 < ticks2) {
return -1;
} else if (ticks1 == ticks2) {
return 0;
} else {
return 1;
}
} else if (secs1 < secs2) {
return -1;
} else {
return 1;
}
}
void
time64_add_ticks(uint32_t *secs, uint32_t *ticks,
uint32_t ticks2)
{
if (UINT32_MAX - *ticks > ticks2) {
*secs += 1;
}
*ticks += ticks2;
}
bool
time64_is_elapsed(uint32_t secs1, uint32_t ticks1,
uint32_t secs2, uint32_t ticks2,
uint32_t ticks_elapsed)
{
time64_add_ticks(&secs1, &ticks1, ticks_elapsed);
return time64_compare(secs1, ticks1, secs2, ticks2) >= 0;
}

42
zpu/lib/time64.h Normal file
View File

@@ -0,0 +1,42 @@
/*
* Copyright 2017 Alexander Chemeris <Alexander.Chemeris@fairwaves.co>
*
* 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/>.
*/
/* Operations with time64 counters handling wrapping safely */
/* Read counters */
void
time64_read(uint32_t *secs, uint32_t *ticks);
/* Compare two counters.
* Return -1 if conter 1 is less than counter 2.
* Return 0 if conter 1 is equal counter 2.
* Return 1 if conter 1 is larger than counter 2.
*/
int
time64_compare(uint32_t secs1, uint32_t ticks1,
uint32_t secs2, uint32_t ticks2);
/* Add ticks to a counter */
void
time64_add_ticks(uint32_t *secs, uint32_t *ticks,
uint32_t ticks2);
/* Is a given amount of ticks elapsed? */
bool
time64_is_elapsed(uint32_t secs1, uint32_t ticks1,
uint32_t secs2, uint32_t ticks2,
uint32_t ticks_elapsed);